don't bugger nd->seq on set_root_rcu() from follow_dotdot_rcu()
[pandora-kernel.git] / fs / namei.c
index b52bc68..4945ef6 100644 (file)
@@ -137,7 +137,7 @@ static int do_getname(const char __user *filename, char *page)
        return retval;
 }
 
-static char *getname_flags(const char __user * filename, int flags)
+static char *getname_flags(const char __user *filename, int flags, int *empty)
 {
        char *tmp, *result;
 
@@ -148,6 +148,8 @@ static char *getname_flags(const char __user * filename, int flags)
 
                result = tmp;
                if (retval < 0) {
+                       if (retval == -ENOENT && empty)
+                               *empty = 1;
                        if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
                                __putname(tmp);
                                result = ERR_PTR(retval);
@@ -160,7 +162,7 @@ static char *getname_flags(const char __user * filename, int flags)
 
 char *getname(const char __user * filename)
 {
-       return getname_flags(filename, 0);
+       return getname_flags(filename, 0, 0);
 }
 
 #ifdef CONFIG_AUDITSYSCALL
@@ -221,14 +223,12 @@ static int check_acl(struct inode *inode, int mask)
 }
 
 /*
- * This does basic POSIX ACL permission checking
+ * This does the basic permission checking
  */
 static int acl_permission_check(struct inode *inode, int mask)
 {
        unsigned int mode = inode->i_mode;
 
-       mask &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
-
        if (current_user_ns() != inode_userns(inode))
                goto other_perms;
 
@@ -257,7 +257,7 @@ other_perms:
 /**
  * generic_permission -  check for access rights on a Posix-like filesystem
  * @inode:     inode to check access rights for
- * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)
  *
  * Used to check for read/write/execute permissions on a file.
  * We use "fsuid" for this, letting us set arbitrary permissions
@@ -273,7 +273,7 @@ int generic_permission(struct inode *inode, int mask)
        int ret;
 
        /*
-        * Do the basic POSIX ACL permission checks.
+        * Do the basic permission checks.
         */
        ret = acl_permission_check(inode, mask);
        if (ret != -EACCES)
@@ -331,12 +331,14 @@ static inline int do_inode_permission(struct inode *inode, int mask)
 /**
  * inode_permission  -  check for access rights to a given inode
  * @inode:     inode to check permission on
- * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ * @mask:      right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC, ...)
  *
  * Used to check for read/write/execute permissions on an inode.
  * We use "fsuid" for this, letting us set arbitrary permissions
  * for filesystem access without changing the "normal" uids which
  * are used for other things.
+ *
+ * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
  */
 int inode_permission(struct inode *inode, int mask)
 {
@@ -552,24 +554,22 @@ static int complete_walk(struct nameidata *nd)
 
 static __always_inline void set_root(struct nameidata *nd)
 {
-       if (!nd->root.mnt)
-               get_fs_root(current->fs, &nd->root);
+       get_fs_root(current->fs, &nd->root);
 }
 
 static int link_path_walk(const char *, struct nameidata *);
 
-static __always_inline void set_root_rcu(struct nameidata *nd)
+static __always_inline unsigned set_root_rcu(struct nameidata *nd)
 {
-       if (!nd->root.mnt) {
-               struct fs_struct *fs = current->fs;
-               unsigned seq;
+       struct fs_struct *fs = current->fs;
+       unsigned seq, res;
 
-               do {
-                       seq = read_seqcount_begin(&fs->seq);
-                       nd->root = fs->root;
-                       nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
-               } while (read_seqcount_retry(&fs->seq, seq));
-       }
+       do {
+               seq = read_seqcount_begin(&fs->seq);
+               nd->root = fs->root;
+               res = __read_seqcount_begin(&nd->root.dentry->d_seq);
+       } while (read_seqcount_retry(&fs->seq, seq));
+       return res;
 }
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
@@ -580,7 +580,8 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                goto fail;
 
        if (*link == '/') {
-               set_root(nd);
+               if (!nd->root.mnt)
+                       set_root(nd);
                path_put(&nd->path);
                nd->path = nd->root;
                path_get(&nd->root);
@@ -721,12 +722,6 @@ static int follow_automount(struct path *path, unsigned flags,
        if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
                return -EREMOTE;
 
-       /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT
-        * and this is the terminal part of the path.
-        */
-       if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_PARENT))
-               return -EISDIR; /* we actually want to stop here */
-
        /* We don't want to mount if someone's just doing a stat -
         * unless they're stat'ing a directory and appended a '/' to
         * the name.
@@ -739,7 +734,7 @@ static int follow_automount(struct path *path, unsigned flags,
         * of the daemon to instantiate them before they can be used.
         */
        if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
-                    LOOKUP_OPEN | LOOKUP_CREATE)) &&
+                    LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
            path->dentry->d_inode)
                return -EISDIR;
 
@@ -856,7 +851,7 @@ static int follow_managed(struct path *path, unsigned flags)
                mntput(path->mnt);
        if (ret == -EISDIR)
                ret = 0;
-       return ret;
+       return ret < 0 ? ret : need_mntput;
 }
 
 int follow_down_one(struct path *path)
