@@ -214,6 +214,43 @@ int getname_statx_lookup_flags(int flags)
214214 return lookup_flags ;
215215}
216216
217+ static int vfs_statx_path (struct path * path , int flags , struct kstat * stat ,
218+ u32 request_mask )
219+ {
220+ int error = vfs_getattr (path , stat , request_mask , flags );
221+
222+ if (request_mask & STATX_MNT_ID_UNIQUE ) {
223+ stat -> mnt_id = real_mount (path -> mnt )-> mnt_id_unique ;
224+ stat -> result_mask |= STATX_MNT_ID_UNIQUE ;
225+ } else {
226+ stat -> mnt_id = real_mount (path -> mnt )-> mnt_id ;
227+ stat -> result_mask |= STATX_MNT_ID ;
228+ }
229+
230+ if (path_mounted (path ))
231+ stat -> attributes |= STATX_ATTR_MOUNT_ROOT ;
232+ stat -> attributes_mask |= STATX_ATTR_MOUNT_ROOT ;
233+
234+ /* Handle STATX_DIOALIGN for block devices. */
235+ if (request_mask & STATX_DIOALIGN ) {
236+ struct inode * inode = d_backing_inode (path -> dentry );
237+
238+ if (S_ISBLK (inode -> i_mode ))
239+ bdev_statx_dioalign (inode , stat );
240+ }
241+
242+ return error ;
243+ }
244+
245+ static int vfs_statx_fd (int fd , int flags , struct kstat * stat ,
246+ u32 request_mask )
247+ {
248+ CLASS (fd_raw , f )(fd );
249+ if (!f .file )
250+ return - EBADF ;
251+ return vfs_statx_path (& f .file -> f_path , flags , stat , request_mask );
252+ }
253+
217254/**
218255 * vfs_statx - Get basic and extra attributes by filename
219256 * @dfd: A file descriptor representing the base dir for a relative filename
@@ -243,36 +280,13 @@ static int vfs_statx(int dfd, struct filename *filename, int flags,
243280retry :
244281 error = filename_lookup (dfd , filename , lookup_flags , & path , NULL );
245282 if (error )
246- goto out ;
247-
248- error = vfs_getattr (& path , stat , request_mask , flags );
249-
250- if (request_mask & STATX_MNT_ID_UNIQUE ) {
251- stat -> mnt_id = real_mount (path .mnt )-> mnt_id_unique ;
252- stat -> result_mask |= STATX_MNT_ID_UNIQUE ;
253- } else {
254- stat -> mnt_id = real_mount (path .mnt )-> mnt_id ;
255- stat -> result_mask |= STATX_MNT_ID ;
256- }
257-
258- if (path .mnt -> mnt_root == path .dentry )
259- stat -> attributes |= STATX_ATTR_MOUNT_ROOT ;
260- stat -> attributes_mask |= STATX_ATTR_MOUNT_ROOT ;
261-
262- /* Handle STATX_DIOALIGN for block devices. */
263- if (request_mask & STATX_DIOALIGN ) {
264- struct inode * inode = d_backing_inode (path .dentry );
265-
266- if (S_ISBLK (inode -> i_mode ))
267- bdev_statx_dioalign (inode , stat );
268- }
269-
283+ return error ;
284+ error = vfs_statx_path (& path , flags , stat , request_mask );
270285 path_put (& path );
271286 if (retry_estale (error , lookup_flags )) {
272287 lookup_flags |= LOOKUP_REVAL ;
273288 goto retry ;
274289 }
275- out :
276290 return error ;
277291}
278292
@@ -671,7 +685,8 @@ int do_statx(int dfd, struct filename *filename, unsigned int flags,
671685 if ((flags & AT_STATX_SYNC_TYPE ) == AT_STATX_SYNC_TYPE )
672686 return - EINVAL ;
673687
674- /* STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
688+ /*
689+ * STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
675690 * from userland.
676691 */
677692 mask &= ~STATX_CHANGE_COOKIE ;
@@ -683,25 +698,65 @@ int do_statx(int dfd, struct filename *filename, unsigned int flags,
683698 return cp_statx (& stat , buffer );
684699}
685700
701+ int do_statx_fd (int fd , unsigned int flags , unsigned int mask ,
702+ struct statx __user * buffer )
703+ {
704+ struct kstat stat ;
705+ int error ;
706+
707+ if (mask & STATX__RESERVED )
708+ return - EINVAL ;
709+ if ((flags & AT_STATX_SYNC_TYPE ) == AT_STATX_SYNC_TYPE )
710+ return - EINVAL ;
711+
712+ /*
713+ * STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
714+ * from userland.
715+ */
716+ mask &= ~STATX_CHANGE_COOKIE ;
717+
718+ error = vfs_statx_fd (fd , flags , & stat , mask );
719+ if (error )
720+ return error ;
721+
722+ return cp_statx (& stat , buffer );
723+ }
724+
686725/**
687726 * sys_statx - System call to get enhanced stats
688727 * @dfd: Base directory to pathwalk from *or* fd to stat.
689- * @filename: File to stat or "" with AT_EMPTY_PATH
728+ * @filename: File to stat or either NULL or "" with AT_EMPTY_PATH
690729 * @flags: AT_* flags to control pathwalk.
691730 * @mask: Parts of statx struct actually required.
692731 * @buffer: Result buffer.
693732 *
694733 * Note that fstat() can be emulated by setting dfd to the fd of interest,
695- * supplying "" as the filename and setting AT_EMPTY_PATH in the flags.
734+ * supplying "" (or preferably NULL) as the filename and setting AT_EMPTY_PATH
735+ * in the flags.
696736 */
697737SYSCALL_DEFINE5 (statx ,
698738 int , dfd , const char __user * , filename , unsigned , flags ,
699739 unsigned int , mask ,
700740 struct statx __user * , buffer )
701741{
702742 int ret ;
743+ unsigned lflags ;
703744 struct filename * name ;
704745
746+ /*
747+ * Short-circuit handling of NULL and "" paths.
748+ *
749+ * For a NULL path we require and accept only the AT_EMPTY_PATH flag
750+ * (possibly |'d with AT_STATX flags).
751+ *
752+ * However, glibc on 32-bit architectures implements fstatat as statx
753+ * with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags.
754+ * Supporting this results in the uglification below.
755+ */
756+ lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE );
757+ if (lflags == AT_EMPTY_PATH && vfs_empty_path (dfd , filename ))
758+ return do_statx_fd (dfd , flags & ~AT_NO_AUTOMOUNT , mask , buffer );
759+
705760 name = getname_flags (filename , getname_statx_lookup_flags (flags ));
706761 ret = do_statx (dfd , name , flags , mask , buffer );
707762 putname (name );
0 commit comments