1 #ifndef _DARWIN_USE_64_BIT_INODE
2 # define _DARWIN_USE_64_BIT_INODE 1
5 * Copyright (c) 1999-2020 Apple Inc. All rights reserved.
7 * @APPLE_LICENSE_HEADER_START@
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
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.
24 * @APPLE_LICENSE_HEADER_END@
27 * Copyright (c) 1980, 1989, 1993
28 * The Regents of the University of California. All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
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.
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
60 #include <sys/param.h>
62 #include <sys/mount.h>
64 #include <sys/sysctl.h>
65 #include <System/sys/fsctl.h>
68 #include <arpa/inet.h>
81 #include "../edt_fstab/edt_fstab.h"
86 pthread_cond_t
*wakeup_cond
;
87 pthread_mutex_t
*wakeup_lock
;
90 typedef enum { MNTON
, MNTFROM
} mntwhat
;
92 int fake
, fflag
, vflag
;
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 *);
102 int namematch(struct hostent
*);
103 int umountall(char **);
104 int umountfs(char *, char **);
110 pthread_mutex_t
*lock
;
111 int full_sync
= FSCTL_SYNC_WAIT
;
112 struct syncarg
*args
= vap
;
114 rv
= fsctl(args
->mntname
, FSIOC_SYNC_VOLUME
, &full_sync
, 0);
117 warn("fsctl %s", args
->mntname
);
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
);
131 main(int argc
, char *argv
[])
133 int all
, ch
, errs
, mnts
;
134 char **typelist
= NULL
;
135 struct statfs
*mntbuf
;
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).
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.
146 * See 5328558 for some context.
150 while ((ch
= getopt(argc
, argv
, "AaFfh:t:v")) != EOF
)
164 case 'h': /* -h implies -A. */
169 if (typelist
!= NULL
)
170 errx(1, "only one -t option may be specified.");
171 typelist
= makevfslist(optarg
);
183 if ((argc
== 0 && !all
) || (argc
!= 0 && all
))
186 /* -h implies "-t nfs" if no -t flag. */
187 if ((nfshost
!= NULL
) && (typelist
== NULL
))
188 typelist
= makevfslist("nfs");
190 if (fflag
& MNT_FORCE
) {
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.
197 errs
= sysctlbyname("vfs.generic.noremotehang", NULL
, NULL
, &pid
, sizeof(pid
));
198 if ((errs
!= 0) && vflag
)
199 warn("sysctl vfs.generic.noremotehang");
205 if ((mnts
= getmntinfo(&mntbuf
, MNT_NOWAIT
)) == 0) {
210 for (errs
= 0, mnts
--; mnts
> 0; mnts
--) {
211 if (checkvfsname(mntbuf
[mnts
].f_fstypename
, typelist
))
213 if (umountfs(mntbuf
[mnts
].f_mntonname
, typelist
) != 0)
218 if ((setfsent() == 0)) {
219 fprintf(stderr
, "Can't get filesystem checklist: %s\n", strerror(errno
));
222 errs
= umountall(typelist
);
225 for (errs
= 0; *argv
!= NULL
; ++argv
)
226 if (umountfs(*argv
, typelist
) != 0)
234 umountall(char **typelist
)
241 while ((fs
= getfsent()) != NULL
) {
242 /* Ignore the root. */
243 if (strcmp(fs
->fs_file
, "/") == 0)
247 * Historic practice: ignore unknown FSTAB_* fields.
249 if (strcmp(fs
->fs_type
, FSTAB_RW
) &&
250 strcmp(fs
->fs_type
, FSTAB_RO
) &&
251 strcmp(fs
->fs_type
, FSTAB_RQ
))
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
);
259 if (checkvfsname(fs
->fs_vfstype
, typelist
))
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.
268 cp_len
= (size_t)strlen(fs
->fs_file
) + 1;
269 if ((cp
= malloc(cp_len
)) == NULL
)
271 (void)strlcpy(cp
, fs
->fs_file
, cp_len
);
272 rval
= umountall(typelist
);
273 rval
= umountfs(cp
, typelist
) || rval
;
281 umountfs(char *name
, char **typelist
)
283 struct hostent
*hp
, *hp6
;
286 char *type
, *delimp
, *hostname
, *mntpt
, rname
[MAXPATHLEN
], *tname
;
287 char *pname
= name
; /* save the name parameter */
290 * First directly check the
291 * current mount list for a match. If we find it,
292 * we skip the realpath()/stat() below.
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 */
299 tname
= getmntname(mntpt
, MNTFROM
, &type
);
301 if (mntpt
&& tname
) {
302 if (fflag
& MNT_FORCE
) {
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.
310 * We only want to do this in the event of a forced unmount.
314 pthread_cond_t cond
= PTHREAD_COND_INITIALIZER
;
315 pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
317 struct timespec timeout
;
319 /* we found a match */
322 args
.mntname
= mntpt
;
323 args
.wakeup_flag
= 0;
324 args
.wakeup_cond
= &cond
;
325 args
.wakeup_lock
= &lock
;
327 timeout
.tv_sec
= time(NULL
) + 10; /* Wait 10 seconds */
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
);
335 /* If this fails, not much we can do at this point... */
336 (void)pthread_mutex_unlock(&lock
);
339 warn("pthread_cond_timeout failed; continuing with unmount");
343 goto got_mount_point
;
347 * Note: in the face of path resolution errors (realpath/stat),
348 * we just try using the name passed in as is.
350 /* even if path resolution succeeds, but can't find mountpoint
351 * with the resolved path, we still want to try using the name
355 if (realpath(name
, rname
) == NULL
) {
357 warn("realpath(%s)", rname
);
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
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.
371 if (stat(name
, &sb
) < 0) {
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
;
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
)) {
386 if ((name
= getmntname(mntpt
, MNTFROM
, &type
)))
387 goto got_mount_point
;
389 warnx("%s: not a directory or special device", name
);
392 /* haven't found mountpoint.
394 * if we were not using the name as passed in, then try using it.
396 if ((NULL
== name
) || (strcmp(name
, pname
) != 0)) {
399 if ((mntpt
= getmntname(name
, MNTON
, &type
)))
400 goto got_mount_point
;
402 if ((name
= getmntname(mntpt
, MNTFROM
, &type
)))
403 goto got_mount_point
;
406 warnx("%s: not currently mounted", pname
);
411 if (checkvfsname(type
, typelist
))
414 if (!strncmp("ftp://", name
, 6))
421 if (nfshost
&& !strcmp(type
, "nfs") && !isftpfs
) {
423 * Parse the NFS host out of the name.
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.
430 * Scan the name string to find ":/" (or just ":"). The name
431 * is the portion of the string preceding the first ":/" (or ":").
433 char *p
, *colon
, *colonslash
, c
;
434 hostname
= colon
= colonslash
= NULL
;
436 if (*p
== '[') { /* Looks like an IPv6 literal address */
438 while (isxdigit(*p
) || (*p
== ':')) {
442 if (!colonslash
&& (*(p
+1) == '/'))
447 if ((*p
== ']') && (*(p
+1) == ':')) {
448 /* Found "[IPv6]:", double check that it's acceptable and use it. */
449 struct sockaddr_in6 sin6
;
452 if (inet_pton(AF_INET6
, name
+1, &sin6
))
457 /* if hostname not found yet, search for ":/" and ":" */
458 while (!hostname
&& *p
&& (!colon
|| !colonslash
)) {
462 if (!colonslash
&& (*(p
+1) == '/'))
467 if (!hostname
&& (colonslash
|| colon
)) {
468 /* host name is the string preceding the colon(slash) */
478 /* we just want all the names/aliases */
479 hp
= getipnodebyname(hostname
, AF_INET
, 0, &errnum
);
480 hp6
= getipnodebyname(hostname
, AF_INET6
, 0, &errnum
);
485 if (nfshost
&& (hp
|| hp6
)) {
486 int match
= (namematch(hp
) || namematch(hp6
));
496 (void)printf("%s unmount from %s\n", name
, mntpt
);
500 if (unmount(mntpt
, fflag
) < 0) {
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)
507 if ((getuid() == 0) &&
508 ((errno
== ESTALE
) || (errno
== ENOENT
) || (fflag
& MNT_FORCE
))) {
510 warn("unmount(%s)", mntpt
);
511 if (unmount_by_fsid(mntpt
, fflag
) < 0) {
512 warn("unmount(%s)", mntpt
);
515 } else if (errno
== EBUSY
) {
516 fprintf(stderr
, "umount(%s): %s -- try 'diskutil unmount'\n", mntpt
, strerror(errno
));
519 warn("unmount(%s)", mntpt
);
527 static struct statfs
*mntbuf
;
531 getmntname(const char *name
, mntwhat what
, char **type
)
535 if (mntbuf
== NULL
&&
536 (mntsize
= getmntinfo(&mntbuf
, MNT_NOWAIT
)) == 0) {
540 for (i
= mntsize
-1; i
>= 0; i
--) {
541 if ((what
== MNTON
) && !strcmp(mntbuf
[i
].f_mntfromname
, name
)) {
543 *type
= mntbuf
[i
].f_fstypename
;
544 return (mntbuf
[i
].f_mntonname
);
546 if ((what
== MNTFROM
) && !strcmp(mntbuf
[i
].f_mntonname
, name
)) {
548 *type
= mntbuf
[i
].f_fstypename
;
549 return (mntbuf
[i
].f_mntfromname
);
556 getmntfsid(const char *name
, fsid_t
*fsid
)
560 if (mntbuf
== NULL
&&
561 (mntsize
= getmntinfo(&mntbuf
, MNT_NOWAIT
)) == 0) {
565 for (i
= mntsize
-1; i
>= 0; i
--) {
566 if (!strcmp(mntbuf
[i
].f_mntonname
, name
)) {
567 *fsid
= mntbuf
[i
].f_fsid
;
575 namematch(struct hostent
*hp
)
585 if (strcasecmp(nfshost
, hp
->h_name
) == 0)
588 if ((cp
= strchr(hp
->h_name
, '.')) != NULL
) {
590 if (strcasecmp(nfshost
, hp
->h_name
) == 0)
593 for (np
= hp
->h_aliases
; *np
; np
++) {
594 if (strcasecmp(nfshost
, *np
) == 0)
596 if ((cp
= strchr(*np
, '.')) != NULL
) {
598 if (strcasecmp(nfshost
, *np
) == 0)
615 int ctlname
[CTL_MAXNAME
+2];
617 const char *sysstr
= "vfs.generic.ctlbyfsid";
620 ctllen
= CTL_MAXNAME
+2;
621 if (sysctlnametomib(sysstr
, ctlname
, &ctllen
) == -1) {
622 warn("sysctlnametomib(%s)", sysstr
);
625 ctlname
[ctllen
] = op
;
627 bzero(&vc
, sizeof(vc
));
628 vc
.vc_vers
= VFS_CTL_VERS1
;
632 return (sysctl(ctlname
, (uint32_t)(ctllen
+ 1), oldp
, oldlenp
, &vc
, sizeof(vc
)));
637 unmount_by_fsid(const char *mntpt
, int flag
)
640 if (getmntfsid(mntpt
, &fsid
) < 0)
643 printf("attempting to unmount %s by fsid\n", mntpt
);
644 return sysctl_fsid(VFS_CTL_UMOUNT
, &fsid
, NULL
, 0, &flag
, sizeof(flag
));
650 (void)fprintf(stderr
,
652 "umount [-fv] [-t fstypelist] special | node",
653 "umount -a[fv] [-h host] [-t fstypelist]");