Skip to content

Commit a130b3f

Browse files
committed
fuse: add lookupx support
This is an extended version of lookup that supports additional flags and returns attributes with valid masks via fuse_reply_lookupx.
1 parent 6c9ec1d commit a130b3f

4 files changed

Lines changed: 155 additions & 40 deletions

File tree

fs/fuse/dir.c

Lines changed: 114 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ module_param(allow_sys_admin_access, bool, 0644);
2828
MODULE_PARM_DESC(allow_sys_admin_access,
2929
"Allow users with CAP_SYS_ADMIN in initial userns to bypass allow_other access check");
3030

31+
static void fuse_attr_to_statx(struct fuse_attr *attr, struct fuse_statx *sx, uint32_t mask);
32+
3133
static void fuse_advise_use_readdirplus(struct inode *dir)
3234
{
3335
struct fuse_inode *fi = get_fuse_inode(dir);
@@ -169,20 +171,70 @@ static void fuse_invalidate_entry(struct dentry *entry)
169171
fuse_invalidate_entry_cache(entry);
170172
}
171173

172-
static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
173-
u64 nodeid, const struct qstr *name,
174-
struct fuse_entry_out *outarg)
174+
/**
175+
* fuse_do_lookup - Perform a FUSE_LOOKUPX operation
176+
*
177+
* @ext_out: extended output argument structure
178+
* @lookup_flags: lookup flags (e.g., FUSE_LOOKUPX_REVALIDATE)
179+
*/
180+
static int fuse_do_lookup(struct fuse_mount *fm, u64 nodeid,
181+
const struct qstr *name,
182+
struct fuse_lookupx_out *ext_out,
183+
uint32_t lookup_flags)
175184
{
176-
memset(outarg, 0, sizeof(struct fuse_entry_out));
177-
args->opcode = FUSE_LOOKUP;
178-
args->nodeid = nodeid;
179-
args->in_numargs = 2;
180-
fuse_set_zero_arg0(args);
181-
args->in_args[1].size = name->len + 1;
182-
args->in_args[1].value = name->name;
183-
args->out_numargs = 1;
184-
args->out_args[0].size = sizeof(struct fuse_entry_out);
185-
args->out_args[0].value = outarg;
185+
struct fuse_conn *fc = fm->fc;
186+
FUSE_ARGS(args);
187+
struct fuse_lookupx_in inarg;
188+
int err;
189+
190+
memset(ext_out, 0, sizeof(*ext_out));
191+
args.nodeid = nodeid;
192+
193+
if (!fc->lookupx)
194+
goto fallback;
195+
196+
args.opcode = FUSE_LOOKUPX;
197+
memset(&inarg, 0, sizeof(inarg));
198+
inarg.lookup_flags = lookup_flags;
199+
200+
args.in_numargs = 3;
201+
args.in_args[0].size = sizeof(inarg);
202+
args.in_args[0].value = &inarg;
203+
args.in_args[1].size = 0;
204+
args.in_args[1].value = NULL;
205+
args.in_args[2].size = name->len + 1;
206+
args.in_args[2].value = name->name;
207+
args.out_numargs = 1;
208+
args.out_args[0].size = sizeof(struct fuse_lookupx_out);
209+
args.out_args[0].value = ext_out;
210+
211+
err = fuse_simple_request(fm, &args);
212+
if (err) {
213+
if (err == -ENOSYS) {
214+
fc->lookupx = 0;
215+
goto fallback;
216+
}
217+
return err;
218+
}
219+
220+
return 0;
221+
222+
fallback:
223+
args.opcode = FUSE_LOOKUP;
224+
args.in_numargs = 2;
225+
fuse_set_zero_arg0(&args);
226+
args.in_args[1].size = name->len + 1;
227+
args.in_args[1].value = name->name;
228+
args.out_numargs = 1;
229+
args.out_args[0].size = sizeof(struct fuse_entry_out);
230+
args.out_args[0].value = &ext_out->entry;
231+
232+
err = fuse_simple_request(fm, &args);
233+
if (err)
234+
return err;
235+
236+
ext_out->mask = STATX_BASIC_STATS;
237+
return 0;
186238
}
187239

