[PATCH] vfs: *at functions: core
[linux-2.6.git] / fs / namei.c
index 33fb5bd..4acdac0 100644 (file)
@@ -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(&current->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 */