]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - fs/namei.c
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
[net-next-2.6.git] / fs / namei.c
index 868d0cb9d473a92ccdcbe640ac44f4daf24cce62..5362af9b73729905817a76b5c24a5b33c48d5ce7 100644 (file)
@@ -282,8 +282,7 @@ int inode_permission(struct inode *inode, int mask)
        if (retval)
                return retval;
 
-       return security_inode_permission(inode,
-                       mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
+       return security_inode_permission(inode, mask);
 }
 
 /**
@@ -484,13 +483,8 @@ ok:
 
 static __always_inline void set_root(struct nameidata *nd)
 {
-       if (!nd->root.mnt) {
-               struct fs_struct *fs = current->fs;
-               read_lock(&fs->lock);
-               nd->root = fs->root;
-               path_get(&nd->root);
-               read_unlock(&fs->lock);
-       }
+       if (!nd->root.mnt)
+               get_fs_root(current->fs, &nd->root);
 }
 
 static int link_path_walk(const char *, struct nameidata *);
@@ -601,15 +595,16 @@ int follow_up(struct path *path)
 {
        struct vfsmount *parent;
        struct dentry *mountpoint;
-       spin_lock(&vfsmount_lock);
+
+       br_read_lock(vfsmount_lock);
        parent = path->mnt->mnt_parent;
        if (parent == path->mnt) {
-               spin_unlock(&vfsmount_lock);
+               br_read_unlock(vfsmount_lock);
                return 0;
        }
        mntget(parent);
        mountpoint = dget(path->mnt->mnt_mountpoint);
-       spin_unlock(&vfsmount_lock);
+       br_read_unlock(vfsmount_lock);
        dput(path->dentry);
        path->dentry = mountpoint;
        mntput(path->mnt);
@@ -691,6 +686,35 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
        follow_mount(&nd->path);
 }
 
+/*
+ * Allocate a dentry with name and parent, and perform a parent
+ * directory ->lookup on it. Returns the new dentry, or ERR_PTR
+ * on error. parent->d_inode->i_mutex must be held. d_lookup must
+ * have verified that no child exists while under i_mutex.
+ */
+static struct dentry *d_alloc_and_lookup(struct dentry *parent,
+                               struct qstr *name, struct nameidata *nd)
+{
+       struct inode *inode = parent->d_inode;
+       struct dentry *dentry;
+       struct dentry *old;
+
+       /* Don't create child dentry for a dead directory. */
+       if (unlikely(IS_DEADDIR(inode)))
+               return ERR_PTR(-ENOENT);
+
+       dentry = d_alloc(parent, name);
+       if (unlikely(!dentry))
+               return ERR_PTR(-ENOMEM);
+
+       old = inode->i_op->lookup(inode, dentry, nd);
+       if (unlikely(old)) {
+               dput(dentry);
+               dentry = old;
+       }
+       return dentry;
+}
+
 /*
  *  It's more convoluted than I'd like it to be, but... it's still fairly
  *  small and for now I'd prefer to have fast path as straight as possible.
@@ -712,9 +736,15 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
                        return err;
        }
 
+       /*
+        * Rename seqlock is not required here because in the off chance
+        * of a false negative due to a concurrent rename, we're going to
+        * do the non-racy lookup, below.
+        */
        dentry = __d_lookup(nd->path.dentry, name);
        if (!dentry)
                goto need_lookup;
+found:
        if (dentry->d_op && dentry->d_op->d_revalidate)
                goto need_revalidate;
 done:
@@ -730,56 +760,28 @@ need_lookup:
        mutex_lock(&dir->i_mutex);
        /*
         * First re-do the cached lookup just in case it was created
-        * while we waited for the directory semaphore..
+        * while we waited for the directory semaphore, or the first
+        * lookup failed due to an unrelated rename.
         *
-        * FIXME! This could use version numbering or similar to
-        * avoid unnecessary cache lookups.
-        *
-        * The "dcache_lock" is purely to protect the RCU list walker
-        * from concurrent renames at this point (we mustn't get false
-        * negatives from the RCU list walk here, unlike the optimistic
-        * fast walk).
-        *
-        * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
+        * This could use version numbering or similar to avoid unnecessary
+        * cache lookups, but then we'd have to do the first lookup in the
+        * non-racy way. However in the common case here, everything should
+        * be hot in cache, so would it be a big win?
         */
        dentry = d_lookup(parent, name);
