Compare commits

...

27 Commits
v1.14 ... main

Author SHA1 Message Date
Giuseppe Scrivano 2772fa30c7
Merge pull request #446 from giuseppe/tag-1.15
release 1.15
2025-05-08 13:34:23 +02:00
Giuseppe Scrivano 0ea1677326
configure.ac: prepare new release cycle
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-05-08 08:36:11 +02:00
Giuseppe Scrivano c5f3ef142e
NEWS: tag 1.15
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-05-08 08:36:11 +02:00
Giuseppe Scrivano e8532dce73
Merge pull request #445 from giuseppe/fix-lookup-lower-layers-of-freshly-created-dir
main: lookup upperdir only for created directories
2025-05-07 17:01:02 +02:00
Giuseppe Scrivano 6a1040f01d
main: lookup upperdir only for created directories
always stop looking up at the upper layer for newly created
directories so to not expose the lower directory content.

Closes: https://github.com/containers/fuse-overlayfs/issues/444

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-05-07 11:56:22 +02:00
Giuseppe Scrivano d5b2cec0e7
main: fix layer lookup in make_ovl_node
Stop iterating through layers in `make_ovl_node`
if the current layer being checked is the `last_layer`
of the parent node.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-05-07 11:44:28 +02:00
Giuseppe Scrivano 28ba1fd247
Merge pull request #443 from giuseppe/update-hash-map-module
main: use hash_remove instead of hash_delete
2025-04-30 08:52:47 +02:00
Giuseppe Scrivano b88a093bd4
main: use hash_remove instead of hash_delete
the function is deprecated in gnulib

Closes: https://github.com/containers/fuse-overlayfs/issues/442

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-29 23:41:30 +02:00
Giuseppe Scrivano ce8d185c16
lib: update hash module from gnulib
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-29 23:34:01 +02:00
Giuseppe Scrivano 3af99f6552
Merge pull request #441 from giuseppe/unescape-dirs
main: Allow escaped colons in directory paths
2025-04-29 22:28:08 +02:00
Giuseppe Scrivano 27cabd561b
main: Allow escaped colons in directory paths
Allow directory paths specified for lowerdir, upperdir and workdir to
contain colon characters.

Previously, colons were unconditionally treated as separators,
making it impossible to use directories with colons in their names.

Closes: https://github.com/containers/fuse-overlayfs/issues/440

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano b31d82ff8e
main: use parsed upper layer directory
Use the directory in the loaded layer structure, and do not use the
value passed on the CLI.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano 02a3582501
refactor: Remove unnecessary upperdir realpath
The explicit resolution of the upper directory path using `realpath()`
is not required before passing it to `read_dirs` since
`direct_load_data_source()` already does it.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano 5e7f7d756e
github: update checkout action
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano 4761dd0675
github: update uraimo action
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano fc6a2b916e
github: update upload-artifact action
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano e1aae5aa6b
github: enable unprivileged user namespaces
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2025-04-28 18:46:30 +02:00
Giuseppe Scrivano 6df9cd27c7
Merge pull request #439 from alvistack/AC_INIT-1.15-dev
Update AC_INIT to 1.15-dev
2025-04-28 12:29:34 +02:00
Wong Hoi Sing Edison efb61490c3
Update AC_INIT to 1.15-dev
The version string should update as "1.14" when we have stable release:

    -AC_INIT([fuse-overlayfs], [1.13-dev], [giuseppe@scrivano.org])
    +AC_INIT([fuse-overlayfs], [1.14], [giuseppe@scrivano.org])

But since we had already get
https://github.com/containers/fuse-overlayfs/releases/tag/v1.14 since
2024-06-28, here we bump the version string to "1.15-dev" for next
stable release.

Fixes https://github.com/containers/fuse-overlayfs/issues/438

Signed-off-by: Wong Hoi Sing Edison <hswong3i@pantarei-design.com>
2025-03-26 11:56:57 +08:00
Daniel J Walsh a54a9f2e88
Merge pull request #434 from giuseppe/use-extended-override-xattr
main: use extended override xattr
2024-12-02 10:09:47 -05:00
Giuseppe Scrivano 6bbd62bb13
main: use extended override xattr
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-12-02 09:52:35 +01:00
Giuseppe Scrivano 8d33894c48
github: update run-on-arch-action
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-12-02 09:52:35 +01:00
Giuseppe Scrivano cf8269b5f6
github: update ubuntu version
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-12-02 09:52:35 +01:00
Giuseppe Scrivano af77bb6584
tests: bump fedora version
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-12-02 09:52:35 +01:00
Giuseppe Scrivano ab0b0f6d46
Merge pull request #430 from hholst80/main
Remove unsupported option "lazytime"
2024-08-18 02:33:09 +02:00
Henrik Holst e107b0bfe4 Remove unsupported option "lazytime"
Signed-off-by: Henrik Holst <6200749+hholst80@users.noreply.github.com>
2024-08-17 11:41:49 +02:00
Daniel J Walsh 147609b1fb
Merge pull request #426 from giuseppe/tag-1.14
NEWS: tag 1.14
2024-06-27 13:48:23 -04:00
13 changed files with 1862 additions and 302 deletions

View File

@ -5,7 +5,7 @@ on:
jobs: jobs:
release: release:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:

View File

@ -4,23 +4,23 @@ on: [push, pull_request]
jobs: jobs:
build_job: build_job:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
name: Build on ${{ matrix.arch }} name: Build on ${{ matrix.arch }}
strategy: strategy:
matrix: matrix:
include: include:
- arch: armv7 - arch: armv7
distro: ubuntu22.04 distro: ubuntu_latest
- arch: aarch64 - arch: aarch64
distro: ubuntu22.04 distro: ubuntu_latest
- arch: s390x - arch: s390x
distro: ubuntu22.04 distro: ubuntu_latest
- arch: ppc64le - arch: ppc64le
distro: ubuntu22.04 distro: ubuntu_latest
steps: steps:
- uses: actions/checkout@v2.1.0 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2.7.2 - uses: uraimo/run-on-arch-action@v3.0.1
name: Build name: Build
id: build id: build
with: with:
@ -42,14 +42,14 @@ jobs:
make -j $(nproc) make -j $(nproc)
- name: Archive build artifacts - name: Archive build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.6.2
with: with:
name: fuse-overlayfs-${{ matrix.arch }}-${{ matrix.distro }} name: fuse-overlayfs-${{ matrix.arch }}-${{ matrix.distro }}
path: | path: |
fuse-overlayfs fuse-overlayfs
Test: Test:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -61,7 +61,7 @@ jobs:
TAGS: exclude_graphdriver_devicemapper exclude_graphdriver_btrfs no_libsubid TAGS: exclude_graphdriver_devicemapper exclude_graphdriver_btrfs no_libsubid
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: install dependencies - name: install dependencies
run: | run: |
@ -88,15 +88,18 @@ jobs:
sudo cp fuse-overlayfs /sbin sudo cp fuse-overlayfs /sbin
- name: Archive build artifacts - name: Archive build artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4.6.2
with: with:
name: fuse-overlayfs-x86_64-ubuntu22.04 name: fuse-overlayfs-x86_64-ubuntu-latest
path: | path: |
fuse-overlayfs fuse-overlayfs
if: ${{ matrix.test == 'ovl-whiteouts' }} if: ${{ matrix.test == 'ovl-whiteouts' }}
- name: run test - name: run test
run: | run: |
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
case "${{ matrix.test }}" in case "${{ matrix.test }}" in
ovl-whiteouts) ovl-whiteouts)
sudo sh -c "(cd /unionmount-testsuite; unshare -m ./run --ov --fuse=fuse-overlayfs --xdev)" sudo sh -c "(cd /unionmount-testsuite; unshare -m ./run --ov --fuse=fuse-overlayfs --xdev)"

7
NEWS
View File

@ -1,3 +1,10 @@
* fuse-overlayfs-1.15
- main: lookup upperdir only for created directories.
- main: allow escaped colons in directory paths.
- main: use extended override xattr to support devices.
- remove unsupported option "lazytime".
* fuse-overlayfs-1.14 * fuse-overlayfs-1.14
- isolate security xattrs for STAT_OVERRIDE_CONTAINERS. Prefix all - isolate security xattrs for STAT_OVERRIDE_CONTAINERS. Prefix all

View File

@ -1,5 +1,5 @@
AC_PREREQ([2.69]) AC_PREREQ([2.69])
AC_INIT([fuse-overlayfs], [1.13-dev], [giuseppe@scrivano.org]) AC_INIT([fuse-overlayfs], [1.16-dev], [giuseppe@scrivano.org])
AC_CONFIG_SRCDIR([main.c]) AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])

View File

@ -1,6 +1,6 @@
/* hash - hashing table processing. /* hash - hashing table processing.
Copyright (C) 1998-2004, 2006-2007, 2009-2023 Free Software Foundation, Inc. Copyright (C) 1998-2004, 2006-2007, 2009-2025 Free Software Foundation, Inc.
Written by Jim Meyering, 1992. Written by Jim Meyering, 1992.
@ -29,6 +29,7 @@
#include "bitrotate.h" #include "bitrotate.h"
#include "xalloc-oversized.h" #include "xalloc-oversized.h"
#include <errno.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -499,13 +500,17 @@ compute_bucket_size (size_t candidate, const Hash_tuning *tuning)
{ {
float new_candidate = candidate / tuning->growth_threshold; float new_candidate = candidate / tuning->growth_threshold;
if ((float) SIZE_MAX <= new_candidate) if ((float) SIZE_MAX <= new_candidate)
return 0; goto nomem;
candidate = new_candidate; candidate = new_candidate;
} }
candidate = next_prime (candidate); candidate = next_prime (candidate);
if (xalloc_oversized (candidate, sizeof (struct hash_entry *))) if (xalloc_oversized (candidate, sizeof (struct hash_entry *)))
return 0; goto nomem;
return candidate; return candidate;
nomem:
errno = ENOMEM;
return 0;
} }
Hash_table * Hash_table *
@ -534,6 +539,7 @@ hash_initialize (size_t candidate, const Hash_tuning *tuning,
if the user provides invalid tuning options, we silently revert to if the user provides invalid tuning options, we silently revert to
using the defaults, and ignore further request to change the tuning using the defaults, and ignore further request to change the tuning
options. */ options. */
errno = EINVAL;
goto fail; goto fail;
} }
@ -607,6 +613,7 @@ hash_free (Hash_table *table)
struct hash_entry *bucket; struct hash_entry *bucket;
struct hash_entry *cursor; struct hash_entry *cursor;
struct hash_entry *next; struct hash_entry *next;
int err = errno;
/* Call the user data_freer function. */ /* Call the user data_freer function. */
if (table->data_freer && table->n_entries) if (table->data_freer && table->n_entries)
@ -649,6 +656,8 @@ hash_free (Hash_table *table)
/* Free the remainder of the hash table structure. */ /* Free the remainder of the hash table structure. */
free (table->bucket); free (table->bucket);
free (table); free (table);
errno = err;
} }
/* Insertion and deletion. */ /* Insertion and deletion. */
@ -762,8 +771,8 @@ hash_find_entry (Hash_table *table, const void *entry,
/* Internal helper, to move entries from SRC to DST. Both tables must /* Internal helper, to move entries from SRC to DST. Both tables must
share the same free entry list. If SAFE, only move overflow share the same free entry list. If SAFE, only move overflow
entries, saving bucket heads for later, so that no allocations will entries, saving bucket heads for later, so that no allocations will
occur. Return false if the free entry list is exhausted and an occur. Return false (setting errno) if the free entry list is
allocation fails. */ exhausted and an allocation fails. */
static bool static bool
transfer_entries (Hash_table *dst, Hash_table *src, bool safe) transfer_entries (Hash_table *dst, Hash_table *src, bool safe)
@ -910,12 +919,14 @@ hash_rehash (Hash_table *table, size_t candidate)
passes. Two passes give worse cache performance and takes passes. Two passes give worse cache performance and takes
longer, but at this point, we're already out of memory, so slow longer, but at this point, we're already out of memory, so slow
and safe is better than failure. */ and safe is better than failure. */
int err = errno;
table->free_entry_list = new_table->free_entry_list; table->free_entry_list = new_table->free_entry_list;
if (! (transfer_entries (table, new_table, true) if (! (transfer_entries (table, new_table, true)
&& transfer_entries (table, new_table, false))) && transfer_entries (table, new_table, false)))
abort (); abort ();
/* table->n_entries already holds its value. */ /* table->n_entries already holds its value. */
free (new_table->bucket); free (new_table->bucket);
errno = err;
return false; return false;
} }
@ -962,7 +973,10 @@ hash_insert_if_absent (Hash_table *table, void const *entry,
* tuning->growth_threshold)); * tuning->growth_threshold));
if ((float) SIZE_MAX <= candidate) if ((float) SIZE_MAX <= candidate)
{
errno = ENOMEM;
return -1; return -1;
}
/* If the rehash fails, arrange to return NULL. */ /* If the rehash fails, arrange to return NULL. */
if (!hash_rehash (table, candidate)) if (!hash_rehash (table, candidate))

