changeset 1380:9748833918b0

Try to lookup all umounts in /proc/mounts so we can auto-zap loopback devices.
author Rob Landley <rob@landley.net>
date Sat, 05 Jul 2014 23:26:05 -0500
parents b02c05e64f20
children cae43f3cfff4
files toys/lsb/umount.c
diffstat 1 files changed, 56 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/toys/lsb/umount.c	Fri Jul 04 21:46:00 2014 -0500
+++ b/toys/lsb/umount.c	Sat Jul 05 23:26:05 2014 -0500
@@ -8,7 +8,7 @@
  * nor per-process mount namespaces can work sanely with mtab. The kernel
  * tracks mount points now, a userspace application can't do so anymore.
 
-USE_UMOUNT(NEWTOY(umount, "ndDflrat*v", TOYFLAG_BIN|TOYFLAG_STAYROOT))
+USE_UMOUNT(NEWTOY(umount, "ndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
 
 config UMOUNT
   bool "umount"
@@ -19,11 +19,12 @@
     Unmount the listed filesystems.
 
     -a	Unmount all mounts in /proc/mounts instead of command line list
-    -t	Restrict "all" to mounts of TYPE (or use "noTYPE" to skip)
     -D  Don't free loopback device(s).
     -f  Force unmount.
     -l  Lazy unmount (detach from filesystem now, close when last user does).
+    -n	Don't use /proc/mounts
     -r  Remount read only if unmounting fails.
+    -t	Restrict "all" to mounts of TYPE (or use "noTYPE" to skip)
     -v	Verbose
 */
 
@@ -63,19 +64,16 @@
   *old = temp;
 }
 
-void arg_comma_collate(char **old, struct arg_list *arg)
-{
-  while (arg) {
-    comma_collate(old, arg->arg);
-    arg = arg->next;
-  }
-}
-
+// iterate through strings in a comma separated list.
+// returns start of next entry or NULL if none
+// sets *len to length of entry (not including comma)
+// advances *list to start of next entry
 char *comma_iterate(char **list, int *len)
 {
   char *start = *list, *end;
 
   if (!*list) return 0;
+
   if (!(end = strchr(*list, ','))) {
     *len = strlen(*list);
     *list = 0;
@@ -84,25 +82,42 @@
   return start;
 }
 
-static void do_umount(char *dir, int flags)
+static void do_umount(char *dir, char *dev, int flags)
 {
   if (!umount2(dir, flags)) {
-    if (toys.optflags & FLAG_v) printf("%s unmounted", dir);
+    if (toys.optflags & FLAG_v) xprintf("%s unmounted\n", dir);
+
+    // Attempt to disassociate loopback device. This ioctl should be ignored
+    // for anything else, because lanana allocated ioctl range 'L' to loopback
+    if (dev && !(toys.optflags & FLAG_D)) {
+      int lfd = open(dev, O_RDONLY);
+
+      if (lfd != -1) {
+        // This is LOOP_CLR_FD, fetching it from headers is awkward
+        if (!ioctl(lfd, 0x4C01) && (toys.optflags & FLAG_v))
+          xprintf("%s cleared\n", dev);
+        close(lfd);
+      }
+    }
+
     return;
   }
+
   if (toys.optflags & FLAG_r) {
     if (!mount("", dir, "", MS_REMOUNT|MS_RDONLY, "")) {
-      if (toys.optflags & FLAG_v) printf("%s remounted ro", dir);
+      if (toys.optflags & FLAG_v) xprintf("%s remounted ro\n", dir);
       return;
     }
   }
+
   perror_msg("%s", dir);
 }
 
 void umount_main(void)
 {
+  char **optargs, *pm = "/proc/mounts";
+  struct mtab_list *mlsave = 0, *mlrev = 0, *ml;
   int flags=0;
-  char **optargs;
 
   if (!toys.optc && !(toys.optflags & FLAG_a))
     error_exit("Need 1 arg or -a");
@@ -110,27 +125,26 @@
   if (toys.optflags & FLAG_f) flags |= MNT_FORCE;
   if (toys.optflags & FLAG_l) flags |= MNT_DETACH;
 
-  for (optargs = toys.optargs; *optargs; optargs++) do_umount(*optargs, flags);
+  // Load /proc/mounts and get a reversed list (newest first)
+  // We use the list both for -a, and to umount /dev/name or do losetup -d
+  if (!(toys.optflags & FLAG_n) && !access(pm, R_OK))
+    mlrev = dlist_terminate(mlsave = xgetmountlist(pm));
 
+  // Unmount all: loop through mounted filesystems, skip -t, unmount the rest
   if (toys.optflags & FLAG_a) {
-    struct mtab_list *mlsave, *ml;
     char *typestr = 0;
-
-    if (TT.t) arg_comma_collate(&typestr, TT.t);
-
-    // Loop through mounted filesystems
-    mlsave = xgetmountlist(0);
-    ml = ml->prev;
-
-    for (ml = dlist_terminate(mlsave); ml; ml = ml->prev) {
-      if (TT.t) {
+    struct arg_list *tal;
+    
+    for (tal = TT.t; tal; tal = tal->next) comma_collate(&typestr, tal->arg);
+    for (ml = mlrev; ml; ml = ml->prev) {
+      if (typestr) {
         char *type, *types = typestr;
         int len, skip = strncmp(types, "no", 2);
 
-        // Loop through -t filters
         for (;;) {
           if (!(type = comma_iterate(&types, &len))) break;
           if (!skip) {
+            // If one -t starts with "no", the rest must too
             if (strncmp(type, "no", 2)) error_exit("bad -t");
             if (!strncmp(type+2, ml->type, len-2)) {
               skip = 1;
@@ -143,13 +157,27 @@
         }
         if (skip) continue;
       }
-      do_umount(ml->dir, flags);
+
+      do_umount(ml->dir, ml->device, flags);
     }
     if (CFG_TOYBOX_FREE) {
       free(typestr);
       llist_traverse(mlsave, free);
     }
+  // TODO: under what circumstances do we umount non-absolute path?
+  } else for (optargs = toys.optargs; *optargs; optargs++) {
+    char *abs = xabspath(*optargs, 0);
 
-    return;
+    for (ml = abs ? mlrev : 0; ml; ml = ml->prev) {
+      if (!strcmp(ml->dir, abs)) break;
+      if (!strcmp(ml->device, abs)) {
+        free(abs);
+        abs = ml->dir;
+        break;
+      }
+    }
+
+    do_umount(abs ? abs : *optargs, ml ? ml->device : 0, flags);
+    if (ml && abs != ml->dir) free(abs);
   }
 }