]>
Commit | Line | Data |
---|---|---|
6dbe3af9 | 1 | /* |
d162fcb5 | 2 | * umount(8) for Linux 0.99 - jrs, 1993 |
6dbe3af9 KZ |
3 | */ |
4 | ||
66ee8158 | 5 | #include <stdio.h> |
fd6b7a7f KZ |
6 | #include <unistd.h> |
7 | #include <getopt.h> | |
8 | #include <string.h> | |
9 | #include <errno.h> | |
66ee8158 | 10 | #include <ctype.h> |
fd6b7a7f | 11 | #include <sys/stat.h> |
d162fcb5 | 12 | #include <sys/wait.h> |
fd6b7a7f KZ |
13 | #include <sys/mount.h> |
14 | #include "mount_constants.h" | |
6dbe3af9 | 15 | #include "sundries.h" |
5c36a0eb | 16 | #include "getusername.h" |
fd6b7a7f KZ |
17 | #include "lomount.h" |
18 | #include "loop.h" | |
19 | #include "fstab.h" | |
364cda48 | 20 | #include "env.h" |
7eda085c | 21 | #include "nls.h" |
6dbe3af9 KZ |
22 | |
23 | #ifdef HAVE_NFS | |
24 | #include <sys/socket.h> | |
25 | #include <sys/time.h> | |
26 | #include <netdb.h> | |
27 | #include <rpc/rpc.h> | |
28 | #include <rpc/pmap_clnt.h> | |
29 | #include <rpc/pmap_prot.h> | |
fd6b7a7f | 30 | #include "nfsmount.h" |
6dbe3af9 KZ |
31 | #include <arpa/inet.h> |
32 | #endif | |
33 | ||
364cda48 | 34 | #if defined(MNT_FORCE) && !defined(__sparc__) && !defined(__arm__) |
5c36a0eb KZ |
35 | /* Interesting ... it seems libc knows about MNT_FORCE and presumably |
36 | about umount2 as well -- need not do anything */ | |
37 | #else /* MNT_FORCE */ | |
38 | ||
39 | /* Does the present kernel source know about umount2? */ | |
40 | #include <linux/unistd.h> | |
41 | #ifdef __NR_umount2 | |
eb63b9b8 KZ |
42 | |
43 | static int umount2(const char *path, int flags); | |
44 | ||
5c36a0eb | 45 | _syscall2(int, umount2, const char *, path, int, flags); |
eb63b9b8 | 46 | |
5c36a0eb | 47 | #else /* __NR_umount2 */ |
eb63b9b8 | 48 | |
5c36a0eb KZ |
49 | static int |
50 | umount2(const char *path, int flags) { | |
7eda085c | 51 | fprintf(stderr, _("umount: compiled without support for -f\n")); |
5c36a0eb KZ |
52 | errno = ENOSYS; |
53 | return -1; | |
54 | } | |
55 | #endif /* __NR_umount2 */ | |
56 | ||
eb63b9b8 | 57 | #if !defined(MNT_FORCE) |
5c36a0eb KZ |
58 | /* dare not try to include <linux/mount.h> -- lots of errors */ |
59 | #define MNT_FORCE 1 | |
eb63b9b8 | 60 | #endif |
5c36a0eb KZ |
61 | |
62 | #endif /* MNT_FORCE */ | |
63 | ||
e8f26419 KZ |
64 | #if !defined(MNT_DETACH) |
65 | #define MNT_DETACH 2 | |
66 | #endif | |
67 | ||
d162fcb5 KZ |
68 | |
69 | /* True if we are allowed to call /sbin/umount.${FSTYPE} */ | |
70 | int external_allowed = 1; | |
71 | ||
5c36a0eb | 72 | /* Nonzero for force umount (-f). There is kernel support since 2.1.116. */ |
6dbe3af9 | 73 | int force = 0; |
6dbe3af9 | 74 | |
e8f26419 KZ |
75 | /* Nonzero for lazy umount (-l). There is kernel support since 2.4.11. */ |
76 | int lazy = 0; | |
77 | ||
fd6b7a7f KZ |
78 | /* When umount fails, attempt a read-only remount (-r). */ |
79 | int remount = 0; | |
80 | ||
81 | /* Don't write a entry in /etc/mtab (-n). */ | |
82 | int nomtab = 0; | |
83 | ||
66ee8158 KZ |
84 | /* Call losetup -d for each unmounted loop device. */ |
85 | int delloop = 0; | |
86 | ||
87 | /* Nonzero for chatty (-v). */ | |
6dbe3af9 KZ |
88 | int verbose = 0; |
89 | ||
90 | /* True if ruid != euid. */ | |
91 | int suid = 0; | |
92 | ||
d162fcb5 KZ |
93 | /* |
94 | * check_special_umountprog() | |
95 | * If there is a special umount program for this type, exec it. | |
96 | * returns: 0: no exec was done, 1: exec was done, status has result | |
97 | */ | |
364cda48 | 98 | static int |
d162fcb5 KZ |
99 | check_special_umountprog(const char *spec, const char *node, |
100 | const char *type, int *status) { | |
101 | char umountprog[120]; | |
102 | struct stat statbuf; | |
103 | int res; | |
104 | ||
105 | if (!external_allowed) | |
106 | return 0; | |
107 | ||
108 | if (type && strlen(type) < 100) { | |
109 | sprintf(umountprog, "/sbin/umount.%s", type); | |
110 | if (stat(umountprog, &statbuf) == 0) { | |
111 | res = fork(); | |
112 | if (res == 0) { | |
113 | char *umountargs[8]; | |
114 | int i = 0; | |
115 | ||
116 | setuid(getuid()); | |
117 | setgid(getgid()); | |
118 | umountargs[i++] = umountprog; | |
119 | umountargs[i++] = xstrdup(node); | |
120 | if (nomtab) | |
121 | umountargs[i++] = "-n"; | |
122 | if (lazy) | |
123 | umountargs[i++] = "-l"; | |
124 | if (force) | |
125 | umountargs[i++] = "-f"; | |
126 | if (verbose) | |
127 | umountargs[i++] = "-v"; | |
128 | if (remount) | |
129 | umountargs[i++] = "-r"; | |
130 | umountargs[i] = NULL; | |
131 | execv(umountprog, umountargs); | |
132 | exit(1); /* exec failed */ | |
133 | } else if (res != -1) { | |
134 | int st; | |
135 | wait(&st); | |
136 | *status = (WIFEXITED(st) ? WEXITSTATUS(st) | |
137 | : EX_SYSERR); | |
138 | return 1; | |
139 | } else { | |
140 | int errsv = errno; | |
141 | error(_("umount: cannot fork: %s"), | |
142 | strerror(errsv)); | |
143 | } | |
144 | } | |
145 | } | |
146 | return 0; | |
364cda48 | 147 | } |
364cda48 | 148 | |
6dbe3af9 KZ |
149 | #ifdef HAVE_NFS |
150 | static int xdr_dir(XDR *xdrsp, char *dirp) | |
151 | { | |
152 | return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); | |
153 | } | |
6dbe3af9 | 154 | |
6dbe3af9 | 155 | static int |
fd6b7a7f | 156 | nfs_umount_rpc_call(const char *spec, const char *opts) |
6dbe3af9 | 157 | { |
6dbe3af9 KZ |
158 | register CLIENT *clp; |
159 | struct sockaddr_in saddr; | |
160 | struct timeval pertry, try; | |
161 | enum clnt_stat clnt_stat; | |
66ee8158 | 162 | int port = 0; |
6dbe3af9 | 163 | int so = RPC_ANYSOCK; |
6dbe3af9 | 164 | struct hostent *hostp; |
fd6b7a7f KZ |
165 | char *hostname; |
166 | char *dirname; | |
167 | char *p; | |
168 | ||
169 | if (spec == NULL || (p = strchr(spec,':')) == NULL) | |
170 | return 0; | |
171 | hostname = xstrndup(spec, p-spec); | |
172 | dirname = xstrdup(p+1); | |
173 | #ifdef DEBUG | |
7eda085c | 174 | printf(_("host: %s, directory: %s\n"), hostname, dirname); |
fd6b7a7f KZ |
175 | #endif |
176 | ||
177 | if (opts && (p = strstr(opts, "addr="))) { | |
178 | char *q; | |
179 | ||
180 | free(hostname); | |
181 | p += 5; | |
182 | q = p; | |
183 | while (*q && *q != ',') q++; | |
184 | hostname = xstrndup(p,q-p); | |
185 | } | |
186 | ||
66ee8158 KZ |
187 | if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10))) |
188 | port = atoi(p+10); | |
189 | ||
fd6b7a7f KZ |
190 | if (hostname[0] >= '0' && hostname[0] <= '9') |
191 | saddr.sin_addr.s_addr = inet_addr(hostname); | |
2b6fc908 | 192 | else { |
fd6b7a7f | 193 | if ((hostp = gethostbyname(hostname)) == NULL) { |
7eda085c | 194 | fprintf(stderr, _("umount: can't get address for %s\n"), |
fd6b7a7f KZ |
195 | hostname); |
196 | return 1; | |
2b6fc908 KZ |
197 | } |
198 | if (hostp->h_length > sizeof(struct in_addr)) { | |
7eda085c | 199 | fprintf(stderr, _("umount: got bad hostp->h_length\n")); |
2b6fc908 KZ |
200 | hostp->h_length = sizeof(struct in_addr); |
201 | } | |
202 | memcpy(&saddr.sin_addr, hostp->h_addr, hostp->h_length); | |
203 | } | |
fd6b7a7f KZ |
204 | |
205 | saddr.sin_family = AF_INET; | |
66ee8158 | 206 | saddr.sin_port = htons(port); |
fd6b7a7f KZ |
207 | pertry.tv_sec = 3; |
208 | pertry.tv_usec = 0; | |
e8f26419 KZ |
209 | if (opts && (p = strstr(opts, "tcp"))) { |
210 | /* possibly: make sure option is not "notcp" | |
211 | possibly: try udp if tcp fails */ | |
212 | if ((clp = clnttcp_create(&saddr, MOUNTPROG, MOUNTVERS, | |
213 | &so, 0, 0)) == NULL) { | |
214 | clnt_pcreateerror("Cannot MOUNTPROG RPC (tcp)"); | |
215 | return 1; | |
216 | } | |
217 | } else { | |
218 | if ((clp = clntudp_create(&saddr, MOUNTPROG, MOUNTVERS, | |
219 | pertry, &so)) == NULL) { | |
220 | clnt_pcreateerror("Cannot MOUNTPROG RPC"); | |
221 | return 1; | |
222 | } | |
fd6b7a7f KZ |
223 | } |
224 | clp->cl_auth = authunix_create_default(); | |
225 | try.tv_sec = 20; | |
226 | try.tv_usec = 0; | |
227 | clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, | |
228 | (xdrproc_t) xdr_dir, dirname, | |
229 | (xdrproc_t) xdr_void, (caddr_t) 0, | |
230 | try); | |
231 | ||
232 | if (clnt_stat != RPC_SUCCESS) { | |
233 | clnt_perror(clp, "Bad UMNT RPC"); | |
e8f26419 | 234 | return 1; |
fd6b7a7f KZ |
235 | } |
236 | auth_destroy(clp->cl_auth); | |
237 | clnt_destroy(clp); | |
238 | ||
e8f26419 | 239 | return 0; |
fd6b7a7f | 240 | } |
6dbe3af9 | 241 | #endif /* HAVE_NFS */ |
fd6b7a7f KZ |
242 | |
243 | /* complain about a failed umount */ | |
244 | static void complain(int err, const char *dev) { | |
245 | switch (err) { | |
246 | case ENXIO: | |
7eda085c | 247 | error (_("umount: %s: invalid block device"), dev); break; |
fd6b7a7f | 248 | case EINVAL: |
7eda085c | 249 | error (_("umount: %s: not mounted"), dev); break; |
fd6b7a7f | 250 | case EIO: |
7eda085c | 251 | error (_("umount: %s: can't write superblock"), dev); break; |
fd6b7a7f KZ |
252 | case EBUSY: |
253 | /* Let us hope fstab has a line "proc /proc ..." | |
254 | and not "none /proc ..."*/ | |
7eda085c | 255 | error (_("umount: %s: device is busy"), dev); break; |
fd6b7a7f | 256 | case ENOENT: |
7eda085c | 257 | error (_("umount: %s: not found"), dev); break; |
fd6b7a7f | 258 | case EPERM: |
7eda085c | 259 | error (_("umount: %s: must be superuser to umount"), dev); break; |
fd6b7a7f | 260 | case EACCES: |
7eda085c | 261 | error (_("umount: %s: block devices not permitted on fs"), dev); break; |
fd6b7a7f | 262 | default: |
7eda085c | 263 | error (_("umount: %s: %s"), dev, strerror (err)); break; |
fd6b7a7f KZ |
264 | } |
265 | } | |
266 | ||
267 | /* Umount a single device. Return a status code, so don't exit | |
268 | on a non-fatal error. We lock/unlock around each umount. */ | |
269 | static int | |
270 | umount_one (const char *spec, const char *node, const char *type, | |
66ee8158 KZ |
271 | const char *opts, struct mntentchn *mc) { |
272 | int umnt_err, umnt_err2; | |
273 | int isroot; | |
274 | int res; | |
d162fcb5 | 275 | int status; |
66ee8158 KZ |
276 | const char *loopdev; |
277 | ||
278 | /* Special case for root. As of 0.99pl10 we can (almost) unmount root; | |
279 | the kernel will remount it readonly so that we can carry on running | |
280 | afterwards. The readonly remount is illegal if any files are opened | |
281 | for writing at the time, so we can't update mtab for an unmount of | |
282 | root. As it is only really a remount, this doesn't matter too | |
283 | much. [sct May 29, 1993] */ | |
284 | isroot = (streq (node, "/") || streq (node, "root") | |
285 | || streq (node, "rootfs")); | |
286 | if (isroot) | |
287 | nomtab++; | |
d162fcb5 KZ |
288 | |
289 | /* | |
290 | * Call umount.TYPE for types that require a separate umount program. | |
291 | * All such special things must occur isolated in the types string. | |
292 | */ | |
293 | if (check_special_umountprog(spec, node, type, &status)) | |
294 | return status; | |
6dbe3af9 KZ |
295 | |
296 | #ifdef HAVE_NFS | |
66ee8158 KZ |
297 | /* Ignore any RPC errors, so that you can umount the filesystem |
298 | if the server is down. */ | |
299 | if (strcasecmp(type, "nfs") == 0) | |
300 | nfs_umount_rpc_call(spec, opts); | |
6dbe3af9 | 301 | #endif |
6dbe3af9 | 302 | |
66ee8158 | 303 | umnt_err = umnt_err2 = 0; |
e8f26419 KZ |
304 | if (lazy) { |
305 | res = umount2 (node, MNT_DETACH); | |
1d4ad1de KZ |
306 | if (res < 0) |
307 | umnt_err = errno; | |
308 | goto writemtab; | |
e8f26419 | 309 | } |
1d4ad1de | 310 | |
e8f26419 KZ |
311 | if (force) { /* only supported for NFS */ |
312 | res = umount2 (node, MNT_FORCE); | |
66ee8158 | 313 | if (res == -1) { |
d03dd608 | 314 | int errsv = errno; |
66ee8158 | 315 | perror("umount2"); |
d03dd608 | 316 | errno = errsv; |
66ee8158 KZ |
317 | if (errno == ENOSYS) { |
318 | if (verbose) | |
319 | printf(_("no umount2, trying umount...\n")); | |
320 | res = umount (node); | |
321 | } | |
322 | } | |
323 | } else | |
324 | res = umount (node); | |
325 | ||
326 | if (res < 0) { | |
327 | umnt_err = errno; | |
328 | /* A device might have been mounted on a node that has since | |
329 | been deleted or renamed, so if node fails, also try spec. */ | |
330 | /* Note that this is incorrect in case spec was mounted | |
331 | several times. */ | |
332 | /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ | |
333 | if (umnt_err != EBUSY && strcmp(node, spec)) { | |
334 | if (verbose) | |
335 | printf (_("could not umount %s - trying %s instead\n"), | |
336 | node, spec); | |
337 | res = umount (spec); | |
338 | if (res < 0) | |
339 | umnt_err2 = errno; | |
340 | /* Do not complain about remote NFS mount points */ | |
341 | if (errno == ENOENT && index(spec, ':')) | |
342 | umnt_err2 = 0; | |
343 | } | |
344 | } | |
345 | ||
346 | if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { | |
347 | /* Umount failed - let us try a remount */ | |
348 | res = mount(spec, node, NULL, | |
349 | MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); | |
350 | if (res == 0) { | |
d26aa358 | 351 | struct my_mntent remnt; |
66ee8158 KZ |
352 | fprintf(stderr, |
353 | _("umount: %s busy - remounted read-only\n"), | |
354 | spec); | |
355 | remnt.mnt_type = remnt.mnt_fsname = NULL; | |
356 | remnt.mnt_dir = xstrdup(node); | |
d162fcb5 | 357 | remnt.mnt_opts = xstrdup("ro"); |
66ee8158 KZ |
358 | update_mtab(node, &remnt); |
359 | return 0; | |
360 | } else if (errno != EBUSY) { /* hmm ... */ | |
361 | perror("remount"); | |
362 | fprintf(stderr, | |
363 | _("umount: could not remount %s read-only\n"), | |
364 | spec); | |
365 | } | |
366 | } | |
367 | ||
368 | loopdev = 0; | |
369 | if (res >= 0) { | |
370 | /* Umount succeeded */ | |
371 | if (verbose) | |
372 | printf (_("%s umounted\n"), spec); | |
373 | ||
374 | /* Free any loop devices that we allocated ourselves */ | |
375 | if (mc) { | |
376 | char *optl; | |
377 | ||
378 | /* old style mtab line? */ | |
379 | if (streq(mc->m.mnt_type, "loop")) { | |
380 | loopdev = spec; | |
381 | goto gotloop; | |
382 | } | |
383 | ||
384 | /* new style mtab line? */ | |
385 | optl = mc->m.mnt_opts ? xstrdup(mc->m.mnt_opts) : ""; | |
386 | for (optl = strtok (optl, ","); optl; | |
387 | optl = strtok (NULL, ",")) { | |
388 | if (!strncmp(optl, "loop=", 5)) { | |
389 | loopdev = optl+5; | |
390 | goto gotloop; | |
391 | } | |
392 | } | |
393 | } else { | |
394 | /* | |
395 | * If option "-o loop=spec" occurs in mtab, | |
396 | * note the mount point, and delete mtab line. | |
397 | */ | |
398 | if ((mc = getmntoptfile (spec)) != NULL) | |
399 | node = mc->m.mnt_dir; | |
400 | } | |
401 | ||
402 | /* Also free loop devices when -d flag is given */ | |
403 | if (delloop && is_loop_device(spec)) | |
404 | loopdev = spec; | |
405 | } | |
406 | gotloop: | |
407 | if (loopdev) | |
408 | del_loop(loopdev); | |
409 | ||
1d4ad1de | 410 | writemtab: |
66ee8158 KZ |
411 | if (!nomtab && mtab_is_writable() && |
412 | (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { | |
413 | update_mtab (node, NULL); | |
414 | } | |
415 | ||
416 | if (res >= 0) | |
417 | return 0; | |
6dbe3af9 | 418 | |
66ee8158 KZ |
419 | if (umnt_err2) |
420 | complain(umnt_err2, spec); | |
421 | if (umnt_err && umnt_err != umnt_err2) | |
422 | complain(umnt_err, node); | |
423 | return 1; | |
6dbe3af9 KZ |
424 | } |
425 | ||
5c36a0eb | 426 | /* |
d26aa358 KZ |
427 | * umount_one_bw: unmount FILE that has last occurrence MC0 |
428 | * | |
5c36a0eb KZ |
429 | * Why this loop? |
430 | * 1. People who boot a system with a bad fstab root entry | |
431 | * will get an incorrect "/dev/foo on /" in mtab. | |
432 | * If later /dev/foo is actually mounted elsewhere, | |
433 | * it will occur twice in mtab. | |
434 | * 2. With overmounting one can get the situation that | |
435 | * the same filename is used as mount point twice. | |
436 | * In both cases, it is best to try the last occurrence first. | |
437 | */ | |
438 | static int | |
d03dd608 KZ |
439 | umount_one_bw (const char *file, struct mntentchn *mc0) { |
440 | struct mntentchn *mc; | |
441 | int res = 1; | |
442 | ||
443 | mc = mc0; | |
444 | while (res && mc) { | |
445 | res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, | |
446 | mc->m.mnt_type, mc->m.mnt_opts, mc); | |
447 | mc = getmntdirbackward(file, mc); | |
448 | } | |
449 | mc = mc0; | |
450 | while (res && mc) { | |
451 | res = umount_one(mc->m.mnt_fsname, mc->m.mnt_dir, | |
452 | mc->m.mnt_type, mc->m.mnt_opts, mc); | |
453 | mc = getmntdevbackward(file, mc); | |
454 | } | |
455 | return res; | |
5c36a0eb KZ |
456 | } |
457 | ||
6dbe3af9 | 458 | /* Unmount all filesystems of type VFSTYPES found in mtab. Since we are |
c07ebfa1 | 459 | concurrently updating mtab after every successful umount, we have to |
6dbe3af9 KZ |
460 | slurp in the entire file before we start. This isn't too bad, because |
461 | in any case it's important to umount mtab entries in reverse order | |
fd6b7a7f | 462 | to mount, e.g. /usr/spool before /usr. */ |
6dbe3af9 | 463 | static int |
95f1bdee | 464 | umount_all (char *types, char *test_opts) { |
fd6b7a7f KZ |
465 | struct mntentchn *mc, *hd; |
466 | int errors = 0; | |
467 | ||
468 | hd = mtab_head(); | |
469 | if (!hd->prev) | |
7eda085c | 470 | die (2, _("umount: cannot find list of filesystems to unmount")); |
fd6b7a7f | 471 | for (mc = hd->prev; mc != hd; mc = mc->prev) { |
95f1bdee KZ |
472 | if (matching_type (mc->m.mnt_type, types) |
473 | && matching_opts (mc->m.mnt_opts, test_opts)) { | |
66ee8158 KZ |
474 | errors |= umount_one (mc->m.mnt_fsname, mc->m.mnt_dir, |
475 | mc->m.mnt_type, mc->m.mnt_opts, mc); | |
fd6b7a7f KZ |
476 | } |
477 | } | |
6dbe3af9 | 478 | |
fd6b7a7f KZ |
479 | sync (); |
480 | return errors; | |
6dbe3af9 KZ |
481 | } |
482 | ||
483 | extern char version[]; | |
484 | static struct option longopts[] = | |
485 | { | |
486 | { "all", 0, 0, 'a' }, | |
487 | { "force", 0, 0, 'f' }, | |
488 | { "help", 0, 0, 'h' }, | |
fd6b7a7f | 489 | { "no-mtab", 0, 0, 'n' }, |
95f1bdee | 490 | { "test-opts", 1, 0, 'O' }, |
6dbe3af9 KZ |
491 | { "verbose", 0, 0, 'v' }, |
492 | { "version", 0, 0, 'V' }, | |
fd6b7a7f | 493 | { "read-only", 0, 0, 'r' }, |
6dbe3af9 KZ |
494 | { "types", 1, 0, 't' }, |
495 | { NULL, 0, 0, 0 } | |
496 | }; | |
497 | ||
6dbe3af9 KZ |
498 | static void |
499 | usage (FILE *fp, int n) | |
500 | { | |
7eda085c | 501 | fprintf (fp, _("Usage: umount [-hV]\n" |
95f1bdee | 502 | " umount -a [-f] [-r] [-n] [-v] [-t vfstypes] [-O opts]\n" |
7eda085c | 503 | " umount [-f] [-r] [-n] [-v] special | node...\n")); |
6dbe3af9 KZ |
504 | exit (n); |
505 | } | |
506 | ||
726f69e2 KZ |
507 | int mount_quiet = 0; |
508 | ||
d26aa358 KZ |
509 | /* |
510 | * Look for an option in a comma-separated list | |
511 | */ | |
ffc43748 | 512 | static int |
d26aa358 KZ |
513 | contains(const char *list, const char *s) { |
514 | int n = strlen(s); | |
515 | ||
516 | while (*list) { | |
517 | if (strncmp(list, s, n) == 0 && | |
518 | (list[n] == 0 || list[n] == ',')) | |
ffc43748 | 519 | return 1; |
d26aa358 | 520 | while (*list && *list++ != ',') ; |
ffc43748 KZ |
521 | } |
522 | return 0; | |
523 | } | |
524 | ||
525 | /* | |
526 | * If list contains "user=peter" and we ask for "user=", return "peter" | |
527 | */ | |
528 | static char * | |
d26aa358 KZ |
529 | get_value(const char *list, const char *s) { |
530 | const char *t; | |
ffc43748 | 531 | int n = strlen(s); |
d26aa358 KZ |
532 | |
533 | while (*list) { | |
534 | if (strncmp(list, s, n) == 0) { | |
535 | s = t = list+n; | |
536 | while (*s && *s != ',') | |
537 | s++; | |
538 | return xstrndup(t, s-t); | |
539 | } | |
540 | while (*list && *list++ != ',') ; | |
ffc43748 KZ |
541 | } |
542 | return 0; | |
543 | } | |
364cda48 KZ |
544 | |
545 | static int | |
546 | umount_file (char *arg) { | |
547 | struct mntentchn *mc, *fs; | |
d26aa358 | 548 | const char *file, *options; |
d162fcb5 KZ |
549 | int fstab_has_user, fstab_has_users, fstab_has_owner, fstab_has_group; |
550 | int ok; | |
364cda48 | 551 | |
5213517f KZ |
552 | if (!*arg) { /* "" would be expanded to `pwd` */ |
553 | die(2, _("Cannot umount \"\"\n")); | |
554 | return 0; | |
555 | } | |
556 | ||
d03dd608 | 557 | file = canonicalize(arg); /* mtab paths are canonicalized */ |
364cda48 KZ |
558 | if (verbose > 1) |
559 | printf(_("Trying to umount %s\n"), file); | |
560 | ||
d03dd608 KZ |
561 | mc = getmntdirbackward(file, NULL); |
562 | if (!mc) | |
563 | mc = getmntdevbackward(file, NULL); | |
364cda48 KZ |
564 | if (!mc && verbose) |
565 | printf(_("Could not find %s in mtab\n"), file); | |
566 | ||
567 | if (suid) { | |
d03dd608 KZ |
568 | char *mtab_user = NULL; |
569 | ||
364cda48 | 570 | if (!mc) |
d03dd608 KZ |
571 | die(2, |
572 | _("umount: %s is not mounted (according to mtab)"), | |
573 | file); | |
756bfd01 KZ |
574 | /* The 2.4 kernel will generally refuse to mount the same |
575 | filesystem on the same mount point, but will accept NFS. | |
576 | So, unmounting must be possible. */ | |
577 | if (!is_mounted_once(file) && strcmp(mc->m.mnt_type,"nfs")) | |
d03dd608 KZ |
578 | die(2, |
579 | _("umount: it seems %s is mounted multiple times"), | |
580 | file); | |
364cda48 KZ |
581 | |
582 | /* If fstab contains the two lines | |
583 | /dev/sda1 /mnt/zip auto user,noauto 0 0 | |
584 | /dev/sda4 /mnt/zip auto user,noauto 0 0 | |
585 | then "mount /dev/sda4" followed by "umount /mnt/zip" | |
586 | used to fail. So, we must not look for file, but for | |
587 | the pair (spec,file) in fstab. */ | |
588 | fs = getfsspecfile(mc->m.mnt_fsname, mc->m.mnt_dir); | |
589 | if (!fs) { | |
590 | if (!getfsspec (file) && !getfsfile (file)) | |
591 | die (2, | |
d03dd608 KZ |
592 | _("umount: %s is not in the fstab " |
593 | "(and you are not root)"), | |
364cda48 KZ |
594 | file); |
595 | else | |
d03dd608 KZ |
596 | die (2, _("umount: %s mount disagrees with " |
597 | "the fstab"), file); | |
364cda48 KZ |
598 | } |
599 | ||
d162fcb5 KZ |
600 | /* |
601 | * User mounting and unmounting is allowed only | |
602 | * if fstab contains one of the options `user', | |
603 | * `users' or `owner' or `group'. | |
604 | * | |
605 | * The option `users' allows arbitrary users to mount | |
606 | * and unmount - this may be a security risk. | |
607 | * | |
608 | * The options `user', `owner' and `group' only allow | |
609 | * unmounting by the user that mounted (visible in mtab). | |
610 | */ | |
ffc43748 | 611 | |
d26aa358 KZ |
612 | options = fs->m.mnt_opts; |
613 | if (!options) | |
614 | options = ""; | |
ffc43748 KZ |
615 | fstab_has_user = contains(options, "user"); |
616 | fstab_has_users = contains(options, "users"); | |
617 | fstab_has_owner = contains(options, "owner"); | |
d162fcb5 | 618 | fstab_has_group = contains(options, "group"); |
ffc43748 KZ |
619 | ok = 0; |
620 | ||
621 | if (fstab_has_users) | |
622 | ok = 1; | |
623 | ||
d162fcb5 KZ |
624 | if (!ok && (fstab_has_user || fstab_has_owner || |
625 | fstab_has_group)) { | |
364cda48 KZ |
626 | char *user = getusername(); |
627 | ||
d26aa358 KZ |
628 | options = mc->m.mnt_opts; |
629 | if (!options) | |
630 | options = ""; | |
ffc43748 KZ |
631 | mtab_user = get_value(options, "user="); |
632 | ||
633 | if (user && mtab_user && streq (user, mtab_user)) | |
634 | ok = 1; | |
364cda48 | 635 | } |
ffc43748 | 636 | if (!ok) |
d03dd608 KZ |
637 | die (2, _("umount: only %s can unmount %s from %s"), |
638 | mtab_user ? mtab_user : "root", | |
ffc43748 | 639 | fs->m.mnt_fsname, fs->m.mnt_dir); |
364cda48 KZ |
640 | } |
641 | ||
642 | if (mc) | |
643 | return umount_one_bw (file, mc); | |
644 | else | |
645 | return umount_one (arg, arg, arg, arg, NULL); | |
6dbe3af9 | 646 | } |
fd6b7a7f | 647 | |
d162fcb5 KZ |
648 | char *progname; |
649 | ||
364cda48 KZ |
650 | int |
651 | main (int argc, char *argv[]) { | |
652 | int c; | |
653 | int all = 0; | |
d162fcb5 | 654 | char *types = NULL, *test_opts = NULL, *p; |
364cda48 KZ |
655 | int result = 0; |
656 | ||
657 | sanitize_env(); | |
658 | setlocale(LC_ALL, ""); | |
659 | bindtextdomain(PACKAGE, LOCALEDIR); | |
660 | textdomain(PACKAGE); | |
661 | ||
d162fcb5 KZ |
662 | progname = argv[0]; |
663 | if ((p = strrchr(progname, '/')) != NULL) | |
664 | progname = p+1; | |
665 | ||
b22550fa | 666 | umask(022); |
0e6f4a20 | 667 | |
d162fcb5 | 668 | while ((c = getopt_long (argc, argv, "adfhlnrit:O:vV", |
ffc43748 | 669 | longopts, NULL)) != -1) |
364cda48 KZ |
670 | switch (c) { |
671 | case 'a': /* umount everything */ | |
672 | ++all; | |
673 | break; | |
674 | /* fall through? */ | |
675 | case 'd': /* do losetup -d for unmounted loop devices */ | |
676 | ++delloop; | |
677 | break; | |
678 | case 'f': /* force umount */ | |
679 | ++force; | |
680 | break; | |
681 | case 'h': /* help */ | |
682 | usage (stdout, 0); | |
683 | break; | |
e8f26419 KZ |
684 | case 'l': /* lazy umount */ |
685 | ++lazy; | |
686 | break; | |
364cda48 KZ |
687 | case 'n': /* do not write in /etc/mtab */ |
688 | ++nomtab; | |
689 | break; | |
95f1bdee KZ |
690 | case 'O': /* specify file system options */ |
691 | test_opts = optarg; | |
692 | break; | |
364cda48 KZ |
693 | case 'r': /* remount read-only if umount fails */ |
694 | ++remount; | |
695 | break; | |
696 | case 'v': /* make noise */ | |
697 | ++verbose; | |
698 | break; | |
699 | case 'V': /* version */ | |
700 | printf ("umount: %s\n", version); | |
701 | exit (0); | |
702 | case 't': /* specify file system type */ | |
703 | types = optarg; | |
704 | break; | |
d162fcb5 KZ |
705 | case 'i': |
706 | external_allowed = 0; | |
707 | break; | |
364cda48 KZ |
708 | case 0: |
709 | break; | |
710 | case '?': | |
711 | default: | |
712 | usage (stderr, 1); | |
713 | } | |
714 | ||
715 | if (getuid () != geteuid ()) { | |
716 | suid = 1; | |
717 | if (all || types || nomtab || force) | |
718 | die (2, _("umount: only root can do that")); | |
719 | } | |
720 | ||
721 | argc -= optind; | |
722 | argv += optind; | |
723 | ||
724 | if (all) { | |
d03dd608 | 725 | /* nodev stuff: sysfs, usbfs, oprofilefs, ... */ |
364cda48 | 726 | if (types == NULL) |
d03dd608 | 727 | types = "noproc,nodevfs,nodevpts"; |
95f1bdee | 728 | result = umount_all (types, test_opts); |
364cda48 KZ |
729 | } else if (argc < 1) { |
730 | usage (stderr, 2); | |
731 | } else while (argc--) { | |
732 | result += umount_file(*argv++); | |
733 | } | |
734 | exit (result); /* nonzero on at least one failure */ | |
735 | } |