Bug 803799

Summary: [util-linux/mount/umount.c] umount -d does not check for LD_FLAGS_AUTOCLEAR flag
Product: Red Hat Enterprise Linux 7 Reporter: Boris Ranto <branto>
Component: util-linuxAssignee: Karel Zak <kzak>
Status: CLOSED NOTABUG QA Contact:
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 7.0Keywords: Regression
Target Milestone: rc   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2012-04-02 16:06:27 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Boris Ranto 2012-03-15 16:50:18 UTC
Description of problem:
The umount command (with -d option) in rhel7 will attempt to release loop device even when it is set to be cleared automatically (LD_FLAGS_AUTOCLEAR).

Version-Release number of selected component (if applicable):
util-linux-2.20.1

How reproducible:
Always

Steps to Reproduce:
1. truncate -s 5G fs.img; mkfs.xfs fs.img
2. mount -o loop fs.img /media
3. umount -d /media
  
Actual results:
Umount tries to release the loop device that is set to be cleared automatically.

Expected results:
Umount does not try to release the loop device once it was already released.

Additional info:
This seems to be caused by a missing condition in mount/umount.c:303 (although the commentary references the missing condition):
299         /*
300          * Ignore the option "-d" for non-loop devices and loop devices with
301          * LO_FLAGS_AUTOCLEAR flag.
302          */
303         if (delloop && is_loop_device(spec))
304                 myloop = 1;
305 

Further looking at the code, the condition just moved to line 411, the problem is that the condition is called once the umount unmounted the file system (and even released the loop device in case of the autoclear mechanism). This means that the condition will be satisfied and the umount command will attempt to release a loop device that was already released.

I've tested the umount behaviour with the following simple patch and it fixed the issue for me:

--- mount/umount-old.c	2012-03-15 11:34:41.294141058 -0400
+++ mount/umount.c	2012-03-15 11:45:43.900117187 -0400
@@ -300,7 +300,7 @@ umount_one (const char *spec, const char
 	 * Ignore the option "-d" for non-loop devices and loop devices with
 	 * LO_FLAGS_AUTOCLEAR flag.
 	 */
-	if (delloop && is_loop_device(spec))
+	if (delloop && is_loop_device(spec) && !is_loop_autoclear(spec))
 		myloop = 1;
 
 	if (restricted) {
@@ -408,7 +408,7 @@ umount_one (const char *spec, const char
 			loopdev = spec;
 	}
  gotloop:
-	if (loopdev && !is_loop_autoclear(loopdev))
+	if (loopdev)
 		del_loop(loopdev);
 
  writemtab:

I suspect that the reason for the move was to fix the cases when the loop autoclear mechanism fails (this occurred several times while using xfstests test no. 073). Then it would probably make more sense to move the condition completely. The problem is that if we move the condition completely it will succeed trivially for a released loop device (the device will be a loop device and it will not have the autoclear flag set) and the code will try to release already released device, again.
In this case the best fix would probably be to check that the device is assigned and without the autoclear flag, i.e something like this (assuming the function is_loop_assigned is defined somewhere else and tests whether the given device is an assigned loop device):

--- mount/umount-old.c	2012-03-15 11:34:41.294141058 -0400
+++ mount/umount.c	2012-03-15 12:42:02.171998383 -0400
@@ -271,7 +271,6 @@ umount_one (const char *spec, const char
 	int extra_flags = 0;
 	const char *loopdev, *target = node;
 	char *targetbuf = NULL;
-	int myloop = 0;
 
 	/* Special case for root.  As of 0.99pl10 we can (almost) unmount root;
 	   the kernel will remount it readonly so that we can carry on running
@@ -296,12 +295,6 @@ umount_one (const char *spec, const char
 	/* Skip the actual umounting for --fake */
 	if (fake)
 		goto writemtab;
-	/*
-	 * Ignore the option "-d" for non-loop devices and loop devices with
-	 * LO_FLAGS_AUTOCLEAR flag.
-	 */
-	if (delloop && is_loop_device(spec))
-		myloop = 1;
 
 	if (restricted) {
 		if (umount_nofollow_support())
@@ -403,12 +396,16 @@ umount_one (const char *spec, const char
 				node = mc->m.mnt_dir;
 		}
 
-		/* Also free loop devices when -d flag is given */
-		if (myloop)
+		/* Also attemp to free loop devices when -d flag is given */
+		if (delloop)
 			loopdev = spec;
 	}
  gotloop:
-	if (loopdev && !is_loop_autoclear(loopdev))
+	/*
+	 * Ignore the option "-d" for non-loop devices and loop devices with
+	 * LO_FLAGS_AUTOCLEAR flag.
+	 */
+	if (loopdev && is_loop_assigned(loopdev) && !is_loop_autoclear(loopdev))
 		del_loop(loopdev);
 
  writemtab:

Comment 1 Karel Zak 2012-04-02 16:06:27 UTC
For RHEL7 we will use util-linux 2.21 where is pure libmount based umount(8)(rebase requested :bug #804780).

Anyway, I have ported your patch to the current upstream code, because old mount(8) is still upstream default. (commit 72bec4d0dd5c94f15ed7c17d3452ba87ed737209)