@@ -1182,8 +1182,13 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
11821182 attr -> blksize = sx -> blksize ;
11831183}
11841184
1185+ /*
1186+ * @param sx_mask request mask send to to fuse-server
1187+ * @param mandatory_sx_mask subset of (or complete) sx_mask that the server
1188+ * has to fulfill
1189+ */
11851190static int fuse_do_statx (struct inode * inode , struct file * file ,
1186- struct kstat * stat )
1191+ struct kstat * stat , u32 sx_mask , u32 mandatory_sx_mask )
11871192{
11881193 int err ;
11891194 struct fuse_attr attr ;
@@ -1194,6 +1199,12 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
11941199 u64 attr_version = fuse_get_attr_version (fm -> fc );
11951200 FUSE_ARGS (args );
11961201
1202+ /*
1203+ * mandatory_sx_mask should be a subset of sx_mask.
1204+ * If it's not, we have a logic error somewhere in the call chain.
1205+ */
1206+ WARN_ON_ONCE ((mandatory_sx_mask & sx_mask ) != mandatory_sx_mask );
1207+
11971208 memset (& inarg , 0 , sizeof (inarg ));
11981209 memset (& outarg , 0 , sizeof (outarg ));
11991210 /* Directories have separate file-handle space */
@@ -1203,9 +1214,12 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
12031214 inarg .getattr_flags |= FUSE_GETATTR_FH ;
12041215 inarg .fh = ff -> fh ;
12051216 }
1206- /* For now leave sync hints as the default, request all stats. */
1217+ /*
1218+ * For permission checks, we only need mode, uid, gid.
1219+ * This is an optimization to avoid fetching all stats when not needed.
1220+ */
12071221 inarg .sx_flags = 0 ;
1208- inarg .sx_mask = STATX_BASIC_STATS | STATX_BTIME ;
1222+ inarg .sx_mask = sx_mask ;
12091223 args .opcode = FUSE_STATX ;
12101224 args .nodeid = get_node_id (inode );
12111225 args .in_numargs = 1 ;
@@ -1219,6 +1233,17 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
12191233 return err ;
12201234
12211235 sx = & outarg .stat ;
1236+
1237+ /*
1238+ * Verify the server returned at least what we requested.
1239+ * The server may return more attributes than requested (which is fine),
1240+ * but must not return fewer.
1241+ */
1242+ if ((sx -> mask & mandatory_sx_mask ) != mandatory_sx_mask ) {
1243+ fuse_make_bad (inode );
1244+ return - EIO ;
1245+ }
1246+
12221247 if (((sx -> mask & STATX_SIZE ) && !fuse_valid_size (sx -> size )) ||
12231248 ((sx -> mask & STATX_TYPE ) && (!fuse_valid_type (sx -> mode ) ||
12241249 inode_wrong_type (inode , sx -> mode )))) {
@@ -1227,7 +1252,7 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
12271252 }
12281253
12291254 fuse_statx_to_attr (& outarg .stat , & attr );
1230- if (( sx -> mask & STATX_BASIC_STATS ) == STATX_BASIC_STATS ) {
1255+ if (sx -> mask & STATX_BASIC_STATS ) {
12311256 fuse_change_attributes (inode , & attr , & outarg .stat ,
12321257 ATTR_TIMEOUT (& outarg ), attr_version );
12331258 }
@@ -1292,30 +1317,31 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
12921317 bool sync ;
12931318 u32 inval_mask = READ_ONCE (fi -> inval_mask );
12941319 u32 cache_mask = fuse_get_cache_mask (inode );
1295-
1320+ u32 mandatory_sx_mask = request_mask & STATX_BASIC_STATS ;
1321+ u32 sx_mask = request_mask ;
12961322
12971323 /* FUSE only supports basic stats and possibly btime */
1298- request_mask &= STATX_BASIC_STATS | STATX_BTIME ;
1324+ sx_mask &= STATX_BASIC_STATS | STATX_BTIME ;
12991325retry :
13001326 if (fc -> no_statx )
1301- request_mask &= STATX_BASIC_STATS ;
1327+ sx_mask &= STATX_BASIC_STATS ;
13021328
1303- if (!request_mask )
1329+ if (!sx_mask )
13041330 sync = false;
13051331 else if (flags & AT_STATX_FORCE_SYNC )
13061332 sync = true;
13071333 else if (flags & AT_STATX_DONT_SYNC )
13081334 sync = false;
1309- else if (request_mask & inval_mask & ~cache_mask )
1335+ else if (sx_mask & inval_mask & ~cache_mask )
13101336 sync = true;
13111337 else
13121338 sync = time_before64 (fi -> i_time , get_jiffies_64 ());
13131339
13141340 if (sync ) {
13151341 forget_all_cached_acls (inode );
1316- /* Try statx if BTIME is requested */
1317- if (! fc -> no_statx && ( request_mask & ~ STATX_BASIC_STATS )) {
1318- err = fuse_do_statx ( inode , file , stat );
1342+ if (! fc -> no_statx ) {
1343+ err = fuse_do_statx ( inode , file , stat , sx_mask ,
1344+ mandatory_sx_mask );
13191345 if (err == - ENOSYS ) {
13201346 fc -> no_statx = 1 ;
13211347 err = 0 ;
@@ -1477,13 +1503,14 @@ static int fuse_access(struct inode *inode, int mask)
14771503 return err ;
14781504}
14791505
1480- static int fuse_perm_getattr (struct inode * inode , int mask )
1506+ static int fuse_perm_getattr (struct inode * inode , int mask , int perm_mask )
14811507{
14821508 if (mask & MAY_NOT_BLOCK )
14831509 return - ECHILD ;
14841510
14851511 forget_all_cached_acls (inode );
1486- return fuse_do_getattr (inode , NULL , NULL );
1512+ return fuse_update_get_attr (inode , NULL , NULL , perm_mask ,
1513+ AT_STATX_FORCE_SYNC );
14871514}
14881515
14891516/*
@@ -1505,6 +1532,7 @@ static int fuse_permission(struct mnt_idmap *idmap,
15051532 struct fuse_conn * fc = get_fuse_conn (inode );
15061533 bool refreshed = false;
15071534 int err = 0 ;
1535+ int perm_mask = STATX_MODE | STATX_UID | STATX_GID ;
15081536
15091537 if (fuse_is_bad (inode ))
15101538 return - EIO ;
@@ -1518,13 +1546,12 @@ static int fuse_permission(struct mnt_idmap *idmap,
15181546 if (fc -> default_permissions ||
15191547 ((mask & MAY_EXEC ) && S_ISREG (inode -> i_mode ))) {
15201548 struct fuse_inode * fi = get_fuse_inode (inode );
1521- u32 perm_mask = STATX_MODE | STATX_UID | STATX_GID ;
15221549
15231550 if (perm_mask & READ_ONCE (fi -> inval_mask ) ||
15241551 time_before64 (fi -> i_time , get_jiffies_64 ())) {
15251552 refreshed = true;
15261553
1527- err = fuse_perm_getattr (inode , mask );
1554+ err = fuse_perm_getattr (inode , mask , perm_mask );
15281555 if (err )
15291556 return err ;
15301557 }
@@ -1537,7 +1564,7 @@ static int fuse_permission(struct mnt_idmap *idmap,
15371564 attributes. This is also needed, because the root
15381565 node will at first have no permissions */
15391566 if (err == - EACCES && !refreshed ) {
1540- err = fuse_perm_getattr (inode , mask );
1567+ err = fuse_perm_getattr (inode , mask , perm_mask );
15411568 if (!err )
15421569 err = generic_permission (& nop_mnt_idmap ,
15431570 inode , mask );
@@ -1554,7 +1581,7 @@ static int fuse_permission(struct mnt_idmap *idmap,
15541581 if (refreshed )
15551582 return - EACCES ;
15561583
1557- err = fuse_perm_getattr (inode , mask );
1584+ err = fuse_perm_getattr (inode , mask , perm_mask );
15581585 if (!err && !(inode -> i_mode & S_IXUGO ))
15591586 return - EACCES ;
15601587 }
0 commit comments