@@ -904,6 +899,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                        break;
                path->mnt = mounted;
                path->dentry = mounted->mnt_root;
+               nd->flags |= LOOKUP_JUMPED;
                nd->seq = read_seqcount_begin(&path->dentry->d_seq);
                /*
                 * Update the inode too. We don't need to re-check the
@@ -930,7 +926,8 @@ static void follow_mount_rcu(struct nameidata *nd)
 
 static int follow_dotdot_rcu(struct nameidata *nd)
 {
-       set_root_rcu(nd);
+       if (!nd->root.mnt)
+               set_root_rcu(nd);
 
        while (1) {
                if (nd->path.dentry == nd->root.dentry &&
@@ -1033,7 +1030,8 @@ static void follow_mount(struct path *path)
 
 static void follow_dotdot(struct nameidata *nd)
 {
-       set_root(nd);
+       if (!nd->root.mnt)
+               set_root(nd);
 
        while(1) {
                struct dentry *old = nd->path.dentry;
@@ -1097,8 +1095,10 @@ static struct dentry *d_inode_lookup(struct dentry *parent, struct dentry *dentr
        struct dentry *old;
 
        /* Don't create child dentry for a dead directory. */
-       if (unlikely(IS_DEADDIR(inode)))
+       if (unlikely(IS_DEADDIR(inode))) {
+               dput(dentry);
                return ERR_PTR(-ENOENT);
+       }
 
        old = inode->i_op->lookup(inode, dentry, nd);
        if (unlikely(old)) {
@@ -1217,6 +1217,8 @@ retry:
                path_put_conditional(path, nd);
                return err;
        }
+       if (err)
+               nd->flags |= LOOKUP_JUMPED;
        *inode = path->dentry->d_inode;
        return 0;
 }
@@ -1503,7 +1505,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
                if (flags & LOOKUP_RCU) {
                        br_read_lock(vfsmount_lock);
                        rcu_read_lock();
-                       set_root_rcu(nd);
+                       nd->seq = set_root_rcu(nd);
                } else {
                        set_root(nd);
                        path_get(&nd->root);
@@ -1804,11 +1806,11 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        return __lookup_hash(&this, base, NULL);
 }
 
-int user_path_at(int dfd, const char __user *name, unsigned flags,
-                struct path *path)
+int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
+                struct path *path, int *empty)
 {
        struct nameidata nd;
-       char *tmp = getname_flags(name, flags);
+       char *tmp = getname_flags(name, flags, empty);
        int err = PTR_ERR(tmp);
        if (!IS_ERR(tmp)) {
 
@@ -1822,6 +1824,12 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,
        return err;
 }
 
+int user_path_at(int dfd, const char __user *name, unsigned flags,
+                struct path *path)
+{
+       return user_path_at_empty(dfd, name, flags, path, 0);
+}
+
 static int user_path_parent(int dfd, const char __user *path,
                        struct nameidata *nd, char **name)
 {
@@ -2041,10 +2049,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
        if (flag & O_NOATIME && !inode_owner_or_capable(inode))
                return -EPERM;
 
-       /*
-        * Ensure there are no outstanding leases on the file.
-        */
-       return break_lease(inode, flag);
+       return 0;
 }
 
 static int handle_truncate(struct file *filp)
@@ -2135,7 +2140,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                /* sayonara */
                error = complete_walk(nd);
                if (error)
-                       return ERR_PTR(-ECHILD);
+                       return ERR_PTR(error);
 
                error = -ENOTDIR;
                if (nd->flags & LOOKUP_DIRECTORY) {
@@ -2147,6 +2152,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        }
 
        /* create side of things */
+       /*
+        * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED has been
+        * cleared when we got to the last component we are about to look up
+        */
        error = complete_walk(nd);
        if (error)
                return ERR_PTR(error);
@@ -2215,6 +2224,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (error < 0)
                goto exit_dput;
 
+       if (error)
+               nd->flags |= LOOKUP_JUMPED;
+
        error = -ENOENT;
        if (!path->dentry->d_inode)
                goto exit_dput;
@@ -2224,6 +2236,10 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 
        path_to_nameidata(path, nd);
        nd->inode = path->dentry->d_inode;
+       /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
+       error = complete_walk(nd);
+       if (error)
+               return ERR_PTR(error);
        error = -EISDIR;
        if (S_ISDIR(nd->inode->i_mode))
                goto exit;
@@ -2616,6 +2632,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->rmdir)
                return -EPERM;
 
+       dget(dentry);
        mutex_lock(&dentry->d_inode->i_mutex);
 
        error = -EBUSY;
@@ -2636,6 +2653,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 
 out:
        mutex_unlock(&dentry->d_inode->i_mutex);
+       dput(dentry);
        if (!error)
                d_delete(dentry);
        return error;
@@ -3025,6 +3043,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
        if (error)
                return error;
 
+       dget(new_dentry);
        if (target)
                mutex_lock(&target->i_mutex);
 
@@ -3045,6 +3064,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
 out:
        if (target)
                mutex_unlock(&target->i_mutex);
+       dput(new_dentry);
        if (!error)
                if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry,new_dentry);