Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[sfrench/cifs-2.6.git] / fs / namei.c
index a6575ca9f9d77eb49e2d14fbe27068cd1a6e70f0..e179f71bfcb058df613f83cb1ff923d47ffc4618 100644 (file)
  * any extra contention...
  */
 
-static int link_path_walk(const char *name, struct nameidata *nd);
+static int __link_path_walk(const char *name, struct nameidata *nd);
 
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
@@ -549,20 +549,51 @@ walk_init_root(const char *name, struct nameidata *nd)
        struct fs_struct *fs = current->fs;
 
        read_lock(&fs->lock);
-       if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-               nd->path.mnt = mntget(fs->altrootmnt);
-               nd->path.dentry = dget(fs->altroot);
+       if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
+               nd->path = fs->altroot;
+               path_get(&fs->altroot);
                read_unlock(&fs->lock);
                if (__emul_lookup_dentry(name,nd))
                        return 0;
                read_lock(&fs->lock);
        }
-       nd->path.mnt = mntget(fs->rootmnt);
-       nd->path.dentry = dget(fs->root);
+       nd->path = fs->root;
+       path_get(&fs->root);
        read_unlock(&fs->lock);
        return 1;
 }
 
+/*
+ * Wrapper to retry pathname resolution whenever the underlying
+ * file system returns an ESTALE.
+ *
+ * Retry the whole path once, forcing real lookup requests
+ * instead of relying on the dcache.
+ */
+static __always_inline int link_path_walk(const char *name, struct nameidata *nd)
+{
+       struct path save = nd->path;
+       int result;
+
+       /* make sure the stuff we saved doesn't go away */
+       dget(save.dentry);
+       mntget(save.mnt);
+
+       result = __link_path_walk(name, nd);
+       if (result == -ESTALE) {
+               /* nd->path had been dropped */
+               nd->path = save;
+               dget(nd->path.dentry);
+               mntget(nd->path.mnt);
+               nd->flags |= LOOKUP_REVAL;
+               result = __link_path_walk(name, nd);
+       }
+
+       path_put(&save);
+
+       return result;
+}
+
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
        int res = 0;
@@ -755,8 +786,8 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
                struct dentry *old = nd->path.dentry;
 
                 read_lock(&fs->lock);
-               if (nd->path.dentry == fs->root &&
-                   nd->path.mnt == fs->rootmnt) {
+               if (nd->path.dentry == fs->root.dentry &&
+                   nd->path.mnt == fs->root.mnt) {
                         read_unlock(&fs->lock);
                        break;
                }
@@ -1020,36 +1051,6 @@ return_err:
        return err;
 }
 
