/* umount.c - Unmount a mount point. * * Copyright 2012 Rob Landley * * 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, "cndDflrat*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); } }