Merge branch 'ovl-fixes' into for-linus
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 17 May 2016 06:17:59 +0000 (02:17 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 17 May 2016 06:17:59 +0000 (02:17 -0400)
Backmerge to resolve a conflict in ovl_lookup_real();
"ovl_lookup_real(): use lookup_one_len_unlocked()" instead,
but it was too late in the cycle to rebase.

1  2 
fs/namei.c
fs/open.c
fs/overlayfs/super.c
fs/proc/base.c
include/linux/dcache.h

diff --combined fs/namei.c
@@@ -265,7 -265,7 +265,7 @@@ static int check_acl(struct inode *inod
                if (!acl)
                        return -EAGAIN;
                /* no ->get_acl() calls in RCU mode... */
 -              if (acl == ACL_NOT_CACHED)
 +              if (is_uncached_acl(acl))
                        return -ECHILD;
                return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
        }
@@@ -1603,42 -1603,32 +1603,42 @@@ static struct dentry *lookup_slow(cons
                                  struct dentry *dir,
                                  unsigned int flags)
  {
 -      struct dentry *dentry;
 -      inode_lock(dir->d_inode);
 -      dentry = d_lookup(dir, name);
 -      if (unlikely(dentry)) {
 +      struct dentry *dentry = ERR_PTR(-ENOENT), *old;
 +      struct inode *inode = dir->d_inode;
 +      DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 +
 +      inode_lock_shared(inode);
 +      /* Don't go there if it's already dead */
 +      if (unlikely(IS_DEADDIR(inode)))
 +              goto out;
 +again:
 +      dentry = d_alloc_parallel(dir, name, &wq);
 +      if (IS_ERR(dentry))
 +              goto out;
 +      if (unlikely(!d_in_lookup(dentry))) {
                if ((dentry->d_flags & DCACHE_OP_REVALIDATE) &&
                    !(flags & LOOKUP_NO_REVAL)) {
                        int error = d_revalidate(dentry, flags);
                        if (unlikely(error <= 0)) {
 -                              if (!error)
 +                              if (!error) {
                                        d_invalidate(dentry);
 +                                      dput(dentry);
 +                                      goto again;
 +                              }
                                dput(dentry);
                                dentry = ERR_PTR(error);
                        }
                }
 -              if (dentry) {
 -                      inode_unlock(dir->d_inode);
 -                      return dentry;
 +      } else {
 +              old = inode->i_op->lookup(inode, dentry, flags);
 +              d_lookup_done(dentry);
 +              if (unlikely(old)) {
 +                      dput(dentry);
 +                      dentry = old;
                }
        }
 -      dentry = d_alloc(dir, name);
 -      if (unlikely(!dentry)) {
 -              inode_unlock(dir->d_inode);
 -              return ERR_PTR(-ENOMEM);
 -      }
 -      dentry = lookup_real(dir->d_inode, dentry, flags);
 -      inode_unlock(dir->d_inode);
 +out:
 +      inode_unlock_shared(inode);
        return dentry;
  }
  
@@@ -2276,6 -2266,33 +2276,33 @@@ int vfs_path_lookup(struct dentry *dent
  }
  EXPORT_SYMBOL(vfs_path_lookup);
  
+ /**
+  * lookup_hash - lookup single pathname component on already hashed name
+  * @name:     name and hash to lookup
+  * @base:     base directory to lookup from
+  *
+  * The name must have been verified and hashed (see lookup_one_len()).  Using
+  * this after just full_name_hash() is unsafe.
+  *
+  * This function also doesn't check for search permission on base directory.
+  *
+  * Use lookup_one_len_unlocked() instead, unless you really know what you are
+  * doing.
+  *
+  * Do not hold i_mutex; this helper takes i_mutex if necessary.
+  */
+ struct dentry *lookup_hash(const struct qstr *name, struct dentry *base)
+ {
+       struct dentry *ret;
+       ret = lookup_dcache(name, base, 0);
+       if (!ret)
+               ret = lookup_slow(name, base, 0);
+       return ret;
+ }
+ EXPORT_SYMBOL(lookup_hash);
  /**
   * lookup_one_len - filesystem helper to lookup single pathname component
   * @name:     pathname component to lookup
@@@ -2347,7 -2364,6 +2374,6 @@@ struct dentry *lookup_one_len_unlocked(
        struct qstr this;
        unsigned int c;
        int err;
-       struct dentry *ret;
  
        this.name = name;
        this.len = len;
        if (err)
                return ERR_PTR(err);
  
-       ret = lookup_dcache(&this, base, 0);
-       if (!ret)
-               ret = lookup_slow(&this, base, 0);
-       return ret;
+       return lookup_hash(&this, base);
  }
  EXPORT_SYMBOL(lookup_one_len_unlocked);
  
@@@ -2665,7 -2678,7 +2688,7 @@@ struct dentry *lock_rename(struct dentr
                return NULL;
        }
  
 -      mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
 +      mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
  
        p = d_ancestor(p2, p1);
        if (p) {
@@@ -2692,7 -2705,7 +2715,7 @@@ void unlock_rename(struct dentry *p1, s
        inode_unlock(p1->d_inode);
        if (p1 != p2) {
                inode_unlock(p2->d_inode);
 -              mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex);
 +              mutex_unlock(&p1->d_sb->s_vfs_rename_mutex);
        }
  }
  EXPORT_SYMBOL(unlock_rename);
@@@ -2824,56 -2837,155 +2847,56 @@@ static int may_o_create(struct path *di
  static int atomic_open(struct nameidata *nd, struct dentry *dentry,
                        struct path *path, struct file *file,
                        const struct open_flags *op,
 -                      bool got_write, bool need_lookup,
 +                      int open_flag, umode_t mode,
                        int *opened)
  {
 +      struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
        struct inode *dir =  nd->path.dentry->d_inode;
 -      unsigned open_flag = open_to_namei_flags(op->open_flag);
 -      umode_t mode;
        int error;
 -      int acc_mode;
 -      int create_error = 0;
 -      struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
 -      bool excl;
 -
 -      BUG_ON(dentry->d_inode);
 -
 -      /* Don't create child dentry for a dead directory. */
 -      if (unlikely(IS_DEADDIR(dir))) {
 -              error = -ENOENT;
 -              goto out;
 -      }
 -
 -      mode = op->mode;
 -      if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
 -              mode &= ~current_umask();
  
 -      excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
 -      if (excl)
 +      if (!(~open_flag & (O_EXCL | O_CREAT))) /* both O_EXCL and O_CREAT */
                open_flag &= ~O_TRUNC;
  
 -      /*
 -       * Checking write permission is tricky, bacuse we don't know if we are
 -       * going to actually need it: O_CREAT opens should work as long as the
 -       * file exists.  But checking existence breaks atomicity.  The trick is
 -       * to check access and if not granted clear O_CREAT from the flags.
 -       *
 -       * Another problem is returing the "right" error value (e.g. for an
 -       * O_EXCL open we want to return EEXIST not EROFS).
 -       */
 -      if (((open_flag & (O_CREAT | O_TRUNC)) ||
 -          (open_flag & O_ACCMODE) != O_RDONLY) && unlikely(!got_write)) {
 -              if (!(open_flag & O_CREAT)) {
 -                      /*
 -                       * No O_CREATE -> atomicity not a requirement -> fall
 -                       * back to lookup + open
 -                       */
 -                      goto no_open;
 -              } else if (open_flag & (O_EXCL | O_TRUNC)) {
 -                      /* Fall back and fail with the right error */
 -                      create_error = -EROFS;
 -                      goto no_open;
 -              } else {
 -                      /* No side effects, safe to clear O_CREAT */
 -                      create_error = -EROFS;
 -                      open_flag &= ~O_CREAT;
 -              }
 -      }
 -
 -      if (open_flag & O_CREAT) {
 -              error = may_o_create(&nd->path, dentry, mode);
 -              if (error) {
 -                      create_error = error;
 -                      if (open_flag & O_EXCL)
 -                              goto no_open;
 -                      open_flag &= ~O_CREAT;
 -              }
 -      }
 -
        if (nd->flags & LOOKUP_DIRECTORY)
                open_flag |= O_DIRECTORY;
  
        file->f_path.dentry = DENTRY_NOT_SET;
        file->f_path.mnt = nd->path.mnt;
 -      error = dir->i_op->atomic_open(dir, dentry, file, open_flag, mode,
 -                                    opened);
 -      if (error < 0) {
 -              if (create_error && error == -ENOENT)
 -                      error = create_error;
 -              goto out;
 -      }
 -
 -      if (error) {    /* returned 1, that is */
 +      error = dir->i_op->atomic_open(dir, dentry, file,
 +                                     open_to_namei_flags(open_flag),
 +                                     mode, opened);
 +      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)) {
                        error = -EIO;
 -                      goto out;
 -              }
 -              if (file->f_path.dentry) {
 -                      dput(dentry);
 -                      dentry = file->f_path.dentry;
 -              }
 -              if (*opened & FILE_CREATED)
 -                      fsnotify_create(dir, dentry);
 -              if (!dentry->d_inode) {
 -                      WARN_ON(*opened & FILE_CREATED);
 -                      if (create_error) {
 -                              error = create_error;
 -                              goto out;
 -                      }
                } else {
 -                      if (excl && !(*opened & FILE_CREATED)) {
 -                              error = -EEXIST;
 -                              goto out;
 +                      if (file->f_path.dentry) {
 +                              dput(dentry);
 +                              dentry = file->f_path.dentry;
                        }
 +                      if (*opened & FILE_CREATED)
 +                              fsnotify_create(dir, dentry);
 +                      path->dentry = dentry;
 +                      path->mnt = nd->path.mnt;
 +                      return 1;
                }
 -              goto looked_up;
        }
 -
 -      /*
 -       * We didn't have the inode before the open, so check open permission
 -       * here.
 -       */
 -      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 (error)
 -              fput(file);
 -
 -out:
        dput(dentry);
        return error;
 -
 -no_open:
 -      if (need_lookup) {
 -              dentry = lookup_real(dir, dentry, nd->flags);
 -              if (IS_ERR(dentry))
 -                      return PTR_ERR(dentry);
 -
 -              if (create_error) {
 -                      int open_flag = op->open_flag;
 -
 -                      error = create_error;
 -                      if ((open_flag & O_EXCL)) {
 -                              if (!dentry->d_inode)
 -                                      goto out;
 -                      } else if (!dentry->d_inode) {
 -                              goto out;
 -                      } else if ((open_flag & O_TRUNC) &&
 -                                 d_is_reg(dentry)) {
 -                              goto out;
 -                      }
 -                      /* will fail later, go on to get the right error */
 -              }
 -      }
 -looked_up:
 -      path->dentry = dentry;
 -      path->mnt = nd->path.mnt;
 -      return 1;
  }
  
  /*
@@@ -2901,118 -3013,62 +2924,118 @@@ static int lookup_open(struct nameidat
  {
        struct dentry *dir = nd->path.dentry;
        struct inode *dir_inode = dir->d_inode;
 +      int open_flag = op->open_flag;
        struct dentry *dentry;
 -      int error;
 -      bool need_lookup = false;
 +      int error, create_error = 0;
 +      umode_t mode = op->mode;
 +      DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 +
 +      if (unlikely(IS_DEADDIR(dir_inode)))
 +              return -ENOENT;
  
        *opened &= ~FILE_CREATED;
 -      dentry = lookup_dcache(&nd->last, dir, nd->flags);
 -      if (IS_ERR(dentry))
 -              return PTR_ERR(dentry);
 +      dentry = d_lookup(dir, &nd->last);
 +      for (;;) {
 +              if (!dentry) {
 +                      dentry = d_alloc_parallel(dir, &nd->last, &wq);
 +                      if (IS_ERR(dentry))
 +                              return PTR_ERR(dentry);
 +              }
 +              if (d_in_lookup(dentry))
 +                      break;
  
 -      if (!dentry) {
 -              dentry = d_alloc(dir, &nd->last);
 -              if (unlikely(!dentry))
 -                      return -ENOMEM;
 -              need_lookup = true;
 -      } else if (dentry->d_inode) {
 +              if (!(dentry->d_flags & DCACHE_OP_REVALIDATE))
 +                      break;
 +
 +              error = d_revalidate(dentry, nd->flags);
 +              if (likely(error > 0))
 +                      break;
 +              if (error)
 +                      goto out_dput;
 +              d_invalidate(dentry);
 +              dput(dentry);
 +              dentry = NULL;
 +      }
 +      if (dentry->d_inode) {
                /* Cached positive dentry: will open in f_op->open */
                goto out_no_open;
        }
  
 -      if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {
 -              return atomic_open(nd, dentry, path, file, op, got_write,
 -                                 need_lookup, opened);
 +      /*
 +       * Checking write permission is tricky, bacuse we don't know if we are
 +       * going to actually need it: O_CREAT opens should work as long as the
 +       * file exists.  But checking existence breaks atomicity.  The trick is
 +       * to check access and if not granted clear O_CREAT from the flags.
 +       *
 +       * Another problem is returing the "right" error value (e.g. for an
 +       * O_EXCL open we want to return EEXIST not EROFS).
 +       */
 +      if (open_flag & O_CREAT) {
 +              if (!IS_POSIXACL(dir->d_inode))
 +                      mode &= ~current_umask();
 +              if (unlikely(!got_write)) {
 +                      create_error = -EROFS;
 +                      open_flag &= ~O_CREAT;
 +                      if (open_flag & (O_EXCL | O_TRUNC))
 +                              goto no_open;
 +                      /* No side effects, safe to clear O_CREAT */
 +              } else {
 +                      create_error = may_o_create(&nd->path, dentry, mode);
 +                      if (create_error) {
 +                              open_flag &= ~O_CREAT;
 +                              if (open_flag & O_EXCL)
 +                                      goto no_open;
 +                      }
 +              }
 +      } else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) &&
 +                 unlikely(!got_write)) {
 +              /*
 +               * No O_CREATE -> atomicity not a requirement -> fall
 +               * back to lookup + open
 +               */
 +              goto no_open;
        }
  
 -      if (need_lookup) {
 -              BUG_ON(dentry->d_inode);
 +      if (dir_inode->i_op->atomic_open) {
 +              error = atomic_open(nd, dentry, path, file, op, open_flag,
 +                                  mode, opened);
 +              if (unlikely(error == -ENOENT) && create_error)
 +                      error = create_error;
 +              return error;
 +      }
  
 -              dentry = lookup_real(dir_inode, dentry, nd->flags);
 -              if (IS_ERR(dentry))
 -                      return PTR_ERR(dentry);
 +no_open:
 +      if (d_in_lookup(dentry)) {
 +              struct dentry *res = dir_inode->i_op->lookup(dir_inode, dentry,
 +                                                           nd->flags);
 +              d_lookup_done(dentry);
 +              if (unlikely(res)) {
 +                      if (IS_ERR(res)) {
 +                              error = PTR_ERR(res);
 +                              goto out_dput;
 +                      }
 +                      dput(dentry);
 +                      dentry = res;
 +              }
        }
  
        /* Negative dentry, just create the file */
 -      if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
 -              umode_t mode = op->mode;
 -              if (!IS_POSIXACL(dir->d_inode))
 -                      mode &= ~current_umask();
 -              /*
 -               * This write is needed to ensure that a
 -               * rw->ro 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 finish_open().
 -               */
 -              if (!got_write) {
 -                      error = -EROFS;
 -                      goto out_dput;
 -              }
 +      if (!dentry->d_inode && (open_flag & O_CREAT)) {
                *opened |= FILE_CREATED;
 -              error = security_path_mknod(&nd->path, dentry, mode, 0);
 -              if (error)
 +              audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
 +              if (!dir_inode->i_op->create) {
 +                      error = -EACCES;
                        goto out_dput;
 -              error = vfs_create(dir->d_inode, dentry, mode,
 -                                 nd->flags & LOOKUP_EXCL);
 +              }
 +              error = dir_inode->i_op->create(dir_inode, dentry, mode,
 +                                              open_flag & O_EXCL);
                if (error)
                        goto out_dput;
 +              fsnotify_create(dir_inode, dentry);
 +      }
 +      if (unlikely(create_error) && !dentry->d_inode) {
 +              error = create_error;
 +              goto out_dput;
        }
  out_no_open:
        path->dentry = dentry;
@@@ -3084,7 -3140,7 +3107,7 @@@ static int do_last(struct nameidata *nd
        }
  
  retry_lookup:
 -      if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
 +      if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
                error = mnt_want_write(nd->path.mnt);
                if (!error)
                        got_write = true;
                 * dropping this one anyway.
                 */
        }
 -      inode_lock(dir->d_inode);
 +      if (open_flag & O_CREAT)
 +              inode_lock(dir->d_inode);
 +      else
 +              inode_lock_shared(dir->d_inode);
        error = lookup_open(nd, &path, file, op, got_write, opened);
 -      inode_unlock(dir->d_inode);
 +      if (open_flag & O_CREAT)
 +              inode_unlock(dir->d_inode);
 +      else
 +              inode_unlock_shared(dir->d_inode);
  
        if (error <= 0) {
                if (error)
@@@ -3182,6 -3232,10 +3205,6 @@@ finish_open
                return error;
        }
        audit_inode(nd->name, nd->path.dentry, 0);
 -      if (unlikely(d_is_symlink(nd->path.dentry)) && !(open_flag & O_PATH)) {
 -              error = -ELOOP;
 -              goto out;
 -      }
        error = -EISDIR;
        if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
                goto out;
                got_write = true;
        }
  finish_open_created:
 -      if (likely(!(open_flag & O_PATH))) {
 -              error = may_open(&nd->path, acc_mode, open_flag);
 -              if (error)
 -                      goto out;
 -      }
 +      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());
        if (!error) {
        }
  opened:
        error = open_check_o_direct(file);
 -      if (error)
 -              goto exit_fput;
 -      error = ima_file_check(file, op->acc_mode, *opened);
 -      if (error)
 -              goto exit_fput;
 -
 -      if (will_truncate) {
 +      if (!error)
 +              error = ima_file_check(file, op->acc_mode, *opened);
 +      if (!error && will_truncate)
                error = handle_truncate(file);
 -              if (error)
 -                      goto exit_fput;
 -      }
  out:
 +      if (unlikely(error) && (*opened & FILE_OPENED))
 +              fput(file);
        if (unlikely(error > 0)) {
                WARN_ON(1);
                error = -EINVAL;
        path_put(&save_parent);
        return error;
  
 -exit_fput:
 -      fput(file);
 -      goto out;
 -
  stale_open:
        /* If no saved parent or already retried then can't retry */
        if (!save_parent.dentry || retried)
        return error;
  }
  
 +static int do_o_path(struct nameidata *nd, unsigned flags, struct file *file)
 +{
 +      struct path path;
 +      int error = path_lookupat(nd, flags, &path);
 +      if (!error) {
 +              audit_inode(nd->name, path.dentry, 0);
 +              error = vfs_open(&path, file, current_cred());
 +              path_put(&path);
 +      }
 +      return error;
 +}
 +
  static struct file *path_openat(struct nameidata *nd,
                        const struct open_flags *op, unsigned flags)
  {
                goto out2;
        }
  
 +      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);
