Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21f62174 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

fuse: fix LOOKUP vs INIT compat handling



Analysis from Marc:

 "Commit 7078187a ("fuse: introduce fuse_simple_request() helper")
  from the above pull request triggers some EIO errors for me in some tests
  that rely on fuse

  Looking at the code changes and a bit of debugging info I think there's a
  general problem here that fuse_get_req checks and possibly waits for
  fc->initialized, and this was always called first.  But this commit
  changes the ordering and in many places fc->minor is now possibly used
  before fuse_get_req, and we can't be sure that fc has been initialized.
  In my case fuse_lookup_init sets req->out.args[0].size to the wrong size
  because fc->minor at that point is still 0, leading to the EIO error."

Fix by moving the compat adjustments into fuse_simple_request() to after
fuse_get_req().

This is also more readable than the original, since now compatibility is
handled in a single function instead of cluttering each operation.

Reported-by: default avatarMarc Dionne <marc.c.dionne@gmail.com>
Tested-by: default avatarMarc Dionne <marc.c.dionne@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@suse.cz>
Fixes: 7078187a ("fuse: introduce fuse_simple_request() helper")
parent b1940cd2
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -511,6 +511,39 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
}
EXPORT_SYMBOL_GPL(fuse_request_send);

static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)
{
	if (fc->minor < 4 && args->in.h.opcode == FUSE_STATFS)
		args->out.args[0].size = FUSE_COMPAT_STATFS_SIZE;

	if (fc->minor < 9) {
		switch (args->in.h.opcode) {
		case FUSE_LOOKUP:
		case FUSE_CREATE:
		case FUSE_MKNOD:
		case FUSE_MKDIR:
		case FUSE_SYMLINK:
		case FUSE_LINK:
			args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
			break;
		case FUSE_GETATTR:
		case FUSE_SETATTR:
			args->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
			break;
		}
	}
	if (fc->minor < 12) {
		switch (args->in.h.opcode) {
		case FUSE_CREATE:
			args->in.args[0].size = sizeof(struct fuse_open_in);
			break;
		case FUSE_MKNOD:
			args->in.args[0].size = FUSE_COMPAT_MKNOD_IN_SIZE;
			break;
		}
	}
}

ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
{
	struct fuse_req *req;
@@ -520,6 +553,9 @@ ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
	if (IS_ERR(req))
		return PTR_ERR(req);

	/* Needs to be done after fuse_get_req() so that fc->minor is valid */
	fuse_adjust_compat(fc, args);

	req->in.h.opcode = args->in.h.opcode;
	req->in.h.nodeid = args->in.h.nodeid;
	req->in.numargs = args->in.numargs;
+7 −24
Original line number Diff line number Diff line
@@ -156,9 +156,6 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
	args->in.args[0].size = name->len + 1;
	args->in.args[0].value = name->name;
	args->out.numargs = 1;
	if (fc->minor < 9)
		args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
	else
	args->out.args[0].size = sizeof(struct fuse_entry_out);
	args->out.args[0].value = outarg;
}
@@ -422,15 +419,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	args.in.h.opcode = FUSE_CREATE;
	args.in.h.nodeid = get_node_id(dir);
	args.in.numargs = 2;
	args.in.args[0].size = fc->minor < 12 ? sizeof(struct fuse_open_in) :
						sizeof(inarg);
	args.in.args[0].size = sizeof(inarg);
	args.in.args[0].value = &inarg;
	args.in.args[1].size = entry->d_name.len + 1;
	args.in.args[1].value = entry->d_name.name;
	args.out.numargs = 2;
	if (fc->minor < 9)
		args.out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
	else
	args.out.args[0].size = sizeof(outentry);
	args.out.args[0].value = &outentry;
	args.out.args[1].size = sizeof(outopen);
@@ -539,9 +532,6 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
	memset(&outarg, 0, sizeof(outarg));
	args->in.h.nodeid = get_node_id(dir);
	args->out.numargs = 1;
	if (fc->minor < 9)
		args->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
	else
	args->out.args[0].size = sizeof(outarg);
	args->out.args[0].value = &outarg;
	err = fuse_simple_request(fc, args);
@@ -592,8 +582,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
	inarg.umask = current_umask();
	args.in.h.opcode = FUSE_MKNOD;
	args.in.numargs = 2;
	args.in.args[0].size = fc->minor < 12 ? FUSE_COMPAT_MKNOD_IN_SIZE :
						sizeof(inarg);
	args.in.args[0].size = sizeof(inarg);
	args.in.args[0].value = &inarg;
	args.in.args[1].size = entry->d_name.len + 1;
	args.in.args[1].value = entry->d_name.name;
@@ -899,9 +888,6 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
	args.in.args[0].size = sizeof(inarg);
	args.in.args[0].value = &inarg;
	args.out.numargs = 1;
	if (fc->minor < 9)
		args.out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
	else
	args.out.args[0].size = sizeof(outarg);
	args.out.args[0].value = &outarg;
	err = fuse_simple_request(fc, &args);
@@ -1574,9 +1560,6 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args,
	args->in.args[0].size = sizeof(*inarg_p);
	args->in.args[0].value = inarg_p;
	args->out.numargs = 1;
	if (fc->minor < 9)
		args->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
	else
	args->out.args[0].size = sizeof(*outarg_p);
	args->out.args[0].value = outarg_p;
}
+1 −2
Original line number Diff line number Diff line
@@ -424,8 +424,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
	args.in.h.opcode = FUSE_STATFS;
	args.in.h.nodeid = get_node_id(dentry->d_inode);
	args.out.numargs = 1;
	args.out.args[0].size =
		fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg);
	args.out.args[0].size = sizeof(outarg);
	args.out.args[0].value = &outarg;
	err = fuse_simple_request(fc, &args);
	if (!err)