[PATCH] discover/device-handler: Attempt to retry failed mounts

Sam Mendoza-Jonas sam at mendozajonas.com
Wed Mar 16 13:26:59 AEDT 2016


Commit 6c1a9dd, "discover: Allow fs recovery if snapshot available",
forced the use of 'norecovery' for all XFS mounts to avoid failing when
a cross-endian journal existed. This is a bit heavy handed, healthy XFS
file systems can still be safely mounted, as can dirty filesystems in
the same endian as Petitboot.

This adds try_mount() which opportunistically mounts devices and falls
back to using 'norecovery' where possible on failure. This enables XFS
filesystems to be mounted read-write when possible. try_mount() contains
the logic previously described by fs_parameters(), and should be used in
place of any existing calls to mount().

Signed-off-by: Sam Mendoza-Jonas <sam at mendozajonas.com>
---
 discover/device-handler.c | 100 ++++++++++++++++++++++++++--------------------
 1 file changed, 56 insertions(+), 44 deletions(-)

diff --git a/discover/device-handler.c b/discover/device-handler.c
index 9de2a19..489ecd7 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -1258,30 +1258,6 @@ static void device_handler_reinit_sources(struct device_handler *handler)
 			handler->dry_run);
 }
 
-static const char *fs_parameters(struct discover_device *dev,
-				 unsigned int rw_flags)
-{
-	const char *fstype = discover_device_get_param(dev, "ID_FS_TYPE");
-
-	/* XFS journals are not cross-endian compatible; don't try recovery
-	 * even if we have a snapshot */
-	if (!strncmp(fstype, "xfs", strlen("xfs")))
-		return "norecovery";
-
-	/* If we have a snapshot available allow touching the filesystem */
-	if (dev->ramdisk)
-		return "";
-
-	if ((rw_flags | MS_RDONLY) != MS_RDONLY)
-		return "";
-
-	/* Avoid writes due to journal replay if we don't have a snapshot */
-	if (!strncmp(fstype, "ext4", strlen("ext4")))
-		return "norecovery";
-
-	return "";
-}
-
 static inline const char *get_device_path(struct discover_device *dev)
 {
 	return dev->ramdisk ? dev->ramdisk->snapshot : dev->device_path;
@@ -1368,6 +1344,52 @@ static bool check_existing_mount(struct discover_device *dev)
 	return mnt != NULL;
 }
 
+/*
+ * Attempt to mount a filesystem safely, while handling certain filesytem-
+ * specific options
+ */
+static int try_mount(const char *device_path, const char *mount_path,
+			     const char *fstype, unsigned long flags,
+			     bool have_snapshot)
+{
+	const char *fs, *safe_opts;
+	int rc;
+
+	/* Mount ext3 as ext4 instead so 'norecovery' can be used */
+	if (strncmp(fstype, "ext3", strlen("ext3")) == 0) {
+		pb_debug("Mounting ext3 filesystem as ext4\n");
+		fs = "ext4";
+	} else
+		fs = fstype;
+
+	if (strncmp(fs, "xfs", strlen("xfs")) == 0 ||
+	    strncmp(fs, "ext4", strlen("ext4")) == 0)
+		safe_opts = "norecovery";
+	else
+		safe_opts = NULL;
+
+	errno = 0;
+	/* If no snapshot is available don't attempt recovery */
+	if (!have_snapshot)
+		return mount(device_path, mount_path, fs, flags, safe_opts);
+
+	rc = mount(device_path, mount_path, fs, flags, NULL);
+
+	if (!rc)
+		return rc;
+
+	/* Mounting failed; some filesystems will fail to mount if a recovery
+	 * journal exists (eg. cross-endian XFS), so try again with norecovery
+	 * where that option is available.
+	 * If mounting read-write just return the error as norecovery is not a
+	 * valid option */
+	if ((flags & MS_RDONLY) != MS_RDONLY || !safe_opts)
+		return rc;
+
+	errno = 0;
+	return mount(device_path, mount_path, fs, flags, safe_opts);
+}
+
 static int mount_device(struct discover_device *dev)
 {
 	const char *fstype, *device_path;
@@ -1386,13 +1408,6 @@ 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);
 
@@ -1405,10 +1420,9 @@ static int mount_device(struct discover_device *dev)
 	device_path = get_device_path(dev);
 
 	pb_log("mounting device %s read-only\n", dev->device_path);
-	errno = 0;
-	rc = mount(device_path, dev->mount_path, fstype,
-			MS_RDONLY | MS_SILENT,
-			fs_parameters(dev, MS_RDONLY));
+	rc = try_mount(device_path, dev->mount_path, fstype,
+		       MS_RDONLY | MS_SILENT, dev->ramdisk);
+
 	if (!rc) {
 		dev->mounted = true;
 		dev->mounted_rw = false;
@@ -1488,9 +1502,8 @@ int device_request_write(struct discover_device *dev, bool *release)
 		return -1;
 	}
 
-	rc = mount(device_path, dev->mount_path, fstype,
-			MS_SILENT,
-			fs_parameters(dev, MS_REMOUNT));
+	rc = try_mount(device_path, dev->mount_path, fstype,
+		       MS_SILENT, dev->ramdisk);
 	if (rc)
 		goto mount_ro;
 
@@ -1501,9 +1514,9 @@ int device_request_write(struct discover_device *dev, bool *release)
 mount_ro:
 	pb_log("Unable to remount device %s read-write: %s\n",
 	       device_path, strerror(errno));
-	if (mount(device_path, dev->mount_path, fstype,
-			MS_RDONLY | MS_SILENT,
-			fs_parameters(dev, MS_RDONLY)))
+	rc = try_mount(device_path, dev->mount_path, fstype,
+		       MS_RDONLY | MS_SILENT, dev->ramdisk);
+	if (rc)
 		pb_log("Unable to recover mount for %s: %s\n",
 		       device_path, strerror(errno));
 	return -1;
@@ -1534,9 +1547,8 @@ void device_release_write(struct discover_device *dev, bool release)
 		device_path = get_device_path(dev);
 	}
 
-	if (mount(device_path, dev->mount_path, fstype,
-			MS_RDONLY | MS_SILENT,
-			fs_parameters(dev, MS_RDONLY)))
+	if (try_mount(device_path, dev->mount_path, fstype,
+		       MS_RDONLY | MS_SILENT, dev->ramdisk))
 		pb_log("Failed to remount %s read-only: %s\n",
 		       device_path, strerror(errno));
 	else
-- 
2.7.3



More information about the Petitboot mailing list