-/*
- * Wrapper to retry pathname resolution whenever the underlying
- * file system returns an ESTALE.
- *
- * Retry the whole path once, forcing real lookup requests
- * instead of relying on the dcache.
- */
-static int link_path_walk(const char *name, struct nameidata *nd)
-{
-       struct nameidata save = *nd;
-       int result;
-
-       /* make sure the stuff we saved doesn't go away */
-       dget(save.path.dentry);
-       mntget(save.path.mnt);
-
-       result = __link_path_walk(name, nd);
-       if (result == -ESTALE) {
-               *nd = save;
-               dget(nd->path.dentry);
-               mntget(nd->path.mnt);
-               nd->flags |= LOOKUP_REVAL;
-               result = __link_path_walk(name, nd);
-       }
-
-       path_put(&save.path);
-
-       return result;
-}
-
 static int path_walk(const char *name, struct nameidata *nd)
 {
        current->total_link_count = 0;
@@ -1078,8 +1079,8 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
                 */
                nd->last_type = LAST_ROOT;
                read_lock(&fs->lock);
-               nd->path.mnt = mntget(fs->rootmnt);
-               nd->path.dentry = dget(fs->root);
+               nd->path = fs->root;
+               path_get(&fs->root);
                read_unlock(&fs->lock);
                if (path_walk(name, nd) == 0) {
                        if (nd->path.dentry->d_inode) {
@@ -1099,29 +1100,22 @@ void set_fs_altroot(void)
 {
        char *emul = __emul_prefix();
        struct nameidata nd;
-       struct vfsmount *mnt = NULL, *oldmnt;
-       struct dentry *dentry = NULL, *olddentry;
+       struct path path = {}, old_path;
        int err;
        struct fs_struct *fs = current->fs;
 
        if (!emul)
                goto set_it;
        err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
-       if (!err) {
-               mnt = nd.path.mnt;
-               dentry = nd.path.dentry;
-       }
+       if (!err)
+               path = nd.path;
 set_it:
        write_lock(&fs->lock);
-       oldmnt = fs->altrootmnt;
-       olddentry = fs->altroot;
-       fs->altrootmnt = mnt;
-       fs->altroot = dentry;
+       old_path = fs->altroot;
+       fs->altroot = path;
        write_unlock(&fs->lock);
-       if (olddentry) {
-               dput(olddentry);
-               mntput(oldmnt);
-       }
+       if (old_path.dentry)
+               path_put(&old_path);
 }
 
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
@@ -1139,21 +1133,21 @@ static int do_path_lookup(int dfd, const char *name,
 
        if (*name=='/') {
                read_lock(&fs->lock);
-               if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
-                       nd->path.mnt = mntget(fs->altrootmnt);
-                       nd->path.dentry = dget(fs->altroot);
+               if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
+                       nd->path = fs->altroot;
+                       path_get(&fs->altroot);
                        read_unlock(&fs->lock);
                        if (__emul_lookup_dentry(name,nd))
                                goto out; /* found in altroot */
                        read_lock(&fs->lock);
                }
-               nd->path.mnt = mntget(fs->rootmnt);
-               nd->path.dentry = dget(fs->root);
+               nd->path = fs->root;
+               path_get(&fs->root);
                read_unlock(&fs->lock);
        } else if (dfd == AT_FDCWD) {
                read_lock(&fs->lock);
-               nd->path.mnt = mntget(fs->pwdmnt);
-               nd->path.dentry = dget(fs->pwd);
+               nd->path = fs->pwd;
+               path_get(&fs->pwd);
                read_unlock(&fs->lock);
        } else {
                struct dentry *dentry;
@@ -1371,13 +1365,13 @@ static int __lookup_one_len(const char *name, struct qstr *this,
 }
 
 /**
- * lookup_one_len filesystem helper to lookup single pathname component
+ * 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 useage and should
- * not be called by generic code.  Also note that by using this function to
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.  Also note that by using this function the
  * nameidata argument is passed to the filesystem methods and a filesystem
  * using this helper needs to be prepared for that.
  */
@@ -1629,8 +1623,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                        return -EACCES;
 
                flag &= ~O_TRUNC;
-       } else if (IS_RDONLY(inode) && (acc_mode & MAY_WRITE))
-               return -EROFS;
+       }
 
        error = vfs_permission(nd, acc_mode);
        if (error)
@@ -1683,7 +1676,12 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
        return 0;
 }
 
-static int open_namei_create(struct nameidata *nd, struct path *path,
+/*
+ * Be careful about ever adding any more callers of this
+ * function.  Its flags must be in the namei format, not
+ * what get passed to sys_open().
+ */
+static int __open_namei_create(struct nameidata *nd, struct path *path,
                                int flag, int mode)
 {
        int error;
@@ -1702,26 +1700,56 @@ static int open_namei_create(struct nameidata *nd, struct path *path,
 }
 
 /*
- *     open_namei()
- *
- * namei for open - this is in fact almost the whole open-routine.
+ * Note that while the flag value (low two bits) for sys_open means:
+ *     00 - read-only
+ *     01 - write-only
+ *     10 - read-write
+ *     11 - special
+ * it is changed into
+ *     00 - no permissions needed
+ *     01 - read-permission
+ *     10 - write-permission
+ *     11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc)
+ * This is more logical, and also allows the 00 "no perm needed"
+ * to be used for symlinks (where the permissions are checked
+ * later).
  *
- * Note that the low bits of "flag" aren't the same as in the open
- * system call - they are 00 - no permissions needed
- *                       01 - read permission needed
- *                       10 - write permission needed
- *                       11 - read/write permissions needed
- * which is a lot more logical, and also allows the "no perm" needed
- * for symlinks (where the permissions are checked later).
- * SMP-safe
+*/
+static inline int open_to_namei_flags(int flag)
+{
+       if ((flag+1) & O_ACCMODE)
+               flag++;
+       return flag;
+}
+
+static int open_will_write_to_fs(int flag, struct inode *inode)
+{
+       /*
+        * We'll never write to the fs underlying
+        * a device file.
+        */
+       if (special_file(inode->i_mode))
+               return 0;
+       return (flag & O_TRUNC);
+}
+
+/*
+ * Note that the low bits of the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
  */
-int open_namei(int dfd, const char *pathname, int flag,
-               int mode, struct nameidata *nd)
+struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode)
 {
+       struct file *filp;
+       struct nameidata nd;
        int acc_mode, error;
        struct path path;
        struct dentry *dir;
        int count = 0;
+       int will_write;
+       int flag = open_to_namei_flags(open_flag);
 
        acc_mode = ACC_MODE(flag);
 
@@ -1739,18 +1767,19 @@ int open_namei(int dfd, const char *pathname, int flag,
         */
        if (!(flag & O_CREAT)) {
                error = path_lookup_open(dfd, pathname, lookup_flags(flag),
-                                        nd, flag);
+                                        &nd, flag);
                if (error)
-                       return error;
+                       return ERR_PTR(error);
                goto ok;
        }
 
        /*
         * Create - we need to know the parent.
         */
-       error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
+       error = path_lookup_create(dfd, pathname, LOOKUP_PARENT,
+                                  &nd, flag, mode);
        if (error)
-               return error;
+               return ERR_PTR(error);
 
        /*
         * We have the parent and last component. First of all, check
@@ -1758,14 +1787,14 @@ int open_namei(int dfd, const char *pathname, int flag,
         * will not do.
         */
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
+       if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
                goto exit;
 
-       dir = nd->path.dentry;
-       nd->flags &= ~LOOKUP_PARENT;
+       dir = nd.path.dentry;
+       nd.flags &= ~LOOKUP_PARENT;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->path.mnt;
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
 
 do_last:
        error = PTR_ERR(path.dentry);
@@ -1774,18 +1803,31 @@ do_last:
                goto exit;
        }
 
-       if (IS_ERR(nd->intent.open.file)) {
-               mutex_unlock(&dir->d_inode->i_mutex);
-               error = PTR_ERR(nd->intent.open.file);
-               goto exit_dput;
+       if (IS_ERR(nd.intent.open.file)) {
+               error = PTR_ERR(nd.intent.open.file);
+               goto exit_mutex_unlock;
        }
 
        /* Negative dentry, just create the file */
        if (!path.dentry->d_inode) {
-               error = open_namei_create(nd, &path, flag, mode);
+               /*
+                * This write is needed to ensure that a
+                * ro->rw transition does not occur between
+                * the time when the file is created and when
+                * a permanent write count is taken through
+                * the 'struct file' in nameidata_to_filp().
+                */
+               error = mnt_want_write(nd.path.mnt);
                if (error)
+                       goto exit_mutex_unlock;
+               error = __open_namei_create(&nd, &path, flag, mode);
+               if (error) {
+                       mnt_drop_write(nd.path.mnt);
                        goto exit;
-               return 0;
+               }
+               filp = nameidata_to_filp(&nd, open_flag);
+               mnt_drop_write(nd.path.mnt);
+               return filp;
        }
 
        /*
@@ -1810,23 +1852,52 @@ do_last:
        if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
                goto do_link;
 
-       path_to_nameidata(&path, nd);
+       path_to_nameidata(&path, &nd);
        error = -EISDIR;
        if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
                goto exit;
 ok:
-       error = may_open(nd, acc_mode, flag);
-       if (error)
+       /*
+        * Consider:
+        * 1. may_open() truncates a file
+        * 2. a rw->ro mount transition occurs
+        * 3. nameidata_to_filp() fails due to
+        *    the ro mount.
+        * That would be inconsistent, and should
+        * be avoided. Taking this mnt write here
+        * ensures that (2) can not occur.
+        */
+       will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);
+       if (will_write) {
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit;
+       }
+       error = may_open(&nd, acc_mode, flag);
+       if (error) {
+               if (will_write)
+                       mnt_drop_write(nd.path.mnt);
                goto exit;
-       return 0;
+       }
+       filp = nameidata_to_filp(&nd, open_flag);
+       /*
+        * It is now safe to drop the mnt write
+        * because the filp has had a write taken
+        * on its behalf.
+        */
+       if (will_write)
+               mnt_drop_write(nd.path.mnt);
+       return filp;
 
+exit_mutex_unlock:
+       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
-       path_put_conditional(&path, nd);
+       path_put_conditional(&path, &nd);
 exit:
-       if (!IS_ERR(nd->intent.open.file))
-               release_open_intent(nd);
-       path_put(&nd->path);
-       return error;
+       if (!IS_ERR(nd.intent.open.file))
+               release_open_intent(&nd);
+       path_put(&nd.path);
+       return ERR_PTR(error);
 
 do_link:
        error = -ELOOP;
@@ -1842,42 +1913,59 @@ do_link:
         * stored in nd->last.name and we will have to putname() it when we
         * are done. Procfs-like symlinks just set LAST_BIND.
         */
-       nd->flags |= LOOKUP_PARENT;
-       error = security_inode_follow_link(path.dentry, nd);
+       nd.flags |= LOOKUP_PARENT;
+       error = security_inode_follow_link(path.dentry, &nd);
        if (error)
                goto exit_dput;
-       error = __do_follow_link(&path, nd);
+       error = __do_follow_link(&path, &nd);
        if (error) {
                /* Does someone understand code flow here? Or it is only
                 * me so stupid? Anathema to whoever designed this non-sense
                 * with "intent.open".
                 */
-               release_open_intent(nd);
-               return error;
+               release_open_intent(&nd);
+               return ERR_PTR(error);
        }
-       nd->flags &= ~LOOKUP_PARENT;
-       if (nd->last_type == LAST_BIND)
+       nd.flags &= ~LOOKUP_PARENT;
+       if (nd.last_type == LAST_BIND)
                goto ok;
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM)
+       if (nd.last_type != LAST_NORM)
                goto exit;
-       if (nd->last.name[nd->last.len]) {
-               __putname(nd->last.name);
+       if (nd.last.name[nd.last.len]) {
+               __putname(nd.last.name);
                goto exit;
        }
        error = -ELOOP;
        if (count++==32) {
-               __putname(nd->last.name);
+               __putname(nd.last.name);
                goto exit;
        }
-       dir = nd->path.dentry;
+       dir = nd.path.dentry;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->path.mnt;
-       __putname(nd->last.name);
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
+       __putname(nd.last.name);
        goto do_last;
 }
 
+/**
+ * filp_open - open file and return file pointer
+ *
+ * @filename:  path to open
+ * @flags:     open flags as per the open(2) second argument
+ * @mode:      mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open(const char *filename, int flags, int mode)
+{
+       return do_filp_open(AT_FDCWD, filename, flags, mode);
+}
+EXPORT_SYMBOL(filp_open);
+
 /**
  * lookup_create - lookup a dentry, creating it if it doesn't exist
  * @nd: nameidata info
@@ -1951,6 +2039,23 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        return error;
 }
 
+static int may_mknod(mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFCHR:
+       case S_IFBLK:
+       case S_IFIFO:
+       case S_IFSOCK:
+       case 0: /* zero mode translates to S_IFREG */
+               return 0;
+       case S_IFDIR:
+               return -EPERM;
+       default:
+               return -EINVAL;
+       }
+}
+
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                                unsigned dev)
 {
@@ -1969,12 +2074,19 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
        if (error)
                goto out;
        dentry = lookup_create(&nd, 0);
-       error = PTR_ERR(dentry);
-
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
+               goto out_unlock;
+       }
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
                mode &= ~current->fs->umask;
