| /* umount.c - Unmount a mount point. |
| * |
| * Copyright 2012 Rob Landley <rob@landley.net> |
| * |
| * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/umount.html |
| * |
| * Note: -n (/etc/mtab) is obsolete, /proc/mounts replaced it. Neither chroot |
| * 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[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT)) |
| |
| config UMOUNT |
| bool "umount" |
| default y |
| help |
| usage: umount [-a [-t TYPE[,TYPE...]]] [-vrfD] [DIR...] |
| |
| Unmount the listed filesystems. |
| |
| -a Unmount all mounts in /proc/mounts instead of command line list |
| -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 |
| */ |
| |
| #define FOR_umount |
| #include "toys.h" |
| |
| GLOBALS( |
| struct arg_list *t; |
| |
| char *types; |
| ) |
| |
| // todo (done?) |
| // borrow df code to identify filesystem? |
| // umount -a from fstab |
| // umount when getpid() not 0, according to fstab |
| // lookup mount: losetup -d, bind, file, block |
| // loopback delete |
| // fstab -o user |
| |
| // TODO |
| // swapon, swapoff |
| |
| static void do_umount(char *dir, char *dev, int flags) |
| { |
| // is it ok for this user to umount this mount? |
| if (CFG_TOYBOX_SUID && getuid()) { |
| struct mtab_list *mt = dlist_terminate(xgetmountlist("/etc/fstab")); |
| int len, user = 0; |
| |
| while (mt) { |
| struct mtab_list *mtemp = mt; |
| char *s; |
| |
| if (!strcmp(mt->dir, dir)) while ((s = comma_iterate(&mt->opts, &len))) { |
| if (len == 4 && strncmp(s, "user", 4)) user = 1; |
| else if (len == 6 && strncmp(s, "nouser", 6)) user = 0; |
| } |
| |
| mt = mt->next; |
| free(mtemp); |
| } |
| |
| if (!user) { |
| error_msg("not root"); |
| |
| return; |
| } |
| } |
| |
| if (!umount2(dir, flags)) { |
| 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) xprintf("%s remounted ro\n", dir); |
| return; |
| } |
| } |
| |
| perror_msg_raw(dir); |
| } |
| |
| void umount_main(void) |
| { |
| char **optargs, *pm = "/proc/mounts"; |
| struct mtab_list *mlsave = 0, *mlrev = 0, *ml; |
| int flags=0; |
| |
| if (!toys.optc && !(toys.optflags & FLAG_a)) |
| error_exit("Need 1 arg or -a"); |
| |
| if (toys.optflags & FLAG_f) flags |= MNT_FORCE; |
| if (toys.optflags & FLAG_l) flags |= MNT_DETACH; |
| |
| // 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) { |
| char *typestr = 0; |
| 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 (mountlist_istype(ml, typestr)) 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); |
| |
| 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); |
| } |
| } |