@@@ -4190,7 -4236,11 +4213,11 @@@ int vfs_rename(struct inode *old_dir, s
        bool new_is_dir = false;
        unsigned max_links = new_dir->i_sb->s_max_links;
  
-       if (source == target)
+       /*
+        * Check source == target.
+        * On overlayfs need to look at underlying inodes.
+        */
+       if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0))
                return 0;
  
        error = may_delete(old_dir, old_dentry, is_dir);
diff --combined fs/open.c
+++ b/fs/open.c
@@@ -713,7 -713,7 +713,7 @@@ static int do_dentry_open(struct file *
        }
  
        /* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
 -      if (S_ISREG(inode->i_mode))
 +      if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
                f->f_mode |= FMODE_ATOMIC_POS;
  
        f->f_op = fops_get(inode->i_fop);
@@@ -840,16 -840,12 +840,12 @@@ EXPORT_SYMBOL(file_path)
  int vfs_open(const struct path *path, struct file *file,
             const struct cred *cred)
  {
-       struct dentry *dentry = path->dentry;
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = vfs_select_inode(path->dentry, file->f_flags);
  
-       file->f_path = *path;
-       if (dentry->d_flags & DCACHE_OP_SELECT_INODE) {
-               inode = dentry->d_op->d_select_inode(dentry, file->f_flags);
-               if (IS_ERR(inode))
-                       return PTR_ERR(inode);
-       }
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
  
+       file->f_path = *path;
        return do_dentry_open(file, inode, NULL, cred);
  }
  
diff --combined fs/overlayfs/super.c
@@@ -274,7 -274,7 +274,7 @@@ static bool ovl_is_opaquedir(struct den
        if (!S_ISDIR(inode->i_mode) || !inode->i_op->getxattr)
                return false;
  
 -      res = inode->i_op->getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
 +      res = inode->i_op->getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1);
        if (res == 1 && val == 'y')
                return true;
  
@@@ -411,7 -411,7 +411,7 @@@ static inline struct dentry *ovl_lookup
  {
        struct dentry *dentry;
  
-       dentry = lookup_one_len_unlocked(name->name, dir, name->len);
+       dentry = lookup_hash(name, dir);
  
        if (IS_ERR(dentry)) {
                if (PTR_ERR(dentry) == -ENOENT)
diff --combined fs/proc/base.c
@@@ -955,7 -955,8 +955,8 @@@ static ssize_t environ_read(struct fil
        struct mm_struct *mm = file->private_data;
        unsigned long env_start, env_end;
  
-       if (!mm)
+       /* Ensure the process spawned far enough to have an environment. */
+       if (!mm || !mm->env_end)
                return 0;
  
        page = (char *)__get_free_page(GFP_TEMPORARY);