-       if (!IS_ERR(dentry)) {
-               switch (mode & S_IFMT) {
+       error = may_mknod(mode);
+       if (error)
+               goto out_dput;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       switch (mode & S_IFMT) {
                case 0: case S_IFREG:
                        error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
                        break;
@@ -1985,14 +2097,11 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                case S_IFIFO: case S_IFSOCK:
                        error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
                        break;
-               case S_IFDIR:
-                       error = -EPERM;
-                       break;
-               default:
-                       error = -EINVAL;
-               }
-               dput(dentry);
        }
+       mnt_drop_write(nd.path.mnt);
+out_dput:
+       dput(dentry);
+out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
 out:
@@ -2050,7 +2159,12 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
 
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
                mode &= ~current->fs->umask;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2157,7 +2271,12 @@ static long do_rmdir(int dfd, const char __user *pathname)
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit2;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit3;
        error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+       mnt_drop_write(nd.path.mnt);
+exit3:
        dput(dentry);
 exit2:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2238,7 +2357,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                inode = dentry->d_inode;
                if (inode)
                        atomic_inc(&inode->i_count);
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit2;
                error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+               mnt_drop_write(nd.path.mnt);
        exit2:
                dput(dentry);
        }
@@ -2319,7 +2442,12 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        if (IS_ERR(dentry))
                goto out_unlock;
 
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2414,7 +2542,12 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
                goto out_unlock;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(new_dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2640,8 +2773,12 @@ static int do_rename(int olddfd, const char *oldname,
        if (new_dentry == trap)
                goto exit5;
 
+       error = mnt_want_write(oldnd.path.mnt);
+       if (error)
+               goto exit5;
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
+       mnt_drop_write(oldnd.path.mnt);
 exit5:
        dput(new_dentry);
 exit4: