Compare commits

..

No commits in common. "main" and "v1.13" have entirely different histories.
main ... v1.13

27 changed files with 930 additions and 2725 deletions

View File

@ -1,111 +0,0 @@
---
BasedOnStyle: GNU
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: All
AlwaysBreakAfterReturnType: AllDefinitions
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: true
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
ContinuationIndentWidth: 4
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentGotoLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: Always
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
TabWidth: 8
UseCRLF: false
UseTab: Never
...

View File

@ -1,77 +1,98 @@
name: Release name: Release
on: on:
push: push:
pull_request: tags:
- 'test-cross-*'
- 'v*'
jobs: jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: - uses: docker/setup-buildx-action@v1
# Reproduce the exact commit hash value
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
- uses: docker/setup-buildx-action@v2
- name: Cache var-cache-apt
uses: actions/cache@v3
with:
path: var-cache-apt
key: var-cache-apt-${{ hashFiles('Containerfile.cross') }}
- name: Cache var-lib-apt
uses: actions/cache@v3
with:
path: var-lib-apt
key: var-lib-apt-${{ hashFiles('Containerfile.cross') }}
- name: inject var-cache-apt into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2
with:
cache-source: var-cache-apt
cache-target: /var/cache/apt
- name: inject var-lib-apt into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2
with:
cache-source: var-lib-apt
cache-target: /var/lib/apt
- name: "Build binaries from Containerfile.cross" - name: "Build binaries from Containerfile.cross"
run: docker buildx build -o /tmp/fuse-overlayfs-builds --build-arg SOURCE_DATE_EPOCH --platform=amd64,arm64,arm,s390x,ppc64le,riscv64 -f Containerfile.cross . run: docker buildx build -o out --platform=amd64,arm64,arm,s390x,ppc64le,riscv64 -f Containerfile.cross .
- name: "Create /tmp/artifact" - name: "Create out/artifact"
run: | run: |
mkdir -p /tmp/artifact mkdir -p out/artifact
mv /tmp/fuse-overlayfs-builds/linux_amd64/fuse-overlayfs /tmp/artifact/fuse-overlayfs-x86_64 mv out/linux_amd64/fuse-overlayfs out/artifact/fuse-overlayfs-x86_64
mv /tmp/fuse-overlayfs-builds/linux_arm64/fuse-overlayfs /tmp/artifact/fuse-overlayfs-aarch64 mv out/linux_arm64/fuse-overlayfs out/artifact/fuse-overlayfs-aarch64
mv /tmp/fuse-overlayfs-builds/linux_arm_v7/fuse-overlayfs /tmp/artifact/fuse-overlayfs-armv7l mv out/linux_arm_v7/fuse-overlayfs out/artifact/fuse-overlayfs-armv7l
mv /tmp/fuse-overlayfs-builds/linux_s390x/fuse-overlayfs /tmp/artifact/fuse-overlayfs-s390x mv out/linux_s390x/fuse-overlayfs out/artifact/fuse-overlayfs-s390x
mv /tmp/fuse-overlayfs-builds/linux_ppc64le/fuse-overlayfs /tmp/artifact/fuse-overlayfs-ppc64le mv out/linux_ppc64le/fuse-overlayfs out/artifact/fuse-overlayfs-ppc64le
mv /tmp/fuse-overlayfs-builds/linux_riscv64/fuse-overlayfs /tmp/artifact/fuse-overlayfs-riscv64 mv out/linux_riscv64/fuse-overlayfs out/artifact/fuse-overlayfs-riscv64
echo "${SOURCE_DATE_EPOCH}" >/tmp/artifact/SOURCE_DATE_EPOCH
- name: "SHA256SUMS" - name: "SHA256SUMS"
run: (cd /tmp/artifact; sha256sum *) | tee /tmp/SHA256SUMS run: (cd out/artifact; sha256sum *) | tee out/SHA256SUMS
- name: "The sha256sum of the SHA256SUMS file"
run: sha256sum /tmp/SHA256SUMS
- name: "Prepare the release note"
run: |
cat << EOF | tee /tmp/release-note.txt
#### About the binaries
The binaries were built automatically on GitHub Actions.
The build log is available for 90 days: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
The binaries should be reproducible with the following command:
\`\`\`
docker buildx build \
-o /tmp/fuse-overlayfs-builds \
--build-arg SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \
--build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 \
--platform=amd64,arm64,arm,s390x,ppc64le,riscv64 \
-f Containerfile.cross \
"https://github.com/${{ github.repository }}.git#${tag}"
\`\`\`
EOF
- name: "Create release" - name: "Create release"
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') uses: actions/create-release@v1
id: create_release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | with:
tag="${GITHUB_REF##*/}" tag_name: ${{ github.ref }}
gh release create -F /tmp/release-note.txt --draft --title "${tag}" "${tag}" /tmp/artifact/* /tmp/SHA256SUMS release_name: ${{ github.ref }}
draft: true
- name: "Upload fuse-overlayfs-x86_64"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-x86_64
asset_name: fuse-overlayfs-x86_64
asset_content_type: application/octet-stream
- name: "Upload fuse-overlayfs-aarch64"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-aarch64
asset_name: fuse-overlayfs-aarch64
asset_content_type: application/octet-stream
- name: "Upload fuse-overlayfs-armv7l"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-armv7l
asset_name: fuse-overlayfs-armv7l
asset_content_type: application/octet-stream
- name: "Upload fuse-overlayfs-s390x"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-s390x
asset_name: fuse-overlayfs-s390x
asset_content_type: application/octet-stream
- name: "Upload fuse-overlayfs-ppc64le"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-ppc64le
asset_name: fuse-overlayfs-ppc64le
asset_content_type: application/octet-stream
- name: "Upload fuse-overlayfs-riscv64"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/artifact/fuse-overlayfs-riscv64
asset_name: fuse-overlayfs-riscv64
asset_content_type: application/octet-stream
- name: "Upload SHA256SUMS"
uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: out/SHA256SUMS
asset_name: SHA256SUMS
asset_content_type: text/plain

View File

@ -4,23 +4,23 @@ on: [push, pull_request]
jobs: jobs:
build_job: build_job:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
name: Build on ${{ matrix.arch }} name: Build on ${{ matrix.arch }}
strategy: strategy:
matrix: matrix:
include: include:
- arch: armv7 - arch: armv7
distro: ubuntu_latest distro: ubuntu20.04
- arch: aarch64 - arch: aarch64
distro: ubuntu_latest distro: ubuntu20.04
- arch: s390x - arch: s390x
distro: ubuntu_latest distro: ubuntu20.04
- arch: ppc64le - arch: ppc64le
distro: ubuntu_latest distro: ubuntu20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2.1.0
- uses: uraimo/run-on-arch-action@v3.0.1 - uses: uraimo/run-on-arch-action@v2.0.5
name: Build name: Build
id: build id: build
with: with:
@ -34,7 +34,7 @@ jobs:
install: | install: |
apt-get update -q -y apt-get update -q -y
apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel libfuse3-dev bats apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3.8 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel libfuse3-dev bats
run: | run: |
./autogen.sh ./autogen.sh
@ -42,14 +42,14 @@ jobs:
make -j $(nproc) make -j $(nproc)
- name: Archive build artifacts - name: Archive build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v3
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-latest runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -61,12 +61,12 @@ 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@v4 uses: actions/checkout@v2
- name: install dependencies - name: install dependencies
run: | run: |
sudo apt-get update -q -y sudo apt-get update -q -y
sudo apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel wget libfuse3-dev bats sudo apt-get install -q -y attr automake autotools-dev git make gcc pkg-config xz-utils python3.8 g++ python3-setuptools libdevmapper-dev btrfs-progs libbtrfs-dev go-md2man parallel wget libfuse3-dev bats
sudo mkdir -p /lower /upper /mnt $GOPATH/src/github.com/containers sudo mkdir -p /lower /upper /mnt $GOPATH/src/github.com/containers
sudo sh -c "cd $GOPATH/src/github.com/containers; git clone --depth=1 https://github.com/containers/storage" sudo sh -c "cd $GOPATH/src/github.com/containers; git clone --depth=1 https://github.com/containers/storage"
@ -88,18 +88,15 @@ jobs:
sudo cp fuse-overlayfs /sbin sudo cp fuse-overlayfs /sbin
- name: Archive build artifacts - name: Archive build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v3
with: with:
name: fuse-overlayfs-x86_64-ubuntu-latest name: fuse-overlayfs-x86_64-ubuntu20.04
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)"

View File

@ -3,7 +3,7 @@ WORKDIR /build
RUN apk add git make gcc libc-dev musl-dev glib-static gettext eudev-dev \ RUN apk add git make gcc libc-dev musl-dev glib-static gettext eudev-dev \
linux-headers automake autoconf cmake meson ninja clang go-md2man linux-headers automake autoconf cmake meson ninja clang go-md2man
RUN git clone https://github.com/libfuse/libfuse -b fuse-3.16.2 && \ RUN git clone https://github.com/libfuse/libfuse && \
cd libfuse && \ cd libfuse && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \

View File

@ -1,38 +1,14 @@
# Usage: FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.1.0 AS xx
# docker buildx build \ FROM --platform=$BUILDPLATFORM ubuntu:22.04 AS fuse-overlayfs
# -o /tmp/fuse-overlayfs-builds \ RUN apt-get update && \
# --build-arg SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \
# --platform=amd64,arm64,arm,s390x,ppc64le,riscv64 \
# -f Containerfile.cross .
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.2.1 AS xx
FROM --platform=$BUILDPLATFORM ubuntu:jammy-20230804 AS fuse-overlayfs
ADD --chmod=0755 \
https://raw.githubusercontent.com/reproducible-containers/repro-sources-list.sh/v0.1.0/repro-sources-list.sh \
/usr/local/bin/repro-sources-list.sh
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
repro-sources-list.sh && \
apt-get update && \
apt-get install --no-install-recommends -y \ apt-get install --no-install-recommends -y \
git make automake autoconf pkgconf file go-md2man git make automake autoconf pkgconf file go-md2man
# Set SOURCE_DATE_EPOCH after running repro-sources-list.sh, for cache efficiency
ARG SOURCE_DATE_EPOCH
COPY . /fuse-overlayfs COPY . /fuse-overlayfs
WORKDIR /fuse-overlayfs WORKDIR /fuse-overlayfs
COPY --from=xx / / COPY --from=xx / /
ARG TARGETPLATFORM ARG TARGETPLATFORM
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
# xx-apt-get cannot be used, as it clobbers /etc/apt/sources.list created by repro-sources-list.sh RUN xx-apt-get install -y gcc libfuse3-dev
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
darch="$(xx-info debian-arch)" && \
dpkg --add-architecture ${darch} && \
apt-get update && \
gcc="gcc" && \
if xx-info is-cross; then gcc="gcc-$(xx-info triple)"; fi; \
apt-get install -y "${gcc}" "libfuse3-dev:${darch}"
RUN ./autogen.sh && \ RUN ./autogen.sh && \
LIBS="-ldl" LDFLAGS="-static" ./configure --host=$(xx-info) && \ LIBS="-ldl" LDFLAGS="-static" ./configure --host=$(xx-info) && \
make && mkdir /out && cp fuse-overlayfs /out && \ make && mkdir /out && cp fuse-overlayfs /out && \

View File

@ -3,7 +3,7 @@ WORKDIR /build
RUN dnf update -y && \ RUN dnf update -y && \
dnf install -y git make automake autoconf gcc glibc-static meson ninja-build clang dnf install -y git make automake autoconf gcc glibc-static meson ninja-build clang
RUN git clone https://github.com/libfuse/libfuse -b fuse-3.16.2 && \ RUN git clone https://github.com/libfuse/libfuse && \
cd libfuse && \ cd libfuse && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \

View File

@ -36,6 +36,3 @@ srpm: dist-gzip fuse-overlayfs.spec
echo $(VERSION) echo $(VERSION)
$(MAKE) -C $(WD) dist-xz $(MAKE) -C $(WD) dist-xz
rpmbuild -bs --define "_sourcedir $(WD)" --define "_specdir $(WD)" --define "_builddir $(WD)" --define "_srcrpmdir $(WD)" --define "_rpmdir $(WD)" --define "_buildrootdir $(WD)/.build" fuse-overlayfs.spec rpmbuild -bs --define "_sourcedir $(WD)" --define "_specdir $(WD)" --define "_builddir $(WD)" --define "_srcrpmdir $(WD)" --define "_rpmdir $(WD)" --define "_buildrootdir $(WD)/.build" fuse-overlayfs.spec
clang-format:
git ls-files | grep -E "\\.[hc]$$" | grep -v "^lib/" | xargs clang-format -style=file -i

19
NEWS
View File

@ -1,22 +1,3 @@
* 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
- isolate security xattrs for STAT_OVERRIDE_CONTAINERS. Prefix all
security xattrs with XATTR_CONTAINERS_OVERRIDE_PREFIX.
- prefer user.containers.override_stat over user.fuseoverlayfs.
- do not force -1 for owner overriding extended attributes. Otherwise
the value is written to the override extended attribute.
- fix file owner retrieval for chmod.
- honor umask with xattr_permissions.
- honor mode for devices with xattr_permissions.
- propagate extended attributes permissions with copyup.
* fuse-overlayfs-1.13 * fuse-overlayfs-1.13
- fix a performance issue when dealing with big directories. - fix a performance issue when dealing with big directories.

View File

@ -1,5 +1,5 @@
AC_PREREQ([2.69]) AC_PREREQ([2.69])
AC_INIT([fuse-overlayfs], [1.16-dev], [giuseppe@scrivano.org]) AC_INIT([fuse-overlayfs], [1.13-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

@ -6,12 +6,12 @@ import stat
import errno import errno
XATTR_OVERRIDE_STAT_PRIVILEGED = "security.fuseoverlayfs.override_stat" XATTR_OVERRIDE_STAT_PRIVILEGED = "security.fuseoverlayfs.override_stat"
XATTR_OVERRIDE_CONTAINERS_STAT = "user.fuseoverlayfs.override_stat" XATTR_OVERRIDE_STAT = "user.fuseoverlayfs.override_stat"
if os.geteuid() == 0: if os.geteuid() == 0:
xattr_name = XATTR_OVERRIDE_STAT_PRIVILEGED xattr_name = XATTR_OVERRIDE_STAT_PRIVILEGED
else: else:
xattr_name = XATTR_OVERRIDE_CONTAINERS_STAT xattr_name = XATTR_OVERRIDE_STAT
cwd_fd = os.open(".", os.O_PATH) cwd_fd = os.open(".", os.O_PATH)
@ -23,7 +23,7 @@ def fix_path(path):
os.setxattr(path, xattr_name, str.encode(content), flags=os.XATTR_CREATE, follow_symlinks=False) os.setxattr(path, xattr_name, str.encode(content), flags=os.XATTR_CREATE, follow_symlinks=False)
except Exception as e: except Exception as e:
if e.errno == errno.EEXIST: if e.errno == errno.EEXIST:
print("attr %s already present for %s: %s" % (xattr_name, path, e.errno)) print("attr %s already present for %s: %s" % (XATTR_OVERRIDE_STAT, path, e.errno))
return return
raise e raise e

View File

@ -76,7 +76,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask,
#ifdef HAVE_STATX #ifdef HAVE_STATX
struct statx stx; struct statx stx;
ret = statx (fd, "", AT_STATX_DONT_SYNC | AT_EMPTY_PATH, mask, &stx); ret = statx (fd, "", AT_STATX_DONT_SYNC|AT_EMPTY_PATH, mask, &stx);
if (ret < 0 && (errno == ENOSYS || errno == EINVAL)) if (ret < 0 && (errno == ENOSYS || errno == EINVAL))
goto fallback; goto fallback;
if (ret == 0) if (ret == 0)
@ -88,7 +88,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask,
return ret; return ret;
#endif #endif
fallback: fallback:
ret = fstat (fd, st); ret = fstat (fd, st);
if (ret != 0) if (ret != 0)
return ret; return ret;
@ -103,7 +103,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags
#ifdef HAVE_STATX #ifdef HAVE_STATX
struct statx stx; struct statx stx;
ret = statx (l->fd, path, AT_STATX_DONT_SYNC | flags, mask, &stx); ret = statx (l->fd, path, AT_STATX_DONT_SYNC|flags, mask, &stx);
if (ret < 0 && (errno == ENOSYS || errno == EINVAL)) if (ret < 0 && (errno == ENOSYS || errno == EINVAL))
goto fallback; goto fallback;
if (ret == 0) if (ret == 0)
@ -114,7 +114,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags
return ret; return ret;
#endif #endif
fallback: fallback:
ret = fstatat (l->fd, path, st, flags); ret = fstatat (l->fd, path, st, flags);
if (ret != 0) if (ret != 0)
return ret; return ret;
@ -186,10 +186,10 @@ direct_load_data_source (struct ovl_layer *l, const char *opaque, const char *pa
if (fgetxattr (l->fd, XATTR_PRIVILEGED_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0) if (fgetxattr (l->fd, XATTR_PRIVILEGED_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_PRIVILEGED; l->stat_override_mode = STAT_OVERRIDE_PRIVILEGED;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_CONTAINERS;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0) else if (fgetxattr (l->fd, XATTR_OVERRIDE_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_USER; l->stat_override_mode = STAT_OVERRIDE_USER;
else if (fgetxattr (l->fd, XATTR_OVERRIDE_CONTAINERS_STAT, tmp, sizeof (tmp)) >= 0)
l->stat_override_mode = STAT_OVERRIDE_CONTAINERS;
return 0; return 0;
} }
@ -212,22 +212,24 @@ direct_support_acls (struct ovl_layer *l)
char value[32]; char value[32];
return fgetxattr (l->fd, ACL_XATTR, value, sizeof (value)) >= 0 return fgetxattr (l->fd, ACL_XATTR, value, sizeof (value)) >= 0
|| errno != ENOTSUP; || errno != ENOTSUP;
} }
struct data_source direct_access_ds = {
.num_of_layers = direct_num_of_layers, struct data_source direct_access_ds =
.load_data_source = direct_load_data_source, {
.cleanup = direct_cleanup, .num_of_layers = direct_num_of_layers,
.file_exists = direct_file_exists, .load_data_source = direct_load_data_source,
.statat = direct_statat, .cleanup = direct_cleanup,
.fstat = direct_fstat, .file_exists = direct_file_exists,
.opendir = direct_opendir, .statat = direct_statat,
.readdir = direct_readdir, .fstat = direct_fstat,
.closedir = direct_closedir, .opendir = direct_opendir,
.openat = direct_openat, .readdir = direct_readdir,
.getxattr = direct_getxattr, .closedir = direct_closedir,
.listxattr = direct_listxattr, .openat = direct_openat,
.readlinkat = direct_readlinkat, .getxattr = direct_getxattr,
.support_acls = direct_support_acls, .listxattr = direct_listxattr,
}; .readlinkat = direct_readlinkat,
.support_acls = direct_support_acls,
};

View File

@ -16,15 +16,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef FUSE_OVERLAYFS_H #ifndef FUSE_OVERLAYFS_H
#define FUSE_OVERLAYFS_H # define FUSE_OVERLAYFS_H
#define _GNU_SOURCE # define _GNU_SOURCE
#include <sys/stat.h> # include <sys/stat.h>
#include <plugin-manager.h> # include <plugin-manager.h>
#include <stdbool.h> # include <stdbool.h>
#include <sys/types.h> # include <sys/types.h>
#define ACL_XATTR "system.posix_acl_default" # define ACL_XATTR "system.posix_acl_default"
typedef struct hash_table Hash_table; typedef struct hash_table Hash_table;
@ -143,39 +143,39 @@ struct ovl_layer
struct data_source struct data_source
{ {
int (*num_of_layers) (const char *opaque, const char *path); int (*num_of_layers) (const char *opaque, const char *path);
int (*load_data_source) (struct ovl_layer *l, const char *opaque, const char *path, int n_layer); int (*load_data_source)(struct ovl_layer *l, const char *opaque, const char *path, int n_layer);
int (*cleanup) (struct ovl_layer *l); int (*cleanup)(struct ovl_layer *l);
int (*file_exists) (struct ovl_layer *l, const char *pathname); int (*file_exists)(struct ovl_layer *l, const char *pathname);
int (*statat) (struct ovl_layer *l, const char *path, struct stat *st, int flags, unsigned int mask); int (*statat)(struct ovl_layer *l, const char *path, struct stat *st, int flags, unsigned int mask);
int (*fstat) (struct ovl_layer *l, int fd, const char *path, unsigned int mask, struct stat *st); int (*fstat)(struct ovl_layer *l, int fd, const char *path, unsigned int mask, struct stat *st);
void *(*opendir) (struct ovl_layer *l, const char *path); void *(*opendir)(struct ovl_layer *l, const char *path);
struct dirent *(*readdir) (void *dirp); struct dirent *(*readdir)(void *dirp);
int (*closedir) (void *dirp); int (*closedir)(void *dirp);
int (*openat) (struct ovl_layer *l, const char *path, int flags, mode_t mode); int (*openat)(struct ovl_layer *l, const char *path, int flags, mode_t mode);
int (*listxattr) (struct ovl_layer *l, const char *path, char *buf, size_t size); int (*listxattr)(struct ovl_layer *l, const char *path, char *buf, size_t size);
int (*getxattr) (struct ovl_layer *l, const char *path, const char *name, char *buf, size_t size); int (*getxattr)(struct ovl_layer *l, const char *path, const char *name, char *buf, size_t size);
ssize_t (*readlinkat) (struct ovl_layer *l, const char *path, char *buf, size_t bufsiz); ssize_t (*readlinkat)(struct ovl_layer *l, const char *path, char *buf, size_t bufsiz);
bool (*support_acls) (struct ovl_layer *l); bool (*support_acls)(struct ovl_layer *l);
}; };
/* passthrough to the file system. */ /* passthrough to the file system. */
extern struct data_source direct_access_ds; extern struct data_source direct_access_ds;
#ifndef HAVE_STATX # ifndef HAVE_STATX
# define STATX_TYPE 0x00000001U /* Want/got stx_mode & S_IFMT */ # define STATX_TYPE 0x00000001U /* Want/got stx_mode & S_IFMT */
# define STATX_MODE 0x00000002U /* Want/got stx_mode & ~S_IFMT */ # define STATX_MODE 0x00000002U /* Want/got stx_mode & ~S_IFMT */
# define STATX_NLINK 0x00000004U /* Want/got stx_nlink */ # define STATX_NLINK 0x00000004U /* Want/got stx_nlink */
# define STATX_UID 0x00000008U /* Want/got stx_uid */ # define STATX_UID 0x00000008U /* Want/got stx_uid */
# define STATX_GID 0x00000010U /* Want/got stx_gid */ # define STATX_GID 0x00000010U /* Want/got stx_gid */
# define STATX_ATIME 0x00000020U /* Want/got stx_atime */ # define STATX_ATIME 0x00000020U /* Want/got stx_atime */
# define STATX_MTIME 0x00000040U /* Want/got stx_mtime */ # define STATX_MTIME 0x00000040U /* Want/got stx_mtime */
# define STATX_CTIME 0x00000080U /* Want/got stx_ctime */ # define STATX_CTIME 0x00000080U /* Want/got stx_ctime */
# define STATX_INO 0x00000100U /* Want/got stx_ino */ # define STATX_INO 0x00000100U /* Want/got stx_ino */
# define STATX_SIZE 0x00000200U /* Want/got stx_size */ # define STATX_SIZE 0x00000200U /* Want/got stx_size */
# define STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */ # define STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */
# define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */ # define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */
# define STATX_BTIME 0x00000800U /* Want/got stx_btime */ # define STATX_BTIME 0x00000800U /* Want/got stx_btime */
# define STATX_ALL 0x00000fffU /* All currently supported flags */ # define STATX_ALL 0x00000fffU /* All currently supported flags */
#endif # endif
#endif #endif

View File

@ -17,26 +17,24 @@
*/ */
#ifndef FUSE_OVERLAYFS_ERROR_H #ifndef FUSE_OVERLAYFS_ERROR_H
#define FUSE_OVERLAYFS_ERROR_H # define FUSE_OVERLAYFS_ERROR_H
#include <config.h> # include <config.h>
#ifdef HAVE_ERROR_H # ifdef HAVE_ERROR_H
# include <error.h> # include <error.h>
#else # else
# define error(status, errno, fmt, ...) \ # define error(status, errno, fmt, ...) do { \
do \ if (errno == 0) \
fprintf (stderr, "fuse-overlayfs: " fmt "\n", ##__VA_ARGS__); \
else \
{ \ { \
if (errno == 0) \ fprintf (stderr, "fuse-overlayfs: " fmt, ##__VA_ARGS__); \
fprintf (stderr, "fuse-overlayfs: " fmt "\n", ##__VA_ARGS__); \ fprintf (stderr, ": %s\n", strerror (errno)); \
else \ } \
{ \ if (status) \
fprintf (stderr, "fuse-overlayfs: " fmt, ##__VA_ARGS__); \ exit (status); \
fprintf (stderr, ": %s\n", strerror (errno)); \ } while(0)
} \ # endif
if (status) \
exit (status); \
} while (0)
#endif
#endif #endif

View File

@ -1,6 +1,6 @@
/* hash - hashing table processing. /* hash - hashing table processing.
Copyright (C) 1998-2004, 2006-2007, 2009-2025 Free Software Foundation, Inc. Copyright (C) 1998-2004, 2006-2007, 2009-2023 Free Software Foundation, Inc.
Written by Jim Meyering, 1992. Written by Jim Meyering, 1992.
@ -29,7 +29,6 @@
#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>
@ -500,17 +499,13 @@ 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)
goto nomem; return 0;
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 *)))
goto nomem; return 0;
return candidate; return candidate;
nomem:
errno = ENOMEM;
return 0;
} }
Hash_table * Hash_table *
@ -539,7 +534,6 @@ 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;
} }
@ -613,7 +607,6 @@ 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)
@ -656,8 +649,6 @@ 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. */
@ -771,8 +762,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 (setting errno) if the free entry list is occur. Return false if the free entry list is exhausted and an
exhausted and an allocation fails. */ 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)
@ -919,14 +910,12 @@ 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;
} }
@ -973,10 +962,7 @@ 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)
{ return -1;
errno = ENOMEM;
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-2025 Free Software Foundation, Copyright (C) 1998-1999, 2001, 2003, 2009-2023 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,40 +124,25 @@ 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. This function preserves errno, like 'free'. */ entry. */
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
@ -192,30 +177,23 @@ 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);
/* Like hash_initialize, but invokes xalloc_die instead of returning NULL. */ /* Same as hash_initialize, but invokes xalloc_die on memory exhaustion. */
/* 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 data of any Apply the user-specified function data_freer (if any) to the datas of any
affected entries. */ affected entries. */
extern void hash_clear (Hash_table *table); extern void hash_clear (Hash_table *table);
@ -229,26 +207,23 @@ 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 (setting errno) if the storage required for insertion Return NULL if the storage required for insertion cannot be allocated.
cannot be allocated. This implementation does not support This implementation does not support duplicate entries or insertion of
duplicate entries or insertion of NULL. */ 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 instead of returning NULL. */ /* Same as hash_insert, but invokes xalloc_die on memory exhaustion. */
/* 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 (setting errno) upon memory allocation failure. Return -1 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
@ -272,7 +247,6 @@ 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,84 +1,43 @@
# 00gnulib.m4 # 00gnulib.m4 serial 3
# serial 9 dnl Copyright (C) 2009-2019 Free Software Foundation, Inc.
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 the clang fix has dnl gnulib-provided .m4 files. It is needed until such time as we can
dnl been included in Autoconf. dnl assume Autoconf 2.64, with its improved AC_DEFUN_ONCE and
dnl m4_divert semantics.
# The following definitions arrange to use a compiler option # Until autoconf 2.63, handling of the diversion stack required m4_init
# -Werror=implicit-function-declaration in AC_CHECK_DECL, when the # to be called first; but this does not happen with aclocal. Wrapping
# compiler is clang. Without it, clang implicitly declares "known" # the entire execution in another layer of the diversion stack fixes this.
# library functions in C mode, but not in C++ mode, which would cause # Worse, prior to autoconf 2.62, m4_wrap depended on the underlying m4
# Gnulib to omit a declaration and thus later produce an error in C++ # for whether it was FIFO or LIFO; in order to properly balance with
# mode. As of clang 9.0, these "known" functions are identified through # m4_init, we need to undo our push just before anything wrapped within
# LIBBUILTIN invocations in the LLVM source file # the m4_init body. The way to ensure this is to wrap both sides of
# llvm/tools/clang/include/clang/Basic/Builtins.def. # m4_init with a one-shot macro that does the pop at the right time.
# It's not possible to AC_REQUIRE the extra tests from AC_CHECK_DECL, m4_ifndef([_m4_divert_diversion],
# because AC_CHECK_DECL, like other Autoconf built-ins, is not supposed [m4_divert_push([KILL])
# to AC_REQUIRE anything: some configure.ac files have their first m4_define([gl_divert_fixup], [m4_divert_pop()m4_define([$0])])
# AC_CHECK_DECL executed conditionally. Therefore append the extra tests m4_define([m4_init],
# to AC_PROG_CC. [gl_divert_fixup()]m4_defn([m4_init])[gl_divert_fixup()])])
AC_DEFUN([gl_COMPILER_CLANG],
[
dnl AC_REQUIRE([AC_PROG_CC]) # AC_DEFUN_ONCE([NAME], VALUE)
AC_CACHE_CHECK([whether the compiler is clang], # ----------------------------
[gl_cv_compiler_clang], # Define NAME to expand to VALUE on the first use (whether by direct
[dnl Use _AC_COMPILE_IFELSE instead of AC_EGREP_CPP, to avoid error # expansion, or by AC_REQUIRE), and to nothing on all subsequent uses.
dnl "circular dependency of AC_LANG_COMPILER(C)" if AC_PROG_CC has # Avoid bugs in AC_REQUIRE in Autoconf 2.63 and earlier. This
dnl not yet been invoked. # definition is slower than the version in Autoconf 2.64, because it
_AC_COMPILE_IFELSE( # can only use interfaces that existed since 2.59; but it achieves the
[AC_LANG_PROGRAM([[ # same effect. Quoting is necessary to avoid confusing Automake.
#ifdef __clang__ m4_version_prereq([2.63.263], [],
barfbarf [m4_define([AC][_DEFUN_ONCE],
#endif [AC][_DEFUN([$1],
]],[[]]) [AC_REQUIRE([_gl_DEFUN_ONCE([$1])],
], [m4_indir([_gl_DEFUN_ONCE([$1])])])])]dnl
[gl_cv_compiler_clang=no], [AC][_DEFUN([_gl_DEFUN_ONCE([$1])], [$2])])])
[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

View File

@ -1,25 +0,0 @@
# 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])

1066
main.c

File diff suppressed because it is too large Load Diff

View File

@ -52,9 +52,9 @@ plugin_load_one (struct ovl_plugin_context *context, const char *path)
plugin_name name; plugin_name name;
struct ovl_plugin *p; struct ovl_plugin *p;
plugin_version version; plugin_version version;
void *handle = dlopen (path, RTLD_NOW | RTLD_LOCAL); void *handle = dlopen (path, RTLD_NOW|RTLD_LOCAL);
if (! handle) if (! handle)
error (EXIT_FAILURE, 0, "cannot load plugin %s: %s", path, dlerror ()); error (EXIT_FAILURE, 0, "cannot load plugin %s: %s", path, dlerror());
p = calloc (1, sizeof (*p)); p = calloc (1, sizeof (*p));
if (p == NULL) if (p == NULL)

View File

@ -17,10 +17,10 @@
*/ */
#ifndef PLUGIN_MANAGER_H #ifndef PLUGIN_MANAGER_H
#define PLUGIN_MANAGER_H # define PLUGIN_MANAGER_H
#include <config.h> # include <config.h>
#include <dlfcn.h> # include <dlfcn.h>
struct ovl_plugin_context struct ovl_plugin_context
{ {
@ -37,7 +37,8 @@ struct ovl_plugin_context *load_plugins (const char *plugins);
/* taken from glibc unistd.h and fixes musl */ /* taken from glibc unistd.h and fixes musl */
#ifndef TEMP_FAILURE_RETRY #ifndef TEMP_FAILURE_RETRY
#define TEMP_FAILURE_RETRY(expression) \ #define TEMP_FAILURE_RETRY(expression) \
(__extension__ ({ long int __result; \ (__extension__ \
({ long int __result; \
do __result = (long int) (expression); \ do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \ while (__result == -1L && errno == EINTR); \
__result; })) __result; }))

View File

@ -17,16 +17,16 @@
*/ */
#ifndef PLUGIN_H #ifndef PLUGIN_H
#define PLUGIN_H # define PLUGIN_H
#include <config.h> # include <config.h>
#include <utils.h> # include <utils.h>
#include <fuse-overlayfs.h> # include <fuse-overlayfs.h>
typedef struct data_source *(*plugin_load_data_source) (const char *opaque, const char *path); typedef struct data_source *(*plugin_load_data_source)(const char *opaque, const char *path);
typedef int (*plugin_release) (); typedef int (*plugin_release)();
typedef const char *(*plugin_name) (); typedef const char *(*plugin_name)();
typedef int (*plugin_version) (); typedef int (*plugin_version)();
struct ovl_plugin struct ovl_plugin
{ {

View File

@ -2,17 +2,17 @@
set -xeuo pipefail set -xeuo pipefail
mkdir lower:1 upper:2 workdir:3 merged mkdir lower upper workdir merged
fuse-overlayfs -o 'sync=0,lowerdir=lower\\:1,upperdir=upper\\:2,workdir=workdir\\:3,suid,dev' merged fuse-overlayfs -o sync=0,lowerdir=lower,upperdir=upper,workdir=workdir,suid,dev merged
docker run --rm -v $(pwd)/merged:/merged fedora dnf --use-host-config --installroot /merged --releasever 41 install -y glibc-common gedit docker run --rm -v $(pwd)/merged:/merged fedora dnf --installroot /merged --releasever 30 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 lower:1 workdir:3 rm -rf workdir lower
mv upper:2 lower mv upper 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 --use-host-config --installroot /merged --releasever 41 install -y emacs texlive 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 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/'
@ -45,10 +45,17 @@ umount merged
rm -rf workdir lower upper rm -rf workdir lower upper
mkdir upper workdir lower mkdir upper workdir lower
fuse-overlayfs -o sync=0,lowerdir=lower,upperdir=upper,workdir=workdir,suid,dev merged
# https://github.com/containers/fuse-overlayfs/issues/86
docker run --rm -v $(pwd)/merged:/merged quay.io/centos/centos:stream8 yum --installroot /merged -y --releasever 8 install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
umount merged
# 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 --use-host-config --installroot /merged --releasever 41 install -y glibc-common gedit docker run --rm -v $(pwd)/merged:/merged quay.io/centos/centos:stream8 yum --installroot /merged -y --releasever 8 install nano
mkdir merged/a-directory mkdir merged/a-directory
@ -258,18 +265,3 @@ 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

View File

@ -14,13 +14,13 @@ main ()
unlink ("suid"); unlink ("suid");
unlink ("nosuid"); unlink ("nosuid");
fd = open ("suid", O_WRONLY | O_CREAT | O_EXCL); fd = open ("suid", O_WRONLY|O_CREAT|O_EXCL);
write (fd, "1", 1); write (fd, "1", 1);
fchown (fd, 0, 0); fchown (fd, 0, 0);
fchmod (fd, S_ISUID | 0755); fchmod (fd, S_ISUID | 0755);
close (fd); close (fd);
fd = open ("nosuid", O_WRONLY | O_CREAT | O_EXCL); fd = open ("nosuid", O_WRONLY|O_CREAT|O_EXCL);
write (fd, "1", 1); write (fd, "1", 1);
fchown (fd, 0, 0); fchown (fd, 0, 0);
fchmod (fd, S_ISUID | 0755); fchmod (fd, S_ISUID | 0755);

View File

@ -29,27 +29,3 @@ else
fi fi
fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ] fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ]
# xattr_permissions=2
rm -rf lower upper workdir merged
mkdir lower upper workdir merged
touch upper/file
unshare -r setcap cap_net_admin+ep upper/file
fuse-overlayfs -o lowerdir=lower,upperdir=upper,workdir=workdir,xattr_permissions=2 merged
# Ensure the security xattr namespace is isolated.
test "$(unshare -r getcap merged/file)" = ''
unshare -r setcap cap_net_admin+ep merged/file
test "$(unshare -r getcap merged/file)" = 'merged/file cap_net_admin=ep'
# Ensure UID is preserved with chgrp.
podman unshare chgrp 1 merged/file
test $(podman unshare stat -c %u:%g merged/file) = 0:1
# Ensure UID and GID are preserved with chmod.
chmod 600 merged/file
test $(podman unshare stat -c %u:%g merged/file) = 0:1
fusermount -u merged || [ $? -eq "${EXPECT_UMOUNT_STATUS:-0}" ]

151
utils.c
View File

@ -32,42 +32,48 @@
#include <sys/xattr.h> #include <sys/xattr.h>
#ifndef TEMP_FAILURE_RETRY #ifndef TEMP_FAILURE_RETRY
# define TEMP_FAILURE_RETRY(expression) \ #define TEMP_FAILURE_RETRY(expression) \
(__extension__ ({ long int __result; \ (__extension__ \
({ long int __result; \
do __result = (long int) (expression); \ do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \ while (__result == -1L && errno == EINTR); \
__result; })) __result; }))
#endif #endif
#ifndef RESOLVE_IN_ROOT #ifndef RESOLVE_IN_ROOT
# define RESOLVE_IN_ROOT 0x10 # define RESOLVE_IN_ROOT 0x10
#endif #endif
#ifndef __NR_openat2 #ifndef __NR_openat2
# define __NR_openat2 437 # define __NR_openat2 437
#endif #endif
/* uClibc and uClibc-ng don't provide O_TMPFILE */ /* uClibc and uClibc-ng don't provide O_TMPFILE */
#ifndef O_TMPFILE #ifndef O_TMPFILE
# define O_TMPFILE (020000000 | O_DIRECTORY) # define O_TMPFILE (020000000 | O_DIRECTORY)
#endif #endif
/* List of all valid flags for the open/openat flags argument: */ /* List of all valid flags for the open/openat flags argument: */
#define VALID_OPEN_FLAGS \ #define VALID_OPEN_FLAGS \
(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | O_SYNC | O_DSYNC | FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | O_PATH | O_TMPFILE) (O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
O_APPEND | O_NDELAY | O_NONBLOCK | O_NDELAY | O_SYNC | O_DSYNC | \
FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
O_NOATIME | O_CLOEXEC | O_PATH | O_TMPFILE)
static int static int
syscall_openat2 (int dirfd, const char *path, uint64_t flags, uint64_t mode, uint64_t resolve) syscall_openat2 (int dirfd, const char *path, uint64_t flags, uint64_t mode, uint64_t resolve)
{ {
struct openat2_open_how struct openat2_open_how
{ {
uint64_t flags; uint64_t flags;
uint64_t mode; uint64_t mode;
uint64_t resolve; uint64_t resolve;
} how = { }
.flags = flags & VALID_OPEN_FLAGS, how =
.mode = (flags & O_CREAT) ? (mode & 07777) : 0, {
.resolve = resolve, .flags = flags & VALID_OPEN_FLAGS,
}; .mode = (flags & O_CREAT) ? (mode & 07777) : 0,
.resolve = resolve,
};
return (int) syscall (__NR_openat2, dirfd, path, &how, sizeof (how), 0); return (int) syscall (__NR_openat2, dirfd, path, &how, sizeof (how), 0);
} }
@ -91,19 +97,18 @@ safe_openat (int dirfd, const char *pathname, int flags, mode_t mode)
} }
return ret; return ret;
} }
fallback: fallback:
return openat (dirfd, pathname, flags, mode); return openat (dirfd, pathname, flags, mode);
} }
int int
file_exists_at (int dirfd, const char *pathname) file_exists_at (int dirfd, const char *pathname)
{ {
int ret = faccessat (dirfd, pathname, F_OK, AT_SYMLINK_NOFOLLOW | AT_EACCESS); int ret = faccessat (dirfd, pathname, F_OK, AT_SYMLINK_NOFOLLOW|AT_EACCESS);
if (ret < 0 && errno == EINVAL) if (ret < 0 && errno == EINVAL) {
{ struct stat buf;
struct stat buf; return fstatat (dirfd, pathname, &buf, AT_SYMLINK_NOFOLLOW);
return fstatat (dirfd, pathname, &buf, AT_SYMLINK_NOFOLLOW); }
}
return ret; return ret;
} }
@ -214,7 +219,7 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd,
{ {
out[0] = '\0'; out[0] = '\0';
*fd = l->ds->openat (l, path, O_NONBLOCK | O_NOFOLLOW | flags, 0); *fd = l->ds->openat (l, path, O_NONBLOCK|O_NOFOLLOW|flags, 0);
if (*fd < 0 && (errno == ELOOP || errno == EISDIR || errno == ENXIO)) if (*fd < 0 && (errno == ELOOP || errno == EISDIR || errno == ENXIO))
{ {
strconcat3 (out, PATH_MAX, l->path, "/", path); strconcat3 (out, PATH_MAX, l->path, "/", path);
@ -224,38 +229,16 @@ 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 = 0; mode_t mode;
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)
{ {
@ -292,10 +275,14 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
if (fd >= 0) if (fd >= 0)
{ {
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1); ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
if (ret < 0)
return ret;
} }
else if (abs_path) else if (abs_path)
{ {
ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1); ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1);
if (ret < 0)
return ret;
} }
else else
{ {
@ -310,53 +297,20 @@ override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *pa
if (fd >= 0) if (fd >= 0)
ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1); ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1);
else else
ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1); {
} ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1);
if (ret < 0 && errno == ENODATA)
return 0;
}
if (ret < 0) if (ret < 0)
return errno == ENODATA ? 0 : ret; return ret;
}
buf[ret] = '\0'; buf[ret] = '\0';
ret = sscanf (buf, "%d:%d:%o:%ms", &uid, &gid, &mode, &type); ret = sscanf (buf, "%d:%d:%o", &uid, &gid, &mode);
if (ret == 4) if (ret != 3)
{
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;
@ -364,24 +318,7 @@ 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 = mode; st->st_mode = (st->st_mode & S_IFMT) | 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;
}

46
utils.h
View File

@ -16,25 +16,25 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H # define UTILS_H
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
# define _GNU_SOURCE # define _GNU_SOURCE
#endif #endif
#include <config.h> # include <config.h>
#include <dirent.h> # include <dirent.h>
#include <fcntl.h> # include <fcntl.h>
#include <limits.h> # include <limits.h>
#include <stdio.h> # include <stdio.h>
#include <stdlib.h> # include <stdlib.h>
#include <sys/types.h> # include <sys/types.h>
#include <unistd.h> # include <unistd.h>
#include "fuse-overlayfs.h" # include "fuse-overlayfs.h"
#define XATTR_OVERRIDE_STAT "user.fuseoverlayfs.override_stat" # define XATTR_OVERRIDE_STAT "user.fuseoverlayfs.override_stat"
#define XATTR_PRIVILEGED_OVERRIDE_STAT "security.fuseoverlayfs.override_stat" # define XATTR_PRIVILEGED_OVERRIDE_STAT "security.fuseoverlayfs.override_stat"
#define XATTR_OVERRIDE_CONTAINERS_STAT "user.containers.override_stat" # define XATTR_OVERRIDE_CONTAINERS_STAT "user.containers.override_stat"
void cleanup_freep (void *p); void cleanup_freep (void *p);
void cleanup_filep (FILE **f); void cleanup_filep (FILE **f);
@ -46,22 +46,20 @@ int file_exists_at (int dirfd, const char *pathname);
int strconcat3 (char *dest, size_t size, const char *s1, const char *s2, const char *s3); int strconcat3 (char *dest, size_t size, const char *s1, const char *s2, const char *s3);
int open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd, int flags); int open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd, int flags);
#define cleanup_file __attribute__ ((cleanup (cleanup_filep))) # define cleanup_file __attribute__((cleanup (cleanup_filep)))
#define cleanup_free __attribute__ ((cleanup (cleanup_freep))) # define cleanup_free __attribute__((cleanup (cleanup_freep)))
#define cleanup_close __attribute__ ((cleanup (cleanup_closep))) # define cleanup_close __attribute__((cleanup (cleanup_closep)))
#define cleanup_dir __attribute__ ((cleanup (cleanup_dirp))) # define cleanup_dir __attribute__((cleanup (cleanup_dirp)))
#define LIKELY(x) __builtin_expect ((x), 1) # define LIKELY(x) __builtin_expect((x),1)
#define UNLIKELY(x) __builtin_expect ((x), 0) # define UNLIKELY(x) __builtin_expect((x),0)
#ifdef HAVE_STATX # ifdef HAVE_STATX
void statx_to_stat (struct statx *stx, struct stat *st); void statx_to_stat (struct statx *stx, struct stat *st);
#endif # endif
int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode); 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