Skip to content

Commit 686518e

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. (cherry picked from commit 1bcbb38)
1 parent 7fcc07e commit 686518e

4 files changed

Lines changed: 130 additions & 14 deletions

File tree

fs/fuse/dir.c

Lines changed: 106 additions & 14 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);
@@ -185,6 +187,69 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
185187
args->out_args[0].value = outarg;
186188
}
187189

190+
/**
191+
* fuse_do_lookupx - Perform a FUSE_LOOKUPX operation
192+
*
193+
* @ext_out: extended output argument structure
194+
* @lookup_flags: lookup flags (e.g., FUSE_LOOKUPX_FOR_REVALIDATE)
195+
*/
196+
static int fuse_do_lookupx(struct fuse_mount *fm, u64 nodeid,
197+
const struct qstr *name,
198+
struct fuse_lookupx_out *ext_out,
199+
uint32_t lookup_flags)
200+
{
201+
struct fuse_conn *fc = fm->fc;
202+
FUSE_ARGS(args);
203+
struct fuse_lookupx_in inarg = { .lookup_flags = lookup_flags };
204+
int err;
205+
206+
memset(ext_out, 0, sizeof(*ext_out));
207+
args.nodeid = nodeid;
208+
209+
if (!fc->lookupx)
210+
goto fallback;
211+
212+
args.opcode = FUSE_LOOKUPX;
213+
args.in_numargs = 3;
214+
args.in_args[0].size = sizeof(inarg);
215+
args.in_args[0].value = &inarg;
216+
args.in_args[1].size = 0;
217+
args.in_args[1].value = NULL;
218+
args.in_args[2].size = name->len + 1;
219+
args.in_args[2].value = name->name;
220+
args.out_numargs = 1;
221+
args.out_args[0].size = sizeof(struct fuse_lookupx_out);
222+
args.out_args[0].value = ext_out;
223+
224+
err = fuse_simple_request(fm, &args);
225+
if (err) {
226+
if (err == -ENOSYS) {
227+
fc->lookupx = 0;
228+
goto fallback;
229+
}
230+
return err;
231+
}
232+
233+
return 0;
234+
235+
fallback:
236+
args.opcode = FUSE_LOOKUP;
237+
args.in_numargs = 2;
238+
fuse_set_zero_arg0(&args);
239+
args.in_args[1].size = name->len + 1;
240+
args.in_args[1].value = name->name;
241+
args.out_numargs = 1;
242+
args.out_args[0].size = sizeof(struct fuse_entry_out);
243+
args.out_args[0].value = &ext_out->entry;
244+
245+
err = fuse_simple_request(fm, &args);
246+
if (err)
247+
return err;
248+
249+
ext_out->mask = STATX_BASIC_STATS;
250+
return 0;
251+
}
252+
188253
/*
189254
* Check whether the dentry is still valid
190255
*
@@ -207,10 +272,11 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
207272
goto invalid;
208273
else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
209274
(flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) {
210-
struct fuse_entry_out outarg;
211-
FUSE_ARGS(args);
275+
struct fuse_lookupx_out ext_out;
276+
struct fuse_statx sx;
212277
struct fuse_forget_link *forget;
213278
u64 attr_version;
279+
uint32_t lookupx_flags = FUSE_LOOKUPX_FOR_REVALIDATE;
214280

215281
/* For negative dentries, always do a fresh lookup */
216282
if (!inode)
@@ -228,21 +294,23 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
228294
goto out;
229295

230296
attr_version = fuse_get_attr_version(fm->fc);
297+
if (S_ISDIR(inode->i_mode))
298+
lookupx_flags |= FUSE_LOOKUPX_TARGET_WAS_DIR;
231299

232300
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);
301+
ret = fuse_do_lookupx(fm, get_node_id(d_inode(parent)),
302+
&entry->d_name, &ext_out,
303+
lookupx_flags);
236304
dput(parent);
237305
/* Zero nodeid is same as -ENOENT */
238-
if (!ret && !outarg.nodeid)
306+
if (!ret && !ext_out.entry.nodeid)
239307
ret = -ENOENT;
240308
if (!ret) {
241309
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)) {
310+
if (ext_out.entry.nodeid != get_node_id(inode) ||
311+
(bool) IS_AUTOMOUNT(inode) != (bool) (ext_out.entry.attr.flags & FUSE_ATTR_SUBMOUNT)) {
244312
fuse_queue_forget(fm->fc, forget,
245-
outarg.nodeid, 1);
313+
ext_out.entry.nodeid, 1);
246314
goto invalid;
247315
}
248316
spin_lock(&fi->lock);
@@ -252,15 +320,17 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
252320
kfree(forget);
253321
if (ret == -ENOMEM || ret == -EINTR)
254322
goto out;
255-
if (ret || fuse_invalid_attr(&outarg.attr) ||
256-
fuse_stale_inode(inode, outarg.generation, &outarg.attr))
323+
if (ret || fuse_invalid_attr(&ext_out.entry.attr) ||
324+
fuse_stale_inode(inode, ext_out.entry.generation, &ext_out.entry.attr))
257325
goto invalid;
258326

