diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 240 |
1 files changed, 125 insertions, 115 deletions
diff --git a/fs/namei.c b/fs/namei.c index cafa365eeb70..186bd2464fd5 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) @@ -222,9 +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); @@ -927,7 +930,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; } @@ -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; } @@ -1474,43 +1478,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, @@ -1601,22 +1598,21 @@ static int lookup_fast(struct nameidata *nd, } /* 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); @@ -1638,11 +1634,21 @@ again: 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) { @@ -2425,56 +2431,63 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } 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); @@ -2494,37 +2507,10 @@ struct dentry *lookup_one_len_unlocked(const char *name, 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); @@ -3381,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: @@ -3463,9 +3447,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: @@ -3729,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; @@ -3773,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) @@ -3804,7 +3791,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; @@ -3829,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) @@ -3873,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; @@ -4109,8 +4101,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; @@ -4140,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); } /** @@ -4234,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; @@ -4299,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); } /** @@ -4479,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; @@ -4622,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) |