diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 172 |
1 files changed, 136 insertions, 36 deletions
@@ -30,6 +30,8 @@ #include <linux/audit.h> #include <linux/capability.h> #include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/namei.h> #include <asm/namei.h> #include <asm/uaccess.h> @@ -1063,7 +1065,8 @@ set_it: } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd) +static int fastcall do_path_lookup(int dfd, const char *name, + unsigned int flags, struct nameidata *nd) { int retval = 0; @@ -1083,9 +1086,38 @@ int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata } nd->mnt = mntget(current->fs->rootmnt); nd->dentry = dget(current->fs->root); - } else { + } else if (dfd == AT_FDCWD) { nd->mnt = mntget(current->fs->pwdmnt); nd->dentry = dget(current->fs->pwd); + } else { + struct file *file; + int fput_needed; + struct dentry *dentry; + + file = fget_light(dfd, &fput_needed); + if (!file) { + retval = -EBADF; + goto out_fail; + } + + dentry = file->f_dentry; + + if (!S_ISDIR(dentry->d_inode->i_mode)) { + retval = -ENOTDIR; + fput_light(file, fput_needed); + goto out_fail; + } + + retval = file_permission(file, MAY_EXEC); + if (retval) { + fput_light(file, fput_needed); + goto out_fail; + } + + nd->mnt = mntget(file->f_vfsmnt); + nd->dentry = dget(dentry); + + fput_light(file, fput_needed); } read_unlock(¤t->fs->lock); current->total_link_count = 0; @@ -1094,11 +1126,19 @@ out: if (unlikely(current->audit_context && nd && nd->dentry && nd->dentry->d_inode)) audit_inode(name, nd->dentry->d_inode, flags); +out_fail: return retval; } -static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags, - struct nameidata *nd, int open_flags, int create_mode) +int fastcall path_lookup(const char *name, unsigned int flags, + struct nameidata *nd) +{ + return do_path_lookup(AT_FDCWD, name, flags, nd); +} + +static int __path_lookup_intent_open(int dfd, const char *name, + unsigned int lookup_flags, struct nameidata *nd, + int open_flags, int create_mode) { struct file *filp = get_empty_filp(); int err; @@ -1108,7 +1148,7 @@ static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags nd->intent.open.file = filp; nd->intent.open.flags = open_flags; nd->intent.open.create_mode = create_mode; - err = path_lookup(name, lookup_flags|LOOKUP_OPEN, nd); + err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd); if (IS_ERR(nd->intent.open.file)) { if (err == 0) { err = PTR_ERR(nd->intent.open.file); @@ -1126,10 +1166,10 @@ static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags * @nd: pointer to nameidata * @open_flags: open intent flags */ -int path_lookup_open(const char *name, unsigned int lookup_flags, +int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags, struct nameidata *nd, int open_flags) { - return __path_lookup_intent_open(name, lookup_flags, nd, + return __path_lookup_intent_open(dfd, name, lookup_flags, nd, open_flags, 0); } @@ -1141,12 +1181,12 @@ int path_lookup_open(const char *name, unsigned int lookup_flags, * @open_flags: open intent flags * @create_mode: create intent flags */ -static int path_lookup_create(const char *name, unsigned int lookup_flags, - struct nameidata *nd, int open_flags, - int create_mode) +static int path_lookup_create(int dfd, const char *name, + unsigned int lookup_flags, struct nameidata *nd, + int open_flags, int create_mode) { - return __path_lookup_intent_open(name, lookup_flags|LOOKUP_CREATE, nd, - open_flags, create_mode); + return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE, + nd, open_flags, create_mode); } int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, @@ -1156,7 +1196,7 @@ int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, int err = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0); + err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0); putname(tmp); } return err; @@ -1248,18 +1288,24 @@ access: * that namei follows links, while lnamei does not. * SMP-safe */ -int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) +int fastcall __user_walk_fd(int dfd, const char __user *name, unsigned flags, + struct nameidata *nd) { char *tmp = getname(name); int err = PTR_ERR(tmp); if (!IS_ERR(tmp)) { - err = path_lookup(tmp, flags, nd); + err = do_path_lookup(dfd, tmp, flags, nd); putname(tmp); } return err; } +int fastcall __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) +{ + return __user_walk_fd(AT_FDCWD, name, flags, nd); +} + /* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. @@ -1518,7 +1564,8 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) * for symlinks (where the permissions are checked later). * SMP-safe */ -int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) +int open_namei(int dfd, const char *pathname, int flag, + int mode, struct nameidata *nd) { int acc_mode, error; struct path path; @@ -1540,7 +1587,8 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) * The simplest case - just a plain lookup. */ if (!(flag & O_CREAT)) { - error = path_lookup_open(pathname, lookup_flags(flag), nd, flag); + error = path_lookup_open(dfd, pathname, lookup_flags(flag), + nd, flag); if (error) return error; goto ok; @@ -1549,7 +1597,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) /* * Create - we need to know the parent. */ - error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode); + error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode); if (error) return error; @@ -1744,7 +1792,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) return error; } -asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev) +asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode, + unsigned dev) { int error = 0; char * tmp; @@ -1757,7 +1806,7 @@ asmlinkage long sys_mknod(const char __user * filename, int mode, unsigned dev) if (IS_ERR(tmp)) return PTR_ERR(tmp); - error = path_lookup(tmp, LOOKUP_PARENT, &nd); + error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); @@ -1793,6 +1842,11 @@ out: return error; } +asmlinkage long sys_mknod(const char __user *filename, int mode, unsigned dev) +{ + return sys_mknodat(AT_FDCWD, filename, mode, dev); +} + int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error = may_create(dir, dentry, NULL); @@ -1815,7 +1869,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) return error; } -asmlinkage long sys_mkdir(const char __user * pathname, int mode) +asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode) { int error = 0; char * tmp; @@ -1826,7 +1880,7 @@ asmlinkage long sys_mkdir(const char __user * pathname, int mode) struct dentry *dentry; struct nameidata nd; - error = path_lookup(tmp, LOOKUP_PARENT, &nd); + error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 1); @@ -1846,6 +1900,11 @@ out: return error; } +asmlinkage long sys_mkdir(const char __user *pathname, int mode) +{ + return sys_mkdirat(AT_FDCWD, pathname, mode); +} + /* * We try to drop the dentry early: we should have * a usage count of 2 if we're the only user of this @@ -1907,7 +1966,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) return error; } -asmlinkage long sys_rmdir(const char __user * pathname) +static long do_rmdir(int dfd, const char __user *pathname) { int error = 0; char * name; @@ -1918,7 +1977,7 @@ asmlinkage long sys_rmdir(const char __user * pathname) if(IS_ERR(name)) return PTR_ERR(name); - error = path_lookup(name, LOOKUP_PARENT, &nd); + error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd); if (error) goto exit; @@ -1948,6 +2007,11 @@ exit: return error; } +asmlinkage long sys_rmdir(const char __user *pathname) +{ + return do_rmdir(AT_FDCWD, pathname); +} + int vfs_unlink(struct inode *dir, struct dentry *dentry) { int error = may_delete(dir, dentry, 0); @@ -1984,7 +2048,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) * writeout happening, and we don't want to prevent access to the directory * while waiting on the I/O. */ -asmlinkage long sys_unlink(const char __user * pathname) +static long do_unlinkat(int dfd, const char __user *pathname) { int error = 0; char * name; @@ -1996,7 +2060,7 @@ asmlinkage long sys_unlink(const char __user * pathname) if(IS_ERR(name)) return PTR_ERR(name); - error = path_lookup(name, LOOKUP_PARENT, &nd); + error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd); if (error) goto exit; error = -EISDIR; @@ -2031,6 +2095,22 @@ slashes: goto exit2; } +asmlinkage long sys_unlinkat(int dfd, const char __user *pathname, int flag) +{ + if ((flag & ~AT_REMOVEDIR) != 0) + return -EINVAL; + + if (flag & AT_REMOVEDIR) + return do_rmdir(dfd, pathname); + + return do_unlinkat(dfd, pathname); +} + +asmlinkage long sys_unlink(const char __user *pathname) +{ + return do_unlinkat(AT_FDCWD, pathname); +} + int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode) { int error = may_create(dir, dentry, NULL); @@ -2052,7 +2132,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i return error; } -asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname) +asmlinkage long sys_symlinkat(const char __user *oldname, + int newdfd, const char __user *newname) { int error = 0; char * from; @@ -2067,7 +2148,7 @@ asmlinkage long sys_symlink(const char __user * oldname, const char __user * new struct dentry *dentry; struct nameidata nd; - error = path_lookup(to, LOOKUP_PARENT, &nd); + error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); @@ -2085,6 +2166,11 @@ out: return error; } +asmlinkage long sys_symlink(const char __user *oldname, const char __user *newname) +{ + return sys_symlinkat(oldname, AT_FDCWD, newname); +} + int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { struct inode *inode = old_dentry->d_inode; @@ -2132,7 +2218,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */ -asmlinkage long sys_link(const char __user * oldname, const char __user * newname) +asmlinkage long sys_linkat(int olddfd, const char __user *oldname, + int newdfd, const char __user *newname) { struct dentry *new_dentry; struct nameidata nd, old_nd; @@ -2143,10 +2230,10 @@ asmlinkage long sys_link(const char __user * oldname, const char __user * newnam if (IS_ERR(to)) return PTR_ERR(to); - error = __user_walk(oldname, 0, &old_nd); + error = __user_walk_fd(olddfd, oldname, 0, &old_nd); if (error) goto exit; - error = path_lookup(to, LOOKUP_PARENT, &nd); + error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); if (error) goto out; error = -EXDEV; @@ -2169,6 +2256,11 @@ exit: return error; } +asmlinkage long sys_link(const char __user *oldname, const char __user *newname) +{ + return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname); +} + /* * The worst of all namespace operations - renaming directory. "Perverted" * doesn't even start to describe it. Somebody in UCB had a heck of a trip... @@ -2315,7 +2407,8 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, return error; } -static int do_rename(const char * oldname, const char * newname) +static int do_rename(int olddfd, const char *oldname, + int newdfd, const char *newname) { int error = 0; struct dentry * old_dir, * new_dir; @@ -2323,11 +2416,11 @@ static int do_rename(const char * oldname, const char * newname) struct dentry * trap; struct nameidata oldnd, newnd; - error = path_lookup(oldname, LOOKUP_PARENT, &oldnd); + error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd); if (error) goto exit; - error = path_lookup(newname, LOOKUP_PARENT, &newnd); + error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd); if (error) goto exit1; @@ -2391,7 +2484,8 @@ exit: return error; } -asmlinkage long sys_rename(const char __user * oldname, const char __user * newname) +asmlinkage long sys_renameat(int olddfd, const char __user *oldname, + int newdfd, const char __user *newname) { int error; char * from; @@ -2403,13 +2497,18 @@ asmlinkage long sys_rename(const char __user * oldname, const char __user * newn to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { - error = do_rename(from,to); + error = do_rename(olddfd, from, newdfd, to); putname(to); } putname(from); return error; } +asmlinkage long sys_rename(const char __user *oldname, const char __user *newname) +{ + return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname); +} + int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link) { int len; @@ -2553,6 +2652,7 @@ struct inode_operations page_symlink_inode_operations = { }; EXPORT_SYMBOL(__user_walk); +EXPORT_SYMBOL(__user_walk_fd); EXPORT_SYMBOL(follow_down); EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ |