mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-19 16:16:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
947024914a | ||
|
|
7ec6f8e2f2 | ||
|
|
5f7fa17ad8 | ||
|
|
30d59d4458 | ||
|
|
02dc36080c | ||
|
|
8734847968 | ||
|
|
608cd00764 | ||
|
|
b13727abde | ||
|
|
231aac15cd | ||
|
|
8b17d5f35e | ||
|
|
8608b2be46 | ||
|
|
1e0bd54989 | ||
|
|
56285fc6e5 | ||
|
|
dfe053a0ae | ||
|
|
12ac368e8b | ||
|
|
c8f32f9f25 | ||
|
|
ce3737ff33 | ||
|
|
84e4cdeb7e | ||
|
|
6d6d73c7b3 | ||
|
|
bea31543a0 | ||
|
|
99835a0178 | ||
|
|
342a1efcfb | ||
|
|
c587a127fb | ||
|
|
f1034a3ad6 | ||
|
|
8117998205 | ||
|
|
cc780bc521 | ||
|
|
14b7c8e607 |
@@ -31,3 +31,6 @@ rpmbuild/
|
||||
# mkdocs files
|
||||
mkdocs/venv
|
||||
__pycache__/
|
||||
|
||||
# Claude Code session state
|
||||
.claude/
|
||||
|
||||
@@ -29,6 +29,8 @@ MKDIR ?= mkdir
|
||||
MKTEMP ?= mktemp
|
||||
MV ?= mv
|
||||
RM ?= rm
|
||||
RPMBUILD ?= rpmbuild
|
||||
RPMBUILD_FLAGS ?=
|
||||
SED ?= sed
|
||||
STRIP ?= strip
|
||||
TAR ?= tar
|
||||
@@ -37,7 +39,7 @@ TOUCH ?= touch
|
||||
BUILDDIR := build
|
||||
GITHUB_REPO ?= "https://github.com/trapexit/mergerfs"
|
||||
|
||||
DEFAULT_RELEASE_SAMPLE := debian:13.amd64
|
||||
DEFAULT_TARGET := debian:13.amd64
|
||||
|
||||
ifndef GIT_REPO
|
||||
ifneq ($(shell $(GIT) --version 2> /dev/null),)
|
||||
@@ -48,11 +50,7 @@ endif
|
||||
endif
|
||||
|
||||
USE_XATTR ?= 1
|
||||
UGID_USE_RWLOCK ?= 0
|
||||
|
||||
ifdef NDEBUG
|
||||
OPT_FLAGS := -O2 -DNDEBUG
|
||||
else
|
||||
ifdef SANITIZE
|
||||
ifeq ($(SANITIZE),1)
|
||||
override SANITIZE := -fsanitize=address,undefined,leak
|
||||
@@ -60,14 +58,22 @@ else ifeq ($(filter -fsanitize=%,$(SANITIZE)),)
|
||||
override SANITIZE := -fsanitize=$(SANITIZE)
|
||||
endif
|
||||
endif
|
||||
OPT_FLAGS := -O0 \
|
||||
-g \
|
||||
-fno-omit-frame-pointer \
|
||||
$(SANITIZE) \
|
||||
-fstack-protector-strong \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-DDEBUG
|
||||
|
||||
COMMON_OPT_FLAGS += \
|
||||
-fstack-protector-strong \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter
|
||||
|
||||
DEBUG_OPT_FLAGS += \
|
||||
-O0 \
|
||||
-g3 \
|
||||
-fno-omit-frame-pointer \
|
||||
$(SANITIZE)
|
||||
|
||||
ifdef RELEASE
|
||||
OPT_FLAGS := -O3 $(COMMON_OPT_FLAGS)
|
||||
else
|
||||
OPT_FLAGS ?= $(DEBUG_OPT_FLAGS) $(COMMON_OPT_FLAGS)
|
||||
endif
|
||||
|
||||
ifdef STATIC
|
||||
@@ -117,7 +123,7 @@ CXXFLAGS ?= \
|
||||
-Wno-unused-result \
|
||||
-pipe
|
||||
override CXXFLAGS += \
|
||||
-std=c++17 \
|
||||
-std=c++20 \
|
||||
-MMD \
|
||||
-MP
|
||||
override INC_FLAGS := \
|
||||
@@ -125,8 +131,7 @@ override INC_FLAGS := \
|
||||
-Ivendored \
|
||||
-Ivendored/libfuse/include
|
||||
override MFS_FLAGS := \
|
||||
-DUSE_XATTR=$(USE_XATTR) \
|
||||
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
|
||||
-DUSE_XATTR=$(USE_XATTR)
|
||||
override TESTS_FLAGS := \
|
||||
-Isrc \
|
||||
-Ivendored \
|
||||
@@ -198,7 +203,7 @@ libfuse: $(LIBFUSE)
|
||||
$(MAKE) -C vendored/libfuse
|
||||
|
||||
$(LIBFUSE):
|
||||
$(MAKE) -C vendored/libfuse $(BUILDDIR)/libfuse.a
|
||||
$(MAKE) -C vendored/libfuse $(BUILDDIR)/libfuse.a RELEASE=$(RELEASE)
|
||||
|
||||
tests: $(BUILDDIR)/tests
|
||||
|
||||
@@ -340,7 +345,7 @@ rpm: tarball
|
||||
$(SED) 's/__VERSION__/$(VERSION)/g' mergerfs.spec > \
|
||||
rpmbuild/SOURCES/mergerfs.spec
|
||||
cp -ar mergerfs-$(VERSION).tar.gz rpmbuild/SOURCES
|
||||
rpmbuild -ba rpmbuild/SOURCES/mergerfs.spec \
|
||||
$(RPMBUILD) $(RPMBUILD_FLAGS) -ba rpmbuild/SOURCES/mergerfs.spec \
|
||||
--define "_topdir $(CURDIR)/rpmbuild"
|
||||
|
||||
.PHONY: install-build-pkgs
|
||||
@@ -362,14 +367,16 @@ define build_release
|
||||
--branch=$(GITREF)
|
||||
endef
|
||||
|
||||
.PHONY: release
|
||||
.PHONY: release release-all
|
||||
.PHONY: release-amd64 release-arm64 release-armhf release-riscv64
|
||||
.PHONY: release-sample release-static release-tarball
|
||||
.PHONY: release-sample release-static release-tarball release-installer
|
||||
release:
|
||||
$(MAKE) RELEASE=1 CPPFLAGS="$(CPPFLAGS) -DNDEBUG" all
|
||||
release-all:
|
||||
$(call build_release,"all")
|
||||
release-sample:
|
||||
$(if $(RELEASE_SAMPLE),,$(eval RELEASE_SAMPLE := $(shell ./buildtools/detect-local-target 2>/dev/null)))
|
||||
$(call build_release,$(if $(RELEASE_SAMPLE),$(RELEASE_SAMPLE),$(DEFAULT_RELEASE_SAMPLE)))
|
||||
$(if $(TARGET),,$(eval TARGET := $(shell ./buildtools/detect-local-target 2>/dev/null)))
|
||||
$(call build_release,$(if $(TARGET),$(TARGET),$(DEFAULT_TARGET)))
|
||||
release-amd64:
|
||||
$(call build_release,"amd64")
|
||||
release-arm64:
|
||||
@@ -382,6 +389,8 @@ release-static:
|
||||
$(call build_release,"static")
|
||||
release-tarball:
|
||||
$(call build_release,"tarball")
|
||||
release-installer:
|
||||
$(call build_release,"installer")
|
||||
|
||||
container:
|
||||
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ -e /tmp/build-env ]; then
|
||||
. /tmp/build-env
|
||||
fi
|
||||
|
||||
rpmbuild_flags()
|
||||
{
|
||||
case "$(command -v "${CXX:-g++}")" in
|
||||
/opt/rh/*)
|
||||
printf '%s' '--undefine _annotated_build'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "usage: $0 <branch> <git_repo>"
|
||||
echo "argv: $@"
|
||||
@@ -28,10 +41,9 @@ mkdir "/build"
|
||||
if [ -e /usr/bin/apt-get ]; then
|
||||
make deb
|
||||
elif [ -e /usr/bin/dnf ]; then
|
||||
make rpm
|
||||
make RPMBUILD_FLAGS="$(rpmbuild_flags)" rpm
|
||||
elif [ -e /usr/bin/yum ]; then
|
||||
. /opt/rh/devtoolset-9/enable
|
||||
make rpm
|
||||
make RPMBUILD_FLAGS="$(rpmbuild_flags)" rpm
|
||||
elif [ -e /sbin/apk ]; then
|
||||
echo "NOT YET SUPPORTED"
|
||||
exit 1
|
||||
|
||||
@@ -18,10 +18,12 @@ def build(git_repo,
|
||||
'--force-rm']
|
||||
if os.path.exists(git_repo):
|
||||
git_repo = os.path.realpath(git_repo)
|
||||
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro']
|
||||
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro:z']
|
||||
args += ['--build-arg=GIT_REPO=file:///tmp/mergerfs.git']
|
||||
else:
|
||||
args += [f'--build-arg=GIT_REPO={git_repo}']
|
||||
os.makedirs(pkgdirpath,exist_ok=True)
|
||||
args += ['-v',f'{os.path.realpath(pkgdirpath)}:/pkgs:ro']
|
||||
args += ['-o',pkgdirpath,
|
||||
'-f',containerfile,
|
||||
f'--build-arg=BRANCH={branch}',
|
||||
@@ -30,8 +32,6 @@ def build(git_repo,
|
||||
# TODO: Capture output and write to log
|
||||
print(args)
|
||||
rv = subprocess.run(args)
|
||||
|
||||
os.makedirs(pkgdirpath,exist_ok=True)
|
||||
report_filepath = os.path.join(pkgdirpath,"build-report.txt")
|
||||
with open(report_filepath,"a+") as f:
|
||||
build = os.path.basename(containerfile)
|
||||
@@ -66,6 +66,30 @@ def setup():
|
||||
sys.exit(1)
|
||||
print(args)
|
||||
subprocess.run(args)
|
||||
setup_binfmt()
|
||||
|
||||
|
||||
def setup_binfmt():
|
||||
"""Register qemu-user-static binfmt handlers (needed on Arch, etc.)."""
|
||||
binfmt_conf = """\
|
||||
:qemu-aarch64:M::\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xb7\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/qemu-aarch64-static:F
|
||||
:qemu-arm:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/qemu-arm-static:F
|
||||
:qemu-armeb:M::\\x7fELF\\x01\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff:/usr/bin/qemu-armeb-static:F
|
||||
:qemu-riscv64:M::\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xf3\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/qemu-riscv64-static:F
|
||||
:qemu-i386:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x03\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/qemu-i386-static:F
|
||||
"""
|
||||
conf_path = '/etc/binfmt.d/qemu-static.conf'
|
||||
print(f"Writing {conf_path}")
|
||||
try:
|
||||
with open(conf_path, 'w') as f:
|
||||
f.write(binfmt_conf)
|
||||
except PermissionError:
|
||||
print(f"Permission denied writing {conf_path}; trying with sudo...")
|
||||
subprocess.run(['sudo', 'tee', conf_path],
|
||||
input=binfmt_conf.encode(), check=True)
|
||||
|
||||
print("Restarting systemd-binfmt...")
|
||||
subprocess.run(['sudo', 'systemctl', 'restart', 'systemd-binfmt'])
|
||||
|
||||
|
||||
def podman_cleanup():
|
||||
@@ -92,7 +116,7 @@ def parse_args():
|
||||
required=False,
|
||||
action='store_true')
|
||||
p.add_argument('--git-repo',
|
||||
required=True,
|
||||
required=False,
|
||||
type=str)
|
||||
|
||||
return p.parse_args()
|
||||
@@ -112,6 +136,10 @@ def main():
|
||||
setup()
|
||||
sys.exit(0)
|
||||
|
||||
if not args.git_repo:
|
||||
print("error: the following arguments are required: --git-repo")
|
||||
sys.exit(1)
|
||||
|
||||
containerfiles = []
|
||||
basepath = 'buildtools/containerfiles'
|
||||
if os.path.exists(f'{basepath}/{args.target}'):
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=amd64 fedora:44 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=arm64 fedora:44 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -1,9 +1,13 @@
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
FROM --platform=linux/amd64 ubuntu:latest as build
|
||||
RUN apt-get update && apt-get install -y makeself
|
||||
COPY install-build-pkgs /tmp/
|
||||
RUN /tmp/install-build-pkgs && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -qy --no-install-suggests --no-install-recommends install \
|
||||
makeself
|
||||
RUN mkdir -p /tmp/mergerfs/
|
||||
RUN --mount=type=bind,src=pkgs,dst=/pkgs ls -lhd /pkgs
|
||||
RUN --mount=type=bind,src=pkgs,dst=/pkgs cp -v /pkg/*static*.gz /tmp/mergerfs/
|
||||
RUN ls -lhd /pkgs
|
||||
RUN cp -v /pkgs/*static*.gz /tmp/mergerfs/
|
||||
RUN mkdir -p /build
|
||||
RUN makeself /tmp/mergerfs/ /build/mergerfs-installer.run "mergerfs" echo "foo"
|
||||
|
||||
FROM scratch
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=amd64 ubuntu:26.04 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=arm64 ubuntu:26.04 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=armhf ubuntu:26.04 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM --platform=riscv64 ubuntu:26.04 as build
|
||||
COPY install-build-pkgs build-mergerfs /tmp/
|
||||
RUN /tmp/install-build-pkgs
|
||||
ARG BUILD_TIMESTAMP=0
|
||||
ARG GIT_REPO
|
||||
ARG BRANCH
|
||||
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /build/ /
|
||||
@@ -1,39 +1,162 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
BUILD_ENV=/tmp/build-env
|
||||
|
||||
write_gcc_env()
|
||||
{
|
||||
{
|
||||
printf 'export CC=%s\n' "$1"
|
||||
printf 'export CXX=%s\n' "$2"
|
||||
} > "$BUILD_ENV"
|
||||
}
|
||||
|
||||
write_toolset_env()
|
||||
{
|
||||
{
|
||||
printf '. %s\n' "$1"
|
||||
printf 'export CC=gcc\n'
|
||||
printf 'export CXX=g++\n'
|
||||
} > "$BUILD_ENV"
|
||||
}
|
||||
|
||||
latest_apt_gxx()
|
||||
{
|
||||
apt-cache --names-only search '^g[+][+]-[0-9]' \
|
||||
| sed -n 's/^\(g++-[0-9][0-9]*\) .*/\1/p' \
|
||||
| sort -t- -k2,2V \
|
||||
| tail -n 1
|
||||
}
|
||||
|
||||
latest_dnf_toolset()
|
||||
{
|
||||
dnf -q list --showduplicates 'gcc-toolset-*-gcc-c++' 2>/dev/null \
|
||||
| sed -n 's/^gcc-toolset-\([0-9][0-9]*\)-gcc-c++\..*/\1/p' \
|
||||
| sort -V \
|
||||
| tail -n 1
|
||||
}
|
||||
|
||||
latest_yum_toolset()
|
||||
{
|
||||
yum -q list available 'devtoolset-*-gcc-c++' 2>/dev/null \
|
||||
| sed -n 's/^devtoolset-\([0-9][0-9]*\)-gcc-c++\..*/\1/p' \
|
||||
| sort -V \
|
||||
| tail -n 1
|
||||
}
|
||||
|
||||
version_gt()
|
||||
{
|
||||
highest="$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n 1)"
|
||||
[ "$highest" = "$1" ] && [ "$1" != "$2" ]
|
||||
}
|
||||
|
||||
check_cxx20()
|
||||
{
|
||||
cxx="${CXX:-g++}"
|
||||
test_src=/tmp/mergerfs-cxx20-check.cpp
|
||||
test_bin=/tmp/mergerfs-cxx20-check
|
||||
|
||||
cat > "$test_src" <<'EOF'
|
||||
template <typename T>
|
||||
concept Incrementable = requires(T value) { value + 1; };
|
||||
|
||||
static_assert(Incrementable<int>);
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
if ! "$cxx" -std=c++20 "$test_src" -o "$test_bin" >/dev/null 2>&1; then
|
||||
echo "error: $cxx does not support C++20" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$test_src" "$test_bin"
|
||||
}
|
||||
|
||||
: > "$BUILD_ENV"
|
||||
|
||||
if [ -e /usr/bin/apt-get ]; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get -qy update
|
||||
cxx_pkg="$(latest_apt_gxx)"
|
||||
if [ -n "$cxx_pkg" ]; then
|
||||
gcc_pkg="gcc-${cxx_pkg#g++-}"
|
||||
compiler_pkgs="$gcc_pkg $cxx_pkg"
|
||||
write_gcc_env "$gcc_pkg" "$cxx_pkg"
|
||||
else
|
||||
compiler_pkgs="g++"
|
||||
write_gcc_env gcc g++
|
||||
fi
|
||||
apt-get -qy --force-yes --no-install-suggests --no-install-recommends \
|
||||
install \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
dpkg-dev \
|
||||
git \
|
||||
g++ \
|
||||
$compiler_pkgs \
|
||||
debhelper \
|
||||
lsb-release \
|
||||
fakeroot
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
elif [ -e /usr/bin/dnf ]; then
|
||||
dnf -y update
|
||||
toolset="$(latest_dnf_toolset)"
|
||||
dnf -y install \
|
||||
git rpm-build gcc-c++ make which libatomic
|
||||
default_gxx_major="$(g++ -dumpversion | cut -f1 -d.)"
|
||||
if [ -n "$toolset" ] && version_gt "$toolset" "$default_gxx_major"; then
|
||||
dnf -y install \
|
||||
"gcc-toolset-${toolset}-gcc" \
|
||||
"gcc-toolset-${toolset}-gcc-c++"
|
||||
write_toolset_env "/opt/rh/gcc-toolset-${toolset}/enable"
|
||||
else
|
||||
write_gcc_env gcc g++
|
||||
fi
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
elif [ -e /usr/bin/yum ]; then
|
||||
yum -y update
|
||||
yum -y install \
|
||||
git rpm-build gcc-c++ make which
|
||||
yum -y install centos-release-scl
|
||||
yum -y install devtoolset-9-gcc\*
|
||||
yum -y install centos-release-scl || true
|
||||
yum -y update
|
||||
toolset="$(latest_yum_toolset)"
|
||||
default_gxx_major="$(g++ -dumpversion | cut -f1 -d.)"
|
||||
if [ -n "$toolset" ] && version_gt "$toolset" "$default_gxx_major"; then
|
||||
yum -y install \
|
||||
"devtoolset-${toolset}-gcc" \
|
||||
"devtoolset-${toolset}-gcc-c++"
|
||||
write_toolset_env "/opt/rh/devtoolset-${toolset}/enable"
|
||||
else
|
||||
write_gcc_env gcc g++
|
||||
fi
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
elif [ -e /usr/bin/zypper ]; then
|
||||
zypper update -y
|
||||
zypper install -y \
|
||||
git rpm-build gcc-c++ make which
|
||||
write_gcc_env gcc g++
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
elif [ -e /sbin/apk ]; then
|
||||
apk add \
|
||||
abuild git gcc g++ make \
|
||||
linux-headers
|
||||
write_gcc_env gcc g++
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
elif [ -e /usr/sbin/pkg ]; then
|
||||
pkg install \
|
||||
git gmake gcc
|
||||
elif [ -e /usr/bin/pacman ]; then
|
||||
pacman -Sy --noconfirm base-devel git fakeroot
|
||||
write_gcc_env gcc g++
|
||||
. "$BUILD_ENV"
|
||||
check_cxx20
|
||||
fi
|
||||
|
||||
Vendored
+4
-1
@@ -6,8 +6,11 @@ export DH_VERBOSE=1
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
$(MAKE) release
|
||||
|
||||
override_dh_auto_install:
|
||||
$(MAKE) \
|
||||
DESTDIR=$(CURDIR)/debian/mergerfs \
|
||||
PREFIX=/usr \
|
||||
install
|
||||
install
|
||||
@@ -15,6 +15,8 @@ function or have been replaced. **They should not be used.**
|
||||
* **hard_remove**: Does nothing.
|
||||
* **kernel_cache**: Use [cache.files=full](cache.md).
|
||||
* **negative_entry**: Use [cache.negative-entry](cache.md).
|
||||
* **remember**: Use `never-forget-nodes`.
|
||||
* **remember-nodes**: Use `never-forget-nodes`.
|
||||
* **noforget**: Use `never-forget-nodes`.
|
||||
* **nonempty**: Does nothing.
|
||||
* **splice_move**: Does nothing.
|
||||
|
||||
@@ -213,12 +213,11 @@ config file.
|
||||
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
|
||||
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
|
||||
another filesystem. (default: 1)
|
||||
* **remember-nodes=INT**: The number of seconds to keep the internal
|
||||
representation of a file once the OS tells mergerfs it is no longer
|
||||
needed. Really only needed for [exporting mergerfs via
|
||||
NFS](../remote_filesystems.md) (default: 0)
|
||||
* **never-forget-nodes=BOOL**: Sets `remember-nodes` to
|
||||
infinity. Formerly `noforget`. (default: false)
|
||||
* **never-forget-nodes=BOOL**: Keep the internal representation of
|
||||
a file after the OS tells mergerfs it is no longer needed, until the
|
||||
node is unlinked, removed, or replaced. Really only needed for
|
||||
[exporting mergerfs via NFS](../remote_filesystems.md). Formerly
|
||||
`noforget`. (default: false)
|
||||
* **allow-idmap=BOOL**: Enables idmap (identity mapping). (default:
|
||||
true)
|
||||
* **debug**: Enable debugging information. At the moment limited to a
|
||||
|
||||
@@ -2,6 +2,30 @@
|
||||
|
||||
## mergerfs
|
||||
|
||||
### Advisory file locking not currently supported
|
||||
|
||||
mergerfs does not implement POSIX or BSD advisory file locks as
|
||||
provided by FUSE. Doing so is complex because, as a union filesystem,
|
||||
it would need to forward lock requests to the underlying branches,
|
||||
which could result in blocking calls. Locally this is not a major
|
||||
concern—the kernel still manages locks for applications that all go
|
||||
through the mergerfs mount. However, if an application locks a file
|
||||
via the mergerfs mount while another locks the same file directly on
|
||||
the underlying filesystem, the locks will not interact as one might
|
||||
expect. That usage pattern is uncommon, so it is generally not a
|
||||
problem.
|
||||
|
||||
The issue becomes more significant if you rely on locks over a remote
|
||||
filesystem like NFS across multiple hosts, because mergerfs never
|
||||
actually makes a lock call to the underlying branch. While network
|
||||
locks are possible, they are generally discouraged, so this may not be
|
||||
a huge loss either.
|
||||
|
||||
That said, there is a strategy that could add lock support if there is
|
||||
sufficient demand, though it would probably be opt-in for performance
|
||||
and resource-usage reasons.
|
||||
|
||||
|
||||
### FreeBSD version
|
||||
|
||||
* [https://wiki.freebsd.org/FUSEFS](https://wiki.freebsd.org/FUSEFS)
|
||||
@@ -27,7 +51,7 @@
|
||||
* ...
|
||||
|
||||
|
||||
#### Host vs Container identity
|
||||
### Host vs Container identity
|
||||
|
||||
While **not** a bug some users have found when using containers that
|
||||
supplemental groups defined inside the container don't work as
|
||||
|
||||
@@ -57,7 +57,7 @@ NFS export settings:
|
||||
* `softreval`: NFS client will serve up cached data after `retrans`
|
||||
attempts to revalidate the data. Helps with intermittent network
|
||||
issues.
|
||||
* `timeo=150`: Timeout till retrying request.
|
||||
* `timeo=150`: Timeout till retrying request.
|
||||
* `retrans=3`: Number of retrying a request.
|
||||
* `rsize=131072`: Read size.
|
||||
* `wsize=131072`: Write size.
|
||||
@@ -76,12 +76,11 @@ but if you run into problems it may be worth trying Samba/SMB.
|
||||
|
||||
**mergerfs settings:**
|
||||
|
||||
* `never-forget-nodes=true` or `remember-nodes=SECONDS`
|
||||
* `never-forget-nodes=true`
|
||||
* `inodecalc=path-hash`
|
||||
* `lazy-umount-mountpoint=false`
|
||||
|
||||
`never-forget-nodes=true` (or `remember-nodes` with a large value such
|
||||
as `86400` for a full day) is needed because NFS uses the
|
||||
`never-forget-nodes=true` is needed because NFS uses the
|
||||
`name_to_handle_at` and `open_by_handle_at` functions which allow a
|
||||
program to keep a reference to a file without technically having it
|
||||
open in the typical sense. The problem is that FUSE has no way to know
|
||||
@@ -89,8 +88,8 @@ that NFS has a handle that it will later use to open the file
|
||||
again. As a result, it is possible for the kernel to tell mergerfs to
|
||||
forget about the file node and should NFS ever ask for that node's
|
||||
details in the future it would have nothing to respond with. Keeping
|
||||
nodes around forever (or however long is set with `remember-nodes`) is
|
||||
not ideal but at the moment the only way to manage the situation.
|
||||
nodes around until they are unlinked, removed, or replaced is not ideal
|
||||
but at the moment the only way to manage the situation.
|
||||
|
||||
`inodecalc=path-hash` is needed because NFS is sensitive to
|
||||
out-of-band changes. FUSE doesn't care if a file's inode value changes
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
depending on [fuse-msg-size](config/fuse-msg-size.md)
|
||||
* buffers allocated temporarily for reading directories
|
||||
* FUSE nodes
|
||||
* never-forget-nodes=true nodes
|
||||
* never-forget-nodes=true retained nodes, until unlinked or removed
|
||||
|
||||
|
||||
## Management
|
||||
|
||||
+1
-2
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "base_types.h"
|
||||
#include "strvec.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -43,7 +42,7 @@ public:
|
||||
public:
|
||||
std::variant<u64,const u64*> _minfreespace;
|
||||
Mode mode;
|
||||
fs::path path;
|
||||
std::string path;
|
||||
|
||||
public:
|
||||
Branch();
|
||||
|
||||
+89
-35
@@ -28,9 +28,14 @@
|
||||
#include "str.hpp"
|
||||
#include "syslog.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
|
||||
@@ -223,13 +228,24 @@ namespace l
|
||||
fs::realpathize(&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
std::error_code ec;
|
||||
struct stat st;
|
||||
int rv;
|
||||
|
||||
if(!std::filesystem::exists(path,ec))
|
||||
rv = ::stat(path.c_str(),&st);
|
||||
if(rv < 0)
|
||||
{
|
||||
SysLog::notice("branch `{}` does not currently exist",path);
|
||||
// Distinguish "doesn't exist" (the common case where the
|
||||
// backing storage isn't mounted yet) from harder errors
|
||||
// like permission, name-too-long, ELOOP, etc., which
|
||||
// suggest a config problem rather than transient absence.
|
||||
if(errno == ENOENT)
|
||||
SysLog::notice("branch `{}` does not currently exist",path);
|
||||
else
|
||||
SysLog::warning("branch `{}` could not be stat'd: {}",
|
||||
path,
|
||||
strerror(errno));
|
||||
}
|
||||
else if(!std::filesystem::is_directory(path,ec))
|
||||
else if(!S_ISDIR(st.st_mode))
|
||||
{
|
||||
SysLog::warning("branch `{}` is not a directory, skipping",path);
|
||||
continue;
|
||||
@@ -415,14 +431,14 @@ Branches::Impl::to_paths(StrVec &paths_) const
|
||||
{
|
||||
for(auto &branch : *this)
|
||||
{
|
||||
paths_.push_back(branch.path);
|
||||
paths_.emplace_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fs::path>
|
||||
std::vector<std::string>
|
||||
Branches::Impl::to_paths() const
|
||||
{
|
||||
std::vector<fs::path> vp;
|
||||
std::vector<std::string> vp;
|
||||
|
||||
for(const auto &branch : *this)
|
||||
vp.emplace_back(branch.path);
|
||||
@@ -437,24 +453,44 @@ Branches::from_string(const std::string_view str_)
|
||||
Branches::Ptr impl;
|
||||
Branches::Ptr new_impl;
|
||||
|
||||
impl = std::atomic_load(&_impl);
|
||||
|
||||
new_impl = std::make_shared<Branches::Impl>(&minfreespace);
|
||||
*new_impl = *impl;
|
||||
|
||||
rv = new_impl->from_string(str_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
for(;;)
|
||||
{
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(_mutex);
|
||||
impl = _impl;
|
||||
}
|
||||
|
||||
std::atomic_store(&_impl,new_impl);
|
||||
*new_impl = *impl;
|
||||
|
||||
return 0;
|
||||
rv = new_impl->from_string(str_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lk(_mutex);
|
||||
if(impl != _impl)
|
||||
continue;
|
||||
|
||||
_impl = std::move(new_impl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
return std::atomic_load(&_impl)->to_string();
|
||||
Branches::Ptr impl;
|
||||
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(_mutex);
|
||||
impl = _impl;
|
||||
}
|
||||
|
||||
return impl->to_string();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -462,32 +498,50 @@ Branches::find_and_set_mode_ro()
|
||||
{
|
||||
Branches::Ptr impl;
|
||||
Branches::Ptr new_impl;
|
||||
std::vector<std::string> ro_paths;
|
||||
|
||||
impl = std::atomic_load(&_impl);
|
||||
new_impl = std::make_shared<Branches::Impl>(&minfreespace);
|
||||
|
||||
*new_impl = *impl;
|
||||
|
||||
bool changed = false;
|
||||
for(auto &branch : *new_impl)
|
||||
for(;;)
|
||||
{
|
||||
if(branch.mode != Branch::Mode::RW)
|
||||
continue;
|
||||
ro_paths.clear();
|
||||
|
||||
if(!fs::is_rofs_but_not_mounted_ro(branch.path))
|
||||
continue;
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(_mutex);
|
||||
impl = _impl;
|
||||
}
|
||||
|
||||
SysLog::warning("branch `{}` found to be readonly - setting its mode=RO",
|
||||
branch.path.string());
|
||||
*new_impl = *impl;
|
||||
|
||||
branch.mode = Branch::Mode::RO;
|
||||
changed = true;
|
||||
for(auto &branch : *new_impl)
|
||||
{
|
||||
if(branch.mode != Branch::Mode::RW)
|
||||
continue;
|
||||
|
||||
if(!fs::is_rofs_but_not_mounted_ro(branch.path))
|
||||
continue;
|
||||
|
||||
branch.mode = Branch::Mode::RO;
|
||||
ro_paths.emplace_back(branch.path);
|
||||
}
|
||||
|
||||
if(ro_paths.empty())
|
||||
return;
|
||||
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lk(_mutex);
|
||||
if(impl != _impl)
|
||||
continue;
|
||||
|
||||
_impl = std::move(new_impl);
|
||||
}
|
||||
|
||||
for(const auto &path : ro_paths)
|
||||
SysLog::warning("branch `{}` found to be readonly - setting its mode=RO",
|
||||
path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(not changed)
|
||||
return;
|
||||
|
||||
std::atomic_store(&_impl,new_impl);
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
|
||||
+15
-6
@@ -19,13 +19,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -54,7 +53,7 @@ public:
|
||||
public:
|
||||
const u64 &minfreespace(void) const;
|
||||
void to_paths(StrVec &strvec) const;
|
||||
std::vector<fs::path> to_paths() const;
|
||||
std::vector<std::string> to_paths() const;
|
||||
|
||||
public:
|
||||
Impl& operator=(Impl &impl_);
|
||||
@@ -69,7 +68,8 @@ public:
|
||||
u64 minfreespace = MINFREESPACE_DEFAULT;
|
||||
|
||||
private:
|
||||
mutable std::shared_ptr<Impl> _impl;
|
||||
Branches::Ptr _impl;
|
||||
mutable std::shared_mutex _mutex;
|
||||
|
||||
public:
|
||||
Branches()
|
||||
@@ -82,8 +82,17 @@ public:
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
operator Ptr() const { return std::atomic_load(&_impl); }
|
||||
Ptr operator->() const { return std::atomic_load(&_impl); }
|
||||
operator Ptr() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(_mutex);
|
||||
return _impl;
|
||||
}
|
||||
|
||||
Ptr operator->() const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(_mutex);
|
||||
return _impl;
|
||||
}
|
||||
|
||||
public:
|
||||
void find_and_set_mode_ro();
|
||||
|
||||
+18
-17
@@ -46,16 +46,16 @@ int
|
||||
Config::CfgConfigFile::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
fs::path cfg_file;
|
||||
std::string cfg_file;
|
||||
|
||||
if(_depth > 5)
|
||||
return -ELOOP;
|
||||
_depth++;
|
||||
struct DepthGuard { int &d; ~DepthGuard() { --d; } } guard{_depth};
|
||||
|
||||
cfg_file = (s_.empty() ? _cfg_file : s_);
|
||||
cfg_file = (s_.empty() ? std::string_view(_cfg_file) : s_);
|
||||
|
||||
rv = cfg.from_file(cfg_file);
|
||||
rv = cfg.from_file(cfg_file.c_str());
|
||||
if(rv == 0)
|
||||
_cfg_file = cfg_file;
|
||||
|
||||
@@ -135,10 +135,6 @@ Config::Config()
|
||||
_max_background(fuse_cfg.max_background),
|
||||
_mount(mountpoint),
|
||||
_mountpoint(mountpoint),
|
||||
_never_forget_nodes(),
|
||||
_noforget(),
|
||||
_remember(fuse_cfg.remember_nodes),
|
||||
_remember_nodes(fuse_cfg.remember_nodes),
|
||||
_srcmounts(branches),
|
||||
_threads(fuse_cfg.read_thread_count),
|
||||
_uid(fuse_cfg.uid),
|
||||
@@ -147,13 +143,22 @@ Config::Config()
|
||||
|
||||
_initialized(false)
|
||||
{
|
||||
allow_idmap.ro =
|
||||
_congestion_threshold.ro =
|
||||
_max_background.ro =
|
||||
_mount.ro =
|
||||
_mountpoint.ro =
|
||||
_never_forget_nodes.ro =
|
||||
_noforget.ro =
|
||||
_remember.ro =
|
||||
_remember_nodes.ro =
|
||||
_srcmounts.ro =
|
||||
_version.ro =
|
||||
allow_idmap.ro =
|
||||
async_read.ro =
|
||||
branches_mount_timeout.ro =
|
||||
branches_mount_timeout_fail.ro =
|
||||
cache_symlinks.ro =
|
||||
cache_writeback.ro =
|
||||
_congestion_threshold.ro =
|
||||
direct_io_allow_mmap.ro =
|
||||
export_support.ro =
|
||||
fsname.ro =
|
||||
@@ -161,9 +166,6 @@ Config::Config()
|
||||
handle_killpriv.ro =
|
||||
handle_killpriv_v2.ro =
|
||||
kernel_permissions_check.ro =
|
||||
_max_background.ro =
|
||||
_mount.ro =
|
||||
_mountpoint.ro =
|
||||
nullrw.ro =
|
||||
pid.ro =
|
||||
pin_threads.ro =
|
||||
@@ -172,8 +174,6 @@ Config::Config()
|
||||
process_thread_queue_depth.ro =
|
||||
read_thread_count.ro =
|
||||
scheduling_priority.ro =
|
||||
_srcmounts.ro =
|
||||
_version.ro =
|
||||
true;
|
||||
_congestion_threshold.display =
|
||||
_gid.display =
|
||||
@@ -181,6 +181,7 @@ Config::Config()
|
||||
_mount.display =
|
||||
_noforget.display =
|
||||
_remember.display =
|
||||
_remember_nodes.display =
|
||||
_srcmounts.display =
|
||||
_threads.display =
|
||||
_uid.display =
|
||||
@@ -425,7 +426,7 @@ Config::from_stream(std::istream &istrm_)
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_file(const std::string &filepath_)
|
||||
Config::from_file(const char *filepath_)
|
||||
{
|
||||
std::ifstream ifstrm;
|
||||
|
||||
@@ -448,13 +449,13 @@ Config::finish_initializing()
|
||||
}
|
||||
|
||||
bool
|
||||
Config::is_rootdir(const fs::path &fusepath_)
|
||||
Config::is_rootdir(const fs::relpath &fusepath_)
|
||||
{
|
||||
return fusepath_.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
Config::is_ctrl_file(const fs::path &fusepath_)
|
||||
Config::is_ctrl_file(const fs::relpath &fusepath_)
|
||||
{
|
||||
return (fusepath_ == ".mergerfs");
|
||||
}
|
||||
|
||||
+10
-10
@@ -67,7 +67,7 @@ typedef ToFromWrapper<u64> ConfigU64;
|
||||
typedef ToFromWrapper<s64> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<fs::path> ConfigPath;
|
||||
typedef ToFromWrapper<std::string> ConfigPath;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
typedef ROToFromWrapper<std::string> ConfigROSTR;
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
class CfgConfigFile : public ToFromString
|
||||
{
|
||||
private:
|
||||
fs::path _cfg_file;
|
||||
std::string _cfg_file;
|
||||
static thread_local int _depth;
|
||||
|
||||
public:
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
LinkEXDEV link_exdev;
|
||||
LogFile log_file;
|
||||
TFSRef<u64> minfreespace;
|
||||
fs::path mountpoint;
|
||||
std::string mountpoint;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
ConfigBOOL nullrw;
|
||||
@@ -172,12 +172,12 @@ private:
|
||||
CfgDummy _dummy;
|
||||
TFSRef<s64> _gid;
|
||||
TFSRef<int> _max_background;
|
||||
TFSRef<fs::path> _mount;
|
||||
TFSRef<fs::path> _mountpoint;
|
||||
TFSRef<std::string> _mount;
|
||||
TFSRef<std::string> _mountpoint;
|
||||
CfgNoforget _never_forget_nodes;
|
||||
CfgNoforget _noforget;
|
||||
TFSRef<s64> _remember;
|
||||
TFSRef<s64> _remember_nodes;
|
||||
CfgNoforget _remember;
|
||||
CfgNoforget _remember_nodes;
|
||||
SrcMounts _srcmounts;
|
||||
TFSRef<int> _threads;
|
||||
TFSRef<s64> _uid;
|
||||
@@ -207,11 +207,11 @@ public:
|
||||
|
||||
public:
|
||||
int from_stream(std::istream &istrm);
|
||||
int from_file(const std::string &filepath);
|
||||
int from_file(const char *filepath);
|
||||
|
||||
public:
|
||||
static bool is_rootdir(const fs::path &fusepath);
|
||||
static bool is_ctrl_file(const fs::path &fusepath);
|
||||
static bool is_rootdir(const fs::relpath &fusepath);
|
||||
static bool is_ctrl_file(const fs::relpath &fusepath);
|
||||
static bool is_mergerfs_xattr(const char *attrname);
|
||||
static bool is_cmd_xattr(const std::string_view &attrname);
|
||||
static std::string_view prune_ctrl_xattr(const std::string_view s);
|
||||
|
||||
+15
-9
@@ -32,18 +32,24 @@ public:
|
||||
{
|
||||
int rv;
|
||||
bool b = false;
|
||||
s64 i = 0;
|
||||
|
||||
rv = str::from(s_,&b);
|
||||
if((b == true) || s_.empty())
|
||||
if(s_.empty())
|
||||
{
|
||||
fuse_cfg.remember_nodes = -1;
|
||||
return 0;
|
||||
b = true;
|
||||
}
|
||||
else if((rv = str::from(s_,&i)) == 0)
|
||||
{
|
||||
b = (i != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = str::from(s_,&b);
|
||||
if(rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
fuse_cfg.remember_nodes = 0;
|
||||
fuse_cfg.remember_nodes = b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -51,7 +57,7 @@ public:
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
if(fuse_cfg.remember_nodes == -1)
|
||||
if(fuse_cfg.remember_nodes)
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public:
|
||||
u64 to_fh() const;
|
||||
|
||||
public:
|
||||
DirInfo(const fs::path &fusepath_)
|
||||
DirInfo(const fs::relpath &fusepath_)
|
||||
: FH(fusepath_)
|
||||
{
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,12 +24,12 @@
|
||||
class FH
|
||||
{
|
||||
public:
|
||||
FH(const fs::path &fusepath_)
|
||||
FH(const fs::relpath &fusepath_)
|
||||
: fusepath(fusepath_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
fs::path fusepath;
|
||||
fs::relpath fusepath;
|
||||
};
|
||||
|
||||
+14
-5
@@ -48,10 +48,10 @@
|
||||
#include "fh.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include "mutex.hpp"
|
||||
|
||||
#include "base_types.h"
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
|
||||
class FileInfo : public FH
|
||||
{
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
public:
|
||||
FileInfo(const int fd_,
|
||||
const Branch *branch_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const bool direct_io_)
|
||||
: FH(fusepath_),
|
||||
fd(fd_),
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
|
||||
FileInfo(const int fd_,
|
||||
const Branch &branch_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const bool direct_io_)
|
||||
: FH(fusepath_),
|
||||
fd(fd_),
|
||||
@@ -97,7 +97,16 @@ public:
|
||||
int fd;
|
||||
Branch branch;
|
||||
u32 direct_io:1;
|
||||
Mutex mutex;
|
||||
// Serializes the fd state across concurrent writes on the same open
|
||||
// file. Concurrent writes happen with:
|
||||
// 1) writeback-cache + page-cache mode
|
||||
// 2) parallel_direct_writes + direct_io=true
|
||||
//
|
||||
// Common case (plain pwrite on stable fd) only needs a shared lock
|
||||
// since the kernel already serializes fd mutations. The danger is
|
||||
// the fs::dup2() rebinding when moveonenospc activates. Writers
|
||||
// take shared lock; move takes unique.
|
||||
std::shared_mutex mutex;
|
||||
};
|
||||
|
||||
inline
|
||||
|
||||
@@ -206,12 +206,3 @@ str::from(const std::string_view value_,
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
str::from(const std::string_view value_,
|
||||
fs::path *path_)
|
||||
{
|
||||
*path_ = value_;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "base_types.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -33,5 +32,4 @@ namespace str
|
||||
int from(const std::string_view, s64 *);
|
||||
int from(const std::string_view, std::string *);
|
||||
int from(const std::string_view, const std::string *);
|
||||
int from(const std::string_view, fs::path *);
|
||||
}
|
||||
|
||||
+2
-3
@@ -21,16 +21,15 @@
|
||||
#include "fs_lgetxattr.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
constexpr const char POSIX_ACL_DEFAULT_XATTR[] = "system.posix_acl_default";
|
||||
|
||||
|
||||
bool
|
||||
fs::acl::dir_has_defaults(const fs::path &fullpath_)
|
||||
fs::acl::dir_has_defaults(const fs::relpath &fullpath_)
|
||||
{
|
||||
int rv;
|
||||
fs::path dirpath;
|
||||
fs::relpath dirpath;
|
||||
|
||||
dirpath = fullpath_.parent_path();
|
||||
|
||||
|
||||
+1
-1
@@ -26,6 +26,6 @@ namespace fs
|
||||
namespace acl
|
||||
{
|
||||
bool
|
||||
dir_has_defaults(const fs::path &fullpath);
|
||||
dir_has_defaults(const fs::relpath &fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ _get_fs_ioc_flags(const int fd,
|
||||
|
||||
static
|
||||
int
|
||||
_get_fs_ioc_flags(const string &file,
|
||||
int &flags)
|
||||
_get_fs_ioc_flags(const char *file,
|
||||
int &flags)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
@@ -81,8 +81,8 @@ _set_fs_ioc_flags(const int fd,
|
||||
|
||||
static
|
||||
int
|
||||
_set_fs_ioc_flags(const string &file,
|
||||
const int flags)
|
||||
_set_fs_ioc_flags(const char *file,
|
||||
const int flags)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
@@ -121,15 +121,15 @@ fs::attr::copy(const int fdin,
|
||||
}
|
||||
|
||||
int
|
||||
fs::attr::copy(const string &from,
|
||||
const string &to)
|
||||
fs::attr::copy(const std::string &from_,
|
||||
const std::string &to_)
|
||||
{
|
||||
int rv;
|
||||
int flags;
|
||||
|
||||
rv = ::_get_fs_ioc_flags(from,flags);
|
||||
rv = ::_get_fs_ioc_flags(from_.c_str(),flags);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return ::_set_fs_ioc_flags(to,flags);
|
||||
return ::_set_fs_ioc_flags(to_.c_str(),flags);
|
||||
}
|
||||
|
||||
@@ -16,25 +16,37 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "fs_cleanpath.hpp"
|
||||
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
void
|
||||
fs::cleanpath(std::string *path_)
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
flock(const int fd_,
|
||||
const int operation_)
|
||||
{
|
||||
int rv;
|
||||
std::string &s = *path_;
|
||||
std::size_t w = 0;
|
||||
bool prev_slash = false;
|
||||
|
||||
rv = ::flock(fd_,operation_);
|
||||
for(std::size_t r = 0; r < s.size(); r++)
|
||||
{
|
||||
const char c = s[r];
|
||||
if(c == '/')
|
||||
{
|
||||
if(prev_slash)
|
||||
continue;
|
||||
prev_slash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_slash = false;
|
||||
}
|
||||
s[w++] = c;
|
||||
}
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
// Strip trailing '/' but leave a bare "/" intact.
|
||||
if(w > 1 && s[w - 1] == '/')
|
||||
w--;
|
||||
|
||||
s.resize(w);
|
||||
}
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
using path = std::filesystem::path;
|
||||
void cleanpath(std::string *path);
|
||||
}
|
||||
+71
-47
@@ -61,60 +61,84 @@ _ignorable_error(const int err_)
|
||||
The directories which already exist are left alone.
|
||||
The new directories have metadata set to match the original if
|
||||
possible. Optionally ignore errors on metadata copies.
|
||||
|
||||
Iterative root-to-leaf walk. Stack usage is O(1) in component count.
|
||||
*/
|
||||
int
|
||||
fs::clonepath(const fs::path &srcpath_,
|
||||
const fs::path &dstpath_,
|
||||
const fs::path &relpath_,
|
||||
const bool return_metadata_errors_)
|
||||
fs::clonepath(const std::string_view srcpath_,
|
||||
const std::string_view dstpath_,
|
||||
const fs::relpath &relpath_,
|
||||
const bool return_metadata_errors_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
fs::path dstpath;
|
||||
fs::path srcpath;
|
||||
fs::path dirname;
|
||||
|
||||
if(relpath_.empty())
|
||||
return 0;
|
||||
|
||||
dirname = relpath_.parent_path();
|
||||
if(!dirname.empty())
|
||||
std::string srcpath;
|
||||
std::string dstpath;
|
||||
srcpath.reserve(srcpath_.size() + relpath_.size() + 1);
|
||||
dstpath.reserve(dstpath_.size() + relpath_.size() + 1);
|
||||
srcpath = srcpath_;
|
||||
dstpath = dstpath_;
|
||||
|
||||
std::string_view rel = relpath_.native();
|
||||
std::size_t pos = 0;
|
||||
while(pos < rel.size())
|
||||
{
|
||||
rv = fs::clonepath(srcpath_,dstpath_,dirname,return_metadata_errors_);
|
||||
std::size_t next = rel.find('/',pos);
|
||||
if(next == std::string_view::npos)
|
||||
next = rel.size();
|
||||
|
||||
std::string_view component = rel.substr(pos,next - pos);
|
||||
pos = next + 1;
|
||||
// Defensive: canonical rel form should not produce empty
|
||||
// segments, but skip them if the input is malformed (e.g. a
|
||||
// double slash) rather than appending nothing and re-stat'ing
|
||||
// the same path.
|
||||
if(component.empty())
|
||||
continue;
|
||||
|
||||
srcpath += '/';
|
||||
srcpath.append(component);
|
||||
dstpath += '/';
|
||||
dstpath.append(component);
|
||||
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
rv = fs::lstat(srcpath,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
if(!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
rv = fs::mkdir(dstpath,st.st_mode);
|
||||
if(rv < 0 && rv != -EEXIST)
|
||||
return rv;
|
||||
// EEXIST: directory already there (could be from a prior clone
|
||||
// or a race). Leave it alone, do NOT re-copy metadata, matching
|
||||
// the recursive version's behavior when its mkdir hit EEXIST.
|
||||
if(rv == -EEXIST)
|
||||
continue;
|
||||
|
||||
// it may not support it... it's fine...
|
||||
rv = fs::attr::copy(srcpath,dstpath);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
// it may not support it... it's fine...
|
||||
rv = fs::xattr::copy(srcpath,dstpath);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
rv = fs::lchown_check_on_error(dstpath,st);
|
||||
if(return_metadata_errors_ && (rv < 0))
|
||||
return rv;
|
||||
|
||||
rv = fs::lutimens(dstpath,st);
|
||||
if(return_metadata_errors_ && (rv < 0))
|
||||
return rv;
|
||||
}
|
||||
|
||||
srcpath = srcpath_ / relpath_;
|
||||
rv = fs::lstat(srcpath,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
else if(!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
dstpath = dstpath_ / relpath_;
|
||||
rv = fs::mkdir(dstpath,st.st_mode);
|
||||
if(rv < 0)
|
||||
return ((rv == -EEXIST) ? 0 : rv);
|
||||
|
||||
// it may not support it... it's fine...
|
||||
rv = fs::attr::copy(srcpath,dstpath);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
// it may not support it... it's fine...
|
||||
rv = fs::xattr::copy(srcpath,dstpath);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
rv = fs::lchown_check_on_error(dstpath,st);
|
||||
if(return_metadata_errors_ && (rv < 0))
|
||||
return rv;
|
||||
|
||||
rv = fs::lutimens(dstpath,st);
|
||||
if(return_metadata_errors_ && (rv < 0))
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -122,10 +146,10 @@ fs::clonepath(const fs::path &srcpath_,
|
||||
// WORK IN PROGRESS
|
||||
static
|
||||
int
|
||||
_clonepath2(const int srcfd_,
|
||||
const int dstfd_,
|
||||
const fs::path &dirname_,
|
||||
const bool return_metadata_errors_)
|
||||
_clonepath2(const int srcfd_,
|
||||
const int dstfd_,
|
||||
const fs::relpath &dirname_,
|
||||
const bool return_metadata_errors_)
|
||||
{
|
||||
int rv;
|
||||
int srcdirfd;
|
||||
|
||||
@@ -20,11 +20,16 @@
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int clonepath(const fs::path &srcpath,
|
||||
const fs::path &dstpath,
|
||||
const fs::path &relpath,
|
||||
const bool return_metadata_errors = false);
|
||||
// srcpath / dstpath are absolute base paths; relpath is the rel-form
|
||||
// chain to walk and create. Accepts string_view so an fs::relpath
|
||||
// holding an absolute fullpath also works without conversion.
|
||||
int clonepath(const std::string_view srcpath,
|
||||
const std::string_view dstpath,
|
||||
const fs::relpath &relpath,
|
||||
const bool return_metadata_errors = false);
|
||||
}
|
||||
|
||||
+4
-4
@@ -98,7 +98,7 @@ fs::copyfile(const int src_fd_,
|
||||
|
||||
s64
|
||||
fs::copyfile(const int src_fd_,
|
||||
const fs::path &dst_filepath_,
|
||||
const char *dst_filepath_,
|
||||
const fs::CopyFileFlags &flags_)
|
||||
{
|
||||
s64 rv;
|
||||
@@ -155,7 +155,7 @@ fs::copyfile(const int src_fd_,
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = fs::rename(dst_tmppath,dst_filepath_);
|
||||
rv = fs::rename(dst_tmppath.c_str(),dst_filepath_);
|
||||
if((rv < 0) && (flags_.cleanup_failure))
|
||||
fs::unlink(dst_tmppath);
|
||||
break;
|
||||
@@ -170,8 +170,8 @@ fs::copyfile(const int src_fd_,
|
||||
#endif
|
||||
|
||||
s64
|
||||
fs::copyfile(const fs::path &src_filepath_,
|
||||
const fs::path &dst_filepath_,
|
||||
fs::copyfile(const char *src_filepath_,
|
||||
const char *dst_filepath_,
|
||||
const fs::CopyFileFlags &flags_)
|
||||
{
|
||||
int src_fd;
|
||||
|
||||
+15
-4
@@ -19,7 +19,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "base_types.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -40,11 +41,21 @@ namespace fs
|
||||
|
||||
s64
|
||||
copyfile(const int src_fd,
|
||||
const fs::path &dst_filepath,
|
||||
const char *dst_filepath,
|
||||
const CopyFileFlags &flags);
|
||||
|
||||
s64
|
||||
copyfile(const fs::path &src_filepath,
|
||||
const fs::path &dst_filepath,
|
||||
copyfile(const char *src_filepath,
|
||||
const char *dst_filepath,
|
||||
const CopyFileFlags &flags);
|
||||
|
||||
static
|
||||
inline
|
||||
s64
|
||||
copyfile(const std::string &src_filepath,
|
||||
const std::string &dst_filepath,
|
||||
const CopyFileFlags &flags)
|
||||
{
|
||||
return fs::copyfile(src_filepath.c_str(),dst_filepath.c_str(),flags);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -53,8 +53,8 @@ fs::cow::is_eligible(const int flags_,
|
||||
}
|
||||
|
||||
bool
|
||||
fs::cow::is_eligible(const fs::path &fullpath_,
|
||||
const int flags_)
|
||||
fs::cow::is_eligible(const std::string &fullpath_,
|
||||
const int flags_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
@@ -70,7 +70,7 @@ fs::cow::is_eligible(const fs::path &fullpath_,
|
||||
}
|
||||
|
||||
int
|
||||
fs::cow::break_link(const fs::path &src_filepath_)
|
||||
fs::cow::break_link(const std::string &src_filepath_)
|
||||
{
|
||||
return fs::copyfile(src_filepath_,
|
||||
src_filepath_,
|
||||
|
||||
+3
-4
@@ -18,8 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -32,8 +31,8 @@ namespace fs
|
||||
bool is_eligible(const struct stat &st);
|
||||
bool is_eligible(const int flags, const struct stat &st);
|
||||
|
||||
bool is_eligible(const fs::path &fullpath, const int flags);
|
||||
bool is_eligible(const std::string &fullpath, const int flags);
|
||||
|
||||
int break_link(const fs::path &fullpath);
|
||||
int break_link(const std::string &fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
eaccess(const fs::path &path_,
|
||||
eaccess(const fs::relpath &path_,
|
||||
const int mode_)
|
||||
{
|
||||
return fs::eaccess(path_.c_str(),mode_);
|
||||
|
||||
+65
-28
@@ -27,8 +27,8 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &path_,
|
||||
struct stat *st_)
|
||||
exists(const char *path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &path_)
|
||||
exists(const char *path_)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
@@ -50,13 +50,49 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &basepath_,
|
||||
const char *relpath_,
|
||||
struct stat *st_)
|
||||
exists(const std::string &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
fs::path fullpath;
|
||||
return fs::exists(path_.c_str(),st_);
|
||||
}
|
||||
|
||||
fullpath = basepath_ / relpath_;
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const std::string &path_)
|
||||
{
|
||||
return fs::exists(path_.c_str());
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::relpath &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
return fs::exists(path_.c_str(),st_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::relpath &path_)
|
||||
{
|
||||
return fs::exists(path_.c_str());
|
||||
}
|
||||
|
||||
// Two-arg helpers for the per-branch loop pattern: build
|
||||
// basepath/'/'/relpath into an fs::relpath and stat it.
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const std::string &basepath_,
|
||||
const fs::relpath &relpath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
fs::relpath fullpath;
|
||||
|
||||
fullpath.assign_concat(basepath_,relpath_);
|
||||
|
||||
return fs::exists(fullpath,st_);
|
||||
}
|
||||
@@ -64,36 +100,37 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &basepath_,
|
||||
const fs::path &relpath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
fs::path fullpath;
|
||||
|
||||
fullpath = basepath_ / relpath_;
|
||||
|
||||
return fs::exists(fullpath,st_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &basepath_,
|
||||
const char *relpath_)
|
||||
exists(const std::string &basepath_,
|
||||
const fs::relpath &relpath_)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
return fs::exists(basepath_,relpath_,&st);
|
||||
}
|
||||
|
||||
// Hot-path variant for per-branch fan-out loops. The caller builds
|
||||
// the fullpath once (with the rel portion set) outside the loop, and
|
||||
// each iteration only rewrites the prefix area via set_prefix --
|
||||
// skipping the per-iteration rel memmove that the (basepath,
|
||||
// relpath) overload incurs.
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(const fs::path &basepath_,
|
||||
const fs::path &relpath_)
|
||||
exists(fs::relpath &fullpath_,
|
||||
const std::string &basepath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
struct stat st;
|
||||
fullpath_.set_prefix(basepath_);
|
||||
return fs::exists(fullpath_.c_str(),st_);
|
||||
}
|
||||
|
||||
return fs::exists(basepath_,relpath_,&st);
|
||||
static
|
||||
inline
|
||||
bool
|
||||
exists(fs::relpath &fullpath_,
|
||||
const std::string &basepath_)
|
||||
{
|
||||
fullpath_.set_prefix(basepath_);
|
||||
return fs::exists(fullpath_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
faccessat(const int dirfd_,
|
||||
const fs::path &path_,
|
||||
const fs::relpath &path_,
|
||||
const int mode_,
|
||||
const int flags_)
|
||||
{
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
fchmodat(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const fs::relpath &pathname_,
|
||||
const mode_t mode_,
|
||||
const int flags_)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "to_neg_errno.hpp"
|
||||
#include "xattr.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -27,18 +27,18 @@
|
||||
|
||||
void
|
||||
fs::findallfiles(const std::vector<std::string> &basepaths_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
std::vector<std::string> *paths_)
|
||||
{
|
||||
fs::path fullpath;
|
||||
fs::relpath fullpath = fusepath_;
|
||||
|
||||
for(const auto &basepath : basepaths_)
|
||||
{
|
||||
fullpath = basepath / fusepath_;
|
||||
fullpath.set_prefix(basepath);
|
||||
|
||||
if(!fs::exists(fullpath))
|
||||
continue;
|
||||
|
||||
paths_->push_back(fullpath);
|
||||
paths_->emplace_back(fullpath.native());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,6 @@ namespace fs
|
||||
{
|
||||
void
|
||||
findallfiles(const std::vector<std::string> &basepaths,
|
||||
const fs::path &fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
std::vector<std::string> *paths);
|
||||
}
|
||||
|
||||
+14
-4
@@ -30,14 +30,14 @@
|
||||
static
|
||||
int
|
||||
_findonfs(const Branches::Ptr &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const int fd_,
|
||||
std::string *basepath_)
|
||||
{
|
||||
int rv;
|
||||
dev_t dev;
|
||||
struct stat st;
|
||||
fs::path fullpath;
|
||||
fs::relpath fullpath = fusepath_;
|
||||
|
||||
rv = fs::fstat(fd_,&st);
|
||||
if(rv < 0)
|
||||
@@ -46,7 +46,7 @@ _findonfs(const Branches::Ptr &branches_,
|
||||
dev = st.st_dev;
|
||||
for(const auto &branch : *branches_)
|
||||
{
|
||||
fullpath = branch.path / fusepath_;
|
||||
fullpath.set_prefix(branch.path);
|
||||
|
||||
rv = fs::lstat(fullpath,&st);
|
||||
if(rv < 0)
|
||||
@@ -70,5 +70,15 @@ fs::findonfs(const Branches::Ptr &branches_,
|
||||
const int fd_,
|
||||
std::string *basepath_)
|
||||
{
|
||||
return ::_findonfs(branches_,fusepath_,fd_,basepath_);
|
||||
// The public API accepts std::string for caller convenience.
|
||||
// Internally we need a canonical-rel fusepath (no leading '/') so
|
||||
// the per-branch loop's set_prefix() produces a single separator.
|
||||
// Strip the leading '/' by hand if present.
|
||||
std::string_view sv(fusepath_);
|
||||
if(!sv.empty() && sv.front() == '/')
|
||||
sv.remove_prefix(1);
|
||||
|
||||
fs::relpath fusepath(sv);
|
||||
|
||||
return ::_findonfs(branches_,fusepath,fd_,basepath_);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
// Public API takes std::string for caller convenience. Internally
|
||||
// we strip any leading '/' so the per-branch loop's set_prefix()
|
||||
// produces a single separator; this entry point handles the
|
||||
// conversion.
|
||||
int
|
||||
findonfs(const Branches::Ptr &branches,
|
||||
const std::string &fusepath,
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
fstatat(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const fs::relpath &pathname_,
|
||||
struct stat *statbuf_,
|
||||
const int flags_)
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
fstatat_nofollow(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const fs::relpath &pathname_,
|
||||
struct stat *statbuf_)
|
||||
{
|
||||
return fs::fstatat(dirfd_,
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
static
|
||||
inline
|
||||
bool
|
||||
_can_call_lutimes(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const int flags_)
|
||||
_can_call_lutimes(const int dirfd_,
|
||||
const char *path_,
|
||||
const int flags_)
|
||||
{
|
||||
return ((flags_ == AT_SYMLINK_NOFOLLOW) &&
|
||||
((dirfd_ == AT_FDCWD) ||
|
||||
@@ -117,7 +117,7 @@ static
|
||||
inline
|
||||
int
|
||||
_set_utime_omit_to_current_value(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const char *path_,
|
||||
const struct timespec ts_[2],
|
||||
struct timeval tv_[2],
|
||||
const int flags_)
|
||||
@@ -203,7 +203,7 @@ static
|
||||
inline
|
||||
int
|
||||
_convert_timespec_to_timeval(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const char *path_,
|
||||
const struct timespec ts_[2],
|
||||
struct timeval tv_[2],
|
||||
struct timeval **tvp_,
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
#include "fs_statvfs.hpp"
|
||||
#include "statvfs_util.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
bool
|
||||
fs::has_space(const std::string &str_,
|
||||
cs64 size_)
|
||||
fs::has_space(const char *str_,
|
||||
cs64 size_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
|
||||
+11
-2
@@ -26,6 +26,15 @@
|
||||
namespace fs
|
||||
{
|
||||
bool
|
||||
has_space(const std::string &str,
|
||||
cs64 size);
|
||||
has_space(const char *str,
|
||||
cs64 size);
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
has_space(const std::string &path,
|
||||
cs64 size)
|
||||
{
|
||||
return fs::has_space(path.c_str(),size);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,13 +32,13 @@ using std::string;
|
||||
|
||||
|
||||
int
|
||||
fs::info(const string &path_,
|
||||
fs::info_t *info_)
|
||||
fs::info(const char *path_,
|
||||
fs::info_t *info_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
|
||||
rv = fs::statvfs_cache(path_.c_str(),&st);
|
||||
rv = fs::statvfs_cache(path_,&st);
|
||||
if(rv == 0)
|
||||
{
|
||||
info_->readonly = StatVFS::readonly(st);
|
||||
|
||||
+11
-2
@@ -26,6 +26,15 @@
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
info(const std::string &path,
|
||||
fs::info_t *info);
|
||||
info(const char *path,
|
||||
fs::info_t *info);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
info(const std::string &path_,
|
||||
fs::info_t *info_)
|
||||
{
|
||||
return fs::info(path_.c_str(),info_);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-43
@@ -289,11 +289,11 @@ fs::inode::get_algo(void)
|
||||
return {};
|
||||
}
|
||||
|
||||
fs::inode::ReaddirCalc::ReaddirCalc(const fs::path &branch_path_,
|
||||
const fs::path &dirpath_)
|
||||
fs::inode::ReaddirCalc::ReaddirCalc(const std::string &branch_path_,
|
||||
const fs::relpath &dirpath_)
|
||||
: _algo(::_algo_from_func(g_func.load())),
|
||||
_branch_seed(::_branch_seed(branch_path_.native())),
|
||||
_fusepath((dirpath_ / "__mergerfs__").native()),
|
||||
_branch_seed(::_branch_seed(branch_path_)),
|
||||
_fusepath(fs::relpath::concat(dirpath_,"__mergerfs__")),
|
||||
_filename_offset(_fusepath.size() - std::string_view("__mergerfs__").size())
|
||||
{
|
||||
}
|
||||
@@ -337,23 +337,11 @@ fs::inode::ReaddirCalc::calc(const char *name_,
|
||||
|
||||
u64
|
||||
fs::inode::calc(const std::string &branch_path_,
|
||||
const std::string &fusepath_,
|
||||
const mode_t mode_,
|
||||
const ino_t ino_)
|
||||
{
|
||||
return g_func.load()(branch_path_,
|
||||
fusepath_,
|
||||
mode_,
|
||||
ino_);
|
||||
}
|
||||
|
||||
u64
|
||||
fs::inode::calc(const fs::path &branch_path_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const mode_t mode_,
|
||||
const ino_t ino_)
|
||||
{
|
||||
return g_func.load()(branch_path_.native(),
|
||||
return g_func.load()(branch_path_,
|
||||
fusepath_.native(),
|
||||
mode_,
|
||||
ino_);
|
||||
@@ -361,18 +349,7 @@ fs::inode::calc(const fs::path &branch_path_,
|
||||
|
||||
void
|
||||
fs::inode::calc(const std::string &branch_path_,
|
||||
const std::string &fusepath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
st_->st_ino = calc(branch_path_,
|
||||
fusepath_,
|
||||
st_->st_mode,
|
||||
st_->st_ino);
|
||||
}
|
||||
|
||||
void
|
||||
fs::inode::calc(const fs::path &branch_path_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
st_->st_ino = calc(branch_path_,
|
||||
@@ -382,19 +359,8 @@ fs::inode::calc(const fs::path &branch_path_,
|
||||
}
|
||||
|
||||
void
|
||||
fs::inode::calc(const std::string &branch_path_,
|
||||
const std::string &fusepath_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
st_->ino = calc(branch_path_,
|
||||
fusepath_,
|
||||
st_->mode,
|
||||
st_->ino);
|
||||
}
|
||||
|
||||
void
|
||||
fs::inode::calc(const fs::path &branch_path_,
|
||||
const fs::path &fusepath_,
|
||||
fs::inode::calc(const std::string &branch_path_,
|
||||
const fs::relpath &fusepath_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
st_->ino = calc(branch_path_,
|
||||
|
||||
+6
-16
@@ -47,8 +47,8 @@ namespace fs
|
||||
class ReaddirCalc
|
||||
{
|
||||
public:
|
||||
ReaddirCalc(const fs::path &branch_path,
|
||||
const fs::path &dirpath);
|
||||
ReaddirCalc(const std::string &branch_path,
|
||||
const fs::relpath &dirpath);
|
||||
|
||||
public:
|
||||
u64 calc(const char *name,
|
||||
@@ -59,7 +59,7 @@ namespace fs
|
||||
private:
|
||||
Algo _algo;
|
||||
u64 _branch_seed;
|
||||
std::string _fusepath;
|
||||
fs::relpath _fusepath;
|
||||
std::size_t _filename_offset;
|
||||
};
|
||||
|
||||
@@ -67,25 +67,15 @@ namespace fs
|
||||
std::string get_algo(void);
|
||||
|
||||
u64 calc(const std::string &basepath,
|
||||
const std::string &fusepath,
|
||||
const mode_t mode,
|
||||
const ino_t ino);
|
||||
u64 calc(const fs::path &basepath,
|
||||
const fs::path &fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
const mode_t mode,
|
||||
const ino_t ino);
|
||||
|
||||
void calc(const std::string &basepath,
|
||||
const std::string &fusepath,
|
||||
struct stat *st);
|
||||
void calc(const fs::path &basepath,
|
||||
const fs::path &fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
struct stat *st);
|
||||
void calc(const std::string &basepath,
|
||||
const std::string &fusepath,
|
||||
struct fuse_statx *st);
|
||||
void calc(const fs::path &basepath,
|
||||
const fs::path &fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
struct fuse_statx *st);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -20,11 +20,12 @@
|
||||
|
||||
#include "fs_close.hpp"
|
||||
#include "fs_mktemp.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_statvfs.hpp"
|
||||
#include "fs_unlink.hpp"
|
||||
#include "statvfs_util.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
@@ -33,7 +34,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_mounted_rofs(const fs::path &path_)
|
||||
is_mounted_rofs(const std::string &path_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
@@ -46,7 +47,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_rofs(const fs::path &path_)
|
||||
is_rofs(const std::string &path_)
|
||||
{
|
||||
int fd;
|
||||
std::string tmp_filepath;
|
||||
@@ -64,7 +65,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_rofs_but_not_mounted_ro(const fs::path &path_)
|
||||
is_rofs_but_not_mounted_ro(const std::string &path_)
|
||||
{
|
||||
if(fs::is_mounted_rofs(path_))
|
||||
return false;
|
||||
|
||||
+10
-1
@@ -71,7 +71,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
lchown_check_on_error(const std::string &path_,
|
||||
lchown_check_on_error(const char *path_,
|
||||
const struct stat &st_)
|
||||
{
|
||||
int rv;
|
||||
@@ -94,4 +94,13 @@ namespace fs
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lchown_check_on_error(const std::string &path_,
|
||||
const struct stat &st_)
|
||||
{
|
||||
return fs::lchown_check_on_error(path_.c_str(),st_);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -30,14 +30,22 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
link(const std::string &oldpath_,
|
||||
const std::string &newpath_)
|
||||
link(const char *oldpath_,
|
||||
const char *newpath_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::link(oldpath_.c_str(),
|
||||
newpath_.c_str());
|
||||
rv = ::link(oldpath_,newpath_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
link(const std::string &oldpath_,
|
||||
const std::string &newpath_)
|
||||
{
|
||||
return fs::link(oldpath_.c_str(),newpath_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -29,17 +29,26 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
lremovexattr(const std::string &path_,
|
||||
const char *attrname_)
|
||||
lremovexattr(const char *path_,
|
||||
const char *attrname_)
|
||||
{
|
||||
#ifdef USE_XATTR
|
||||
int rv;
|
||||
|
||||
rv = ::lremovexattr(path_.c_str(),attrname_);
|
||||
rv = ::lremovexattr(path_,attrname_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lremovexattr(const std::string &path_,
|
||||
const char *attrname_)
|
||||
{
|
||||
return fs::lremovexattr(path_.c_str(),attrname_);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -36,8 +36,8 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
lstatvfs(const std::string &path_,
|
||||
struct statvfs *st_)
|
||||
lstatvfs(const char *path_,
|
||||
struct statvfs *st_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
@@ -52,4 +52,13 @@ namespace fs
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lstatvfs(const std::string &path_,
|
||||
struct statvfs *st_)
|
||||
{
|
||||
return fs::lstatvfs(path_.c_str(),st_);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-2
@@ -27,7 +27,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
lutimens(const std::string &path_,
|
||||
lutimens(const char *path_,
|
||||
const struct timespec ts_[2])
|
||||
{
|
||||
return fs::utimensat(AT_FDCWD,path_,ts_,AT_SYMLINK_NOFOLLOW);
|
||||
@@ -36,7 +36,16 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
lutimens(const std::string &path_,
|
||||
lutimens(const std::string &path_,
|
||||
const struct timespec ts_[2])
|
||||
{
|
||||
return fs::lutimens(path_.c_str(),ts_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lutimens(const char *path_,
|
||||
const struct stat &st_)
|
||||
{
|
||||
struct timespec ts[2];
|
||||
@@ -46,4 +55,13 @@ namespace fs
|
||||
|
||||
return fs::lutimens(path_,ts);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lutimens(const std::string &path_,
|
||||
const struct stat &st_)
|
||||
{
|
||||
return fs::lutimens(path_.c_str(),st_);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir(const fs::path &path_,
|
||||
mkdir(const fs::relpath &path_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
return fs::mkdir(path_.c_str(),
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
mkdirat(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const fs::relpath &pathname_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
return fs::mkdirat(dirfd_,
|
||||
|
||||
+31
-21
@@ -28,7 +28,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
@@ -39,9 +38,9 @@ static char const CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno
|
||||
static size_t const CHARS_SIZE = (sizeof(CHARS) - 1);
|
||||
|
||||
static
|
||||
fs::path
|
||||
_generate_tmp_path(const fs::path &dirpath_,
|
||||
const std::string &src_filename_)
|
||||
std::string
|
||||
_generate_tmp_path(const std::string &dirpath_,
|
||||
const std::string_view src_filename_)
|
||||
{
|
||||
long name_max;
|
||||
size_t substr_len;
|
||||
@@ -54,24 +53,25 @@ _generate_tmp_path(const fs::path &dirpath_,
|
||||
substr_len = std::min(src_filename_.size(),
|
||||
(size_t)(name_max - PAD_LEN - 2ULL));
|
||||
|
||||
tmp_filename = '.';
|
||||
tmp_filename += src_filename_.substr(0,substr_len);
|
||||
tmp_filename = dirpath_;
|
||||
tmp_filename += "/.";
|
||||
tmp_filename.append(src_filename_.substr(0,substr_len));
|
||||
tmp_filename += '_';
|
||||
for(size_t i = 0; i < PAD_LEN; i++)
|
||||
tmp_filename += CHARS[RND::rand64(CHARS_SIZE)];
|
||||
|
||||
return dirpath_ / tmp_filename;
|
||||
return tmp_filename;
|
||||
}
|
||||
|
||||
std::tuple<int,fs::path>
|
||||
fs::mktemp_in_dir(const fs::path &dirpath_,
|
||||
const fs::path &filename_,
|
||||
const int flags_)
|
||||
std::tuple<int,std::string>
|
||||
fs::mktemp_in_dir(const std::string &dirpath_,
|
||||
const std::string_view filename_,
|
||||
const int flags_)
|
||||
{
|
||||
int fd;
|
||||
int count;
|
||||
int flags;
|
||||
fs::path tmp_filepath;
|
||||
std::string tmp_filepath;
|
||||
|
||||
count = MAX_ATTEMPTS;
|
||||
flags = (flags_ | O_EXCL | O_CREAT);
|
||||
@@ -83,19 +83,29 @@ fs::mktemp_in_dir(const fs::path &dirpath_,
|
||||
if(fd == -EEXIST)
|
||||
continue;
|
||||
if(fd < 0)
|
||||
return std::make_tuple(fd,fs::path());
|
||||
return std::make_tuple(fd,std::string());
|
||||
|
||||
return std::make_tuple(fd,tmp_filepath);
|
||||
return std::make_tuple(fd,std::move(tmp_filepath));
|
||||
}
|
||||
|
||||
return std::make_tuple(-EEXIST,fs::path());
|
||||
return std::make_tuple(-EEXIST,std::string());
|
||||
}
|
||||
|
||||
std::tuple<int,fs::path>
|
||||
fs::mktemp(const fs::path &filepath_,
|
||||
const int flags_)
|
||||
std::tuple<int,std::string>
|
||||
fs::mktemp(const std::string &filepath_,
|
||||
const int flags_)
|
||||
{
|
||||
return fs::mktemp_in_dir(filepath_.parent_path(),
|
||||
filepath_.filename(),
|
||||
flags_);
|
||||
// Split filepath_ into dirpath and filename without an allocation:
|
||||
// the dirpath is referenced as a substring; mktemp_in_dir copies
|
||||
// it into the result. filename_view aliases the suffix.
|
||||
auto slash = filepath_.rfind('/');
|
||||
std::string dirpath = (slash == std::string::npos)
|
||||
? std::string(".")
|
||||
: filepath_.substr(0,slash);
|
||||
std::string_view filename_view =
|
||||
(slash == std::string::npos)
|
||||
? std::string_view(filepath_)
|
||||
: std::string_view(filepath_).substr(slash + 1);
|
||||
|
||||
return fs::mktemp_in_dir(dirpath,filename_view,flags_);
|
||||
}
|
||||
|
||||
+8
-9
@@ -18,20 +18,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
std::tuple<int,fs::path>
|
||||
mktemp_in_dir(const fs::path &dirpath,
|
||||
const fs::path &filename,
|
||||
int const flags);
|
||||
std::tuple<int,std::string>
|
||||
mktemp_in_dir(const std::string &dirpath,
|
||||
const std::string_view filename,
|
||||
int const flags);
|
||||
|
||||
std::tuple<int,fs::path>
|
||||
mktemp(const fs::path &filepath,
|
||||
const int flags);
|
||||
std::tuple<int,std::string>
|
||||
mktemp(const std::string &filepath,
|
||||
const int flags);
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
int
|
||||
fs::mount(const std::string &tgt_)
|
||||
fs::mount(const char *tgt_)
|
||||
{
|
||||
return subprocess::call({"mount",tgt_});
|
||||
}
|
||||
|
||||
+10
-1
@@ -20,7 +20,16 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int mount(const std::string &tgt);
|
||||
int mount(const char *tgt);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
mount(const std::string &tgt_)
|
||||
{
|
||||
return fs::mount(tgt_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -18,8 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -27,7 +26,7 @@ namespace fs
|
||||
{
|
||||
struct Mount
|
||||
{
|
||||
fs::path dir;
|
||||
std::string dir;
|
||||
std::string fsname;
|
||||
std::string type;
|
||||
std::string opts;
|
||||
|
||||
@@ -63,22 +63,19 @@ static
|
||||
int
|
||||
_movefile_and_open(const Policy::Create &createFunc_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &branchpath_,
|
||||
const fs::path &fusepath_,
|
||||
const std::string &branchpath_,
|
||||
const fs::relpath &fusepath_,
|
||||
int origfd_)
|
||||
{
|
||||
int rv;
|
||||
int dstfd_flags;
|
||||
int origfd_flags;
|
||||
s64 src_size;
|
||||
fs::path fusedir;
|
||||
fs::path src_branch;
|
||||
fs::path src_filepath;
|
||||
fs::path dst_filepath;
|
||||
fs::relpath fusedir;
|
||||
fs::relpath src_filepath;
|
||||
fs::relpath dst_filepath;
|
||||
std::vector<Branch*> dst_branch;
|
||||
|
||||
src_branch = branchpath_;
|
||||
|
||||
rv = createFunc_(branches_,fusepath_,dst_branch);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
@@ -98,12 +95,17 @@ _movefile_and_open(const Policy::Create &createFunc_,
|
||||
|
||||
fusedir = fusepath_.parent_path();
|
||||
|
||||
rv = fs::clonepath(src_branch,dst_branch[0]->path,fusedir);
|
||||
rv = fs::clonepath(branchpath_,dst_branch[0]->path,fusedir);
|
||||
if(rv < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
src_filepath = src_branch / fusepath_;
|
||||
dst_filepath = dst_branch[0]->path / fusepath_;
|
||||
// src_filepath / dst_filepath are fs::relpath in the per-branch
|
||||
// splice form: rel anchored, prefix written via set_prefix() so the
|
||||
// syscall sees branch + '/' + fusepath as a single string.
|
||||
src_filepath = fusepath_;
|
||||
src_filepath.set_prefix(branchpath_);
|
||||
dst_filepath = fusepath_;
|
||||
dst_filepath.set_prefix(dst_branch[0]->path);
|
||||
|
||||
rv = fs::copyfile(src_filepath,dst_filepath,{.cleanup_failure = true});
|
||||
if(rv < 0)
|
||||
@@ -122,8 +124,8 @@ _movefile_and_open(const Policy::Create &createFunc_,
|
||||
int
|
||||
fs::movefile_and_open(const Policy::Create &policy_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &branchpath_,
|
||||
const fs::path &fusepath_,
|
||||
const std::string &branchpath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const int origfd_)
|
||||
{
|
||||
return ::_movefile_and_open(policy_,
|
||||
@@ -136,8 +138,8 @@ fs::movefile_and_open(const Policy::Create &policy_,
|
||||
int
|
||||
fs::movefile_and_open_as_root(const Policy::Create &policy_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &branchpath_,
|
||||
const fs::path &fusepath_,
|
||||
const std::string &branchpath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const int origfd_)
|
||||
{
|
||||
return fs::movefile_and_open(policy_,
|
||||
|
||||
@@ -30,14 +30,14 @@ namespace fs
|
||||
int
|
||||
movefile_and_open(const Policy::Create &policy,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &branchpath,
|
||||
const fs::path &fusepath,
|
||||
const std::string &branchpath,
|
||||
const fs::relpath &fusepath,
|
||||
const int origfd);
|
||||
|
||||
int
|
||||
movefile_and_open_as_root(const Policy::Create &policy,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &branchpath,
|
||||
const fs::path &fusepath,
|
||||
const std::string &branchpath,
|
||||
const fs::relpath &fusepath,
|
||||
int origfd);
|
||||
}
|
||||
|
||||
+9
-1
@@ -78,8 +78,16 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
open_dir_ro(const std::string &path_)
|
||||
open_dir_ro(const char *path_)
|
||||
{
|
||||
return fs::open(path_,O_RDONLY|O_DIRECTORY);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
open_dir_ro(const std::string &path_)
|
||||
{
|
||||
return fs::open(path_.c_str(),O_RDONLY|O_DIRECTORY);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
open_as(const ugid_t ugid_,
|
||||
const fs::path &path_,
|
||||
const fs::relpath &path_,
|
||||
const int flags_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
open_as(const ugid_t ugid_,
|
||||
const fs::path &path_,
|
||||
const fs::relpath &path_,
|
||||
const int flags_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
openat(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const fs::relpath &pathname_,
|
||||
const int flags_,
|
||||
const mode_t mode_ = 0)
|
||||
{
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2026, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fs_path.hpp"
|
||||
@@ -73,8 +73,8 @@ fs::readahead(cu64 dev_,
|
||||
}
|
||||
|
||||
int
|
||||
fs::readahead(const std::string path_,
|
||||
cu64 size_in_kb_)
|
||||
fs::readahead(const char *path_,
|
||||
cu64 size_in_kb_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "base_types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@@ -35,6 +33,6 @@ namespace fs
|
||||
cu64 size_in_kb);
|
||||
|
||||
int
|
||||
readahead(const std::string path,
|
||||
cu64 size_in_kb);
|
||||
readahead(const char *path,
|
||||
cu64 size_in_kb);
|
||||
}
|
||||
|
||||
+15
-5
@@ -27,6 +27,20 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
ssize_t
|
||||
readlink(const char *path_,
|
||||
char *buf_,
|
||||
const size_t bufsiz_)
|
||||
{
|
||||
ssize_t rv;
|
||||
|
||||
rv = ::readlink(path_,buf_,bufsiz_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
ssize_t
|
||||
@@ -34,10 +48,6 @@ namespace fs
|
||||
char *buf_,
|
||||
const size_t bufsiz_)
|
||||
{
|
||||
ssize_t rv;
|
||||
|
||||
rv = ::readlink(path_.c_str(),buf_,bufsiz_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
return fs::readlink(path_.c_str(),buf_,bufsiz_);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-7
@@ -18,9 +18,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
@@ -29,16 +26,16 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
char *
|
||||
realpath(const std::string &path_,
|
||||
char *resolved_path_)
|
||||
realpath(const char *path_,
|
||||
char *resolved_path_)
|
||||
{
|
||||
return ::realpath(path_.c_str(),resolved_path_);
|
||||
return ::realpath(path_,resolved_path_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
char *
|
||||
realpath(const std::string &path_)
|
||||
realpath(const char *path_)
|
||||
{
|
||||
return fs::realpath(path_,NULL);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
void
|
||||
fs::realpathize(std::vector<std::string> *strs_)
|
||||
@@ -32,10 +34,8 @@ fs::realpathize(std::vector<std::string> *strs_)
|
||||
|
||||
for(size_t i = 0; i < strs_->size(); i++)
|
||||
{
|
||||
rv = fs::realpath((*strs_)[i],resolved_path);
|
||||
if(rv == NULL)
|
||||
continue;
|
||||
|
||||
(*strs_)[i] = rv;
|
||||
rv = fs::realpath((*strs_)[i].c_str(),resolved_path);
|
||||
if(rv != NULL)
|
||||
(*strs_)[i] = rv;
|
||||
}
|
||||
}
|
||||
|
||||
+56
-25
@@ -21,8 +21,8 @@
|
||||
#include "fs_statvfs.hpp"
|
||||
#include "statvfs_util.hpp"
|
||||
|
||||
#include "mutex.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -36,11 +36,32 @@ struct Element
|
||||
struct statvfs st;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string,Element> statvfs_cache;
|
||||
// Transparent hash / equality so we can look up by std::string_view
|
||||
// (or const char*) without constructing a std::string. Requires
|
||||
// unordered_map's Hash and KeyEqual to be transparent (C++20). The
|
||||
// insert path still constructs a string for the key when missing,
|
||||
// but the hot read path is alloc-free.
|
||||
struct string_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
std::size_t operator()(const std::string &s) const noexcept { return std::hash<std::string_view>{}(s); }
|
||||
std::size_t operator()(std::string_view s) const noexcept { return std::hash<std::string_view>{}(s); }
|
||||
std::size_t operator()(const char *s) const noexcept { return std::hash<std::string_view>{}(s); }
|
||||
};
|
||||
|
||||
static u64 g_timeout = 0;
|
||||
static statvfs_cache g_cache;
|
||||
static Mutex g_cache_lock;
|
||||
struct string_eq
|
||||
{
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const noexcept { return a == b; }
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string,Element,string_hash,string_eq> statvfs_cache;
|
||||
|
||||
// Cache is read heavy. Use a shared mutex so concurrent reads can
|
||||
// work in parallel and only inserts or refreshes take a unique lock.
|
||||
static std::shared_mutex g_cache_mutex;
|
||||
static u64 g_timeout = 0;
|
||||
static statvfs_cache g_cache;
|
||||
|
||||
|
||||
static
|
||||
@@ -67,39 +88,49 @@ fs::statvfs_cache_timeout(cu64 timeout_)
|
||||
}
|
||||
|
||||
int
|
||||
fs::statvfs_cache(const std::string &path_,
|
||||
struct statvfs *st_)
|
||||
fs::statvfs_cache(const char *path_,
|
||||
struct statvfs *st_)
|
||||
{
|
||||
int rv;
|
||||
Element *e;
|
||||
u64 now;
|
||||
|
||||
if(g_timeout == 0)
|
||||
return fs::statvfs(path_,st_);
|
||||
|
||||
rv = 0;
|
||||
now = ::_get_time();
|
||||
|
||||
mutex_lock(g_cache_lock);
|
||||
std::string_view key(path_);
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lk(g_cache_mutex);
|
||||
|
||||
e = &g_cache[path_];
|
||||
auto it = g_cache.find(key);
|
||||
if((it != g_cache.end()) && ((now - it->second.time) <= g_timeout))
|
||||
{
|
||||
*st_ = it->second.st;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if((now - e->time) > g_timeout)
|
||||
int rv = 0;
|
||||
std::unique_lock<std::shared_mutex> lk(g_cache_mutex);
|
||||
|
||||
// operator[] requires a string key (no transparent insert overload),
|
||||
// so this path does construct a std::string. Only happens on insert /
|
||||
// refresh -- the read-only fast path above is alloc-free.
|
||||
Element &e = g_cache[std::string(key)];
|
||||
if((now - e.time) > g_timeout)
|
||||
{
|
||||
e->time = now;
|
||||
rv = fs::statvfs(path_,&e->st);
|
||||
e.time = now;
|
||||
rv = fs::statvfs(path_,&e.st);
|
||||
}
|
||||
|
||||
*st_ = e->st;
|
||||
|
||||
mutex_unlock(g_cache_lock);
|
||||
*st_ = e.st;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
fs::statvfs_cache_readonly(const std::string &path_,
|
||||
bool *readonly_)
|
||||
fs::statvfs_cache_readonly(const char *path_,
|
||||
bool *readonly_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
@@ -112,8 +143,8 @@ fs::statvfs_cache_readonly(const std::string &path_,
|
||||
}
|
||||
|
||||
int
|
||||
fs::statvfs_cache_spaceavail(const std::string &path_,
|
||||
u64 *spaceavail_)
|
||||
fs::statvfs_cache_spaceavail(const char *path_,
|
||||
u64 *spaceavail_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
@@ -126,8 +157,8 @@ fs::statvfs_cache_spaceavail(const std::string &path_,
|
||||
}
|
||||
|
||||
int
|
||||
fs::statvfs_cache_spaceused(const std::string &path_,
|
||||
u64 *spaceused_)
|
||||
fs::statvfs_cache_spaceused(const char *path_,
|
||||
u64 *spaceused_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
|
||||
@@ -33,18 +33,54 @@ namespace fs
|
||||
statvfs_cache_timeout(cu64 timeout);
|
||||
|
||||
int
|
||||
statvfs_cache(const std::string &path,
|
||||
struct statvfs *st);
|
||||
statvfs_cache(const char *path,
|
||||
struct statvfs *st);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statvfs_cache(const std::string &path_,
|
||||
struct statvfs *st_)
|
||||
{
|
||||
return fs::statvfs_cache(path_.c_str(),st_);
|
||||
}
|
||||
|
||||
int
|
||||
statvfs_cache_readonly(const std::string &path,
|
||||
bool *readonly);
|
||||
statvfs_cache_readonly(const char *path,
|
||||
bool *readonly);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statvfs_cache_readonly(const std::string &path_,
|
||||
bool *readonly_)
|
||||
{
|
||||
return fs::statvfs_cache_readonly(path_.c_str(),readonly_);
|
||||
}
|
||||
|
||||
int
|
||||
statvfs_cache_spaceavail(const std::string &path,
|
||||
u64 *spaceavail);
|
||||
statvfs_cache_spaceavail(const char *path,
|
||||
u64 *spaceavail);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statvfs_cache_spaceavail(const std::string &path_,
|
||||
u64 *spaceavail_)
|
||||
{
|
||||
return fs::statvfs_cache_spaceavail(path_.c_str(),spaceavail_);
|
||||
}
|
||||
|
||||
int
|
||||
statvfs_cache_spaceused(const std::string &path,
|
||||
u64 *spaceused);
|
||||
statvfs_cache_spaceused(const char *path,
|
||||
u64 *spaceused);
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
statvfs_cache_spaceused(const std::string &path_,
|
||||
u64 *spaceused_)
|
||||
{
|
||||
return fs::statvfs_cache_spaceused(path_.c_str(),spaceused_);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -21,6 +21,8 @@
|
||||
#include "errno.hpp"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/param.h>
|
||||
# include <sys/mount.h>
|
||||
@@ -30,19 +32,18 @@
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
umount2(const std::string target_,
|
||||
const int flags_)
|
||||
umount2(const char *target_,
|
||||
const int flags_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::umount2(target_.c_str(),
|
||||
rv = ::umount2(target_,
|
||||
flags_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
@@ -51,8 +52,16 @@ namespace fs
|
||||
static
|
||||
inline
|
||||
int
|
||||
umount_lazy(const std::string target_)
|
||||
umount_lazy(const char *target_)
|
||||
{
|
||||
return fs::umount2(target_,MNT_DETACH);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
umount_lazy(const std::string &target_)
|
||||
{
|
||||
return fs::umount_lazy(target_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
static
|
||||
inline
|
||||
bool
|
||||
_can_call_lutimes(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const int flags_)
|
||||
_can_call_lutimes(const int dirfd_,
|
||||
const char *path_,
|
||||
const int flags_)
|
||||
{
|
||||
return ((flags_ == AT_SYMLINK_NOFOLLOW) &&
|
||||
((dirfd_ == AT_FDCWD) ||
|
||||
@@ -120,7 +120,7 @@ static
|
||||
inline
|
||||
int
|
||||
_set_utime_omit_to_current_value(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const char *path_,
|
||||
const struct timespec ts_[2],
|
||||
struct timeval tv_[2],
|
||||
const int flags_)
|
||||
@@ -206,7 +206,7 @@ static
|
||||
inline
|
||||
int
|
||||
_convert_timespec_to_timeval(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const char *path_,
|
||||
const struct timespec ts_[2],
|
||||
struct timeval tv_[2],
|
||||
struct timeval **tvp_,
|
||||
@@ -265,7 +265,7 @@ namespace fs
|
||||
inline
|
||||
int
|
||||
utimensat(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const char *path_,
|
||||
const struct timespec ts_[2],
|
||||
const int flags_)
|
||||
{
|
||||
@@ -291,4 +291,15 @@ namespace fs
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
utimensat(const int dirfd_,
|
||||
const std::string &path_,
|
||||
const struct timespec ts_[2],
|
||||
const int flags_)
|
||||
{
|
||||
return fs::utimensat(dirfd_,path_.c_str(),ts_,flags_);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -36,17 +36,17 @@ constexpr std::chrono::milliseconds SLEEP_DURATION = std::chrono::milliseconds(3
|
||||
static
|
||||
bool
|
||||
_branch_is_mounted(const struct stat &src_st_,
|
||||
const fs::path &branch_path_)
|
||||
const std::string &branch_path_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
fs::path filepath;
|
||||
std::string filepath;
|
||||
|
||||
rv = fs::lgetxattr(branch_path_,"user.mergerfs.branch",NULL,0);
|
||||
if(rv >= 0)
|
||||
return true;
|
||||
|
||||
filepath = branch_path_ / ".mergerfs.branch";
|
||||
filepath = branch_path_ + "/.mergerfs.branch";
|
||||
rv = fs::exists(filepath);
|
||||
if(rv)
|
||||
return true;
|
||||
@@ -55,7 +55,7 @@ _branch_is_mounted(const struct stat &src_st_,
|
||||
if(rv >= 0)
|
||||
return false;
|
||||
|
||||
filepath = branch_path_ / ".mergerfs.branch_mounts_here";
|
||||
filepath = branch_path_ + "/.mergerfs.branch_mounts_here";
|
||||
rv = fs::exists(filepath);
|
||||
if(rv)
|
||||
return false;
|
||||
@@ -69,13 +69,13 @@ _branch_is_mounted(const struct stat &src_st_,
|
||||
|
||||
static
|
||||
void
|
||||
_check_mounted(const struct stat &src_st_,
|
||||
const std::set<fs::path> &tgt_paths_,
|
||||
std::vector<fs::path> *successes_,
|
||||
std::vector<fs::path> *failures_)
|
||||
_check_mounted(const struct stat &src_st_,
|
||||
const std::set<std::string> &tgt_paths_,
|
||||
std::vector<std::string> *successes_,
|
||||
std::vector<std::string> *failures_)
|
||||
{
|
||||
std::vector<fs::path> &successes = *successes_;
|
||||
std::vector<fs::path> &failures = *failures_;
|
||||
std::vector<std::string> &successes = *successes_;
|
||||
std::vector<std::string> &failures = *failures_;
|
||||
|
||||
for(auto const &tgt_path : tgt_paths_)
|
||||
{
|
||||
@@ -92,13 +92,13 @@ _check_mounted(const struct stat &src_st_,
|
||||
static
|
||||
int
|
||||
_wait_for_mount(const struct stat &src_st_,
|
||||
const std::vector<fs::path> &tgt_paths_,
|
||||
const std::vector<std::string> &tgt_paths_,
|
||||
const std::chrono::milliseconds &timeout_)
|
||||
{
|
||||
bool first_loop;
|
||||
std::vector<fs::path> successes;
|
||||
std::vector<fs::path> failures;
|
||||
std::set<fs::path> tgt_paths;
|
||||
std::vector<std::string> successes;
|
||||
std::vector<std::string> failures;
|
||||
std::set<std::string> tgt_paths;
|
||||
std::chrono::time_point<std::chrono::steady_clock> now;
|
||||
std::chrono::time_point<std::chrono::steady_clock> deadline;
|
||||
|
||||
@@ -120,13 +120,13 @@ _wait_for_mount(const struct stat &src_st_,
|
||||
for(const auto &path : successes)
|
||||
{
|
||||
tgt_paths.erase(path);
|
||||
SysLog::info("{} is ready",path.string());
|
||||
SysLog::info("{} is ready",path);
|
||||
}
|
||||
|
||||
if(first_loop)
|
||||
{
|
||||
for(const auto &path : failures)
|
||||
SysLog::notice("{} is not ready, waiting",path.string());
|
||||
SysLog::notice("{} is not ready, waiting",path);
|
||||
first_loop = false;
|
||||
}
|
||||
|
||||
@@ -135,14 +135,14 @@ _wait_for_mount(const struct stat &src_st_,
|
||||
}
|
||||
|
||||
for(const auto &path : failures)
|
||||
SysLog::notice("{} not ready within timeout",path.string());
|
||||
SysLog::notice("{} not ready within timeout",path);
|
||||
|
||||
return failures.size();
|
||||
}
|
||||
|
||||
int
|
||||
fs::wait_for_mount(const fs::path &src_path_,
|
||||
const std::vector<fs::path> &tgt_paths_,
|
||||
fs::wait_for_mount(const std::string &src_path_,
|
||||
const std::vector<std::string> &tgt_paths_,
|
||||
const std::chrono::milliseconds &timeout_)
|
||||
{
|
||||
int rv;
|
||||
@@ -151,7 +151,7 @@ fs::wait_for_mount(const fs::path &src_path_,
|
||||
rv = fs::stat(src_path_,&src_st);
|
||||
if(rv < 0)
|
||||
SysLog::error("Error stat'ing mount path: {} ({})",
|
||||
src_path_.string(),
|
||||
src_path_,
|
||||
::strerror(-rv));
|
||||
|
||||
for(auto &tgt_path : tgt_paths_)
|
||||
@@ -163,7 +163,7 @@ fs::wait_for_mount(const fs::path &src_path_,
|
||||
|
||||
rv = fs::mount(tgt_path);
|
||||
SysLog::notice("mount {}: {}",
|
||||
tgt_path.string(),
|
||||
tgt_path,
|
||||
((rv == 0) ? "success" : "fail"));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
wait_for_mount(const fs::path &srcpath,
|
||||
const std::vector<fs::path> &tgtpaths,
|
||||
wait_for_mount(const std::string &srcpath,
|
||||
const std::vector<std::string> &tgtpaths,
|
||||
const std::chrono::milliseconds &timeout);
|
||||
}
|
||||
|
||||
+5
-6
@@ -31,12 +31,12 @@ static
|
||||
int
|
||||
_access(const Policy::Search &searchFunc_,
|
||||
const Branches::Ptr branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const int mask_)
|
||||
{
|
||||
int rv;
|
||||
StrVec basepaths;
|
||||
fs::path fullpath;
|
||||
fs::relpath fullpath;
|
||||
std::vector<Branch*> branches;
|
||||
|
||||
rv = searchFunc_(branches_,fusepath_,branches);
|
||||
@@ -45,7 +45,7 @@ _access(const Policy::Search &searchFunc_,
|
||||
if(branches.empty())
|
||||
return -ENOENT;
|
||||
|
||||
fullpath = branches[0]->path / fusepath_;
|
||||
fullpath.assign_concat(branches[0]->path,fusepath_);
|
||||
|
||||
rv = fs::eaccess(fullpath,mask_);
|
||||
|
||||
@@ -54,13 +54,12 @@ _access(const Policy::Search &searchFunc_,
|
||||
|
||||
int
|
||||
FUSE::access(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
int mask_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_access(cfg.func.access.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
fusepath_,
|
||||
mask_);
|
||||
}
|
||||
|
||||
+2
-1
@@ -19,11 +19,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
access(const fuse_req_ctx_t *ctx,
|
||||
const char *fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
int mask);
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
int
|
||||
FUSE::bmap(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
size_t blocksize_,
|
||||
u64 *idx_)
|
||||
{
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "base_types.h"
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace FUSE
|
||||
{
|
||||
int
|
||||
bmap(const fuse_req_ctx_t *ctx,
|
||||
const char *fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
size_t blocksize,
|
||||
u64 *idx);
|
||||
}
|
||||
|
||||
+14
-24
@@ -30,33 +30,24 @@
|
||||
#include <string>
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_chmod_loop_core(const std::string &basepath_,
|
||||
const fs::path &fusepath_,
|
||||
const mode_t mode_,
|
||||
PolicyRV *prv_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
|
||||
fullpath = basepath_ / fusepath_;
|
||||
|
||||
rv = fs::lchmod(fullpath,mode_);
|
||||
|
||||
prv_->insert(rv,basepath_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_chmod_loop(const std::vector<Branch*> &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const mode_t mode_,
|
||||
PolicyRV *prv_)
|
||||
{
|
||||
fs::relpath fullpath = fusepath_;
|
||||
|
||||
for(auto &branch : branches_)
|
||||
{
|
||||
::_chmod_loop_core(branch->path,fusepath_,mode_,prv_);
|
||||
int rv;
|
||||
|
||||
fullpath.set_prefix(branch->path);
|
||||
|
||||
rv = fs::lchmod(fullpath,mode_);
|
||||
|
||||
prv_->insert(rv,branch->path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +56,7 @@ int
|
||||
_chmod(const Policy::Action &actionFunc_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Branches::Ptr branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
@@ -96,7 +87,7 @@ _chmod(const Policy::Action &actionFunc_,
|
||||
|
||||
static
|
||||
int
|
||||
_chmod(const fs::path &fusepath_,
|
||||
_chmod(const fs::relpath &fusepath_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
|
||||
@@ -109,10 +100,9 @@ _chmod(const fs::path &fusepath_,
|
||||
|
||||
int
|
||||
FUSE::chmod(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
mode_t mode_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_chmod(fusepath,mode_);
|
||||
return ::_chmod(fusepath_,mode_);
|
||||
}
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -27,6 +28,6 @@ namespace FUSE
|
||||
{
|
||||
int
|
||||
chmod(const fuse_req_ctx_t *ctx,
|
||||
const char *fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
mode_t mode);
|
||||
}
|
||||
|
||||
+13
-24
@@ -30,35 +30,25 @@
|
||||
#include <vector>
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_chown_loop_core(const fs::path &basepath_,
|
||||
const fs::path &fusepath_,
|
||||
const uid_t uid_,
|
||||
const gid_t gid_,
|
||||
PolicyRV *prv_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
|
||||
fullpath = basepath_ / fusepath_;
|
||||
|
||||
rv = fs::lchown(fullpath,uid_,gid_);
|
||||
|
||||
prv_->insert(rv,basepath_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_chown_loop(const std::vector<Branch*> &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const uid_t uid_,
|
||||
const gid_t gid_,
|
||||
PolicyRV *prv_)
|
||||
{
|
||||
fs::relpath fullpath = fusepath_;
|
||||
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
::_chown_loop_core(branch->path,fusepath_,uid_,gid_,prv_);
|
||||
int rv;
|
||||
|
||||
fullpath.set_prefix(branch->path);
|
||||
|
||||
rv = fs::lchown(fullpath,uid_,gid_);
|
||||
|
||||
prv_->insert(rv,branch->path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +57,7 @@ int
|
||||
_chown(const Policy::Action &actionFunc_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Branches::Ptr branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
{
|
||||
@@ -100,16 +90,15 @@ _chown(const Policy::Action &actionFunc_,
|
||||
|
||||
int
|
||||
FUSE::chown(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
uid_t uid_,
|
||||
gid_t gid_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_chown(cfg.func.chown.policy,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
fusepath_,
|
||||
uid_,
|
||||
gid_);
|
||||
}
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -27,7 +28,7 @@ namespace FUSE
|
||||
{
|
||||
int
|
||||
chown(const fuse_req_ctx_t *ctx,
|
||||
const char *fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
uid_t uid,
|
||||
gid_t gid);
|
||||
}
|
||||
|
||||
+11
-12
@@ -165,7 +165,7 @@ _config_to_ffi_flags(const int tid_,
|
||||
static
|
||||
int
|
||||
_create_core(const ugid_t ugid_,
|
||||
const fs::path &fullpath_,
|
||||
const fs::relpath &fullpath_,
|
||||
mode_t mode_,
|
||||
const mode_t umask_,
|
||||
const int flags_)
|
||||
@@ -180,16 +180,16 @@ static
|
||||
int
|
||||
_create_core(const ugid_t ugid_,
|
||||
const Branch *branch_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
fuse_file_info_t *ffi_,
|
||||
const mode_t mode_,
|
||||
const mode_t umask_)
|
||||
{
|
||||
int rv;
|
||||
FileInfo *fi;
|
||||
fs::path fullpath;
|
||||
fs::relpath fullpath;
|
||||
|
||||
fullpath = branch_->path / fusepath_;
|
||||
fullpath.assign_concat(branch_->path,fusepath_);
|
||||
|
||||
rv = ::_create_core(ugid_,fullpath,mode_,umask_,ffi_->flags);
|
||||
if(rv < 0)
|
||||
@@ -208,14 +208,14 @@ _create(const ugid_t ugid_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches::Ptr branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
fuse_file_info_t *ffi_,
|
||||
const mode_t mode_,
|
||||
const mode_t umask_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
fs::path fusedirpath;
|
||||
fs::relpath fullpath;
|
||||
fs::relpath fusedirpath;
|
||||
std::vector<Branch*> createpaths;
|
||||
std::vector<Branch*> existingpaths;
|
||||
|
||||
@@ -258,7 +258,7 @@ _(const PassthroughIOEnum e_,
|
||||
static
|
||||
int
|
||||
_create(const fuse_req_ctx_t *ctx_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
mode_t mode_,
|
||||
fuse_file_info_t *ffi_)
|
||||
{
|
||||
@@ -334,7 +334,7 @@ _create(const fuse_req_ctx_t *ctx_,
|
||||
delete fi;
|
||||
ffi_->fh = 0;
|
||||
|
||||
SysLog::crit(msg);
|
||||
SysLog::crit("{}",msg);
|
||||
fmt::println(stderr,"{}",msg);
|
||||
|
||||
return -EIO;
|
||||
@@ -345,11 +345,10 @@ _create(const fuse_req_ctx_t *ctx_,
|
||||
|
||||
int
|
||||
FUSE::create(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
mode_t mode_,
|
||||
fuse_file_info_t *ffi_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_create(ctx_,fusepath,mode_,ffi_);
|
||||
return ::_create(ctx_,fusepath_,mode_,ffi_);
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ namespace FUSE
|
||||
{
|
||||
int
|
||||
create(const fuse_req_ctx_t *ctx,
|
||||
const char *fusepath,
|
||||
const fs::relpath &fusepath,
|
||||
mode_t mode,
|
||||
fuse_file_info_t *ffi);
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2026, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fuse_flock.hpp"
|
||||
|
||||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_flock.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
||||
static
|
||||
int
|
||||
_flock(const int fd_,
|
||||
const int operation_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::flock(fd_,operation_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// In cases where the node was opened but unlinked there will be no
|
||||
// path and no guarentee of a `fh`. It could always do the lookup but
|
||||
// why bother if the kernel has provided it to us? The reason the
|
||||
// function doesn't need to run within the visit lambda is because
|
||||
// since the request is outstanding the kernel won't be releasing the
|
||||
// node and therefore the entry will be valid over the lifetime of
|
||||
// this function.
|
||||
int
|
||||
FUSE::flock(const fuse_req_ctx_t *ctx_,
|
||||
const fuse_file_info_t *ffi_,
|
||||
int op_)
|
||||
{
|
||||
FileInfo *fi;
|
||||
|
||||
fi = state.get_fi(ctx_,ffi_->fh);
|
||||
if(not fi)
|
||||
return -EBADF;
|
||||
|
||||
return ::_flock(fi->fd,op_);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2026, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
flock(const fuse_req_ctx_t *ctx,
|
||||
const fuse_file_info_t *ffi,
|
||||
int op);
|
||||
}
|
||||
+8
-19
@@ -37,7 +37,7 @@
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const fs::path &path_,
|
||||
_set_stat_if_leads_to_dir(const fs::relpath &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
@@ -55,7 +55,7 @@ _set_stat_if_leads_to_dir(const fs::path &path_,
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const fs::path &path_,
|
||||
_set_stat_if_leads_to_reg(const fs::relpath &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
@@ -129,14 +129,14 @@ static
|
||||
int
|
||||
_getattr(const Policy::Search &searchFunc_,
|
||||
const Branches::Ptr branches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
struct stat *st_,
|
||||
const bool symlinkify_,
|
||||
const time_t symlinkify_timeout_,
|
||||
FollowSymlinks followsymlinks_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
fs::relpath fullpath;
|
||||
std::vector<Branch*> branches;
|
||||
|
||||
rv = searchFunc_(branches_,fusepath_,branches);
|
||||
@@ -145,7 +145,7 @@ _getattr(const Policy::Search &searchFunc_,
|
||||
if(branches.empty())
|
||||
return -ENOENT;
|
||||
|
||||
fullpath = branches[0]->path / fusepath_;
|
||||
fullpath.assign_concat(branches[0]->path,fusepath_);
|
||||
|
||||
switch(followsymlinks_)
|
||||
{
|
||||
@@ -183,7 +183,7 @@ _getattr(const Policy::Search &searchFunc_,
|
||||
}
|
||||
|
||||
int
|
||||
_getattr(const fs::path &fusepath_,
|
||||
_getattr(const fs::relpath &fusepath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
@@ -209,18 +209,7 @@ _getattr(const fs::path &fusepath_,
|
||||
|
||||
int
|
||||
FUSE::getattr(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return FUSE::getattr(ctx_,fusepath,st_,timeout_);
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::getattr(const fuse_req_ctx_t *ctx_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::relpath &fusepath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
@@ -228,7 +217,7 @@ FUSE::getattr(const fuse_req_ctx_t *ctx_,
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::getattr(const fs::path &fusepath_,
|
||||
FUSE::getattr(const fs::relpath &fusepath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user