I spent most of the day tracking down this subtle remount bug. I
think this is the correct solution but I'd appreciate some
double-checking. I suspect this bug will munge the mount options
whenever you remount a file system mounted on the same mountpoint as
another file system, using the mountpoint as the handle.
-VAL
commit
c010b3a0783430e2b94f3b3dc0929ae299e383eb
Author: Valerie Aurora <vaurora@redhat.com>
Date: Tue Aug 3 16:32:52 2010 -0700
mount: get most recently mounted fs from /etc/mtab.
In mount, when using /etc/mtab to lookup a mount entry, get the most
recently mounted entry instead of the first mounted entry. You want
to manipulate the most recent mount, not a covered mount. See comment
to umount_one_bw().
This bug has been util-linux-ng since the first git checkin. It
finally showed up on my system with the change to stop using
SETLOOP_AUTOCLEAR if /etc/mtab is writable (commit
af092544). If you
do a remount of a file system mounted on the same dir as another file
system, it will take the options from the first mount and write them
out to /etc/mtab as the options to the second mount - including, in
the case of a loop device, loop=/dev/loop0. Then when you umount the
second mount, it grabs the line from /etc/mtab and tries to tear down
the loop device, which complains because it is still in use by the
first mount.
Reproducible test case (on a system with writable /etc/mtab):
mount -o loop,ro /tmp/ro /mnt
mount -t tmpfs tmpfs /mnt
mount -o remount,ro /mnt
cat /etc/mtab | tail -2
Signed-off-by: Valerie Aurora <vaurora@redhat.com>
return NULL;
}
+/*
+ * Given the name NAME, and the place MCPREV we found it last time,
+ * try to find it in mtab.
+ */
+struct mntentchn *
+getmntfilebackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = mtab_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, name) ||
+ streq(mc->m.mnt_fsname, name))
+ return mc;
+ return NULL;
+}
+
/*
* Given the directory name NAME, and the place MCPREV we found it last time,
* try to find more occurrences.
struct mntentchn *mtab_head (void);
struct mntentchn *getmntfile (const char *name);
+struct mntentchn *getmntfilebackward (const char *name, struct mntentchn *mcprev);
struct mntentchn *getmntoptfile (const char *file);
struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc);
struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc);
/*
* D) remount -- try /etc/mtab
* Earlier mtab was tried first, but this would sometimes try the
- * wrong mount in case mtab had the root device entry wrong.
+ * wrong mount in case mtab had the root device entry wrong. Try
+ * the last occurrence first, since that is the visible mount.
*/
if (!mc && (devname || spec))
- mc = getmntfile (devname ? devname : spec);
+ mc = getmntfilebackward (devname ? devname : spec, NULL);
my_free(devname);
return mc;