-       if (!dentry) {
-               struct dentry *new;
-
-               /* Don't create child dentry for a dead directory. */
-               dentry = ERR_PTR(-ENOENT);
-               if (IS_DEADDIR(dir))
-                       goto out_unlock;
-
-               new = d_alloc(parent, name);
-               dentry = ERR_PTR(-ENOMEM);
-               if (new) {
-                       dentry = dir->i_op->lookup(dir, new, nd);
-                       if (dentry)
-                               dput(new);
-                       else
-                               dentry = new;
-               }
-out_unlock:
+       if (likely(!dentry)) {
+               dentry = d_alloc_and_lookup(parent, name, nd);
                mutex_unlock(&dir->i_mutex);
                if (IS_ERR(dentry))
                        goto fail;
                goto done;
        }
-
        /*
         * Uhhuh! Nasty case: the cache was re-populated while
         * we waited on the semaphore. Need to revalidate.
         */
        mutex_unlock(&dir->i_mutex);
-       if (dentry->d_op && dentry->d_op->d_revalidate) {
-               dentry = do_revalidate(dentry, nd);
-               if (!dentry)
-                       dentry = ERR_PTR(-ENOENT);
-       }
-       if (IS_ERR(dentry))
-               goto fail;
-       goto done;
+       goto found;
 
 need_revalidate:
        dentry = do_revalidate(dentry, nd);
@@ -1016,11 +1018,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
                nd->path = nd->root;
                path_get(&nd->root);
        } else if (dfd == AT_FDCWD) {
-               struct fs_struct *fs = current->fs;
-               read_lock(&fs->lock);
-               nd->path = fs->pwd;
-               path_get(&fs->pwd);
-               read_unlock(&fs->lock);
+               get_fs_pwd(current->fs, &nd->path);
        } else {
                struct dentry *dentry;
 
@@ -1123,11 +1121,13 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
 static struct dentry *__lookup_hash(struct qstr *name,
                struct dentry *base, struct nameidata *nd)
 {
+       struct inode *inode = base->d_inode;
        struct dentry *dentry;
-       struct inode *inode;
        int err;
 
-       inode = base->d_inode;
+       err = exec_permission(inode);
+       if (err)
+               return ERR_PTR(err);
 
        /*
         * See if the low-level filesystem might want
@@ -1140,35 +1140,18 @@ static struct dentry *__lookup_hash(struct qstr *name,
                        goto out;
        }
 
-       dentry = __d_lookup(base, name);
-
-       /* lockess __d_lookup may fail due to concurrent d_move()
-        * in some unrelated directory, so try with d_lookup
+       /*
+        * Don't bother with __d_lookup: callers are for creat as
+        * well as unlink, so a lot of the time it would cost
+        * a double lookup.
         */
-       if (!dentry)
-               dentry = d_lookup(base, name);
+       dentry = d_lookup(base, name);
 
        if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
                dentry = do_revalidate(dentry, nd);
 
-       if (!dentry) {
-               struct dentry *new;
-
-               /* Don't create child dentry for a dead directory. */
-               dentry = ERR_PTR(-ENOENT);
-               if (IS_DEADDIR(inode))
-                       goto out;
-
-               new = d_alloc(base, name);
-               dentry = ERR_PTR(-ENOMEM);
-               if (!new)
-                       goto out;
-               dentry = inode->i_op->lookup(inode, new, nd);
-               if (!dentry)
-                       dentry = new;
-               else
-                       dput(new);
-       }
+       if (!dentry)
+               dentry = d_alloc_and_lookup(base, name, nd);
 out:
        return dentry;
 }
@@ -1180,11 +1163,6 @@ out:
  */
 static struct dentry *lookup_hash(struct nameidata *nd)
 {
-       int err;
-
-       err = exec_permission(nd->path.dentry->d_inode);
-       if (err)
-               return ERR_PTR(err);
        return __lookup_hash(&nd->last, nd->path.dentry, nd);
 }
 
@@ -1232,9 +1210,6 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        if (err)
                return ERR_PTR(err);
 
-       err = exec_permission(base->d_inode);
-       if (err)
-               return ERR_PTR(err);
        return __lookup_hash(&this, base, NULL);
 }
 
@@ -1484,8 +1459,7 @@ static int handle_truncate(struct path *path)
         */
        error = locks_verify_locked(inode);
        if (!error)
-               error = security_path_truncate(path, 0,
-                                      ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
+               error = security_path_truncate(path);
        if (!error) {
                error = do_truncate(path->dentry, 0,
                                    ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
@@ -1600,6 +1574,7 @@ static struct file *finish_open(struct nameidata *nd,
         */
        if (will_truncate)
                mnt_drop_write(nd->path.mnt);
+       path_put(&nd->path);
        return filp;
 
 exit:
@@ -1701,6 +1676,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                }
                filp = nameidata_to_filp(nd);
                mnt_drop_write(nd->path.mnt);
+               path_put(&nd->path);
                if (!IS_ERR(filp)) {
                        error = ima_file_check(filp, acc_mode);
                        if (error) {
@@ -2311,7 +2287,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                        goto slashes;
                inode = dentry->d_inode;
                if (inode)
-                       atomic_inc(&inode->i_count);
+                       ihold(inode);
                error = mnt_want_write(nd.path.mnt);
                if (error)
                        goto exit2;
@@ -2635,7 +2611,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 {
        int error;
        int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
-       const char *old_name;
+       const unsigned char *old_name;
 
        if (old_dentry->d_inode == new_dentry->d_inode)
                return 0;