1 /* umount.c - Unmount a mount point. 2 * 3 * Copyright 2012 Rob Landley <rob (at) landley.net> 4 * 5 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/umount.html 6 * 7 * Note: -n (/etc/mtab) is obsolete, /proc/mounts replaced it. Neither chroot 8 * nor per-process mount namespaces can work sanely with mtab. The kernel 9 * tracks mount points now, a userspace application can't do so anymore. 10 11 USE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT)) 12 13 config UMOUNT 14 bool "umount" 15 default y 16 help 17 usage: umount [-a [-t TYPE[,TYPE...]]] [-vrfD] [DIR...] 18 19 Unmount the listed filesystems. 20 21 -a Unmount all mounts in /proc/mounts instead of command line list 22 -D Don't free loopback device(s) 23 -f Force unmount 24 -l Lazy unmount (detach from filesystem now, close when last user does) 25 -n Don't use /proc/mounts 26 -r Remount read only if unmounting fails 27 -t Restrict "all" to mounts of TYPE (or use "noTYPE" to skip) 28 -v Verbose 29 */ 30 31 #define FOR_umount 32 #include "toys.h" 33 34 GLOBALS( 35 struct arg_list *t; 36 37 char *types; 38 ) 39 40 // todo (done?) 41 // borrow df code to identify filesystem? 42 // umount -a from fstab 43 // umount when getpid() not 0, according to fstab 44 // lookup mount: losetup -d, bind, file, block 45 // loopback delete 46 // fstab -o user 47 48 // TODO 49 // swapon, swapoff 50 51 static void do_umount(char *dir, char *dev, int flags) 52 { 53 // is it ok for this user to umount this mount? 54 if (CFG_TOYBOX_SUID && getuid()) { 55 struct mtab_list *mt = dlist_terminate(xgetmountlist("/etc/fstab")); 56 int len, user = 0; 57 58 while (mt) { 59 struct mtab_list *mtemp = mt; 60 char *s; 61 62 if (!strcmp(mt->dir, dir)) while ((s = comma_iterate(&mt->opts, &len))) { 63 if (len == 4 && strncmp(s, "user", 4)) user = 1; 64 else if (len == 6 && strncmp(s, "nouser", 6)) user = 0; 65 } 66 67 mt = mt->next; 68 free(mtemp); 69 } 70 71 if (!user) { 72 error_msg("not root"); 73 74 return; 75 } 76 } 77 78 if (!umount2(dir, flags)) { 79 if (toys.optflags & FLAG_v) xprintf("%s unmounted\n", dir); 80 81 // Attempt to disassociate loopback device. This ioctl should be ignored 82 // for anything else, because lanana allocated ioctl range 'L' to loopback 83 if (dev && !(toys.optflags & FLAG_D)) { 84 int lfd = open(dev, O_RDONLY); 85 86 if (lfd != -1) { 87 // This is LOOP_CLR_FD, fetching it from headers is awkward 88 if (!ioctl(lfd, 0x4C01) && (toys.optflags & FLAG_v)) 89 xprintf("%s cleared\n", dev); 90 close(lfd); 91 } 92 } 93 94 return; 95 } 96 97 if (toys.optflags & FLAG_r) { 98 if (!mount("", dir, "", MS_REMOUNT|MS_RDONLY, "")) { 99 if (toys.optflags & FLAG_v) xprintf("%s remounted ro\n", dir); 100 return; 101 } 102 } 103 104 perror_msg_raw(dir); 105 } 106 107 void umount_main(void) 108 { 109 char **optargs, *pm = "/proc/mounts"; 110 struct mtab_list *mlsave = 0, *mlrev = 0, *ml; 111 int flags=0; 112 113 if (!toys.optc && !(toys.optflags & FLAG_a)) 114 error_exit("Need 1 arg or -a"); 115 116 if (toys.optflags & FLAG_f) flags |= MNT_FORCE; 117 if (toys.optflags & FLAG_l) flags |= MNT_DETACH; 118 119 // Load /proc/mounts and get a reversed list (newest first) 120 // We use the list both for -a, and to umount /dev/name or do losetup -d 121 if (!(toys.optflags & FLAG_n) && !access(pm, R_OK)) 122 mlrev = dlist_terminate(mlsave = xgetmountlist(pm)); 123 124 // Unmount all: loop through mounted filesystems, skip -t, unmount the rest 125 if (toys.optflags & FLAG_a) { 126 char *typestr = 0; 127 struct arg_list *tal; 128 129 for (tal = TT.t; tal; tal = tal->next) comma_collate(&typestr, tal->arg); 130 for (ml = mlrev; ml; ml = ml->prev) 131 if (mountlist_istype(ml, typestr)) do_umount(ml->dir, ml->device, flags); 132 if (CFG_TOYBOX_FREE) { 133 free(typestr); 134 llist_traverse(mlsave, free); 135 } 136 // TODO: under what circumstances do we umount non-absolute path? 137 } else for (optargs = toys.optargs; *optargs; optargs++) { 138 char *abs = xabspath(*optargs, 0); 139 140 for (ml = abs ? mlrev : 0; ml; ml = ml->prev) { 141 if (!strcmp(ml->dir, abs)) break; 142 if (!strcmp(ml->device, abs)) { 143 free(abs); 144 abs = ml->dir; 145 break; 146 } 147 } 148 149 do_umount(abs ? abs : *optargs, ml ? ml->device : 0, flags); 150 if (ml && abs != ml->dir) free(abs); 151 } 152 } 153