From 11b916cdabd9f57a4cddf10fd743af2165930dcd Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Tue, 3 Mar 2020 11:39:10 +0100
Subject: [PATCH] libmount: try read-only mount on write-protected superblock
 too

The classic mount(8) behavior is to try read-only on write-protected devices
if the first mount syscall attempt ends with EACCES.

It seems we can implement this feature also for EBUSY if the same mount source
is already mounted with "ro" superblock option.

The typical use-case is iso image (always read-only) mounted on two places.

Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1809124
Signed-off-by: Karel Zak <kzak@redhat.com>
---
 libmount/src/context_mount.c | 21 ++++++++++++++++++---
 sys-utils/mount.8            | 13 +++++++++----
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index 0c4b765..efd7050 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -1149,6 +1149,18 @@ static struct libmnt_fs *get_already_mounted_source(struct libmnt_context *cxt)
 	return NULL;
 }
 
+/*
+ * Checks if source filesystem superblock is already ro-mounted. Note that we
+ * care about FS superblock as VFS node is irrelevant here.
+ */
+static int is_source_already_rdonly(struct libmnt_context *cxt)
+{
+	struct libmnt_fs *fs = get_already_mounted_source(cxt);
+	const char *opts = fs ? mnt_fs_get_fs_options(fs) : NULL;
+
+	return opts && mnt_optstr_get_option(opts, "ro", NULL, NULL) == 0;
+}
+
 /**
  * mnt_context_finalize_mount:
  * @cxt: context
@@ -1250,11 +1262,14 @@ again:
 		rc = mnt_context_update_tabs(cxt);
 
 	/*
-	 * Read-only device; try mount filesystem read-only
+	 * Read-only device or already read-only mounted FS.
+	 * Try mount the filesystem read-only.
 	 */
 	if ((rc == -EROFS && !mnt_context_syscall_called(cxt))	/* before syscall; rdonly loopdev */
 	     || mnt_context_get_syscall_errno(cxt) == EROFS	/* syscall failed with EROFS */
-	     || mnt_context_get_syscall_errno(cxt) == EACCES)	/* syscall failed with EACCES */
+	     || mnt_context_get_syscall_errno(cxt) == EACCES	/* syscall failed with EACCES */
+	     || (mnt_context_get_syscall_errno(cxt) == EBUSY	/* already ro-mounted FS */
+		 && is_source_already_rdonly(cxt)))
 	{
 		unsigned long mflags = 0;
 
@@ -1630,7 +1645,7 @@ int mnt_context_get_mount_excode(
 		 * Libmount success && syscall success.
 		 */
 		if (buf && mnt_context_forced_rdonly(cxt))
-			snprintf(buf, bufsz, _("WARNING: device write-protected, mounted read-only"));
+			snprintf(buf, bufsz, _("WARNING: source write-protected, mounted read-only"));
 		return MNT_EX_SUCCESS;
 	}
 
diff --git a/sys-utils/mount.8 b/sys-utils/mount.8
index 698b0f0..eddf05a 100644
--- a/sys-utils/mount.8
+++ b/sys-utils/mount.8
@@ -931,12 +931,17 @@ Mount the partition that has the specified
 Verbose mode.
 .TP
 .BR \-w , " \-\-rw" , " \-\-read\-write"
-Mount the filesystem read/write. The read-write is kernel default.  A synonym is
+Mount the filesystem read/write.  The read-write is kernel default and
+.BR mount (8)
+default is to try read-only if the previous mount syscall with read-write flags
+on write-protected devices of filesystems failed.
+.sp
+A synonym is
 .BR "\-o rw" .
 
-Note that specify \fB\-w\fR on command line forces \fBmount\fR command
-to never try read-only mount on write-protected devices. The default is
-try read-only if the previous mount syscall with read-write flags failed.
+Note that specify \fB\-w\fR on command line forces \fBmount\fR command to never
+try read-only mount on write-protected devices or already mounted read-only
+filesystems.
 .TP
 .BR \-V , " \-\-version"
 Display version information and exit.
-- 
1.8.3.1

