@@ -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 );
@@ -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
0 commit comments