xfs
[Top] [All Lists]

[PATCH 3/3] xfs: test for valid remount options, error if not

To: xfs@xxxxxxxxxxx
Subject: [PATCH 3/3] xfs: test for valid remount options, error if not
From: Eric Sandeen <sandeen@xxxxxxxxxxx>
Date: Wed, 10 Feb 2016 17:45:33 -0600
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <56BBC982.50804@xxxxxxxxxx>
References: <56BBC982.50804@xxxxxxxxxx>
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:38.0) Gecko/20100101 Thunderbird/38.5.1
This patch attempts to check for a valid set of remount
options.  As far as I can tell, it's tricky; as the old
comment says, on remount we may get a long string of
options from /proc/mounts and/or /etc/mtab, as well
as options specified on the commandline.  Later options
may negate previous options, etc.

At the most basic level, we may be handed a mount option
which we do not handle on remount, but which may not actually
be a change from the current mount option set.

Unfortunately our mount option state is somewhat far flung;
a combinations of m_flags, and values in various other
mount structure members; see the showargs function for
a taste of that.

So this extends xfs_test_remount_options() to do a full set
of mount processing of the options remount sees, to arrive
at a final state, then compares that state to the current
state, and determines if we can proceed.

Signed-off-by: Eric Sandeen <sandeen@xxxxxxxxxx>
---

This is lightly tested; mostly just a sanity check to see
if this approach is a "wtf?" or a "yeah, seems ok."

diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 986290c..3d4187c 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -455,7 +455,7 @@ xfs_set_maxicount(xfs_mount_t *mp)
  * We use smaller I/O sizes when the file system
  * is being used for NFS service (wsync mount option).
  */
-STATIC void
+void
 xfs_set_rw_sizes(xfs_mount_t *mp)
 {
        xfs_sb_t        *sbp = &(mp->m_sb);
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index a4e03ab..bee9284 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -335,6 +335,7 @@ extern int  xfs_sb_validate_fsb_count(struct xfs_sb *, 
__uint64_t);
 
 extern int     xfs_dev_is_read_only(struct xfs_mount *, char *);
 
+extern void    xfs_set_rw_sizes(xfs_mount_t *);
 extern void    xfs_set_low_space_thresholds(struct xfs_mount *);
 
 int    xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index d1cd4fa..50e15d8 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -65,6 +65,8 @@ static struct kset *xfs_kset;         /* top-level xfs sysfs 
dir */
 static struct xfs_kobj xfs_dbg_kobj;   /* global debug sysfs attrs */
 #endif
 
+STATIC int xfs_finish_flags(struct xfs_mount *mp);
+
 /*
  * Table driven mount option parser.
  */
@@ -1167,24 +1169,87 @@ xfs_quiesce_attr(
        xfs_log_quiesce(mp);
 }
 
+#define XFS_BAD_REMOUNT_GOTO(mp, option, l)    \
+       { \
+               xfs_warn(mp, \
+                 option " options may not be changed via remount");    \
+               goto l; \
+       }
+/*
+ * Remount can only handle a few options today.
+ * First, check for completely bogus options by looking for errors
+ * from xfs_parseargs.
+ * But if that passes, look at the result to make sure we're not changin
+ * anything that is not, in fact, remountable.  Do this by parsing all
+ * options into a dummy mount structure, and compare the final result
+ * to the current one so we can see what actually changed.
+ */
 STATIC int
 xfs_test_remount_options(
        struct super_block      *sb,
        struct xfs_mount        *mp,
        char                    *options)
 {
-       int                     error = 0;
-       struct xfs_mount        *tmp;
+       int                     error;
+       struct xfs_mount        *tmp_mp;
+
+       __uint64_t              ok_flags, changed_flags;
 
-       tmp = kmem_zalloc(sizeof(*tmp), KM_MAYFAIL);
-       if (!tmp)
+       tmp_mp = kmem_zalloc(sizeof(*tmp_mp), KM_MAYFAIL);
+       if (!tmp_mp)
                return -ENOMEM;
 
-       tmp->m_super = sb;
-       error = xfs_parseargs(tmp, options);
-       xfs_free_fsname(tmp);
-       kfree(tmp);
+       tmp_mp->m_super = sb;
+       /* structure copy */
+       tmp_mp->m_sb = mp->m_sb;
+
+       /* Emulate mount parsing & flag setting on tmp_mp */
+       error = xfs_parseargs(tmp_mp, options);
+       if (error)
+               goto out;
+
+       error = xfs_finish_flags(tmp_mp);
+       if (error)
+               goto out;
+
+       xfs_set_rw_sizes(tmp_mp);
 
+       /* The only flags we can change on remount */
+       ok_flags = XFS_MOUNT_BARRIER | XFS_MOUNT_RDONLY |
+                  XFS_MOUNT_SMALL_INUMS | XFS_MOUNT_32BITINODES; 
+       /* This is only used internally, so OK as well */
+       ok_flags |= XFS_MOUNT_WAS_CLEAN;
+
+       /* The flags that *did* change */
+       changed_flags = (tmp_mp->m_flags ^ mp->m_flags);
+
+       error = -EINVAL;
+
+       if (tmp_mp->m_qflags != mp->m_qflags)
+               XFS_BAD_REMOUNT_GOTO(mp, "quota", out);
+
+       if (tmp_mp->m_logbufs != mp->m_logbufs ||
+           tmp_mp->m_logbsize != mp->m_logbsize)
+               XFS_BAD_REMOUNT_GOTO(mp, "logbufs/logbsize", out);
+
+       if (tmp_mp->m_readio_log != mp->m_readio_log ||
+           tmp_mp->m_writeio_log != mp->m_writeio_log)
+               XFS_BAD_REMOUNT_GOTO(mp, "allocsize/biosize", out);
+
+       if ((tmp_mp->m_logname && mp->m_logname &&
+            strcmp(tmp_mp->m_logname, mp->m_logname)) ||
+           (tmp_mp->m_rtname &&  mp->m_rtname &&
+            strcmp(tmp_mp->m_rtname, mp->m_rtname)))
+               XFS_BAD_REMOUNT_GOTO(mp, "logdev/rtdev", out);
+
+       /* Catch-all for anything else */
+       if (changed_flags & ~ok_flags)
+               XFS_BAD_REMOUNT_GOTO(mp, "specified", out);
+
+       error = 0;
+out:
+       xfs_free_fsname(tmp_mp);
+       kfree(tmp_mp);
        return error;
 }
 
@@ -1200,7 +1265,12 @@ xfs_fs_remount(
        char                    *p;
        int                     error;
 
-       /* First, check for complete junk; i.e. invalid options */
+       /*
+        * Remounting is tricky; we get various combinations
+        * of options, both pre-existing and changed, here.
+        * This function tries to ensure that what we got
+        * is a sane set for remounting, and errors if not.
+        */
        error = xfs_test_remount_options(sb, mp, options);
        if (error)
                return error;
@@ -1228,28 +1298,13 @@ xfs_fs_remount(
                        break;
                default:
                        /*
-                        * Logically we would return an error here to prevent
-                        * users from believing they might have changed
-                        * mount options using remount which can't be changed.
-                        *
-                        * But unfortunately mount(8) adds all options from
-                        * mtab and fstab to the mount arguments in some cases
-                        * so we can't blindly reject options, but have to
-                        * check for each specified option if it actually
-                        * differs from the currently set option and only
-                        * reject it if that's the case.
-                        *
-                        * Until that is implemented we return success for
-                        * every remount request, and silently ignore all
-                        * options that we can't actually change.
+                        * xfs_test_remount_options really should have errored
+                        * out on any non-remountable options; anything that 
got 
+                        * here should be a no-op; a re-statement of existing
+                        * options. If something slipped through: too bad!
+                        * We'll just ignore it.
                         */
-#if 0
-                       xfs_info(mp,
-               "mount option \"%s\" not supported for remount", p);
-                       return -EINVAL;
-#else
                        break;
-#endif
                }
        }
 


<Prev in Thread] Current Thread [Next in Thread>