From: Bernd Schubert Date: Sat, 28 Dec 2024 14:10:03 +0000 (+0100) Subject: Add 64-bit conn::{capable,want}_ext fields X-Git-Tag: fuse-3.17.1-rc0~37^2~2 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=24f5b129c4e1b03ebbd05ac0c7673f306facea1a;p=qemu-gpiodev%2Flibfuse.git Add 64-bit conn::{capable,want}_ext fields The previous fields are left for ABI compatibility, although it is not beautiful to add that complexity when we have to increase the so-version as we had ABI breakage anyway. example/printcap is simplified to use an array, as every line would have needed to be modified anyway. Missing 'FUSE_CAP_PASSTHROUGH' was added. Signed-off-by: Bernd Schubert --- diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c index 309d8dd..5f1fde9 100644 --- a/example/passthrough_ll.c +++ b/example/passthrough_ll.c @@ -168,18 +168,20 @@ static bool lo_debug(fuse_req_t req) static void lo_init(void *userdata, struct fuse_conn_info *conn) { - struct lo_data *lo = (struct lo_data*) userdata; - - if (lo->writeback && - conn->capable & FUSE_CAP_WRITEBACK_CACHE) { - if (lo->debug) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); - conn->want |= FUSE_CAP_WRITEBACK_CACHE; + struct lo_data *lo = (struct lo_data *)userdata; + bool has_flag; + + if (lo->writeback) { + has_flag = fuse_set_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE); + if (lo->debug && has_flag) + fuse_log(FUSE_LOG_DEBUG, + "lo_init: activating writeback\n"); } if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { - if (lo->debug) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; + has_flag = fuse_set_feature_flag(conn, FUSE_CAP_FLOCK_LOCKS); + if (lo->debug && has_flag) + fuse_log(FUSE_LOG_DEBUG, + "lo_init: activating flock locks\n"); } /* Disable the receiving and processing of FUSE_INTERRUPT requests */ diff --git a/example/printcap.c b/example/printcap.c index 01b4d3f..82a7598 100644 --- a/example/printcap.c +++ b/example/printcap.c @@ -29,68 +29,63 @@ struct fuse_session *se; -static void pc_init(void *userdata, - struct fuse_conn_info *conn) +// Define a structure to hold capability information +struct cap_info { + uint64_t flag; + const char *name; +}; + +// Define an array of all capabilities +static const struct cap_info capabilities[] = { + {FUSE_CAP_ASYNC_READ, "FUSE_CAP_ASYNC_READ"}, + {FUSE_CAP_POSIX_LOCKS, "FUSE_CAP_POSIX_LOCKS"}, + {FUSE_CAP_ATOMIC_O_TRUNC, "FUSE_CAP_ATOMIC_O_TRUNC"}, + {FUSE_CAP_EXPORT_SUPPORT, "FUSE_CAP_EXPORT_SUPPORT"}, + {FUSE_CAP_DONT_MASK, "FUSE_CAP_DONT_MASK"}, + {FUSE_CAP_SPLICE_MOVE, "FUSE_CAP_SPLICE_MOVE"}, + {FUSE_CAP_SPLICE_READ, "FUSE_CAP_SPLICE_READ"}, + {FUSE_CAP_SPLICE_WRITE, "FUSE_CAP_SPLICE_WRITE"}, + {FUSE_CAP_FLOCK_LOCKS, "FUSE_CAP_FLOCK_LOCKS"}, + {FUSE_CAP_IOCTL_DIR, "FUSE_CAP_IOCTL_DIR"}, + {FUSE_CAP_AUTO_INVAL_DATA, "FUSE_CAP_AUTO_INVAL_DATA"}, + {FUSE_CAP_READDIRPLUS, "FUSE_CAP_READDIRPLUS"}, + {FUSE_CAP_READDIRPLUS_AUTO, "FUSE_CAP_READDIRPLUS_AUTO"}, + {FUSE_CAP_ASYNC_DIO, "FUSE_CAP_ASYNC_DIO"}, + {FUSE_CAP_WRITEBACK_CACHE, "FUSE_CAP_WRITEBACK_CACHE"}, + {FUSE_CAP_NO_OPEN_SUPPORT, "FUSE_CAP_NO_OPEN_SUPPORT"}, + {FUSE_CAP_PARALLEL_DIROPS, "FUSE_CAP_PARALLEL_DIROPS"}, + {FUSE_CAP_POSIX_ACL, "FUSE_CAP_POSIX_ACL"}, + {FUSE_CAP_CACHE_SYMLINKS, "FUSE_CAP_CACHE_SYMLINKS"}, + {FUSE_CAP_NO_OPENDIR_SUPPORT, "FUSE_CAP_NO_OPENDIR_SUPPORT"}, + {FUSE_CAP_EXPLICIT_INVAL_DATA, "FUSE_CAP_EXPLICIT_INVAL_DATA"}, + {FUSE_CAP_EXPIRE_ONLY, "FUSE_CAP_EXPIRE_ONLY"}, + {FUSE_CAP_SETXATTR_EXT, "FUSE_CAP_SETXATTR_EXT"}, + {FUSE_CAP_HANDLE_KILLPRIV, "FUSE_CAP_HANDLE_KILLPRIV"}, + {FUSE_CAP_HANDLE_KILLPRIV_V2, "FUSE_CAP_HANDLE_KILLPRIV_V2"}, + {FUSE_CAP_DIRECT_IO_ALLOW_MMAP, "FUSE_CAP_DIRECT_IO_ALLOW_MMAP"}, + {FUSE_CAP_NO_EXPORT_SUPPORT, "FUSE_CAP_NO_EXPORT_SUPPORT"}, + {FUSE_CAP_PASSTHROUGH, "FUSE_CAP_PASSTHROUGH"}, + // Add any new capabilities here + {0, NULL} // Sentinel to mark the end of the array +}; + +static void print_capabilities(struct fuse_conn_info *conn) +{ + printf("Capabilities:\n"); + for (const struct cap_info *cap = capabilities; cap->name != NULL; cap++) { + if (fuse_get_feature_flag(conn, cap->flag)) { + printf("\t%s\n", cap->name); + } + } +} + +static void pc_init(void *userdata, struct fuse_conn_info *conn) { (void) userdata; - + printf("Protocol version: %d.%d\n", conn->proto_major, conn->proto_minor); - printf("Capabilities:\n"); - if(conn->capable & FUSE_CAP_ASYNC_READ) - printf("\tFUSE_CAP_ASYNC_READ\n"); - if(conn->capable & FUSE_CAP_POSIX_LOCKS) - printf("\tFUSE_CAP_POSIX_LOCKS\n"); - if(conn->capable & FUSE_CAP_ATOMIC_O_TRUNC) - printf("\tFUSE_CAP_ATOMIC_O_TRUNC\n"); - if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) - printf("\tFUSE_CAP_EXPORT_SUPPORT\n"); - if(conn->capable & FUSE_CAP_DONT_MASK) - printf("\tFUSE_CAP_DONT_MASK\n"); - if(conn->capable & FUSE_CAP_SPLICE_MOVE) - printf("\tFUSE_CAP_SPLICE_MOVE\n"); - if(conn->capable & FUSE_CAP_SPLICE_READ) - printf("\tFUSE_CAP_SPLICE_READ\n"); - if(conn->capable & FUSE_CAP_SPLICE_WRITE) - printf("\tFUSE_CAP_SPLICE_WRITE\n"); - if(conn->capable & FUSE_CAP_FLOCK_LOCKS) - printf("\tFUSE_CAP_FLOCK_LOCKS\n"); - if(conn->capable & FUSE_CAP_IOCTL_DIR) - printf("\tFUSE_CAP_IOCTL_DIR\n"); - if(conn->capable & FUSE_CAP_AUTO_INVAL_DATA) - printf("\tFUSE_CAP_AUTO_INVAL_DATA\n"); - if(conn->capable & FUSE_CAP_READDIRPLUS) - printf("\tFUSE_CAP_READDIRPLUS\n"); - if(conn->capable & FUSE_CAP_READDIRPLUS_AUTO) - printf("\tFUSE_CAP_READDIRPLUS_AUTO\n"); - if(conn->capable & FUSE_CAP_ASYNC_DIO) - printf("\tFUSE_CAP_ASYNC_DIO\n"); - if(conn->capable & FUSE_CAP_WRITEBACK_CACHE) - printf("\tFUSE_CAP_WRITEBACK_CACHE\n"); - if(conn->capable & FUSE_CAP_NO_OPEN_SUPPORT) - printf("\tFUSE_CAP_NO_OPEN_SUPPORT\n"); - if(conn->capable & FUSE_CAP_PARALLEL_DIROPS) - printf("\tFUSE_CAP_PARALLEL_DIROPS\n"); - if(conn->capable & FUSE_CAP_POSIX_ACL) - printf("\tFUSE_CAP_POSIX_ACL\n"); - if(conn->capable & FUSE_CAP_CACHE_SYMLINKS) - printf("\tFUSE_CAP_CACHE_SYMLINKS\n"); - if(conn->capable & FUSE_CAP_NO_OPENDIR_SUPPORT) - printf("\tFUSE_CAP_NO_OPENDIR_SUPPORT\n"); - if(conn->capable & FUSE_CAP_EXPLICIT_INVAL_DATA) - printf("\tFUSE_CAP_EXPLICIT_INVAL_DATA\n"); - if(conn->capable & FUSE_CAP_EXPIRE_ONLY) - printf("\tFUSE_CAP_EXPIRE_ONLY\n"); - if(conn->capable & FUSE_CAP_SETXATTR_EXT) - printf("\tFUSE_CAP_SETXATTR_EXT\n"); - if(conn->capable & FUSE_CAP_HANDLE_KILLPRIV) - printf("\tFUSE_CAP_HANDLE_KILLPRIV\n"); - if(conn->capable & FUSE_CAP_HANDLE_KILLPRIV_V2) - printf("\tFUSE_CAP_HANDLE_KILLPRIV_V2\n"); - if(conn->capable & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) - printf("\tFUSE_CAP_DIRECT_IO_ALLOW_MMAP\n"); - if (conn->capable & FUSE_CAP_NO_EXPORT_SUPPORT) - printf("\tFUSE_CAP_NO_EXPORT_SUPPORT\n"); + print_capabilities(conn); fuse_session_exit(se); } @@ -110,7 +105,7 @@ int main(int argc, char **argv) perror("mkdtemp"); return 1; } - + printf("FUSE library version %s\n", fuse_pkgversion()); fuse_lowlevel_version(); diff --git a/include/fuse_common.h b/include/fuse_common.h index 2c866c4..fbf16ba 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -540,6 +540,10 @@ struct fuse_loop_config_v1 { * Some of the elements are read-write, these can be changed to * indicate the value requested by the filesystem. The requested * value must usually be smaller than the indicated value. + * + * Note: The `capable` and `want` fields are limited to 32 bits for + * ABI compatibility. For full 64-bit capability support, use the + * `capable_ext` and `want_ext` fields instead. */ struct fuse_conn_info { /** @@ -578,6 +582,8 @@ struct fuse_conn_info { /** * Capability flags that the kernel supports (read-only) + * + * Deprecated left over for ABI compatibility, use capable_ext */ uint32_t capable; @@ -586,6 +592,10 @@ struct fuse_conn_info { * * libfuse attempts to initialize this field with * reasonable default values before calling the init() handler. + * + * Deprecated left over for ABI compatibility. + * Use want_ext with the helper functions + * fuse_set_feature_flag() / fuse_unset_feature_flag() */ uint32_t want; @@ -679,10 +689,26 @@ struct fuse_conn_info { /* reserved bits for future use */ uint32_t padding : 31; + /** + * Extended capability flags that the kernel supports (read-only) + * This field provides full 64-bit capability support. + */ + uint64_t capable_ext; + + /** + * Extended capability flags that the filesystem wants to enable. + * This field provides full 64-bit capability support. + * + * Don't set this field directly, but use the helper functions + * fuse_set_feature_flag() / fuse_unset_feature_flag() + * + */ + uint64_t want_ext; + /** * For future use. */ - uint32_t reserved[20]; + uint32_t reserved[16]; }; fuse_static_assert(sizeof(struct fuse_conn_info) == 128, "Size of struct fuse_conn_info must be 128 bytes"); @@ -1076,8 +1102,8 @@ void fuse_loop_cfg_convert(struct fuse_loop_config *config, static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { - if (conn->capable & flag) { - conn->want |= flag; + if (conn->capable_ext & flag) { + conn->want_ext |= flag; return true; } return false; @@ -1086,7 +1112,13 @@ static inline bool fuse_set_feature_flag(struct fuse_conn_info *conn, static inline void fuse_unset_feature_flag(struct fuse_conn_info *conn, uint64_t flag) { - conn->want &= ~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; } /* ----------------------------------------------------------- * diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c index cd68787..5387f84 100644 --- a/lib/cuse_lowlevel.c +++ b/lib/cuse_lowlevel.c @@ -208,8 +208,10 @@ void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } se->conn.proto_major = arg->major; se->conn.proto_minor = arg->minor; - se->conn.capable = 0; - se->conn.want = 0; + + /* XXX This is not right.*/ + se->conn.capable_ext = 0; + se->conn.want_ext = 0; if (arg->major < 7) { fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n", diff --git a/lib/fuse.c b/lib/fuse.c index b327bab..a1537af 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -2619,8 +2619,7 @@ static void fuse_lib_init(void *data, struct fuse_conn_info *conn) struct fuse *f = (struct fuse *) data; fuse_create_context(f); - if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) - conn->want |= FUSE_CAP_EXPORT_SUPPORT; + fuse_set_feature_flag(conn, FUSE_CAP_EXPORT_SUPPORT); fuse_fs_init(f->fs, conn, &f->conf); if (f->conf.intr) { diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index a0d8647..d84c678 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -728,7 +728,7 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, goto fallback; if (se->conn.proto_minor < 14 || - !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) + !(se->conn.want_ext & FUSE_CAP_SPLICE_WRITE)) goto fallback; llp = fuse_ll_get_pipe(se); @@ -869,7 +869,7 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, splice_flags = 0; if ((flags & FUSE_BUF_SPLICE_MOVE) && - (se->conn.want & FUSE_CAP_SPLICE_MOVE)) + (se->conn.want_ext & FUSE_CAP_SPLICE_MOVE)) splice_flags |= SPLICE_F_MOVE; if (se->io != NULL && se->io->splice_send != NULL) { @@ -1410,7 +1410,7 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (req->se->op.open) req->se->op.open(req, nodeid, &fi); - else if (req->se->conn.want & FUSE_CAP_NO_OPEN_SUPPORT) + else if (req->se->conn.want_ext & FUSE_CAP_NO_OPEN_SUPPORT) fuse_reply_err(req, ENOSYS); else fuse_reply_open(req, &fi); @@ -1568,7 +1568,7 @@ static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (req->se->op.opendir) req->se->op.opendir(req, nodeid, &fi); - else if (req->se->conn.want & FUSE_CAP_NO_OPENDIR_SUPPORT) + else if (req->se->conn.want_ext & FUSE_CAP_NO_OPENDIR_SUPPORT) fuse_reply_err(req, ENOSYS); else fuse_reply_open(req, &fi); @@ -1651,7 +1651,7 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_session *se = req->se; - unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT); + unsigned int xattr_ext = !!(se->conn.want_ext & FUSE_CAP_SETXATTR_EXT); struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; char *name = xattr_ext ? PARAM(arg) : (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE; @@ -1882,7 +1882,7 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) struct fuse_file_info fi; if (flags & FUSE_IOCTL_DIR && - !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { + !(req->se->conn.want_ext & FUSE_CAP_IOCTL_DIR)) { fuse_reply_err(req, ENOTTY); return; } @@ -1997,6 +1997,27 @@ static bool want_flags_valid(uint64_t capable, uint64_t want) return true; } +/** + * Get the wanted capability flags, converting from old format if necessary + * Also applies the first 32 bits of capable_ext to capable + * + */ +static inline int convert_to_conn_want_ext(struct fuse_conn_info *conn, + uint64_t want_ext_default) +{ + /* Convert want to want_ext if necessary */ + if (conn->want != 0) { + if (conn->want_ext != want_ext_default) { + fuse_log(FUSE_LOG_ERR, + "fuse: both 'want' and 'want_ext' are set\n"); + return -EINVAL; + } + conn->want_ext |= conn->want; + } + + return 0; +} + /* Prevent bogus data races (bogus since "init" is called before * multi-threading becomes relevant */ static __attribute__((no_sanitize("thread"))) @@ -2021,8 +2042,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } se->conn.proto_major = arg->major; se->conn.proto_minor = arg->minor; - se->conn.capable = 0; - se->conn.want = 0; + se->conn.capable_ext = 0; + se->conn.want_ext = 0; memset(&outarg, 0, sizeof(outarg)); outarg.major = FUSE_KERNEL_VERSION; @@ -2048,45 +2069,45 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) if (inargflags & FUSE_INIT_EXT) inargflags = inargflags | (uint64_t) arg->flags2 << 32; if (inargflags & FUSE_ASYNC_READ) - se->conn.capable |= FUSE_CAP_ASYNC_READ; + se->conn.capable_ext |= FUSE_CAP_ASYNC_READ; if (inargflags & FUSE_POSIX_LOCKS) - se->conn.capable |= FUSE_CAP_POSIX_LOCKS; + se->conn.capable_ext |= FUSE_CAP_POSIX_LOCKS; if (inargflags & FUSE_ATOMIC_O_TRUNC) - se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; + se->conn.capable_ext |= FUSE_CAP_ATOMIC_O_TRUNC; if (inargflags & FUSE_EXPORT_SUPPORT) - se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; + se->conn.capable_ext |= FUSE_CAP_EXPORT_SUPPORT; if (inargflags & FUSE_DONT_MASK) - se->conn.capable |= FUSE_CAP_DONT_MASK; + se->conn.capable_ext |= FUSE_CAP_DONT_MASK; if (inargflags & FUSE_FLOCK_LOCKS) - se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; + se->conn.capable_ext |= FUSE_CAP_FLOCK_LOCKS; if (inargflags & FUSE_AUTO_INVAL_DATA) - se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; + se->conn.capable_ext |= FUSE_CAP_AUTO_INVAL_DATA; if (inargflags & FUSE_DO_READDIRPLUS) - se->conn.capable |= FUSE_CAP_READDIRPLUS; + se->conn.capable_ext |= FUSE_CAP_READDIRPLUS; if (inargflags & FUSE_READDIRPLUS_AUTO) - se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; + se->conn.capable_ext |= FUSE_CAP_READDIRPLUS_AUTO; if (inargflags & FUSE_ASYNC_DIO) - se->conn.capable |= FUSE_CAP_ASYNC_DIO; + se->conn.capable_ext |= FUSE_CAP_ASYNC_DIO; if (inargflags & FUSE_WRITEBACK_CACHE) - se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; + se->conn.capable_ext |= FUSE_CAP_WRITEBACK_CACHE; if (inargflags & FUSE_NO_OPEN_SUPPORT) - se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; + se->conn.capable_ext |= FUSE_CAP_NO_OPEN_SUPPORT; if (inargflags & FUSE_PARALLEL_DIROPS) - se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; + se->conn.capable_ext |= FUSE_CAP_PARALLEL_DIROPS; if (inargflags & FUSE_POSIX_ACL) - se->conn.capable |= FUSE_CAP_POSIX_ACL; + se->conn.capable_ext |= FUSE_CAP_POSIX_ACL; if (inargflags & FUSE_HANDLE_KILLPRIV) - se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; + se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV; if (inargflags & FUSE_HANDLE_KILLPRIV_V2) - se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2; + se->conn.capable_ext |= FUSE_CAP_HANDLE_KILLPRIV_V2; if (inargflags & FUSE_CACHE_SYMLINKS) - se->conn.capable |= FUSE_CAP_CACHE_SYMLINKS; + se->conn.capable_ext |= FUSE_CAP_CACHE_SYMLINKS; if (inargflags & FUSE_NO_OPENDIR_SUPPORT) - se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; + se->conn.capable_ext |= FUSE_CAP_NO_OPENDIR_SUPPORT; if (inargflags & FUSE_EXPLICIT_INVAL_DATA) - se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA; + se->conn.capable_ext |= FUSE_CAP_EXPLICIT_INVAL_DATA; if (inargflags & FUSE_SETXATTR_EXT) - se->conn.capable |= FUSE_CAP_SETXATTR_EXT; + se->conn.capable_ext |= FUSE_CAP_SETXATTR_EXT; if (!(inargflags & FUSE_MAX_PAGES)) { size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() @@ -2097,13 +2118,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) buf_reallocable = false; } if (inargflags & FUSE_DIRECT_IO_ALLOW_MMAP) - se->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; + se->conn.capable_ext |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; if (arg->minor >= 38 || (inargflags & FUSE_HAS_EXPIRE_ONLY)) - se->conn.capable |= FUSE_CAP_EXPIRE_ONLY; + se->conn.capable_ext |= FUSE_CAP_EXPIRE_ONLY; if (inargflags & FUSE_PASSTHROUGH) - se->conn.capable |= FUSE_CAP_PASSTHROUGH; + se->conn.capable_ext |= FUSE_CAP_PASSTHROUGH; if (inargflags & FUSE_NO_EXPORT_SUPPORT) - se->conn.capable |= FUSE_CAP_NO_EXPORT_SUPPORT; + se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT; } else { se->conn.max_readahead = 0; } @@ -2112,16 +2133,17 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) #ifdef HAVE_SPLICE #ifdef HAVE_VMSPLICE if ((se->io == NULL) || (se->io->splice_send != NULL)) { - se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; + se->conn.capable_ext |= FUSE_CAP_SPLICE_WRITE | + FUSE_CAP_SPLICE_MOVE; } #endif if ((se->io == NULL) || (se->io->splice_receive != NULL)) { - se->conn.capable |= FUSE_CAP_SPLICE_READ; + se->conn.capable_ext |= FUSE_CAP_SPLICE_READ; } #endif } if (se->conn.proto_minor >= 18) - se->conn.capable |= FUSE_CAP_IOCTL_DIR; + se->conn.capable_ext |= FUSE_CAP_IOCTL_DIR; /* Default settings for modern filesystems. * @@ -2130,9 +2152,10 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) * we can finally enable them by default (as long as they're * supported by the kernel). */ -#define LL_SET_DEFAULT(cond, cap) \ - if ((cond) && (se->conn.capable & (cap))) \ - se->conn.want |= (cap) +#define LL_SET_DEFAULT(cond, cap) \ + if ((cond)) \ + fuse_set_feature_flag(&se->conn, cap) + LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); @@ -2154,10 +2177,31 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) se->conn.time_gran = 1; se->got_init = 1; - if (se->op.init) + if (se->op.init) { + uint32_t want_ext_default = se->conn.want_ext; + int rc; + + // Apply the first 32 bits of capable_ext to capable + se->conn.capable = + (uint32_t)(se->conn.capable_ext & 0xFFFFFFFF); + se->op.init(se->userdata, &se->conn); - if (!want_flags_valid(se->conn.capable, se->conn.want)) { + /* + * se->conn.want is 32-bit value and deprecated in favour of + * 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); + 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)) { fuse_reply_err(req, EPROTO); se->error = -EPROTO; fuse_session_exit(se); @@ -2196,45 +2240,45 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) by the max_write option */ outargflags |= FUSE_BIG_WRITES; - if (se->conn.want & FUSE_CAP_ASYNC_READ) + if (se->conn.want_ext & FUSE_CAP_ASYNC_READ) outargflags |= FUSE_ASYNC_READ; - if (se->conn.want & FUSE_CAP_POSIX_LOCKS) + if (se->conn.want_ext & FUSE_CAP_POSIX_LOCKS) outargflags |= FUSE_POSIX_LOCKS; - if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) + if (se->conn.want_ext & FUSE_CAP_ATOMIC_O_TRUNC) outargflags |= FUSE_ATOMIC_O_TRUNC; - if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) + if (se->conn.want_ext & FUSE_CAP_EXPORT_SUPPORT) outargflags |= FUSE_EXPORT_SUPPORT; - if (se->conn.want & FUSE_CAP_DONT_MASK) + if (se->conn.want_ext & FUSE_CAP_DONT_MASK) outargflags |= FUSE_DONT_MASK; - if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) + if (se->conn.want_ext & FUSE_CAP_FLOCK_LOCKS) outargflags |= FUSE_FLOCK_LOCKS; - if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) + if (se->conn.want_ext & FUSE_CAP_AUTO_INVAL_DATA) outargflags |= FUSE_AUTO_INVAL_DATA; - if (se->conn.want & FUSE_CAP_READDIRPLUS) + if (se->conn.want_ext & FUSE_CAP_READDIRPLUS) outargflags |= FUSE_DO_READDIRPLUS; - if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) + if (se->conn.want_ext & FUSE_CAP_READDIRPLUS_AUTO) outargflags |= FUSE_READDIRPLUS_AUTO; - if (se->conn.want & FUSE_CAP_ASYNC_DIO) + if (se->conn.want_ext & FUSE_CAP_ASYNC_DIO) outargflags |= FUSE_ASYNC_DIO; - if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) + if (se->conn.want_ext & FUSE_CAP_WRITEBACK_CACHE) outargflags |= FUSE_WRITEBACK_CACHE; - if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS) + if (se->conn.want_ext & FUSE_CAP_PARALLEL_DIROPS) outargflags |= FUSE_PARALLEL_DIROPS; - if (se->conn.want & FUSE_CAP_POSIX_ACL) + if (se->conn.want_ext & FUSE_CAP_POSIX_ACL) outargflags |= FUSE_POSIX_ACL; - if (se->conn.want & FUSE_CAP_HANDLE_KILLPRIV) + if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV) outargflags |= FUSE_HANDLE_KILLPRIV; - if (se->conn.want & FUSE_CAP_HANDLE_KILLPRIV_V2) + if (se->conn.want_ext & FUSE_CAP_HANDLE_KILLPRIV_V2) outargflags |= FUSE_HANDLE_KILLPRIV_V2; - if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS) + if (se->conn.want_ext & FUSE_CAP_CACHE_SYMLINKS) outargflags |= FUSE_CACHE_SYMLINKS; - if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA) + if (se->conn.want_ext & FUSE_CAP_EXPLICIT_INVAL_DATA) outargflags |= FUSE_EXPLICIT_INVAL_DATA; - if (se->conn.want & FUSE_CAP_SETXATTR_EXT) + if (se->conn.want_ext & FUSE_CAP_SETXATTR_EXT) outargflags |= FUSE_SETXATTR_EXT; - if (se->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) + if (se->conn.want_ext & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP; - if (se->conn.want & FUSE_CAP_PASSTHROUGH) { + if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) { outargflags |= FUSE_PASSTHROUGH; /* * outarg.max_stack_depth includes the fuse stack layer, @@ -2242,7 +2286,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) */ outarg.max_stack_depth = se->conn.max_backing_stack_depth + 1; } - if (se->conn.want & FUSE_CAP_NO_EXPORT_SUPPORT) + if (se->conn.want_ext & FUSE_CAP_NO_EXPORT_SUPPORT) outargflags |= FUSE_NO_EXPORT_SUPPORT; if (inargflags & FUSE_INIT_EXT) { @@ -2282,7 +2326,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.congestion_threshold); fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); - if (se->conn.want & FUSE_CAP_PASSTHROUGH) + if (se->conn.want_ext & FUSE_CAP_PASSTHROUGH) fuse_log(FUSE_LOG_DEBUG, " max_stack_depth=%u\n", outarg.max_stack_depth); } @@ -2467,7 +2511,7 @@ int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent if (!se) return -EINVAL; - if (!(se->conn.capable & FUSE_CAP_EXPIRE_ONLY)) + if (!(se->conn.capable_ext & FUSE_CAP_EXPIRE_ONLY)) return -ENOSYS; return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY); @@ -3006,7 +3050,7 @@ static int _fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf tmpbuf; if (se->conn.proto_minor < 14 || - !(se->conn.want & FUSE_CAP_SPLICE_READ)) + !(se->conn.want_ext & FUSE_CAP_SPLICE_READ)) goto fallback; llp = fuse_ll_get_pipe(se); diff --git a/test/test_write_cache.c b/test/test_write_cache.c index cc827c7..d3c7af0 100644 --- a/test/test_write_cache.c +++ b/test/test_write_cache.c @@ -65,7 +65,7 @@ static void tfs_init (void *userdata, struct fuse_conn_info *conn) (void) userdata; if(options.writeback) { - assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE); + assert(fuse_get_feature_flag(conn, FUSE_CAP_WRITEBACK_CACHE)); conn->want |= FUSE_CAP_WRITEBACK_CACHE; } }