188240
/*
@@ -207,8 +259,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
207259
goto invalid;
208260
else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
209261
(flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) {
210-
struct fuse_entry_out outarg;
211-
FUSE_ARGS(args);
262+
struct fuse_lookupx_out ext_out;
263+
struct fuse_statx sx;
212264
struct fuse_forget_link *forget;
213265
u64 attr_version;
214266

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

232284
parent = dget_parent(entry);
233-
fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
234-
&entry->d_name, &outarg);
235-
ret = fuse_simple_request(fm, &args);
285+
ret = fuse_do_lookup(fm, get_node_id(d_inode(parent)),
286+
&entry->d_name, &ext_out,
287+
FUSE_LOOKUPX_REVALIDATE);
236288
dput(parent);
237289
/* Zero nodeid is same as -ENOENT */
238-
if (!ret && !outarg.nodeid)
290+
if (!ret && !ext_out.entry.nodeid)
239291
ret = -ENOENT;
240292
if (!ret) {
241293
fi = get_fuse_inode(inode);
242-
if (outarg.nodeid != get_node_id(inode) ||
243-
(bool) IS_AUTOMOUNT(inode) != (bool) (outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) {
294+
if (ext_out.entry.nodeid != get_node_id(inode) ||
295+
(bool) IS_AUTOMOUNT(inode) != (bool) (ext_out.entry.attr.flags & FUSE_ATTR_SUBMOUNT)) {
244296
fuse_queue_forget(fm->fc, forget,
245-
outarg.nodeid, 1);
297+
ext_out.entry.nodeid, 1);
246298
goto invalid;
247299
}
248300
spin_lock(&fi->lock);
@@ -252,15 +304,16 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
252304
kfree(forget);
253305
if (ret == -ENOMEM || ret == -EINTR)
254306
goto out;
255-
if (ret || fuse_invalid_attr(&outarg.attr) ||
256-
fuse_stale_inode(inode, outarg.generation, &outarg.attr))
307+
if (ret || fuse_invalid_attr(&ext_out.entry.attr) ||
308+
fuse_stale_inode(inode, ext_out.entry.generation, &ext_out.entry.attr))
257309
goto invalid;
258310

259311
forget_all_cached_acls(inode);
260-
fuse_change_attributes(inode, &outarg.attr, NULL,
261-
ATTR_TIMEOUT(&outarg),
312+
fuse_attr_to_statx(&ext_out.entry.attr, &sx, ext_out.mask);
313+
fuse_change_attributes(inode, &ext_out.entry.attr, &sx,
314+
ATTR_TIMEOUT(&ext_out.entry),
262315
attr_version);
263-
fuse_change_entry_timeout(entry, &outarg);
316+
fuse_change_entry_timeout(entry, &ext_out.entry);
264317
} else if (inode) {
265318
fi = get_fuse_inode(inode);
266319
if (flags & LOOKUP_RCU) {
@@ -366,7 +419,8 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
366419
struct fuse_entry_out *outarg, struct inode **inode)
367420
{
368421
struct fuse_mount *fm = get_fuse_mount_super(sb);
369-
FUSE_ARGS(args);
422+
struct fuse_lookupx_out ext_out;
423+
struct fuse_statx sx;
370424
struct fuse_forget_link *forget;
371425
u64 attr_version, evict_ctr;
372426
int err;
@@ -385,29 +439,30 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
385439
attr_version = fuse_get_attr_version(fm->fc);
386440
evict_ctr = fuse_get_evict_ctr(fm->fc);
387441

388-
fuse_lookup_init(fm->fc, &args, nodeid, name, outarg);
389-
err = fuse_simple_request(fm, &args);
442+
err = fuse_do_lookup(fm, nodeid, name, &ext_out, 0);
390443
/* Zero nodeid is same as -ENOENT, but with valid timeout */
391-
if (err || !outarg->nodeid)
444+
if (err || !ext_out.entry.nodeid)
392445
goto out_put_forget;
393446

394447
err = -EIO;
395-
if (fuse_invalid_attr(&outarg->attr))
448+
if (fuse_invalid_attr(&ext_out.entry.attr))
396449
goto out_put_forget;
397-
if (outarg->nodeid == FUSE_ROOT_ID && outarg->generation != 0) {
450+
if (ext_out.entry.nodeid == FUSE_ROOT_ID && ext_out.entry.generation != 0) {
398451
pr_warn_once("root generation should be zero\n");
399-
outarg->generation = 0;
452+
ext_out.entry.generation = 0;
400453
}
401454

402-
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
403-
&outarg->attr, ATTR_TIMEOUT(outarg),
455+
fuse_attr_to_statx(&ext_out.entry.attr, &sx, ext_out.mask);
456+
*inode = fuse_iget_sx(sb, ext_out.entry.nodeid, ext_out.entry.generation,
457+
&ext_out.entry.attr, &sx, ATTR_TIMEOUT(&ext_out.entry),
404458
attr_version, evict_ctr);
405459
err = -ENOMEM;
406460
if (!*inode) {
407-
fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1);
461+
fuse_queue_forget(fm->fc, forget, ext_out.entry.nodeid, 1);
408462
goto out;
409463
}
410464
err = 0;
465+
memcpy(outarg, &ext_out.entry, sizeof(*outarg));
411466

412467
out_put_forget:
413468
kfree(forget);
@@ -1183,6 +1238,28 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
11831238
attr->blksize = sx->blksize;
11841239
}
11851240

1241+
static void fuse_attr_to_statx(struct fuse_attr *attr, struct fuse_statx *sx, uint32_t mask)
1242+
{
1243+
memset(sx, 0, sizeof(*sx));
1244+
sx->mask = mask;
1245+
sx->ino = attr->ino;
1246+
sx->size = attr->size;
1247+
sx->blocks = attr->blocks;
1248+
sx->atime.tv_sec = attr->atime;
1249+
sx->mtime.tv_sec = attr->mtime;
1250+
sx->ctime.tv_sec = attr->ctime;
1251+
sx->atime.tv_nsec = attr->atimensec;
1252+
sx->mtime.tv_nsec = attr->mtimensec;
1253+
sx->ctime.tv_nsec = attr->ctimensec;
1254+
sx->mode = attr->mode;
1255+
sx->nlink = attr->nlink;
1256+
sx->uid = attr->uid;
1257+
sx->gid = attr->gid;
1258+
sx->rdev_major = MAJOR(attr->rdev);
1259+
sx->rdev_minor = MINOR(attr->rdev);
1260+
sx->blksize = attr->blksize;
1261+
}
1262+
11861263
static int fuse_do_statx(struct inode *inode, struct file *file,
11871264
struct kstat *stat)
11881265
{

fs/fuse/fuse_i.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,9 @@ struct fuse_conn {
864864
/* do we have support for dlm in the fs? */
865865
unsigned int dlm:1;
866866

867+
/* Is extended lookup implemented by fs? */
868+
unsigned int lookupx:1;
869+
867870
/* Is synchronous FUSE_INIT allowed? */
868871
unsigned int sync_init:1;
869872

@@ -1092,6 +1095,12 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
10921095
u64 attr_valid, u64 attr_version,
10931096
u64 evict_ctr);
10941097

1098+
struct inode *fuse_iget_sx(struct super_block *sb, u64 nodeid,
1099+
int generation, struct fuse_attr *attr,
1100+
struct fuse_statx *sx,
1101+
u64 attr_valid, u64 attr_version,
1102+
u64 evict_ctr);
1103+
10951104
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
10961105
struct fuse_entry_out *outarg, struct inode **inode);
10971106

fs/fuse/inode.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,9 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)
458458
return 0;
459459
}
460460

461-
struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
461+
struct inode *fuse_iget_sx(struct super_block *sb, u64 nodeid,
462462
int generation, struct fuse_attr *attr,
463+
struct fuse_statx *sx,
463464
u64 attr_valid, u64 attr_version,
464465
u64 evict_ctr)
465466
{
@@ -522,11 +523,20 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
522523
fi->nlookup++;
523524
spin_unlock(&fi->lock);
524525
done:
525-
fuse_change_attributes_i(inode, attr, NULL, attr_valid, attr_version,
526+
fuse_change_attributes_i(inode, attr, sx, attr_valid, attr_version,
526527
evict_ctr);
527528
return inode;
528529
}
529530

531+
struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
532+
int generation, struct fuse_attr *attr,
533+
u64 attr_valid, u64 attr_version,
534+
u64 evict_ctr)
535+
{
536+
return fuse_iget_sx(sb, nodeid, generation, attr, NULL,
537+
attr_valid, attr_version, evict_ctr);
538+
}
539+
530540
struct inode *fuse_ilookup(struct fuse_conn *fc, u64 nodeid,
531541
struct fuse_mount **fm)
532542
{
@@ -611,7 +621,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
611621

612622
fi->attr_version = atomic64_inc_return(&fc->attr_version);
613623
spin_unlock(&fi->lock);
614-
624+
615625
if (fc->inval_inode_entries)
616626
fuse_invalidate_inode_entry(inode);
617627
else if (fc->expire_inode_entries)
@@ -1043,6 +1053,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
10431053
fc->initialized = 0;
10441054
fc->connected = 1;
10451055
fc->dlm = 1;
1056+
fc->lookupx = 1;
10461057

10471058
/* module option for now */
10481059
fc->compound_open_getattr = enable_compound;

include/uapi/linux/fuse.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,12 @@ struct fuse_file_lock {
575575
*/
576576
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
577577

578+
/**
579+
* Lookup flags
580+
* FUSE_LOOKUPX_REVALIDATE: lookup called from revalidate
581+
*/
582+
#define FUSE_LOOKUPX_REVALIDATE (1 << 0)
583+
578584
/**
579585
* setxattr flags
580586
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
@@ -660,6 +666,9 @@ enum fuse_opcode {
660666
*/
661667
FUSE_COMPOUND = 101,
662668

669+
/* Extented lookup operation */
670+
FUSE_LOOKUPX = 102,
671+
663672
/* CUSE specific operations */
664673
CUSE_INIT = 4096,
665674

@@ -694,6 +703,15 @@ struct fuse_entry_out {
694703
struct fuse_attr attr;
695704
};
696705

706+
struct fuse_lookupx_in {
707+
uint32_t lookup_flags;
708+
};
709+
710+
struct fuse_lookupx_out {
711+
struct fuse_entry_out entry;
712+
uint32_t mask; /* Mask of valid attributes in statx format */
713+
};
714+
697715
struct fuse_forget_in {
698716
uint64_t nlookup;
699717
};

0 commit comments

Comments
 (0)