Merge branches 'work.misc' and 'work.dcache' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / fs / namei.c
index 734cef5..3cd3962 100644 (file)
@@ -2028,6 +2028,8 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 {
        int err;
 
+       if (IS_ERR(name))
+               return PTR_ERR(name);
        while (*name=='/')
                name++;
        if (!*name)
@@ -2125,12 +2127,15 @@ OK:
        }
 }
 
+/* must be paired with terminate_walk() */
 static const char *path_init(struct nameidata *nd, unsigned flags)
 {
        const char *s = nd->name->name;
 
        if (!*s)
                flags &= ~LOOKUP_RCU;
+       if (flags & LOOKUP_RCU)
+               rcu_read_lock();
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
@@ -2143,7 +2148,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
                nd->path = nd->root;
                nd->inode = inode;
                if (flags & LOOKUP_RCU) {
-                       rcu_read_lock();
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                        nd->root_seq = nd->seq;
                        nd->m_seq = read_seqbegin(&mount_lock);
@@ -2159,21 +2163,15 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 
        nd->m_seq = read_seqbegin(&mount_lock);
        if (*s == '/') {
-               if (flags & LOOKUP_RCU)
-                       rcu_read_lock();
                set_root(nd);
                if (likely(!nd_jump_root(nd)))
                        return s;
-               nd->root.mnt = NULL;
-               rcu_read_unlock();
                return ERR_PTR(-ECHILD);
        } else if (nd->dfd == AT_FDCWD) {
                if (flags & LOOKUP_RCU) {
                        struct fs_struct *fs = current->fs;
                        unsigned seq;
 
-                       rcu_read_lock();
-
                        do {
                                seq = read_seqcount_begin(&fs->seq);
                                nd->path = fs->pwd;
@@ -2195,16 +2193,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 
                dentry = f.file->f_path.dentry;
 
-               if (*s) {
-                       if (!d_can_lookup(dentry)) {
-                               fdput(f);
-                               return ERR_PTR(-ENOTDIR);
-                       }
+               if (*s && unlikely(!d_can_lookup(dentry))) {
+                       fdput(f);
+                       return ERR_PTR(-ENOTDIR);
                }
 
                nd->path = f.file->f_path;
                if (flags & LOOKUP_RCU) {
-                       rcu_read_lock();
                        nd->inode = nd->path.dentry->d_inode;
                        nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
                } else {
@@ -2272,24 +2267,15 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
        const char *s = path_init(nd, flags);
        int err;
 
-       if (IS_ERR(s))
-               return PTR_ERR(s);
-
-       if (unlikely(flags & LOOKUP_DOWN)) {
+       if (unlikely(flags & LOOKUP_DOWN) && !IS_ERR(s)) {
                err = handle_lookup_down(nd);
-               if (unlikely(err < 0)) {
-                       terminate_walk(nd);
-                       return err;
-               }
+               if (unlikely(err < 0))
+                       s = ERR_PTR(err);
        }
 
        while (!(err = link_path_walk(s, nd))
                && ((err = lookup_last(nd)) > 0)) {
                s = trailing_symlink(nd);
-               if (IS_ERR(s)) {
-                       err = PTR_ERR(s);
-                       break;
-               }
        }
        if (!err)
                err = complete_walk(nd);
@@ -2336,10 +2322,7 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
                                struct path *parent)
 {
        const char *s = path_init(nd, flags);
-       int err;
-       if (IS_ERR(s))
-               return PTR_ERR(s);
-       err = link_path_walk(s, nd);
+       int err = link_path_walk(s, nd);
        if (!err)
                err = complete_walk(nd);
        if (!err) {
@@ -2666,15 +2649,10 @@ path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path)
 {
        const char *s = path_init(nd, flags);
        int err;
-       if (IS_ERR(s))
-               return PTR_ERR(s);
+
        while (!(err = link_path_walk(s, nd)) &&
                (err = mountpoint_last(nd)) > 0) {
                s = trailing_symlink(nd);
-               if (IS_ERR(s)) {
-                       err = PTR_ERR(s);
-                       break;
-               }
        }
        if (!err) {
                *path = nd->path;
@@ -3027,17 +3005,16 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m
  * Returns 0 if successful.  The file will have been created and attached to
  * @file by the filesystem calling finish_open().
  *
- * Returns 1 if the file was looked up only or didn't need creating.  The
- * caller will need to perform the open themselves.  @path will have been
- * updated to point to the new dentry.  This may be negative.
+ * If the file was looked up only or didn't need creating, FMODE_OPENED won't
+ * be set.  The caller will need to perform the open themselves.  @path will
+ * have been updated to point to the new dentry.  This may be negative.
  *
  * Returns an error code otherwise.
  */
 static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        struct path *path, struct file *file,
                        const struct open_flags *op,
-                       int open_flag, umode_t mode,
-                       int *opened)
+                       int open_flag, umode_t mode)
 {
        struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
        struct inode *dir =  nd->path.dentry->d_inode;
@@ -3052,39 +3029,38 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
        file->f_path.dentry = DENTRY_NOT_SET;
        file->f_path.mnt = nd->path.mnt;
        error = dir->i_op->atomic_open(dir, dentry, file,
-                                      open_to_namei_flags(open_flag),
-                                      mode, opened);
+                                      open_to_namei_flags(open_flag), mode);
        d_lookup_done(dentry);
        if (!error) {
-               /*
-                * We didn't have the inode before the open, so check open
-                * permission here.
-                */
-               int acc_mode = op->acc_mode;
-               if (*opened & FILE_CREATED) {
-                       WARN_ON(!(open_flag & O_CREAT));
-                       fsnotify_create(dir, dentry);
-                       acc_mode = 0;
-               }
-               error = may_open(&file->f_path, acc_mode, open_flag);
-               if (WARN_ON(error > 0))
-                       error = -EINVAL;
-       } else if (error > 0) {
-               if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
+               if (file->f_mode & FMODE_OPENED) {
+                       /*
+                        * We didn't have the inode before the open, so check open
+                        * permission here.
+                        */
+                       int acc_mode = op->acc_mode;
+                       if (file->f_mode & FMODE_CREATED) {
+                               WARN_ON(!(open_flag & O_CREAT));
+                               fsnotify_create(dir, dentry);
+                               acc_mode = 0;
+                       }
+                       error = may_open(&file->f_path, acc_mode, open_flag);
+                       if (WARN_ON(error > 0))
+                               error = -EINVAL;
+               } else if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
                        error = -EIO;
                } else {
                        if (file->f_path.dentry) {
                                dput(dentry);
                                dentry = file->f_path.dentry;
                        }
-                       if (*opened & FILE_CREATED)
+                       if (file->f_mode & FMODE_CREATED)
                                fsnotify_create(dir, dentry);
                        if (unlikely(d_is_negative(dentry))) {
                                error = -ENOENT;
                        } else {
                                path->dentry = dentry;
                                path->mnt = nd->path.mnt;
-                               return 1;
+                               return 0;
                        }
                }
        }
@@ -3095,25 +3071,22 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
 /*
  * Look up and maybe create and open the last component.
  *
- * Must be called with i_mutex held on parent.
- *
- * Returns 0 if the file was successfully atomically created (if necessary) and
- * opened.  In this case the file will be returned attached to @file.
- *
- * Returns 1 if the file was not completely opened at this time, though lookups
- * and creations will have been performed and the dentry returned in @path will
- * be positive upon return if O_CREAT was specified.  If O_CREAT wasn't
- * specified then a negative dentry may be returned.
+ * Must be called with parent locked (exclusive in O_CREAT case).
  *
- * An error code is returned otherwise.
+ * Returns 0 on success, that is, if
+ *  the file was successfully atomically created (if necessary) and opened, or
+ *  the file was not completely opened at this time, though lookups and
+ *  creations were performed.
+ * These case are distinguished by presence of FMODE_OPENED on file->f_mode.
+ * In the latter case dentry returned in @path might be negative if O_CREAT
+ * hadn't been specified.
  *
- * FILE_CREATE will be set in @*opened if the dentry was created and will be
- * cleared otherwise prior to returning.
+ * An error code is returned on failure.
  */
 static int lookup_open(struct nameidata *nd, struct path *path,
                        struct file *file,
                        const struct open_flags *op,
-                       bool got_write, int *opened)
+                       bool got_write)
 {
        struct dentry *dir = nd->path.dentry;
        struct inode *dir_inode = dir->d_inode;
@@ -3126,7 +3099,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
        if (unlikely(IS_DEADDIR(dir_inode)))
                return -ENOENT;
 
-       *opened &= ~FILE_CREATED;
+       file->f_mode &= ~FMODE_CREATED;
        dentry = d_lookup(dir, &nd->last);
        for (;;) {
                if (!dentry) {
@@ -3188,7 +3161,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
 
        if (dir_inode->i_op->atomic_open) {
                error = atomic_open(nd, dentry, path, file, op, open_flag,
-                                   mode, opened);
+                                   mode);
                if (unlikely(error == -ENOENT) && create_error)
                        error = create_error;
                return error;
@@ -3211,7 +3184,7 @@ no_open:
 
        /* Negative dentry, just create the file */
        if (!dentry->d_inode && (open_flag & O_CREAT)) {
-               *opened |= FILE_CREATED;
+               file->f_mode |= FMODE_CREATED;
                audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
                if (!dir_inode->i_op->create) {
                        error = -EACCES;
@@ -3230,7 +3203,7 @@ no_open:
 out_no_open:
        path->dentry = dentry;
        path->mnt = nd->path.mnt;
-       return 1;
+       return 0;
 
 out_dput:
        dput(dentry);
@@ -3241,8 +3214,7 @@ out_dput:
  * Handle the last step of open()
  */
 static int do_last(struct nameidata *nd,
-                  struct file *file, const struct open_flags *op,
-                  int *opened)
+                  struct file *file, const struct open_flags *op)
 {
        struct dentry *dir = nd->path.dentry;
        int open_flag = op->open_flag;
@@ -3308,17 +3280,17 @@ static int do_last(struct nameidata *nd,
                inode_lock(dir->d_inode);
        else
                inode_lock_shared(dir->d_inode);
-       error = lookup_open(nd, &path, file, op, got_write, opened);
+       error = lookup_open(nd, &path, file, op, got_write);
        if (open_flag & O_CREAT)
                inode_unlock(dir->d_inode);
        else
                inode_unlock_shared(dir->d_inode);
 
-       if (error <= 0) {
-               if (error)
-                       goto out;
+       if (error)
+               goto out;
 
-               if ((*opened & FILE_CREATED) ||
+       if (file->f_mode & FMODE_OPENED) {
+               if ((file->f_mode & FMODE_CREATED) ||
                    !S_ISREG(file_inode(file)->i_mode))
                        will_truncate = false;
 
@@ -3326,7 +3298,7 @@ static int do_last(struct nameidata *nd,
                goto opened;
        }
 
-       if (*opened & FILE_CREATED) {
+       if (file->f_mode & FMODE_CREATED) {
                /* Don't check for write permission, don't truncate */
                open_flag &= ~O_TRUNC;
                will_truncate = false;
@@ -3395,20 +3367,15 @@ finish_open_created:
        error = may_open(&nd->path, acc_mode, open_flag);
        if (error)
                goto out;
-       BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
-       error = vfs_open(&nd->path, file, current_cred());
+       BUG_ON(file->f_mode & FMODE_OPENED); /* once it's opened, it's opened */
+       error = vfs_open(&nd->path, file);
        if (error)
                goto out;
-       *opened |= FILE_OPENED;
 opened:
-       error = open_check_o_direct(file);
-       if (!error)
-               error = ima_file_check(file, op->acc_mode, *opened);
+       error = ima_file_check(file, op->acc_mode);
        if (!error && will_truncate)
                error = handle_truncate(file);
 out:
-       if (unlikely(error) && (*opened & FILE_OPENED))
-               fput(file);
        if (unlikely(error > 0)) {
                WARN_ON(1);
                error = -EINVAL;
@@ -3458,7 +3425,7 @@ EXPORT_SYMBOL(vfs_tmpfile);
 
 static int do_tmpfile(struct nameidata *nd, unsigned flags,
                const struct open_flags *op,
-               struct file *file, int *opened)
+               struct file *file)
 {
        struct dentry *child;
        struct path path;
@@ -3480,12 +3447,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
        if (error)
                goto out2;
        file->f_path.mnt = path.mnt;
-       error = finish_open(file, child, NULL, opened);
-       if (error)
-               goto out2;
-       error = open_check_o_direct(file);
-       if (error)
-               fput(file);
+       error = finish_open(file, child, NULL);
 out2:
        mnt_drop_write(path.mnt);
 out:
@@ -3499,7 +3461,7 @@ static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
        int error = path_lookupat(nd, flags, &path);
        if (!error) {
                audit_inode(nd->name, path.dentry, 0);
-               error = vfs_open(&path, file, current_cred());
+               error = vfs_open(&path, file);
                path_put(&path);
        }
        return error;
@@ -3508,59 +3470,40 @@ static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
 static struct file *path_openat(struct nameidata *nd,
                        const struct open_flags *op, unsigned flags)
 {
-       const char *s;
        struct file *file;
-       int opened = 0;
        int error;
 
-       file = get_empty_filp();
+       file = alloc_empty_file(op->open_flag, current_cred());
        if (IS_ERR(file))
                return file;
 
-       file->f_flags = op->open_flag;
-
        if (unlikely(file->f_flags & __O_TMPFILE)) {
-               error = do_tmpfile(nd, flags, op, file, &opened);
-               goto out2;
-       }
-
-       if (unlikely(file->f_flags & O_PATH)) {
+               error = do_tmpfile(nd, flags, op, file);
+       } else if (unlikely(file->f_flags & O_PATH)) {
                error = do_o_path(nd, flags, file);
-               if (!error)
-                       opened |= FILE_OPENED;
-               goto out2;
-       }
-
-       s = path_init(nd, flags);
-       if (IS_ERR(s)) {
-               put_filp(file);
-               return ERR_CAST(s);
-       }
-       while (!(error = link_path_walk(s, nd)) &&
-               (error = do_last(nd, file, op, &opened)) > 0) {
-               nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
-               s = trailing_symlink(nd);
-               if (IS_ERR(s)) {
-                       error = PTR_ERR(s);
-                       break;
+       } else {
+               const char *s = path_init(nd, flags);
+               while (!(error = link_path_walk(s, nd)) &&
+                       (error = do_last(nd, file, op)) > 0) {
+                       nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
+                       s = trailing_symlink(nd);
                }
+               terminate_walk(nd);
        }
-       terminate_walk(nd);
-out2:
-       if (!(opened & FILE_OPENED)) {
-               BUG_ON(!error);
-               put_filp(file);
+       if (likely(!error)) {
+               if (likely(file->f_mode & FMODE_OPENED))
+                       return file;
+               WARN_ON(1);
+               error = -EINVAL;
        }
-       if (unlikely(error)) {
-               if (error == -EOPENSTALE) {
-                       if (flags & LOOKUP_RCU)
-                               error = -ECHILD;
-                       else
-                               error = -ESTALE;
-               }
-               file = ERR_PTR(error);
+       fput(file);
+       if (error == -EOPENSTALE) {
+               if (flags & LOOKUP_RCU)
+                       error = -ECHILD;
+               else
+                       error = -ESTALE;
        }
-       return file;
+       return ERR_PTR(error);
 }
 
 struct file *do_filp_open(int dfd, struct filename *pathname,
@@ -4712,29 +4655,6 @@ out:
        return len;
 }
 
-/*
- * A helper for ->readlink().  This should be used *ONLY* for symlinks that
- * have ->get_link() not calling nd_jump_link().  Using (or not using) it
- * for any given inode is up to filesystem.
- */
-static int generic_readlink(struct dentry *dentry, char __user *buffer,
-                           int buflen)
-{
-       DEFINE_DELAYED_CALL(done);
-       struct inode *inode = d_inode(dentry);
-       const char *link = inode->i_link;
-       int res;
-
-       if (!link) {
-               link = inode->i_op->get_link(dentry, inode, &done);
-               if (IS_ERR(link))
-                       return PTR_ERR(link);
-       }
-       res = readlink_copy(buffer, buflen, link);
-       do_delayed_call(&done);
-       return res;
-}
-
 /**
  * vfs_readlink - copy symlink body into userspace buffer
  * @dentry: dentry on which to get symbolic link
@@ -4748,6 +4668,9 @@ static int generic_readlink(struct dentry *dentry, char __user *buffer,
 int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
        struct inode *inode = d_inode(dentry);
+       DEFINE_DELAYED_CALL(done);
+       const char *link;
+       int res;
 
        if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
                if (unlikely(inode->i_op->readlink))
@@ -4761,7 +4684,15 @@ int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
                spin_unlock(&inode->i_lock);
        }
 
-       return generic_readlink(dentry, buffer, buflen);
+       link = inode->i_link;
+       if (!link) {
+               link = inode->i_op->get_link(dentry, inode, &done);
+               if (IS_ERR(link))
+                       return PTR_ERR(link);
+       }
+       res = readlink_copy(buffer, buflen, link);
+       do_delayed_call(&done);
+       return res;
 }
 EXPORT_SYMBOL(vfs_readlink);