[PATCH] fuse: Add open-gettr for fuse-file-open

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

 



This is to update attributes on open to achieve close-to-open
coherency even if an inode has a attribute cache timeout.

Signed-off-by: Bernd Schubert <bschubert@xxxxxxx>

---
libfuse patch:
https://github.com/libfuse/libfuse/pull/1020
(FUSE_OPENDIR_GETATTR still missing at time of writing)

Note: This does not make use of existing atomic-open patches
as these are more complex than two new opcodes for open-getattr.

Note2: This is an alternative to Joannes patch that adds
FOPEN_FETCH_ATTR, which would need to kernel/userspace transitions
https://lore.kernel.org/all/20240813212149.1909627-1-joannelkoong@xxxxxxxxx/

Question for reviewers:
- Should this better use statx fields? Probably not needed for
  coherency?
- Should this introduce a new struct that contains
  struct fuse_open_out and struct fuse_attr_out, with
  additional padding between them to avoid incompat issues
  if either struct should be extended?
---
 fs/fuse/file.c            | 94 ++++++++++++++++++++++++++++++++++++++-
 fs/fuse/fuse_i.h          |  7 +++
 fs/fuse/ioctl.c           |  2 +-
 include/uapi/linux/fuse.h |  5 +++
 4 files changed, 105 insertions(+), 3 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f39456c65ed7..d470e6a2b3d4 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -51,6 +51,78 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
 	return fuse_simple_request(fm, &args);
 }
 
+/*
+ * Open the file and update inode attributes
+ */
+static int fuse_file_open_getattr(struct fuse_mount *fm, u64 nodeid,
+				  struct inode *inode, unsigned int open_flags,
+				  int opcode,
+				  struct fuse_open_out *open_outargp)
+{
+	struct fuse_conn *fc = fm->fc;
+	u64 attr_version = fuse_get_attr_version(fc);
+	struct fuse_open_in inarg;
+	struct fuse_attr_out attr_outarg;
+	FUSE_ARGS(args);
+	int err;
+
+	/* convert the opcode from plain open to open-with-getattr */
+	if (opcode == FUSE_OPEN) {
+		if (fc->no_open_getattr)
+			return -ENOSYS;
+		opcode = FUSE_OPEN_GETATTR;
+	} else {
+		if (fc->no_opendir_getattr)
+			return -ENOSYS;
+		opcode = FUSE_OPENDIR_GETATTR;
+	}
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.flags = open_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
+	if (!fm->fc->atomic_o_trunc)
+		inarg.flags &= ~O_TRUNC;
+
+	if (fm->fc->handle_killpriv_v2 &&
+	    (inarg.flags & O_TRUNC) && !capable(CAP_FSETID)) {
+		inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
+	}
+
+	args.opcode = opcode;
+	args.nodeid = nodeid;
+	args.in_numargs = 1;
+	args.in_args[0].size = sizeof(inarg);
+	args.in_args[0].value = &inarg;
+	args.out_numargs = 2;
+	args.out_args[0].size = sizeof(*open_outargp);
+	args.out_args[0].value = open_outargp;
+	args.out_args[1].size = sizeof(attr_outarg);
+	args.out_args[1].value = &attr_outarg;
+
+	err = fuse_simple_request(fm, &args);
+	if (err) {
+		if (err == -ENOSYS) {
+			if (opcode == FUSE_OPEN)
+				fc->no_open_getattr = 1;
+			else
+				fc->no_opendir_getattr = 1;
+		}
+		return err;
+	}
+
+	err = -EIO;
+	if (fuse_invalid_attr(&attr_outarg.attr) ||
+	    inode_wrong_type(inode, attr_outarg.attr.mode)) {
+		fuse_make_bad(inode);
+		return err;
+	}
+
+	fuse_change_attributes(inode, &attr_outarg.attr, NULL,
+			       ATTR_TIMEOUT(&attr_outarg), attr_version);
+
+	return 0;
+}
+
+
 struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
 {
 	struct fuse_file *ff;
@@ -123,7 +195,12 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
 	}
 }
 
+/*
+ * @inode might be NULL, the caller indicates the attr updates are not
+ *        needed on open
+ */
 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
+				 struct inode *inode,
 				 unsigned int open_flags, bool isdir)
 {
 	struct fuse_conn *fc = fm->fc;
@@ -143,7 +220,19 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
 		struct fuse_open_out *outargp = &ff->args->open_outarg;
 		int err;
 
-		err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);
+		err = -ENOSYS;
+		if (inode) {
+			/*
+			 * open-with-getattr preferred for
+			 * close-to-open coherency
+			 */
+			err = fuse_file_open_getattr(fm, nodeid, inode,
+						     open_flags, opcode,
+						     outargp);
+		}
+		if (err == -ENOSYS)
+			err = fuse_send_open(fm, nodeid, open_flags, opcode,
+					     outargp);
 		if (!err) {
 			ff->fh = outargp->fh;
 			ff->open_flags = outargp->open_flags;
@@ -172,7 +261,8 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
 int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
 		 bool isdir)
 {
-	struct fuse_file *ff = fuse_file_open(fm, nodeid, file->f_flags, isdir);
+	struct fuse_file *ff = fuse_file_open(fm, nodeid, file_inode(file),
+					      file->f_flags, isdir);
 
 	if (!IS_ERR(ff))
 		file->private_data = ff;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f23919610313..5c0ea7ce0b4a 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -731,9 +731,15 @@ struct fuse_conn {
 	/** Is open/release not implemented by fs? */
 	unsigned no_open:1;
 
+	/** Is open-getattr not implemented by fs */
+	unsigned no_open_getattr:1;
+
 	/** Is opendir/releasedir not implemented by fs? */
 	unsigned no_opendir:1;
 
+	/** Is opendir-getattr not implemented by fs? */
+	unsigned no_opendir_getattr:1;
+
 	/** Is fsync not implemented by fs? */
 	unsigned no_fsync:1;
 
@@ -1404,6 +1410,7 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode);
 
 /* file.c */
 struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
+				 struct inode *inode,
 				 unsigned int open_flags, bool isdir);
 void fuse_file_release(struct inode *inode, struct fuse_file *ff,
 		       unsigned int open_flags, fl_owner_t id, bool isdir);
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index 572ce8a82ceb..d5322f6a904d 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -493,7 +493,7 @@ static struct fuse_file *fuse_priv_ioctl_prepare(struct inode *inode)
 	if (!S_ISREG(inode->i_mode) && !isdir)
 		return ERR_PTR(-ENOTTY);
 
-	return fuse_file_open(fm, get_node_id(inode), O_RDONLY, isdir);
+	return fuse_file_open(fm, get_node_id(inode), NULL, O_RDONLY, isdir);
 }
 
 static void fuse_priv_ioctl_cleanup(struct inode *inode, struct fuse_file *ff)
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index d08b99d60f6f..34b06cf62c16 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -217,6 +217,9 @@
  *  - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
  *  - add FUSE_NO_EXPORT_SUPPORT init flag
  *  - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
+ *
+ * 7.41
+ *  - add FUSE_OPEN_GETATTR/FUSE_OPENDIR_GETATTR
  */
 
 #ifndef _LINUX_FUSE_H
@@ -633,6 +636,8 @@ enum fuse_opcode {
 	FUSE_SYNCFS		= 50,
 	FUSE_TMPFILE		= 51,
 	FUSE_STATX		= 52,
+	FUSE_OPEN_GETATTR	= 53,
+	FUSE_OPENDIR_GETATTR	= 54,
 
 	/* CUSE specific operations */
 	CUSE_INIT		= 4096,
-- 
2.43.0





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux