From 5d79666f8f7563ed9904a7ce2087f430ee2702b6 Mon Sep 17 00:00:00 2001 From: Valerie Aurora Date: Tue, 3 Aug 2010 20:09:19 -0400 Subject: [PATCH] mount: get most recently mounted fs from /etc/mtab. 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 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 --- mount/fstab.c | 18 ++++++++++++++++++ mount/fstab.h | 1 + mount/mount.c | 5 +++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/mount/fstab.c b/mount/fstab.c index 97d64a2c..bfb2e5d3 100644 --- a/mount/fstab.c +++ b/mount/fstab.c @@ -215,6 +215,24 @@ getmntfile (const char *name) { 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. diff --git a/mount/fstab.h b/mount/fstab.h index 38f7bab9..e9855064 100644 --- a/mount/fstab.h +++ b/mount/fstab.h @@ -15,6 +15,7 @@ struct mntentchn { 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); diff --git a/mount/mount.c b/mount/mount.c index 433941e1..2e819e95 100644 --- a/mount/mount.c +++ b/mount/mount.c @@ -2140,10 +2140,11 @@ getfs(const char *spec, const char *uuid, const char *label) /* * 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; -- 2.39.5