From 0c0c6855c1ac85e5ed5a716a6814e118ed406324 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:51:00 +0100 Subject: [PATCH] Migrate to FUSE 3.x API (#350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Migrate to FUSE 3.x API - Update FUSE_USE_VERSION from 26 to 30 - Replace fuse_mount/fuse_unmount with fuse_session_mount/fuse_session_unmount - Update fuse_new() to take args first, remove channel parameter - Add flags parameter to readdir handler (enum fuse_readdir_flags) - Add flags parameter to rename handler (for renameat2 support) - Update filler function calls to include FUSE_FILL_DIR_PLUS flag - Merge ftruncate into truncate handler (FUSE 3 combines them) - Update fuse_loop_mt to fuse_loop_mt_31 with loop config - Link against libfuse3 instead of libfuse - Remove fuse_chan usage (deprecated in FUSE 3) Addresses #342 * Remove deprecated FUSE options nonempty and hard_remove - nonempty: Removed in FUSE 3.0, mounting on non-empty directories is now default behavior - hard_remove: High-level API option not available in FUSE 3, immediate unlink is standard These options are no longer recognized by FUSE 3 and cause mount errors. * Fix init handler signature for FUSE 3 FUSE 3 init handler requires struct fuse_config* parameter. This parameter provides access to high-level API configuration options that can be modified during initialization. Without this parameter, the init handler signature is incompatible with FUSE 3, causing filesystem operations to fail. * Add FUSE 2/3 backward compatibility Automatically detects and builds against either FUSE 2 or FUSE 3: - detect_fuse.sh script checks for fuse3 or fuse pkg-config - Makefile dynamically sets FUSE_USE_VERSION, CFLAGS, and LDFLAGS - Conditional compilation (#if FUSE_USE_VERSION >= 30) for API differences: * init handler signature (fuse_config parameter) * readdir handler signature (flags parameter) * rename handler signature (flags parameter) * truncate handler signature (fuse_file_info parameter) * filler function calls (flags parameter) * fuse_loop_mt vs fuse_loop_mt_31 * mount/unmount sequence (fuse_session_mount vs fuse_mount) * ftruncate operations struct member Maintains full compatibility with both FUSE 2.x (Debian Bookworm) and FUSE 3.x (Debian Testing+) without requiring separate code branches. * Add FORCE_FUSE build option and fix hardcoded version - Remove hardcoded FUSE_USE_VERSION defines from source files - Add FORCE_FUSE=2 or FORCE_FUSE=3 Makefile option to override detection - Fix FUSE 2 channel tracking (psync_fuse_channel variable) - Properly store and use channel in mount/unmount for FUSE 2 Usage: make # Auto-detect (prefers FUSE 3) make FORCE_FUSE=2 # Force FUSE 2 build make FORCE_FUSE=3 # Force FUSE 3 build Verified both FUSE 2 and FUSE 3 builds work correctly. * Fix CI/CD: Add fallback for systems without pkg-config - detect_fuse.sh now checks for pkg-config availability first - Falls back to checking for header files in standard locations - Makefile provides default CFLAGS when pkg-config unavailable - Fixes build on minimal CI/CD environments Fallback locations checked: - /usr/include/fuse3/fuse.h (FUSE 3) - /usr/local/include/fuse3/fuse.h (FUSE 3) - /usr/include/fuse/fuse.h (FUSE 2) - /usr/local/include/fuse/fuse.h (FUSE 2) * Enhance FUSE detection for non-standard locations Multi-stage detection strategy: 1. Try pkg-config (fastest, most reliable) 2. Search common header locations: - /usr/include, /usr/local/include - /opt/local/include, /opt/include 3. Compiler test as last resort: - Attempts to preprocess #include - Tests both FUSE 3 and FUSE 2 versions This handles: - Minimal CI/CD environments (no pkg-config) - Non-standard install locations (Homebrew, custom builds) - Distro-specific paths (BSD, macOS, custom Linux) The compiler test ensures detection works even when headers are in unusual locations that gcc can find via its search paths. * Support FUSE 3.12+ API - Use fuse_loop_mt_312 for FUSE 3.12+, fuse_loop_mt_31 for 3.0-3.11 - Conditionally add fuse_file_info param to getattr/chmod/chown/utimens - Replace fuse_session_mount/unmount with fuse_mount/unmount - Tested with FUSE 3.18 on Debian forky * Fix pcl-1ib: correct FUSE 3.x handler signatures and loop_mt call - Replace FUSE_MINOR_VERSION >= 12 checks with FUSE_USE_VERSION >= 30 for getattr/chmod/chown/utimens handler signatures; FUSE 3.x always requires the extra struct fuse_file_info* parameter — the wrong signature caused type mismatches and "Transport endpoint is not connected" failures - Replace non-existent fuse_loop_mt_312 extern with correct conditional: FUSE_USE_VERSION >= 32 uses fuse_loop_mt(f, &config), else fuse_loop_mt_31(f, clone_fd) - Fix inverted return logic in is_fuse3_installed_on_system(): was returning 0 on success (when fusermount3 found), now correctly returns non-zero Co-Authored-By: Claude Sonnet 4.6 * Remove unused is_fuse3_installed_on_system() from pfs.c The function was static and had no remaining call sites after the -ononempty/-ohard_remove args block was cleaned up, producing a -Wunused-function warning. Co-Authored-By: Claude Sonnet 4.6 * Restore shutdown_requested definition removed during rebase conflict resolution The variable is declared extern in pfs.h and referenced in both pfs.c (psync_signal_handler) and control_tools.cpp; the definition must exist in pfs.c. Co-Authored-By: Claude Sonnet 4.6 * Fix pfs_creat: missing canmodify assignment causes EACCES on write pfs_creat called pfs_create_file() but never set of->canmodify, leaving it zero-initialized. pfs_write and pfs_ftruncate both check canmodify and return -EACCES if false, making all writes to newly created files fail with Permission denied. All five open paths in pfs_open set canmodify correctly (added in #326), but pfs_creat was missed. Fix by applying the same pattern immediately after psql_unlock(), while fpath is still in scope. Caught during FUSE 2/3 regression testing (Phase 2 write test). Co-Authored-By: Claude Sonnet 4.6 * Extend CI matrix: add FUSE 2, FUSE 3 legacy/current/forky build jobs Replaces single build job with four jobs covering all tested FUSE configs: - fuse2: debian:trixie + libfuse-dev (FUSE_USE_VERSION=26) - fuse3-legacy: ubuntu:20.04 + libfuse3-dev 3.9.x (fuse_loop_mt_31 path) - fuse3-current: ubuntu:22.04 + libfuse3-dev 3.12.x - fuse3-forky: debian:forky + libfuse3-dev 3.18.1 Each job verifies correct symbol linkage after build. Runtime mount tests are outside CI scope (require privileged containers with /dev/fuse). Co-Authored-By: Claude Sonnet 4.6 * CI: replace Ubuntu 20.04/22.04 with ubuntu:24.04 for FUSE3 job ubuntu:20.04 has old compiler (g++-9) and mbedTLS 2.x. ubuntu:22.04 ships mbedTLS 2.28.x; incompatible with our mbedTLS 3.x-only code. ubuntu:24.04 has mbedTLS 3.x and libfuse3 3.16.x, covering the 3.12-3.17 range. FUSE3 < 3.12 is verified locally but not representable in CI without fighting old toolchains. Co-Authored-By: Claude Sonnet 4.6 * CI: add archlinux:latest job for rolling-release compatibility Arch Linux is a rolling distro shipping latest fuse3, mbedtls 3.x, and gcc — useful as an early-warning canary for future API breakage. Also directly covers the maintained Arch package. Co-Authored-By: Claude Sonnet 4.6 * CI: add fedora:41 and fedora:latest jobs with mbedtls3-devel Fedora ships both mbedtls 2.x and 3.x; explicitly install mbedtls3-devel to match our mbedTLS 3.x-only requirement. fedora:41 pins a known-good release; fedora:latest tracks current as a rolling canary alongside Arch. Co-Authored-By: Claude Sonnet 4.6 * README: add compatibility matrix with CI badge references Documents the 6-distro build matrix tested in CI, mbedTLS 3.x requirement, and notes on manual runtime mount testing coverage. Co-Authored-By: Claude Sonnet 4.6 * Fix CI package names; clean up README compatibility matrix - Fedora 41: mbedtls3-devel -> mbedtls3.6-devel (correct versioned name) - Fedora latest (43): mbedtls3-devel -> mbedtls-devel (now ships 3.6.5) - Ubuntu 24.04: replaced with debian:trixie FORCE_FUSE=3 (Ubuntu has no mbedTLS 3.x in standard repos; trixie has both libfuse3-dev and mbedTLS 3.x) - README: remove CI badge links from compatibility matrix Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Levi Neely Co-authored-by: Claude Sonnet 4.6 --- .github/workflows/c-cpp.yml | 111 ++++++++++++++++++++++++- Makefile | 37 +++++++-- README.md | 13 +++ detect_fuse.sh | 48 +++++++++++ pclsync/pfs.c | 161 +++++++++++++++++++++++++++--------- pclsync/pfsxattr.c | 2 - 6 files changed, 320 insertions(+), 52 deletions(-) create mode 100755 detect_fuse.sh diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index c0617d2..649d930 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -7,7 +7,8 @@ on: branches: [ "main" ] jobs: - build: + fuse2: + name: FUSE 2 (debian:trixie) runs-on: ubuntu-latest container: image: debian:trixie @@ -16,6 +17,108 @@ jobs: - name: Install dependencies run: | apt-get update - apt-get install -y build-essential libreadline-dev libudev-dev libfuse-dev libsqlite3-dev zlib1g-dev libboost-dev libboost-system-dev libboost-program-options-dev libmbedtls-dev - - name: make - run: make + apt-get install -y build-essential pkg-config libreadline-dev libudev-dev \ + libfuse-dev libsqlite3-dev zlib1g-dev \ + libboost-dev libboost-program-options-dev libmbedtls-dev + - name: Build (FUSE 2) + run: make FORCE_FUSE=2 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep -E 'fuse_loop_mt@|fuse_mount@|psync_fuse_channel' + ldd pcloudcc | grep libfuse.so.2 + + fuse3-current: + name: FUSE 3 current (debian:trixie) + runs-on: ubuntu-latest + container: + image: debian:trixie + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + apt-get update + apt-get install -y build-essential pkg-config libreadline-dev libudev-dev \ + libfuse3-dev libsqlite3-dev zlib1g-dev \ + libboost-dev libboost-program-options-dev libmbedtls-dev + - name: Build (FUSE 3 current) + run: make FORCE_FUSE=3 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep 'fuse_loop_mt_31' + ldd pcloudcc | grep libfuse3 + + fuse3-arch: + name: FUSE 3 rolling (archlinux:latest) + runs-on: ubuntu-latest + container: + image: archlinux:latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + pacman -Sy --noconfirm base-devel pkg-config readline fuse3 sqlite3 \ + zlib boost mbedtls + - name: Build (FUSE 3 rolling) + run: make FORCE_FUSE=3 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep 'fuse_loop_mt' + ldd pcloudcc | grep libfuse3 + + fuse3-fedora41: + name: FUSE 3 (fedora:41) + runs-on: ubuntu-latest + container: + image: fedora:41 + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + dnf install -y gcc gcc-c++ make pkg-config binutils \ + readline-devel systemd-devel fuse3-devel sqlite-devel zlib-devel \ + boost-devel boost-program-options mbedtls3.6-devel + - name: Build (FUSE 3 Fedora 41) + run: make FORCE_FUSE=3 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep 'fuse_loop_mt' + ldd pcloudcc | grep libfuse3 + + fuse3-fedora-latest: + name: FUSE 3 (fedora:latest) + runs-on: ubuntu-latest + container: + image: fedora:latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + dnf install -y gcc gcc-c++ make pkg-config binutils \ + readline-devel systemd-devel fuse3-devel sqlite-devel zlib-devel \ + boost-devel boost-program-options mbedtls-devel + - name: Build (FUSE 3 Fedora latest) + run: make FORCE_FUSE=3 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep 'fuse_loop_mt' + ldd pcloudcc | grep libfuse3 + + fuse3-forky: + name: FUSE 3.18+ (debian:forky) + runs-on: ubuntu-latest + container: + image: debian:forky + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + apt-get update + apt-get install -y build-essential pkg-config libreadline-dev libudev-dev \ + libfuse3-dev libsqlite3-dev zlib1g-dev \ + libboost-dev libboost-program-options-dev libmbedtls-dev + - name: Build (FUSE 3.18+) + run: make FORCE_FUSE=3 BUILD=release + - name: Verify symbols + run: | + nm pcloudcc | grep 'fuse_loop_mt_31' + ldd pcloudcc | grep libfuse3 diff --git a/Makefile b/Makefile index 1eda170..60e5c27 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,33 @@ AR := ar DIST_CFLAGS := $(CFLAGS) DIST_CXXFLAGS := $(CXXFLAGS) +# Detect FUSE version (can be overridden with FORCE_FUSE=2 or FORCE_FUSE=3) +ifdef FORCE_FUSE + ifeq ($(FORCE_FUSE),2) + FUSE_VERSION := FUSE2 + else ifeq ($(FORCE_FUSE),3) + FUSE_VERSION := FUSE3 + else + $(error FORCE_FUSE must be 2 or 3) + endif +else + FUSE_VERSION := $(shell ./detect_fuse.sh) +endif + +ifeq ($(FUSE_VERSION),FUSE3) + FUSE_CFLAGS := $(shell pkg-config --cflags fuse3 2>/dev/null || echo "-I/usr/include/fuse3 -D_FILE_OFFSET_BITS=64") + FUSE_LIBS := -lfuse3 + FUSE_USE_VERSION := 30 +else ifeq ($(FUSE_VERSION),FUSE2) + FUSE_CFLAGS := $(shell pkg-config --cflags fuse 2>/dev/null || echo "-I/usr/include/fuse -D_FILE_OFFSET_BITS=64") + FUSE_LIBS := -lfuse + FUSE_USE_VERSION := 26 +else + $(error FUSE library not found. Install libfuse-dev or libfuse3-dev) +endif + COMMONFLAGS = -fsanitize=address -CFLAGS = -fPIC $(COMMONFLAGS) -I./pclsync -I/usr/include $(shell pkg-config --cflags $$(pkg-config --list-all | grep -o 'mbedtls[0-9.]*\s' | head -1) 2>/dev/null || pkg-config --cflags mbedtls 2>/dev/null || echo "-I/usr/local/include") +CFLAGS = -fPIC $(COMMONFLAGS) -I./pclsync -I/usr/include $(FUSE_CFLAGS) $(shell pkg-config --cflags $$(pkg-config --list-all | grep -o 'mbedtls[0-9.]*\s' | head -1) 2>/dev/null || pkg-config --cflags mbedtls 2>/dev/null || echo "-I/usr/local/include") ifneq (,$(filter clang%,$(CC))) CFLAGS += -Wthread-safety endif @@ -20,7 +45,7 @@ LIBLDFLAGS = $(COMMONFLAGS) -lreadline -lpthread -ludev -lsqlite3 -lz $(shell \ else \ pkg-config --libs mbedtls mbedx509 mbedcrypto 2>/dev/null || echo "-L/usr/local/lib -lmbedtls -lmbedx509 -lmbedcrypto"; \ fi) -EXECLDFLAGS = $(COMMONFLAGS) -lboost_program_options -lfuse +EXECLDFLAGS = $(COMMONFLAGS) -lboost_program_options $(FUSE_LIBS) SCAN := 0 SRCDIR := . @@ -42,14 +67,14 @@ LIBOUT := libpcloudcc_lib.so # Build type specific flags ifeq ($(BUILD), debug) - CFLAGS += -g -O0 -DDEBUG -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) - CXXFLAGS += -g -O0 -DDEBUG -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) + CFLAGS += -g -O0 -DDEBUG -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=$(FUSE_USE_VERSION) -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) + CXXFLAGS += -g -O0 -DDEBUG -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=$(FUSE_USE_VERSION) -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) DEBUGSRC := $(wildcard $(LIBDIR)/debug/*.c) DEBUGOBJ := $(notdir $(DEBUGSRC:%.c=%.o)) COBJ += $(DEBUGOBJ) else ifeq ($(BUILD), release) - CFLAGS += -g -O0 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) - CXXFLAGS += -g -O0 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) + CFLAGS += -O2 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=$(FUSE_USE_VERSION) -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) + CXXFLAGS += -O2 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=$(FUSE_USE_VERSION) -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) COMMONFLAGS := $(filter-out -fsanitize=address,$(COMMONFLAGS)) LIBLDFLAGS := $(filter-out -fsanitize=address,$(LIBLDFLAGS)) EXECLDFLAGS := $(filter-out -fsanitize=address,$(EXECLDFLAGS)) diff --git a/README.md b/README.md index 0362192..ebea4df 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,19 @@ The target operating system and platform for this program is `linux/amd64`, and I aim to support as many distributions as possible. I maintain a package for [AUR](https://aur.archlinux.org/packages/pcloudcc-lneely) and [Nix](https://github.com/lneely/nixpkgs). I do not plan on providing or maintaining any other packages, but encourage anyone interested in doing so for their own distributions. +# Compatibility Matrix + +| Distribution | FUSE | +|---|---| +| Debian trixie | 2.x | +| Debian trixie | 3.x | +| Arch Linux (rolling) | 3.x latest | +| Fedora 41 | 3.x | +| Fedora (latest) | 3.x latest | +| Debian forky | 3.18+ | + +All configurations require **mbedTLS 3.x**. Runtime mount testing was performed manually on FUSE 2 (libfuse 2.9.x), FUSE 3 (libfuse3 3.10.5), and FUSE 3.18+ (Debian forky container). + # Notices ## Security Notice diff --git a/detect_fuse.sh b/detect_fuse.sh new file mode 100755 index 0000000..a73e16f --- /dev/null +++ b/detect_fuse.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Detect FUSE version and set appropriate flags + +# Check if pkg-config is available +if command -v pkg-config >/dev/null 2>&1; then + # Use pkg-config if available + if pkg-config --exists fuse3 2>/dev/null; then + echo "FUSE3" + exit 0 + elif pkg-config --exists fuse 2>/dev/null; then + echo "FUSE2" + exit 0 + fi +fi + +# Fallback: search for header files in common locations +SEARCH_PATHS="/usr/include /usr/local/include /opt/local/include /opt/include" + +for base in $SEARCH_PATHS; do + if [ -f "$base/fuse3/fuse.h" ]; then + echo "FUSE3" + exit 0 + fi +done + +for base in $SEARCH_PATHS; do + if [ -f "$base/fuse/fuse.h" ]; then + echo "FUSE2" + exit 0 + fi +done + +# Last resort: try to compile a test program +if command -v gcc >/dev/null 2>&1; then + # Try FUSE 3 + if echo '#include ' | gcc -E -DFUSE_USE_VERSION=30 - >/dev/null 2>&1; then + echo "FUSE3" + exit 0 + fi + # Try FUSE 2 + if echo '#include ' | gcc -E -DFUSE_USE_VERSION=26 - >/dev/null 2>&1; then + echo "FUSE2" + exit 0 + fi +fi + +echo "NONE" +exit 1 diff --git a/pclsync/pfs.c b/pclsync/pfs.c index 560fabb..394c20b 100644 --- a/pclsync/pfs.c +++ b/pclsync/pfs.c @@ -92,8 +92,10 @@ typedef off_t fuse_off_t; static int shutdown_in_progress = 0; volatile sig_atomic_t shutdown_requested = 0; -static struct fuse_chan *psync_fuse_channel = NULL; static struct fuse *psync_fuse = NULL; +#if FUSE_USE_VERSION < 30 +static struct fuse_chan *psync_fuse_channel = NULL; +#endif static char *psync_current_mountpoint = NULL; static psync_generic_callback_t psync_start_callback = NULL; char *pfs_fake_prefix = NULL; @@ -786,7 +788,11 @@ static int pfs_getrootattr(struct FUSE_STAT *stbuf) { } \ } while (0) +#if FUSE_USE_VERSION >= 30 +static int pfs_getattr(const char *path, struct FUSE_STAT *stbuf, struct fuse_file_info *fi) { +#else static int pfs_getattr(const char *path, struct FUSE_STAT *stbuf) { +#endif psync_sql_res *res; psync_variant_row row; psync_fspath_t *fpath; @@ -877,15 +883,29 @@ static int filler_decoded(pcrypto_textdec_t dec, namedec = pcryptofolder_flddecode_filename(dec, name); if (!namedec) return 0; +#if FUSE_USE_VERSION >= 30 + ret = filler(buf, namedec, st, off, FUSE_FILL_DIR_PLUS); +#else ret = filler(buf, namedec, st, off); +#endif pmem_free(PMEM_SUBSYS_OTHER, namedec); return ret; } else +#if FUSE_USE_VERSION >= 30 + return filler(buf, name, st, off, FUSE_FILL_DIR_PLUS); +#else return filler(buf, name, st, off); +#endif } +#if FUSE_USE_VERSION >= 30 +static int pfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + fuse_off_t offset, struct fuse_file_info *fi, + enum fuse_readdir_flags readdir_flags) { +#else static int pfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t offset, struct fuse_file_info *fi) { +#endif psync_sql_res *res; psync_variant_row row; psync_fsfolderid_t folderid; @@ -918,9 +938,15 @@ static int pfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, } } else dec = NULL; +#if FUSE_USE_VERSION >= 30 + filler(buf, ".", NULL, 0, FUSE_FILL_DIR_PLUS); + if (folderid != 0) + filler(buf, "..", NULL, 0, FUSE_FILL_DIR_PLUS); +#else filler(buf, ".", NULL, 0); if (folderid != 0) filler(buf, "..", NULL, 0); +#endif folder = pfs_task_get_folder_tasks_rdlocked(folderid); if (folderid >= 0) { res = psql_query_nolock( @@ -1689,6 +1715,7 @@ static int pfs_creat(const char *path, mode_t mode, encoder); pfs_task_release_folder_tasks_locked(folder); psql_unlock(); + of->canmodify = (fpath->permissions & PSYNC_PERM_MODIFY) != 0; of->newfile = 1; of->modified = 1; ret = open_write_files(of, 1); @@ -2885,7 +2912,11 @@ static int pfs_is_nonempty_folder(psync_fsfolderid_t parent_folderid, return ret; } +#if FUSE_USE_VERSION >= 30 +static int pfs_rename(const char *old_path, const char *new_path, unsigned int rename_flags) { +#else static int pfs_rename(const char *old_path, const char *new_path) { +#endif psync_fspath_t *fold_path, *fnew_path; psync_sql_res *res; psync_fstask_folder_t *folder; @@ -3057,13 +3088,21 @@ static int pfs_statfs(const char *path, struct statvfs *stbuf) { return 0; } +#if FUSE_USE_VERSION >= 30 +static int pfs_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { +#else static int pfs_chmod(const char *path, mode_t mode) { +#endif pfs_set_thread_name(); pdbg_logf(D_NOTICE, "chmod %s %u", path, (unsigned)mode); return 0; } +#if FUSE_USE_VERSION >= 30 +static int pfs_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { +#else static int pfs_chown(const char *path, uid_t uid, gid_t gid) { +#endif pfs_set_thread_name(); pdbg_logf(D_NOTICE, "chown %s %u %u", path, (unsigned)uid, (unsigned)gid); return 0; @@ -3229,7 +3268,11 @@ static int pfs_setcrtime(const char *path, const struct timespec *tv) { } #endif +#if FUSE_USE_VERSION >= 30 +static int pfs_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) { +#else static int pfs_utimens(const char *path, const struct timespec tv[2]) { +#endif pfs_set_thread_name(); pdbg_logf(D_NOTICE, "utimens %s %lu", path, tv[1].tv_sec); return pfs_set_time(path, &tv[1], 0); @@ -3286,6 +3329,27 @@ static int pfs_ftruncate(const char *path, fuse_off_t size, (unsigned long)size); } +#if FUSE_USE_VERSION >= 30 +static int pfs_truncate(const char *path, fuse_off_t size, struct fuse_file_info *fi) { + struct fuse_file_info fi_local; + int ret; + pfs_set_thread_name(); + pdbg_logf(D_NOTICE, "truncate %s %lu", path, (unsigned long)size); + + if (fi) { + return pfs_ftruncate(path, size, fi); + } + + memset(&fi_local, 0, sizeof(fi_local)); + ret = pfs_open(path, &fi_local); + if (ret) + return ret; + ret = pfs_ftruncate(path, size, &fi_local); + pfs_flush(path, &fi_local); + pfs_release(path, &fi_local); + return ret; +} +#else static int pfs_truncate(const char *path, fuse_off_t size) { struct fuse_file_info fi; int ret; @@ -3300,6 +3364,7 @@ static int pfs_truncate(const char *path, fuse_off_t size) { pfs_release(path, &fi); return ret; } +#endif static void pfs_start_callback_timer(psync_timer_t timer, void *ptr) { psync_generic_callback_t callback; @@ -3309,7 +3374,11 @@ static void pfs_start_callback_timer(psync_timer_t timer, void *ptr) { prun_thread("fs start callback", callback); } +#if FUSE_USE_VERSION >= 30 +static void *pfs_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { +#else static void *pfs_init(struct fuse_conn_info *conn) { +#endif #if defined(FUSE_CAP_ASYNC_READ) conn->want |= FUSE_CAP_ASYNC_READ; #endif @@ -3546,8 +3615,12 @@ static void pfs_do_stop(void) { mp = psync_fuse_get_mountpoint(); if (mp) { if (stat(mp, &st_before) == 0) { +#if FUSE_USE_VERSION >= 30 + fuse_unmount(psync_fuse); +#else fuse_unmount(mp, psync_fuse_channel); psync_fuse_channel = NULL; +#endif clock_gettime(CLOCK_REALTIME, &ts); // Check if the mountpoint is still accessible @@ -3633,13 +3706,16 @@ static void psync_fuse_thread() { } pthread_mutex_unlock(&start_mutex); pdbg_logf(D_NOTICE, "running fuse_loop_mt"); +#if FUSE_USE_VERSION >= 32 + struct fuse_loop_config loop_config; + loop_config.clone_fd = 1; + loop_config.max_idle_threads = 10; + fr = fuse_loop_mt(psync_fuse, &loop_config); +#elif FUSE_USE_VERSION >= 30 + fr = fuse_loop_mt_31(psync_fuse, 1); +#else fr = fuse_loop_mt(psync_fuse); - - if (shutdown_requested) { - pdbg_logf(D_NOTICE, "shutdown requested, exiting fuse loop"); - pfs_do_stop(); - } - +#endif pdbg_logf(D_NOTICE, "fuse_loop_mt exited with code %d, running fuse_destroy", fr); pthread_mutex_lock(&start_mutex); fuse_destroy(psync_fuse); @@ -3650,28 +3726,6 @@ static void psync_fuse_thread() { pthread_mutex_unlock(&start_mutex); } -// Returns true if FUSE 3 is installed on the user's machine. -// Returns false if FUSE version is less than 3. -static char is_fuse3_installed_on_system() { - // Assuming that fusermount3 is only available on FUSE 3. - FILE *pipe = popen("which fusermount3", "r"); - - if (!pipe) { - return 0; - } - - char output[1024]; - memset(output, 0, sizeof(output)); - - if (fgets(output, sizeof(output), pipe) != NULL) { - return 0; - } - - pclose(pipe); - size_t outlen = strlen(output); - - return outlen > 0; -} static int pfs_do_start() { char *mp; @@ -3684,10 +3738,6 @@ static int pfs_do_start() { fuse_opt_add_arg(&args, "argv"); fuse_opt_add_arg(&args, "-oauto_unmount"); fuse_opt_add_arg(&args, "-ofsname=" DEFAULT_FUSE_MOUNT_POINT ".fs"); - if (!is_fuse3_installed_on_system()) { - fuse_opt_add_arg(&args, "-ononempty"); - } - fuse_opt_add_arg(&args, "-ohard_remove"); // Add user-specified FUSE options from environment variable const char *fuse_opts_env = getenv("PCLOUD_FUSE_OPTS"); @@ -3739,8 +3789,12 @@ static int pfs_do_start() { psync_oper.chmod = pfs_chmod; psync_oper.chown = pfs_chown; psync_oper.utimens = pfs_utimens; +#if FUSE_USE_VERSION >= 30 + psync_oper.truncate = pfs_truncate; +#else psync_oper.ftruncate = pfs_ftruncate; psync_oper.truncate = pfs_truncate; +#endif psync_oper.setxattr = pfs_xatr_set; psync_oper.getxattr = pfs_xatr_get; @@ -3770,18 +3824,45 @@ static int pfs_do_start() { goto err00; } - psync_fuse_channel = fuse_mount(mp, &args); - if (pdbg_unlikely(!psync_fuse_channel)) { +#if FUSE_USE_VERSION >= 30 + psync_fuse = fuse_new(&args, &psync_oper, sizeof(psync_oper), NULL); + if (pdbg_unlikely(!psync_fuse)) { + pdbg_logf(D_CRITICAL, + "CRITICAL ERROR: fuse_new() failed. " + "The FUSE filesystem cannot be started. errno=%d (%s)", + errno, strerror(errno)); + goto err0; + } + + if (fuse_mount(psync_fuse, mp) != 0) { + pdbg_logf(D_CRITICAL, + "CRITICAL ERROR: fuse_mount() failed for mount point %s. " + "The FUSE filesystem cannot be started. errno=%d (%s)", + mp, errno, strerror(errno)); + goto err1; + } +#else + struct fuse_chan *ch = fuse_mount(mp, &args); + if (pdbg_unlikely(!ch)) { pdbg_logf(D_CRITICAL, "CRITICAL ERROR: fuse_mount() failed for mount point %s. " "The FUSE filesystem cannot be started. errno=%d (%s)", mp, errno, strerror(errno)); goto err0; } - psync_fuse = fuse_new(psync_fuse_channel, &args, &psync_oper, - sizeof(psync_oper), NULL); - if (pdbg_unlikely(!psync_fuse)) - goto err1; + + psync_fuse = fuse_new(ch, &args, &psync_oper, sizeof(psync_oper), NULL); + if (pdbg_unlikely(!psync_fuse)) { + pdbg_logf(D_CRITICAL, + "CRITICAL ERROR: fuse_new() failed. " + "The FUSE filesystem cannot be started. errno=%d (%s)", + errno, strerror(errno)); + fuse_unmount(mp, ch); + goto err0; + } + psync_fuse_channel = ch; +#endif + psync_current_mountpoint = mp; started = 1; pthread_mutex_unlock(&start_mutex); @@ -3789,7 +3870,7 @@ static int pfs_do_start() { prun_thread("fuse", psync_fuse_thread); return 0; err1: - fuse_unmount(mp, psync_fuse_channel); + fuse_destroy(psync_fuse); err0: pmem_free(PMEM_SUBSYS_OTHER, mp); err00: diff --git a/pclsync/pfsxattr.c b/pclsync/pfsxattr.c index d56071b..392d00b 100644 --- a/pclsync/pfsxattr.c +++ b/pclsync/pfsxattr.c @@ -29,8 +29,6 @@ DAMAGE. */ -#define FUSE_USE_VERSION 26 - #include #include #include