]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/umount.tproj/umount.c
adv_cmds: Fix BINDIRs
[apple_cmds.git] / diskdev_cmds / umount.tproj / umount.c
1 #ifndef _DARWIN_USE_64_BIT_INODE
2 # define _DARWIN_USE_64_BIT_INODE 1
3 #endif
4 /*
5 * Copyright (c) 1999-2020 Apple Inc. All rights reserved.
6 *
7 * @APPLE_LICENSE_HEADER_START@
8 *
9 * This file contains Original Code and/or Modifications of Original Code
10 * as defined in and that are subject to the Apple Public Source License
11 * Version 2.0 (the 'License'). You may not use this file except in
12 * compliance with the License. Please obtain a copy of the License at
13 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * file.
15 *
16 * The Original Code and all software distributed under the License are
17 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
18 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
19 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
21 * Please see the License for the specific language governing rights and
22 * limitations under the License.
23 *
24 * @APPLE_LICENSE_HEADER_END@
25 */
26 /*-
27 * Copyright (c) 1980, 1989, 1993
28 * The Regents of the University of California. All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
42 * 4. Neither the name of the University nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59
60 #include <sys/param.h>
61 #include <sys/stat.h>
62 #include <sys/mount.h>
63 #include <sys/time.h>
64 #include <sys/sysctl.h>
65 #include <System/sys/fsctl.h>
66
67 #include <netdb.h>
68 #include <arpa/inet.h>
69
70 #include <err.h>
71 #include <fstab.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <ctype.h>
76 #include <unistd.h>
77 #include <errno.h>
78
79 #include <pthread.h>
80
81 #include "../edt_fstab/edt_fstab.h"
82
83 struct syncarg {
84 const char *mntname;
85 int wakeup_flag;
86 pthread_cond_t *wakeup_cond;
87 pthread_mutex_t *wakeup_lock;
88 };
89
90 typedef enum { MNTON, MNTFROM } mntwhat;
91
92 int fake, fflag, vflag;
93 char *nfshost;
94
95 int checkvfsname(const char *, char **);
96 char *getmntname(const char *, mntwhat, char **);
97 int getmntfsid(const char *, fsid_t *);
98 int sysctl_fsid(int, fsid_t *, void *, size_t *, void *, size_t);
99 int unmount_by_fsid(const char *mntpt, int flag);
100 char **makevfslist(char *);
101 int selected(int);
102 int namematch(struct hostent *);
103 int umountall(char **);
104 int umountfs(char *, char **);
105 void usage(void);
106
107 static void*
108 syncit(void *vap) {
109 int rv;
110 pthread_mutex_t *lock;
111 int full_sync = FSCTL_SYNC_WAIT;
112 struct syncarg *args = vap;
113
114 rv = fsctl(args->mntname, FSIOC_SYNC_VOLUME, &full_sync, 0);
115 if (rv == -1) {
116 #ifdef DEBUG
117 warn("fsctl %s", args->mntname);
118 #endif
119 }
120
121 lock = args->wakeup_lock;
122 (void)pthread_mutex_lock(lock);
123 args->wakeup_flag = 1;
124 pthread_cond_signal(args->wakeup_cond);
125 (void)pthread_mutex_unlock(lock);
126
127 return NULL;
128 }
129
130 int
131 main(int argc, char *argv[])
132 {
133 int all, ch, errs, mnts;
134 char **typelist = NULL;
135 struct statfs *mntbuf;
136
137 /*
138 * We used to call sync(2) here, but this should be unneccessary
139 * given that a sync occurs at a more proper level (VFS_SYNC()
140 * in dounmount() in the non-forced unmount case).
141 *
142 * We add the sync() back in for the -f case below to cover the
143 * situation where the filesystem was mounted RW and force
144 * unmounted when it really didn't have to be.
145 *
146 * See 5328558 for some context.
147 */
148
149 all = 0;
150 while ((ch = getopt(argc, argv, "AaFfh:t:v")) != EOF)
151 switch (ch) {
152 case 'A':
153 all = 2;
154 break;
155 case 'a':
156 all = 1;
157 break;
158 case 'F':
159 fake = 1;
160 break;
161 case 'f':
162 fflag = MNT_FORCE;
163 break;
164 case 'h': /* -h implies -A. */
165 all = 2;
166 nfshost = optarg;
167 break;
168 case 't':
169 if (typelist != NULL)
170 errx(1, "only one -t option may be specified.");
171 typelist = makevfslist(optarg);
172 break;
173 case 'v':
174 vflag = 1;
175 break;
176 default:
177 usage();
178 /* NOTREACHED */
179 }
180 argc -= optind;
181 argv += optind;
182
183 if ((argc == 0 && !all) || (argc != 0 && all))
184 usage();
185
186 /* -h implies "-t nfs" if no -t flag. */
187 if ((nfshost != NULL) && (typelist == NULL))
188 typelist = makevfslist("nfs");
189
190 if (fflag & MNT_FORCE) {
191 /*
192 * If we really mean business, we don't want to get hung up on
193 * any remote file systems. So, we set the "noremotehang" flag.
194 */
195 pid_t pid;
196 pid = getpid();
197 errs = sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &pid, sizeof(pid));
198 if ((errs != 0) && vflag)
199 warn("sysctl vfs.generic.noremotehang");
200 }
201
202 errs = EXIT_SUCCESS;
203 switch (all) {
204 case 2:
205 if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
206 warn("getmntinfo");
207 errs = 1;
208 break;
209 }
210 for (errs = 0, mnts--; mnts > 0; mnts--) {
211 if (checkvfsname(mntbuf[mnts].f_fstypename, typelist))
212 continue;
213 if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0)
214 errs = 1;
215 }
216 break;
217 case 1:
218 if ((setfsent() == 0)) {
219 fprintf(stderr, "Can't get filesystem checklist: %s\n", strerror(errno));
220 exit(1);
221 }
222 errs = umountall(typelist);
223 break;
224 case 0:
225 for (errs = 0; *argv != NULL; ++argv)
226 if (umountfs(*argv, typelist) != 0)
227 errs = 1;
228 break;
229 }
230 exit(errs);
231 }
232
233 int
234 umountall(char **typelist)
235 {
236 struct fstab *fs;
237 int rval;
238 size_t cp_len;
239 char *cp;
240
241 while ((fs = getfsent()) != NULL) {
242 /* Ignore the root. */
243 if (strcmp(fs->fs_file, "/") == 0)
244 continue;
245 /*
246 * !!!
247 * Historic practice: ignore unknown FSTAB_* fields.
248 */
249 if (strcmp(fs->fs_type, FSTAB_RW) &&
250 strcmp(fs->fs_type, FSTAB_RO) &&
251 strcmp(fs->fs_type, FSTAB_RQ))
252 continue;
253 #if 0
254 /* If an unknown file system type, complain. */
255 if (getvfsbyname(fs->fs_vfstype, &vfc) < 0) {
256 warnx("%s: unknown mount type `%s'", fs->fs_spec, fs->fs_vfstype);
257 continue;
258 }
259 if (checkvfsname(fs->fs_vfstype, typelist))
260 continue;
261 #endif
262
263 /*
264 * We want to unmount the file systems in the reverse order
265 * that they were mounted. So, we save off the file name
266 * in some allocated memory, and then call recursively.
267 */
268 cp_len = (size_t)strlen(fs->fs_file) + 1;
269 if ((cp = malloc(cp_len)) == NULL)
270 err(1, NULL);
271 (void)strlcpy(cp, fs->fs_file, cp_len);
272 rval = umountall(typelist);
273 rval = umountfs(cp, typelist) || rval;
274 free(cp);
275 return (rval);
276 }
277 return (0);
278 }
279
280 int
281 umountfs(char *name, char **typelist)
282 {
283 struct hostent *hp, *hp6;
284 struct stat sb;
285 int isftpfs, errnum;
286 char *type, *delimp, *hostname, *mntpt, rname[MAXPATHLEN], *tname;
287 char *pname = name; /* save the name parameter */
288
289 /*
290 * First directly check the
291 * current mount list for a match. If we find it,
292 * we skip the realpath()/stat() below.
293 */
294 tname = name;
295 /* check if name is a non-device "mount from" name */
296 if ((mntpt = getmntname(tname, MNTON, &type)) == NULL) {
297 /* or if name is a mounted-on directory */
298 mntpt = tname;
299 tname = getmntname(mntpt, MNTFROM, &type);
300 }
301 if (mntpt && tname) {
302 if (fflag & MNT_FORCE) {
303 /*
304 * The bulk of this block is to try to do a sync on the filesystem
305 * being unmounted. We want to do this in another thread, so we
306 * can avoid blocking for a hardware or network reason. We will
307 * wait 10 seconds for the sync to finish; after that, we just
308 * ignore it and go ahead with the unmounting.
309 *
310 * We only want to do this in the event of a forced unmount.
311 */
312 int rv;
313 pthread_t tid;
314 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
315 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
316 struct syncarg args;
317 struct timespec timeout;
318
319 /* we found a match */
320 name = tname;
321
322 args.mntname = mntpt;
323 args.wakeup_flag = 0;
324 args.wakeup_cond = &cond;
325 args.wakeup_lock = &lock;
326
327 timeout.tv_sec = time(NULL) + 10; /* Wait 10 seconds */
328 timeout.tv_nsec = 0;
329
330 rv = pthread_create(&tid, NULL, &syncit, &args);
331 if (rv == 0 && pthread_mutex_lock(&lock) == 0) {
332 while (args.wakeup_flag == 0 && rv == 0)
333 rv = pthread_cond_timedwait(&cond, &lock, &timeout);
334
335 /* If this fails, not much we can do at this point... */
336 (void)pthread_mutex_unlock(&lock);
337 if (rv != 0) {
338 errno = rv;
339 warn("pthread_cond_timeout failed; continuing with unmount");
340 }
341 }
342 }
343 goto got_mount_point;
344 }
345
346 /*
347 * Note: in the face of path resolution errors (realpath/stat),
348 * we just try using the name passed in as is.
349 */
350 /* even if path resolution succeeds, but can't find mountpoint
351 * with the resolved path, we still want to try using the name
352 * as passed in.
353 */
354
355 if (realpath(name, rname) == NULL) {
356 if (vflag)
357 warn("realpath(%s)", rname);
358 } else {
359 name = rname;
360 }
361
362 /* we could just try MNTON and MNTFROM on name and again (if
363 * name is not the passed in param) MNTON and MNTFROM on
364 * pname.
365 *
366 * but we stat(name) here to avoid umounting the wrong thing
367 * if the mount table has an entry with the MNTFROM that is
368 * the same as the MNTON in another entry.
369 */
370
371 if (stat(name, &sb) < 0) {
372 if (vflag)
373 warn("stat(%s)", name);
374 /* maybe name is a non-device "mount from" name? */
375 if ((mntpt = getmntname(name, MNTON, &type)))
376 goto got_mount_point;
377 mntpt = name;
378 /* or name is a directory we simply can't reach? */
379 if ((name = getmntname(mntpt, MNTFROM, &type)))
380 goto got_mount_point;
381 } else if (S_ISBLK(sb.st_mode)) {
382 if ((mntpt = getmntname(name, MNTON, &type)))
383 goto got_mount_point;
384 } else if (S_ISDIR(sb.st_mode)) {
385 mntpt = name;
386 if ((name = getmntname(mntpt, MNTFROM, &type)))
387 goto got_mount_point;
388 } else {
389 warnx("%s: not a directory or special device", name);
390 }
391
392 /* haven't found mountpoint.
393 *
394 * if we were not using the name as passed in, then try using it.
395 */
396 if ((NULL == name) || (strcmp(name, pname) != 0)) {
397 name = pname;
398
399 if ((mntpt = getmntname(name, MNTON, &type)))
400 goto got_mount_point;
401 mntpt = name;
402 if ((name = getmntname(mntpt, MNTFROM, &type)))
403 goto got_mount_point;
404 }
405
406 warnx("%s: not currently mounted", pname);
407 return (1);
408
409 got_mount_point:
410
411 if (checkvfsname(type, typelist))
412 return (1);
413
414 if (!strncmp("ftp://", name, 6))
415 isftpfs = 1;
416 else
417 isftpfs = 0;
418
419 hp = hp6 = NULL;
420 delimp = NULL;
421 if (nfshost && !strcmp(type, "nfs") && !isftpfs) {
422 /*
423 * Parse the NFS host out of the name.
424 *
425 * If it starts with '[' then skip IPv6 literal characters
426 * until we find ']'. If we find other characters (or the
427 * closing ']' isn't followed by a ':', then don't consider
428 * it to be an IPv6 literal address.
429 *
430 * Scan the name string to find ":/" (or just ":"). The name
431 * is the portion of the string preceding the first ":/" (or ":").
432 */
433 char *p, *colon, *colonslash, c;
434 hostname = colon = colonslash = NULL;
435 p = name;
436 if (*p == '[') { /* Looks like an IPv6 literal address */
437 p++;
438 while (isxdigit(*p) || (*p == ':')) {
439 if (*p == ':') {
440 if (!colon)
441 colon = p;
442 if (!colonslash && (*(p+1) == '/'))
443 colonslash = p;
444 }
445 p++;
446 }
447 if ((*p == ']') && (*(p+1) == ':')) {
448 /* Found "[IPv6]:", double check that it's acceptable and use it. */
449 struct sockaddr_in6 sin6;
450 c = *p;
451 *p = '\0';
452 if (inet_pton(AF_INET6, name+1, &sin6))
453 hostname = name + 1;
454 *p = c;
455 }
456 }
457 /* if hostname not found yet, search for ":/" and ":" */
458 while (!hostname && *p && (!colon || !colonslash)) {
459 if (*p == ':') {
460 if (!colon)
461 colon = p;
462 if (!colonslash && (*(p+1) == '/'))
463 colonslash = p;
464 }
465 p++;
466 }
467 if (!hostname && (colonslash || colon)) {
468 /* host name is the string preceding the colon(slash) */
469 hostname = name;
470 if (colonslash)
471 p = colonslash;
472 else if (colon)
473 p = colon;
474 }
475 if (hostname) {
476 c = *p;
477 *p = '\0';
478 /* we just want all the names/aliases */
479 hp = getipnodebyname(hostname, AF_INET, 0, &errnum);
480 hp6 = getipnodebyname(hostname, AF_INET6, 0, &errnum);
481 *p = c;
482 }
483 }
484
485 if (nfshost && (hp || hp6)) {
486 int match = (namematch(hp) || namematch(hp6));
487 if (hp)
488 freehostent(hp);
489 if (hp6)
490 freehostent(hp6);
491 if (!match)
492 return (1);
493 }
494
495 if (vflag)
496 (void)printf("%s unmount from %s\n", name, mntpt);
497 if (fake)
498 return (0);
499
500 if (unmount(mntpt, fflag) < 0) {
501 /*
502 * If we're root and it looks like the error is that the
503 * mounted on directory is just not reachable or if we really
504 * want this filesystem unmounted (MNT_FORCE), then try doing
505 * the unmount by fsid. (Note: the sysctl only works for root)
506 */
507 if ((getuid() == 0) &&
508 ((errno == ESTALE) || (errno == ENOENT) || (fflag & MNT_FORCE))) {
509 if (vflag)
510 warn("unmount(%s)", mntpt);
511 if (unmount_by_fsid(mntpt, fflag) < 0) {
512 warn("unmount(%s)", mntpt);
513 return (1);
514 }
515 } else if (errno == EBUSY) {
516 fprintf(stderr, "umount(%s): %s -- try 'diskutil unmount'\n", mntpt, strerror(errno));
517 return (1);
518 } else {
519 warn("unmount(%s)", mntpt);
520 return (1);
521 }
522 }
523
524 return (0);
525 }
526
527 static struct statfs *mntbuf;
528 static int mntsize;
529
530 char *
531 getmntname(const char *name, mntwhat what, char **type)
532 {
533 int i;
534
535 if (mntbuf == NULL &&
536 (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
537 warn("getmntinfo");
538 return (NULL);
539 }
540 for (i = mntsize-1; i >= 0; i--) {
541 if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) {
542 if (type)
543 *type = mntbuf[i].f_fstypename;
544 return (mntbuf[i].f_mntonname);
545 }
546 if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) {
547 if (type)
548 *type = mntbuf[i].f_fstypename;
549 return (mntbuf[i].f_mntfromname);
550 }
551 }
552 return (NULL);
553 }
554
555 int
556 getmntfsid(const char *name, fsid_t *fsid)
557 {
558 int i;
559
560 if (mntbuf == NULL &&
561 (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
562 warn("getmntinfo");
563 return (-1);
564 }
565 for (i = mntsize-1; i >= 0; i--) {
566 if (!strcmp(mntbuf[i].f_mntonname, name)) {
567 *fsid = mntbuf[i].f_fsid;
568 return (0);
569 }
570 }
571 return (-1);
572 }
573
574 int
575 namematch(struct hostent *hp)
576 {
577 char *cp, **np;
578
579 if (nfshost == NULL)
580 return (1);
581
582 if (hp == NULL)
583 return (0);
584
585 if (strcasecmp(nfshost, hp->h_name) == 0)
586 return (1);
587
588 if ((cp = strchr(hp->h_name, '.')) != NULL) {
589 *cp = '\0';
590 if (strcasecmp(nfshost, hp->h_name) == 0)
591 return (1);
592 }
593 for (np = hp->h_aliases; *np; np++) {
594 if (strcasecmp(nfshost, *np) == 0)
595 return (1);
596 if ((cp = strchr(*np, '.')) != NULL) {
597 *cp = '\0';
598 if (strcasecmp(nfshost, *np) == 0)
599 return (1);
600 }
601 }
602 return (0);
603 }
604
605
606 int
607 sysctl_fsid(
608 int op,
609 fsid_t *fsid,
610 void *oldp,
611 size_t *oldlenp,
612 void *newp,
613 size_t newlen)
614 {
615 int ctlname[CTL_MAXNAME+2];
616 size_t ctllen;
617 const char *sysstr = "vfs.generic.ctlbyfsid";
618 struct vfsidctl vc;
619
620 ctllen = CTL_MAXNAME+2;
621 if (sysctlnametomib(sysstr, ctlname, &ctllen) == -1) {
622 warn("sysctlnametomib(%s)", sysstr);
623 return (-1);
624 };
625 ctlname[ctllen] = op;
626
627 bzero(&vc, sizeof(vc));
628 vc.vc_vers = VFS_CTL_VERS1;
629 vc.vc_fsid = *fsid;
630 vc.vc_ptr = newp;
631 vc.vc_len = newlen;
632 return (sysctl(ctlname, (uint32_t)(ctllen + 1), oldp, oldlenp, &vc, sizeof(vc)));
633 }
634
635
636 int
637 unmount_by_fsid(const char *mntpt, int flag)
638 {
639 fsid_t fsid;
640 if (getmntfsid(mntpt, &fsid) < 0)
641 return (-1);
642 if (vflag)
643 printf("attempting to unmount %s by fsid\n", mntpt);
644 return sysctl_fsid(VFS_CTL_UMOUNT, &fsid, NULL, 0, &flag, sizeof(flag));
645 }
646
647 void
648 usage()
649 {
650 (void)fprintf(stderr,
651 "usage: %s\n %s\n",
652 "umount [-fv] [-t fstypelist] special | node",
653 "umount -a[fv] [-h host] [-t fstypelist]");
654 exit(1);
655 }