[PATCH] io: statx, fstatat64: allow using NULL as path
Miao Wang via B4 Relay
devnull+shankerwangmiao.gmail.com@kernel.org
Tue Sep 24 16:37:50 GMT 2024
From: Miao Wang <shankerwangmiao@gmail.com>
Since linux 6.11, fstatat and statx syscalls allow using NULL as path
when AT_EMPTY_PATH is given. We also expose this to glibc users, and
change path to an empty string when this is not supported by the kernel.
Signed-off-by: Miao Wang <shankerwangmiao@gmail.com>
---
Since kernel 6.11, statx/fstatat(..., NULL, AT_EMPTH_PATH...) are
supported for better performance by avoiding copying buffer data from
userland. Since currently the declaration of statx and fstatat flags
the path argument as nonull, it prevents users from utilizing this
kernel feature. I can come up with the following possible solutions:
1. Directly remove the nonull annotation on the path argument, and
pass the parameter given by the user as-is to the kernel, exposing
the kernel behavior. This would be confusing since on older kernels,
the user would receive EFAULT and need to handle this.
2. Based on 1, but if the user provides NULL, the handling of EFAULT is
integrated in glibc, who implicitly change the parameter to an empty
string if EFAULT received.
In either case, I think there is no need to introduce a new ABI version
of statx() and fstatat(), since we are loosening the restriction on the
given parameters, and executables linked with previous versions of statx
and fstatat can still work after this change.
This RFC patch contains changes for the solution 2 above.
---
io/bits/statx-generic.h | 2 +-
io/sys/stat.h | 10 +++++-----
io/tst-statx.c | 6 ++++++
sysdeps/mach/hurd/fstatat64.c | 2 ++
sysdeps/unix/sysv/linux/fstatat64.c | 12 ++++++++++++
sysdeps/unix/sysv/linux/statx.c | 7 ++++++-
6 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/io/bits/statx-generic.h b/io/bits/statx-generic.h
index 19c3565edc..c0e00c9d8b 100644
--- a/io/bits/statx-generic.h
+++ b/io/bits/statx-generic.h
@@ -62,6 +62,6 @@ __BEGIN_DECLS
/* Fill *BUF with information about PATH in DIRFD. */
int statx (int __dirfd, const char *__restrict __path, int __flags,
unsigned int __mask, struct statx *__restrict __buf)
- __THROW __nonnull ((2, 5));
+ __THROW __nonnull ((5));
__END_DECLS
diff --git a/io/sys/stat.h b/io/sys/stat.h
index 3b4ba80132..7ea39b8755 100644
--- a/io/sys/stat.h
+++ b/io/sys/stat.h
@@ -263,14 +263,14 @@ extern int __REDIRECT_NTH (fstat64, (int __fd, struct stat64 *__buf),
# ifndef __USE_FILE_OFFSET64
extern int fstatat (int __fd, const char *__restrict __file,
struct stat *__restrict __buf, int __flag)
- __THROW __nonnull ((2, 3));
+ __THROW __nonnull ((3));
# else
# ifdef __USE_TIME64_REDIRECTS
# ifdef __REDIRECT_NTH
extern int __REDIRECT_NTH (fstatat, (int __fd, const char *__restrict __file,
struct stat *__restrict __buf,
int __flag),
- __fstatat64_time64) __nonnull ((2, 3));
+ __fstatat64_time64) __nonnull ((3));
# else
# define fstatat __fstatat64_time64
# endif
@@ -279,7 +279,7 @@ extern int __REDIRECT_NTH (fstatat, (int __fd, const char *__restrict __file,
extern int __REDIRECT_NTH (fstatat, (int __fd, const char *__restrict __file,
struct stat *__restrict __buf,
int __flag),
- fstatat64) __nonnull ((2, 3));
+ fstatat64) __nonnull ((3));
# else
# define fstatat fstatat64
# endif
@@ -290,7 +290,7 @@ extern int __REDIRECT_NTH (fstatat, (int __fd, const char *__restrict __file,
# ifndef __USE_TIME64_REDIRECTS
extern int fstatat64 (int __fd, const char *__restrict __file,
struct stat64 *__restrict __buf, int __flag)
- __THROW __nonnull ((2, 3));
+ __THROW __nonnull ((3));
# else
# ifdef __REDIRECT_NTH
extern int __REDIRECT_NTH (fstatat64, (int __fd,
@@ -298,7 +298,7 @@ extern int __REDIRECT_NTH (fstatat64, (int __fd,
struct stat64 *__restrict __buf,
int __flag),
__fstatat64_time64)
- __nonnull ((2, 3));
+ __nonnull ((3));
# else
# define fstatat64 __fstatat64_time64
# endif
diff --git a/io/tst-statx.c b/io/tst-statx.c
index 685924ae76..4b70999e96 100644
--- a/io/tst-statx.c
+++ b/io/tst-statx.c
@@ -74,6 +74,12 @@ both_implementations_tests (statx_function impl, const char *path, int fd)
TEST_COMPARE (buf.stx_size, 3);
TEST_COMPARE (buf.stx_ino, ino);
}
+ {
+ struct statx buf = { 0, };
+ TEST_COMPARE (statx (fd, NULL, AT_EMPTY_PATH, STATX_BASIC_STATS, &buf), 0);
+ TEST_COMPARE (buf.stx_size, 3);
+ TEST_COMPARE (buf.stx_ino, ino);
+ }
{
struct statx stx = { 0, };
TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &stx), 0);
diff --git a/sysdeps/mach/hurd/fstatat64.c b/sysdeps/mach/hurd/fstatat64.c
index 61b6593f3a..18dc9d2f08 100644
--- a/sysdeps/mach/hurd/fstatat64.c
+++ b/sysdeps/mach/hurd/fstatat64.c
@@ -45,6 +45,8 @@ __fstatat64_common (int fd, const char *filename, struct stat64 *buf, int at_fla
int
__fstatat64 (int fd, const char *filename, struct stat64 *buf, int at_flags)
{
+ if ((at_flags & AT_EMPTY_PATH) && filename == NULL)
+ filename = "";
return __fstatat64_common (fd, filename, buf, at_flags, 0);
}
libc_hidden_def (__fstatat64)
diff --git a/sysdeps/unix/sysv/linux/fstatat64.c b/sysdeps/unix/sysv/linux/fstatat64.c
index 59b461cbcf..a9e3b85179 100644
--- a/sysdeps/unix/sysv/linux/fstatat64.c
+++ b/sysdeps/unix/sysv/linux/fstatat64.c
@@ -156,6 +156,10 @@ __fstatat64_time64 (int fd, const char *file, struct __stat64_t64 *buf,
{
int r;
+#ifndef __ASSUME_STATX_NULL_PATH
+retry:
+#endif
+
#if FSTATAT_USE_STATX
r = fstatat64_time64_statx (fd, file, buf, flag);
# ifndef __ASSUME_STATX
@@ -166,6 +170,14 @@ __fstatat64_time64 (int fd, const char *file, struct __stat64_t64 *buf,
r = fstatat64_time64_stat (fd, file, buf, flag);
#endif
+#ifndef __ASSUME_STATX_NULL_PATH
+ if (file == NULL && (flag & AT_EMPTY_PATH) && r == -EFAULT)
+ {
+ file = "";
+ goto retry;
+ }
+#endif
+
return INTERNAL_SYSCALL_ERROR_P (r)
? INLINE_SYSCALL_ERROR_RETURN_VALUE (-r)
: 0;
diff --git a/sysdeps/unix/sysv/linux/statx.c b/sysdeps/unix/sysv/linux/statx.c
index a6295a155d..9549614a6b 100644
--- a/sysdeps/unix/sysv/linux/statx.c
+++ b/sysdeps/unix/sysv/linux/statx.c
@@ -25,7 +25,12 @@ int
statx (int fd, const char *path, int flags,
unsigned int mask, struct statx *buf)
{
- int ret = INLINE_SYSCALL_CALL (statx, fd, path, flags, mask, buf);
+ int ret;
+ ret = INLINE_SYSCALL_CALL (statx, fd, path, flags, mask, buf);
+#ifndef __ASSUME_STATX_NULL_PATH
+ if (path == NULL && (flags & AT_EMPTY_PATH) && errno == EFAULT)
+ ret = INLINE_SYSCALL_CALL (statx, fd, "", flags, mask, buf);
+#endif
#ifdef __ASSUME_STATX
return ret;
#else
---
base-commit: 2eee835eca960c9d4119279804214b7a1ed5d156
change-id: 20240925-statx-null-path-user-c0c4ebbdf21c
prerequisite-change-id: 20240821-statx-null-path-531c0775bba4:v5
prerequisite-patch-id: cf313d4d0660fa972ce419167e3847722c3481d3
Best regards,
--
Miao Wang <shankerwangmiao@gmail.com>
More information about the Libc-alpha
mailing list