X-Git-Url: http://git.monstr.eu/?p=linux-2.6-microblaze.git;a=blobdiff_plain;f=fs%2Fnamei.c;h=d049d39726957292bdd825200c64e2f9e76211d1;hp=bf6d8a738c599e62bf6de18b59c27cf2bd19725f;hb=87045e6546078dae215d1bd3b2bc82b3ada3ca77;hpb=79e48a21045e6f2ccd9f59737685336d9ad75d54 diff --git a/fs/namei.c b/fs/namei.c index bf6d8a738c59..d049d3972695 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -203,6 +203,14 @@ getname_flags(const char __user *filename, int flags, int *empty) return result; } +struct filename * +getname_uflags(const char __user *filename, int uflags) +{ + int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + + return getname_flags(filename, flags, NULL); +} + struct filename * getname(const char __user * filename) { @@ -247,6 +255,9 @@ getname_kernel(const char * filename) void putname(struct filename *name) { + if (IS_ERR_OR_NULL(name)) + return; + BUG_ON(name->refcnt <= 0); if (--name->refcnt > 0) @@ -2456,7 +2467,7 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path return err; } -int filename_lookup(int dfd, struct filename *name, unsigned flags, +static int __filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root) { int retval; @@ -2474,6 +2485,14 @@ int filename_lookup(int dfd, struct filename *name, unsigned flags, audit_inode(name, path->dentry, flags & LOOKUP_MOUNTPOINT ? AUDIT_INODE_NOEVAL : 0); restore_nameidata(); + return retval; +} + +int filename_lookup(int dfd, struct filename *name, unsigned flags, + struct path *path, struct path *root) +{ + int retval = __filename_lookup(dfd, name, flags, path, root); + putname(name); return retval; } @@ -2495,7 +2514,7 @@ static int path_parentat(struct nameidata *nd, unsigned flags, return err; } -static struct filename *filename_parentat(int dfd, struct filename *name, +static int __filename_parentat(int dfd, struct filename *name, unsigned int flags, struct path *parent, struct qstr *last, int *type) { @@ -2503,7 +2522,7 @@ static struct filename *filename_parentat(int dfd, struct filename *name, struct nameidata nd; if (IS_ERR(name)) - return name; + return PTR_ERR(name); set_nameidata(&nd, dfd, name, NULL); retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); if (unlikely(retval == -ECHILD)) @@ -2514,29 +2533,34 @@ static struct filename *filename_parentat(int dfd, struct filename *name, *last = nd.last; *type = nd.last_type; audit_inode(name, parent->dentry, AUDIT_INODE_PARENT); - } else { - putname(name); - name = ERR_PTR(retval); } restore_nameidata(); - return name; + return retval; +} + +static int filename_parentat(int dfd, struct filename *name, + unsigned int flags, struct path *parent, + struct qstr *last, int *type) +{ + int retval = __filename_parentat(dfd, name, flags, parent, last, type); + + putname(name); + return retval; } /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { - struct filename *filename; struct dentry *d; struct qstr last; - int type; + int type, error; - filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, + error = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, &last, &type); - if (IS_ERR(filename)) - return ERR_CAST(filename); + if (error) + return ERR_PTR(error); if (unlikely(type != LAST_NORM)) { path_put(path); - putname(filename); return ERR_PTR(-EINVAL); } inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); @@ -2545,7 +2569,6 @@ struct dentry *kern_path_locked(const char *name, struct path *path) inode_unlock(path->dentry->d_inode); path_put(path); } - putname(filename); return d; } @@ -2575,8 +2598,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, - int len, struct qstr *this) +static int lookup_one_common(struct user_namespace *mnt_userns, + const char *name, struct dentry *base, int len, + struct qstr *this) { this->name = name; this->len = len; @@ -2604,7 +2628,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(&init_user_ns, base->d_inode, MAY_EXEC); + return inode_permission(mnt_userns, base->d_inode, MAY_EXEC); } /** @@ -2628,7 +2652,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2655,7 +2679,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -2664,6 +2688,36 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) } EXPORT_SYMBOL(lookup_one_len); +/** + * lookup_one - filesystem helper to lookup single pathname component + * @mnt_userns: user namespace of the mount the lookup is performed from + * @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(struct user_namespace *mnt_userns, 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_common(mnt_userns, name, base, len, &this); + if (err) + return ERR_PTR(err); + + dentry = lookup_dcache(&this, base, 0); + return dentry ? dentry : __lookup_slow(&this, base, 0); +} +EXPORT_SYMBOL(lookup_one); + /** * lookup_one_len_unlocked - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -2683,7 +2737,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_common(&init_user_ns, name, base, len, &this); if (err) return ERR_PTR(err); @@ -3023,9 +3077,7 @@ static int handle_truncate(struct user_namespace *mnt_userns, struct file *filp) /* * Refuse to truncate files with mandatory locks held on them. */ - error = locks_verify_locked(filp); - if (!error) - error = security_path_truncate(path); + error = security_path_truncate(path); if (!error) { error = do_truncate(mnt_userns, path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, @@ -3566,7 +3618,7 @@ struct file *do_file_open_root(const struct path *root, return file; } -static struct dentry *filename_create(int dfd, struct filename *name, +static struct dentry *__filename_create(int dfd, struct filename *name, struct path *path, unsigned int lookup_flags) { struct dentry *dentry = ERR_PTR(-EEXIST); @@ -3582,9 +3634,9 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - name = filename_parentat(dfd, name, lookup_flags, path, &last, &type); - if (IS_ERR(name)) - return ERR_CAST(name); + error = __filename_parentat(dfd, name, lookup_flags, path, &last, &type); + if (error) + return ERR_PTR(error); /* * Yucky last component or no last component at all? @@ -3622,7 +3674,6 @@ static struct dentry *filename_create(int dfd, struct filename *name, error = err2; goto fail; } - putname(name); return dentry; fail: dput(dentry); @@ -3633,10 +3684,18 @@ unlock: mnt_drop_write(path->mnt); out: path_put(path); - putname(name); return dentry; } +static inline struct dentry *filename_create(int dfd, struct filename *name, + struct path *path, unsigned int lookup_flags) +{ + struct dentry *res = __filename_create(dfd, name, path, lookup_flags); + + putname(name); + return res; +} + struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { @@ -3725,7 +3784,7 @@ static int may_mknod(umode_t mode) } } -static long do_mknodat(int dfd, const char __user *filename, umode_t mode, +static int do_mknodat(int dfd, struct filename *name, umode_t mode, unsigned int dev) { struct user_namespace *mnt_userns; @@ -3736,17 +3795,18 @@ static long do_mknodat(int dfd, const char __user *filename, umode_t mode, error = may_mknod(mode); if (error) - return error; + goto out1; retry: - dentry = user_path_create(dfd, filename, &path, lookup_flags); + dentry = __filename_create(dfd, name, &path, lookup_flags); + error = PTR_ERR(dentry); if (IS_ERR(dentry)) - return PTR_ERR(dentry); + goto out1; if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); error = security_path_mknod(&path, dentry, mode, dev); if (error) - goto out; + goto out2; mnt_userns = mnt_user_ns(path.mnt); switch (mode & S_IFMT) { @@ -3765,24 +3825,26 @@ retry: dentry, mode, 0); break; } -out: +out2: done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { 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, filename, mode, dev); + return do_mknodat(dfd, getname(filename), mode, dev); } SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev) { - return do_mknodat(AT_FDCWD, filename, mode, dev); + return do_mknodat(AT_FDCWD, getname(filename), mode, dev); } /** @@ -3827,7 +3889,7 @@ int vfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_mkdir); -static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +int do_mkdirat(int dfd, struct filename *name, umode_t mode) { struct dentry *dentry; struct path path; @@ -3835,9 +3897,10 @@ static long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) unsigned int lookup_flags = LOOKUP_DIRECTORY; retry: - dentry = user_path_create(dfd, pathname, &path, lookup_flags); + dentry = __filename_create(dfd, name, &path, lookup_flags); + error = PTR_ERR(dentry); if (IS_ERR(dentry)) - return PTR_ERR(dentry); + goto out_putname; if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); @@ -3853,17 +3916,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, pathname, mode); + return do_mkdirat(dfd, getname(pathname), mode); } SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) { - return do_mkdirat(AT_FDCWD, pathname, mode); + return do_mkdirat(AT_FDCWD, getname(pathname), mode); } /** @@ -3921,62 +3986,62 @@ out: } EXPORT_SYMBOL(vfs_rmdir); -long do_rmdir(int dfd, struct filename *name) +int do_rmdir(int dfd, struct filename *name) { struct user_namespace *mnt_userns; - int error = 0; + int error; struct dentry *dentry; struct path path; struct qstr last; int type; unsigned int lookup_flags = 0; retry: - name = filename_parentat(dfd, name, lookup_flags, - &path, &last, &type); - if (IS_ERR(name)) - return PTR_ERR(name); + error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type); + if (error) + goto exit1; switch (type) { case LAST_DOTDOT: error = -ENOTEMPTY; - goto exit1; + goto exit2; case LAST_DOT: error = -EINVAL; - goto exit1; + goto exit2; case LAST_ROOT: error = -EBUSY; - goto exit1; + goto exit2; } error = mnt_want_write(path.mnt); if (error) - goto exit1; + goto exit2; inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto exit2; + goto exit3; if (!dentry->d_inode) { error = -ENOENT; - goto exit3; + goto exit4; } error = security_path_rmdir(&path, dentry); if (error) - goto exit3; + goto exit4; mnt_userns = mnt_user_ns(path.mnt); error = vfs_rmdir(mnt_userns, path.dentry->d_inode, dentry); -exit3: +exit4: dput(dentry); -exit2: +exit3: inode_unlock(path.dentry->d_inode); mnt_drop_write(path.mnt); -exit1: +exit2: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } +exit1: putname(name); return error; } @@ -4059,7 +4124,7 @@ EXPORT_SYMBOL(vfs_unlink); * writeout happening, and we don't want to prevent access to the directory * while waiting on the I/O. */ -long do_unlinkat(int dfd, struct filename *name) +int do_unlinkat(int dfd, struct filename *name) { int error; struct dentry *dentry; @@ -4070,17 +4135,17 @@ long do_unlinkat(int dfd, struct filename *name) struct inode *delegated_inode = NULL; unsigned int lookup_flags = 0; retry: - name = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); - if (IS_ERR(name)) - return PTR_ERR(name); + error = __filename_parentat(dfd, name, lookup_flags, &path, &last, &type); + if (error) + goto exit1; error = -EISDIR; if (type != LAST_NORM) - goto exit1; + goto exit2; error = mnt_want_write(path.mnt); if (error) - goto exit1; + goto exit2; retry_deleg: inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); dentry = __lookup_hash(&last, path.dentry, lookup_flags); @@ -4097,11 +4162,11 @@ retry_deleg: ihold(inode); error = security_path_unlink(&path, dentry); if (error) - goto exit2; + goto exit3; mnt_userns = mnt_user_ns(path.mnt); error = vfs_unlink(mnt_userns, path.dentry->d_inode, dentry, &delegated_inode); -exit2: +exit3: dput(dentry); } inode_unlock(path.dentry->d_inode); @@ -4114,13 +4179,14 @@ exit2: goto retry_deleg; } mnt_drop_write(path.mnt); -exit1: +exit2: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; inode = NULL; goto retry; } +exit1: putname(name); return error; @@ -4131,7 +4197,7 @@ slashes: error = -EISDIR; else error = -ENOTDIR; - goto exit2; + goto exit3; } SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) @@ -4186,23 +4252,22 @@ int vfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, } EXPORT_SYMBOL(vfs_symlink); -static long do_symlinkat(const char __user *oldname, int newdfd, - const char __user *newname) +int do_symlinkat(struct filename *from, int newdfd, struct filename *to) { int error; - struct filename *from; struct dentry *dentry; struct path path; unsigned int lookup_flags = 0; - from = getname(oldname); - if (IS_ERR(from)) - return PTR_ERR(from); + if (IS_ERR(from)) { + error = PTR_ERR(from); + goto out_putnames; + } retry: - dentry = user_path_create(newdfd, newname, &path, lookup_flags); + dentry = __filename_create(newdfd, to, &path, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) - goto out_putname; + goto out_putnames; error = security_path_symlink(&path, dentry, from->name); if (!error) { @@ -4217,7 +4282,8 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } -out_putname: +out_putnames: + putname(to); putname(from); return error; } @@ -4225,12 +4291,12 @@ out_putname: SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, int, newdfd, const char __user *, newname) { - return do_symlinkat(oldname, newdfd, newname); + return do_symlinkat(getname(oldname), newdfd, getname(newname)); } SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) { - return do_symlinkat(oldname, AT_FDCWD, newname); + return do_symlinkat(getname(oldname), AT_FDCWD, getname(newname)); } /** @@ -4331,8 +4397,8 @@ EXPORT_SYMBOL(vfs_link); * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -static int do_linkat(int olddfd, const char __user *oldname, int newdfd, - const char __user *newname, int flags) +int do_linkat(int olddfd, struct filename *old, int newdfd, + struct filename *new, int flags) { struct user_namespace *mnt_userns; struct dentry *new_dentry; @@ -4341,31 +4407,32 @@ static int do_linkat(int olddfd, const char __user *oldname, int newdfd, int how = 0; int error; - if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) - return -EINVAL; + if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) { + error = -EINVAL; + goto out_putnames; + } /* * To use null names we require CAP_DAC_READ_SEARCH * This ensures that not everyone will be able to create * handlink using the passed filedescriptor. */ - if (flags & AT_EMPTY_PATH) { - if (!capable(CAP_DAC_READ_SEARCH)) - return -ENOENT; - how = LOOKUP_EMPTY; + if (flags & AT_EMPTY_PATH && !capable(CAP_DAC_READ_SEARCH)) { + error = -ENOENT; + goto out_putnames; } if (flags & AT_SYMLINK_FOLLOW) how |= LOOKUP_FOLLOW; retry: - error = user_path_at(olddfd, oldname, how, &old_path); + error = __filename_lookup(olddfd, old, how, &old_path, NULL); if (error) - return error; + goto out_putnames; - new_dentry = user_path_create(newdfd, newname, &new_path, + new_dentry = __filename_create(newdfd, new, &new_path, (how & LOOKUP_REVAL)); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) - goto out; + goto out_putpath; error = -EXDEV; if (old_path.mnt != new_path.mnt) @@ -4393,8 +4460,11 @@ out_dput: how |= LOOKUP_REVAL; goto retry; } -out: +out_putpath: path_put(&old_path); +out_putnames: + putname(old); + putname(new); return error; } @@ -4402,12 +4472,13 @@ out: 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); + return do_linkat(olddfd, getname_uflags(oldname, flags), + newdfd, getname(newname), flags); } SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) { - return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); + return do_linkat(AT_FDCWD, getname(oldname), AT_FDCWD, getname(newname), 0); } /** @@ -4602,29 +4673,25 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd, int error = -EINVAL; if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) - goto put_both; + goto put_names; if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) && (flags & RENAME_EXCHANGE)) - goto put_both; + goto put_names; if (flags & RENAME_EXCHANGE) target_flags = 0; retry: - from = filename_parentat(olddfd, from, lookup_flags, &old_path, + error = __filename_parentat(olddfd, from, lookup_flags, &old_path, &old_last, &old_type); - if (IS_ERR(from)) { - error = PTR_ERR(from); - goto put_new; - } + if (error) + goto put_names; - to = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, + error = __filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last, &new_type); - if (IS_ERR(to)) { - error = PTR_ERR(to); + if (error) goto exit1; - } error = -EXDEV; if (old_path.mnt != new_path.mnt) @@ -4727,12 +4794,9 @@ exit1: lookup_flags |= LOOKUP_REVAL; goto retry; } -put_both: - if (!IS_ERR(from)) - putname(from); -put_new: - if (!IS_ERR(to)) - putname(to); +put_names: + putname(from); + putname(to); return error; }