Snap for 10453938 from 3bcd6a5bd79717fd982b3c94abe4d6224d5a908a to mainline-odp-release

Change-Id: Idecbb1d1eafbefcda7a42836e279fb7db2c644e8
diff --git a/Android.bp b/Android.bp
index c862300..5373206 100644
--- a/Android.bp
+++ b/Android.bp
@@ -41,7 +41,10 @@
 
 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 @@
         "-Wno-unused-variable",
     ],
 
-    clang: true,
     sdk_version: "current",
     min_sdk_version: "30",
 
@@ -70,7 +72,10 @@
         "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 @@
 #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 @@
  *
  * This feature is disabled by default.
  */
-#define FUSE_CAP_PASSTHROUGH            (1 << 31)
+#define FUSE_CAP_PASSTHROUGH            (1LL << 63)
 
 /**
  * Ioctl flags
@@ -473,7 +480,7 @@
 	/**
 	 * 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 @@
 	 * 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 @@
 	uint32_t	gid;
 	uint32_t	rdev;
 	uint32_t	blksize;
-	uint32_t	padding;
+	uint32_t	flags;
 };
 
 struct fuse_kstatfs {
@@ -229,6 +290,7 @@
 #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 @@
  * 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 @@
  * 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 @@
 #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 @@
  *
  * 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 @@
  */
 #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 @@
 	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 @@
 	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_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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	uint32_t	uid;
 	uint32_t	gid;
 	uint32_t	pid;
-	uint32_t	padding;
+	uint32_t	error_in;
 };
 
 struct fuse_out_header {
@@ -783,9 +926,12 @@
 	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 @@
 };
 
 /* 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 @@
 	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 @@
 #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 @@
 	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_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 @@
     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 @@
 
         return send_reply_ok(req, &arg_ext, size);
     } else {
-        fill_entry(&arg, e);
         return send_reply_ok(req, &arg, size);
     }
 }
@@ -1187,6 +1187,28 @@
 		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 @@
 		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_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 @@
 	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 @@
 	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 @@
 			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 @@
 				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 @@
 		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 @@
 	[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 @@
 	[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 @@
 		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);