diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 121 |
1 files changed, 71 insertions, 50 deletions
diff --git a/fs/namei.c b/fs/namei.c index 921ae32dbc80..a66ed5a1622a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -39,6 +39,7 @@ #include <linux/bitops.h> #include <linux/init_task.h> #include <linux/uaccess.h> +#include <linux/build_bug.h> #include "internal.h" #include "mount.h" @@ -130,6 +131,7 @@ getname_flags(const char __user *filename, int flags, int *empty) struct filename *result; char *kname; int len; + BUILD_BUG_ON(offsetof(struct filename, iname) % sizeof(long) != 0); result = audit_reusename(filename); if (result) @@ -559,9 +561,10 @@ static int __nd_alloc_stack(struct nameidata *nd) 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); @@ -926,7 +929,8 @@ static inline int may_follow_link(struct nameidata *nd) 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; } @@ -992,7 +996,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; } @@ -1473,43 +1477,36 @@ static struct dentry *lookup_dcache(const struct qstr *name, } /* - * Call i_op->lookup on the dentry. The dentry must be negative and - * unhashed. - * - * dir->d_inode->i_mutex must be held + * Parent directory has inode locked exclusive. This is one + * and only case when ->lookup() gets called on non in-lookup + * dentries - as the matter of fact, this only gets called + * when directory is guaranteed to have no in-lookup children + * at all. */ -static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct dentry *old; - - /* Don't create child dentry for a dead directory. */ - if (unlikely(IS_DEADDIR(dir))) { - dput(dentry); - return ERR_PTR(-ENOENT); - } - - old = dir->i_op->lookup(dir, dentry, flags); - if (unlikely(old)) { - dput(dentry); - dentry = old; - } - return dentry; -} - static struct dentry *__lookup_hash(const struct qstr *name, struct dentry *base, unsigned int flags) { struct dentry *dentry = lookup_dcache(name, base, flags); + struct dentry *old; + struct inode *dir = base->d_inode; if (dentry) return dentry; + /* Don't create child dentry for a dead directory. */ + if (unlikely(IS_DEADDIR(dir))) + return ERR_PTR(-ENOENT); + dentry = d_alloc(base, name); if (unlikely(!dentry)) return ERR_PTR(-ENOMEM); - return lookup_real(base->d_inode, dentry, flags); + old = dir->i_op->lookup(dir, dentry, flags); + if (unlikely(old)) { + dput(dentry); + dentry = old; + } + return dentry; } static int lookup_fast(struct nameidata *nd, @@ -3380,9 +3377,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: @@ -3462,9 +3457,6 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, 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: @@ -3728,8 +3720,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; @@ -3772,9 +3764,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) @@ -3803,7 +3801,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) } 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; @@ -3828,9 +3826,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) @@ -3872,7 +3875,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; @@ -4108,8 +4111,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) } 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; @@ -4139,9 +4142,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); } /** @@ -4233,8 +4242,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; @@ -4298,9 +4307,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); } /** @@ -4478,8 +4493,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; @@ -4621,15 +4636,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) |