Skip to content

Commit 0ef625b

Browse files
mjguzikbrauner
authored andcommitted
vfs: support statx(..., NULL, AT_EMPTY_PATH, ...)
The newly used helper also checks for empty ("") paths. NULL paths with any flag value other than AT_EMPTY_PATH go the usual route and end up with -EFAULT to retain compatibility (Rust is abusing calls of the sort to detect availability of statx). This avoids path lookup code, lockref management, memory allocation and in case of NULL path userspace memory access (which can be quite expensive with SMAP on x86_64). Benchmarked with statx(..., AT_EMPTY_PATH, ...) running on Sapphire Rapids, with the "" path for the first two cases and NULL for the last one. Results in ops/s: stock: 4231237 pre-check: 5944063 (+40%) NULL path: 6601619 (+11%/+56%) Signed-off-by: Mateusz Guzik <[email protected]> Link: https://lore.kernel.org/r/[email protected] Tested-by: Xi Ruoyao <[email protected]> [brauner: use path_mounted() and other tweaks] Signed-off-by: Christian Brauner <[email protected]>
1 parent 27a2d0c commit 0ef625b

File tree

3 files changed

+97
-41
lines changed

3 files changed

+97
-41
lines changed

fs/internal.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ extern const struct dentry_operations ns_dentry_operations;
247247
int getname_statx_lookup_flags(int flags);
248248
int do_statx(int dfd, struct filename *filename, unsigned int flags,
249249
unsigned int mask, struct statx __user *buffer);
250+
int do_statx_fd(int fd, unsigned int flags, unsigned int mask,
251+
struct statx __user *buffer);
250252

251253
/*
252254
* fs/splice.c:
@@ -321,3 +323,15 @@ struct stashed_operations {
321323
int path_from_stashed(struct dentry **stashed, struct vfsmount *mnt, void *data,
322324
struct path *path);
323325
void stashed_dentry_prune(struct dentry *dentry);
326+
/**
327+
* path_mounted - check whether path is mounted
328+
* @path: path to check
329+
*
330+
* Determine whether @path refers to the root of a mount.
331+
*
332+
* Return: true if @path is the root of a mount, false if not.
333+
*/
334+
static inline bool path_mounted(const struct path *path)
335+
{
336+
return path->mnt->mnt_root == path->dentry;
337+
}

fs/namespace.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,19 +1846,6 @@ bool may_mount(void)
18461846
return ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN);
18471847
}
18481848

1849-
/**
1850-
* path_mounted - check whether path is mounted
1851-
* @path: path to check
1852-
*
1853-
* Determine whether @path refers to the root of a mount.
1854-
*
1855-
* Return: true if @path is the root of a mount, false if not.
1856-
*/
1857-
static inline bool path_mounted(const struct path *path)
1858-
{
1859-
return path->mnt->mnt_root == path->dentry;
1860-
}
1861-
18621849
static void warn_mandlock(void)
18631850
{
18641851
pr_warn_once("=======================================================\n"

fs/stat.c

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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,
243280
retry:
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
*/
697737
SYSCALL_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

Comments
 (0)