[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1106401: unblock: fuse3/3.17.2-2 (pre-approval)



Control: tags -1 confirmed

On 2025-05-24 14:37:26 +0200, László Böszörményi (GCS) wrote:
> Package: release.debian.org
> Severity: normal
> Tags: d-i
> User: release.debian.org@packages.debian.org
> Usertags: unblock
> X-Debbugs-Cc: kibi@debian.org
> Control: affects -1 + src:fuse3
> 
> Hi RMs,
> 
> Please pre-approve unblocking of package fuse3.

Please go ahead.

Cheers

> 
> [ Reason ]
> There is still a regression in ceph-fuse due to this library. Upstream
> fixed it with three small patches. Also contains a small change in
> d/rules which adds sh4 architecture to ones which need to link with
> atomic.
> Then I've updated the symbols file (also filed as RC bug) to use the
> current package version. As the minimal version of the package name
> (libfuse3-4) is already 3.17.2 it is more of a formal change.
> 
> [ Impact ]
> Without this change, ceph-fuse would not work correctly. Then makes
> sh4 architecture in sync with the Trixie release.
> 
> [ Tests ]
> I've tested it myself and Jakob Haufe (another DD) also tested this.
> 
> [ Risks ]
> This affects the installer due to an udeb, but none of these changes
> risk it in my opinion.
> 
> [ Checklist ]
>   [x] all changes are documented in the d/changelog
>   [x] I reviewed all changes and I approve them
>   [x] attach debdiff against the package in testing
> 
> Regards,
> Laszlo/GCS

> diff -Nru fuse3-3.17.2/debian/changelog fuse3-3.17.2/debian/changelog
> --- fuse3-3.17.2/debian/changelog	2025-04-27 08:10:01.000000000 +0200
> +++ fuse3-3.17.2/debian/changelog	2025-05-19 20:39:08.000000000 +0200
> @@ -1,3 +1,17 @@
> +fuse3 (3.17.2-2) unstable; urgency=medium
> +
> +  [ Laszlo Boszormenyi (GCS) ]
> +  * Backport upstream fixes (closes: #1101305):
> +    - make conn->want/want_ext conversion non fatal,
> +    - add container_of and ROUND_UP macros,
> +    - conn->want conversion: Fix fuse_apply_conn_info_opts() .
> +  * Update symbols file (closes: #1105099).
> +
> +  [ Helmut Grohne <helmut@subdivi.de> ]
> +  * Update -latomic architecture list (closes: #1105150).
> +
> + -- Laszlo Boszormenyi (GCS) <gcs@debian.org>  Mon, 19 May 2025 20:39:08 +0200
> +
>  fuse3 (3.17.2-1) unstable; urgency=medium
>  
>    * New upstream release:
> diff -Nru fuse3-3.17.2/debian/libfuse3-4.symbols fuse3-3.17.2/debian/libfuse3-4.symbols
> --- fuse3-3.17.2/debian/libfuse3-4.symbols	2025-02-14 22:49:04.000000000 +0100
> +++ fuse3-3.17.2/debian/libfuse3-4.symbols	2025-05-19 20:39:08.000000000 +0200
> @@ -1,10 +1,11 @@
>  libfuse3.so.4 #PACKAGE# #MINVER#
> -* Build-Depends-Package: libfuse-dev
> - (symver)FUSE_3.0 3.2.3
> - (symver)FUSE_3.1 3.2.3
> - (symver)FUSE_3.2 3.2.3
> - (symver)FUSE_3.3 3.4.1
> - (symver)FUSE_3.4 3.4.1
> - (symver)FUSE_3.7 3.7.0
> - (symver)FUSE_3.12 3.12.0
> - (symver)FUSE_3.17 3.17.1~rc0
> +* Build-Depends-Package: libfuse3-dev
> + (symver)FUSE_3.0 3.17.2
> + (symver)FUSE_3.1 3.17.2
> + (symver)FUSE_3.2 3.17.2
> + (symver)FUSE_3.3 3.17.2
> + (symver)FUSE_3.4 3.17.2
> + (symver)FUSE_3.7 3.17.2
> + (symver)FUSE_3.12 3.17.2
> + (symver)FUSE_3.17 3.17.2
> + (symver)FUSE_3.17.3 3.17.2
> diff -Nru fuse3-3.17.2/debian/patches/Add-container_of-and-ROUND_UP-macros.patch fuse3-3.17.2/debian/patches/Add-container_of-and-ROUND_UP-macros.patch
> --- fuse3-3.17.2/debian/patches/Add-container_of-and-ROUND_UP-macros.patch	1970-01-01 01:00:00.000000000 +0100
> +++ fuse3-3.17.2/debian/patches/Add-container_of-and-ROUND_UP-macros.patch	2025-05-19 20:39:08.000000000 +0200
> @@ -0,0 +1,61 @@
> +From d6a9799fc04e6ada5fd7fd7bbde14fb14981fc8b Mon Sep 17 00:00:00 2001
> +From: Bernd Schubert <bschubert@ddn.com>
> +Date: Tue, 15 Apr 2025 22:03:09 +0200
> +Subject: [PATCH] Add container_of and ROUND_UP macros
> +
> +Needed by follow up commits. container_of is actually
> +just moved/consolidated to util.h.
> +
> +Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> +(cherry picked from commit c5a032b3410d7225ac0355355faa63565a209943)
> +---
> + lib/fuse.c          | 4 ----
> + lib/fuse_lowlevel.c | 4 ----
> + lib/util.h          | 6 ++++++
> + 3 files changed, 6 insertions(+), 8 deletions(-)
> +
> +diff --git a/lib/fuse.c b/lib/fuse.c
> +index c0d00edbc..4964de20f 100644
> +--- a/lib/fuse.c
> ++++ b/lib/fuse.c
> +@@ -92,10 +92,6 @@ struct node_table {
> + 	size_t split;
> + };
> + 
> +-#define container_of(ptr, type, member) ({                              \
> +-			const typeof( ((type *)0)->member ) *__mptr = (ptr); \
> +-			(type *)( (char *)__mptr - offsetof(type,member) );})
> +-
> + #define list_entry(ptr, type, member)           \
> + 	container_of(ptr, type, member)
> + 
> +diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
> +index 9ee88b160..cb046aae0 100644
> +--- a/lib/fuse_lowlevel.c
> ++++ b/lib/fuse_lowlevel.c
> +@@ -44,10 +44,6 @@
> + #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
> + #define OFFSET_MAX 0x7fffffffffffffffLL
> + 
> +-#define container_of(ptr, type, member) ({				\
> +-			const typeof( ((type *)0)->member ) *__mptr = (ptr); \
> +-			(type *)( (char *)__mptr - offsetof(type,member) );})
> +-
> + struct fuse_pollhandle {
> + 	uint64_t kh;
> + 	struct fuse_session *se;
> +diff --git a/lib/util.h b/lib/util.h
> +index 508fafb12..ed03ad40e 100644
> +--- a/lib/util.h
> ++++ b/lib/util.h
> +@@ -30,4 +30,10 @@ static inline uint64_t fuse_higher_32_bits(uint64_t nr)
> + #define FUSE_VAR_UNUSED(var) (__attribute__((unused)) var)
> + #endif
> + 
> ++#define container_of(ptr, type, member)                      \
> ++	({                                                   \
> ++		unsigned long __mptr = (unsigned long)(ptr); \
> ++		((type *)(__mptr - offsetof(type, member))); \
> ++	})
> ++
> + #endif
> diff -Nru fuse3-3.17.2/debian/patches/Fix-fuse_apply_conn_info_opts.patch fuse3-3.17.2/debian/patches/Fix-fuse_apply_conn_info_opts.patch
> --- fuse3-3.17.2/debian/patches/Fix-fuse_apply_conn_info_opts.patch	1970-01-01 01:00:00.000000000 +0100
> +++ fuse3-3.17.2/debian/patches/Fix-fuse_apply_conn_info_opts.patch	2025-05-19 20:39:08.000000000 +0200
> @@ -0,0 +1,559 @@
> +From 066e8111f0ff522e4682a31bd63b5a3532e8af86 Mon Sep 17 00:00:00 2001
> +From: Bernd Schubert <bschubert@ddn.com>
> +Date: Sun, 18 May 2025 00:24:07 +0200
> +Subject: [PATCH] conn->want conversion: Fix fuse_apply_conn_info_opts()
> +
> +fuse_apply_conn_info_opts() was applying to 'want_ext',
> +which would cause conflicts with 'want' if the application
> +applied its own flags to 'conn->want'.
> +
> +Solution is:
> +    - to move fuse_{set,unset,get}_feature_flag and
> +      convert_to_conn_want_ext() to fuse_lowlevel.c and
> +      to define them as part of the public API, although
> +      convert_to_conn_want_ext() should not be used - it is
> +      currently needed to be a public function due as it needs
> +      to be defined for the tests.
> +
> +Related to https://github.com/libfuse/libfuse/issues/1171 and
> +https://github.com/libfuse/libfuse/pull/1172.
> +
> +Closes: https://github.com/libfuse/libfuse/issues/1171
> +Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> +---
> + include/fuse_common.h       | 50 +++++++++++--------
> + lib/fuse.c                  |  9 +---
> + lib/fuse_i.h                | 38 +++------------
> + lib/fuse_lowlevel.c         | 78 ++++++++++++++++++++++++++---
> + lib/fuse_versionscript      | 10 ++++
> + lib/helper.c                | 15 ++++--
> + lib/util.c                  |  8 +++
> + lib/util.h                  |  3 ++
> + test/test_want_conversion.c | 97 ++++++++++++++++++++++---------------
> + 9 files changed, 200 insertions(+), 108 deletions(-)
> +
> +diff --git a/include/fuse_common.h b/include/fuse_common.h
> +index 582505fa9..dd08f444a 100644
> +--- a/include/fuse_common.h
> ++++ b/include/fuse_common.h
> +@@ -1096,28 +1096,40 @@ void fuse_loop_cfg_convert(struct fuse_loop_config *config,
> + 			   struct fuse_loop_config_v1 *v1_conf);
> + #endif
> + 
> ++/**
> ++ * Set a feature flag in the want_ext field of fuse_conn_info.
> ++ *
> ++ * @param conn connection information
> ++ * @param flag feature flag to be set
> ++ * @return true if the flag was set, false if the flag is not supported
> ++ */
> ++bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
> + 
> +-static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn,
> +-					 uint64_t flag)
> +-{
> +-	if (conn->capable_ext & flag) {
> +-		conn->want_ext |= flag;
> +-		return true;
> +-	}
> +-	return false;
> +-}
> ++/**
> ++ * Unset a feature flag in the want_ext field of fuse_conn_info.
> ++ *
> ++ * @param conn connection information
> ++ * @param flag feature flag to be unset
> ++ */
> ++void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
> ++
> ++/**
> ++ * Get the value of a feature flag in the want_ext field of fuse_conn_info.
> ++ *
> ++ * @param conn connection information
> ++ * @param flag feature flag to be checked
> ++ * @return true if the flag is set, false otherwise
> ++ */
> ++bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag);
> ++
> ++/*
> ++ * DO NOT USE: Not part of public API, for internal test use only.
> ++ * The function signature or any use of it is not guaranteeed to
> ++ * remain stable. And neither are results of what this function does.
> ++ */
> ++int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn);
> + 
> +-static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn,
> +-					 uint64_t flag)
> +-{
> +-	conn->want_ext &= ~flag;
> +-}
> + 
> +-static inline bool fuse_get_feature_flag(struct fuse_conn_info *conn,
> +-					     uint64_t flag)
> +-{
> +-	return conn->capable_ext & flag ? true : false;
> +-}
> + 
> + /* ----------------------------------------------------------- *
> +  * Compatibility stuff					       *
> +diff --git a/lib/fuse.c b/lib/fuse.c
> +index 4964de20f..85914546e 100644
> +--- a/lib/fuse.c
> ++++ b/lib/fuse.c
> +@@ -2611,15 +2611,8 @@ void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
> + 		fuse_unset_feature_flag(conn, FUSE_CAP_POSIX_LOCKS);
> + 	if (!fs->op.flock)
> + 		fuse_unset_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS);
> +-	if (fs->op.init) {
> +-		uint64_t want_ext_default = conn->want_ext;
> +-		uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
> +-
> +-		conn->want = want_default;
> ++	if (fs->op.init)
> + 		fs->user_data = fs->op.init(conn, cfg);
> +-
> +-		convert_to_conn_want_ext(conn, want_ext_default, want_default);
> +-	}
> + }
> + 
> + static int fuse_init_intr_signal(int signum, int *installed);
> +diff --git a/lib/fuse_i.h b/lib/fuse_i.h
> +index bf5e2ca41..718fa142c 100644
> +--- a/lib/fuse_i.h
> ++++ b/lib/fuse_i.h
> +@@ -85,6 +85,13 @@ struct fuse_session {
> + 
> + 	/* true if reading requests from /dev/fuse are handled internally */
> + 	bool buf_reallocable;
> ++
> ++	/*
> ++	 * conn->want and conn_want_ext options set by libfuse , needed
> ++	 * to correctly convert want to want_ext
> ++	 */
> ++	uint32_t conn_want;
> ++	uint64_t conn_want_ext;
> + };
> + 
> + struct fuse_chan {
> +@@ -227,34 +234,3 @@ int fuse_loop_cfg_verify(struct fuse_loop_config *config);
> + /* room needed in buffer to accommodate header */
> + #define FUSE_BUFFER_HEADER_SIZE 0x1000
> + 
> +-/**
> +- * Get the wanted capability flags, converting from old format if necessary
> +- */
> +-static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
> +-					   uint64_t want_ext_default,
> +-					   uint32_t want_default)
> +-{
> +-	/*
> +-	 * Convert want to want_ext if necessary.
> +-	 * For the high level interface this function might be called
> +-	 * twice, once from the high level interface and once from the
> +-	 * low level interface. Both, with different want_ext_default and
> +-	 * want_default values. In order to suppress a failure for the
> +-	 * second call, we check if the lower 32 bits of want_ext are
> +-	 * already set to the value of want.
> +-	 */
> +-	if (conn->want != want_default &&
> +-	    fuse_lower_32_bits(conn->want_ext) != conn->want) {
> +-		if (conn->want_ext != want_ext_default)
> +-			return -EINVAL;
> +-
> +-		/* high bits from want_ext, low bits from want */
> +-		conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
> +-				 conn->want;
> +-	}
> +-
> +-	/* ensure there won't be a second conversion */
> +-	conn->want = fuse_lower_32_bits(conn->want_ext);
> +-
> +-	return 0;
> +-}
> +diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
> +index cb046aae0..1276a0fd9 100644
> +--- a/lib/fuse_lowlevel.c
> ++++ b/lib/fuse_lowlevel.c
> +@@ -1994,6 +1994,77 @@ static bool want_flags_valid(uint64_t capable, uint64_t want)
> + 	return true;
> + }
> + 
> ++/**
> ++ * Get the wanted capability flags, converting from old format if necessary
> ++ */
> ++int fuse_convert_to_conn_want_ext(struct fuse_conn_info *conn)
> ++{
> ++	struct fuse_session *se = container_of(conn, struct fuse_session, conn);
> ++
> ++	/*
> ++	 * Convert want to want_ext if necessary.
> ++	 * For the high level interface this function might be called
> ++	 * twice, once from the high level interface and once from the
> ++	 * low level interface. Both, with different want_ext_default and
> ++	 * want_default values. In order to suppress a failure for the
> ++	 * second call, we check if the lower 32 bits of want_ext are
> ++	 * already set to the value of want.
> ++	 */
> ++	if (conn->want != se->conn_want &&
> ++	    fuse_lower_32_bits(conn->want_ext) != conn->want) {
> ++		if (conn->want_ext != se->conn_want_ext) {
> ++			fuse_log(FUSE_LOG_ERR,
> ++				"%s: Both conn->want_ext and conn->want are set.\n"
> ++				"want=%x, want_ext=%lx, se->want=%lx se->want_ext=%lx\n",
> ++				__func__, conn->want, conn->want_ext,
> ++				se->conn_want, se->conn_want_ext);
> ++			return -EINVAL;
> ++		}
> ++
> ++		/* high bits from want_ext, low bits from want */
> ++		conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
> ++				 conn->want;
> ++	}
> ++
> ++	/* ensure there won't be a second conversion */
> ++	conn->want = fuse_lower_32_bits(conn->want_ext);
> ++
> ++	return 0;
> ++}
> ++
> ++bool fuse_set_feature_flag(struct fuse_conn_info *conn,
> ++					 uint64_t flag)
> ++{
> ++	struct fuse_session *se = container_of(conn, struct fuse_session, conn);
> ++
> ++	if (conn->capable_ext & flag) {
> ++		conn->want_ext |= flag;
> ++		se->conn_want_ext |= flag;
> ++		conn->want  |= flag;
> ++		se->conn_want |= flag;
> ++		return true;
> ++	}
> ++	return false;
> ++}
> ++
> ++void fuse_unset_feature_flag(struct fuse_conn_info *conn,
> ++					 uint64_t flag)
> ++{
> ++	struct fuse_session *se = container_of(conn, struct fuse_session, conn);
> ++
> ++	conn->want_ext &= ~flag;
> ++	se->conn_want_ext &= ~flag;
> ++	conn->want  &= ~flag;
> ++	se->conn_want &= ~flag;
> ++}
> ++
> ++bool fuse_get_feature_flag(struct fuse_conn_info *conn,
> ++					     uint64_t flag)
> ++{
> ++	return conn->capable_ext & flag ? true : false;
> ++}
> ++
> ++
> + /* Prevent bogus data races (bogus since "init" is called before
> +  * multi-threading becomes relevant */
> + static __attribute__((no_sanitize("thread")))
> +@@ -2154,12 +2225,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
> + 
> + 	se->got_init = 1;
> + 	if (se->op.init) {
> +-		uint64_t want_ext_default = se->conn.want_ext;
> +-		uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext);
> +-
> + 		// Apply the first 32 bits of capable_ext to capable
> + 		se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
> +-		se->conn.want = want_default;
> + 
> + 		se->op.init(se->userdata, &se->conn);
> + 
> +@@ -2168,8 +2235,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
> + 		 * se->conn.want_ext
> + 		 * Userspace might still use conn.want - we need to convert it
> + 		 */
> +-		convert_to_conn_want_ext(&se->conn, want_ext_default,
> +-					      want_default);
> ++		fuse_convert_to_conn_want_ext(&se->conn);
> + 	}
> + 
> + 	if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
> +diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
> +index 6c5fc83eb..a2653fcdd 100644
> +--- a/lib/fuse_versionscript
> ++++ b/lib/fuse_versionscript
> +@@ -202,6 +202,16 @@ FUSE_3.17 {
> + 		fuse_log_close_syslog;
> + } FUSE_3.12;
> + 
> ++FUSE_3.17.3 {
> ++	global:
> ++		fuse_set_feature_flag;
> ++		fuse_unset_feature_flag;
> ++		fuse_get_feature_flag;
> ++
> ++		# Not part of public API, for internal test use only
> ++		fuse_convert_to_conn_want_ext;
> ++} FUSE_3.17;
> ++
> + # Local Variables:
> + # indent-tabs-mode: t
> + # End:
> +diff --git a/lib/helper.c b/lib/helper.c
> +index 59dd48881..aceff9fd5 100644
> +--- a/lib/helper.c
> ++++ b/lib/helper.c
> +@@ -423,10 +423,17 @@ void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts,
> + 	if(opts->set_max_readahead)
> + 		conn->max_readahead = opts->max_readahead;
> + 
> +-#define LL_ENABLE(cond,cap) \
> +-	if (cond) conn->want_ext |= (cap)
> +-#define LL_DISABLE(cond,cap) \
> +-	if (cond) conn->want_ext &= ~(cap)
> ++#define LL_ENABLE(cond, cap)                     \
> ++	do {                                     \
> ++		if (cond)                        \
> ++			fuse_set_feature_flag(conn, cap); \
> ++	} while (0)
> ++
> ++#define LL_DISABLE(cond, cap)                     \
> ++	do {                                      \
> ++		if (cond)                         \
> ++			fuse_unset_feature_flag(conn, cap); \
> ++	} while (0)
> + 
> + 	LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ);
> + 	LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ);
> +diff --git a/lib/util.c b/lib/util.c
> +index a529d383c..956c3d2e9 100644
> +--- a/lib/util.c
> ++++ b/lib/util.c
> +@@ -1,7 +1,14 @@
> + #include <stdlib.h>
> + #include <errno.h>
> + 
> ++#ifndef FUSE_USE_VERSION
> ++#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 18))
> ++#endif
> ++
> + #include "util.h"
> ++#include "fuse_log.h"
> ++#include "fuse_lowlevel.h"
> ++#include <stdio.h>
> + 
> + int libfuse_strtol(const char *str, long *res)
> + {
> +@@ -25,3 +32,4 @@ int libfuse_strtol(const char *str, long *res)
> + 	*res = val;
> + 	return 0;
> + }
> ++
> +diff --git a/lib/util.h b/lib/util.h
> +index ed03ad40e..f24401a29 100644
> +--- a/lib/util.h
> ++++ b/lib/util.h
> +@@ -2,12 +2,15 @@
> + #define FUSE_UTIL_H_
> + 
> + #include <stdint.h>
> ++#include <stdbool.h>
> + 
> + #define ROUND_UP(val, round_to) (((val) + (round_to - 1)) & ~(round_to - 1))
> + 
> + #define likely(x) __builtin_expect(!!(x), 1)
> + #define unlikely(x) __builtin_expect(!!(x), 0)
> + 
> ++struct fuse_conn_info;
> ++
> + int libfuse_strtol(const char *str, long *res);
> + 
> + /**
> +diff --git a/test/test_want_conversion.c b/test/test_want_conversion.c
> +index bee23cc6e..db731edbf 100644
> +--- a/test/test_want_conversion.c
> ++++ b/test/test_want_conversion.c
> +@@ -1,16 +1,22 @@
> +-#include "util.h"
> + #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 17)
> + 
> ++#include "util.h"
> + #include "fuse_i.h"
> ++#include "fuse_lowlevel.h"
> + #include <stdio.h>
> + #include <assert.h>
> + #include <inttypes.h>
> + #include <stdbool.h>
> ++#include <err.h>
> + 
> + static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
> + {
> +-	printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64 "\n", prefix,
> +-	       conn->want, conn->want_ext);
> ++	struct fuse_session *se = container_of(conn, struct fuse_session, conn);
> ++
> ++	printf("%s: want=0x%" PRIx32 " want_ext=0x%" PRIx64
> ++		" want_default=0x%" PRIx32 " want_ext_default=0x%" PRIx64 "\n",
> ++		prefix, conn->want, conn->want_ext, se->conn_want,
> ++		se->conn_want_ext);
> + }
> + 
> + static void application_init_old_style(struct fuse_conn_info *conn)
> +@@ -18,33 +24,31 @@ static void application_init_old_style(struct fuse_conn_info *conn)
> + 	/* Simulate application init the old style */
> + 	conn->want |= FUSE_CAP_ASYNC_READ;
> + 	conn->want &= ~FUSE_CAP_SPLICE_READ;
> ++
> ++	/*
> ++	 * Also use new style API, as that might happen through
> ++	 * fuse_apply_conn_info_opts()
> ++	 */
> ++	fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
> + }
> + 
> + static void application_init_new_style(struct fuse_conn_info *conn)
> + {
> + 	/* Simulate application init the new style */
> + 	fuse_set_feature_flag(conn, FUSE_CAP_ASYNC_READ);
> ++	fuse_set_feature_flag(conn, FUSE_CAP_IOCTL_DIR);
> + 	fuse_unset_feature_flag(conn, FUSE_CAP_SPLICE_READ);
> + }
> + 
> + static void test_fuse_fs_init(struct fuse_conn_info *conn, bool new_style)
> + {
> +-	uint64_t want_ext_default = conn->want_ext;
> +-	uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
> +-	int rc;
> +-
> + 	/* High-level init */
> + 	fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT);
> + 
> +-	conn->want = want_default;
> +-
> + 	if (new_style)
> + 		application_init_new_style(conn);
> + 	else
> + 		application_init_old_style(conn);
> +-
> +-	rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
> +-	assert(rc == 0);
> + }
> + 
> + static void test_do_init(struct fuse_conn_info *conn, bool new_style)
> +@@ -53,49 +57,71 @@ static void test_do_init(struct fuse_conn_info *conn, bool new_style)
> + 	conn->capable_ext = FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_WRITE |
> + 			    FUSE_CAP_SPLICE_MOVE | FUSE_CAP_POSIX_LOCKS |
> + 			    FUSE_CAP_FLOCK_LOCKS | FUSE_CAP_EXPORT_SUPPORT |
> +-			    FUSE_CAP_ASYNC_READ;
> ++			    FUSE_CAP_ASYNC_READ | FUSE_CAP_IOCTL_DIR;
> + 	conn->capable = fuse_lower_32_bits(conn->capable_ext);
> +-	conn->want_ext = conn->capable_ext;
> ++
> ++	fuse_set_feature_flag(conn, FUSE_CAP_SPLICE_READ |
> ++				    FUSE_CAP_SPLICE_WRITE |
> ++				    FUSE_CAP_SPLICE_MOVE);
> + 
> + 	print_conn_info("Initial state", conn);
> + 
> +-	uint64_t want_ext_default = conn->want_ext;
> +-	uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
> + 	int rc;
> + 
> +-	conn->want = want_default;
> +-	conn->capable = fuse_lower_32_bits(conn->capable_ext);
> +-
> + 	test_fuse_fs_init(conn, new_style);
> ++	print_conn_info("After init", conn);
> + 
> +-	rc = convert_to_conn_want_ext(conn, want_ext_default, want_default);
> ++	rc = fuse_convert_to_conn_want_ext(conn);
> + 	assert(rc == 0);
> + 
> + 	/* Verify all expected flags are set */
> + 	assert(!(conn->want_ext & FUSE_CAP_SPLICE_READ));
> + 	assert(conn->want_ext & FUSE_CAP_SPLICE_WRITE);
> + 	assert(conn->want_ext & FUSE_CAP_SPLICE_MOVE);
> +-	assert(conn->want_ext & FUSE_CAP_POSIX_LOCKS);
> +-	assert(conn->want_ext & FUSE_CAP_FLOCK_LOCKS);
> + 	assert(conn->want_ext & FUSE_CAP_EXPORT_SUPPORT);
> + 	assert(conn->want_ext & FUSE_CAP_ASYNC_READ);
> ++	assert(conn->want_ext & FUSE_CAP_IOCTL_DIR);
> ++
> + 	/* Verify no other flags are set */
> + 	assert(conn->want_ext ==
> + 	       (FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE |
> +-		FUSE_CAP_POSIX_LOCKS | FUSE_CAP_FLOCK_LOCKS |
> +-		FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ));
> ++		FUSE_CAP_EXPORT_SUPPORT | FUSE_CAP_ASYNC_READ |
> ++		FUSE_CAP_IOCTL_DIR));
> + 
> + 	print_conn_info("After init", conn);
> + }
> + 
> + static void test_want_conversion_basic(void)
> + {
> +-	struct fuse_conn_info conn = { 0 };
> ++	const struct fuse_lowlevel_ops ops = { 0 };
> ++	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
> ++	struct fuse_session *se;
> ++	struct fuse_conn_info *conn;
> ++
> ++	/* Add the program name to arg[0] */
> ++	if (fuse_opt_add_arg(&args, "test_signals")) {
> ++		fprintf(stderr, "Failed to add argument\n");
> ++		errx(1, "Failed to add argument");
> ++	}
> ++
> ++
> ++	se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
> ++	assert(se);
> ++	conn = &se->conn;
> ++	printf("\nTesting basic want conversion, old style:\n");
> ++	test_do_init(conn, false);
> ++	fuse_session_destroy(se);
> ++
> ++	se = fuse_session_new(&args, &ops, sizeof(ops), NULL);
> ++	assert(se);
> ++	conn = &se->conn;
> ++	printf("\nTesting basic want conversion, new style:\n");
> ++	test_do_init(conn, true);
> ++	print_conn_info("After init", conn);
> ++	fuse_session_destroy(se);
> ++
> ++	fuse_opt_free_args(&args);
> + 
> +-	printf("\nTesting basic want conversion:\n");
> +-	test_do_init(&conn, false);
> +-	test_do_init(&conn, true);
> +-	print_conn_info("After init", &conn);
> + }
> + 
> + static void test_want_conversion_conflict(void)
> +@@ -115,16 +141,11 @@ static void test_want_conversion_conflict(void)
> + 	conn.want = fuse_lower_32_bits(conn.want_ext);
> + 	print_conn_info("Test conflict initial", &conn);
> + 
> +-	/* Initialize default values like in basic test */
> +-	uint64_t want_ext_default_ll = conn.want_ext;
> +-	uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
> +-
> + 	/* Simulate application init modifying capabilities */
> + 	conn.want_ext |= FUSE_CAP_ATOMIC_O_TRUNC; /* Add new capability */
> + 	conn.want &= ~FUSE_CAP_SPLICE_READ; /* Remove a capability */
> + 
> +-	rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
> +-				      want_default_ll);
> ++	rc = fuse_convert_to_conn_want_ext(&conn);
> + 	assert(rc == -EINVAL);
> + 	print_conn_info("Test conflict after", &conn);
> + 
> +@@ -143,11 +164,7 @@ static void test_want_conversion_high_bits(void)
> + 	conn.want = fuse_lower_32_bits(conn.want_ext);
> + 	print_conn_info("Test high bits initial", &conn);
> + 
> +-	uint64_t want_ext_default_ll = conn.want_ext;
> +-	uint32_t want_default_ll = fuse_lower_32_bits(want_ext_default_ll);
> +-
> +-	rc = convert_to_conn_want_ext(&conn, want_ext_default_ll,
> +-				      want_default_ll);
> ++	rc = fuse_convert_to_conn_want_ext(&conn);
> + 	assert(rc == 0);
> + 	assert(conn.want_ext == ((1ULL << 33) | FUSE_CAP_ASYNC_READ));
> + 	print_conn_info("Test high bits after", &conn);
> diff -Nru fuse3-3.17.2/debian/patches/Make_conn-want-want_ext_conversion_non_fatal.patch fuse3-3.17.2/debian/patches/Make_conn-want-want_ext_conversion_non_fatal.patch
> --- fuse3-3.17.2/debian/patches/Make_conn-want-want_ext_conversion_non_fatal.patch	1970-01-01 01:00:00.000000000 +0100
> +++ fuse3-3.17.2/debian/patches/Make_conn-want-want_ext_conversion_non_fatal.patch	2025-05-19 20:39:08.000000000 +0200
> @@ -0,0 +1,92 @@
> +From 28a6e40302fbd3eeac1aee9434e1bcf69b1a8e25 Mon Sep 17 00:00:00 2001
> +From: Bernd Schubert <bschubert@ddn.com>
> +Date: Sat, 17 May 2025 23:52:47 +0200
> +Subject: [PATCH] Make conn->want/want_ext conversion non fatal
> +
> +there are too many issues with conn->want and conn->want_ext
> +conversion, for now just log a warning, but setting both
> +flags is now not fatal anymore.
> +
> +Signed-off-by: Bernd Schubert <bschubert@ddn.com>
> +---
> + lib/fuse.c          | 16 +---------------
> + lib/fuse_i.h        |  5 +----
> + lib/fuse_lowlevel.c |  9 +--------
> + 3 files changed, 3 insertions(+), 27 deletions(-)
> +
> +diff --git a/lib/fuse.c b/lib/fuse.c
> +index 49f57112a..c0d00edbc 100644
> +--- a/lib/fuse.c
> ++++ b/lib/fuse.c
> +@@ -2618,25 +2618,11 @@ void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
> + 	if (fs->op.init) {
> + 		uint64_t want_ext_default = conn->want_ext;
> + 		uint32_t want_default = fuse_lower_32_bits(conn->want_ext);
> +-		int rc;
> + 
> + 		conn->want = want_default;
> + 		fs->user_data = fs->op.init(conn, cfg);
> + 
> +-		rc = convert_to_conn_want_ext(conn, want_ext_default,
> +-					      want_default);
> +-
> +-		if (rc != 0) {
> +-			/*
> +-			 * This is a grave developer error, but
> +-			 * we cannot return an error here, as the function
> +-			 * signature does not allow it.
> +-			 */
> +-			fuse_log(
> +-				FUSE_LOG_ERR,
> +-				"fuse: Aborting due to invalid conn want flags.\n");
> +-			_exit(EXIT_FAILURE);
> +-		}
> ++		convert_to_conn_want_ext(conn, want_ext_default, want_default);
> + 	}
> + }
> + 
> +diff --git a/lib/fuse_i.h b/lib/fuse_i.h
> +index 48b8294f3..bf5e2ca41 100644
> +--- a/lib/fuse_i.h
> ++++ b/lib/fuse_i.h
> +@@ -245,11 +245,8 @@ static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn,
> + 	 */
> + 	if (conn->want != want_default &&
> + 	    fuse_lower_32_bits(conn->want_ext) != conn->want) {
> +-		if (conn->want_ext != want_ext_default) {
> +-			fuse_log(FUSE_LOG_ERR,
> +-				 "fuse: both 'want' and 'want_ext' are set\n");
> ++		if (conn->want_ext != want_ext_default)
> + 			return -EINVAL;
> +-		}
> + 
> + 		/* high bits from want_ext, low bits from want */
> + 		conn->want_ext = fuse_higher_32_bits(conn->want_ext) |
> +diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
> +index 9ebaaf08e..9ee88b160 100644
> +--- a/lib/fuse_lowlevel.c
> ++++ b/lib/fuse_lowlevel.c
> +@@ -2160,7 +2160,6 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
> + 	if (se->op.init) {
> + 		uint64_t want_ext_default = se->conn.want_ext;
> + 		uint32_t want_default = fuse_lower_32_bits(se->conn.want_ext);
> +-		int rc;
> + 
> + 		// Apply the first 32 bits of capable_ext to capable
> + 		se->conn.capable = fuse_lower_32_bits(se->conn.capable_ext);
> +@@ -2173,14 +2172,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
> + 		 * se->conn.want_ext
> + 		 * Userspace might still use conn.want - we need to convert it
> + 		 */
> +-		rc = convert_to_conn_want_ext(&se->conn, want_ext_default,
> ++		convert_to_conn_want_ext(&se->conn, want_ext_default,
> + 					      want_default);
> +-		if (rc != 0) {
> +-			fuse_reply_err(req, EPROTO);
> +-			se->error = -EPROTO;
> +-			fuse_session_exit(se);
> +-			return;
> +-		}
> + 	}
> + 
> + 	if (!want_flags_valid(se->conn.capable_ext, se->conn.want_ext)) {
> diff -Nru fuse3-3.17.2/debian/patches/series fuse3-3.17.2/debian/patches/series
> --- fuse3-3.17.2/debian/patches/series	2025-04-27 08:10:01.000000000 +0200
> +++ fuse3-3.17.2/debian/patches/series	2025-05-19 20:39:08.000000000 +0200
> @@ -1,2 +1,5 @@
>  Fix_meson_function_tests.patch
>  meson.build-make-special_funcs-check-more-reliable.patch
> +Make_conn-want-want_ext_conversion_non_fatal.patch
> +Add-container_of-and-ROUND_UP-macros.patch
> +Fix-fuse_apply_conn_info_opts.patch
> diff -Nru fuse3-3.17.2/debian/rules fuse3-3.17.2/debian/rules
> --- fuse3-3.17.2/debian/rules	2025-02-22 07:44:45.000000000 +0100
> +++ fuse3-3.17.2/debian/rules	2025-05-19 20:39:08.000000000 +0200
> @@ -10,7 +10,7 @@
>  
>  export DEB_BUILD_MAINT_OPTIONS = hardening=+all
>  
> -ifneq (,$(filter $(DEB_HOST_ARCH), armel m68k powerpc))
> +ifneq (,$(filter $(DEB_HOST_ARCH), arc armel m68k mips mipsel powerpc sh3 sh4 sparc))
>     export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed -latomic
>  endif
>  


-- 
Sebastian Ramacher


Reply to: