From a2886a907a07a301fa4d6f233136fa2b13a21cf5 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 4 Mar 2026 20:05:53 +0100 Subject: [PATCH] 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. --- Makefile | 26 ++++++++++++---- detect_fuse.sh | 11 +++++++ pclsync/pfs.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 6 deletions(-) create mode 100755 detect_fuse.sh diff --git a/Makefile b/Makefile index f29dde1..1463b76 100644 --- a/Makefile +++ b/Makefile @@ -5,8 +5,22 @@ AR := ar DIST_CFLAGS := $(CFLAGS) DIST_CXXFLAGS := $(CXXFLAGS) +# Detect FUSE version +FUSE_VERSION := $(shell ./detect_fuse.sh) +ifeq ($(FUSE_VERSION),FUSE3) + FUSE_CFLAGS := $(shell pkg-config --cflags fuse3) + FUSE_LIBS := -lfuse3 + FUSE_USE_VERSION := 30 +else ifeq ($(FUSE_VERSION),FUSE2) + FUSE_CFLAGS := $(shell pkg-config --cflags fuse) + 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 fuse3 2>/dev/null) $(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 +34,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 -lfuse3 +EXECLDFLAGS = $(COMMONFLAGS) -lboost_program_options $(FUSE_LIBS) SCAN := 0 SRCDIR := . @@ -42,14 +56,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=30 -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 += -O2 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=30 -D_GNU_SOURCE -DPSYNC_SSL_DEBUG_LEVEL=$(SSLDBGLVL) - CXXFLAGS += -O2 -DNDEBUG -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=30 -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/detect_fuse.sh b/detect_fuse.sh new file mode 100755 index 0000000..3f051d0 --- /dev/null +++ b/detect_fuse.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Detect FUSE version and set appropriate flags + +if pkg-config --exists fuse3; then + echo "FUSE3" +elif pkg-config --exists fuse; then + echo "FUSE2" +else + echo "NONE" + exit 1 +fi diff --git a/pclsync/pfs.c b/pclsync/pfs.c index 0eff989..fb6f2b0 100644 --- a/pclsync/pfs.c +++ b/pclsync/pfs.c @@ -28,6 +28,8 @@ DAMAGE. */ +#define FUSE_USE_VERSION 30 + #include #include #include @@ -875,16 +877,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; @@ -917,9 +932,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( @@ -2884,7 +2905,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; @@ -3285,6 +3310,7 @@ 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; @@ -3304,6 +3330,22 @@ static int pfs_truncate(const char *path, fuse_off_t size, struct fuse_file_info 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; + pfs_set_thread_name(); + pdbg_logf(D_NOTICE, "truncate %s %lu", path, (unsigned long)size); + memset(&fi, 0, sizeof(fi)); + ret = pfs_open(path, &fi); + if (ret) + return ret; + ret = pfs_ftruncate(path, size, &fi); + pfs_flush(path, &fi); + pfs_release(path, &fi); + return ret; +} +#endif static void pfs_start_callback_timer(psync_timer_t timer, void *ptr) { psync_generic_callback_t callback; @@ -3313,7 +3355,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 @@ -3550,8 +3596,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 struct fuse_session *se = fuse_get_session(psync_fuse); fuse_session_unmount(se); +#else + fuse_unmount(mp, fuse_get_session(psync_fuse)); +#endif clock_gettime(CLOCK_REALTIME, &ts); // Check if the mountpoint is still accessible @@ -3637,10 +3687,14 @@ static void psync_fuse_thread() { } pthread_mutex_unlock(&start_mutex); pdbg_logf(D_NOTICE, "running fuse_loop_mt"); +#if FUSE_USE_VERSION >= 30 struct fuse_loop_config loop_config; loop_config.clone_fd = 1; loop_config.max_idle_threads = 10; fr = fuse_loop_mt_31(psync_fuse, &loop_config); +#else + fr = fuse_loop_mt(psync_fuse); +#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); @@ -3736,7 +3790,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; @@ -3766,6 +3825,7 @@ static int pfs_do_start() { goto err00; } +#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, @@ -3783,6 +3843,26 @@ static int pfs_do_start() { 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(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; + } +#endif psync_current_mountpoint = mp; started = 1;