@@ -28,6 +28,8 @@ module_param(allow_sys_admin_access, bool, 0644);
2828MODULE_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+
3133static 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+
11861263static int fuse_do_statx (struct inode * inode , struct file * file ,
11871264 struct kstat * stat )
11881265{
0 commit comments