diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 16:58:28 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 16:58:28 -0800 |
| commit | 26c9342bb761e463774a64fb6210b4f95f5bc035 (patch) | |
| tree | 486d504cf5dcfe041eba4d9d5eb468a5a7b81566 | |
| parent | 8a5203c630c67d578975ff237413f5e0b5000af8 (diff) | |
| parent | 0787a93baa1aab9fd0cb8500105d11d3d3a58f7a (diff) | |
Merge tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs 'struct filename' updates from Al Viro:
"[Mostly] sanitize struct filename handling"
* tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (68 commits)
sysfs(2): fs_index() argument is _not_ a pathname
alpha: switch osf_mount() to strndup_user()
ksmbd: use CLASS(filename_kernel)
mqueue: switch to CLASS(filename)
user_statfs(): switch to CLASS(filename)
statx: switch to CLASS(filename_maybe_null)
quotactl_block(): switch to CLASS(filename)
chroot(2): switch to CLASS(filename)
move_mount(2): switch to CLASS(filename_maybe_null)
namei.c: switch user pathname imports to CLASS(filename{,_flags})
namei.c: convert getname_kernel() callers to CLASS(filename_kernel)
do_f{chmod,chown,access}at(): use CLASS(filename_uflags)
do_readlinkat(): switch to CLASS(filename_flags)
do_sys_truncate(): switch to CLASS(filename)
do_utimes_path(): switch to CLASS(filename_uflags)
chdir(2): unspaghettify a bit...
do_fchownat(): unspaghettify a bit...
fspick(2): use CLASS(filename_flags)
name_to_handle_at(): use CLASS(filename_uflags)
vfs_open_tree(): use CLASS(filename_uflags)
...
37 files changed, 563 insertions, 827 deletions
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index ed3ac56e3c76..79e2c3008289 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1340,3 +1340,14 @@ The ->setlease() file_operation must now be explicitly set in order to provide support for leases. When set to NULL, the kernel will now return -EINVAL to attempts to set a lease. Filesystems that wish to use the kernel-internal lease implementation should set it to generic_setlease(). + +--- + +**mandatory** + +fs/namei.c primitives that consume filesystem references (do_renameat2(), +do_linkat(), do_symlinkat(), do_mkdirat(), do_mknodat(), do_unlinkat() +and do_rmdir()) are gone; they are replaced with non-consuming analogues +(filename_renameat2(), etc.) +Callers are adjusted - responsibility for dropping the filenames belongs +to them now. diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index a08e8edef1a4..7b6543d2cca3 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -454,42 +454,30 @@ static int osf_ufs_mount(const char __user *dirname, struct ufs_args __user *args, int flags) { - int retval; - struct cdfs_args tmp; - struct filename *devname; + struct ufs_args tmp; + char *devname __free(kfree) = NULL; - retval = -EFAULT; if (copy_from_user(&tmp, args, sizeof(tmp))) - goto out; - devname = getname(tmp.devname); - retval = PTR_ERR(devname); + return -EFAULT; + devname = strndup_user(tmp.devname, PATH_MAX); if (IS_ERR(devname)) - goto out; - retval = do_mount(devname->name, dirname, "ext2", flags, NULL); - putname(devname); - out: - return retval; + return PTR_ERR(devname); + return do_mount(devname, dirname, "ext2", flags, NULL); } static int osf_cdfs_mount(const char __user *dirname, struct cdfs_args __user *args, int flags) { - int retval; struct cdfs_args tmp; - struct filename *devname; + char *devname __free(kfree) = NULL; - retval = -EFAULT; if (copy_from_user(&tmp, args, sizeof(tmp))) - goto out; - devname = getname(tmp.devname); - retval = PTR_ERR(devname); + return -EFAULT; + devname = strndup_user(tmp.devname, PATH_MAX); if (IS_ERR(devname)) - goto out; - retval = do_mount(devname->name, dirname, "iso9660", flags, NULL); - putname(devname); - out: - return retval; + return PTR_ERR(devname); + return do_mount(devname, dirname, "iso9660", flags, NULL); } static int diff --git a/fs/coredump.c b/fs/coredump.c index 8feb9c1cf83d..d9597610a6ca 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -895,11 +895,12 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm, * privs and don't want to unlink another user's coredump. */ if (!coredump_force_suid_safe(cprm)) { + CLASS(filename_kernel, name)(cn->corename); /* * If it doesn't exist, that's fine. If there's some * other problem, we'll catch it at the filp_open(). */ - do_unlinkat(AT_FDCWD, getname_kernel(cn->corename)); + filename_unlinkat(AT_FDCWD, name); } /* diff --git a/fs/dcache.c b/fs/dcache.c index 7088df2d042c..2758fe7322d4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -3297,10 +3297,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; @@ -3314,9 +3310,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/exec.c b/fs/exec.c index d0606e53376f..2e3a6593c6fd 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -777,10 +777,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) return ERR_PTR(-EINVAL); if (flags & AT_SYMLINK_NOFOLLOW) open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - open_exec_flags.lookup_flags |= LOOKUP_EMPTY; - file = do_filp_open(fd, name, &open_exec_flags); + file = do_file_open(fd, name, &open_exec_flags); if (IS_ERR(file)) return file; @@ -815,14 +813,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) */ struct file *open_exec(const char *name) { - struct filename *filename = getname_kernel(name); - struct file *f = ERR_CAST(filename); - - if (!IS_ERR(filename)) { - f = do_open_execat(AT_FDCWD, filename, 0); - putname(filename); - } - return f; + CLASS(filename_kernel, filename)(name); + return do_open_execat(AT_FDCWD, filename, 0); } EXPORT_SYMBOL(open_exec); @@ -1471,6 +1463,9 @@ out_free: return ERR_PTR(retval); } +DEFINE_CLASS(bprm, struct linux_binprm *, if (!IS_ERR(_T)) free_bprm(_T), + alloc_bprm(fd, name, flags), int fd, struct filename *name, int flags) + int bprm_change_interp(const char *interp, struct linux_binprm *bprm) { /* If a binfmt changed the interp, free it first. */ @@ -1785,12 +1780,8 @@ static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr envp, int flags) { - struct linux_binprm *bprm; int retval; - if (IS_ERR(filename)) - return PTR_ERR(filename); - /* * We move the actual failure in case of RLIMIT_NPROC excess from * set*uid() to execve() because too many poorly written programs @@ -1798,47 +1789,43 @@ static int do_execveat_common(int fd, struct filename *filename, * whether NPROC limit is still exceeded. */ if ((current->flags & PF_NPROC_EXCEEDED) && - is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) { - retval = -EAGAIN; - goto out_ret; - } + is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) + return -EAGAIN; /* We're below the limit (still or again), so we don't want to make * further execve() calls fail. */ current->flags &= ~PF_NPROC_EXCEEDED; - bprm = alloc_bprm(fd, filename, flags); - if (IS_ERR(bprm)) { - retval = PTR_ERR(bprm); - goto out_ret; - } + CLASS(bprm, bprm)(fd, filename, flags); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); retval = count(argv, MAX_ARG_STRINGS); if (retval < 0) - goto out_free; + return retval; bprm->argc = retval; retval = count(envp, MAX_ARG_STRINGS); if (retval < 0) - goto out_free; + return retval; bprm->envc = retval; retval = bprm_stack_limits(bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_string_kernel(bprm->filename, bprm); if (retval < 0) - goto out_free; + return retval; bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) - goto out_free; + return retval; /* * When argv is empty, add an empty string ("") as argv[0] to @@ -1849,133 +1836,61 @@ static int do_execveat_common(int fd, struct filename *filename, if (bprm->argc == 0) { retval = copy_string_kernel("", bprm); if (retval < 0) - goto out_free; + return retval; bprm->argc = 1; pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n", current->comm, bprm->filename); } - retval = bprm_execve(bprm); -out_free: - free_bprm(bprm); - -out_ret: - putname(filename); - return retval; + return bprm_execve(bprm); } int kernel_execve(const char *kernel_filename, const char *const *argv, const char *const *envp) { - struct filename *filename; - struct linux_binprm *bprm; - int fd = AT_FDCWD; int retval; /* It is non-sense for kernel threads to call execve */ if (WARN_ON_ONCE(current->flags & PF_KTHREAD)) return -EINVAL; - filename = getname_kernel(kernel_filename); - if (IS_ERR(filename)) - return PTR_ERR(filename); - - bprm = alloc_bprm(fd, filename, 0); - if (IS_ERR(bprm)) { - retval = PTR_ERR(bprm); - goto out_ret; - } + CLASS(filename_kernel, filename)(kernel_filename); + CLASS(bprm, bprm)(AT_FDCWD, filename, 0); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); retval = count_strings_kernel(argv); if (WARN_ON_ONCE(retval == 0)) - retval = -EINVAL; + return -EINVAL; if (retval < 0) - goto out_free; + return retval; bprm->argc = retval; retval = count_strings_kernel(envp); if (retval < 0) - goto out_free; + return retval; bprm->envc = retval; retval = bprm_stack_limits(bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_string_kernel(bprm->filename, bprm); if (retval < 0) - goto out_free; + return retval; bprm->exec = bprm->p; retval = copy_strings_kernel(bprm->envc, envp, bprm); if (retval < 0) - goto out_free; + return retval; retval = copy_strings_kernel(bprm->argc, argv, bprm); if (retval < 0) - goto out_free; - - retval = bprm_execve(bprm); -out_free: - free_bprm(bprm); -out_ret: - putname(filename); - return retval; -} - -static int do_execve(struct filename *filename, - const char __user *const __user *__argv, - const char __user *const __user *__envp) -{ - struct user_arg_ptr argv = { .ptr.native = __argv }; - struct user_arg_ptr envp = { .ptr.native = __envp }; - return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); -} - -static int do_execveat(int fd, struct filename *filename, - const char __user *const __user *__argv, - const char __user *const __user *__envp, - int flags) -{ - struct user_arg_ptr argv = { .ptr.native = __argv }; - struct user_arg_ptr envp = { .ptr.native = __envp }; - - return do_execveat_common(fd, filename, argv, envp, flags); -} - -#ifdef CONFIG_COMPAT -static int compat_do_execve(struct filename *filename, - const compat_uptr_t __user *__argv, - const compat_uptr_t __user *__envp) -{ - struct user_arg_ptr argv = { - .is_compat = true, - .ptr.compat = __argv, - }; - struct user_arg_ptr envp = { - .is_compat = true, - .ptr.compat = __envp, - }; - return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); -} + return retval; -static int compat_do_execveat(int fd, struct filename *filename, - const compat_uptr_t __user *__argv, - const compat_uptr_t __user *__envp, - int flags) -{ - struct user_arg_ptr argv = { - .is_compat = true, - .ptr.compat = __argv, - }; - struct user_arg_ptr envp = { - .is_compat = true, - .ptr.compat = __envp, - }; - return do_execveat_common(fd, filename, argv, envp, flags); + return bprm_execve(bprm); } -#endif void set_binfmt(struct linux_binfmt *new) { @@ -2001,12 +1916,19 @@ void set_dumpable(struct mm_struct *mm, int value) __mm_flags_set_mask_dumpable(mm, value); } +static inline struct user_arg_ptr native_arg(const char __user *const __user *p) +{ + return (struct user_arg_ptr){.ptr.native = p}; +} + SYSCALL_DEFINE3(execve, const char __user *, filename, const char __user *const __user *, argv, const char __user *const __user *, envp) { - return do_execve(getname(filename), argv, envp); + CLASS(filename, name)(filename); + return do_execveat_common(AT_FDCWD, name, + native_arg(argv), native_arg(envp), 0); } SYSCALL_DEFINE5(execveat, @@ -2015,17 +1937,25 @@ SYSCALL_DEFINE5(execveat, const char __user *const __user *, envp, int, flags) { - return do_execveat(fd, - getname_uflags(filename, flags), - argv, envp, flags); + CLASS(filename_uflags, name)(filename, flags); + return do_execveat_common(fd, name, + native_arg(argv), native_arg(envp), flags); } #ifdef CONFIG_COMPAT + +static inline struct user_arg_ptr compat_arg(const compat_uptr_t __user *p) +{ + return (struct user_arg_ptr){.is_compat = true, .ptr.compat = p}; +} + COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename, const compat_uptr_t __user *, argv, const compat_uptr_t __user *, envp) { - return compat_do_execve(getname(filename), argv, envp); + CLASS(filename, name)(filename); + return do_execveat_common(AT_FDCWD, name, + compat_arg(argv), compat_arg(envp), 0); } COMPAT_SYSCALL_DEFINE5(execveat, int, fd, @@ -2034,9 +1964,9 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd, const compat_uptr_t __user *, envp, int, flags) { - return compat_do_execveat(fd, - getname_uflags(filename, flags), - argv, envp, flags); + CLASS(filename_uflags, name)(filename, flags); + return do_execveat_common(fd, name, + compat_arg(argv), compat_arg(envp), flags); } #endif diff --git a/fs/fhandle.c b/fs/fhandle.c index 3de1547ec9d4..e15bcf4b0b23 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -157,9 +157,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, fh_flags |= EXPORT_FH_CONNECTABLE; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; - if (flag & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - err = user_path_at(dfd, name, lookup_flags, &path); + CLASS(filename_uflags, filename)(name, flag); + err = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (!err) { err = do_sys_name_to_handle(&path, handle, mnt_id, flag & AT_HANDLE_MNT_ID_UNIQUE, diff --git a/fs/file_attr.c b/fs/file_attr.c index 13cdb31a3e94..42721427245a 100644 --- a/fs/file_attr.c +++ b/fs/file_attr.c @@ -374,7 +374,6 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename, unsigned int, at_flags) { struct path filepath __free(path_put) = {}; - struct filename *name __free(putname) = NULL; unsigned int lookup_flags = 0; struct file_attr fattr; struct file_kattr fa; @@ -395,10 +394,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename, if (usize < FILE_ATTR_SIZE_VER0) return -EINVAL; - name = getname_maybe_null(filename, at_flags); - if (IS_ERR(name)) - return PTR_ERR(name); - + CLASS(filename_maybe_null, name)(filename, at_flags); if (!name && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -431,7 +427,6 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename, unsigned int, at_flags) { struct path filepath __free(path_put) = {}; - struct filename *name __free(putname) = NULL; unsigned int lookup_flags = 0; struct file_attr fattr; struct file_kattr fa; @@ -461,10 +456,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename, if (error) return error; - name = getname_maybe_null(filename, at_flags); - if (IS_ERR(name)) - return PTR_ERR(name); - + CLASS(filename_maybe_null, name)(filename, at_flags); if (!name && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) diff --git a/fs/filesystems.c b/fs/filesystems.c index 95e5256821a5..0c7d2b7ac26c 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -132,24 +132,21 @@ EXPORT_SYMBOL(unregister_filesystem); static int fs_index(const char __user * __name) { struct file_system_type * tmp; - struct filename *name; + char *name __free(kfree) = strndup_user(__name, PATH_MAX); int err, index; - name = getname(__name); - err = PTR_ERR(name); if (IS_ERR(name)) - return err; + return PTR_ERR(name); err = -EINVAL; read_lock(&file_systems_lock); for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) { - if (strcmp(tmp->name, name->name) == 0) { + if (strcmp(tmp->name, name) == 0) { err = index; break; } } read_unlock(&file_systems_lock); - putname(name); return err; } diff --git a/fs/fsopen.c b/fs/fsopen.c index 622ee3926cd5..614922623675 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -181,9 +181,9 @@ SYSCALL_DEFINE3(fspick, int, dfd, const char __user *, path, unsigned int, flags lookup_flags &= ~LOOKUP_FOLLOW; if (flags & FSPICK_NO_AUTOMOUNT) lookup_flags &= ~LOOKUP_AUTOMOUNT; - if (flags & FSPICK_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; - ret = user_path_at(dfd, path, lookup_flags, &target); + CLASS(filename_flags, filename)(path, + (flags & FSPICK_EMPTY_PATH) ? LOOKUP_EMPTY : 0); + ret = filename_lookup(dfd, filename, lookup_flags, &target, NULL); if (ret < 0) goto err; diff --git a/fs/init.c b/fs/init.c index e33b2690d851..33e312d74f58 100644 --- a/fs/init.c +++ b/fs/init.c @@ -157,110 +157,40 @@ int __init init_stat(const char *filename, struct kstat *stat, int flags) int __init init_mknod(const char *filename, umode_t mode, unsigned int dev) { - struct dentry *dentry; - struct path path; - int error; - - if (S_ISFIFO(mode) || S_ISSOCK(mode)) - dev = 0; - else if (!(S_ISBLK(mode) || S_ISCHR(mode))) - return -EINVAL; - - dentry = start_creating_path(AT_FDCWD, filename, &path, 0); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - - mode = mode_strip_umask(d_inode(path.dentry), mode); - error = security_path_mknod(&path, dentry, mode, dev); - if (!error) - error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, mode, new_decode_dev(dev), NULL); - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, name)(filename); + return filename_mknodat(AT_FDCWD, name, mode, dev); } int __init init_link(const char *oldname, const char *newname) { - struct dentry *new_dentry; - struct path old_path, new_path; - struct mnt_idmap *idmap; - int error; - - error = kern_path(oldname, 0, &old_path); - if (error) - return error; - - new_dentry = start_creating_path(AT_FDCWD, newname, &new_path, 0); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out; - - error = -EXDEV; - if (old_path.mnt != new_path.mnt) - goto out_dput; - idmap = mnt_idmap(new_path.mnt); - error = may_linkat(idmap, &old_path); - if (unlikely(error)) - goto out_dput; - error = security_path_link(old_path.dentry, &new_path, new_dentry); - if (error) - goto out_dput; - error = vfs_link(old_path.dentry, idmap, new_path.dentry->d_inode, - new_dentry, NULL); -out_dput: - end_creating_path(&new_path, new_dentry); -out: - path_put(&old_path); - return error; + CLASS(filename_kernel, old)(oldname); + CLASS(filename_kernel, new)(newname); + return filename_linkat(AT_FDCWD, old, AT_FDCWD, new, 0); } int __init init_symlink(const char *oldname, const char *newname) { - struct dentry *dentry; - struct path path; - int error; - - dentry = start_creating_path(AT_FDCWD, newname, &path, 0); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - error = security_path_symlink(&path, dentry, oldname); - if (!error) - error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, oldname, NULL); - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, old)(oldname); + CLASS(filename_kernel, new)(newname); + return filename_symlinkat(old, AT_FDCWD, new); } int __init init_unlink(const char *pathname) { - return do_unlinkat(AT_FDCWD, getname_kernel(pathname)); + CLASS(filename_kernel, name)(pathname); + return filename_unlinkat(AT_FDCWD, name); } int __init init_mkdir(const char *pathname, umode_t mode) { - struct dentry *dentry; - struct path path; - int error; - - dentry = start_creating_path(AT_FDCWD, pathname, &path, - LOOKUP_DIRECTORY); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - mode = mode_strip_umask(d_inode(path.dentry), mode); - error = security_path_mkdir(&path, dentry, mode); - if (!error) { - dentry = vfs_mkdir(mnt_idmap(path.mnt), path.dentry->d_inode, - dentry, mode, NULL); - if (IS_ERR(dentry)) - error = PTR_ERR(dentry); - } - end_creating_path(&path, dentry); - return error; + CLASS(filename_kernel, name)(pathname); + return filename_mkdirat(AT_FDCWD, name, mode); } int __init init_rmdir(const char *pathname) { - return do_rmdir(AT_FDCWD, getname_kernel(pathname)); + CLASS(filename_kernel, name)(pathname); + return filename_rmdir(AT_FDCWD, name); } int __init init_utimes(char *filename, struct timespec64 *ts) diff --git a/fs/internal.h b/fs/internal.h index 5ec0dd514185..cbc384a1aa09 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -53,14 +53,15 @@ extern int finish_clean_context(struct fs_context *fc); */ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, const struct path *root); -int do_rmdir(int dfd, struct filename *name); -int do_unlinkat(int dfd, struct filename *name); +int filename_rmdir(int dfd, struct filename *name); +int filename_unlinkat(int dfd, struct filename *name); int may_linkat(struct mnt_idmap *idmap, const struct path *link); -int do_renameat2(int olddfd, struct filename *oldname, int newdfd, +int filename_renameat2(int olddfd, struct filename *oldname, int newdfd, struct filename *newname, unsigned int flags); -int do_mkdirat(int dfd, struct filename *name, umode_t mode); -int do_symlinkat(struct filename *from, int newdfd, struct filename *to); -int do_linkat(int olddfd, struct filename *old, int newdfd, +int filename_mkdirat(int dfd, struct filename *name, umode_t mode); +int filename_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev); +int filename_symlinkat(struct filename *from, int newdfd, struct filename *to); +int filename_linkat(int olddfd, struct filename *old, int newdfd, struct filename *new, int flags); int vfs_tmpfile(struct mnt_idmap *idmap, const struct path *parentpath, @@ -70,6 +71,8 @@ struct dentry *start_dirop(struct dentry *parent, struct qstr *name, unsigned int lookup_flags); int lookup_noperm_common(struct qstr *qname, struct dentry *base); +void __init filename_init(void); + /* * namespace.c */ @@ -187,7 +190,7 @@ struct open_flags { int intent; int lookup_flags; }; -extern struct file *do_filp_open(int dfd, struct filename *pathname, +extern struct file *do_file_open(int dfd, struct filename *pathname, const struct open_flags *op); extern struct file *do_file_open_root(const struct path *, const char *, const struct open_flags *); diff --git a/fs/namei.c b/fs/namei.c index b28ecb699f32..8e7792de0000 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -41,6 +41,8 @@ #include <linux/init_task.h> #include <linux/uaccess.h> +#include <asm/runtime-const.h> + #include "internal.h" #include "mount.h" @@ -123,27 +125,61 @@ * 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_cache __ro_after_init; +#define names_cache runtime_const_ptr(__names_cache) + +void __init filename_init(void) +{ + __names_cache = kmem_cache_create_usercopy("names_cache", sizeof(struct filename), 0, + SLAB_HWCACHE_ALIGN|SLAB_PANIC, offsetof(struct filename, iname), + EMBEDDED_NAME_MAX, NULL); + runtime_const_init(ptr, __names_cache); +} + +static inline struct filename *alloc_filename(void) +{ + return kmem_cache_alloc(names_cache, GFP_KERNEL); +} -static inline void initname(struct filename *name, const char __user *uptr) +static inline void free_filename(struct filename *p) +{ + kmem_cache_free(names_cache, p); +} + +static inline void initname(struct filename *name) { - name->uptr = uptr; name->aname = NULL; - atomic_set(&name->refcnt, 1); + name->refcnt = 1; } -struct filename * -getname_flags(const char __user *filename, int flags) +static int getname_long(struct filename *name, const char __user *filename) +{ + int len; + char *p __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + + 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 len; + if (unlikely(len == PATH_MAX - EMBEDDED_NAME_MAX)) + return -ENAMETOOLONG; + name->name = no_free_ptr(p); + return 0; +} + +static struct filename * +do_getname(const char __user *filename, int flags, bool incomplete) { struct filename *result; char *kname; int len; - result = audit_reusename(filename); - if (result) - return result; - - result = __getname(); + result = alloc_filename(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); @@ -159,16 +195,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)) { - __putname(result); - return ERR_PTR(len); - } - /* The empty path is special. */ - if (!(flags & LOOKUP_EMPTY)) { - __putname(result); - return ERR_PTR(-ENOENT); - } + if (!len && !(flags & LOOKUP_EMPTY)) + len = -ENOENT; } /* @@ -177,44 +206,25 @@ 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)) { - const size_t size = offsetof(struct filename, iname[1]); - kname = (char *)result; - - /* - * 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); - return ERR_PTR(-ENOMEM); - } - result->name = kname; - len = strncpy_from_user(kname, filename, PATH_MAX); - if (unlikely(len < 0)) { - __putname(kname); - kfree(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); - return ERR_PTR(-ENAMETOOLONG); - } + if (unlikely(len == EMBEDDED_NAME_MAX)) + len = getname_long(result, filename); + if (unlikely(len < 0)) { + free_filename(result); + return ERR_PTR(len); } - initname(result, filename); - audit_getname(result); + + initname(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; @@ -224,7 +234,6 @@ struct filename *getname_uflags(const char __user *filename, int uflags) struct filename *__getname_maybe_null(const char __user *pathname) { - struct filename *name; char c; /* try to save on allocations; loss on um, though */ @@ -233,45 +242,47 @@ struct filename *__getname_maybe_null(const char __user *pathname) if (!c) return NULL; - name = getname_flags(pathname, LOOKUP_EMPTY); - if (!IS_ERR(name) && !(name->name[0])) { - putname(name); - name = NULL; - } - return name; + CLASS(filename_flags, name)(pathname, LOOKUP_EMPTY); + /* empty pathname translates to NULL */ + if (!IS_ERR(name) && !(name->name[0])) + return NULL; + return no_free_ptr(name); } -struct filename *getname_kernel(const char * filename) +static struct filename *do_getname_kernel(const char *filename, bool incomplete) { struct filename *result; int len = strlen(filename) + 1; + char *p; - result = __getname(); + 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)) { - __putname(result); + 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 { - __putname(result); - return ERR_PTR(-ENAMETOOLONG); } - memcpy((char *)result->name, filename, len); - initname(result, NULL); - audit_getname(result); + result->name = p; + initname(result); + if (likely(!incomplete)) + audit_getname(result); return result; } + +struct filename *getname_kernel(const char *filename) +{ + return do_getname_kernel(filename, false); +} EXPORT_SYMBOL(getname_kernel); void putname(struct filename *name) @@ -281,23 +292,64 @@ 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)) { - __putname(name->name); - kfree(name); - } else - __putname(name); + if (unlikely(name->name != name->iname)) + kfree(name->name); + free_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) +{ + if (likely(name->refcnt == 1)) { + v->__incomplete_filename = name; + return 0; + } + name->refcnt--; + v->__incomplete_filename = do_getname_kernel(name->name, true); + return PTR_ERR_OR_ZERO(v->__incomplete_filename); +} + +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 @@ -2948,7 +3000,7 @@ drop: struct dentry *kern_path_parent(const char *name, struct path *path) { struct path parent_path __free(path_put) = {}; - struct filename *filename __free(putname) = getname_kernel(name); + CLASS(filename_kernel, filename)(name); struct dentry *d; struct qstr last; int type, error; @@ -2969,33 +3021,23 @@ struct dentry *kern_path_parent(const char *name, struct path *path) struct dentry *start_removing_path(const char *name, struct path *path) { - struct filename *filename = getname_kernel(name); - struct dentry *res = __start_removing_path(AT_FDCWD, filename, path); - - putname(filename); - return res; + CLASS(filename_kernel, filename)(name); + return __start_removing_path(AT_FDCWD, filename, path); } struct dentry *start_removing_user_path_at(int dfd, const char __user *name, struct path *path) { - struct filename *filename = getname(name); - struct dentry *res = __start_removing_path(dfd, filename, path); - - putname(filename); - return res; + CLASS(filename, filename)(name); + return __start_removing_path(dfd, filename, path); } EXPORT_SYMBOL(start_removing_user_path_at); int kern_path(const char *name, unsigned int flags, struct path *path) { - struct filename *filename = getname_kernel(name); - int ret = filename_lookup(AT_FDCWD, filename, flags, path, NULL); - - putname(filename); - return ret; - + CLASS(filename_kernel, filename)(name); + return filename_lookup(AT_FDCWD, filename, flags, path, NULL); } EXPORT_SYMBOL(kern_path); @@ -3029,15 +3071,11 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { - struct filename *filename; + CLASS(filename_kernel, filename)(name); struct path root = {.mnt = mnt, .dentry = dentry}; - int ret; - filename = getname_kernel(name); /* the first argument of filename_lookup() is ignored with root */ - ret = filename_lookup(AT_FDCWD, filename, flags, path, &root); - putname(filename); - return ret; + return filename_lookup(AT_FDCWD, filename, flags, path, &root); } EXPORT_SYMBOL(vfs_path_lookup); @@ -3570,11 +3608,8 @@ int path_pts(struct path *path) int user_path_at(int dfd, const char __user *name, unsigned flags, struct path *path) { - struct filename *filename = getname_flags(name, flags); - int ret = filename_lookup(dfd, filename, flags, path, NULL); - - putname(filename); - return ret; + CLASS(filename_flags, filename)(name, flags); + return filename_lookup(dfd, filename, flags, path, NULL); } EXPORT_SYMBOL(user_path_at); @@ -4811,13 +4846,15 @@ static struct file *path_openat(struct nameidata *nd, return ERR_PTR(error); } -struct file *do_filp_open(int dfd, struct filename *pathname, +struct file *do_file_open(int dfd, struct filename *pathname, const struct open_flags *op) { struct nameidata nd; int flags = op->lookup_flags; struct file *filp; + if (IS_ERR(pathname)) + return ERR_CAST(pathname); set_nameidata(&nd, dfd, pathname, NULL); filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) @@ -4833,13 +4870,12 @@ struct file *do_file_open_root(const struct path *root, { struct nameidata nd; struct file *file; - struct filename *filename; int flags = op->lookup_flags; if (d_is_symlink(root->dentry) && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); - filename = getname_kernel(name); + CLASS(filename_kernel, filename)(name); if (IS_ERR(filename)) return ERR_CAST(filename); @@ -4850,7 +4886,6 @@ struct file *do_file_open_root(const struct path *root, if (unlikely(file == ERR_PTR(-ESTALE))) file = path_openat(&nd, op, flags | LOOKUP_REVAL); restore_nameidata(); - putname(filename); return file; } @@ -4906,11 +4941,8 @@ out: struct dentry *start_creating_path(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname_kernel(pathname); - struct dentry *res = filename_create(dfd, filename, path, lookup_flags); - - putname(filename); - return res; + CLASS(filename_kernel, filename)(pathname); + return filename_create(dfd, filename, path, lookup_flags); } EXPORT_SYMBOL(start_creating_path); @@ -4937,11 +4969,8 @@ inline struct dentry *start_creating_user_path( int dfd, const char __user *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname(pathname); - struct dentry *res = filename_create(dfd, filename, path, lookup_flags); - - putname(filename); - return res; + CLASS(filename, filename)(pathname); + return filename_create(dfd, filename, path, lookup_flags); } EXPORT_SYMBOL(start_creating_user_path); @@ -5084,8 +5113,8 @@ static int may_mknod(umode_t mode) } } -static int do_mknodat(int dfd, struct filename *name, umode_t mode, - unsigned int dev) +int filename_mknodat(int dfd, struct filename *name, umode_t mode, + unsigned int dev) { struct delegated_inode di = { }; struct mnt_idmap *idmap; @@ -5096,12 +5125,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, error = may_mknod(mode); if (error) - goto out1; + return error; retry: dentry = filename_create(dfd, name, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out1; + return PTR_ERR(dentry); error = security_path_mknod(&path, dentry, mode_strip_umask(path.dentry->d_inode, mode), dev); @@ -5135,20 +5163,20 @@ out2: lookup_flags |= LOOKUP_REVAL; goto retry; } -out1: - putname(name); return error; } SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, unsigned int, dev) { - return do_mknodat(dfd, getname(filename), mode, dev); + CLASS(filename, name)(filename); + return filename_mknodat(dfd, name, mode, dev); } SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev) { - return do_mknodat(AT_FDCWD, getname(filename), mode, dev); + CLASS(filename, name)(filename); + return filename_mknodat(AT_FDCWD, name, mode, dev); } /** @@ -5219,7 +5247,7 @@ err: } EXPORT_SYMBOL(vfs_mkdir); -int do_mkdirat(int dfd, struct filename *name, umode_t mode) +int filename_mkdirat(int dfd, struct filename *name, umode_t mode) { struct dentry *dentry; struct path path; @@ -5229,9 +5257,8 @@ int do_mkdirat(int dfd, struct filename *name, umode_t mode) retry: dentry = filename_create(dfd, name, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putname; + return PTR_ERR(dentry); error = security_path_mkdir(&path, dentry, mode_strip_umask(path.dentry->d_inode, mode)); @@ -5251,19 +5278,19 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putname: - putname(name); return error; } SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(dfd, getname(pathname), mode); + CLASS(filename, name)(pathname); + return filename_mkdirat(dfd, name, mode); } SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(AT_FDCWD, getname(pathname), mode); + CLASS(filename, name)(pathname); + return filename_mkdirat(AT_FDCWD, name, mode); } /** @@ -5326,7 +5353,7 @@ out: } EXPORT_SYMBOL(vfs_rmdir); -int do_rmdir(int dfd, struct filename *name) +int filename_rmdir(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -5338,7 +5365,7 @@ int do_rmdir(int dfd, struct filename *name) retry: error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) - goto exit1; + return error; switch (type) { case LAST_DOTDOT: @@ -5380,14 +5407,13 @@ exit2: lookup_flags |= LOOKUP_REVAL; goto retry; } -exit1: - putname(name); return error; } SYSCALL_DEFINE1(rmdir, const char __user *, pathname) { - return do_rmdir(AT_FDCWD, getname(pathname)); + CLASS(filename, name)(pathname); + return filename_rmdir(AT_FDCWD, name); } /** @@ -5469,7 +5495,7 @@ EXPORT_SYMBOL(vfs_unlink); * writeout happening, and we don't want to prevent access to the directory * while waiting on the I/O. */ -int do_unlinkat(int dfd, struct filename *name) +int filename_unlinkat(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -5482,7 +5508,7 @@ int do_unlinkat(int dfd, struct filename *name) retry: error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) - goto exit_putname; + return error; error = -EISDIR; if (type != LAST_NORM) @@ -5529,8 +5555,6 @@ exit_path_put: lookup_flags |= LOOKUP_REVAL; goto retry; } -exit_putname: - putname(name); return error; } @@ -5539,14 +5563,16 @@ SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) if ((flag & ~AT_REMOVEDIR) != 0) return -EINVAL; + CLASS(filename, name)(pathname); if (flag & AT_REMOVEDIR) - return do_rmdir(dfd, getname(pathname)); - return do_unlinkat(dfd, getname(pathname)); + return filename_rmdir(dfd, name); + return filename_unlinkat(dfd, name); } SYSCALL_DEFINE1(unlink, const char __user *, pathname) { - return do_unlinkat(AT_FDCWD, getname(pathname)); + CLASS(filename, name)(pathname); + return filename_unlinkat(AT_FDCWD, name); } /** @@ -5593,7 +5619,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -int do_symlinkat(struct filename *from, int newdfd, struct filename *to) +int filename_symlinkat(struct filename *from, int newdfd, struct filename *to) { int error; struct dentry *dentry; @@ -5601,15 +5627,13 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to) unsigned int lookup_flags = 0; struct delegated_inode delegated_inode = { }; - if (IS_ERR(from)) { - error = PTR_ERR(from); - goto out_putnames; - } + if (IS_ERR(from)) + return PTR_ERR(from); + retry: dentry = filename_create(newdfd, to, &path, lookup_flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putnames; + return PTR_ERR(dentry); error = security_path_symlink(&path, dentry, from->name); if (!error) @@ -5625,21 +5649,22 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putnames: - putname(to); - putname(from); return error; } SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_symlinkat(getname(oldname), newdfd, getname(newname)); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_symlinkat(old, newdfd, new); } SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) { - return do_symlinkat(getname(oldname), AT_FDCWD, getname(newname)); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_symlinkat(old, AT_FDCWD, new); } /** @@ -5741,9 +5766,9 @@ EXPORT_SYMBOL(vfs_link); * We don't follow them on the oldname either to be compatible * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM - */ -int do_linkat(int olddfd, struct filename *old, int newdfd, - struct filename *new, int flags) +*/ +int filename_linkat(int olddfd, struct filename *old, + int newdfd, struct filename *new, int flags) { struct mnt_idmap *idmap; struct dentry *new_dentry; @@ -5752,10 +5777,8 @@ int do_linkat(int olddfd, struct filename *old, int newdfd, int how = 0; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) { - error = -EINVAL; - goto out_putnames; - } + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + return -EINVAL; /* * To use null names we require CAP_DAC_READ_SEARCH or * that the open-time creds of the dfd matches current. @@ -5770,7 +5793,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd, retry: error = filename_lookup(olddfd, old, how, &old_path, NULL); if (error) - goto out_putnames; + return error; new_dentry = filename_create(newdfd, new, &new_path, (how & LOOKUP_REVAL)); @@ -5806,23 +5829,22 @@ out_dput: } out_putpath: path_put(&old_path); -out_putnames: - putname(old); - putname(new); - return error; } SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, int, flags) { - return do_linkat(olddfd, getname_uflags(oldname, flags), - newdfd, getname(newname), flags); + CLASS(filename_uflags, old)(oldname, flags); + CLASS(filename, new)(newname); + return filename_linkat(olddfd, old, newdfd, new, flags); } SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) { - return do_linkat(AT_FDCWD, getname(oldname), AT_FDCWD, getname(newname), 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_linkat(AT_FDCWD, old, AT_FDCWD, new, 0); } /** @@ -6040,8 +6062,8 @@ out: } EXPORT_SYMBOL(vfs_rename); -int do_renameat2(int olddfd, struct filename *from, int newdfd, - struct filename *to, unsigned int flags) +int filename_renameat2(int olddfd, struct filename *from, + int newdfd, struct filename *to, unsigned int flags) { struct renamedata rd; struct path old_path, new_path; @@ -6050,20 +6072,20 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, struct delegated_inode delegated_inode = { }; unsigned int lookup_flags = 0; bool should_retry = false; - int error = -EINVAL; + int error; if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) - goto put_names; + return -EINVAL; if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) && (flags & RENAME_EXCHANGE)) - goto put_names; + return -EINVAL; retry: error = filename_parentat(olddfd, from, lookup_flags, &old_path, &old_last, &old_type); if (error) - goto put_names; + return error; error = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, &new_type); @@ -6140,30 +6162,30 @@ exit1: lookup_flags |= LOOKUP_REVAL; goto retry; } -put_names: - putname(from); - putname(to); return error; } SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { - return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname), - flags); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(olddfd, old, newdfd, new, flags); } SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname), - 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(olddfd, old, newdfd, new, 0); } SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname) { - return do_renameat2(AT_FDCWD, getname(oldname), AT_FDCWD, - getname(newname), 0); + CLASS(filename, old)(oldname); + CLASS(filename, new)(newname); + return filename_renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); } int readlink_copy(char __user *buffer, int buflen, const char *link, int linklen) diff --git a/fs/namespace.c b/fs/namespace.c index 0cc8c2757500..a67cbe42746d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3195,8 +3195,6 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned lookup_flags &= ~LOOKUP_AUTOMOUNT; if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; /* * If we create a new mount namespace with the cloned mount tree we @@ -3210,7 +3208,8 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned if ((flags & OPEN_TREE_CLONE) && !may_mount()) return ERR_PTR(-EPERM); - ret = user_path_at(dfd, filename, lookup_flags, &path); + CLASS(filename_uflags, name)(filename, flags); + ret = filename_lookup(dfd, name, lookup_flags, &path, NULL); if (unlikely(ret)) return ERR_PTR(ret); @@ -4528,8 +4527,6 @@ SYSCALL_DEFINE5(move_mount, { struct path to_path __free(path_put) = {}; struct path from_path __free(path_put) = {}; - struct filename *to_name __free(putname) = NULL; - struct filename *from_name __free(putname) = NULL; unsigned int lflags, uflags; enum mnt_tree_flags_t mflags = 0; int ret = 0; @@ -4551,10 +4548,7 @@ SYSCALL_DEFINE5(move_mount, if (flags & MOVE_MOUNT_T_EMPTY_PATH) uflags = AT_EMPTY_PATH; - to_name = getname_maybe_null(to_pathname, uflags); - if (IS_ERR(to_name)) - return PTR_ERR(to_name); - + CLASS(filename_maybe_null,to_name)(to_pathname, uflags); if (!to_name && to_dfd >= 0) { CLASS(fd_raw, f_to)(to_dfd); if (fd_empty(f_to)) @@ -4577,10 +4571,7 @@ SYSCALL_DEFINE5(move_mount, if (flags & MOVE_MOUNT_F_EMPTY_PATH) uflags = AT_EMPTY_PATH; - from_name = getname_maybe_null(from_pathname, uflags); - if (IS_ERR(from_name)) - return PTR_ERR(from_name); - + CLASS(filename_maybe_null,from_name)(from_pathname, uflags); if (!from_name && from_dfd >= 0) { CLASS(fd_raw, f_from)(from_dfd); if (fd_empty(f_from)) @@ -5116,8 +5107,6 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, lookup_flags &= ~LOOKUP_AUTOMOUNT; if (flags & AT_SYMLINK_NOFOLLOW) lookup_flags &= ~LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; kattr = (struct mount_kattr) { .lookup_flags = lookup_flags, @@ -5130,7 +5119,8 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, if (err <= 0) return err; - err = user_path_at(dfd, path, kattr.lookup_flags, &target); + CLASS(filename_uflags, name)(path, flags); + err = filename_lookup(dfd, name, kattr.lookup_flags, &target, NULL); if (!err) { err = do_mount_setattr(&target, &kattr); path_put(&target); diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c index b66438e34bbb..596f8c62f033 100644 --- a/fs/ntfs3/dir.c +++ b/fs/ntfs3/dir.c @@ -424,8 +424,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit_dots(file, ctx)) return 0; - /* Allocate PATH_MAX bytes. */ - name = __getname(); + name = kmalloc(PATH_MAX, GFP_KERNEL); if (!name) return -ENOMEM; @@ -503,7 +502,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) out: - __putname(name); + kfree(name); put_indx_node(node); if (err == 1) { diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 5f138f715835..bd67ba7b5015 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -2627,7 +2627,7 @@ int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) u32 uni_bytes; struct ntfs_inode *ni = sbi->volume.ni; /* Allocate PATH_MAX bytes. */ - struct cpu_str *uni = __getname(); + struct cpu_str *uni = kmalloc(PATH_MAX, GFP_KERNEL); if (!uni) return -ENOMEM; @@ -2671,6 +2671,6 @@ unlock_out: err = _ni_write_inode(&ni->vfs_inode, 0); out: - __putname(uni); + kfree(uni); return err; } diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 0a9ac5efeb67..edfb973e4e82 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -1281,7 +1281,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, fa |= FILE_ATTRIBUTE_READONLY; /* Allocate PATH_MAX bytes. */ - new_de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + new_de = kzalloc(PATH_MAX, GFP_KERNEL); if (!new_de) { err = -ENOMEM; goto out1; @@ -1702,7 +1702,7 @@ out3: ntfs_mark_rec_free(sbi, ino, false); out2: - __putname(new_de); + kfree(new_de); kfree(rp); out1: @@ -1723,7 +1723,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) struct NTFS_DE *de; /* Allocate PATH_MAX bytes. */ - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + de = kzalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -1737,7 +1737,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) err = ni_add_name(ntfs_i(d_inode(dentry->d_parent)), ni, de); out: - __putname(de); + kfree(de); return err; } @@ -1760,8 +1760,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) if (ntfs_is_meta_file(sbi, ni->mi.rno)) return -EINVAL; - /* Allocate PATH_MAX bytes. */ - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); + de = kzalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -1797,7 +1796,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) out: ni_unlock(ni); - __putname(de); + kfree(de); return err; } diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 3b24ca02de61..b2af8f695e60 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -68,7 +68,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, u32 flags) { struct ntfs_inode *ni = ntfs_i(dir); - struct cpu_str *uni = __getname(); + struct cpu_str *uni = kmalloc(PATH_MAX, GFP_KERNEL); struct inode *inode; int err; @@ -85,7 +85,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, inode = dir_search_u(dir, uni, NULL); ni_unlock(ni); } - __putname(uni); + kfree(uni); } /* @@ -303,8 +303,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, return err; } - /* Allocate PATH_MAX bytes. */ - de = __getname(); + de = kmalloc(PATH_MAX, GFP_KERNEL); if (!de) return -ENOMEM; @@ -349,7 +348,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ni_unlock(ni); ni_unlock(dir_ni); out: - __putname(de); + kfree(de); return err; } @@ -407,7 +406,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 +428,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 +467,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 +497,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; } diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 37a69a75ce68..b96e6ac90e9f 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -556,8 +556,7 @@ struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (unlikely(is_bad_ni(ni))) return ERR_PTR(-EINVAL); - /* Allocate PATH_MAX bytes. */ - buf = __getname(); + buf = kmalloc(PATH_MAX, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); @@ -588,7 +587,7 @@ struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry, if (!IS_ERR(acl)) set_cached_acl(inode, type, acl); - __putname(buf); + kfree(buf); return acl; } diff --git a/fs/open.c b/fs/open.c index 74c4c1462b3e..91f1139591ab 100644 --- a/fs/open.c +++ b/fs/open.c @@ -135,15 +135,16 @@ int do_sys_truncate(const char __user *pathname, loff_t length) if (length < 0) /* sorry, but loff_t says... */ return -EINVAL; + CLASS(filename, name)(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); - } - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } return error; } @@ -477,8 +478,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(); @@ -486,8 +485,9 @@ static int do_faccessat(int dfd, const char __user *filename, int mode, int flag return -ENOMEM; } + CLASS(filename_uflags, name)(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; @@ -554,24 +554,19 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename) struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + CLASS(filename, name)(filename); retry: - error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); - if (error) - goto out; - - error = path_permission(&path, MAY_EXEC | MAY_CHDIR); - if (error) - goto dput_and_out; - - set_fs_pwd(current->fs, &path); - -dput_and_out: - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + error = filename_lookup(AT_FDCWD, name, lookup_flags, &path, NULL); + if (!error) { + error = path_permission(&path, MAY_EXEC | MAY_CHDIR); + if (!error) + set_fs_pwd(current->fs, &path); + path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } -out: return error; } @@ -597,10 +592,11 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + CLASS(filename, name)(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; + return error; error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (error) @@ -610,18 +606,14 @@ retry: if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT)) goto dput_and_out; error = security_path_chroot(&path); - if (error) - goto dput_and_out; - - set_fs_root(current->fs, &path); - error = 0; + if (!error) + set_fs_root(current->fs, &path); dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: return error; } @@ -685,11 +677,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; - + CLASS(filename_uflags, name)(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); @@ -800,31 +790,28 @@ 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; 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; + CLASS(filename_uflags, name)(filename, flag); retry: - error = user_path_at(dfd, filename, lookup_flags, &path); - if (error) - goto out; - error = mnt_want_write(path.mnt); - if (error) - goto out_release; - error = chown_common(&path, user, group); - mnt_drop_write(path.mnt); -out_release: - path_put(&path); - if (retry_estale(error, lookup_flags)) { - lookup_flags |= LOOKUP_REVAL; - goto retry; + error = filename_lookup(dfd, name, lookup_flags, &path, NULL); + if (!error) { + error = mnt_want_write(path.mnt); + if (!error) { + error = chown_common(&path, user, group); + mnt_drop_write(path.mnt); + } + path_put(&path); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } } -out: return error; } @@ -1334,7 +1321,7 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode) int err = build_open_flags(&how, &op); if (err) return ERR_PTR(err); - return do_filp_open(AT_FDCWD, name, &op); + return do_file_open(AT_FDCWD, name, &op); } /** @@ -1350,14 +1337,8 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode) */ struct file *filp_open(const char *filename, int flags, umode_t mode) { - struct filename *name = getname_kernel(filename); - struct file *file = ERR_CAST(name); - - if (!IS_ERR(name)) { - file = file_open_name(name, flags, mode); - putname(name); - } - return file; + CLASS(filename_kernel, name)(filename); + return file_open_name(name, flags, mode); } EXPORT_SYMBOL(filp_open); @@ -1377,18 +1358,12 @@ static int do_sys_openat2(int dfd, const char __user *filename, struct open_how *how) { struct open_flags op; - struct filename *tmp __free(putname) = NULL; - int err; - - err = build_open_flags(how, &op); + int err = build_open_flags(how, &op); if (unlikely(err)) return err; - tmp = getname(filename); - if (IS_ERR(tmp)) - return PTR_ERR(tmp); - - return FD_ADD(how->flags, do_filp_open(dfd, tmp, &op)); + CLASS(filename, name)(filename); + return FD_ADD(how->flags, do_file_open(dfd, name, &op)); } int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 7c2b75a44485..ed906725e183 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -867,7 +867,7 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) { #ifdef CONFIG_BLOCK struct super_block *sb; - struct filename *tmp = getname(special); + CLASS(filename, tmp)(special); bool excl = false, thawed = false; int error; dev_t dev; @@ -875,7 +875,6 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) if (IS_ERR(tmp)) return ERR_CAST(tmp); error = lookup_bdev(tmp->name, &dev); - putname(tmp); if (error) return ERR_PTR(error); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index b8e648b8300f..fd0a5b2fb6c1 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -54,7 +54,6 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, struct path *path, bool for_remove) { struct qstr last; - struct filename *filename __free(putname) = NULL; const struct path *root_share_path = &share_conf->vfs_path; int err, type; struct dentry *d; @@ -66,10 +65,7 @@ static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf, flags |= LOOKUP_BENEATH; } - filename = getname_kernel(pathname); - if (IS_ERR(filename)) - return PTR_ERR(filename); - + CLASS(filename_kernel, filename)(pathname); err = vfs_path_parent_lookup(filename, flags, path, &last, &type, root_share_path); @@ -667,7 +663,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, struct path new_path; struct qstr new_last; struct renamedata rd; - struct filename *to; struct ksmbd_share_config *share_conf = work->tcon->share_conf; struct ksmbd_file *parent_fp; int new_type; @@ -676,11 +671,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, if (ksmbd_override_fsids(work)) return -ENOMEM; - to = getname_kernel(newname); - if (IS_ERR(to)) { - err = PTR_ERR(to); - goto revert_fsids; - } + CLASS(filename_kernel, to)(newname); retry: err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, @@ -739,8 +730,6 @@ out2: goto retry; } out1: - putname(to); -revert_fsids: ksmbd_revert_fsids(work); return err; } diff --git a/fs/stat.c b/fs/stat.c index 6c79661e1b96..89909746bed1 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -365,17 +365,13 @@ retry: int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, int flags) { - int ret; - int statx_flags = flags | AT_NO_AUTOMOUNT; - struct filename *name = getname_maybe_null(filename, flags); + CLASS(filename_maybe_null, name)(filename, flags); if (!name && dfd >= 0) return vfs_fstat(dfd, stat); - ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS); - putname(name); - - return ret; + return vfs_statx(dfd, name, flags | AT_NO_AUTOMOUNT, + stat, STATX_BASIC_STATS); } #ifdef __ARCH_WANT_OLD_STAT @@ -564,20 +560,17 @@ static int do_readlinkat(int dfd, const char __user *pathname, char __user *buf, int bufsiz) { struct path path; - struct filename *name; int error; - unsigned int lookup_flags = LOOKUP_EMPTY; + unsigned int lookup_flags = 0; if (bufsiz <= 0) return -EINVAL; + CLASS(filename_flags, name)(pathname, LOOKUP_EMPTY); retry: - name = getname_flags(pathname, lookup_flags); error = filename_lookup(dfd, name, lookup_flags, &path, NULL); - if (unlikely(error)) { - putname(name); + if (unlikely(error)) return error; - } /* * AFS mountpoints allow readlink(2) but are not symlinks @@ -593,7 +586,6 @@ retry: error = (name->name[0] == '\0') ? -ENOENT : -EINVAL; } path_put(&path); - putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; @@ -814,16 +806,12 @@ SYSCALL_DEFINE5(statx, unsigned int, mask, struct statx __user *, buffer) { - int ret; - struct filename *name = getname_maybe_null(filename, flags); + CLASS(filename_maybe_null, name)(filename, flags); if (!name && dfd >= 0) return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer); - ret = do_statx(dfd, name, flags, mask, buffer); - putname(name); - - return ret; + return do_statx(dfd, name, flags, mask, buffer); } #if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_STAT) diff --git a/fs/statfs.c b/fs/statfs.c index a45ac85e6048..377bcef7a561 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; + CLASS(filename, name)(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); diff --git a/fs/utimes.c b/fs/utimes.c index 86f8ce8cd6b1..e22664e4115f 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) { @@ -89,21 +90,18 @@ static int do_utimes_path(int dfd, const char __user *filename, if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - if (flags & AT_EMPTY_PATH) - lookup_flags |= LOOKUP_EMPTY; + CLASS(filename_uflags, name)(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; - error = vfs_utimes(&path, times); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } - return error; } diff --git a/fs/xattr.c b/fs/xattr.c index 32d445fb60aa..3e49e612e1ba 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -649,7 +649,6 @@ int file_setxattr(struct file *f, struct kernel_xattr_ctx *ctx) return error; } -/* unconditionally consumes filename */ int filename_setxattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct kernel_xattr_ctx *ctx) { @@ -659,7 +658,7 @@ int filename_setxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = mnt_want_write(path.mnt); if (!error) { error = do_setxattr(mnt_idmap(path.mnt), path.dentry, ctx); @@ -670,9 +669,6 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } - -out: - putname(filename); return error; } @@ -688,7 +684,6 @@ static int path_setxattrat(int dfd, const char __user *pathname, .kname = &kname, .flags = flags, }; - struct filename *filename; unsigned int lookup_flags = 0; int error; @@ -702,7 +697,7 @@ static int path_setxattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -804,7 +799,6 @@ ssize_t file_getxattr(struct file *f, struct kernel_xattr_ctx *ctx) return do_getxattr(file_mnt_idmap(f), f->f_path.dentry, ctx); } -/* unconditionally consumes filename */ ssize_t filename_getxattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct kernel_xattr_ctx *ctx) { @@ -813,15 +807,13 @@ ssize_t filename_getxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = do_getxattr(mnt_idmap(path.mnt), path.dentry, ctx); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -836,7 +828,6 @@ static ssize_t path_getxattrat(int dfd, const char __user *pathname, .kname = &kname, .flags = 0, }; - struct filename *filename; ssize_t error; if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) @@ -846,7 +837,7 @@ static ssize_t path_getxattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename && dfd >= 0) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -943,7 +934,6 @@ ssize_t file_listxattr(struct file *f, char __user *list, size_t size) return listxattr(f->f_path.dentry, list, size); } -/* unconditionally consumes filename */ static ssize_t filename_listxattr(int dfd, struct filename *filename, unsigned int lookup_flags, @@ -954,15 +944,13 @@ ssize_t filename_listxattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = listxattr(path.dentry, list, size); path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -970,13 +958,12 @@ static ssize_t path_listxattrat(int dfd, const char __user *pathname, unsigned int at_flags, char __user *list, size_t size) { - struct filename *filename; int lookup_flags; if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) return -EINVAL; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename) { CLASS(fd, f)(dfd); if (fd_empty(f)) @@ -1036,7 +1023,6 @@ static int file_removexattr(struct file *f, struct xattr_name *kname) return error; } -/* unconditionally consumes filename */ static int filename_removexattr(int dfd, struct filename *filename, unsigned int lookup_flags, struct xattr_name *kname) { @@ -1046,7 +1032,7 @@ static int filename_removexattr(int dfd, struct filename *filename, retry: error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); if (error) - goto out; + return error; error = mnt_want_write(path.mnt); if (!error) { error = removexattr(mnt_idmap(path.mnt), path.dentry, kname->name); @@ -1057,8 +1043,6 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out: - putname(filename); return error; } @@ -1066,7 +1050,6 @@ static int path_removexattrat(int dfd, const char __user *pathname, unsigned int at_flags, const char __user *name) { struct xattr_name kname; - struct filename *filename; unsigned int lookup_flags; int error; @@ -1077,7 +1060,7 @@ static int path_removexattrat(int dfd, const char __user *pathname, if (error) return error; - filename = getname_maybe_null(pathname, at_flags); + CLASS(filename_maybe_null, filename)(pathname, at_flags); if (!filename) { CLASS(fd, f)(dfd); if (fd_empty(f)) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8ca130af301f..eeb070f330bd 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -972,7 +972,8 @@ #define RUNTIME_CONST_VARIABLES \ RUNTIME_CONST(shift, d_hash_shift) \ RUNTIME_CONST(ptr, dentry_hashtable) \ - RUNTIME_CONST(ptr, __dentry_cache) + RUNTIME_CONST(ptr, __dentry_cache) \ + RUNTIME_CONST(ptr, __names_cache) /* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */ #define KUNIT_TABLE() \ diff --git a/include/linux/audit.h b/include/linux/audit.h index 04d16895c56a..a5be09c8497a 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -318,7 +318,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); @@ -382,12 +381,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())) @@ -626,10 +619,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 73911f961c7e..2e4d1e8b0e71 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2418,14 +2418,19 @@ extern struct kobject *fs_kobj; /* fs/open.c */ struct audit_names; -struct filename { + +struct __filename_head { const char *name; /* pointer to actual string */ - const __user char *uptr; /* original userland pointer */ - atomic_t refcnt; + int refcnt; struct audit_names *aname; - const char iname[]; +}; +#define EMBEDDED_NAME_MAX (192 - sizeof(struct __filename_head)) +struct filename { + struct __filename_head; + const char iname[EMBEDDED_NAME_MAX]; }; static_assert(offsetof(struct filename, iname) % sizeof(long) == 0); +static_assert(sizeof(struct filename) % 64 == 0); static inline struct mnt_idmap *file_mnt_idmap(const struct file *file) { @@ -2520,11 +2525,23 @@ 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)) -static inline struct filename *refname(struct filename *name) -{ - atomic_inc(&name->refcnt); - return name; -} +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 *); + +DEFINE_CLASS(filename, struct filename *, putname(_T), getname(p), const char __user *p) +EXTEND_CLASS(filename, _kernel, getname_kernel(p), const char *p) +EXTEND_CLASS(filename, _flags, getname_flags(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _uflags, getname_uflags(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _maybe_null, getname_maybe_null(p, f), const char __user *p, unsigned int f) +EXTEND_CLASS(filename, _complete_delayed, complete_getname(p), struct delayed_filename *p) extern int finish_open(struct file *file, struct dentry *dentry, int (*open)(struct inode *, struct file *)); @@ -2543,10 +2560,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) void emergency_thaw_all(void); extern int sync_filesystem(struct super_block *); diff --git a/io_uring/fs.c b/io_uring/fs.c index 37079a414eab..d0580c754bf8 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; @@ -81,12 +82,14 @@ int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_renameat(struct io_kiocb *req, unsigned int issue_flags) { struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); + CLASS(filename_complete_delayed, old)(&ren->oldpath); + CLASS(filename_complete_delayed, new)(&ren->newpath); int ret; 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 = filename_renameat2(ren->old_dfd, old, + ren->new_dfd, new, ren->flags); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -97,14 +100,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 +122,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; @@ -130,14 +134,15 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) { struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); + CLASS(filename_complete_delayed, name)(&un->filename); int ret; WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); if (un->flags & AT_REMOVEDIR) - ret = do_rmdir(un->dfd, un->filename); + ret = filename_rmdir(un->dfd, name); else - ret = do_unlinkat(un->dfd, un->filename); + ret = filename_unlinkat(un->dfd, name); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -148,13 +153,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 +171,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; @@ -177,11 +183,12 @@ int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) { struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); + CLASS(filename_complete_delayed, name)(&mkd->filename); int ret; WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode); + ret = filename_mkdirat(mkd->dfd, name, mkd->mode); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -192,13 +199,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 +217,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; @@ -227,11 +235,13 @@ int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) { struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); + CLASS(filename_complete_delayed, old)(&sl->oldpath); + CLASS(filename_complete_delayed, new)(&sl->newpath); int ret; WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath); + ret = filename_symlinkat(old, sl->new_dfd, new); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -242,6 +252,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 +265,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; @@ -272,12 +283,13 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) int io_linkat(struct io_kiocb *req, unsigned int issue_flags) { struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); + CLASS(filename_complete_delayed, old)(&lnk->oldpath); + CLASS(filename_complete_delayed, new)(&lnk->newpath); int ret; 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 = filename_linkat(lnk->old_dfd, old, lnk->new_dfd, new, lnk->flags); req->flags &= ~REQ_F_NEED_CLEANUP; io_req_set_res(req, ret, 0); @@ -288,6 +300,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 15dde9bd6ff6..c09dd14108ed 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; - } req->flags |= REQ_F_NEED_CLEANUP; open->file_slot = READ_ONCE(sqe->file_index); @@ -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; + CLASS(filename_complete_delayed, name)(&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_file_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..7bcae4a6c4a3 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); + CLASS(filename_complete_delayed, name)(&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..ba2b98cf13f9 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) @@ -115,12 +109,12 @@ int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags) int io_getxattr(struct io_kiocb *req, unsigned int issue_flags) { struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); + CLASS(filename_complete_delayed, name)(&ix->filename); int ret; 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, name, 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) @@ -196,12 +186,12 @@ int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags) int io_setxattr(struct io_kiocb *req, unsigned int issue_flags) { struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr); + CLASS(filename_complete_delayed, name)(&ix->filename); int ret; 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, name, LOOKUP_FOLLOW, &ix->ctx); io_xattr_finish(req, ret); return IOU_COMPLETE; } diff --git a/ipc/mqueue.c b/ipc/mqueue.c index c4f6d65596cf..53a58f9ba01f 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -912,13 +912,12 @@ static struct file *mqueue_file_open(struct filename *name, static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, struct mq_attr *attr) { - struct filename *name __free(putname) = NULL;; struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt; int fd, ro; audit_mq_open(oflag, mode, attr); - name = getname(u_name); + CLASS(filename, name)(u_name); if (IS_ERR(name)) return PTR_ERR(name); @@ -942,20 +941,19 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) { int err; - struct filename *name; struct dentry *dentry; struct inode *inode; struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; struct vfsmount *mnt = ipc_ns->mq_mnt; + CLASS(filename, name)(u_name); - name = getname(u_name); if (IS_ERR(name)) return PTR_ERR(name); audit_inode_parent_hidden(name, mnt->mnt_root); err = mnt_want_write(mnt); if (err) - goto out_name; + return err; dentry = start_removing_noperm(mnt->mnt_root, &QSTR(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); @@ -971,9 +969,6 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) out_drop_write: mnt_drop_write(mnt); -out_name: - putname(name); - return err; } diff --git a/kernel/acct.c b/kernel/acct.c index 2a2b3c874acd..812808e5b1b8 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -218,7 +218,6 @@ static int acct_on(const char __user *name) /* Difference from BSD - they don't do O_APPEND */ const int open_flags = O_WRONLY|O_APPEND|O_LARGEFILE; struct pid_namespace *ns = task_active_pid_ns(current); - struct filename *pathname __free(putname) = getname(name); struct file *original_file __free(fput) = NULL; // in that order struct path internal __free(path_put) = {}; // in that order struct file *file __free(fput_sync) = NULL; // in that order @@ -226,8 +225,7 @@ static int acct_on(const char __user *name) struct vfsmount *mnt; struct fs_pin *old; - if (IS_ERR(pathname)) - return PTR_ERR(pathname); + CLASS(filename, pathname)(name); original_file = file_open_name(pathname, open_flags, 0); if (IS_ERR(original_file)) return PTR_ERR(original_file); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index dd0563a8e0be..86a44b162a87 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2170,29 +2170,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, } /** - * __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 * @@ -2214,7 +2191,7 @@ void __audit_getname(struct filename *name) n->name = name; n->name_len = AUDIT_NAME_FULL; name->aname = n; - refname(name); + name->refcnt++; } static inline int audit_copy_fcaps(struct audit_names *name, @@ -2346,7 +2323,7 @@ out_alloc: return; if (name) { n->name = name; - refname(name); + name->refcnt++; } out: @@ -2468,7 +2445,7 @@ void __audit_inode_child(struct inode *parent, if (found_parent) { found_child->name = found_parent->name; found_child->name_len = AUDIT_NAME_FULL; - refname(found_child->name); + found_child->name->refcnt++; } } diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 40cf59301c21..a6d37902b73d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4692,23 +4692,18 @@ static int split_huge_pages_in_file(const char *file_path, pgoff_t off_start, pgoff_t off_end, unsigned int new_order, long in_folio_offset) { - struct filename *file; struct file *candidate; struct address_space *mapping; - int ret = -EINVAL; pgoff_t index; int nr_pages = 1; unsigned long total = 0, split = 0; unsigned int min_order; unsigned int target_order; - file = getname_kernel(file_path); - if (IS_ERR(file)) - return ret; - + CLASS(filename_kernel, file)(file_path); candidate = file_open_name(file, O_RDONLY, 0); if (IS_ERR(candidate)) - goto out; + return -EINVAL; pr_debug("split file-backed THPs in file: %s, page offset: [0x%lx - 0x%lx], new_order: %u, in_folio_offset: %ld\n", file_path, off_start, off_end, new_order, in_folio_offset); @@ -4757,12 +4752,8 @@ next: } filp_close(candidate, NULL); - ret = 0; - pr_debug("%lu of %lu file-backed THP split\n", split, total); -out: - putname(file); - return ret; + return 0; } #define MAX_INPUT_BUF_SZ 255 diff --git a/mm/swapfile.c b/mm/swapfile.c index 46d2008e4b99..25120cf7c480 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2831,7 +2831,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) struct file *swap_file, *victim; struct address_space *mapping; struct inode *inode; - struct filename *pathname; unsigned int maxpages; int err, found = 0; @@ -2840,14 +2839,10 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) BUG_ON(!current->mm); - pathname = getname(specialfile); - if (IS_ERR(pathname)) - return PTR_ERR(pathname); - + CLASS(filename, pathname)(specialfile); victim = file_open_name(pathname, O_RDWR|O_LARGEFILE, 0); - err = PTR_ERR(victim); if (IS_ERR(victim)) - goto out; + return PTR_ERR(victim); mapping = victim->f_mapping; spin_lock(&swap_lock); @@ -2964,8 +2959,6 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) out_dput: filp_close(victim, NULL); -out: - putname(pathname); return err; } @@ -3392,7 +3385,6 @@ err: SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) { struct swap_info_struct *si; - struct filename *name; struct file *swap_file = NULL; struct address_space *mapping; struct dentry *dentry; @@ -3422,12 +3414,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) INIT_WORK(&si->discard_work, swap_discard_work); INIT_WORK(&si->reclaim_work, swap_reclaim_work); - name = getname(specialfile); - if (IS_ERR(name)) { - error = PTR_ERR(name); - name = NULL; - goto bad_swap; - } + CLASS(filename, name)(specialfile); swap_file = file_open_name(name, O_RDWR | O_LARGEFILE | O_EXCL, 0); if (IS_ERR(swap_file)) { error = PTR_ERR(swap_file); @@ -3635,8 +3622,6 @@ bad_swap: out: if (!IS_ERR_OR_NULL(folio)) folio_release_kmap(folio, swap_header); - if (name) - putname(name); if (inode) inode_unlock(inode); return error; |
