From: Linus Torvalds Date: Mon, 9 Apr 2018 19:48:05 +0000 (-0700) Subject: Merge branch 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs X-Git-Tag: v4.17-rc1~68 X-Git-Url: https://repo.jachan.dev/linux.git/commitdiff_plain/fd3b36d275660c905da9900b078eea341847d5e4?hp=-c Merge branch 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull vfs namei updates from Al Viro: - make lookup_one_len() safe with parent locked only shared(incoming afs series wants that) - fix of getname_kernel() regression from 2015 (-stable fodder, that one). * 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: getname_kernel() needs to make sure that ->name != ->iname in long case make lookup_one_len() safe to use with directory locked shared new helper: __lookup_slow() merge common parts of lookup_one_len{,_unlocked} into common helper --- fd3b36d275660c905da9900b078eea341847d5e4 diff --combined fs/namei.c index a66ed5a1622a,3aefcf74f041..186bd2464fd5 --- a/fs/namei.c +++ b/fs/namei.c @@@ -39,7 -39,6 +39,7 @@@ #include #include #include +#include #include "internal.h" #include "mount.h" @@@ -131,7 -130,6 +131,7 @@@ getname_flags(const char __user *filena struct filename *result; char *kname; int len; + BUILD_BUG_ON(offsetof(struct filename, iname) % sizeof(long) != 0); result = audit_reusename(filename); if (result) @@@ -224,9 -222,10 +224,10 @@@ getname_kernel(const char * filename 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(sizeof(*tmp), GFP_KERNEL); + tmp = kmalloc(size, GFP_KERNEL); if (unlikely(!tmp)) { __putname(result); return ERR_PTR(-ENOMEM); @@@ -561,10 -560,9 +562,10 @@@ static int __nd_alloc_stack(struct name static bool path_connected(const struct path *path) { struct vfsmount *mnt = path->mnt; + struct super_block *sb = mnt->mnt_sb; - /* Only bind mounts can have disconnected paths */ - if (mnt->mnt_root == mnt->mnt_sb->s_root) + /* Bind mounts and multi-root filesystems can have disconnected paths */ + if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root)) return true; return is_subdir(path->dentry, mnt->mnt_root); @@@ -929,8 -927,7 +930,8 @@@ static inline int may_follow_link(struc if (nd->flags & LOOKUP_RCU) return -ECHILD; - audit_log_link_denied("follow_link", &nd->stack[0].link); + audit_inode(nd->name, nd->stack[0].link.dentry, 0); + audit_log_link_denied("follow_link"); return -EACCES; } @@@ -996,7 -993,7 +997,7 @@@ static int may_linkat(struct path *link if (safe_hardlink_source(inode) || inode_owner_or_capable(inode)) return 0; - audit_log_link_denied("linkat", link); + audit_log_link_denied("linkat"); return -EPERM; } @@@ -1597,22 -1594,21 +1598,21 @@@ static int lookup_fast(struct nameidat } /* Fast lookup failed, do it the slow way */ - static struct dentry *lookup_slow(const struct qstr *name, - struct dentry *dir, - unsigned int flags) + static struct dentry *__lookup_slow(const struct qstr *name, + struct dentry *dir, + unsigned int flags) { - struct dentry *dentry = ERR_PTR(-ENOENT), *old; + struct dentry *dentry, *old; struct inode *inode = dir->d_inode; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); - inode_lock_shared(inode); /* Don't go there if it's already dead */ if (unlikely(IS_DEADDIR(inode))) - goto out; + return ERR_PTR(-ENOENT); again: dentry = d_alloc_parallel(dir, name, &wq); if (IS_ERR(dentry)) - goto out; + return dentry; if (unlikely(!d_in_lookup(dentry))) { if (!(flags & LOOKUP_NO_REVAL)) { int error = d_revalidate(dentry, flags); @@@ -1634,11 -1630,21 +1634,21 @@@ dentry = old; } } - out: - inode_unlock_shared(inode); return dentry; } + static struct dentry *lookup_slow(const struct qstr *name, + struct dentry *dir, + unsigned int flags) + { + struct inode *inode = dir->d_inode; + struct dentry *res; + inode_lock_shared(inode); + res = __lookup_slow(name, dir, flags); + inode_unlock_shared(inode); + return res; + } + static inline int may_lookup(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { @@@ -2421,56 -2427,63 +2431,63 @@@ int vfs_path_lookup(struct dentry *dent } EXPORT_SYMBOL(vfs_path_lookup); - /** - * lookup_one_len - filesystem helper to lookup single pathname component - * @name: pathname component to lookup - * @base: base directory to lookup from - * @len: maximum length @len should be interpreted to - * - * Note that this routine is purely a helper for filesystem usage and should - * not be called by generic code. - * - * The caller must hold base->i_mutex. - */ - struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) + static int lookup_one_len_common(const char *name, struct dentry *base, + int len, struct qstr *this) { - struct qstr this; - unsigned int c; - int err; - - WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - - this.name = name; - this.len = len; - this.hash = full_name_hash(base, name, len); + this->name = name; + this->len = len; + this->hash = full_name_hash(base, name, len); if (!len) - return ERR_PTR(-EACCES); + return -EACCES; if (unlikely(name[0] == '.')) { if (len < 2 || (len == 2 && name[1] == '.')) - return ERR_PTR(-EACCES); + return -EACCES; } while (len--) { - c = *(const unsigned char *)name++; + unsigned int c = *(const unsigned char *)name++; if (c == '/' || c == '\0') - return ERR_PTR(-EACCES); + return -EACCES; } /* * See if the low-level filesystem might want * to use its own hash.. */ if (base->d_flags & DCACHE_OP_HASH) { - int err = base->d_op->d_hash(base, &this); + int err = base->d_op->d_hash(base, this); if (err < 0) - return ERR_PTR(err); + return err; } - err = inode_permission(base->d_inode, MAY_EXEC); + return inode_permission(base->d_inode, MAY_EXEC); + } + + /** + * lookup_one_len - filesystem helper to lookup single pathname component + * @name: pathname component to lookup + * @base: base directory to lookup from + * @len: maximum length @len should be interpreted to + * + * Note that this routine is purely a helper for filesystem usage and should + * not be called by generic code. + * + * The caller must hold base->i_mutex. + */ + struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) + { + struct dentry *dentry; + struct qstr this; + int err; + + WARN_ON_ONCE(!inode_is_locked(base->d_inode)); + + err = lookup_one_len_common(name, base, len, &this); if (err) return ERR_PTR(err); - return __lookup_hash(&this, base, 0); + dentry = lookup_dcache(&this, base, 0); + return dentry ? dentry : __lookup_slow(&this, base, 0); } EXPORT_SYMBOL(lookup_one_len); @@@ -2490,37 -2503,10 +2507,10 @@@ struct dentry *lookup_one_len_unlocked( struct dentry *base, int len) { struct qstr this; - unsigned int c; int err; struct dentry *ret; - this.name = name; - this.len = len; - this.hash = full_name_hash(base, name, len); - if (!len) - return ERR_PTR(-EACCES); - - if (unlikely(name[0] == '.')) { - if (len < 2 || (len == 2 && name[1] == '.')) - return ERR_PTR(-EACCES); - } - - while (len--) { - c = *(const unsigned char *)name++; - if (c == '/' || c == '\0') - return ERR_PTR(-EACCES); - } - /* - * See if the low-level filesystem might want - * to use its own hash.. - */ - if (base->d_flags & DCACHE_OP_HASH) { - int err = base->d_op->d_hash(base, &this); - if (err < 0) - return ERR_PTR(err); - } - - err = inode_permission(base->d_inode, MAY_EXEC); + err = lookup_one_len_common(name, base, len, &this); if (err) return ERR_PTR(err); @@@ -3377,7 -3363,9 +3367,7 @@@ finish_open_created goto out; *opened |= FILE_OPENED; opened: - error = open_check_o_direct(file); - if (!error) - error = ima_file_check(file, op->acc_mode, *opened); + error = ima_file_check(file, op->acc_mode, *opened); if (!error && will_truncate) error = handle_truncate(file); out: @@@ -3457,6 -3445,9 +3447,6 @@@ static int do_tmpfile(struct nameidata error = finish_open(file, child, NULL, opened); if (error) goto out2; - error = open_check_o_direct(file); - if (error) - fput(file); out2: mnt_drop_write(path.mnt); out: @@@ -3720,8 -3711,8 +3710,8 @@@ static int may_mknod(umode_t mode } } -SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, - unsigned, dev) +long do_mknodat(int dfd, const char __user *filename, umode_t mode, + unsigned int dev) { struct dentry *dentry; struct path path; @@@ -3764,15 -3755,9 +3754,15 @@@ out return error; } +SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode, + unsigned int, dev) +{ + return do_mknodat(dfd, filename, mode, dev); +} + SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev) { - return sys_mknodat(AT_FDCWD, filename, mode, dev); + return do_mknodat(AT_FDCWD, filename, mode, dev); } int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) @@@ -3801,7 -3786,7 +3791,7 @@@ } EXPORT_SYMBOL(vfs_mkdir); -SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) +long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) { struct dentry *dentry; struct path path; @@@ -3826,14 -3811,9 +3816,14 @@@ retry return error; } +SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode) +{ + return do_mkdirat(dfd, pathname, mode); +} + SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { - return sys_mkdirat(AT_FDCWD, pathname, mode); + return do_mkdirat(AT_FDCWD, pathname, mode); } int vfs_rmdir(struct inode *dir, struct dentry *dentry) @@@ -3875,7 -3855,7 +3865,7 @@@ out } EXPORT_SYMBOL(vfs_rmdir); -static long do_rmdir(int dfd, const char __user *pathname) +long do_rmdir(int dfd, const char __user *pathname) { int error = 0; struct filename *name; @@@ -4111,8 -4091,8 +4101,8 @@@ int vfs_symlink(struct inode *dir, stru } EXPORT_SYMBOL(vfs_symlink); -SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, - int, newdfd, const char __user *, newname) +long do_symlinkat(const char __user *oldname, int newdfd, + const char __user *newname) { int error; struct filename *from; @@@ -4142,15 -4122,9 +4132,15 @@@ out_putname return error; } +SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, + int, newdfd, const char __user *, newname) +{ + return do_symlinkat(oldname, newdfd, newname); +} + SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) { - return sys_symlinkat(oldname, AT_FDCWD, newname); + return do_symlinkat(oldname, AT_FDCWD, newname); } /** @@@ -4242,8 -4216,8 +4232,8 @@@ EXPORT_SYMBOL(vfs_link) * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, - int, newdfd, const char __user *, newname, int, flags) +int do_linkat(int olddfd, const char __user *oldname, int newdfd, + const char __user *newname, int flags) { struct dentry *new_dentry; struct path old_path, new_path; @@@ -4307,15 -4281,9 +4297,15 @@@ out return error; } +SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, + int, newdfd, const char __user *, newname, int, flags) +{ + return do_linkat(olddfd, oldname, newdfd, newname, flags); +} + SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) { - return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); + return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); } /** @@@ -4493,8 -4461,8 +4483,8 @@@ out } EXPORT_SYMBOL(vfs_rename); -SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, - int, newdfd, const char __user *, newname, unsigned int, flags) +static int do_renameat2(int olddfd, const char __user *oldname, int newdfd, + const char __user *newname, unsigned int flags) { struct dentry *old_dentry, *new_dentry; struct dentry *trap; @@@ -4636,21 -4604,15 +4626,21 @@@ exit return error; } +SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, + int, newdfd, const char __user *, newname, unsigned int, flags) +{ + return do_renameat2(olddfd, oldname, newdfd, newname, flags); +} + SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return sys_renameat2(olddfd, oldname, newdfd, newname, 0); + return do_renameat2(olddfd, oldname, newdfd, newname, 0); } SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname) { - return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0); + return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0); } int vfs_whiteout(struct inode *dir, struct dentry *dentry)