X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fnamei.c;h=e28de846c5919f84ba0f45ea464a4ee91913f7a9;hb=8854eddbdb3e45b8d381ecff2937a942d0cb2067;hp=33fb5bd34a8111c73768cfac883fd4ea49ea07b8;hpb=d65177c1ae7f085723154105c5dc8d9e16ae8265;p=pandora-kernel.git diff --git a/fs/namei.c b/fs/namei.c index 33fb5bd34a81..e28de846c591 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -788,7 +790,7 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) inode = nd->dentry->d_inode; if (nd->depth) - lookup_flags = LOOKUP_FOLLOW; + lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE); /* At this point we know we have a real path component. */ for(;;) { @@ -883,7 +885,8 @@ static fastcall int __link_path_walk(const char * name, struct nameidata *nd) last_with_slashes: lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; last_component: - nd->flags &= ~LOOKUP_CONTINUE; + /* Clear LOOKUP_CONTINUE iff it was previously unset */ + nd->flags &= lookup_flags | ~LOOKUP_CONTINUE; if (lookup_flags & LOOKUP_PARENT) goto lookup_parent; if (this.name[0] == '.') switch (this.len) { @@ -1063,9 +1066,12 @@ 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; + int fput_needed; + struct file *file; nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags; @@ -1083,22 +1089,59 @@ 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 dentry *dentry; + + file = fget_light(dfd, &fput_needed); + retval = -EBADF; + if (!file) + goto unlock_fail; + + dentry = file->f_dentry; + + retval = -ENOTDIR; + if (!S_ISDIR(dentry->d_inode->i_mode)) + goto fput_unlock_fail; + + retval = file_permission(file, MAY_EXEC); + if (retval) + goto fput_unlock_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; retval = link_path_walk(name, nd); out: - if (unlikely(current->audit_context - && nd && nd->dentry && nd->dentry->d_inode)) + if (likely(retval == 0)) { + if (unlikely(current->audit_context && nd && nd->dentry && + nd->dentry->d_inode)) audit_inode(name, nd->dentry->d_inode, flags); + } + return retval; + +fput_unlock_fail: + fput_light(file, fput_needed); +unlock_fail: + read_unlock(¤t->fs->lock); 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 +1151,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); @@ -1121,32 +1164,34 @@ static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags /** * path_lookup_open - lookup a file path with open intent + * @dfd: the directory to use as base, or AT_FDCWD * @name: pointer to file name * @lookup_flags: lookup intent 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); } /** * path_lookup_create - lookup a file path with open + create intent + * @dfd: the directory to use as base, or AT_FDCWD * @name: pointer to file name * @lookup_flags: lookup intent flags * @nd: pointer to nameidata * @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 +1201,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 +1293,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 +1569,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 +1592,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 +1602,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 +1797,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 +1811,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 +1847,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 +1874,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 +1885,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 +1905,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 +1971,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 +1982,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 +2012,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 +2053,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 +2065,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 +2100,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 +2137,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 +2153,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 +2171,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 +2223,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 +2235,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 +2261,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 +2412,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 +2421,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 +2489,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 +2502,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 +2657,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 */