]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'userns/for-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Wed, 7 May 2014 05:40:21 +0000 (15:40 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Wed, 7 May 2014 05:40:21 +0000 (15:40 +1000)
Conflicts:
fs/dcache.c
fs/namei.c
fs/namespace.c

12 files changed:
1  2 
fs/btrfs/ioctl.c
fs/ceph/dir.c
fs/dcache.c
fs/fuse/dir.c
fs/kernfs/dir.c
fs/mount.h
fs/namei.c
fs/namespace.c
fs/nfs/dir.c
fs/proc/base.c
fs/proc/fd.c
include/linux/dcache.h

Simple merge
diff --cc fs/ceph/dir.c
Simple merge
diff --cc fs/dcache.c
index 42ae01eefc0767902b4827ef4113199fd6e57a1a,5b78bd98649ceb1eb3271f6a448d4766554fffac..17010fd1cecd221a328d0428ea3d74b658963636
@@@ -2587,10 -2631,8 +2549,8 @@@ static struct dentry *__d_unalias(struc
                goto out_err;
        m2 = &alias->d_parent->d_inode->i_mutex;
  out_unalias:
-       if (likely(!d_mountpoint(alias))) {
-               __d_move(alias, dentry, false);
-               ret = alias;
-       }
 -      __d_move(alias, dentry);
++      __d_move(alias, dentry, false);
+       ret = alias;
  out_err:
        spin_unlock(&inode->i_lock);
        if (m2)
diff --cc fs/fuse/dir.c
Simple merge
diff --cc fs/kernfs/dir.c
Simple merge
diff --cc fs/mount.h
index d55297f2fa058c18512d0136b8a5437b29bc481c,4104a3cca2383b629c88c87e45e77dc8c6d816f9..84c282321d67645f9a27d626a3d2a2872fecab31
@@@ -19,8 -19,9 +19,9 @@@ struct mnt_pcp 
  };
  
  struct mountpoint {
 -      struct list_head m_hash;
 +      struct hlist_node m_hash;
        struct dentry *m_dentry;
+       struct list_head m_list;
        int m_count;
  };
  
diff --cc fs/namei.c
index 80168273396bbaeb4490677854fe34d637a78551,384fcc6a5606e9fcc2ea76028b728f0af568618c..322069384b5ea9948c647e7d03e382ef1c8280f5
@@@ -4101,33 -4045,17 +4104,33 @@@ int vfs_rename(struct inode *old_dir, s
        if (error)
                return error;
  
 +      old_name = fsnotify_oldname_init(old_dentry->d_name.name);
        dget(new_dentry);
 -      lock_two_nondirectories(source, target);
 +      if (!is_dir || (flags & RENAME_EXCHANGE))
 +              lock_two_nondirectories(source, target);
 +      else if (target)
 +              mutex_lock(&target->i_mutex);
  
        error = -EBUSY;
-       if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
+       if (is_local_mountpoint(old_dentry) || is_local_mountpoint(new_dentry))
                goto out;
  
 -      error = try_break_deleg(source, delegated_inode);
 -      if (error)
 -              goto out;
 -      if (target) {
 +      if (max_links && new_dir != old_dir) {
 +              error = -EMLINK;
 +              if (is_dir && !new_is_dir && new_dir->i_nlink >= max_links)
 +                      goto out;
 +              if ((flags & RENAME_EXCHANGE) && !is_dir && new_is_dir &&
 +                  old_dir->i_nlink >= max_links)
 +                      goto out;
 +      }
 +      if (is_dir && !(flags & RENAME_EXCHANGE) && target)
 +              shrink_dcache_parent(new_dentry);
 +      if (!is_dir) {
 +              error = try_break_deleg(source, delegated_inode);
 +              if (error)
 +                      goto out;
 +      }
 +      if (target && !new_is_dir) {
                error = try_break_deleg(target, delegated_inode);
                if (error)
                        goto out;
        if (error)
                goto out;
  
 -      if (target) {
 +      if (!(flags & RENAME_EXCHANGE) && target) {
 +              if (is_dir)
 +                      target->i_flags |= S_DEAD;
                dont_mount(new_dentry);
+               detach_mounts(new_dentry);
        }
 -      if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
 -              d_move(old_dentry, new_dentry);
 +      if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) {
 +              if (!(flags & RENAME_EXCHANGE))
 +                      d_move(old_dentry, new_dentry);
 +              else
 +                      d_exchange(old_dentry, new_dentry);
 +      }
  out:
 -      unlock_two_nondirectories(source, target);
 +      if (!is_dir || (flags & RENAME_EXCHANGE))
 +              unlock_two_nondirectories(source, target);
 +      else if (target)
 +              mutex_unlock(&target->i_mutex);
        dput(new_dentry);
 -      return error;
 -}
 -
 -/**
 - * vfs_rename - rename a filesystem object
 - * @old_dir:  parent of source
 - * @old_dentry:       source
 - * @new_dir:  parent of destination
 - * @new_dentry:       destination
 - * @delegated_inode: returns an inode needing a delegation break
 - *
 - * The caller must hold multiple mutexes--see lock_rename()).
 - *
 - * If vfs_rename discovers a delegation in need of breaking at either
 - * the source or destination, it will return -EWOULDBLOCK and return a
 - * reference to the inode in delegated_inode.  The caller should then
 - * break the delegation and retry.  Because breaking a delegation may
 - * take a long time, the caller should drop all locks before doing
 - * so.
 - *
 - * Alternatively, a caller may pass NULL for delegated_inode.  This may
 - * be appropriate for callers that expect the underlying filesystem not
 - * to be NFS exported.
 - */
 -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 -             struct inode *new_dir, struct dentry *new_dentry,
 -             struct inode **delegated_inode)
 -{
 -      int error;
 -      int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
 -      const unsigned char *old_name;
 -
 -      if (old_dentry->d_inode == new_dentry->d_inode)
 -              return 0;
 - 
 -      error = may_delete(old_dir, old_dentry, is_dir);
 -      if (error)
 -              return error;
 -
 -      if (!new_dentry->d_inode)
 -              error = may_create(new_dir, new_dentry);
 -      else
 -              error = may_delete(new_dir, new_dentry, is_dir);
 -      if (error)
 -              return error;
 -
 -      if (!old_dir->i_op->rename)
 -              return -EPERM;
 -
 -      old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 -
 -      if (is_dir)
 -              error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
 -      else
 -              error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry,delegated_inode);
 -      if (!error)
 +      if (!error) {
                fsnotify_move(old_dir, new_dir, old_name, is_dir,
 -                            new_dentry->d_inode, old_dentry);
 +                            !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
 +              if (flags & RENAME_EXCHANGE) {
 +                      fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
 +                                    new_is_dir, NULL, new_dentry);
 +              }
 +      }
        fsnotify_oldname_free(old_name);
  
        return error;
