discover: Mount with norecovery, avoid rw mount of XFS filesystems
authorSamuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Mon, 23 Mar 2015 05:54:45 +0000 (16:54 +1100)
committerSamuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Thu, 26 Mar 2015 05:26:02 +0000 (16:26 +1100)
Journaled filesytems may still write to their disk even if the disk is
mounted read only. Petitboot should avoid modifying any disks
automatically, and in mixed-endian systems this can also cause journal
operations to fail. Use the 'norecovery' option on filesystems that
support it to skip the journal replay.

Additionally, mounting an XFS filesystem as read-write in such a case
will cause the call to mount to hang indefinitely. Avoid this generally
by explicitly unmounting and (re)mounting when mounting read-write.

Signed-off-by: Samuel Mendoza-Jonas <sam.mj@au1.ibm.com>
discover/device-handler.c

index 5d9f98892eb792db789de351fb750bb95cd0e3f9..f0537134c1a3bc2a517f40b8cc385525940dfe0b 100644 (file)
@@ -1082,6 +1082,20 @@ static void device_handler_reinit_sources(struct device_handler *handler)
                        handler->dry_run);
 }
 
+static const char *fs_parameters(unsigned int rw_flags, const char *fstype)
+{
+       if ((rw_flags | MS_RDONLY) != MS_RDONLY)
+               return "";
+
+       /* Avoid writing back to the disk on journaled filesystems */
+       if (!strncmp(fstype, "ext4", strlen("ext4")))
+               return "norecovery";
+       if (!strncmp(fstype, "xfs", strlen("xfs")))
+               return "norecovery";
+
+       return "";
+}
+
 static bool check_existing_mount(struct discover_device *dev)
 {
        struct stat devstat, mntstat;
@@ -1155,6 +1169,13 @@ static int mount_device(struct discover_device *dev)
        if (!fstype)
                return 0;
 
+       /* ext3 treats the norecovery option as an error, so mount the device
+        * as an ext4 filesystem instead */
+       if (!strncmp(fstype, "ext3", strlen("ext3"))) {
+               pb_debug("Mounting ext3 filesystem as ext4\n");
+               fstype = talloc_asprintf(dev, "ext4");
+       }
+
        dev->mount_path = join_paths(dev, mount_base(),
                                        dev->device_path);
 
@@ -1167,7 +1188,8 @@ static int mount_device(struct discover_device *dev)
        pb_log("mounting device %s read-only\n", dev->device_path);
        errno = 0;
        rc = mount(dev->device_path, dev->mount_path, fstype,
-                       MS_RDONLY | MS_SILENT, "");
+                       MS_RDONLY | MS_SILENT,
+                       fs_parameters(MS_RDONLY, fstype));
        if (!rc) {
                dev->mounted = true;
                dev->mounted_rw = false;
@@ -1209,6 +1231,7 @@ static int umount_device(struct discover_device *dev)
 
 int device_request_write(struct discover_device *dev, bool *release)
 {
+       const char *fstype;
        int rc;
 
        *release = false;
@@ -1219,25 +1242,48 @@ int device_request_write(struct discover_device *dev, bool *release)
        if (dev->mounted_rw)
                return 0;
 
+       fstype = discover_device_get_param(dev, "ID_FS_TYPE");
+
        pb_log("remounting device %s read-write\n", dev->device_path);
-       rc = mount(dev->device_path, dev->mount_path, "",
-                       MS_REMOUNT | MS_SILENT, "");
-       if (rc)
+
+       rc = umount(dev->mount_path);
+       if (rc) {
+               pb_log("Failed to unmount %s\n", dev->mount_path);
                return -1;
+       }
+       rc = mount(dev->device_path, dev->mount_path, fstype,
+                       MS_SILENT,
+                       fs_parameters(MS_REMOUNT, fstype));
+       if (rc)
+               goto mount_ro;
 
        dev->mounted_rw = true;
        *release = true;
        return 0;
+
+mount_ro:
+       pb_log("Unable to remount device %s read-write\n", dev->device_path);
+       rc = mount(dev->device_path, dev->mount_path, fstype,
+                       MS_RDONLY | MS_SILENT,
+                       fs_parameters(MS_RDONLY, fstype));
+       if (rc)
+               pb_log("Unable to recover mount for %s\n", dev->device_path);
+       return -1;
 }
 
 void device_release_write(struct discover_device *dev, bool release)
 {
+       const char *fstype;
+
        if (!release)
                return;
 
+       fstype = discover_device_get_param(dev, "ID_FS_TYPE");
+
        pb_log("remounting device %s read-only\n", dev->device_path);
        mount(dev->device_path, dev->mount_path, "",
-                       MS_REMOUNT | MS_RDONLY | MS_SILENT, "");
+                       MS_REMOUNT | MS_RDONLY | MS_SILENT,
+                       fs_parameters(MS_RDONLY, fstype));
        dev->mounted_rw = false;
 }