xref: /aosp_15_r20/external/toybox/toys/lsb/umount.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* umount.c - Unmount a mount point.
2  *
3  * Copyright 2012 Rob Landley <[email protected]>
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 
GLOBALS(struct arg_list * t;char * types;)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 (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 && !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) && FLAG(v)) xprintf("%s cleared\n", dev);
89         close(lfd);
90       }
91     }
92 
93     return;
94   }
95 
96   if (FLAG(r)) {
97     if (!mount("", dir, "", MS_REMOUNT|MS_RDONLY, "")) {
98       if (FLAG(v)) xprintf("%s remounted ro\n", dir);
99       return;
100     }
101   }
102 
103   perror_msg_raw(dir);
104 }
105 
umount_main(void)106 void umount_main(void)
107 {
108   char **optargs, *pm = "/proc/mounts";
109   struct mtab_list *mlsave = 0, *mlrev = 0, *ml;
110   int flags=0;
111 
112   if (!toys.optc && !FLAG(a)) error_exit("Need 1 arg or -a");
113 
114   if (FLAG(f)) flags |= MNT_FORCE;
115   if (FLAG(l)) flags |= MNT_DETACH;
116 
117   // Load /proc/mounts and get a reversed list (newest first)
118   // We use the list both for -a, and to umount /dev/name or do losetup -d
119   if (!FLAG(n) && !access(pm, R_OK))
120     mlrev = dlist_terminate(mlsave = xgetmountlist(pm));
121 
122   // Unmount all: loop through mounted filesystems, skip -t, unmount the rest
123   if (FLAG(a)) {
124     char *typestr = 0;
125     struct arg_list *tal;
126 
127     for (tal = TT.t; tal; tal = tal->next) comma_collate(&typestr, tal->arg);
128     for (ml = mlrev; ml; ml = ml->prev)
129       if (mountlist_istype(ml, typestr)) do_umount(ml->dir, ml->device, flags);
130     if (CFG_TOYBOX_FREE) {
131       free(typestr);
132       llist_traverse(mlsave, free);
133     }
134   // TODO: under what circumstances do we umount non-absolute path?
135   } else for (optargs = toys.optargs; *optargs; optargs++) {
136     char *abs = xabspath(*optargs, 0);
137 
138     for (ml = abs ? mlrev : 0; ml; ml = ml->prev) {
139       if (!strcmp(ml->dir, abs)) break;
140       if (!strcmp(ml->device, abs)) {
141         free(abs);
142         abs = ml->dir;
143         break;
144       }
145     }
146 
147     do_umount(abs ? abs : *optargs, ml ? ml->device : 0, flags);
148     if (ml && abs != ml->dir) free(abs);
149   }
150 }
151