From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- diskdev_cmds/umount.tproj/umount.c | 655 +++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 diskdev_cmds/umount.tproj/umount.c (limited to 'diskdev_cmds/umount.tproj/umount.c') diff --git a/diskdev_cmds/umount.tproj/umount.c b/diskdev_cmds/umount.tproj/umount.c new file mode 100644 index 0000000..26e9ba4 --- /dev/null +++ b/diskdev_cmds/umount.tproj/umount.c @@ -0,0 +1,655 @@ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif +/* + * Copyright (c) 1999-2020 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/*- + * Copyright (c) 1980, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../edt_fstab/edt_fstab.h" + +struct syncarg { + const char *mntname; + int wakeup_flag; + pthread_cond_t *wakeup_cond; + pthread_mutex_t *wakeup_lock; +}; + +typedef enum { MNTON, MNTFROM } mntwhat; + +int fake, fflag, vflag; +char *nfshost; + +int checkvfsname(const char *, char **); +char *getmntname(const char *, mntwhat, char **); +int getmntfsid(const char *, fsid_t *); +int sysctl_fsid(int, fsid_t *, void *, size_t *, void *, size_t); +int unmount_by_fsid(const char *mntpt, int flag); +char **makevfslist(char *); +int selected(int); +int namematch(struct hostent *); +int umountall(char **); +int umountfs(char *, char **); +void usage(void); + +static void* +syncit(void *vap) { + int rv; + pthread_mutex_t *lock; + int full_sync = FSCTL_SYNC_WAIT; + struct syncarg *args = vap; + + rv = fsctl(args->mntname, FSIOC_SYNC_VOLUME, &full_sync, 0); + if (rv == -1) { +#ifdef DEBUG + warn("fsctl %s", args->mntname); +#endif + } + + lock = args->wakeup_lock; + (void)pthread_mutex_lock(lock); + args->wakeup_flag = 1; + pthread_cond_signal(args->wakeup_cond); + (void)pthread_mutex_unlock(lock); + + return NULL; +} + +int +main(int argc, char *argv[]) +{ + int all, ch, errs, mnts; + char **typelist = NULL; + struct statfs *mntbuf; + + /* + * We used to call sync(2) here, but this should be unneccessary + * given that a sync occurs at a more proper level (VFS_SYNC() + * in dounmount() in the non-forced unmount case). + * + * We add the sync() back in for the -f case below to cover the + * situation where the filesystem was mounted RW and force + * unmounted when it really didn't have to be. + * + * See 5328558 for some context. + */ + + all = 0; + while ((ch = getopt(argc, argv, "AaFfh:t:v")) != EOF) + switch (ch) { + case 'A': + all = 2; + break; + case 'a': + all = 1; + break; + case 'F': + fake = 1; + break; + case 'f': + fflag = MNT_FORCE; + break; + case 'h': /* -h implies -A. */ + all = 2; + nfshost = optarg; + break; + case 't': + if (typelist != NULL) + errx(1, "only one -t option may be specified."); + typelist = makevfslist(optarg); + break; + case 'v': + vflag = 1; + break; + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if ((argc == 0 && !all) || (argc != 0 && all)) + usage(); + + /* -h implies "-t nfs" if no -t flag. */ + if ((nfshost != NULL) && (typelist == NULL)) + typelist = makevfslist("nfs"); + + if (fflag & MNT_FORCE) { + /* + * If we really mean business, we don't want to get hung up on + * any remote file systems. So, we set the "noremotehang" flag. + */ + pid_t pid; + pid = getpid(); + errs = sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &pid, sizeof(pid)); + if ((errs != 0) && vflag) + warn("sysctl vfs.generic.noremotehang"); + } + + errs = EXIT_SUCCESS; + switch (all) { + case 2: + if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + warn("getmntinfo"); + errs = 1; + break; + } + for (errs = 0, mnts--; mnts > 0; mnts--) { + if (checkvfsname(mntbuf[mnts].f_fstypename, typelist)) + continue; + if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0) + errs = 1; + } + break; + case 1: + if ((setfsent() == 0)) { + fprintf(stderr, "Can't get filesystem checklist: %s\n", strerror(errno)); + exit(1); + } + errs = umountall(typelist); + break; + case 0: + for (errs = 0; *argv != NULL; ++argv) + if (umountfs(*argv, typelist) != 0) + errs = 1; + break; + } + exit(errs); +} + +int +umountall(char **typelist) +{ + struct fstab *fs; + int rval; + size_t cp_len; + char *cp; + + while ((fs = getfsent()) != NULL) { + /* Ignore the root. */ + if (strcmp(fs->fs_file, "/") == 0) + continue; + /* + * !!! + * Historic practice: ignore unknown FSTAB_* fields. + */ + if (strcmp(fs->fs_type, FSTAB_RW) && + strcmp(fs->fs_type, FSTAB_RO) && + strcmp(fs->fs_type, FSTAB_RQ)) + continue; +#if 0 + /* If an unknown file system type, complain. */ + if (getvfsbyname(fs->fs_vfstype, &vfc) < 0) { + warnx("%s: unknown mount type `%s'", fs->fs_spec, fs->fs_vfstype); + continue; + } + if (checkvfsname(fs->fs_vfstype, typelist)) + continue; +#endif + + /* + * We want to unmount the file systems in the reverse order + * that they were mounted. So, we save off the file name + * in some allocated memory, and then call recursively. + */ + cp_len = (size_t)strlen(fs->fs_file) + 1; + if ((cp = malloc(cp_len)) == NULL) + err(1, NULL); + (void)strlcpy(cp, fs->fs_file, cp_len); + rval = umountall(typelist); + rval = umountfs(cp, typelist) || rval; + free(cp); + return (rval); + } + return (0); +} + +int +umountfs(char *name, char **typelist) +{ + struct hostent *hp, *hp6; + struct stat sb; + int isftpfs, errnum; + char *type, *delimp, *hostname, *mntpt, rname[MAXPATHLEN], *tname; + char *pname = name; /* save the name parameter */ + + /* + * First directly check the + * current mount list for a match. If we find it, + * we skip the realpath()/stat() below. + */ + tname = name; + /* check if name is a non-device "mount from" name */ + if ((mntpt = getmntname(tname, MNTON, &type)) == NULL) { + /* or if name is a mounted-on directory */ + mntpt = tname; + tname = getmntname(mntpt, MNTFROM, &type); + } + if (mntpt && tname) { + if (fflag & MNT_FORCE) { + /* + * The bulk of this block is to try to do a sync on the filesystem + * being unmounted. We want to do this in another thread, so we + * can avoid blocking for a hardware or network reason. We will + * wait 10 seconds for the sync to finish; after that, we just + * ignore it and go ahead with the unmounting. + * + * We only want to do this in the event of a forced unmount. + */ + int rv; + pthread_t tid; + pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + struct syncarg args; + struct timespec timeout; + + /* we found a match */ + name = tname; + + args.mntname = mntpt; + args.wakeup_flag = 0; + args.wakeup_cond = &cond; + args.wakeup_lock = &lock; + + timeout.tv_sec = time(NULL) + 10; /* Wait 10 seconds */ + timeout.tv_nsec = 0; + + rv = pthread_create(&tid, NULL, &syncit, &args); + if (rv == 0 && pthread_mutex_lock(&lock) == 0) { + while (args.wakeup_flag == 0 && rv == 0) + rv = pthread_cond_timedwait(&cond, &lock, &timeout); + + /* If this fails, not much we can do at this point... */ + (void)pthread_mutex_unlock(&lock); + if (rv != 0) { + errno = rv; + warn("pthread_cond_timeout failed; continuing with unmount"); + } + } + } + goto got_mount_point; + } + + /* + * Note: in the face of path resolution errors (realpath/stat), + * we just try using the name passed in as is. + */ + /* even if path resolution succeeds, but can't find mountpoint + * with the resolved path, we still want to try using the name + * as passed in. + */ + + if (realpath(name, rname) == NULL) { + if (vflag) + warn("realpath(%s)", rname); + } else { + name = rname; + } + + /* we could just try MNTON and MNTFROM on name and again (if + * name is not the passed in param) MNTON and MNTFROM on + * pname. + * + * but we stat(name) here to avoid umounting the wrong thing + * if the mount table has an entry with the MNTFROM that is + * the same as the MNTON in another entry. + */ + + if (stat(name, &sb) < 0) { + if (vflag) + warn("stat(%s)", name); + /* maybe name is a non-device "mount from" name? */ + if ((mntpt = getmntname(name, MNTON, &type))) + goto got_mount_point; + mntpt = name; + /* or name is a directory we simply can't reach? */ + if ((name = getmntname(mntpt, MNTFROM, &type))) + goto got_mount_point; + } else if (S_ISBLK(sb.st_mode)) { + if ((mntpt = getmntname(name, MNTON, &type))) + goto got_mount_point; + } else if (S_ISDIR(sb.st_mode)) { + mntpt = name; + if ((name = getmntname(mntpt, MNTFROM, &type))) + goto got_mount_point; + } else { + warnx("%s: not a directory or special device", name); + } + + /* haven't found mountpoint. + * + * if we were not using the name as passed in, then try using it. + */ + if ((NULL == name) || (strcmp(name, pname) != 0)) { + name = pname; + + if ((mntpt = getmntname(name, MNTON, &type))) + goto got_mount_point; + mntpt = name; + if ((name = getmntname(mntpt, MNTFROM, &type))) + goto got_mount_point; + } + + warnx("%s: not currently mounted", pname); + return (1); + +got_mount_point: + + if (checkvfsname(type, typelist)) + return (1); + + if (!strncmp("ftp://", name, 6)) + isftpfs = 1; + else + isftpfs = 0; + + hp = hp6 = NULL; + delimp = NULL; + if (nfshost && !strcmp(type, "nfs") && !isftpfs) { + /* + * Parse the NFS host out of the name. + * + * If it starts with '[' then skip IPv6 literal characters + * until we find ']'. If we find other characters (or the + * closing ']' isn't followed by a ':', then don't consider + * it to be an IPv6 literal address. + * + * Scan the name string to find ":/" (or just ":"). The name + * is the portion of the string preceding the first ":/" (or ":"). + */ + char *p, *colon, *colonslash, c; + hostname = colon = colonslash = NULL; + p = name; + if (*p == '[') { /* Looks like an IPv6 literal address */ + p++; + while (isxdigit(*p) || (*p == ':')) { + if (*p == ':') { + if (!colon) + colon = p; + if (!colonslash && (*(p+1) == '/')) + colonslash = p; + } + p++; + } + if ((*p == ']') && (*(p+1) == ':')) { + /* Found "[IPv6]:", double check that it's acceptable and use it. */ + struct sockaddr_in6 sin6; + c = *p; + *p = '\0'; + if (inet_pton(AF_INET6, name+1, &sin6)) + hostname = name + 1; + *p = c; + } + } + /* if hostname not found yet, search for ":/" and ":" */ + while (!hostname && *p && (!colon || !colonslash)) { + if (*p == ':') { + if (!colon) + colon = p; + if (!colonslash && (*(p+1) == '/')) + colonslash = p; + } + p++; + } + if (!hostname && (colonslash || colon)) { + /* host name is the string preceding the colon(slash) */ + hostname = name; + if (colonslash) + p = colonslash; + else if (colon) + p = colon; + } + if (hostname) { + c = *p; + *p = '\0'; + /* we just want all the names/aliases */ + hp = getipnodebyname(hostname, AF_INET, 0, &errnum); + hp6 = getipnodebyname(hostname, AF_INET6, 0, &errnum); + *p = c; + } + } + + if (nfshost && (hp || hp6)) { + int match = (namematch(hp) || namematch(hp6)); + if (hp) + freehostent(hp); + if (hp6) + freehostent(hp6); + if (!match) + return (1); + } + + if (vflag) + (void)printf("%s unmount from %s\n", name, mntpt); + if (fake) + return (0); + + if (unmount(mntpt, fflag) < 0) { + /* + * If we're root and it looks like the error is that the + * mounted on directory is just not reachable or if we really + * want this filesystem unmounted (MNT_FORCE), then try doing + * the unmount by fsid. (Note: the sysctl only works for root) + */ + if ((getuid() == 0) && + ((errno == ESTALE) || (errno == ENOENT) || (fflag & MNT_FORCE))) { + if (vflag) + warn("unmount(%s)", mntpt); + if (unmount_by_fsid(mntpt, fflag) < 0) { + warn("unmount(%s)", mntpt); + return (1); + } + } else if (errno == EBUSY) { + fprintf(stderr, "umount(%s): %s -- try 'diskutil unmount'\n", mntpt, strerror(errno)); + return (1); + } else { + warn("unmount(%s)", mntpt); + return (1); + } + } + + return (0); +} + +static struct statfs *mntbuf; +static int mntsize; + +char * +getmntname(const char *name, mntwhat what, char **type) +{ + int i; + + if (mntbuf == NULL && + (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + warn("getmntinfo"); + return (NULL); + } + for (i = mntsize-1; i >= 0; i--) { + if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) { + if (type) + *type = mntbuf[i].f_fstypename; + return (mntbuf[i].f_mntonname); + } + if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) { + if (type) + *type = mntbuf[i].f_fstypename; + return (mntbuf[i].f_mntfromname); + } + } + return (NULL); +} + +int +getmntfsid(const char *name, fsid_t *fsid) +{ + int i; + + if (mntbuf == NULL && + (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { + warn("getmntinfo"); + return (-1); + } + for (i = mntsize-1; i >= 0; i--) { + if (!strcmp(mntbuf[i].f_mntonname, name)) { + *fsid = mntbuf[i].f_fsid; + return (0); + } + } + return (-1); +} + +int +namematch(struct hostent *hp) +{ + char *cp, **np; + + if (nfshost == NULL) + return (1); + + if (hp == NULL) + return (0); + + if (strcasecmp(nfshost, hp->h_name) == 0) + return (1); + + if ((cp = strchr(hp->h_name, '.')) != NULL) { + *cp = '\0'; + if (strcasecmp(nfshost, hp->h_name) == 0) + return (1); + } + for (np = hp->h_aliases; *np; np++) { + if (strcasecmp(nfshost, *np) == 0) + return (1); + if ((cp = strchr(*np, '.')) != NULL) { + *cp = '\0'; + if (strcasecmp(nfshost, *np) == 0) + return (1); + } + } + return (0); +} + + +int +sysctl_fsid( + int op, + fsid_t *fsid, + void *oldp, + size_t *oldlenp, + void *newp, + size_t newlen) +{ + int ctlname[CTL_MAXNAME+2]; + size_t ctllen; + const char *sysstr = "vfs.generic.ctlbyfsid"; + struct vfsidctl vc; + + ctllen = CTL_MAXNAME+2; + if (sysctlnametomib(sysstr, ctlname, &ctllen) == -1) { + warn("sysctlnametomib(%s)", sysstr); + return (-1); + }; + ctlname[ctllen] = op; + + bzero(&vc, sizeof(vc)); + vc.vc_vers = VFS_CTL_VERS1; + vc.vc_fsid = *fsid; + vc.vc_ptr = newp; + vc.vc_len = newlen; + return (sysctl(ctlname, (uint32_t)(ctllen + 1), oldp, oldlenp, &vc, sizeof(vc))); +} + + +int +unmount_by_fsid(const char *mntpt, int flag) +{ + fsid_t fsid; + if (getmntfsid(mntpt, &fsid) < 0) + return (-1); + if (vflag) + printf("attempting to unmount %s by fsid\n", mntpt); + return sysctl_fsid(VFS_CTL_UMOUNT, &fsid, NULL, 0, &flag, sizeof(flag)); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s\n %s\n", + "umount [-fv] [-t fstypelist] special | node", + "umount -a[fv] [-h host] [-t fstypelist]"); + exit(1); +} -- cgit v1.2.3-56-ge451