@@@ -1819,17 -1820,12 +1820,17 @@@ bool proc_fill_cache(struct file *file
  
        child = d_hash_and_lookup(dir, &qname);
        if (!child) {
 -              child = d_alloc(dir, &qname);
 -              if (!child)
 -                      goto end_instantiate;
 -              if (instantiate(d_inode(dir), child, task, ptr) < 0) {
 -                      dput(child);
 +              DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 +              child = d_alloc_parallel(dir, &qname, &wq);
 +              if (IS_ERR(child))
                        goto end_instantiate;
 +              if (d_in_lookup(child)) {
 +                      int err = instantiate(d_inode(dir), child, task, ptr);
 +                      d_lookup_done(child);
 +                      if (err < 0) {
 +                              dput(child);
 +                              goto end_instantiate;
 +                      }
                }
        }
        inode = d_inode(child);
@@@ -2159,8 -2155,8 +2160,8 @@@ out
  
  static const struct file_operations proc_map_files_operations = {
        .read           = generic_read_dir,
 -      .iterate        = proc_map_files_readdir,
 -      .llseek         = default_llseek,
 +      .iterate_shared = proc_map_files_readdir,
 +      .llseek         = generic_file_llseek,
  };
  
  #ifdef CONFIG_CHECKPOINT_RESTORE
@@@ -2507,8 -2503,8 +2508,8 @@@ static int proc_attr_dir_readdir(struc
  
  static const struct file_operations proc_attr_dir_operations = {
        .read           = generic_read_dir,
 -      .iterate        = proc_attr_dir_readdir,
 -      .llseek         = default_llseek,
 +      .iterate_shared = proc_attr_dir_readdir,
 +      .llseek         = generic_file_llseek,
  };
  
  static struct dentry *proc_attr_dir_lookup(struct inode *dir,
@@@ -2915,8 -2911,8 +2916,8 @@@ static int proc_tgid_base_readdir(struc
  
  static const struct file_operations proc_tgid_base_operations = {
        .read           = generic_read_dir,
 -      .iterate        = proc_tgid_base_readdir,
 -      .llseek         = default_llseek,
 +      .iterate_shared = proc_tgid_base_readdir,
 +      .llseek         = generic_file_llseek,
  };
  
  static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
@@@ -3263,8 -3259,8 +3264,8 @@@ static struct dentry *proc_tid_base_loo
  
  static const struct file_operations proc_tid_base_operations = {
        .read           = generic_read_dir,
 -      .iterate        = proc_tid_base_readdir,
 -      .llseek         = default_llseek,
 +      .iterate_shared = proc_tid_base_readdir,
 +      .llseek         = generic_file_llseek,
  };
  
  static const struct inode_operations proc_tid_base_inode_operations = {
@@@ -3474,6 -3470,6 +3475,6 @@@ static const struct inode_operations pr
  
  static const struct file_operations proc_task_operations = {
        .read           = generic_read_dir,
 -      .iterate        = proc_task_readdir,
 -      .llseek         = default_llseek,
 +      .iterate_shared = proc_task_readdir,
 +      .llseek         = generic_file_llseek,
  };
diff --combined include/linux/dcache.h
@@@ -123,10 -123,7 +123,10 @@@ struct dentry 
        unsigned long d_time;           /* used by d_revalidate */
        void *d_fsdata;                 /* fs-specific data */
  
 -      struct list_head d_lru;         /* LRU list */
 +      union {
 +              struct list_head d_lru;         /* LRU list */
 +              wait_queue_head_t *d_wait;      /* in-lookup ones only */
 +      };
        struct list_head d_child;       /* child of parent list */
        struct list_head d_subdirs;     /* our children */
        /*
         */
        union {
                struct hlist_node d_alias;      /* inode alias list */
 +              struct hlist_bl_node d_in_lookup_hash;  /* only for in-lookup ones */
                struct rcu_head d_rcu;
        } d_u;
  };
@@@ -236,8 -232,6 +236,8 @@@ struct dentry_operations 
  #define DCACHE_ENCRYPTED_WITH_KEY     0x04000000 /* dir is encrypted with a valid key */
  #define DCACHE_OP_REAL                        0x08000000
  
 +#define DCACHE_PAR_LOOKUP             0x10000000 /* being looked up (with parent locked shared) */
 +
  extern seqlock_t rename_lock;
  
  /*
@@@ -254,8 -248,6 +254,8 @@@ extern void d_set_d_op(struct dentry *d
  /* allocate/de-allocate */
  extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
  extern struct dentry * d_alloc_pseudo(struct super_block *, const struct qstr *);
 +extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
 +                                      wait_queue_head_t *);
  extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
  extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
  extern struct dentry * d_exact_alias(struct dentry *, struct inode *);
@@@ -375,22 -367,6 +375,22 @@@ static inline void dont_mount(struct de
        spin_unlock(&dentry->d_lock);
  }
  
 +extern void __d_lookup_done(struct dentry *);
 +
 +static inline int d_in_lookup(struct dentry *dentry)
 +{
 +      return dentry->d_flags & DCACHE_PAR_LOOKUP;
 +}
 +
 +static inline void d_lookup_done(struct dentry *dentry)
 +{
 +      if (unlikely(d_in_lookup(dentry))) {
 +              spin_lock(&dentry->d_lock);
 +              __d_lookup_done(dentry);
 +              spin_unlock(&dentry->d_lock);
 +      }
 +}
 +
  extern void dput(struct dentry *);
  
  static inline bool d_managed(const struct dentry *dentry)
@@@ -589,4 -565,16 +589,16 @@@ static inline struct dentry *d_real(str
                return dentry;
  }
  
+ static inline struct inode *vfs_select_inode(struct dentry *dentry,
+                                            unsigned open_flags)
+ {
+       struct inode *inode = d_inode(dentry);
+       if (inode && unlikely(dentry->d_flags & DCACHE_OP_SELECT_INODE))
+               inode = dentry->d_op->d_select_inode(dentry, open_flags);
+       return inode;
+ }
  #endif        /* __LINUX_DCACHE_H */