diff --cc fs/namespace.c
index 182bc41cd88711d593c4d997171c6ad483a87577,128c051041be8811d754f2a6e0d2c29340a3c055..b10db3d699431dcd675b09495fff97613d317dcd
@@@ -667,13 -632,47 +668,47 @@@ struct vfsmount *lookup_mnt(struct pat
        return m;
  }
  
- static struct mountpoint *new_mountpoint(struct dentry *dentry)
+ /*
+  * __is_local_mountpoint - Test to see if dentry is a mountpoint in the
+  *                         current mount namespace.
+  *
+  * The common case is dentries are not mountpoints at all and that
+  * test is handled inline.  For the slow case when we are actually
+  * dealing with a mountpoint of some kind, walk through all of the
+  * mounts in the current mount namespace and test to see if the dentry
+  * is a mountpoint.
+  *
+  * The mount_hashtable is not usable in the context because we
+  * need to identify all mounts that may be in the current mount
+  * namespace not just a mount that happens to have some specified
+  * parent mount.
+  */
+ bool __is_local_mountpoint(struct dentry *dentry)
+ {
+       struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+       struct mount *mnt;
+       bool is_covered = false;
+       if (!d_mountpoint(dentry))
+               goto out;
+       down_read(&namespace_sem);
+       list_for_each_entry(mnt, &ns->list, mnt_list) {
+               is_covered = (mnt->mnt_mountpoint == dentry);
+               if (is_covered)
+                       break;
+       }
+       up_read(&namespace_sem);
+ out:
+       return is_covered;
+ }
+ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
  {
 -      struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
 +      struct hlist_head *chain = mp_hash(dentry);
        struct mountpoint *mp;
-       int ret;
  
 -      list_for_each_entry(mp, chain, m_hash) {
 +      hlist_for_each_entry(mp, chain, m_hash) {
                if (mp->m_dentry == dentry) {
                        /* might be worth a WARN_ON() */
                        if (d_unlinked(dentry))
                        return mp;
                }
        }
 -      struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
+       return NULL;
+ }
+ static struct mountpoint *new_mountpoint(struct dentry *dentry)
+ {
++      struct hlist_head *chain = mp_hash(dentry);
+       struct mountpoint *mp;
+       int ret;
  
        mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
        if (!mp)
  
        mp->m_dentry = dentry;
        mp->m_count = 1;
 -      list_add(&mp->m_hash, chain);
 +      hlist_add_head(&mp->m_hash, chain);
+       INIT_LIST_HEAD(&mp->m_list);
        return mp;
  }
  
@@@ -748,7 -757,8 +793,8 @@@ static void detach_mnt(struct mount *mn
        mnt->mnt_parent = mnt;
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        list_del_init(&mnt->mnt_child);
 -      list_del_init(&mnt->mnt_hash);
 +      hlist_del_init_rcu(&mnt->mnt_hash);
+       list_del_init(&mnt->mnt_mp_list);
        put_mountpoint(mnt->mnt_mp);
        mnt->mnt_mp = NULL;
  }
@@@ -936,9 -943,35 +983,25 @@@ static struct mount *clone_mnt(struct m
        return ERR_PTR(err);
  }
  
 -static void delayed_free(struct rcu_head *head)
 -{
 -      struct mount *mnt = container_of(head, struct mount, mnt_rcu);
 -      kfree(mnt->mnt_devname);
 -#ifdef CONFIG_SMP
 -      free_percpu(mnt->mnt_pcp);
 -#endif
 -      kmem_cache_free(mnt_cache, mnt);
 -}
 -
+ static void cleanup_mnt(struct mount *mnt)
+ {
+       fsnotify_vfsmount_delete(&mnt->mnt);
+       dput(mnt->mnt.mnt_root);
+       deactivate_super(mnt->mnt.mnt_sb);
+       mnt_free_id(mnt);
+       complete(mnt->mnt_undone);
 -      call_rcu(&mnt->mnt_rcu, delayed_free);
++      call_rcu(&mnt->mnt_rcu, delayed_free_vfsmnt);
+ }
+ static void cleanup_mnt_work(struct work_struct *work)
+ {
+       cleanup_mnt(container_of(work, struct mount, mnt_cleanup_work));
+ }
  static void mntput_no_expire(struct mount *mnt)
  {
- put_again:
+       struct completion undone;
        rcu_read_lock();
        mnt_add_count(mnt, -1);
        if (likely(mnt->mnt_ns)) { /* shouldn't be the last one */
diff --cc fs/nfs/dir.c
Simple merge
diff --cc fs/proc/base.c
Simple merge
diff --cc fs/proc/fd.c
Simple merge
Simple merge