10f66f451Sopenharmony_ci/* umount.c - Unmount a mount point.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/umount.html
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * Note: -n (/etc/mtab) is obsolete, /proc/mounts replaced it. Neither chroot
80f66f451Sopenharmony_ci * nor per-process mount namespaces can work sanely with mtab. The kernel
90f66f451Sopenharmony_ci * tracks mount points now, a userspace application can't do so anymore.
100f66f451Sopenharmony_ci
110f66f451Sopenharmony_ciUSE_UMOUNT(NEWTOY(umount, "cndDflrat*v[!na]", TOYFLAG_BIN|TOYFLAG_STAYROOT))
120f66f451Sopenharmony_ci
130f66f451Sopenharmony_ciconfig UMOUNT
140f66f451Sopenharmony_ci  bool "umount"
150f66f451Sopenharmony_ci  default y
160f66f451Sopenharmony_ci  help
170f66f451Sopenharmony_ci    usage: umount [-a [-t TYPE[,TYPE...]]] [-vrfD] [DIR...]
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    Unmount the listed filesystems.
200f66f451Sopenharmony_ci
210f66f451Sopenharmony_ci    -a	Unmount all mounts in /proc/mounts instead of command line list
220f66f451Sopenharmony_ci    -D	Don't free loopback device(s)
230f66f451Sopenharmony_ci    -f	Force unmount
240f66f451Sopenharmony_ci    -l	Lazy unmount (detach from filesystem now, close when last user does)
250f66f451Sopenharmony_ci    -n	Don't use /proc/mounts
260f66f451Sopenharmony_ci    -r	Remount read only if unmounting fails
270f66f451Sopenharmony_ci    -t	Restrict "all" to mounts of TYPE (or use "noTYPE" to skip)
280f66f451Sopenharmony_ci    -v	Verbose
290f66f451Sopenharmony_ci*/
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ci#define FOR_umount
320f66f451Sopenharmony_ci#include "toys.h"
330f66f451Sopenharmony_ci
340f66f451Sopenharmony_ciGLOBALS(
350f66f451Sopenharmony_ci  struct arg_list *t;
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_ci  char *types;
380f66f451Sopenharmony_ci)
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_ci// todo (done?)
410f66f451Sopenharmony_ci//   borrow df code to identify filesystem?
420f66f451Sopenharmony_ci//   umount -a from fstab
430f66f451Sopenharmony_ci//   umount when getpid() not 0, according to fstab
440f66f451Sopenharmony_ci//   lookup mount: losetup -d, bind, file, block
450f66f451Sopenharmony_ci//   loopback delete
460f66f451Sopenharmony_ci//   fstab -o user
470f66f451Sopenharmony_ci
480f66f451Sopenharmony_ci// TODO
490f66f451Sopenharmony_ci// swapon, swapoff
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_cistatic void do_umount(char *dir, char *dev, int flags)
520f66f451Sopenharmony_ci{
530f66f451Sopenharmony_ci  // is it ok for this user to umount this mount?
540f66f451Sopenharmony_ci  if (CFG_TOYBOX_SUID && getuid()) {
550f66f451Sopenharmony_ci    struct mtab_list *mt = dlist_terminate(xgetmountlist("/etc/fstab"));
560f66f451Sopenharmony_ci    int len, user = 0;
570f66f451Sopenharmony_ci
580f66f451Sopenharmony_ci    while (mt) {
590f66f451Sopenharmony_ci      struct mtab_list *mtemp = mt;
600f66f451Sopenharmony_ci      char *s;
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci      if (!strcmp(mt->dir, dir)) while ((s = comma_iterate(&mt->opts, &len))) {
630f66f451Sopenharmony_ci        if (len == 4 && strncmp(s, "user", 4)) user = 1;
640f66f451Sopenharmony_ci        else if (len == 6 && strncmp(s, "nouser", 6)) user = 0;
650f66f451Sopenharmony_ci      }
660f66f451Sopenharmony_ci
670f66f451Sopenharmony_ci      mt = mt->next;
680f66f451Sopenharmony_ci      free(mtemp);
690f66f451Sopenharmony_ci    }
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_ci    if (!user) {
720f66f451Sopenharmony_ci      error_msg("not root");
730f66f451Sopenharmony_ci
740f66f451Sopenharmony_ci      return;
750f66f451Sopenharmony_ci    }
760f66f451Sopenharmony_ci  }
770f66f451Sopenharmony_ci
780f66f451Sopenharmony_ci  if (!umount2(dir, flags)) {
790f66f451Sopenharmony_ci    if (toys.optflags & FLAG_v) xprintf("%s unmounted\n", dir);
800f66f451Sopenharmony_ci
810f66f451Sopenharmony_ci    // Attempt to disassociate loopback device. This ioctl should be ignored
820f66f451Sopenharmony_ci    // for anything else, because lanana allocated ioctl range 'L' to loopback
830f66f451Sopenharmony_ci    if (dev && !(toys.optflags & FLAG_D)) {
840f66f451Sopenharmony_ci      int lfd = open(dev, O_RDONLY);
850f66f451Sopenharmony_ci
860f66f451Sopenharmony_ci      if (lfd != -1) {
870f66f451Sopenharmony_ci        // This is LOOP_CLR_FD, fetching it from headers is awkward
880f66f451Sopenharmony_ci        if (!ioctl(lfd, 0x4C01) && (toys.optflags & FLAG_v))
890f66f451Sopenharmony_ci          xprintf("%s cleared\n", dev);
900f66f451Sopenharmony_ci        close(lfd);
910f66f451Sopenharmony_ci      }
920f66f451Sopenharmony_ci    }
930f66f451Sopenharmony_ci
940f66f451Sopenharmony_ci    return;
950f66f451Sopenharmony_ci  }
960f66f451Sopenharmony_ci
970f66f451Sopenharmony_ci  if (toys.optflags & FLAG_r) {
980f66f451Sopenharmony_ci    if (!mount("", dir, "", MS_REMOUNT|MS_RDONLY, "")) {
990f66f451Sopenharmony_ci      if (toys.optflags & FLAG_v) xprintf("%s remounted ro\n", dir);
1000f66f451Sopenharmony_ci      return;
1010f66f451Sopenharmony_ci    }
1020f66f451Sopenharmony_ci  }
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_ci  perror_msg_raw(dir);
1050f66f451Sopenharmony_ci}
1060f66f451Sopenharmony_ci
1070f66f451Sopenharmony_civoid umount_main(void)
1080f66f451Sopenharmony_ci{
1090f66f451Sopenharmony_ci  char **optargs, *pm = "/proc/mounts";
1100f66f451Sopenharmony_ci  struct mtab_list *mlsave = 0, *mlrev = 0, *ml;
1110f66f451Sopenharmony_ci  int flags=0;
1120f66f451Sopenharmony_ci
1130f66f451Sopenharmony_ci  if (!toys.optc && !(toys.optflags & FLAG_a))
1140f66f451Sopenharmony_ci    error_exit("Need 1 arg or -a");
1150f66f451Sopenharmony_ci
1160f66f451Sopenharmony_ci  if (toys.optflags & FLAG_f) flags |= MNT_FORCE;
1170f66f451Sopenharmony_ci  if (toys.optflags & FLAG_l) flags |= MNT_DETACH;
1180f66f451Sopenharmony_ci
1190f66f451Sopenharmony_ci  // Load /proc/mounts and get a reversed list (newest first)
1200f66f451Sopenharmony_ci  // We use the list both for -a, and to umount /dev/name or do losetup -d
1210f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_n) && !access(pm, R_OK))
1220f66f451Sopenharmony_ci    mlrev = dlist_terminate(mlsave = xgetmountlist(pm));
1230f66f451Sopenharmony_ci
1240f66f451Sopenharmony_ci  // Unmount all: loop through mounted filesystems, skip -t, unmount the rest
1250f66f451Sopenharmony_ci  if (toys.optflags & FLAG_a) {
1260f66f451Sopenharmony_ci    char *typestr = 0;
1270f66f451Sopenharmony_ci    struct arg_list *tal;
1280f66f451Sopenharmony_ci
1290f66f451Sopenharmony_ci    for (tal = TT.t; tal; tal = tal->next) comma_collate(&typestr, tal->arg);
1300f66f451Sopenharmony_ci    for (ml = mlrev; ml; ml = ml->prev)
1310f66f451Sopenharmony_ci      if (mountlist_istype(ml, typestr)) do_umount(ml->dir, ml->device, flags);
1320f66f451Sopenharmony_ci    if (CFG_TOYBOX_FREE) {
1330f66f451Sopenharmony_ci      free(typestr);
1340f66f451Sopenharmony_ci      llist_traverse(mlsave, free);
1350f66f451Sopenharmony_ci    }
1360f66f451Sopenharmony_ci  // TODO: under what circumstances do we umount non-absolute path?
1370f66f451Sopenharmony_ci  } else for (optargs = toys.optargs; *optargs; optargs++) {
1380f66f451Sopenharmony_ci    char *abs = xabspath(*optargs, 0);
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci    for (ml = abs ? mlrev : 0; ml; ml = ml->prev) {
1410f66f451Sopenharmony_ci      if (!strcmp(ml->dir, abs)) break;
1420f66f451Sopenharmony_ci      if (!strcmp(ml->device, abs)) {
1430f66f451Sopenharmony_ci        free(abs);
1440f66f451Sopenharmony_ci        abs = ml->dir;
1450f66f451Sopenharmony_ci        break;
1460f66f451Sopenharmony_ci      }
1470f66f451Sopenharmony_ci    }
1480f66f451Sopenharmony_ci
1490f66f451Sopenharmony_ci    do_umount(abs ? abs : *optargs, ml ? ml->device : 0, flags);
1500f66f451Sopenharmony_ci    if (ml && abs != ml->dir) free(abs);
1510f66f451Sopenharmony_ci  }
1520f66f451Sopenharmony_ci}
153