* [RFC PATCH v2 00/18] io_uring, struct filename and audit
@ 2025-11-29 17:01 Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 01/18] do_faccessat(): import pathname only once Al Viro
` (18 more replies)
0 siblings, 19 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Changes compared to v1:
* putname_to_delayed(): new primitive, hopefully solving the
io_openat2() breakage spotted by Jens
* Linus' suggestion re saner allocation for struct filename
implemented and carved up [##11--15]
It's obviously doing to slip to the next cycle at this point - I'm not
proposing to merge it in the coming window.
Please, review. Branch in
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.filename-refcnt
individual patches in followups.
Al Viro (17):
do_faccessat(): import pathname only once
do_fchmodat(): import pathname only once
do_fchownat(): import pathname only once
do_utimes_path(): import pathname only once
chdir(2): import pathname only once
chroot(2): import pathname only once
user_statfs(): import pathname only once
do_sys_truncate(): import pathname only once
do_readlinkat(): import pathname only once
get rid of audit_reusename()
ntfs: ->d_compare() must not block
getname_flags() massage, part 1
getname_flags() massage, part 2
struct filename: use names_cachep only for getname() and friends
struct filename: saner handling of long names
allow incomplete imports of filenames
struct filename ->refcnt doesn't need to be atomic
Mateusz Guzik (1):
fs: touch up predicts in putname()
fs/dcache.c | 8 +-
fs/internal.h | 2 +
fs/namei.c | 218 +++++++++++++++++++++++++++---------------
fs/ntfs3/namei.c | 8 +-
fs/open.c | 39 +++++---
fs/stat.c | 6 +-
fs/statfs.c | 4 +-
fs/utimes.c | 13 +--
include/linux/audit.h | 11 ---
include/linux/fs.h | 28 +++---
io_uring/fs.c | 101 ++++++++++---------
io_uring/openclose.c | 26 ++---
io_uring/statx.c | 17 ++--
io_uring/xattr.c | 30 ++----
kernel/auditsc.c | 23 +----
15 files changed, 286 insertions(+), 248 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 26+ messages in thread
* [RFC PATCH v2 01/18] do_faccessat(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 02/18] do_fchmodat(): " Al Viro
` (17 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
Since we have the default logics for use of LOOKUP_EMPTY (passed iff
AT_EMPTY_PATH is present in flags), just use getname_uflags() and
don't bother with setting LOOKUP_EMPTY in lookup_flags - getname_uflags()
will pass the right thing to getname_flags() and filename_lookup()
doesn't care about LOOKUP_EMPTY at all.
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/open.c b/fs/open.c
index 3d64372ecc67..db8fe2b5463d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -471,6 +471,7 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
const struct cred *old_cred = NULL;
+ struct filename *name;
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
@@ -480,8 +481,6 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag
if (flags & AT_SYMLINK_NOFOLLOW)
lookup_flags &= ~LOOKUP_FOLLOW;
- if (flags & AT_EMPTY_PATH)
- lookup_flags |= LOOKUP_EMPTY;
if (access_need_override_creds(flags)) {
old_cred = access_override_creds();
@@ -489,8 +488,9 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag
return -ENOMEM;
}
+ name = getname_uflags(filename, flags);
retry:
- res = user_path_at(dfd, filename, lookup_flags, &path);
+ res = filename_lookup(dfd, name, lookup_flags, &path, NULL);
if (res)
goto out;
@@ -530,6 +530,7 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag
goto retry;
}
out:
+ putname(name);
if (old_cred)
put_cred(revert_creds(old_cred));
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 02/18] do_fchmodat(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 01/18] do_faccessat(): import pathname only once Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 03/18] do_fchownat(): " Al Viro
` (16 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
Since we have the default logics for use of LOOKUP_EMPTY (passed iff
AT_EMPTY_PATH is present in flags), just use getname_uflags() and
don't bother with setting LOOKUP_EMPTY in lookup_flags - getname_uflags()
will pass the right thing to getname_flags() and filename_lookup()
doesn't care about LOOKUP_EMPTY at all.
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/open.c b/fs/open.c
index db8fe2b5463d..e9a08a820e49 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -682,6 +682,7 @@ static int do_fchmodat(int dfd, const char __user *filename, umode_t mode,
unsigned int flags)
{
struct path path;
+ struct filename *name;
int error;
unsigned int lookup_flags;
@@ -689,11 +690,9 @@ static int do_fchmodat(int dfd, const char __user *filename, umode_t mode,
return -EINVAL;
lookup_flags = (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
- if (flags & AT_EMPTY_PATH)
- lookup_flags |= LOOKUP_EMPTY;
-
+ name = getname_uflags(filename, flags);
retry:
- error = user_path_at(dfd, filename, lookup_flags, &path);
+ error = filename_lookup(dfd, name, lookup_flags, &path, NULL);
if (!error) {
error = chmod_common(&path, mode);
path_put(&path);
@@ -702,6 +701,7 @@ static int do_fchmodat(int dfd, const char __user *filename, umode_t mode,
goto retry;
}
}
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 03/18] do_fchownat(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 01/18] do_faccessat(): import pathname only once Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 02/18] do_fchmodat(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 04/18] do_utimes_path(): " Al Viro
` (15 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
Since we have the default logics for use of LOOKUP_EMPTY (passed iff
AT_EMPTY_PATH is present in flags), just use getname_uflags() and
don't bother with setting LOOKUP_EMPTY in lookup_flags - getname_uflags()
will pass the right thing to getname_flags() and filename_lookup()
doesn't care about LOOKUP_EMPTY at all.
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/fs/open.c b/fs/open.c
index e9a08a820e49..e5110f5e80c7 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -804,17 +804,17 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
int flag)
{
struct path path;
- int error = -EINVAL;
+ int error;
int lookup_flags;
+ struct filename *name;
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
- goto out;
+ return -EINVAL;
lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
- if (flag & AT_EMPTY_PATH)
- lookup_flags |= LOOKUP_EMPTY;
+ name = getname_uflags(filename, flag);
retry:
- error = user_path_at(dfd, filename, lookup_flags, &path);
+ error = filename_lookup(dfd, name, lookup_flags, &path, NULL);
if (error)
goto out;
error = mnt_want_write(path.mnt);
@@ -829,6 +829,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
goto retry;
}
out:
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 04/18] do_utimes_path(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (2 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 03/18] do_fchownat(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 05/18] chdir(2): " Al Viro
` (14 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
Since we have the default logics for use of LOOKUP_EMPTY (passed iff
AT_EMPTY_PATH is present in flags), just use getname_uflags() and
don't bother with setting LOOKUP_EMPTY in lookup_flags - getname_uflags()
will pass the right thing to getname_flags() and filename_lookup()
doesn't care about LOOKUP_EMPTY at all.
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/utimes.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/utimes.c b/fs/utimes.c
index c7c7958e57b2..262a4ddeb9cc 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -8,6 +8,7 @@
#include <linux/compat.h>
#include <asm/unistd.h>
#include <linux/filelock.h>
+#include "internal.h"
static bool nsec_valid(long nsec)
{
@@ -82,27 +83,27 @@ static int do_utimes_path(int dfd, const char __user *filename,
{
struct path path;
int lookup_flags = 0, error;
+ struct filename *name;
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
return -EINVAL;
if (!(flags & AT_SYMLINK_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW;
- if (flags & AT_EMPTY_PATH)
- lookup_flags |= LOOKUP_EMPTY;
+ name = getname_uflags(filename, flags);
retry:
- error = user_path_at(dfd, filename, lookup_flags, &path);
+ error = filename_lookup(dfd, name, lookup_flags, &path, NULL);
if (error)
- return error;
-
+ goto out;
error = vfs_utimes(&path, times);
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
-
+out:
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 05/18] chdir(2): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (3 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 04/18] do_utimes_path(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 06/18] chroot(2): " Al Viro
` (13 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
In this case we never pass LOOKUP_EMPTY, so getname_flags() is equivalent
to plain getname().
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/open.c b/fs/open.c
index e5110f5e80c7..8bc2f313f4a9 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -558,8 +558,9 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+ struct filename *name = getname(filename);
retry:
- error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
+ error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL);
if (error)
goto out;
@@ -576,6 +577,7 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename)
goto retry;
}
out:
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 06/18] chroot(2): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (4 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 05/18] chdir(2): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 07/18] user_statfs(): " Al Viro
` (12 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
In this case we never pass LOOKUP_EMPTY, so getname_flags() is equivalent
to plain getname().
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/open.c b/fs/open.c
index 8bc2f313f4a9..e67baae339fc 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -603,8 +603,9 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+ struct filename *name = getname(filename);
retry:
- error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
+ error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL);
if (error)
goto out;
@@ -628,6 +629,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename)
goto retry;
}
out:
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 07/18] user_statfs(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (5 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 06/18] chroot(2): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 08/18] do_sys_truncate(): " Al Viro
` (11 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
In this case we never pass LOOKUP_EMPTY, so getname_flags() is equivalent
to plain getname().
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/statfs.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/statfs.c b/fs/statfs.c
index a45ac85e6048..a5671bf6c7f0 100644
--- a/fs/statfs.c
+++ b/fs/statfs.c
@@ -99,8 +99,9 @@ int user_statfs(const char __user *pathname, struct kstatfs *st)
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT;
+ struct filename *name = getname(pathname);
retry:
- error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+ error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL);
if (!error) {
error = vfs_statfs(&path, st);
path_put(&path);
@@ -109,6 +110,7 @@ int user_statfs(const char __user *pathname, struct kstatfs *st)
goto retry;
}
}
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 08/18] do_sys_truncate(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (6 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 07/18] user_statfs(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 09/18] do_readlinkat(): " Al Viro
` (10 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Convert the user_path_at() call inside a retry loop into getname_flags() +
filename_lookup() + putname() and leave only filename_lookup() inside
the loop.
In this case we never pass LOOKUP_EMPTY, so getname_flags() is equivalent
to plain getname().
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/open.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/open.c b/fs/open.c
index e67baae339fc..eb2ff940393d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -129,14 +129,16 @@ EXPORT_SYMBOL_GPL(vfs_truncate);
int do_sys_truncate(const char __user *pathname, loff_t length)
{
unsigned int lookup_flags = LOOKUP_FOLLOW;
+ struct filename *name;
struct path path;
int error;
if (length < 0) /* sorry, but loff_t says... */
return -EINVAL;
+ name = getname(pathname);
retry:
- error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
+ error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL);
if (!error) {
error = vfs_truncate(&path, length);
path_put(&path);
@@ -145,6 +147,7 @@ int do_sys_truncate(const char __user *pathname, loff_t length)
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 09/18] do_readlinkat(): import pathname only once
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (7 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 08/18] do_sys_truncate(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 10/18] get rid of audit_reusename() Al Viro
` (9 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Take getname_flags() and putname() outside of retry loop.
Since getname_flags() is the only thing that cares about LOOKUP_EMPTY,
don't bother with setting LOOKUP_EMPTY in lookup_flags - just pass it
to getname_flags() and be done with that.
The things could be further simplified by use of cleanup.h stuff, but
let's not clutter the patch with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/stat.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/stat.c b/fs/stat.c
index 6c79661e1b96..ee9ae2c3273a 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -566,13 +566,13 @@ static int do_readlinkat(int dfd, const char __user *pathname,
struct path path;
struct filename *name;
int error;
- unsigned int lookup_flags = LOOKUP_EMPTY;
+ unsigned int lookup_flags = 0;
if (bufsiz <= 0)
return -EINVAL;
+ name = getname_flags(pathname, LOOKUP_EMPTY);
retry:
- name = getname_flags(pathname, lookup_flags);
error = filename_lookup(dfd, name, lookup_flags, &path, NULL);
if (unlikely(error)) {
putname(name);
@@ -593,11 +593,11 @@ static int do_readlinkat(int dfd, const char __user *pathname,
error = (name->name[0] == '\0') ? -ENOENT : -EINVAL;
}
path_put(&path);
- putname(name);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
+ putname(name);
return error;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 10/18] get rid of audit_reusename()
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (8 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 09/18] do_readlinkat(): " Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-12-16 2:14 ` Paul Moore
2025-11-29 17:01 ` [RFC PATCH v2 11/18] ntfs: ->d_compare() must not block Al Viro
` (8 subsequent siblings)
18 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Originally we tried to avoid multiple insertions into audit names array
during retry loop by a cute hack - memorize the userland pointer and
if there already is a match, just grab an extra reference to it.
Cute as it had been, it had problems - two identical pointers had
audit aux entries merged, two identical strings did not. Having
different behaviour for syscalls that differ only by addresses of
otherwise identical string arguments is obviously wrong - if nothing
else, compiler can decide to merge identical string literals.
Besides, this hack does nothing for non-audited processes - they get
a fresh copy for retry. It's not time-critical, but having behaviour
subtly differ that way is bogus.
These days we have very few places that import filename more than once
(9 functions total) and it's easy to massage them so we get rid of all
re-imports. With that done, we don't need audit_reusename() anymore.
There's no need to memorize userland pointer either.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 11 +++--------
include/linux/audit.h | 11 -----------
include/linux/fs.h | 1 -
kernel/auditsc.c | 23 -----------------------
4 files changed, 3 insertions(+), 43 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 7377020a2cba..dd86e41deeeb 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -125,9 +125,8 @@
#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname))
-static inline void initname(struct filename *name, const char __user *uptr)
+static inline void initname(struct filename *name)
{
- name->uptr = uptr;
name->aname = NULL;
atomic_set(&name->refcnt, 1);
}
@@ -139,10 +138,6 @@ getname_flags(const char __user *filename, int flags)
char *kname;
int len;
- result = audit_reusename(filename);
- if (result)
- return result;
-
result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
@@ -210,7 +205,7 @@ getname_flags(const char __user *filename, int flags)
return ERR_PTR(-ENAMETOOLONG);
}
}
- initname(result, filename);
+ initname(result);
audit_getname(result);
return result;
}
@@ -268,7 +263,7 @@ struct filename *getname_kernel(const char * filename)
return ERR_PTR(-ENAMETOOLONG);
}
memcpy((char *)result->name, filename, len);
- initname(result, NULL);
+ initname(result);
audit_getname(result);
return result;
}
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 536f8ee8da81..d936a604d056 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -316,7 +316,6 @@ extern void __audit_uring_exit(int success, long code);
extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void __audit_syscall_exit(int ret_success, long ret_value);
-extern struct filename *__audit_reusename(const __user char *uptr);
extern void __audit_getname(struct filename *name);
extern void __audit_inode(struct filename *name, const struct dentry *dentry,
unsigned int flags);
@@ -380,12 +379,6 @@ static inline void audit_syscall_exit(void *pt_regs)
__audit_syscall_exit(success, return_code);
}
}
-static inline struct filename *audit_reusename(const __user char *name)
-{
- if (unlikely(!audit_dummy_context()))
- return __audit_reusename(name);
- return NULL;
-}
static inline void audit_getname(struct filename *name)
{
if (unlikely(!audit_dummy_context()))
@@ -624,10 +617,6 @@ static inline struct audit_context *audit_context(void)
{
return NULL;
}
-static inline struct filename *audit_reusename(const __user char *name)
-{
- return NULL;
-}
static inline void audit_getname(struct filename *name)
{ }
static inline void audit_inode(struct filename *name,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c895146c1444..bbae3cfdc338 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2835,7 +2835,6 @@ extern struct kobject *fs_kobj;
struct audit_names;
struct filename {
const char *name; /* pointer to actual string */
- const __user char *uptr; /* original userland pointer */
atomic_t refcnt;
struct audit_names *aname;
const char iname[];
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index d1966144bdfe..e59a094bb9f7 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2169,29 +2169,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
return aname;
}
-/**
- * __audit_reusename - fill out filename with info from existing entry
- * @uptr: userland ptr to pathname
- *
- * Search the audit_names list for the current audit context. If there is an
- * existing entry with a matching "uptr" then return the filename
- * associated with that audit_name. If not, return NULL.
- */
-struct filename *
-__audit_reusename(const __user char *uptr)
-{
- struct audit_context *context = audit_context();
- struct audit_names *n;
-
- list_for_each_entry(n, &context->names_list, list) {
- if (!n->name)
- continue;
- if (n->name->uptr == uptr)
- return refname(n->name);
- }
- return NULL;
-}
-
/**
* __audit_getname - add a name to the list
* @name: name to add
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 11/18] ntfs: ->d_compare() must not block
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (9 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 10/18] get rid of audit_reusename() Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 12/18] getname_flags() massage, part 1 Al Viro
` (7 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
... so don't use __getname() there. Switch it (and ntfs_d_hash(), while
we are at it) to kmalloc(PATH_MAX, GFP_NOWAIT). Yes, ntfs_d_hash()
almost certainly can do with smaller allocations, but let ntfs folks
deal with that - keep the allocation size as-is for now.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/ntfs3/namei.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
index 82c8ae56beee..ab96290ee6d9 100644
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@ -407,7 +407,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name)
/*
* Try slow way with current upcase table
*/
- uni = kmem_cache_alloc(names_cachep, GFP_NOWAIT);
+ uni = kmalloc(PATH_MAX, GFP_NOWAIT);
if (!uni)
return -ENOMEM;
@@ -429,7 +429,7 @@ static int ntfs_d_hash(const struct dentry *dentry, struct qstr *name)
err = 0;
out:
- kmem_cache_free(names_cachep, uni);
+ kfree(uni);
return err;
}
@@ -468,7 +468,7 @@ static int ntfs_d_compare(const struct dentry *dentry, unsigned int len1,
* Try slow way with current upcase table
*/
sbi = dentry->d_sb->s_fs_info;
- uni1 = __getname();
+ uni1 = kmalloc(PATH_MAX, GFP_NOWAIT);
if (!uni1)
return -ENOMEM;
@@ -498,7 +498,7 @@ static int ntfs_d_compare(const struct dentry *dentry, unsigned int len1,
ret = !ntfs_cmp_names_cpu(uni1, uni2, sbi->upcase, false) ? 0 : 1;
out:
- __putname(uni1);
+ kfree(uni1);
return ret;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 12/18] getname_flags() massage, part 1
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (10 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 11/18] ntfs: ->d_compare() must not block Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 13/18] getname_flags() massage, part 2 Al Viro
` (6 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
In case of long name don't reread what we'd already copied.
memmove() it instead. That avoids the possibility of ending
up with empty name there and the need to look at the flags
on the slow path.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 33 ++++++++++++++++-----------------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index dd86e41deeeb..bc5fe9732949 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -174,36 +174,35 @@ getname_flags(const char __user *filename, int flags)
*/
if (unlikely(len == EMBEDDED_NAME_MAX)) {
const size_t size = offsetof(struct filename, iname[1]);
- kname = (char *)result;
+ struct filename *p;
/*
* size is chosen that way we to guarantee that
* result->iname[0] is within the same object and that
* kname can't be equal to result->iname, no matter what.
*/
- result = kzalloc(size, GFP_KERNEL);
- if (unlikely(!result)) {
- __putname(kname);
+ p = kzalloc(size, GFP_KERNEL);
+ if (unlikely(!p)) {
+ __putname(result);
return ERR_PTR(-ENOMEM);
}
- result->name = kname;
- len = strncpy_from_user(kname, filename, PATH_MAX);
+ memmove(result, &result->iname, EMBEDDED_NAME_MAX);
+ kname = (char *)result;
+ p->name = kname;
+ len = strncpy_from_user(kname + EMBEDDED_NAME_MAX,
+ filename + EMBEDDED_NAME_MAX,
+ PATH_MAX - EMBEDDED_NAME_MAX);
if (unlikely(len < 0)) {
- __putname(kname);
- kfree(result);
+ kfree(p);
+ __putname(result);
return ERR_PTR(len);
}
- /* The empty path is special. */
- if (unlikely(!len) && !(flags & LOOKUP_EMPTY)) {
- __putname(kname);
- kfree(result);
- return ERR_PTR(-ENOENT);
- }
- if (unlikely(len == PATH_MAX)) {
- __putname(kname);
- kfree(result);
+ if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX)) {
+ kfree(p);
+ __putname(result);
return ERR_PTR(-ENAMETOOLONG);
}
+ result = p;
}
initname(result);
audit_getname(result);
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 13/18] getname_flags() massage, part 2
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (11 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 12/18] getname_flags() massage, part 1 Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 14/18] struct filename: use names_cachep only for getname() and friends Al Viro
` (5 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Take the "long name" case into a helper (getname_long()). In
case of failure have the caller deal with freeing the original
struct filename.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 56 ++++++++++++++++++++++++++++--------------------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index bc5fe9732949..62e992e4f152 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -131,6 +131,32 @@ static inline void initname(struct filename *name)
atomic_set(&name->refcnt, 1);
}
+static struct filename *getname_long(struct filename *old,
+ const char __user *filename)
+{
+ int len;
+ /*
+ * size is chosen that way we to guarantee that
+ * p->iname[0] is within the same object and that
+ * p->name can't be equal to p->iname, no matter what.
+ */
+ const size_t size = offsetof(struct filename, iname[1]);
+ struct filename *p __free(kfree) = kzalloc(size, GFP_KERNEL);
+ if (unlikely(!p))
+ return ERR_PTR(-ENOMEM);
+
+ memmove(old, &old->iname, EMBEDDED_NAME_MAX);
+ p->name = (char *)old;
+ len = strncpy_from_user((char *)old + EMBEDDED_NAME_MAX,
+ filename + EMBEDDED_NAME_MAX,
+ PATH_MAX - EMBEDDED_NAME_MAX);
+ if (unlikely(len < 0))
+ return ERR_PTR(len);
+ if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX))
+ return ERR_PTR(-ENAMETOOLONG);
+ return no_free_ptr(p);
+}
+
struct filename *
getname_flags(const char __user *filename, int flags)
{
@@ -173,34 +199,10 @@ getname_flags(const char __user *filename, int flags)
* userland.
*/
if (unlikely(len == EMBEDDED_NAME_MAX)) {
- const size_t size = offsetof(struct filename, iname[1]);
- struct filename *p;
-
- /*
- * size is chosen that way we to guarantee that
- * result->iname[0] is within the same object and that
- * kname can't be equal to result->iname, no matter what.
- */
- p = kzalloc(size, GFP_KERNEL);
- if (unlikely(!p)) {
- __putname(result);
- return ERR_PTR(-ENOMEM);
- }
- memmove(result, &result->iname, EMBEDDED_NAME_MAX);
- kname = (char *)result;
- p->name = kname;
- len = strncpy_from_user(kname + EMBEDDED_NAME_MAX,
- filename + EMBEDDED_NAME_MAX,
- PATH_MAX - EMBEDDED_NAME_MAX);
- if (unlikely(len < 0)) {
- kfree(p);
- __putname(result);
- return ERR_PTR(len);
- }
- if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX)) {
- kfree(p);
+ struct filename *p = getname_long(result, filename);
+ if (IS_ERR(p)) {
__putname(result);
- return ERR_PTR(-ENAMETOOLONG);
+ return p;
}
result = p;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 14/18] struct filename: use names_cachep only for getname() and friends
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (12 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 13/18] getname_flags() massage, part 2 Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 15/18] struct filename: saner handling of long names Al Viro
` (4 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Instances of struct filename come from names_cachep (via
__getname()). That is done by getname_flags() and getname_kernel()
and these two are the main callers of __getname(). However, there are
other callers that simply want to allocate PATH_MAX bytes for uses that
have nothing to do with struct filename.
We want saner allocation rules for long pathnames, so that struct
filename would *always* come from names_cachep, with the out-of-line
pathname getting kmalloc'ed. For that we need to be able to change the
size of objects allocated by getname_flags()/getname_kernel().
That requires the rest of __getname() users to stop using
names_cachep; we could explicitly switch all of those to kmalloc(),
but that would cause quite a bit of noise. So the plan is to switch
getname_...() to new helpers and turn __getname() into a wrapper for
kmalloc(). Remaining __getname() users could be converted to explicit
kmalloc() at leisure, hopefully along with figuring out what size do
they really want - PATH_MAX is an overkill for some of them, used out
of laziness ("we have a convenient helper that does 4K allocations and
that's large enough, let's use it").
As a side benefit, names_cachep is no longer used outside
of fs/namei.c, so we can move it there and be done with that.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/dcache.c | 8 +-------
fs/internal.h | 2 ++
fs/namei.c | 37 ++++++++++++++++++++++++++++---------
include/linux/fs.h | 6 ++----
4 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/fs/dcache.c b/fs/dcache.c
index 035cccbc9276..761283f13200 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -3246,10 +3246,6 @@ static void __init dcache_init(void)
runtime_const_init(ptr, dentry_hashtable);
}
-/* SLAB cache for __getname() consumers */
-struct kmem_cache *names_cachep __ro_after_init;
-EXPORT_SYMBOL(names_cachep);
-
void __init vfs_caches_init_early(void)
{
int i;
@@ -3263,9 +3259,7 @@ void __init vfs_caches_init_early(void)
void __init vfs_caches_init(void)
{
- names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
-
+ filename_init();
dcache_init();
inode_init();
files_init();
diff --git a/fs/internal.h b/fs/internal.h
index 9b2b4d116880..e16e72b246c2 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -68,6 +68,8 @@ int vfs_tmpfile(struct mnt_idmap *idmap,
struct file *file, umode_t mode);
struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
+void __init filename_init(void);
+
/*
* namespace.c
*/
diff --git a/fs/namei.c b/fs/namei.c
index 62e992e4f152..cf7d8608ce4e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -125,6 +125,25 @@
#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname))
+/* SLAB cache for struct filename instances */
+static struct kmem_cache *names_cachep __ro_after_init;
+
+void __init filename_init(void)
+{
+ names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
+}
+
+static inline struct filename *alloc_filename(void)
+{
+ return kmem_cache_alloc(names_cachep, GFP_KERNEL);
+}
+
+static inline void free_filename(struct filename *p)
+{
+ kmem_cache_free(names_cachep, p);
+}
+
static inline void initname(struct filename *name)
{
name->aname = NULL;
@@ -164,7 +183,7 @@ getname_flags(const char __user *filename, int flags)
char *kname;
int len;
- result = __getname();
+ result = alloc_filename();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
@@ -181,13 +200,13 @@ getname_flags(const char __user *filename, int flags)
*/
if (unlikely(len <= 0)) {
if (unlikely(len < 0)) {
- __putname(result);
+ free_filename(result);
return ERR_PTR(len);
}
/* The empty path is special. */
if (!(flags & LOOKUP_EMPTY)) {
- __putname(result);
+ free_filename(result);
return ERR_PTR(-ENOENT);
}
}
@@ -201,7 +220,7 @@ getname_flags(const char __user *filename, int flags)
if (unlikely(len == EMBEDDED_NAME_MAX)) {
struct filename *p = getname_long(result, filename);
if (IS_ERR(p)) {
- __putname(result);
+ free_filename(result);
return p;
}
result = p;
@@ -242,7 +261,7 @@ struct filename *getname_kernel(const char * filename)
struct filename *result;
int len = strlen(filename) + 1;
- result = __getname();
+ result = alloc_filename();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
@@ -254,13 +273,13 @@ struct filename *getname_kernel(const char * filename)
tmp = kmalloc(size, GFP_KERNEL);
if (unlikely(!tmp)) {
- __putname(result);
+ free_filename(result);
return ERR_PTR(-ENOMEM);
}
tmp->name = (char *)result;
result = tmp;
} else {
- __putname(result);
+ free_filename(result);
return ERR_PTR(-ENAMETOOLONG);
}
memcpy((char *)result->name, filename, len);
@@ -287,10 +306,10 @@ void putname(struct filename *name)
}
if (name->name != name->iname) {
- __putname(name->name);
+ free_filename((struct filename *)name->name);
kfree(name);
} else
- __putname(name);
+ free_filename(name);
}
EXPORT_SYMBOL(putname);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index bbae3cfdc338..59c5c67985ab 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2957,10 +2957,8 @@ static inline int finish_open_simple(struct file *file, int error)
extern void __init vfs_caches_init_early(void);
extern void __init vfs_caches_init(void);
-extern struct kmem_cache *names_cachep;
-
-#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
-#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
+#define __getname() kmalloc(PATH_MAX, GFP_KERNEL)
+#define __putname(name) kfree(name)
extern struct super_block *blockdev_superblock;
static inline bool sb_is_blkdev_sb(struct super_block *sb)
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 15/18] struct filename: saner handling of long names
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (13 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 14/18] struct filename: use names_cachep only for getname() and friends Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:33 ` Mateusz Guzik
2025-11-29 17:01 ` [RFC PATCH v2 16/18] allow incomplete imports of filenames Al Viro
` (3 subsequent siblings)
18 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
Always allocate struct filename from names_cachep, long name or short;
names shorter than 128 characters would be embedded into struct filename.
Longer ones do not cannibalize the original struct filename - put them
into PATH_MAX-sized kmalloc'ed buffers.
Simplifies logics in getname()/putname() and friends.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 87 ++++++++++++++++++----------------------------
include/linux/fs.h | 4 ++-
2 files changed, 36 insertions(+), 55 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index cf7d8608ce4e..fc04f938d7e1 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -123,15 +123,14 @@
* PATH_MAX includes the nul terminator --RR.
*/
-#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname))
-
/* SLAB cache for struct filename instances */
static struct kmem_cache *names_cachep __ro_after_init;
void __init filename_init(void)
{
- names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
+ names_cachep = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),
+ EMBEDDED_NAME_MAX, NULL);
}
static inline struct filename *alloc_filename(void)
@@ -150,30 +149,23 @@ static inline void initname(struct filename *name)
atomic_set(&name->refcnt, 1);
}
-static struct filename *getname_long(struct filename *old,
- const char __user *filename)
+static int getname_long(struct filename *name, const char __user *filename)
{
int len;
- /*
- * size is chosen that way we to guarantee that
- * p->iname[0] is within the same object and that
- * p->name can't be equal to p->iname, no matter what.
- */
- const size_t size = offsetof(struct filename, iname[1]);
- struct filename *p __free(kfree) = kzalloc(size, GFP_KERNEL);
+ char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL);
if (unlikely(!p))
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
- memmove(old, &old->iname, EMBEDDED_NAME_MAX);
- p->name = (char *)old;
- len = strncpy_from_user((char *)old + EMBEDDED_NAME_MAX,
+ memcpy(p, &name->iname, EMBEDDED_NAME_MAX);
+ len = strncpy_from_user(p + EMBEDDED_NAME_MAX,
filename + EMBEDDED_NAME_MAX,
PATH_MAX - EMBEDDED_NAME_MAX);
if (unlikely(len < 0))
- return ERR_PTR(len);
+ return len;
if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX))
- return ERR_PTR(-ENAMETOOLONG);
- return no_free_ptr(p);
+ return -ENAMETOOLONG;
+ name->name = no_free_ptr(p);
+ return 0;
}
struct filename *
@@ -199,16 +191,9 @@ getname_flags(const char __user *filename, int flags)
* Handle both empty path and copy failure in one go.
*/
if (unlikely(len <= 0)) {
- if (unlikely(len < 0)) {
- free_filename(result);
- return ERR_PTR(len);
- }
-
/* The empty path is special. */
- if (!(flags & LOOKUP_EMPTY)) {
- free_filename(result);
- return ERR_PTR(-ENOENT);
- }
+ if (!len && !(flags & LOOKUP_EMPTY))
+ len = -ENOENT;
}
/*
@@ -217,14 +202,13 @@ getname_flags(const char __user *filename, int flags)
* names_cache allocation for the pathname, and re-do the copy from
* userland.
*/
- if (unlikely(len == EMBEDDED_NAME_MAX)) {
- struct filename *p = getname_long(result, filename);
- if (IS_ERR(p)) {
- free_filename(result);
- return p;
- }
- result = p;
+ if (unlikely(len == EMBEDDED_NAME_MAX))
+ len = getname_long(result, filename);
+ if (unlikely(len < 0)) {
+ free_filename(result);
+ return ERR_PTR(len);
}
+
initname(result);
audit_getname(result);
return result;
@@ -260,29 +244,26 @@ struct filename *getname_kernel(const char * filename)
{
struct filename *result;
int len = strlen(filename) + 1;
+ char *p;
+
+ if (unlikely(len > PATH_MAX))
+ return ERR_PTR(-ENAMETOOLONG);
result = alloc_filename();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
if (len <= EMBEDDED_NAME_MAX) {
- result->name = (char *)result->iname;
- } else if (len <= PATH_MAX) {
- const size_t size = offsetof(struct filename, iname[1]);
- struct filename *tmp;
-
- tmp = kmalloc(size, GFP_KERNEL);
- if (unlikely(!tmp)) {
+ p = (char *)result->iname;
+ memcpy(p, filename, len);
+ } else {
+ p = kmemdup(filename, len, GFP_KERNEL);
+ if (unlikely(!p)) {
free_filename(result);
return ERR_PTR(-ENOMEM);
}
- tmp->name = (char *)result;
- result = tmp;
- } else {
- free_filename(result);
- return ERR_PTR(-ENAMETOOLONG);
}
- memcpy((char *)result->name, filename, len);
+ result->name = p;
initname(result);
audit_getname(result);
return result;
@@ -305,11 +286,9 @@ void putname(struct filename *name)
return;
}
- if (name->name != name->iname) {
- free_filename((struct filename *)name->name);
- kfree(name);
- } else
- free_filename(name);
+ if (unlikely(name->name != name->iname))
+ kfree(name->name);
+ free_filename(name);
}
EXPORT_SYMBOL(putname);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 59c5c67985ab..0b01adcfa425 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2833,11 +2833,13 @@ extern struct kobject *fs_kobj;
/* fs/open.c */
struct audit_names;
+
+#define EMBEDDED_NAME_MAX 128
struct filename {
const char *name; /* pointer to actual string */
atomic_t refcnt;
struct audit_names *aname;
- const char iname[];
+ const char iname[EMBEDDED_NAME_MAX];
};
static_assert(offsetof(struct filename, iname) % sizeof(long) == 0);
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 16/18] allow incomplete imports of filenames
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (14 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 15/18] struct filename: saner handling of long names Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 17/18] fs: touch up predicts in putname() Al Viro
` (2 subsequent siblings)
18 siblings, 0 replies; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
There are two filename-related problems in io_uring and its
interplay with audit.
Filenames are imported when request is submitted and used when
it is processed. Unfortunately, the latter may very well
happen in a different thread. In that case the reference to
filename is put into the wrong audit_context - that of submitting
thread, not the processing one. Audit logics is called by
the latter, and it really wants to be able to find the names
in audit_context current (== processing) thread.
Another related problem is the headache with refcounts -
normally all references to given struct filename are visible
only to one thread (the one that uses that struct filename).
io_uring violates that - an extra reference is stashed in
audit_context of submitter. It gets dropped when submitter
returns to userland, which can happen simultaneously with
processing thread deciding to drop the reference it got.
We paper over that by making refcount atomic, but that means
pointless headache for everyone.
Solution: the notion of partially imported filenames. Namely,
already copied from userland, but *not* exposed to audit yet.
io_uring can create that in submitter thread, and complete the
import (obtaining the usual reference to struct filename) in
processing thread.
Object: struct delayed_filename.
Primitives for working with it:
delayed_getname(&delayed_filename, user_string) - copies the name
from userland, returning 0 and stashing the address of (still incomplete)
struct filename in delayed_filename on success and returning -E... on
error.
delayed_getname_uflags(&delayed_filename, user_string, atflags) - similar,
in the same relation to delayed_getname() as getname_uflags() is to getname()
complete_getname(&delayed_filename) - completes the import of filename stashed
in delayed_getname and returns struct filename to caller, emptying delayed_getname.
dismiss_delayed_filename(&delayed_filename) - destructor; drops whatever
might be stashed in delayed_getname, emptying it.
putname_to_delayed(&delayed_filename, name) - if name is shared,
stashes its copy into delayed_filename and drops the reference to name,
otherwise stashes the name itself in there.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 72 ++++++++++++++++++++++++++++--
include/linux/fs.h | 11 +++++
io_uring/fs.c | 101 +++++++++++++++++++++++--------------------
io_uring/openclose.c | 26 +++++------
io_uring/statx.c | 17 +++-----
io_uring/xattr.c | 30 +++++--------
6 files changed, 164 insertions(+), 93 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index fc04f938d7e1..37b13339f046 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -168,8 +168,8 @@ static int getname_long(struct filename *name, const char __user *filename)
return 0;
}
-struct filename *
-getname_flags(const char __user *filename, int flags)
+static struct filename *
+do_getname(const char __user *filename, int flags, bool incomplete)
{
struct filename *result;
char *kname;
@@ -210,10 +210,17 @@ getname_flags(const char __user *filename, int flags)
}
initname(result);
- audit_getname(result);
+ if (likely(!incomplete))
+ audit_getname(result);
return result;
}
+struct filename *
+getname_flags(const char __user *filename, int flags)
+{
+ return do_getname(filename, flags, false);
+}
+
struct filename *getname_uflags(const char __user *filename, int uflags)
{
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
@@ -292,6 +299,65 @@ void putname(struct filename *name)
}
EXPORT_SYMBOL(putname);
+static inline int __delayed_getname(struct delayed_filename *v,
+ const char __user *string, int flags)
+{
+ v->__incomplete_filename = do_getname(string, flags, true);
+ return PTR_ERR_OR_ZERO(v->__incomplete_filename);
+}
+
+int delayed_getname(struct delayed_filename *v, const char __user *string)
+{
+ return __delayed_getname(v, string, 0);
+}
+
+int delayed_getname_uflags(struct delayed_filename *v, const char __user *string,
+ int uflags)
+{
+ int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
+ return __delayed_getname(v, string, flags);
+}
+
+int putname_to_delayed(struct delayed_filename *v, struct filename *__name)
+{
+ struct filename *name __free(putname) = no_free_ptr(__name);
+ struct filename *copy;
+
+ if (likely(atomic_read(&name->refcnt) == 1)) {
+ v->__incomplete_filename = no_free_ptr(name);
+ return 0;
+ }
+ copy = alloc_filename();
+ if (unlikely(!copy))
+ return -ENOMEM;
+ if (likely(name->name == name->iname)) {
+ copy->name = copy->iname;
+ strcpy((char *)copy->iname, name->iname);
+ } else {
+ copy->name = kmemdup(name->name, PATH_MAX, GFP_KERNEL);
+ if (unlikely(!copy->name)) {
+ free_filename(copy);
+ return -ENOMEM;
+ }
+ }
+ initname(copy);
+ v->__incomplete_filename = copy;
+ return 0;
+}
+
+void dismiss_delayed_filename(struct delayed_filename *v)
+{
+ putname(no_free_ptr(v->__incomplete_filename));
+}
+
+struct filename *complete_getname(struct delayed_filename *v)
+{
+ struct filename *res = no_free_ptr(v->__incomplete_filename);
+ if (!IS_ERR(res))
+ audit_getname(res);
+ return res;
+}
+
/**
* check_acl - perform ACL permission checking
* @idmap: idmap of the mount the inode was found from
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0b01adcfa425..52ee3bc1baa9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2936,6 +2936,17 @@ static inline struct filename *getname_maybe_null(const char __user *name, int f
extern void putname(struct filename *name);
DEFINE_FREE(putname, struct filename *, if (!IS_ERR_OR_NULL(_T)) putname(_T))
+struct delayed_filename {
+ struct filename *__incomplete_filename; // don't touch
+};
+#define INIT_DELAYED_FILENAME(ptr) \
+ ((void)(*(ptr) = (struct delayed_filename){}))
+int delayed_getname(struct delayed_filename *, const char __user *);
+int delayed_getname_uflags(struct delayed_filename *v, const char __user *, int);
+void dismiss_delayed_filename(struct delayed_filename *);
+int putname_to_delayed(struct delayed_filename *, struct filename *);
+struct filename *complete_getname(struct delayed_filename *);
+
static inline struct filename *refname(struct filename *name)
{
atomic_inc(&name->refcnt);
diff --git a/io_uring/fs.c b/io_uring/fs.c
index 37079a414eab..c04c6282210a 100644
--- a/io_uring/fs.c
+++ b/io_uring/fs.c
@@ -19,8 +19,8 @@ struct io_rename {
struct file *file;
int old_dfd;
int new_dfd;
- struct filename *oldpath;
- struct filename *newpath;
+ struct delayed_filename oldpath;
+ struct delayed_filename newpath;
int flags;
};
@@ -28,22 +28,22 @@ struct io_unlink {
struct file *file;
int dfd;
int flags;
- struct filename *filename;
+ struct delayed_filename filename;
};
struct io_mkdir {
struct file *file;
int dfd;
umode_t mode;
- struct filename *filename;
+ struct delayed_filename filename;
};
struct io_link {
struct file *file;
int old_dfd;
int new_dfd;
- struct filename *oldpath;
- struct filename *newpath;
+ struct delayed_filename oldpath;
+ struct delayed_filename newpath;
int flags;
};
@@ -51,6 +51,7 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename);
const char __user *oldf, *newf;
+ int err;
if (sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -63,14 +64,14 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
ren->new_dfd = READ_ONCE(sqe->len);
ren->flags = READ_ONCE(sqe->rename_flags);
- ren->oldpath = getname(oldf);
- if (IS_ERR(ren->oldpath))
- return PTR_ERR(ren->oldpath);
+ err = delayed_getname(&ren->oldpath, oldf);
+ if (unlikely(err))
+ return err;
- ren->newpath = getname(newf);
- if (IS_ERR(ren->newpath)) {
- putname(ren->oldpath);
- return PTR_ERR(ren->newpath);
+ err = delayed_getname(&ren->newpath, newf);
+ if (unlikely(err)) {
+ dismiss_delayed_filename(&ren->oldpath);
+ return err;
}
req->flags |= REQ_F_NEED_CLEANUP;
@@ -85,8 +86,9 @@ int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = do_renameat2(ren->old_dfd, ren->oldpath, ren->new_dfd,
- ren->newpath, ren->flags);
+ ret = do_renameat2(ren->old_dfd, complete_getname(&ren->oldpath),
+ ren->new_dfd, complete_getname(&ren->newpath),
+ ren->flags);
req->flags &= ~REQ_F_NEED_CLEANUP;
io_req_set_res(req, ret, 0);
@@ -97,14 +99,15 @@ void io_renameat_cleanup(struct io_kiocb *req)
{
struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename);
- putname(ren->oldpath);
- putname(ren->newpath);
+ dismiss_delayed_filename(&ren->oldpath);
+ dismiss_delayed_filename(&ren->newpath);
}
int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink);
const char __user *fname;
+ int err;
if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -118,9 +121,9 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return -EINVAL;
fname = u64_to_user_ptr(READ_ONCE(sqe->addr));
- un->filename = getname(fname);
- if (IS_ERR(un->filename))
- return PTR_ERR(un->filename);
+ err = delayed_getname(&un->filename, fname);
+ if (unlikely(err))
+ return err;
req->flags |= REQ_F_NEED_CLEANUP;
req->flags |= REQ_F_FORCE_ASYNC;
@@ -135,9 +138,9 @@ int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
if (un->flags & AT_REMOVEDIR)
- ret = do_rmdir(un->dfd, un->filename);
+ ret = do_rmdir(un->dfd, complete_getname(&un->filename));
else
- ret = do_unlinkat(un->dfd, un->filename);
+ ret = do_unlinkat(un->dfd, complete_getname(&un->filename));
req->flags &= ~REQ_F_NEED_CLEANUP;
io_req_set_res(req, ret, 0);
@@ -148,13 +151,14 @@ void io_unlinkat_cleanup(struct io_kiocb *req)
{
struct io_unlink *ul = io_kiocb_to_cmd(req, struct io_unlink);
- putname(ul->filename);
+ dismiss_delayed_filename(&ul->filename);
}
int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir);
const char __user *fname;
+ int err;
if (sqe->off || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -165,9 +169,9 @@ int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
mkd->mode = READ_ONCE(sqe->len);
fname = u64_to_user_ptr(READ_ONCE(sqe->addr));
- mkd->filename = getname(fname);
- if (IS_ERR(mkd->filename))
- return PTR_ERR(mkd->filename);
+ err = delayed_getname(&mkd->filename, fname);
+ if (unlikely(err))
+ return err;
req->flags |= REQ_F_NEED_CLEANUP;
req->flags |= REQ_F_FORCE_ASYNC;
@@ -181,7 +185,7 @@ int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode);
+ ret = do_mkdirat(mkd->dfd, complete_getname(&mkd->filename), mkd->mode);
req->flags &= ~REQ_F_NEED_CLEANUP;
io_req_set_res(req, ret, 0);
@@ -192,13 +196,14 @@ void io_mkdirat_cleanup(struct io_kiocb *req)
{
struct io_mkdir *md = io_kiocb_to_cmd(req, struct io_mkdir);
- putname(md->filename);
+ dismiss_delayed_filename(&md->filename);
}
int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_link *sl = io_kiocb_to_cmd(req, struct io_link);
const char __user *oldpath, *newpath;
+ int err;
if (sqe->len || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -209,14 +214,14 @@ int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
oldpath = u64_to_user_ptr(READ_ONCE(sqe->addr));
newpath = u64_to_user_ptr(READ_ONCE(sqe->addr2));
- sl->oldpath = getname(oldpath);
- if (IS_ERR(sl->oldpath))
- return PTR_ERR(sl->oldpath);
+ err = delayed_getname(&sl->oldpath, oldpath);
+ if (unlikely(err))
+ return err;
- sl->newpath = getname(newpath);
- if (IS_ERR(sl->newpath)) {
- putname(sl->oldpath);
- return PTR_ERR(sl->newpath);
+ err = delayed_getname(&sl->newpath, newpath);
+ if (unlikely(err)) {
+ dismiss_delayed_filename(&sl->oldpath);
+ return err;
}
req->flags |= REQ_F_NEED_CLEANUP;
@@ -231,7 +236,8 @@ int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath);
+ ret = do_symlinkat(complete_getname(&sl->oldpath), sl->new_dfd,
+ complete_getname(&sl->newpath));
req->flags &= ~REQ_F_NEED_CLEANUP;
io_req_set_res(req, ret, 0);
@@ -242,6 +248,7 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link);
const char __user *oldf, *newf;
+ int err;
if (sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -254,14 +261,14 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
newf = u64_to_user_ptr(READ_ONCE(sqe->addr2));
lnk->flags = READ_ONCE(sqe->hardlink_flags);
- lnk->oldpath = getname_uflags(oldf, lnk->flags);
- if (IS_ERR(lnk->oldpath))
- return PTR_ERR(lnk->oldpath);
+ err = delayed_getname_uflags(&lnk->oldpath, oldf, lnk->flags);
+ if (unlikely(err))
+ return err;
- lnk->newpath = getname(newf);
- if (IS_ERR(lnk->newpath)) {
- putname(lnk->oldpath);
- return PTR_ERR(lnk->newpath);
+ err = delayed_getname(&lnk->newpath, newf);
+ if (unlikely(err)) {
+ dismiss_delayed_filename(&lnk->oldpath);
+ return err;
}
req->flags |= REQ_F_NEED_CLEANUP;
@@ -276,8 +283,8 @@ int io_linkat(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = do_linkat(lnk->old_dfd, lnk->oldpath, lnk->new_dfd,
- lnk->newpath, lnk->flags);
+ ret = do_linkat(lnk->old_dfd, complete_getname(&lnk->oldpath),
+ lnk->new_dfd, complete_getname(&lnk->newpath), lnk->flags);
req->flags &= ~REQ_F_NEED_CLEANUP;
io_req_set_res(req, ret, 0);
@@ -288,6 +295,6 @@ void io_link_cleanup(struct io_kiocb *req)
{
struct io_link *sl = io_kiocb_to_cmd(req, struct io_link);
- putname(sl->oldpath);
- putname(sl->newpath);
+ dismiss_delayed_filename(&sl->oldpath);
+ dismiss_delayed_filename(&sl->newpath);
}
diff --git a/io_uring/openclose.c b/io_uring/openclose.c
index bfeb91b31bba..95ba8c5b5fc8 100644
--- a/io_uring/openclose.c
+++ b/io_uring/openclose.c
@@ -23,7 +23,7 @@ struct io_open {
struct file *file;
int dfd;
u32 file_slot;
- struct filename *filename;
+ struct delayed_filename filename;
struct open_how how;
unsigned long nofile;
};
@@ -67,12 +67,9 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
open->dfd = READ_ONCE(sqe->fd);
fname = u64_to_user_ptr(READ_ONCE(sqe->addr));
- open->filename = getname(fname);
- if (IS_ERR(open->filename)) {
- ret = PTR_ERR(open->filename);
- open->filename = NULL;
+ ret = delayed_getname(&open->filename, fname);
+ if (unlikely(ret))
return ret;
- }
open->file_slot = READ_ONCE(sqe->file_index);
if (open->file_slot && (open->how.flags & O_CLOEXEC))
@@ -121,6 +118,7 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
struct file *file;
bool resolve_nonblock, nonblock_set;
bool fixed = !!open->file_slot;
+ struct filename *name __free(putname) = complete_getname(&open->filename);
int ret;
ret = build_open_flags(&open->how, &op);
@@ -140,7 +138,7 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
goto err;
}
- file = do_filp_open(open->dfd, open->filename, &op);
+ file = do_filp_open(open->dfd, name, &op);
if (IS_ERR(file)) {
/*
* We could hang on to this 'fd' on retrying, but seems like
@@ -152,9 +150,13 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
ret = PTR_ERR(file);
/* only retry if RESOLVE_CACHED wasn't already set by application */
- if (ret == -EAGAIN &&
- (!resolve_nonblock && (issue_flags & IO_URING_F_NONBLOCK)))
- return -EAGAIN;
+ if (ret == -EAGAIN && !resolve_nonblock &&
+ (issue_flags & IO_URING_F_NONBLOCK)) {
+ ret = putname_to_delayed(&open->filename,
+ no_free_ptr(name));
+ if (likely(!ret))
+ return -EAGAIN;
+ }
goto err;
}
@@ -167,7 +169,6 @@ int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
ret = io_fixed_fd_install(req, issue_flags, file,
open->file_slot);
err:
- putname(open->filename);
req->flags &= ~REQ_F_NEED_CLEANUP;
if (ret < 0)
req_set_fail(req);
@@ -184,8 +185,7 @@ void io_open_cleanup(struct io_kiocb *req)
{
struct io_open *open = io_kiocb_to_cmd(req, struct io_open);
- if (open->filename)
- putname(open->filename);
+ dismiss_delayed_filename(&open->filename);
}
int __io_close_fixed(struct io_ring_ctx *ctx, unsigned int issue_flags,
diff --git a/io_uring/statx.c b/io_uring/statx.c
index 5111e9befbfe..dc10b48bcde6 100644
--- a/io_uring/statx.c
+++ b/io_uring/statx.c
@@ -16,7 +16,7 @@ struct io_statx {
int dfd;
unsigned int mask;
unsigned int flags;
- struct filename *filename;
+ struct delayed_filename filename;
struct statx __user *buffer;
};
@@ -24,6 +24,7 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx);
const char __user *path;
+ int ret;
if (sqe->buf_index || sqe->splice_fd_in)
return -EINVAL;
@@ -36,14 +37,10 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
sx->buffer = u64_to_user_ptr(READ_ONCE(sqe->addr2));
sx->flags = READ_ONCE(sqe->statx_flags);
- sx->filename = getname_uflags(path, sx->flags);
-
- if (IS_ERR(sx->filename)) {
- int ret = PTR_ERR(sx->filename);
+ ret = delayed_getname_uflags(&sx->filename, path, sx->flags);
- sx->filename = NULL;
+ if (unlikely(ret))
return ret;
- }
req->flags |= REQ_F_NEED_CLEANUP;
req->flags |= REQ_F_FORCE_ASYNC;
@@ -53,11 +50,12 @@ int io_statx_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
int io_statx(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx);
+ struct filename *name __free(putname) = complete_getname(&sx->filename);
int ret;
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = do_statx(sx->dfd, sx->filename, sx->flags, sx->mask, sx->buffer);
+ ret = do_statx(sx->dfd, name, sx->flags, sx->mask, sx->buffer);
io_req_set_res(req, ret, 0);
return IOU_COMPLETE;
}
@@ -66,6 +64,5 @@ void io_statx_cleanup(struct io_kiocb *req)
{
struct io_statx *sx = io_kiocb_to_cmd(req, struct io_statx);
- if (sx->filename)
- putname(sx->filename);
+ dismiss_delayed_filename(&sx->filename);
}
diff --git a/io_uring/xattr.c b/io_uring/xattr.c
index 322b94ff9e4b..0fb4e5303500 100644
--- a/io_uring/xattr.c
+++ b/io_uring/xattr.c
@@ -19,16 +19,14 @@
struct io_xattr {
struct file *file;
struct kernel_xattr_ctx ctx;
- struct filename *filename;
+ struct delayed_filename filename;
};
void io_xattr_cleanup(struct io_kiocb *req)
{
struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
- if (ix->filename)
- putname(ix->filename);
-
+ dismiss_delayed_filename(&ix->filename);
kfree(ix->ctx.kname);
kvfree(ix->ctx.kvalue);
}
@@ -48,7 +46,7 @@ static int __io_getxattr_prep(struct io_kiocb *req,
const char __user *name;
int ret;
- ix->filename = NULL;
+ INIT_DELAYED_FILENAME(&ix->filename);
ix->ctx.kvalue = NULL;
name = u64_to_user_ptr(READ_ONCE(sqe->addr));
ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
@@ -93,11 +91,7 @@ int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
- ix->filename = getname(path);
- if (IS_ERR(ix->filename))
- return PTR_ERR(ix->filename);
-
- return 0;
+ return delayed_getname(&ix->filename, path);
}
int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
@@ -119,8 +113,8 @@ int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = filename_getxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
- ix->filename = NULL;
+ ret = filename_getxattr(AT_FDCWD, complete_getname(&ix->filename),
+ LOOKUP_FOLLOW, &ix->ctx);
io_xattr_finish(req, ret);
return IOU_COMPLETE;
}
@@ -132,7 +126,7 @@ static int __io_setxattr_prep(struct io_kiocb *req,
const char __user *name;
int ret;
- ix->filename = NULL;
+ INIT_DELAYED_FILENAME(&ix->filename);
name = u64_to_user_ptr(READ_ONCE(sqe->addr));
ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
ix->ctx.kvalue = NULL;
@@ -169,11 +163,7 @@ int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
- ix->filename = getname(path);
- if (IS_ERR(ix->filename))
- return PTR_ERR(ix->filename);
-
- return 0;
+ return delayed_getname(&ix->filename, path);
}
int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
@@ -200,8 +190,8 @@ int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
- ret = filename_setxattr(AT_FDCWD, ix->filename, LOOKUP_FOLLOW, &ix->ctx);
- ix->filename = NULL;
+ ret = filename_setxattr(AT_FDCWD, complete_getname(&ix->filename),
+ LOOKUP_FOLLOW, &ix->ctx);
io_xattr_finish(req, ret);
return IOU_COMPLETE;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 17/18] fs: touch up predicts in putname()
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (15 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 16/18] allow incomplete imports of filenames Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-11-29 17:34 ` Mateusz Guzik
2025-11-29 17:01 ` [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic Al Viro
2025-12-10 1:31 ` [RFC PATCH v2 00/18] io_uring, struct filename and audit Jens Axboe
18 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
From: Mateusz Guzik <mjguzik@gmail.com>
1. we already expect the refcount is 1.
2. path creation predicts name == iname
I verified this straightens out the asm, no functional changes.
Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
Link: https://patch.msgid.link/20251029134952.658450-1-mjguzik@gmail.com
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/namei.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/namei.c b/fs/namei.c
index 37b13339f046..8530d75fb270 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -285,7 +285,7 @@ void putname(struct filename *name)
return;
refcnt = atomic_read(&name->refcnt);
- if (refcnt != 1) {
+ if (unlikely(refcnt != 1)) {
if (WARN_ON_ONCE(!refcnt))
return;
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (16 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 17/18] fs: touch up predicts in putname() Al Viro
@ 2025-11-29 17:01 ` Al Viro
2025-12-16 2:18 ` Paul Moore
2025-12-10 1:31 ` [RFC PATCH v2 00/18] io_uring, struct filename and audit Jens Axboe
18 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2025-11-29 17:01 UTC (permalink / raw)
To: linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, axboe, audit, io-uring
... or visible outside of audit, really. Note that references
held in delayed_filename always have refcount 1, and from the
moment of complete_getname() or equivalent point in getname...()
there won't be any references to struct filename instance left
in places visible to other threads.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
fs/namei.c | 10 +++++-----
include/linux/fs.h | 8 +-------
kernel/auditsc.c | 6 ++++++
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 8530d75fb270..d6eac90084e1 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -146,7 +146,7 @@ static inline void free_filename(struct filename *p)
static inline void initname(struct filename *name)
{
name->aname = NULL;
- atomic_set(&name->refcnt, 1);
+ name->refcnt = 1;
}
static int getname_long(struct filename *name, const char __user *filename)
@@ -284,13 +284,13 @@ void putname(struct filename *name)
if (IS_ERR_OR_NULL(name))
return;
- refcnt = atomic_read(&name->refcnt);
+ refcnt = name->refcnt;
if (unlikely(refcnt != 1)) {
if (WARN_ON_ONCE(!refcnt))
return;
- if (!atomic_dec_and_test(&name->refcnt))
- return;
+ name->refcnt--;
+ return;
}
if (unlikely(name->name != name->iname))
@@ -323,7 +323,7 @@ int putname_to_delayed(struct delayed_filename *v, struct filename *__name)
struct filename *name __free(putname) = no_free_ptr(__name);
struct filename *copy;
- if (likely(atomic_read(&name->refcnt) == 1)) {
+ if (likely(name->refcnt == 1)) {
v->__incomplete_filename = no_free_ptr(name);
return 0;
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 52ee3bc1baa9..b21814b93dcd 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2837,7 +2837,7 @@ struct audit_names;
#define EMBEDDED_NAME_MAX 128
struct filename {
const char *name; /* pointer to actual string */
- atomic_t refcnt;
+ int refcnt;
struct audit_names *aname;
const char iname[EMBEDDED_NAME_MAX];
};
@@ -2947,12 +2947,6 @@ void dismiss_delayed_filename(struct delayed_filename *);
int putname_to_delayed(struct delayed_filename *, struct filename *);
struct filename *complete_getname(struct delayed_filename *);
-static inline struct filename *refname(struct filename *name)
-{
- atomic_inc(&name->refcnt);
- return name;
-}
-
extern int finish_open(struct file *file, struct dentry *dentry,
int (*open)(struct inode *, struct file *));
extern int finish_no_open(struct file *file, struct dentry *dentry);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index e59a094bb9f7..d71fc73455b2 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2169,6 +2169,12 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
return aname;
}
+static inline struct filename *refname(struct filename *name)
+{
+ name->refcnt++;
+ return name;
+}
+
/**
* __audit_getname - add a name to the list
* @name: name to add
--
2.47.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 15/18] struct filename: saner handling of long names
2025-11-29 17:01 ` [RFC PATCH v2 15/18] struct filename: saner handling of long names Al Viro
@ 2025-11-29 17:33 ` Mateusz Guzik
2025-11-30 4:06 ` Al Viro
0 siblings, 1 reply; 26+ messages in thread
From: Mateusz Guzik @ 2025-11-29 17:33 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, paul, axboe, audit,
io-uring
On Sat, Nov 29, 2025 at 6:01 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> void __init filename_init(void)
> {
> - names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
> - SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);
> + names_cachep = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0,
> + SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname),
> + EMBEDDED_NAME_MAX, NULL);
> }
>
[snip]
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 59c5c67985ab..0b01adcfa425 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2833,11 +2833,13 @@ extern struct kobject *fs_kobj;
>
> /* fs/open.c */
> struct audit_names;
> +
> +#define EMBEDDED_NAME_MAX 128
> struct filename {
> const char *name; /* pointer to actual string */
> atomic_t refcnt;
> struct audit_names *aname;
> - const char iname[];
> + const char iname[EMBEDDED_NAME_MAX];
> };
> static_assert(offsetof(struct filename, iname) % sizeof(long) == 0);
>
This makes sizeof struct filename 152 bytes. At the same time because
of the SLAB_HWCACHE_ALIGN flag, the obj is going to take 192 bytes.
I don't know what would be the nice way to handle this in Linux, but
as is this is just failing to take advantage of memory which is going
to get allocated anyway.
Perhaps the macro could be bumped to 168 and the size checked with a
static assert on 64 bit platforms? Or some magic based on reported
cache line size.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 17/18] fs: touch up predicts in putname()
2025-11-29 17:01 ` [RFC PATCH v2 17/18] fs: touch up predicts in putname() Al Viro
@ 2025-11-29 17:34 ` Mateusz Guzik
0 siblings, 0 replies; 26+ messages in thread
From: Mateusz Guzik @ 2025-11-29 17:34 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, paul, axboe, audit,
io-uring
this was already included by Christian, although with both predicts. I
don't know if the other one fell off by accident.
regardless, this patch needs to get dropped
On Sat, Nov 29, 2025 at 6:01 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> From: Mateusz Guzik <mjguzik@gmail.com>
>
> 1. we already expect the refcount is 1.
> 2. path creation predicts name == iname
>
> I verified this straightens out the asm, no functional changes.
>
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> Link: https://patch.msgid.link/20251029134952.658450-1-mjguzik@gmail.com
> Reviewed-by: Jan Kara <jack@suse.cz>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> fs/namei.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 37b13339f046..8530d75fb270 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -285,7 +285,7 @@ void putname(struct filename *name)
> return;
>
> refcnt = atomic_read(&name->refcnt);
> - if (refcnt != 1) {
> + if (unlikely(refcnt != 1)) {
> if (WARN_ON_ONCE(!refcnt))
> return;
>
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 15/18] struct filename: saner handling of long names
2025-11-29 17:33 ` Mateusz Guzik
@ 2025-11-30 4:06 ` Al Viro
2025-11-30 4:38 ` Mateusz Guzik
0 siblings, 1 reply; 26+ messages in thread
From: Al Viro @ 2025-11-30 4:06 UTC (permalink / raw)
To: Mateusz Guzik
Cc: linux-fsdevel, torvalds, brauner, jack, paul, axboe, audit,
io-uring
On Sat, Nov 29, 2025 at 06:33:22PM +0100, Mateusz Guzik wrote:
> This makes sizeof struct filename 152 bytes. At the same time because
> of the SLAB_HWCACHE_ALIGN flag, the obj is going to take 192 bytes.
>
> I don't know what would be the nice way to handle this in Linux, but
> as is this is just failing to take advantage of memory which is going
> to get allocated anyway.
>
> Perhaps the macro could be bumped to 168 and the size checked with a
> static assert on 64 bit platforms?
Could be done, even though I wonder how much would that really save.
> Or some magic based on reported
> cache line size.
No comments. At least, none suitable for polite company.
BTW, one thing that might make sense is storing the name length in there...
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 15/18] struct filename: saner handling of long names
2025-11-30 4:06 ` Al Viro
@ 2025-11-30 4:38 ` Mateusz Guzik
0 siblings, 0 replies; 26+ messages in thread
From: Mateusz Guzik @ 2025-11-30 4:38 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, paul, axboe, audit,
io-uring
On Sun, Nov 30, 2025 at 5:06 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Sat, Nov 29, 2025 at 06:33:22PM +0100, Mateusz Guzik wrote:
>
> > This makes sizeof struct filename 152 bytes. At the same time because
> > of the SLAB_HWCACHE_ALIGN flag, the obj is going to take 192 bytes.
> >
> > I don't know what would be the nice way to handle this in Linux, but
> > as is this is just failing to take advantage of memory which is going
> > to get allocated anyway.
> >
> > Perhaps the macro could be bumped to 168 and the size checked with a
> > static assert on 64 bit platforms?
>
> Could be done, even though I wonder how much would that really save.
>
By any chance are you angling for that idea to keep struct filename on
the stack and that's why the size is 128?
With struct nameidata already on the stack and taking 240 bytes,
adding the 152 bytes would result in 392 bytes in total. I would argue
that's prohibitive, at the same time lowering the length to something
like 64 already makes gcc not fit with some of its lookups.
Anyway, I did look into something like this way back and some programs
really liked their *long* paths (way past 128).
Some stats I recently collected on FreeBSD while building packages:
dtrace -n 'vfs:namei:lookup:entry { @ =
lquantize(strlen(stringof(arg1)), 0, 384, 8); }'
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@@ 18105105
8 |@@@@@@@ 16360012
16 |@@@@@@@@@ 21313430
24 |@@@@@@ 15000426
32 |@@@ 6450202
40 |@@ 4209166
48 |@ 2533298
56 |@ 1611506
64 |@ 1203825
72 | 1068207
80 | 877158
88 | 592192
96 | 489958
104 | 709757
112 | 925775
120 | 1041627
128 |@ 1315123
136 | 664687
144 | 276673
152 | 150870
160 | 82661
168 | 40630
176 | 26693
184 | 15112
192 | 7276
200 | 5773
208 | 2462
216 | 1679
224 | 1150
232 | 1301
240 | 1652
248 | 659
256 | 464
264 | 0
> > Or some magic based on reported
> > cache line size.
>
> No comments. At least, none suitable for polite company.
>
> BTW, one thing that might make sense is storing the name length in there...
(gdb) ptype /o struct filename
/* offset | size */ type = struct filename {
/* 0 | 8 */ const char *name;
/* 8 | 8 */ const char *uptr;
/* 16 | 4 */ atomic_t refcnt;
/* XXX 4-byte hole */
/* 24 | 8 */ struct audit_names *aname;
/* 32 | 0 */ const char iname[];
/* total size (bytes): 32 */
}
If the length start getting stored it can presumably go into the hole.
Otherwise is there a reason to not rearrange this? The array could be
only aligned to 4 bytes, on archs which are fine with misaligned
access anyway. That's 4 extra bytes recovered.
All that said, now that I look at it, assuming struct filename will
keep being allocated from slub, why not make it 256 bytes? This gives
232 bytes for the name buffer (covering almost all of the looups I ran
into anyway), archs with 128 byte cacheline are sorted out and one can
trivially unconditionally static assert on the total size being 256
bytes, all while not having space which is never going to be used.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 00/18] io_uring, struct filename and audit
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
` (17 preceding siblings ...)
2025-11-29 17:01 ` [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic Al Viro
@ 2025-12-10 1:31 ` Jens Axboe
18 siblings, 0 replies; 26+ messages in thread
From: Jens Axboe @ 2025-12-10 1:31 UTC (permalink / raw)
To: Al Viro, linux-fsdevel
Cc: torvalds, brauner, jack, mjguzik, paul, audit, io-uring
On 11/29/25 10:01 AM, Al Viro wrote:
> Changes compared to v1:
> * putname_to_delayed(): new primitive, hopefully solving the
> io_openat2() breakage spotted by Jens
> * Linus' suggestion re saner allocation for struct filename
> implemented and carved up [##11--15]
>
> It's obviously doing to slip to the next cycle at this point - I'm not
> proposing to merge it in the coming window.
>
> Please, review. Branch in
> git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #work.filename-refcnt
> individual patches in followups.
Sorry slow to look at this - from the io_uring POV, this looks good to
me.
--
Jens Axboe
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 10/18] get rid of audit_reusename()
2025-11-29 17:01 ` [RFC PATCH v2 10/18] get rid of audit_reusename() Al Viro
@ 2025-12-16 2:14 ` Paul Moore
0 siblings, 0 replies; 26+ messages in thread
From: Paul Moore @ 2025-12-16 2:14 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, mjguzik, axboe, audit,
io-uring
On Sat, Nov 29, 2025 at 12:01 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Originally we tried to avoid multiple insertions into audit names array
> during retry loop by a cute hack - memorize the userland pointer and
> if there already is a match, just grab an extra reference to it.
>
> Cute as it had been, it had problems - two identical pointers had
> audit aux entries merged, two identical strings did not. Having
> different behaviour for syscalls that differ only by addresses of
> otherwise identical string arguments is obviously wrong - if nothing
> else, compiler can decide to merge identical string literals.
>
> Besides, this hack does nothing for non-audited processes - they get
> a fresh copy for retry. It's not time-critical, but having behaviour
> subtly differ that way is bogus.
>
> These days we have very few places that import filename more than once
> (9 functions total) and it's easy to massage them so we get rid of all
> re-imports. With that done, we don't need audit_reusename() anymore.
> There's no need to memorize userland pointer either.
>
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
> fs/namei.c | 11 +++--------
> include/linux/audit.h | 11 -----------
> include/linux/fs.h | 1 -
> kernel/auditsc.c | 23 -----------------------
> 4 files changed, 3 insertions(+), 43 deletions(-)
Acked-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic
2025-11-29 17:01 ` [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic Al Viro
@ 2025-12-16 2:18 ` Paul Moore
0 siblings, 0 replies; 26+ messages in thread
From: Paul Moore @ 2025-12-16 2:18 UTC (permalink / raw)
To: Al Viro
Cc: linux-fsdevel, torvalds, brauner, jack, mjguzik, axboe, audit,
io-uring
On Sat, Nov 29, 2025 at 12:01 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> ... or visible outside of audit, really. Note that references
> held in delayed_filename always have refcount 1, and from the
> moment of complete_getname() or equivalent point in getname...()
> there won't be any references to struct filename instance left
> in places visible to other threads.
>
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
> fs/namei.c | 10 +++++-----
> include/linux/fs.h | 8 +-------
> kernel/auditsc.c | 6 ++++++
> 3 files changed, 12 insertions(+), 12 deletions(-)
Acked-by: Paul Moore <paul@paul-moore.com>
--
paul-moore.com
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2025-12-16 2:18 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-29 17:01 [RFC PATCH v2 00/18] io_uring, struct filename and audit Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 01/18] do_faccessat(): import pathname only once Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 02/18] do_fchmodat(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 03/18] do_fchownat(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 04/18] do_utimes_path(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 05/18] chdir(2): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 06/18] chroot(2): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 07/18] user_statfs(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 08/18] do_sys_truncate(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 09/18] do_readlinkat(): " Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 10/18] get rid of audit_reusename() Al Viro
2025-12-16 2:14 ` Paul Moore
2025-11-29 17:01 ` [RFC PATCH v2 11/18] ntfs: ->d_compare() must not block Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 12/18] getname_flags() massage, part 1 Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 13/18] getname_flags() massage, part 2 Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 14/18] struct filename: use names_cachep only for getname() and friends Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 15/18] struct filename: saner handling of long names Al Viro
2025-11-29 17:33 ` Mateusz Guzik
2025-11-30 4:06 ` Al Viro
2025-11-30 4:38 ` Mateusz Guzik
2025-11-29 17:01 ` [RFC PATCH v2 16/18] allow incomplete imports of filenames Al Viro
2025-11-29 17:01 ` [RFC PATCH v2 17/18] fs: touch up predicts in putname() Al Viro
2025-11-29 17:34 ` Mateusz Guzik
2025-11-29 17:01 ` [RFC PATCH v2 18/18] struct filename ->refcnt doesn't need to be atomic Al Viro
2025-12-16 2:18 ` Paul Moore
2025-12-10 1:31 ` [RFC PATCH v2 00/18] io_uring, struct filename and audit Jens Axboe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox