Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 106 additions & 14 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module_param(allow_sys_admin_access, bool, 0644);
MODULE_PARM_DESC(allow_sys_admin_access,
"Allow users with CAP_SYS_ADMIN in initial userns to bypass allow_other access check");

static void fuse_attr_to_statx(struct fuse_attr *attr, struct fuse_statx *sx, uint32_t mask);

static void fuse_advise_use_readdirplus(struct inode *dir)
{
struct fuse_inode *fi = get_fuse_inode(dir);
Expand Down Expand Up @@ -185,6 +187,72 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
args->out_args[0].value = outarg;
}

/**
* fuse_do_lookupx - Perform a FUSE_LOOKUPX operation
*
* @ext_out: extended output argument structure
* @lookup_flags: lookup flags (e.g., FUSE_LOOKUPX_REVALIDATE)
*/
static int fuse_do_lookupx(struct fuse_mount *fm, u64 nodeid,
const struct qstr *name,
struct fuse_lookupx_out *ext_out,
uint32_t lookup_flags)
{
struct fuse_conn *fc = fm->fc;
FUSE_ARGS(args);
struct fuse_lookupx_in inarg;
int err;

memset(ext_out, 0, sizeof(*ext_out));
args.nodeid = nodeid;

if (!fc->lookupx)
goto fallback;

args.opcode = FUSE_LOOKUPX;
memset(&inarg, 0, sizeof(inarg));
inarg.lookup_flags = lookup_flags;

args.in_numargs = 3;
args.in_args[0].size = sizeof(inarg);
args.in_args[0].value = &inarg;
args.in_args[1].size = 0;
args.in_args[1].value = NULL;
args.in_args[2].size = name->len + 1;
args.in_args[2].value = name->name;
args.out_numargs = 1;
args.out_args[0].size = sizeof(struct fuse_lookupx_out);
args.out_args[0].value = ext_out;

err = fuse_simple_request(fm, &args);
if (err) {
if (err == -ENOSYS) {
fc->lookupx = 0;
goto fallback;
}
return err;
}

return 0;

fallback:
args.opcode = FUSE_LOOKUP;
args.in_numargs = 2;
fuse_set_zero_arg0(&args);
args.in_args[1].size = name->len + 1;
args.in_args[1].value = name->name;
args.out_numargs = 1;
args.out_args[0].size = sizeof(struct fuse_entry_out);
args.out_args[0].value = &ext_out->entry;

err = fuse_simple_request(fm, &args);
if (err)
return err;

ext_out->mask = STATX_BASIC_STATS;
return 0;
}

/*
* Check whether the dentry is still valid
*
Expand All @@ -207,8 +275,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
goto invalid;
else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
(flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) {
struct fuse_entry_out outarg;
FUSE_ARGS(args);
struct fuse_lookupx_out ext_out;
struct fuse_statx sx;
struct fuse_forget_link *forget;
u64 attr_version;

Expand All @@ -230,19 +298,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
attr_version = fuse_get_attr_version(fm->fc);

parent = dget_parent(entry);
fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
&entry->d_name, &outarg);
ret = fuse_simple_request(fm, &args);
ret = fuse_do_lookupx(fm, get_node_id(d_inode(parent)),
&entry->d_name, &ext_out,
FUSE_LOOKUPX_REVALIDATE);
dput(parent);
/* Zero nodeid is same as -ENOENT */
if (!ret && !outarg.nodeid)
if (!ret && !ext_out.entry.nodeid)
ret = -ENOENT;
if (!ret) {
fi = get_fuse_inode(inode);
if (outarg.nodeid != get_node_id(inode) ||
(bool) IS_AUTOMOUNT(inode) != (bool) (outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) {
if (ext_out.entry.nodeid != get_node_id(inode) ||
(bool) IS_AUTOMOUNT(inode) != (bool) (ext_out.entry.attr.flags & FUSE_ATTR_SUBMOUNT)) {
fuse_queue_forget(fm->fc, forget,
outarg.nodeid, 1);
ext_out.entry.nodeid, 1);
goto invalid;
}
spin_lock(&fi->lock);
Expand All @@ -252,15 +320,17 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
kfree(forget);
if (ret == -ENOMEM || ret == -EINTR)
goto out;
if (ret || fuse_invalid_attr(&outarg.attr) ||
fuse_stale_inode(inode, outarg.generation, &outarg.attr))
if (ret || fuse_invalid_attr(&ext_out.entry.attr) ||
fuse_stale_inode(inode, ext_out.entry.generation, &ext_out.entry.attr))
goto invalid;

forget_all_cached_acls(inode);
fuse_change_attributes(inode, &outarg.attr, NULL,
ATTR_TIMEOUT(&outarg),
fuse_attr_to_statx(&ext_out.entry.attr, &sx, ext_out.mask);
fuse_change_attributes(inode, &ext_out.entry.attr, &sx,
ATTR_TIMEOUT(&ext_out.entry),
attr_version);
fuse_change_entry_timeout(entry, &outarg);
if ((ext_out.mask & STATX_BASIC_STATS) == STATX_BASIC_STATS)
fuse_change_entry_timeout(entry, &ext_out.entry);
} else if (inode) {
fi = get_fuse_inode(inode);
if (flags & LOOKUP_RCU) {
Expand Down Expand Up @@ -1183,6 +1253,28 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
attr->blksize = sx->blksize;
}

static void fuse_attr_to_statx(struct fuse_attr *attr, struct fuse_statx *sx, uint32_t mask)
{
memset(sx, 0, sizeof(*sx));
sx->mask = mask;
sx->ino = attr->ino;
sx->size = attr->size;
sx->blocks = attr->blocks;
sx->atime.tv_sec = attr->atime;
sx->mtime.tv_sec = attr->mtime;
sx->ctime.tv_sec = attr->ctime;
sx->atime.tv_nsec = attr->atimensec;
sx->mtime.tv_nsec = attr->mtimensec;
sx->ctime.tv_nsec = attr->ctimensec;
sx->mode = attr->mode;
sx->nlink = attr->nlink;
sx->uid = attr->uid;
sx->gid = attr->gid;
sx->rdev_major = MAJOR(attr->rdev);
sx->rdev_minor = MINOR(attr->rdev);
sx->blksize = attr->blksize;
}

static int fuse_do_statx(struct inode *inode, struct file *file,
struct kstat *stat)
{
Expand Down
3 changes: 3 additions & 0 deletions fs/fuse/fuse_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,9 @@ struct fuse_conn {
/* do we have support for dlm in the fs? */
unsigned int dlm:1;

/* Is extended lookup implemented by fs? */
unsigned int lookupx:1;

/* Is synchronous FUSE_INIT allowed? */
unsigned int sync_init:1;

Expand Down
3 changes: 2 additions & 1 deletion fs/fuse/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,

fi->attr_version = atomic64_inc_return(&fc->attr_version);
spin_unlock(&fi->lock);

if (fc->inval_inode_entries)
fuse_invalidate_inode_entry(inode);
else if (fc->expire_inode_entries)
Expand Down Expand Up @@ -1043,6 +1043,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
fc->initialized = 0;
fc->connected = 1;
fc->dlm = 1;
fc->lookupx = 1;

/* module option for now */
fc->compound_open_getattr = enable_compound;
Expand Down
18 changes: 18 additions & 0 deletions include/uapi/linux/fuse.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,12 @@ struct fuse_file_lock {
*/
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)

/**
* Lookup flags
* FUSE_LOOKUPX_REVALIDATE: lookup called from revalidate
*/
#define FUSE_LOOKUPX_REVALIDATE (1 << 0)

/**
* setxattr flags
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
Expand Down Expand Up @@ -660,6 +666,9 @@ enum fuse_opcode {
*/
FUSE_COMPOUND = 101,

/* Extented lookup operation */
FUSE_LOOKUPX = 102,

/* CUSE specific operations */
CUSE_INIT = 4096,

Expand Down Expand Up @@ -694,6 +703,15 @@ struct fuse_entry_out {
struct fuse_attr attr;
};

struct fuse_lookupx_in {
uint32_t lookup_flags;
};

struct fuse_lookupx_out {
struct fuse_entry_out entry;
uint32_t mask; /* Mask of valid attributes in statx format */
};

struct fuse_forget_in {
uint64_t nlookup;
};
Expand Down