View File

@ -1,5 +1,5 @@
/* hash - hashing table processing. /* hash - hashing table processing.
Copyright (C) 1998-1999, 2001, 2003, 2009-2023 Free Software Foundation, Copyright (C) 1998-1999, 2001, 2003, 2009-2025 Free Software Foundation,
Inc. Inc.
Written by Jim Meyering <meyering@ascend.com>, 1998. Written by Jim Meyering <meyering@ascend.com>, 1998.
@ -61,24 +61,24 @@ typedef struct hash_table Hash_table;
number of buckets (used plus unused), or the maximum number of slots, are number of buckets (used plus unused), or the maximum number of slots, are
the same quantity. */ the same quantity. */
extern size_t hash_get_n_buckets (const Hash_table *table) extern size_t hash_get_n_buckets (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
/* Return the number of slots in use (non-empty buckets). */ /* Return the number of slots in use (non-empty buckets). */
extern size_t hash_get_n_buckets_used (const Hash_table *table) extern size_t hash_get_n_buckets_used (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
/* Return the number of active entries. */ /* Return the number of active entries. */
extern size_t hash_get_n_entries (const Hash_table *table) extern size_t hash_get_n_entries (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
/* Return the length of the longest chain (bucket). */ /* Return the length of the longest chain (bucket). */
extern size_t hash_get_max_bucket_length (const Hash_table *table) extern size_t hash_get_max_bucket_length (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
/* Do a mild validation of a hash table, by traversing it and checking two /* Do a mild validation of a hash table, by traversing it and checking two
statistics. */ statistics. */
extern bool hash_table_ok (const Hash_table *table) extern bool hash_table_ok (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
extern void hash_print_statistics (const Hash_table *table, FILE *stream); extern void hash_print_statistics (const Hash_table *table, FILE *stream);
@ -99,7 +99,7 @@ extern void *hash_lookup (const Hash_table *table, const void *entry);
/* Return the first data in the table, or NULL if the table is empty. */ /* Return the first data in the table, or NULL if the table is empty. */
extern void *hash_get_first (const Hash_table *table) extern void *hash_get_first (const Hash_table *table)
; _GL_ATTRIBUTE_PURE;
/* Return the user data for the entry following ENTRY, where ENTRY has been /* Return the user data for the entry following ENTRY, where ENTRY has been
returned by a previous call to either 'hash_get_first' or 'hash_get_next'. returned by a previous call to either 'hash_get_first' or 'hash_get_next'.
@ -124,25 +124,40 @@ typedef bool (*Hash_processor) (void *entry, void *processor_data);
extern size_t hash_do_for_each (const Hash_table *table, extern size_t hash_do_for_each (const Hash_table *table,
Hash_processor processor, void *processor_data); Hash_processor processor, void *processor_data);
/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1.
This is a convenience routine for constructing other hashing functions. */
extern size_t hash_string (const char *string, size_t n_buckets)
_GL_ATTRIBUTE_PURE;
/* Return a hash code of ENTRY, in the range 0..TABLE_SIZE-1.
This hash code function must have the property that if the comparator of
ENTRY1 and ENTRY2 returns true, the hasher returns the same value for ENTRY1
and for ENTRY2.
The hash code function typically computes an unsigned integer and at the end
performs a % TABLE_SIZE modulo operation. This modulo operation is performed
as part of this hash code function, not by the caller, because in some cases
the unsigned integer will be a 'size_t', in other cases an 'uintmax_t' or
even larger. */
typedef size_t (*Hash_hasher) (const void *entry, size_t table_size);
/* Compare two entries, ENTRY1 (being looked up or being inserted) and
ENTRY2 (already in the table) for equality. Return true for equal,
false otherwise. */
typedef bool (*Hash_comparator) (const void *entry1, const void *entry2);
/* This function is invoked when an ENTRY is removed from the hash table. */
typedef void (*Hash_data_freer) (void *entry);
/* /*
* Allocation and clean-up. * Allocation and clean-up.
*/ */
/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1.
This is a convenience routine for constructing other hashing functions. */
extern size_t hash_string (const char *string, size_t n_buckets)
;
extern void hash_reset_tuning (Hash_tuning *tuning); extern void hash_reset_tuning (Hash_tuning *tuning);
typedef size_t (*Hash_hasher) (const void *entry, size_t table_size);
typedef bool (*Hash_comparator) (const void *entry1, const void *entry2);
typedef void (*Hash_data_freer) (void *entry);
/* Reclaim all storage associated with a hash table. If a data_freer /* Reclaim all storage associated with a hash table. If a data_freer
function has been supplied by the user when the hash table was created, function has been supplied by the user when the hash table was created,
this function applies it to the data of each entry before freeing that this function applies it to the data of each entry before freeing that
entry. */ entry. This function preserves errno, like 'free'. */
extern void hash_free (Hash_table *table); extern void hash_free (Hash_table *table);
/* Allocate and return a new hash table, or NULL upon failure. The initial /* Allocate and return a new hash table, or NULL upon failure. The initial
@ -177,23 +192,30 @@ extern void hash_free (Hash_table *table);
You should specify this function only if you want these functions to free You should specify this function only if you want these functions to free
all of your 'data' data. This is typically the case when your data is all of your 'data' data. This is typically the case when your data is
simply an auxiliary struct that you have malloc'd to aggregate several simply an auxiliary struct that you have malloc'd to aggregate several
values. */ values.
Set errno on failure; otherwise errno is unspecified. */
_GL_ATTRIBUTE_NODISCARD
extern Hash_table *hash_initialize (size_t candidate, extern Hash_table *hash_initialize (size_t candidate,
const Hash_tuning *tuning, const Hash_tuning *tuning,
Hash_hasher hasher, Hash_hasher hasher,
Hash_comparator comparator, Hash_comparator comparator,
Hash_data_freer data_freer); Hash_data_freer data_freer)
_GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC (hash_free, 1);
/* Same as hash_initialize, but invokes xalloc_die on memory exhaustion. */ /* Like hash_initialize, but invokes xalloc_die instead of returning NULL. */
/* This function is defined by module 'xhash'. */ /* This function is defined by module 'xhash'. */
_GL_ATTRIBUTE_NODISCARD
extern Hash_table *hash_xinitialize (size_t candidate, extern Hash_table *hash_xinitialize (size_t candidate,
const Hash_tuning *tuning, const Hash_tuning *tuning,
Hash_hasher hasher, Hash_hasher hasher,
Hash_comparator comparator, Hash_comparator comparator,
Hash_data_freer data_freer); Hash_data_freer data_freer)
_GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC (hash_free, 1)
_GL_ATTRIBUTE_RETURNS_NONNULL;
/* Make all buckets empty, placing any chained entries on the free list. /* Make all buckets empty, placing any chained entries on the free list.
Apply the user-specified function data_freer (if any) to the datas of any Apply the user-specified function data_freer (if any) to the data of any
affected entries. */ affected entries. */
extern void hash_clear (Hash_table *table); extern void hash_clear (Hash_table *table);
@ -207,23 +229,26 @@ extern void hash_clear (Hash_table *table);
the table may receive at least CANDIDATE different user entries, including the table may receive at least CANDIDATE different user entries, including
those already in the table, before any other growth of the hash table size those already in the table, before any other growth of the hash table size
occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the
exact number of buckets desired. Return true iff the rehash succeeded. */ exact number of buckets desired. Return true iff the rehash succeeded,
false (setting errno) otherwise. */
_GL_ATTRIBUTE_NODISCARD
extern bool hash_rehash (Hash_table *table, size_t candidate); extern bool hash_rehash (Hash_table *table, size_t candidate);
/* If ENTRY matches an entry already in the hash table, return the pointer /* If ENTRY matches an entry already in the hash table, return the pointer
to the entry from the table. Otherwise, insert ENTRY and return ENTRY. to the entry from the table. Otherwise, insert ENTRY and return ENTRY.
Return NULL if the storage required for insertion cannot be allocated. Return NULL (setting errno) if the storage required for insertion
This implementation does not support duplicate entries or insertion of cannot be allocated. This implementation does not support
NULL. */ duplicate entries or insertion of NULL. */
_GL_ATTRIBUTE_NODISCARD
extern void *hash_insert (Hash_table *table, const void *entry); extern void *hash_insert (Hash_table *table, const void *entry);
/* Same as hash_insert, but invokes xalloc_die on memory exhaustion. */ /* Same as hash_insert, but invokes xalloc_die instead of returning NULL. */
/* This function is defined by module 'xhash'. */ /* This function is defined by module 'xhash'. */
extern void *hash_xinsert (Hash_table *table, const void *entry); extern void *hash_xinsert (Hash_table *table, const void *entry);
/* Insert ENTRY into hash TABLE if there is not already a matching entry. /* Insert ENTRY into hash TABLE if there is not already a matching entry.
Return -1 upon memory allocation failure. Return -1 (setting errno) upon memory allocation failure.
Return 1 if insertion succeeded. Return 1 if insertion succeeded.
Return 0 if there is already a matching entry in the table, Return 0 if there is already a matching entry in the table,
and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT
@ -247,6 +272,7 @@ extern void *hash_remove (Hash_table *table, const void *entry);
/* Same as hash_remove. This interface is deprecated. /* Same as hash_remove. This interface is deprecated.
FIXME: Remove in 2022. */ FIXME: Remove in 2022. */
_GL_ATTRIBUTE_DEPRECATED
extern void *hash_delete (Hash_table *table, const void *entry); extern void *hash_delete (Hash_table *table, const void *entry);
# ifdef __cplusplus # ifdef __cplusplus

View File

@ -1,43 +1,84 @@
# 00gnulib.m4 serial 3 # 00gnulib.m4
dnl Copyright (C) 2009-2019 Free Software Foundation, Inc. # serial 9
dnl Copyright (C) 2009-2025 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it, dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved. dnl with or without modifications, as long as this notice is preserved.
dnl This file is offered as-is, without any warranty.
dnl This file must be named something that sorts before all other dnl This file must be named something that sorts before all other
dnl gnulib-provided .m4 files. It is needed until such time as we can dnl gnulib-provided .m4 files. It is needed until the clang fix has
dnl assume Autoconf 2.64, with its improved AC_DEFUN_ONCE and dnl been included in Autoconf.
dnl m4_divert semantics.
# Until autoconf 2.63, handling of the diversion stack required m4_init # The following definitions arrange to use a compiler option
# to be called first; but this does not happen with aclocal. Wrapping # -Werror=implicit-function-declaration in AC_CHECK_DECL, when the
# the entire execution in another layer of the diversion stack fixes this. # compiler is clang. Without it, clang implicitly declares "known"
# Worse, prior to autoconf 2.62, m4_wrap depended on the underlying m4 # library functions in C mode, but not in C++ mode, which would cause
# for whether it was FIFO or LIFO; in order to properly balance with # Gnulib to omit a declaration and thus later produce an error in C++
# m4_init, we need to undo our push just before anything wrapped within # mode. As of clang 9.0, these "known" functions are identified through
# the m4_init body. The way to ensure this is to wrap both sides of # LIBBUILTIN invocations in the LLVM source file
# m4_init with a one-shot macro that does the pop at the right time. # llvm/tools/clang/include/clang/Basic/Builtins.def.
m4_ifndef([_m4_divert_diversion], # It's not possible to AC_REQUIRE the extra tests from AC_CHECK_DECL,
[m4_divert_push([KILL]) # because AC_CHECK_DECL, like other Autoconf built-ins, is not supposed
m4_define([gl_divert_fixup], [m4_divert_pop()m4_define([$0])]) # to AC_REQUIRE anything: some configure.ac files have their first
m4_define([m4_init], # AC_CHECK_DECL executed conditionally. Therefore append the extra tests
[gl_divert_fixup()]m4_defn([m4_init])[gl_divert_fixup()])]) # to AC_PROG_CC.
AC_DEFUN([gl_COMPILER_CLANG],
[
# AC_DEFUN_ONCE([NAME], VALUE) dnl AC_REQUIRE([AC_PROG_CC])
# ---------------------------- AC_CACHE_CHECK([whether the compiler is clang],
# Define NAME to expand to VALUE on the first use (whether by direct [gl_cv_compiler_clang],
# expansion, or by AC_REQUIRE), and to nothing on all subsequent uses. [dnl Use _AC_COMPILE_IFELSE instead of AC_EGREP_CPP, to avoid error
# Avoid bugs in AC_REQUIRE in Autoconf 2.63 and earlier. This dnl "circular dependency of AC_LANG_COMPILER(C)" if AC_PROG_CC has
# definition is slower than the version in Autoconf 2.64, because it dnl not yet been invoked.
# can only use interfaces that existed since 2.59; but it achieves the _AC_COMPILE_IFELSE(
# same effect. Quoting is necessary to avoid confusing Automake. [AC_LANG_PROGRAM([[
m4_version_prereq([2.63.263], [], #ifdef __clang__
[m4_define([AC][_DEFUN_ONCE], barfbarf
[AC][_DEFUN([$1], #endif
[AC_REQUIRE([_gl_DEFUN_ONCE([$1])], ]],[[]])
[m4_indir([_gl_DEFUN_ONCE([$1])])])])]dnl ],
[AC][_DEFUN([_gl_DEFUN_ONCE([$1])], [$2])])]) [gl_cv_compiler_clang=no],
[gl_cv_compiler_clang=yes])
])
])
AC_DEFUN([gl_COMPILER_PREPARE_CHECK_DECL],
[
dnl AC_REQUIRE([AC_PROG_CC])
dnl AC_REQUIRE([gl_COMPILER_CLANG])
AC_CACHE_CHECK([for compiler option needed when checking for declarations],
[gl_cv_compiler_check_decl_option],
[if test $gl_cv_compiler_clang = yes; then
dnl Test whether the compiler supports the option
dnl '-Werror=implicit-function-declaration'.
saved_ac_compile="$ac_compile"
ac_compile="$ac_compile -Werror=implicit-function-declaration"
dnl Use _AC_COMPILE_IFELSE instead of AC_COMPILE_IFELSE, to avoid a
dnl warning "AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS".
_AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])],
[gl_cv_compiler_check_decl_option='-Werror=implicit-function-declaration'],
[gl_cv_compiler_check_decl_option=none])
ac_compile="$saved_ac_compile"
else
gl_cv_compiler_check_decl_option=none
fi
])
if test "x$gl_cv_compiler_check_decl_option" != xnone; then
ac_compile_for_check_decl="$ac_compile $gl_cv_compiler_check_decl_option"
else
ac_compile_for_check_decl="$ac_compile"
fi
])
dnl Redefine _AC_CHECK_DECL_BODY so that it references ac_compile_for_check_decl
dnl instead of ac_compile. If, for whatever reason, the override of AC_PROG_CC
dnl in zzgnulib.m4 is inactive, use the original ac_compile.
m4_define([_AC_CHECK_DECL_BODY],
[ ac_saved_ac_compile="$ac_compile"
if test -n "$ac_compile_for_check_decl"; then
ac_compile="$ac_compile_for_check_decl"
fi]
m4_defn([_AC_CHECK_DECL_BODY])[ ac_compile="$ac_saved_ac_compile"
])
# gl_00GNULIB # gl_00GNULIB
# ----------- # -----------

File diff suppressed because it is too large Load Diff

25
m4/zzgnulib.m4 Normal file
View File

@ -0,0 +1,25 @@
# zzgnulib.m4
# serial 1
dnl Copyright (C) 2020-2025 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl This file is offered as-is, without any warranty.
dnl This file must be named something that sorts after all other
dnl package- or gnulib-provided .m4 files - at least for those packages
dnl that redefine AC_PROG_CC.
dnl Redefine AC_PROG_CC so that it ends with invocations of gl_COMPILER_CLANG
dnl and gl_COMPILER_PREPARE_CHECK_DECL.
m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])[
gl_COMPILER_CLANG
gl_COMPILER_PREPARE_CHECK_DECL
])
# gl_ZZGNULIB
# -----------
# Witness macro that this file has been included. Needed to force
# Automake to include this file after all other gnulib .m4 files.
AC_DEFUN([gl_ZZGNULIB])

185
main.c
View File

@ -508,40 +508,23 @@ node_dirfd (struct ovl_node *n)
return n->layer->fd; return n->layer->fd;
} }
static bool
has_prefix (const char *str, const char *pref)
{
while (1)
{
if (*pref == '\0')
return true;
if (*str == '\0')
return false;
if (*pref != *str)
return false;
str++;
pref++;
}
return false;
}
static bool static bool
can_access_xattr (const struct ovl_layer *l, const char *name) can_access_xattr (const struct ovl_layer *l, const char *name)
{ {
return ! (has_prefix (name, XATTR_PREFIX) return ! (has_prefix (name, XATTR_PREFIX)
|| has_prefix (name, PRIVILEGED_XATTR_PREFIX) || has_prefix (name, PRIVILEGED_XATTR_PREFIX)
|| has_prefix (name, UNPRIVILEGED_XATTR_PREFIX) || has_prefix (name, UNPRIVILEGED_XATTR_PREFIX)
|| (l->stat_override_mode == STAT_OVERRIDE_CONTAINERS && || (l->stat_override_mode == STAT_OVERRIDE_CONTAINERS && has_prefix (name, XATTR_SECURITY_PREFIX)));
has_prefix (name, XATTR_SECURITY_PREFIX)));
} }
static bool encoded_xattr_name (const struct ovl_layer *l, const char *name) static bool
encoded_xattr_name (const struct ovl_layer *l, const char *name)
{ {
return has_prefix (name, XATTR_CONTAINERS_OVERRIDE_PREFIX) && return has_prefix (name, XATTR_CONTAINERS_OVERRIDE_PREFIX) && ! can_access_xattr (l, name + sizeof (XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1);
! can_access_xattr (l, name + sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1);
} }
static const char *decode_xattr_name (const struct ovl_layer *l, const char *name) static const char *
decode_xattr_name (const struct ovl_layer *l, const char *name)
{ {
if (encoded_xattr_name (l, name)) if (encoded_xattr_name (l, name))
return name + sizeof (XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1; return name + sizeof (XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1;
@ -552,14 +535,14 @@ static const char *decode_xattr_name (const struct ovl_layer *l, const char *nam
return NULL; return NULL;
} }
static const char *encode_xattr_name (const struct ovl_layer *l, char *buf, static const char *
encode_xattr_name (const struct ovl_layer *l, char *buf,
const char *name) const char *name)
{ {
if (can_access_xattr (l, name)) if (can_access_xattr (l, name))
return name; return name;
if (l->stat_override_mode != STAT_OVERRIDE_CONTAINERS || if (l->stat_override_mode != STAT_OVERRIDE_CONTAINERS || strlen (name) > XATTR_NAME_MAX + 1 - sizeof (XATTR_CONTAINERS_OVERRIDE_PREFIX))
strlen(name) > XATTR_NAME_MAX + 1 - sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX))
return NULL; return NULL;
strcpy (buf, XATTR_CONTAINERS_OVERRIDE_PREFIX); strcpy (buf, XATTR_CONTAINERS_OVERRIDE_PREFIX);
@ -1035,7 +1018,7 @@ node_free (void *p)
if (n->parent) if (n->parent)
{ {
if (n->parent->children && hash_lookup (n->parent->children, n) == n) if (n->parent->children && hash_lookup (n->parent->children, n) == n)
hash_delete (n->parent->children, n); hash_remove (n->parent->children, n);
n->parent->loaded = 0; n->parent->loaded = 0;
n->parent = NULL; n->parent = NULL;
} }
@ -1095,7 +1078,7 @@ drop_node_from_ino (Hash_table *inodes, struct ovl_node *node)
if (ino->lookups == 0) if (ino->lookups == 0)
{ {
hash_delete (inodes, ino); hash_remove (inodes, ino);
inode_free (ino); inode_free (ino);
return; return;
} }
@ -1325,7 +1308,7 @@ do_forget (struct ovl_data *lo, fuse_ino_t ino, uint64_t nlookup)
i->lookups -= nlookup; i->lookups -= nlookup;
if (i->lookups <= 0) if (i->lookups <= 0)
{ {
hash_delete (lo->inodes, i); hash_remove (lo->inodes, i);
inode_free (i); inode_free (i);
} }
return true; return true;
@ -1543,6 +1526,7 @@ make_ovl_node (struct ovl_data *lo, const char *path, struct ovl_layer *layer, c
struct ovl_layer *it; struct ovl_layer *it;
cleanup_free char *npath = NULL; cleanup_free char *npath = NULL;
char whiteout_path[PATH_MAX]; char whiteout_path[PATH_MAX];
bool stop_lookup = false;
npath = strdup (ret->path); npath = strdup (ret->path);
if (npath == NULL) if (npath == NULL)
@ -1553,13 +1537,16 @@ make_ovl_node (struct ovl_data *lo, const char *path, struct ovl_layer *layer, c
else else
strconcat3 (whiteout_path, PATH_MAX, "/.wh.", name, NULL); strconcat3 (whiteout_path, PATH_MAX, "/.wh.", name, NULL);
for (it = layer; it; it = it->next) for (it = layer; it && ! stop_lookup; it = it->next)
{ {
ssize_t s; ssize_t s;
cleanup_free char *val = NULL; cleanup_free char *val = NULL;
cleanup_free char *origin = NULL; cleanup_free char *origin = NULL;
cleanup_close int fd = -1; cleanup_close int fd = -1;
if (parent && parent->last_layer == it)
stop_lookup = true;
if (dir_p) if (dir_p)
{ {
int r; int r;
@ -1676,12 +1663,12 @@ insert_node (struct ovl_node *parent, struct ovl_node *item, bool replace)
if (prev_parent) if (prev_parent)
{ {
if (hash_lookup (prev_parent->children, item) == item) if (hash_lookup (prev_parent->children, item) == item)
hash_delete (prev_parent->children, item); hash_remove (prev_parent->children, item);
} }
if (replace) if (replace)
{ {
old = hash_delete (parent->children, item); old = hash_remove (parent->children, item);
if (old) if (old)
node_free (old); node_free (old);
} }
@ -1804,7 +1791,7 @@ load_dir (struct ovl_data *lo, struct ovl_node *n, struct ovl_layer *layer, char
continue; continue;
else else
{ {
hash_delete (n->children, child); hash_remove (n->children, child);
node_free (child); node_free (child);
child = NULL; child = NULL;
} }
@ -1941,6 +1928,58 @@ cleanup_layerp (struct ovl_layer **p)
#define cleanup_layer __attribute__ ((cleanup (cleanup_layerp))) #define cleanup_layer __attribute__ ((cleanup (cleanup_layerp)))
static void
unescape (char *input)
{
char *dest = input;
if (input == NULL)
return;
for (; *input; input++)
{
if (*input == '\\')
continue;
*dest++ = *input;
}
*dest = '\0';
}
static char *
get_next_path (char *it, char **saveptr)
{
char *ret;
if (*saveptr == NULL)
*saveptr = it;
ret = *saveptr;
if (*ret == '\0')
return NULL;
while (1)
{
if (**saveptr == '\0')
break;
if (**saveptr == ':')
{
**saveptr = '\0';
(*saveptr)++;
break;
}
else if (**saveptr == '\\')
{
memmove (*saveptr, *saveptr + 1, strlen (*saveptr) + 1);
}
(*saveptr)++;
}
return ret;
}
static struct ovl_layer * static struct ovl_layer *
read_dirs (struct ovl_data *lo, char *path, bool low, struct ovl_layer *layers) read_dirs (struct ovl_data *lo, char *path, bool low, struct ovl_layer *layers)
{ {
@ -1959,7 +1998,7 @@ read_dirs (struct ovl_data *lo, char *path, bool low, struct ovl_layer *layers)
while (last && last->next) while (last && last->next)
last = last->next; last = last->next;
for (it = strtok_r (buf, ":", &saveptr); it; it = strtok_r (NULL, ":", &saveptr)) for (it = get_next_path (buf, &saveptr); it; it = get_next_path (NULL, &saveptr))
{ {
char *name, *data; char *name, *data;
char *it_path = it; char *it_path = it;
@ -2645,8 +2684,7 @@ filter_xattrs_list (struct ovl_layer *l, char *buf, ssize_t len)
{ {
char *next = it; char *next = it;
next += encoded_xattr_name (l, it) ? next += encoded_xattr_name (l, it) ? sizeof (XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1 : it_len;
sizeof(XATTR_CONTAINERS_OVERRIDE_PREFIX) - 1 : it_len;
memmove (it, next, buf + len - next); memmove (it, next, buf + len - next);
len -= it_len; len -= it_len;
@ -3462,7 +3500,7 @@ do_rm (fuse_req_t req, fuse_ino_t parent, const char *name, bool dirp)
node_set_name (&key, (char *) name); node_set_name (&key, (char *) name);
rm = hash_delete (pnode->children, &key); rm = hash_remove (pnode->children, &key);
fuse_lowlevel_notify_inval_inode (lo->se, node_to_inode (node), -1, 0); fuse_lowlevel_notify_inval_inode (lo->se, node_to_inode (node), -1, 0);
if (rm) if (rm)
{ {
@ -4589,8 +4627,8 @@ ovl_rename_exchange (fuse_req_t req, fuse_ino_t parent, const char *name,
if (ret < 0) if (ret < 0)
goto error; goto error;
rm1 = hash_delete (destpnode->children, destnode); rm1 = hash_remove (destpnode->children, destnode);
rm2 = hash_delete (pnode->children, node); rm2 = hash_remove (pnode->children, node);
tmp = node->path; tmp = node->path;
node->path = destnode->path; node->path = destnode->path;
@ -4820,7 +4858,7 @@ ovl_rename_direct (fuse_req_t req, fuse_ino_t parent, const char *name,
if (delete_whiteout (lo, destfd, NULL, newname) < 0) if (delete_whiteout (lo, destfd, NULL, newname) < 0)
goto error; goto error;
hash_delete (pnode->children, node); hash_remove (pnode->children, node);
free (node->name); free (node->name);
node_set_name (node, strdup (newname)); node_set_name (node, strdup (newname));
@ -4948,42 +4986,6 @@ ovl_readlink (fuse_req_t req, fuse_ino_t ino)
fuse_reply_readlink (req, buf); fuse_reply_readlink (req, buf);
} }
static int
hide_all (struct ovl_data *lo, struct ovl_node *node)
{
struct ovl_node **nodes;
size_t i, nodes_size;
node = reload_dir (lo, node);
if (node == NULL)
return -1;
nodes_size = hash_get_n_entries (node->children) + 2;
nodes = malloc (sizeof (struct ovl_node *) * nodes_size);
if (nodes == NULL)
return -1;
nodes_size = hash_get_entries (node->children, (void **) nodes, nodes_size);
for (i = 0; i < nodes_size; i++)
{
struct ovl_node *it;
int ret;
it = nodes[i];
ret = create_whiteout (lo, node, it->name, false, true);
node_free (it);
if (ret < 0)
{
free (nodes);
return ret;
}
}
free (nodes);
return 0;
}
static void static void
ovl_mknod (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) ovl_mknod (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev)
{ {
@ -5205,21 +5207,9 @@ ovl_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
return; return;
} }
if (parent_upperdir_only) node->last_layer = get_upper_layer (lo);
{
node->last_layer = pnode->last_layer;
if (get_timeout (lo) > 0) if (get_timeout (lo) > 0)
node->loaded = 1; node->loaded = 1;
}
else
{
ret = hide_all (lo, node);
if (ret < 0)
{
fuse_reply_err (req, errno);
return;
}
}
memset (&e, 0, sizeof (e)); memset (&e, 0, sizeof (e));
@ -5640,7 +5630,7 @@ get_new_args (int *argc, char **argv)
newargv[0] = argv[0]; newargv[0] = argv[0];
if (geteuid () == 0) if (geteuid () == 0)
newargv[1] = "-odefault_permissions,allow_other,suid,noatime,lazytime"; newargv[1] = "-odefault_permissions,allow_other,suid,noatime";
else else
newargv[1] = "-odefault_permissions,noatime"; newargv[1] = "-odefault_permissions,noatime";
for (i = 1; i < *argc; i++) for (i = 1; i < *argc; i++)
@ -5773,18 +5763,7 @@ main (int argc, char *argv[])
if (lo.mountpoint == NULL) if (lo.mountpoint == NULL)
error (EXIT_FAILURE, 0, "no mountpoint specified"); error (EXIT_FAILURE, 0, "no mountpoint specified");
if (lo.upperdir != NULL) unescape (lo.workdir);
{
cleanup_free char *full_path = NULL;
full_path = realpath (lo.upperdir, NULL);
if (full_path == NULL)
error (EXIT_FAILURE, errno, "cannot retrieve path for %s", lo.upperdir);
lo.upperdir = strdup (full_path);
if (lo.upperdir == NULL)
error (EXIT_FAILURE, errno, "cannot allocate memory");
}
set_limits (); set_limits ();
check_can_mknod (&lo); check_can_mknod (&lo);
@ -5908,7 +5887,7 @@ main (int argc, char *argv[])
if (! found) if (! found)
{ {
/* If the mode is missing, set a standard value. */ /* If the mode is missing, set a standard value. */
ret = write_permission_xattr (&lo, get_upper_layer (&lo)->fd, lo.upperdir, 0, 0, 0555); ret = write_permission_xattr (&lo, get_upper_layer (&lo)->fd, get_upper_layer (&lo)->path, 0, 0, 0555);
if (ret < 0) if (ret < 0)
error (EXIT_FAILURE, errno, "write xattr `%s` to upperdir", name); error (EXIT_FAILURE, errno, "write xattr `%s` to upperdir", name);
} }

View File

@ -2,17 +2,17 @@
set -xeuo pipefail set -xeuo pipefail
mkdir lower upper workdir merged mkdir lower:1 upper:2 workdir:3 merged
fuse-overlayfs -o sync=0,lowerdir=lower,upperdir=upper,workdir=workdir,suid,dev merged fuse-overlayfs -o 'sync=0,lowerdir=lower\\:1,upperdir=upper\\:2,workdir=workdir\\:3,suid,dev' merged
docker run --rm -v $(pwd)/merged:/merged fedora dnf --installroot /merged --releasever 30 install -y glibc-common gedit docker run --rm -v $(pwd)/merged:/merged fedora dnf --use-host-config --installroot /merged --releasever 41 install -y glibc-common gedit
umount merged umount merged
# Make sure workdir is empty, and move the upper layer down # Make sure workdir is empty, and move the upper layer down
rm -rf workdir lower rm -rf lower:1 workdir:3
mv upper lower mv upper:2 lower
mkdir upper workdir mkdir upper workdir
gcc -static -o suid-test $(dirname $0)/suid-test.c gcc -static -o suid-test $(dirname $0)/suid-test.c
@ -30,7 +30,7 @@ stat -c %A upper/suid | grep s
stat -c %a upper/nosuid | grep -v s stat -c %a upper/nosuid | grep -v s
# Install some big packages # Install some big packages
docker run --rm -v $(pwd)/merged:/merged fedora dnf --installroot /merged --releasever 30 install -y emacs texlive docker run --rm -v $(pwd)/merged:/merged fedora dnf --use-host-config --installroot /merged --releasever 41 install -y emacs texlive
docker run --rm -v $(pwd)/merged:/merged fedora sh -c 'rm /merged/usr/share/glib-2.0/schemas/gschemas.compiled; glib-compile-schemas /merged/usr/share/glib-2.0/schemas/' docker run --rm -v $(pwd)/merged:/merged fedora sh -c 'rm /merged/usr/share/glib-2.0/schemas/gschemas.compiled; glib-compile-schemas /merged/usr/share/glib-2.0/schemas/'
@ -48,7 +48,7 @@ mkdir upper workdir lower
# fast_ino_check # fast_ino_check
fuse-overlayfs -o fast_ino_check=1,sync=0,lowerdir=lower,upperdir=upper,workdir=workdir,suid,dev merged fuse-overlayfs -o fast_ino_check=1,sync=0,lowerdir=lower,upperdir=upper,workdir=workdir,suid,dev merged
docker run --rm -v $(pwd)/merged:/merged fedora dnf --installroot /merged --releasever 30 install -y glibc-common gedit docker run --rm -v $(pwd)/merged:/merged fedora dnf --use-host-config --installroot /merged --releasever 41 install -y glibc-common gedit
mkdir merged/a-directory mkdir merged/a-directory
@ -258,3 +258,18 @@ fuse-overlayfs -o lowerdir=lower,upperdir=upper,workdir=workdir merged
stat merged/foo stat merged/foo
umount merged umount merged
# https://github.com/containers/fuse-overlayfs/issues/444
rm -rf lower upper workdir merged
mkdir lower upper workdir merged
mkdir -p lower/base/test/test1
touch lower/base/test/test1/test1-file
fuse-overlayfs -o lowerdir=lower,upperdir=upper,workdir=workdir merged
mv merged/base/test/test1 merged/base/test/tmp
cp -r merged/base/test/tmp merged/base/test/test1
umount merged

84
utils.c
View File

@ -224,16 +224,38 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd,
return *fd; return *fd;
} }
int
read_device (const char *s, dev_t *dev)
{
unsigned int major, minor;
int ret;
while (*s == '-')
s++;
ret = sscanf (s, "%u-%u", &major, &minor);
if (ret != 2)
{
errno = EINVAL;
return -1;
}
*dev = makedev (major, minor);
return 0;
}
int int
override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st) override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st)
{ {
int ret; int ret;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
mode_t mode; mode_t mode = 0;
char buf[64]; char buf[64];
cleanup_close int cleanup_fd = -1; cleanup_close int cleanup_fd = -1;
const char *xattr_name; const char *xattr_name;
cleanup_free char *type = NULL;
switch (st->st_mode & S_IFMT) switch (st->st_mode & S_IFMT)
{ {
@ -296,8 +318,45 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
buf[ret] = '\0'; buf[ret] = '\0';
ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode); ret = sscanf (buf, "%d:%d:%o:%ms", &uid, &gid, &mode, &type);
if (ret != 3) if (ret == 4)
{
if (has_prefix (type, "dir"))
mode |= S_IFDIR;
else if (has_prefix (type, "file"))
mode |= S_IFREG;
else if (has_prefix (type, "symlink"))
mode |= S_IFLNK;
else if (has_prefix (type, "pipe"))
mode |= S_IFIFO;
else if (has_prefix (type, "socket"))
mode |= S_IFSOCK;
else if (has_prefix (type, "block"))
{
mode |= S_IFBLK;
ret = read_device (type + strlen ("block"), &st->st_rdev);
if (ret < 0)
return ret;
}
else if (has_prefix (type, "char"))
{
mode |= S_IFCHR;
ret = read_device (type + strlen ("char"), &st->st_rdev);
if (ret < 0)
return ret;
}
else
{
errno = EINVAL;
return -1;
}
}
else if (ret == 3)
{
/* If a type is not specified, keep the original one. */
mode |= (st->st_mode & S_IFMT);
}
else
{ {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
@ -305,7 +364,24 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
st->st_uid = uid; st->st_uid = uid;
st->st_gid = gid; st->st_gid = gid;
st->st_mode = (st->st_mode & S_IFMT) | mode; st->st_mode = mode;
return 0; return 0;
} }
bool
has_prefix (const char *str, const char *pref)
{
while (1)
{
if (*pref == '\0')
return true;
if (*str == '\0')
return false;
if (*pref != *str)
return false;
str++;
pref++;
}
return false;
}

View File

@ -62,4 +62,6 @@ int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode);
int override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st); int override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st);
bool has_prefix (const char *str, const char *pref);
#endif #endif