]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/namespace.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[karo-tx-linux.git] / fs / namespace.c
index 773435ca300de1b10c3fea4881429a293faae42b..e6081996c9a2f9d26525740545445630c4737583 100644 (file)
@@ -273,6 +273,15 @@ static unsigned int mnt_get_writers(struct mount *mnt)
 #endif
 }
 
+static int mnt_is_readonly(struct vfsmount *mnt)
+{
+       if (mnt->mnt_sb->s_readonly_remount)
+               return 1;
+       /* Order wrt setting s_flags/s_readonly_remount in do_remount() */
+       smp_rmb();
+       return __mnt_is_readonly(mnt);
+}
+
 /*
  * Most r/o checks on a fs are for operations that take
  * discrete amounts of time, like a write() or unlink().
@@ -312,12 +321,10 @@ int mnt_want_write(struct vfsmount *m)
         * MNT_WRITE_HOLD is cleared.
         */
        smp_rmb();
-       if (__mnt_is_readonly(m)) {
+       if (mnt_is_readonly(m)) {
                mnt_dec_writers(mnt);
                ret = -EROFS;
-               goto out;
        }
-out:
        preempt_enable();
        return ret;
 }
@@ -435,6 +442,42 @@ static void __mnt_unmake_readonly(struct mount *mnt)
        br_write_unlock(vfsmount_lock);
 }
 
+int sb_prepare_remount_readonly(struct super_block *sb)
+{
+       struct mount *mnt;
+       int err = 0;
+
+       /* Racy optimization.  Recheck the counter under MNT_WRITE_HOLD */
+       if (atomic_long_read(&sb->s_remove_count))
+               return -EBUSY;
+
+       br_write_lock(vfsmount_lock);
+       list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
+               if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
+                       mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
+                       smp_mb();
+                       if (mnt_get_writers(mnt) > 0) {
+                               err = -EBUSY;
+                               break;
+                       }
+               }
+       }
+       if (!err && atomic_long_read(&sb->s_remove_count))
+               err = -EBUSY;
+
+       if (!err) {
+               sb->s_readonly_remount = 1;
+               smp_wmb();
+       }
+       list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
+               if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
+                       mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
+       }
+       br_write_unlock(vfsmount_lock);
+
+       return err;
+}
+
 static void free_vfsmnt(struct mount *mnt)
 {
        kfree(mnt->mnt_devname);
@@ -671,6 +714,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        mnt->mnt.mnt_sb = root->d_sb;
        mnt->mnt_mountpoint = mnt->mnt.mnt_root;
        mnt->mnt_parent = mnt;
+       br_write_lock(vfsmount_lock);
+       list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
+       br_write_unlock(vfsmount_lock);
        return &mnt->mnt;
 }
 EXPORT_SYMBOL_GPL(vfs_kern_mount);
@@ -699,6 +745,9 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
                mnt->mnt.mnt_root = dget(root);
                mnt->mnt_mountpoint = mnt->mnt.mnt_root;
                mnt->mnt_parent = mnt;
+               br_write_lock(vfsmount_lock);
+               list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
+               br_write_unlock(vfsmount_lock);
 
                if (flag & CL_SLAVE) {
                        list_add(&mnt->mnt_slave, &old->mnt_slave_list);
@@ -781,6 +830,7 @@ put_again:
                acct_auto_close_mnt(&mnt->mnt);
                goto put_again;
        }
+       list_del(&mnt->mnt_instance);
        br_write_unlock(vfsmount_lock);
        mntfree(mnt);
 }
@@ -836,12 +886,12 @@ static inline void mangle(struct seq_file *m, const char *s)
  *
  * See also save_mount_options().
  */
-int generic_show_options(struct seq_file *m, struct vfsmount *mnt)
+int generic_show_options(struct seq_file *m, struct dentry *root)
 {
        const char *options;
 
        rcu_read_lock();
-       options = rcu_dereference(mnt->mnt_sb->s_options);
+       options = rcu_dereference(root->d_sb->s_options);
 
        if (options != NULL && options[0]) {
                seq_putc(m, ',');