diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 04:40:51 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 04:40:51 +0000 |
commit | 244a95fbe510ebcff4e146af638e5e080ecf28f0 (patch) | |
tree | c2ff0f50dd51293b5a8073bf01d2848fc7504746 | |
parent | 63199a4c44c372d70a78c3a24badecde54344b4c (diff) | |
parent | 3bcd6a5bd79717fd982b3c94abe4d6224d5a908a (diff) | |
download | libfuse-android14-mainline-appsearch-release.tar.gz |
Snap for 10453563 from 3bcd6a5bd79717fd982b3c94abe4d6224d5a908a to mainline-appsearch-releaseaml_ase_341510000aml_ase_341410000aml_ase_341310010aml_ase_341113000aml_ase_340913000android14-mainline-appsearch-release
Change-Id: I8ff96102d003b5fc53ef34d3e86e2828cafeb4c2
-rw-r--r-- | Android.bp | 11 | ||||
-rw-r--r-- | include/fuse_common.h | 13 | ||||
-rw-r--r-- | include/fuse_kernel.h | 283 | ||||
-rw-r--r-- | include/fuse_lowlevel.h | 46 | ||||
-rw-r--r-- | lib/fuse_lowlevel.c | 104 |
5 files changed, 406 insertions, 51 deletions
@@ -41,7 +41,10 @@ license { cc_defaults { name: "libfuse_default_flags", - local_include_dirs: ["include/", "lib/"], + local_include_dirs: [ + "include/", + "lib/", + ], cflags: [ "-D_FILE_OFFSET_BITS=64", "-DFUSERMOUNT_DIR=\"/system/bin\"", @@ -56,7 +59,6 @@ cc_defaults { "-Wno-unused-variable", ], - clang: true, sdk_version: "current", min_sdk_version: "30", @@ -70,7 +72,10 @@ cc_library { "libfuse_default_flags", ], - export_include_dirs: ["include", "lib"], + export_include_dirs: [ + "include", + "lib", + ], version_script: "lib/fuse_versionscript", diff --git a/include/fuse_common.h b/include/fuse_common.h index 3f836d7..efaf754 100644 --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -400,6 +400,13 @@ struct fuse_loop_config { #define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25) /** + * Indicates that an extended 'struct fuse_setxattr' is used by the kernel + * side - extra_flags are passed, which are used (as of now by acl) processing. + * For example FUSE_SETXATTR_ACL_KILL_SGID might be set. + */ +#define FUSE_CAP_SETXATTR_EXT (1 << 27) + +/** * Indicates support for passthrough mode access for read/write operations. * * If this flag is set in the `capable` field of the `fuse_conn_info` @@ -409,7 +416,7 @@ struct fuse_loop_config { * * This feature is disabled by default. */ -#define FUSE_CAP_PASSTHROUGH (1 << 31) +#define FUSE_CAP_PASSTHROUGH (1LL << 63) /** * Ioctl flags @@ -473,7 +480,7 @@ struct fuse_conn_info { /** * Capability flags that the kernel supports (read-only) */ - unsigned capable; + uint64_t capable; /** * Capability flags that the filesystem wants to enable. @@ -481,7 +488,7 @@ struct fuse_conn_info { * libfuse attempts to initialize this field with * reasonable default values before calling the init() handler. */ - unsigned want; + uint64_t want; /** * Maximum number of pending "background" requests. A diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 70278fa..7928a2f 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -38,6 +38,43 @@ * * Protocol changelog: * + * 7.1: + * - add the following messages: + * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, + * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, + * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, + * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, + * FUSE_RELEASEDIR + * - add padding to messages to accommodate 32-bit servers on 64-bit kernels + * + * 7.2: + * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags + * - add FUSE_FSYNCDIR message + * + * 7.3: + * - add FUSE_ACCESS message + * - add FUSE_CREATE message + * - add filehandle to fuse_setattr_in + * + * 7.4: + * - add frsize to fuse_kstatfs + * - clean up request size limit checking + * + * 7.5: + * - add flags and max_write to fuse_init_out + * + * 7.6: + * - add max_readahead to fuse_init_in and fuse_init_out + * + * 7.7: + * - add FUSE_INTERRUPT message + * - add POSIX file lock support + * + * 7.8: + * - add lock_owner and flags fields to fuse_release_in + * - add FUSE_BMAP message + * - add FUSE_DESTROY message + * * 7.9: * - new fuse_getattr_in input argument of GETATTR * - add lk_flags in fuse_lk_in @@ -133,6 +170,30 @@ * * 7.31 * - add FUSE_WRITE_KILL_PRIV flag + * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING + * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag + * + * 7.32 + * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS + * + * 7.33 + * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID + * - add FUSE_OPEN_KILL_SUIDGID + * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT + * - add FUSE_SETXATTR_ACL_KILL_SGID + * + * 7.34 + * - add FUSE_SYNCFS + * + * 7.35 + * - add FOPEN_NOFLUSH + * + * 7.36 + * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag + * - add flags2 to fuse_init_in and fuse_init_out + * - add FUSE_SECURITY_CTX init flag + * - add security context to create, mkdir, symlink, and mknod requests + * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX */ #ifndef _LINUX_FUSE_H @@ -168,7 +229,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 31 +#define FUSE_KERNEL_MINOR_VERSION 36 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -192,7 +253,7 @@ struct fuse_attr { uint32_t gid; uint32_t rdev; uint32_t blksize; - uint32_t padding; + uint32_t flags; }; struct fuse_kstatfs { @@ -229,6 +290,7 @@ struct fuse_file_lock { #define FATTR_MTIME_NOW (1 << 8) #define FATTR_LOCKOWNER (1 << 9) #define FATTR_CTIME (1 << 10) +#define FATTR_KILL_SUIDGID (1 << 11) /** * Flags returned by the OPEN request @@ -238,12 +300,14 @@ struct fuse_file_lock { * FOPEN_NONSEEKABLE: the file is not seekable * FOPEN_CACHE_DIR: allow caching this directory * FOPEN_STREAM: the file is stream-like (no file position at all) + * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_NONSEEKABLE (1 << 2) #define FOPEN_CACHE_DIR (1 << 3) #define FOPEN_STREAM (1 << 4) +#define FOPEN_NOFLUSH (1 << 5) /** * INIT request/reply flags @@ -274,6 +338,21 @@ struct fuse_file_lock { * FUSE_CACHE_SYMLINKS: cache READLINK responses * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request + * FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for + * foffset and moffset fields in struct + * fuse_setupmapping_out and fuse_removemapping_one. + * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts + * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc. + * Upon write/truncate suid/sgid is only killed if caller + * does not have CAP_FSETID. Additionally upon + * write/truncate sgid is killed only if file has group + * execute permission. (Same as Linux VFS behavior). + * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in + * FUSE_INIT_EXT: extended fuse_init_in request + * FUSE_INIT_RESERVED: reserved, do not use + * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and + * mknod + * FUSE_HAS_INODE_DAX: use per inode DAX */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -301,7 +380,27 @@ struct fuse_file_lock { #define FUSE_CACHE_SYMLINKS (1 << 23) #define FUSE_NO_OPENDIR_SUPPORT (1 << 24) #define FUSE_EXPLICIT_INVAL_DATA (1 << 25) -#define FUSE_PASSTHROUGH (1 << 31) +#define FUSE_MAP_ALIGNMENT (1 << 26) +#define FUSE_SUBMOUNTS (1 << 27) +#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28) +#define FUSE_SETXATTR_EXT (1 << 29) +#define FUSE_INIT_EXT (1 << 30) +#define FUSE_INIT_RESERVED (1 << 31) +/* bits 32..63 get shifted down 32 bits into the flags2 field */ +#define FUSE_SECURITY_CTX (1ULL << 32) +#define FUSE_HAS_INODE_DAX (1ULL << 33) + +/* + * For FUSE < 7.36 FUSE_PASSTHROUGH has value (1 << 31). + * This condition check is not really required, but would prevent having a + * broken commit in the tree. + */ +#if FUSE_KERNEL_VERSION > 7 || \ + (FUSE_KERNEL_VERSION == 7 && FUSE_KERNEL_MINOR_VERSION >= 36) +#define FUSE_PASSTHROUGH (1ULL << 63) +#else +#define FUSE_PASSTHROUGH (1 << 31) +#endif /** * CUSE INIT request/reply flags @@ -331,11 +430,14 @@ struct fuse_file_lock { * * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed * FUSE_WRITE_LOCKOWNER: lock_owner field is valid - * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits + * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits */ #define FUSE_WRITE_CACHE (1 << 0) #define FUSE_WRITE_LOCKOWNER (1 << 1) -#define FUSE_WRITE_KILL_PRIV (1 << 2) +#define FUSE_WRITE_KILL_SUIDGID (1 << 2) + +/* Obsolete alias; this flag implies killing suid/sgid only. */ +#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID /** * Read flags @@ -377,6 +479,27 @@ struct fuse_file_lock { */ #define FUSE_FSYNC_FDATASYNC (1 << 0) +/** + * fuse_attr flags + * + * FUSE_ATTR_SUBMOUNT: Object is a submount root + * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode + */ +#define FUSE_ATTR_SUBMOUNT (1 << 0) +#define FUSE_ATTR_DAX (1 << 1) + +/** + * Open flags + * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable + */ +#define FUSE_OPEN_KILL_SUIDGID (1 << 0) + +/** + * setxattr flags + * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set + */ +#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -423,12 +546,19 @@ enum fuse_opcode { FUSE_RENAME2 = 45, FUSE_LSEEK = 46, FUSE_COPY_FILE_RANGE = 47, + FUSE_SETUPMAPPING = 48, + FUSE_REMOVEMAPPING = 49, + FUSE_SYNCFS = 50, /* Android specific operations */ FUSE_CANONICAL_PATH = 2016, /* CUSE specific operations */ - CUSE_INIT = 4096 + CUSE_INIT = 4096, + + /* Reserved opcodes: helpful to detect structure endian-ness */ + CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ + FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ }; enum fuse_notify_code { @@ -438,7 +568,7 @@ enum fuse_notify_code { FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, - FUSE_NOTIFY_CODE_MAX + FUSE_NOTIFY_CODE_MAX, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -546,14 +676,14 @@ struct fuse_setattr_in { struct fuse_open_in { uint32_t flags; - uint32_t unused; + uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_create_in { uint32_t flags; uint32_t mode; uint32_t umask; - uint32_t padding; + uint32_t open_flags; /* FUSE_OPEN_... */ }; struct fuse_open_out { @@ -586,6 +716,12 @@ struct fuse_read_in { uint32_t padding; }; +struct fuse_read_out { + uint64_t offset; + uint32_t size; + uint32_t again; +}; + struct fuse_passthrough_out_v0 { uint32_t fd; /* For future implementation */ @@ -622,9 +758,13 @@ struct fuse_fsync_in { uint32_t padding; }; +#define FUSE_COMPAT_SETXATTR_IN_SIZE 8 + struct fuse_setxattr_in { uint32_t size; uint32_t flags; + uint32_t setxattr_flags; + uint32_t padding; }; struct fuse_getxattr_in { @@ -659,6 +799,8 @@ struct fuse_init_in { uint32_t minor; uint32_t max_readahead; uint32_t flags; + uint32_t flags2; + uint32_t unused[11]; }; #define FUSE_COMPAT_INIT_OUT_SIZE 8 @@ -674,8 +816,9 @@ struct fuse_init_out { uint32_t max_write; uint32_t time_gran; uint16_t max_pages; - uint16_t padding; - uint32_t unused[8]; + uint16_t map_alignment; + uint32_t flags2; + uint32_t unused[7]; }; #define CUSE_INIT_INFO_MAX 4096 @@ -766,7 +909,7 @@ struct fuse_in_header { uint32_t uid; uint32_t gid; uint32_t pid; - uint32_t padding; + uint32_t error_in; }; struct fuse_out_header { @@ -783,9 +926,12 @@ struct fuse_dirent { char name[]; }; -#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) \ +/* Align variable length records to 64bit boundary */ +#define FUSE_REC_ALIGN(x) \ (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x) #define FUSE_DIRENT_SIZE(d) \ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) @@ -844,7 +990,8 @@ struct fuse_notify_retrieve_in { }; /* Device ioctls: */ -#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) +#define FUSE_DEV_IOC_MAGIC 229 +#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) #define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V0 _IOW(229, 1, struct fuse_passthrough_out_v0) #define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V1 _IOW(229, 127, struct fuse_passthrough_out_v0) #define FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2 _IOW(229, 126, uint32_t) @@ -870,42 +1017,104 @@ struct fuse_copy_file_range_in { uint64_t flags; }; +#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) +#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1) +struct fuse_setupmapping_in { + /* An already open handle */ + uint64_t fh; + /* Offset into the file to start the mapping */ + uint64_t foffset; + /* Length of mapping required */ + uint64_t len; + /* Flags, FUSE_SETUPMAPPING_FLAG_* */ + uint64_t flags; + /* Offset in Memory Window */ + uint64_t moffset; +}; + +struct fuse_removemapping_in { + /* number of fuse_removemapping_one follows */ + uint32_t count; +}; + +struct fuse_removemapping_one { + /* Offset into the dax window start the unmapping */ + uint64_t moffset; + /* Length of mapping required */ + uint64_t len; +}; + +#define FUSE_REMOVEMAPPING_MAX_ENTRY \ + (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) + +struct fuse_syncfs_in { + uint64_t padding; +}; + +/* + * For each security context, send fuse_secctx with size of security context + * fuse_secctx will be followed by security context name and this in turn + * will be followed by actual context label. + * fuse_secctx, name, context + */ +struct fuse_secctx { + uint32_t size; + uint32_t padding; +}; + +/* + * Contains the information about how many fuse_secctx structures are being + * sent and what's the total size of all security contexts (including + * size of fuse_secctx_header). + * + */ +struct fuse_secctx_header { + uint32_t size; + uint32_t nr_secctx; +}; + /** Export fuse_args only for bpf */ #ifdef __KERNEL__ struct fuse_mount; +/* + * Fuse BPF Args + * + * Used to communicate with bpf programs to allow checking or altering certain values. + * The end_offset allows the bpf verifier to check boundaries statically. This reflects + * the ends of the buffer. size shows the length that was actually used. + * + */ + /** One input argument of a request */ -struct fuse_in_arg { - unsigned size; +struct fuse_bpf_in_arg { + uint32_t size; const void *value; + const void *end_offset; }; /** One output argument of a request */ -struct fuse_arg { - unsigned size; +struct fuse_bpf_arg { + uint32_t size; void *value; + void *end_offset; }; -struct fuse_args { +#define FUSE_MAX_IN_ARGS 5 +#define FUSE_MAX_OUT_ARGS 3 + +#define FUSE_BPF_FORCE (1 << 0) +#define FUSE_BPF_OUT_ARGVAR (1 << 6) + +struct fuse_bpf_args { uint64_t nodeid; uint32_t opcode; - unsigned short in_numargs; - unsigned short out_numargs; - int force:1; - int noreply:1; - int nocreds:1; - int in_pages:1; - int out_pages:1; - int out_argvar:1; - int page_zeroing:1; - int page_replace:1; - int may_block:1; - struct fuse_in_arg in_args[5]; - struct fuse_arg out_args[3]; - void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error); - - /* Path used for completing d_canonical_path */ - struct path *canonical_path; + uint32_t error_in; + uint32_t in_numargs; + uint32_t out_numargs; + uint32_t flags; + struct fuse_bpf_in_arg in_args[FUSE_MAX_IN_ARGS]; + struct fuse_bpf_arg out_args[FUSE_MAX_OUT_ARGS]; }; #endif diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index c591f71..92370ea 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -143,6 +143,12 @@ struct fuse_forget_data { #define FUSE_SET_ATTR_CTIME (1 << 10) /* ----------------------------------------------------------- * + * structs from fuse_kernel.h * + * ----------------------------------------------------------- */ +struct fuse_entry_out; +struct fuse_entry_bpf_out; + +/* ----------------------------------------------------------- * * Request methods and replies * * ----------------------------------------------------------- */ @@ -219,6 +225,25 @@ struct fuse_lowlevel_ops { void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); /** + * post filter a lookup + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param error_in the error, or 0, of the lookup + * @param name the name that was looked up + * @param feo the fuse entry out struct from the lookup + * @param febo the fuse entry bpf out struct from the lookup + */ + void (*lookup_postfilter)(fuse_req_t req, fuse_ino_t parent, + uint32_t error_in, const char *name, + struct fuse_entry_out *feo, + struct fuse_entry_bpf_out *febo); + + /** * Forget about an inode * * This function is called when the kernel removes an inode @@ -743,6 +768,27 @@ struct fuse_lowlevel_ops { struct fuse_file_info *fi); /** + * Read directory postfilter + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param error_in the error from the readdir + * @param off_in offset to continue reading the directory stream before backing + * @param off_out offset to continue reading the directory stream after backing + * @param size_out length in bytes of dirents + * @param dirents array of dirents read by backing + * @param fi file information + */ + void (*readdirpostfilter)(fuse_req_t req, fuse_ino_t ino, uint32_t error_in, + off_t off_in, off_t off_out, size_t size_out, + const void *dirents, struct fuse_file_info *fi); + + /** * Release an open directory * * For every opendir call there will be exactly one releasedir diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index aee22b4..4c75e3b 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -421,6 +421,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) { if (!e->ino && req->se->conn.proto_minor < 4) return fuse_reply_err(req, ENOENT); memset(&arg, 0, sizeof(arg)); + fill_entry(&arg, e); if (extended_args) { memset(&bpf_arg, 0, sizeof(bpf_arg)); @@ -435,7 +436,6 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param* e) { return send_reply_ok(req, &arg_ext, size); } else { - fill_entry(&arg, e); return send_reply_ok(req, &arg, size); } } @@ -1187,6 +1187,28 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) fuse_reply_err(req, ENOSYS); } +static void do_lookup_postfilter(fuse_req_t req, fuse_ino_t nodeid, uint32_t error_in, + const void *inarg, size_t size) +{ + if (req->se->op.lookup_postfilter) { + char *name = (char *) inarg; + size_t namelen = strlen(name); + + if (size != namelen + 1 + sizeof(struct fuse_entry_out) + + sizeof(struct fuse_entry_bpf_out)) { + fuse_log(FUSE_LOG_ERR, "%s: Bad size", __func__); + fuse_reply_err(req, EIO); + } else { + struct fuse_entry_out *feo = (void *) (name + namelen + 1); + struct fuse_entry_bpf_out *febo = (char *) feo + sizeof(*feo); + + req->se->op.lookup_postfilter(req, nodeid, error_in, name, feo, + febo); + } + } else + fuse_reply_err(req, ENOSYS); +} + static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; @@ -1629,6 +1651,26 @@ static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) fuse_reply_err(req, ENOSYS); } +static void do_readdir_postfilter(fuse_req_t req, fuse_ino_t nodeid, + uint32_t error_in, const void *inarg, + size_t size) { + struct fuse_read_in *fri = (struct fuse_read_in *) inarg; + struct fuse_read_out *fro = (struct fuse_read_out *) (fri + 1); + struct fuse_dirent *dirents = (struct fuse_dirent *) (fro + 1); + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = fri->fh; + + if (req->se->op.readdirpostfilter) + req->se->op.readdirpostfilter(req, nodeid, error_in, fri->offset, + fro->offset, + size - sizeof(*fri) - sizeof(*fro), + dirents, &fi); + else + fuse_reply_err(req, ENOSYS); +} + static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_release_in *arg = (struct fuse_release_in *) inarg; @@ -1677,10 +1719,14 @@ 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); struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; - char *name = PARAM(arg); + char *name = xattr_ext ? PARAM(arg) : + (char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE; char *value = name + strlen(name) + 1; + /* XXX:The API should be extended to support extra_flags/setxattr_flags */ if (req->se->op.setxattr) req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); @@ -2013,6 +2059,7 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) struct fuse_session *se = req->se; size_t bufsize = se->bufsize; size_t outargsize = sizeof(outarg); + int extended_flags; (void) nodeid; if (se->debug) { @@ -2032,6 +2079,10 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.major = FUSE_KERNEL_VERSION; outarg.minor = FUSE_KERNEL_MINOR_VERSION; + extended_flags = arg->major > 7 || (arg->major == 7 && arg->minor >= 36); + fuse_log(FUSE_LOG_DEBUG, "fuse: protocol version: %u.%u, extended flags: %d\n", + arg->major, arg->minor, extended_flags); + if (arg->major < 7) { fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", arg->major, arg->minor); @@ -2084,6 +2135,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; if (arg->flags & FUSE_EXPLICIT_INVAL_DATA) se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA; + if (arg->flags & FUSE_SETXATTR_EXT) + se->conn.capable |= FUSE_CAP_SETXATTR_EXT; if (!(arg->flags & FUSE_MAX_PAGES)) { size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() @@ -2092,8 +2145,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) bufsize = max_bufsize; } } - if (arg->flags & FUSE_PASSTHROUGH) - se->conn.capable |= FUSE_PASSTHROUGH; + if (extended_flags) { + if (arg->flags2 & (1 << 31)) + se->conn.capable |= FUSE_CAP_PASSTHROUGH; + } else { + if (arg->flags & (1 << 31)) + se->conn.capable |= FUSE_CAP_PASSTHROUGH; + } } else { se->conn.max_readahead = 0; } @@ -2206,12 +2264,18 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) outarg.flags |= FUSE_WRITEBACK_CACHE; if (se->conn.want & FUSE_CAP_POSIX_ACL) outarg.flags |= FUSE_POSIX_ACL; - if (se->conn.want & FUSE_CAP_PASSTHROUGH) - outarg.flags |= FUSE_PASSTHROUGH; + if (se->conn.want & FUSE_CAP_PASSTHROUGH) { + if (extended_flags) + outarg.flags2 |= (1 << 31); + else + outarg.flags |= (1 << 31); + } if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS) outarg.flags |= FUSE_CACHE_SYMLINKS; if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA) outarg.flags |= FUSE_EXPLICIT_INVAL_DATA; + if (se->conn.want & FUSE_CAP_SETXATTR_EXT) + outarg.flags |= FUSE_SETXATTR_EXT; outarg.max_readahead = se->conn.max_readahead; outarg.max_write = se->conn.max_write; if (se->conn.proto_minor >= 13) { @@ -2583,7 +2647,7 @@ static struct { [FUSE_GETATTR] = { do_getattr, "GETATTR" }, [FUSE_SETATTR] = { do_setattr, "SETATTR" }, [FUSE_READLINK] = { do_readlink, "READLINK" }, - [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" }, + [FUSE_CANONICAL_PATH] = { do_canonical_path, "CANONICAL_PATH" }, [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, [FUSE_MKNOD] = { do_mknod, "MKNOD" }, [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, @@ -2627,6 +2691,18 @@ static struct { [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, }; +static struct { + void (*func)( fuse_req_t, fuse_ino_t, const void *); + const char *name; +} fuse_ll_prefilter_ops[] = {}; + +static struct { + void (*func)( fuse_req_t, fuse_ino_t, uint32_t, const void *, size_t size); +} fuse_ll_postfilter_ops[] = { + [FUSE_LOOKUP] = {do_lookup_postfilter}, + [FUSE_READDIR] = {do_readdir_postfilter}, +}; + #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) static const char *opname(enum fuse_opcode opcode) @@ -2804,8 +2880,20 @@ void fuse_session_process_buf_int(struct fuse_session *se, do_write_buf(req, in->nodeid, inarg, buf); else if (in->opcode == FUSE_NOTIFY_REPLY) do_notify_reply(req, in->nodeid, inarg, buf); - else + else if (!opcode_filter) fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + else if (opcode_filter == FUSE_PREFILTER && fuse_ll_prefilter_ops[in->opcode].func) + fuse_ll_prefilter_ops[in->opcode].func(req, in->nodeid, inarg); + else if (opcode_filter == FUSE_POSTFILTER + && fuse_ll_postfilter_ops[in->opcode].func) + fuse_ll_postfilter_ops[in->opcode].func( + req, in->nodeid, in->error_in, inarg, + buf->size - sizeof(struct fuse_in_header)); + else { + fuse_log(FUSE_LOG_ERR, "Bad opcode"); + err = ENOSYS; + goto reply_err; + } out_free: free(mbuf); |