@@ -350,10 +350,14 @@ int fuse_valid_type(int m)
350
350
S_ISBLK (m ) || S_ISFIFO (m ) || S_ISSOCK (m );
351
351
}
352
352
353
+ static bool fuse_valid_size (u64 size )
354
+ {
355
+ return size <= LLONG_MAX ;
356
+ }
357
+
353
358
bool fuse_invalid_attr (struct fuse_attr * attr )
354
359
{
355
- return !fuse_valid_type (attr -> mode ) ||
356
- attr -> size > LLONG_MAX ;
360
+ return !fuse_valid_type (attr -> mode ) || !fuse_valid_size (attr -> size );
357
361
}
358
362
359
363
int fuse_lookup_name (struct super_block * sb , u64 nodeid , const struct qstr * name ,
@@ -1143,6 +1147,84 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
1143
1147
stat -> blksize = 1 << blkbits ;
1144
1148
}
1145
1149
1150
+ static void fuse_statx_to_attr (struct fuse_statx * sx , struct fuse_attr * attr )
1151
+ {
1152
+ memset (attr , 0 , sizeof (* attr ));
1153
+ attr -> ino = sx -> ino ;
1154
+ attr -> size = sx -> size ;
1155
+ attr -> blocks = sx -> blocks ;
1156
+ attr -> atime = sx -> atime .tv_sec ;
1157
+ attr -> mtime = sx -> mtime .tv_sec ;
1158
+ attr -> ctime = sx -> ctime .tv_sec ;
1159
+ attr -> atimensec = sx -> atime .tv_nsec ;
1160
+ attr -> mtimensec = sx -> mtime .tv_nsec ;
1161
+ attr -> ctimensec = sx -> ctime .tv_nsec ;
1162
+ attr -> mode = sx -> mode ;
1163
+ attr -> nlink = sx -> nlink ;
1164
+ attr -> uid = sx -> uid ;
1165
+ attr -> gid = sx -> gid ;
1166
+ attr -> rdev = new_encode_dev (MKDEV (sx -> rdev_major , sx -> rdev_minor ));
1167
+ attr -> blksize = sx -> blksize ;
1168
+ }
1169
+
1170
+ static int fuse_do_statx (struct inode * inode , struct file * file ,
1171
+ struct kstat * stat )
1172
+ {
1173
+ int err ;
1174
+ struct fuse_attr attr ;
1175
+ struct fuse_statx * sx ;
1176
+ struct fuse_statx_in inarg ;
1177
+ struct fuse_statx_out outarg ;
1178
+ struct fuse_mount * fm = get_fuse_mount (inode );
1179
+ u64 attr_version = fuse_get_attr_version (fm -> fc );
1180
+ FUSE_ARGS (args );
1181
+
1182
+ memset (& inarg , 0 , sizeof (inarg ));
1183
+ memset (& outarg , 0 , sizeof (outarg ));
1184
+ /* Directories have separate file-handle space */
1185
+ if (file && S_ISREG (inode -> i_mode )) {
1186
+ struct fuse_file * ff = file -> private_data ;
1187
+
1188
+ inarg .getattr_flags |= FUSE_GETATTR_FH ;
1189
+ inarg .fh = ff -> fh ;
1190
+ }
1191
+ /* For now leave sync hints as the default, request all stats. */
1192
+ inarg .sx_flags = 0 ;
1193
+ inarg .sx_mask = STATX_BASIC_STATS | STATX_BTIME ;
1194
+ args .opcode = FUSE_STATX ;
1195
+ args .nodeid = get_node_id (inode );
1196
+ args .in_numargs = 1 ;
1197
+ args .in_args [0 ].size = sizeof (inarg );
1198
+ args .in_args [0 ].value = & inarg ;
1199
+ args .out_numargs = 1 ;
1200
+ args .out_args [0 ].size = sizeof (outarg );
1201
+ args .out_args [0 ].value = & outarg ;
1202
+ err = fuse_simple_request (fm , & args );
1203
+ if (err )
1204
+ return err ;
1205
+
1206
+ sx = & outarg .stat ;
1207
+ if (((sx -> mask & STATX_SIZE ) && !fuse_valid_size (sx -> size )) ||
1208
+ ((sx -> mask & STATX_TYPE ) && (!fuse_valid_type (sx -> mode ) ||
1209
+ inode_wrong_type (inode , sx -> mode )))) {
1210
+ make_bad_inode (inode );
1211
+ return - EIO ;
1212
+ }
1213
+
1214
+ fuse_statx_to_attr (& outarg .stat , & attr );
1215
+ if ((sx -> mask & STATX_BASIC_STATS ) == STATX_BASIC_STATS ) {
1216
+ fuse_change_attributes (inode , & attr , ATTR_TIMEOUT (& outarg ),
1217
+ attr_version );
1218
+ }
1219
+ stat -> result_mask = sx -> mask & (STATX_BASIC_STATS | STATX_BTIME );
1220
+ stat -> btime .tv_sec = sx -> btime .tv_sec ;
1221
+ stat -> btime .tv_nsec = min_t (u32 , sx -> btime .tv_nsec , NSEC_PER_SEC - 1 );
1222
+ fuse_fillattr (inode , & attr , stat );
1223
+ stat -> result_mask |= STATX_TYPE ;
1224
+
1225
+ return 0 ;
1226
+ }
1227
+
1146
1228
static int fuse_do_getattr (struct inode * inode , struct kstat * stat ,
1147
1229
struct file * file )
1148
1230
{
@@ -1194,13 +1276,18 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
1194
1276
unsigned int flags )
1195
1277
{
1196
1278
struct fuse_inode * fi = get_fuse_inode (inode );
1279
+ struct fuse_conn * fc = get_fuse_conn (inode );
1197
1280
int err = 0 ;
1198
1281
bool sync ;
1199
1282
u32 inval_mask = READ_ONCE (fi -> inval_mask );
1200
1283
u32 cache_mask = fuse_get_cache_mask (inode );
1201
1284
1202
- /* FUSE only supports basic stats */
1203
- request_mask &= STATX_BASIC_STATS ;
1285
+
1286
+ /* FUSE only supports basic stats and possibly btime */
1287
+ request_mask &= STATX_BASIC_STATS | STATX_BTIME ;
1288
+ retry :
1289
+ if (fc -> no_statx )
1290
+ request_mask &= STATX_BASIC_STATS ;
1204
1291
1205
1292
if (!request_mask )
1206
1293
sync = false;
@@ -1215,7 +1302,16 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
1215
1302
1216
1303
if (sync ) {
1217
1304
forget_all_cached_acls (inode );
1218
- err = fuse_do_getattr (inode , stat , file );
1305
+ /* Try statx if BTIME is requested */
1306
+ if (!fc -> no_statx && (request_mask & ~STATX_BASIC_STATS )) {
1307
+ err = fuse_do_statx (inode , file , stat );
1308
+ if (err == - ENOSYS ) {
1309
+ fc -> no_statx = 1 ;
1310
+ goto retry ;
1311
+ }
1312
+ } else {
1313
+ err = fuse_do_getattr (inode , stat , file );
1314
+ }
1219
1315
} else if (stat ) {
1220
1316
generic_fillattr (& nop_mnt_idmap , inode , stat );
1221
1317
stat -> mode = fi -> orig_i_mode ;
0 commit comments