259327
forget_all_cached_acls(inode);
260-
fuse_change_attributes(inode, &outarg.attr, NULL,
261-
ATTR_TIMEOUT(&outarg),
328+
fuse_attr_to_statx(&ext_out.entry.attr, &sx, ext_out.mask);
329+
fuse_change_attributes(inode, &ext_out.entry.attr, &sx,
330+
ATTR_TIMEOUT(&ext_out.entry),
262331
attr_version);
263-
fuse_change_entry_timeout(entry, &outarg);
332+
if ((ext_out.mask & STATX_BASIC_STATS) == STATX_BASIC_STATS)
333+
fuse_change_entry_timeout(entry, &ext_out.entry);
264334
} else if (inode) {
265335
fi = get_fuse_inode(inode);
266336
if (flags & LOOKUP_RCU) {
@@ -1182,6 +1252,28 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
11821252
attr->blksize = sx->blksize;
11831253
}
11841254

1255+
static void fuse_attr_to_statx(struct fuse_attr *attr, struct fuse_statx *sx, uint32_t mask)
1256+
{
1257+
memset(sx, 0, sizeof(*sx));
1258+
sx->mask = mask;
1259+
sx->ino = attr->ino;
1260+
sx->size = attr->size;
1261+
sx->blocks = attr->blocks;
1262+
sx->atime.tv_sec = attr->atime;
1263+
sx->mtime.tv_sec = attr->mtime;
1264+
sx->ctime.tv_sec = attr->ctime;
1265+
sx->atime.tv_nsec = attr->atimensec;
1266+
sx->mtime.tv_nsec = attr->mtimensec;
1267+
sx->ctime.tv_nsec = attr->ctimensec;
1268+
sx->mode = attr->mode;
1269+
sx->nlink = attr->nlink;
1270+
sx->uid = attr->uid;
1271+
sx->gid = attr->gid;
1272+
sx->rdev_major = MAJOR(attr->rdev);
1273+
sx->rdev_minor = MINOR(attr->rdev);
1274+
sx->blksize = attr->blksize;
1275+
}
1276+
11851277
/*
11861278
* @param sx_mask request mask send to to fuse-server
11871279
* @param mandatory_sx_mask subset of (or complete) sx_mask that the server

fs/fuse/fuse_i.h

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

865+
/* Is extended lookup implemented by fs? */
866+
unsigned int lookupx:1;
867+
865868
/* Is synchronous FUSE_INIT allowed? */
866869
unsigned int sync_init:1;
867870

fs/fuse/inode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
11911191
fc->initialized = 0;
11921192
fc->connected = 1;
11931193
fc->dlm = 1;
1194+
fc->lookupx = 1;
11941195

11951196
/* module option for now */
11961197
fc->compound_open_getattr = enable_compound;

include/uapi/linux/fuse.h

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

578+
/**
579+
* Lookup flags
580+
* FUSE_LOOKUPX_FOR_REVALIDATE: lookup called from revalidate
581+
* FUSE_LOOKUPX_TARGET_WASDIR: (hint) the lookup target was a directory
582+
*/
583+
#define FUSE_LOOKUPX_FOR_REVALIDATE (1 << 0)
584+
#define FUSE_LOOKUPX_TARGET_WAS_DIR (1 << 1)
585+
578586
/**
579587
* setxattr flags
580588
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
@@ -660,6 +668,9 @@ enum fuse_opcode {
660668
*/
661669
FUSE_COMPOUND = 101,
662670

671+
/* Extented lookup operation */
672+
FUSE_LOOKUPX = 102,
673+
663674
/* CUSE specific operations */
664675
CUSE_INIT = 4096,
665676

@@ -694,6 +705,15 @@ struct fuse_entry_out {
694705
struct fuse_attr attr;
695706
};
696707

708+
struct fuse_lookupx_in {
709+
uint32_t lookup_flags;
710+
};
711+
712+
struct fuse_lookupx_out {
713+
struct fuse_entry_out entry;
714+
uint32_t mask; /* Mask of valid attributes in statx format */
715+
};
716+
697717
struct fuse_forget_in {
698718
uint64_t nlookup;
699719
};

0 commit comments

Comments
 (0)