18fae3551SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
48fae3551SRodney W. Grimes * Copyright (c) 1980, 1989, 1993
58fae3551SRodney W. Grimes * The Regents of the University of California. All rights reserved.
68fae3551SRodney W. Grimes *
78fae3551SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
88fae3551SRodney W. Grimes * modification, are permitted provided that the following conditions
98fae3551SRodney W. Grimes * are met:
108fae3551SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
118fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
128fae3551SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
138fae3551SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
148fae3551SRodney W. Grimes * documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
168fae3551SRodney W. Grimes * may be used to endorse or promote products derived from this software
178fae3551SRodney W. Grimes * without specific prior written permission.
188fae3551SRodney W. Grimes *
198fae3551SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
208fae3551SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
218fae3551SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
228fae3551SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
238fae3551SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
248fae3551SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
258fae3551SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
268fae3551SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
278fae3551SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288fae3551SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298fae3551SRodney W. Grimes * SUCH DAMAGE.
308fae3551SRodney W. Grimes */
318fae3551SRodney W. Grimes
328fae3551SRodney W. Grimes #include <sys/param.h>
338fae3551SRodney W. Grimes #include <sys/mount.h>
348360efbdSAlfred Perlstein #include <sys/socket.h>
35eddb4805SIan Dowse #include <sys/stat.h>
368fae3551SRodney W. Grimes
378fae3551SRodney W. Grimes #include <netdb.h>
388fae3551SRodney W. Grimes #include <rpc/rpc.h>
390775314bSDoug Rabson #include <rpcsvc/mount.h>
4049a8bad7SRick Macklem #include <nfs/nfssvc.h>
418fae3551SRodney W. Grimes
4238f102c2SIan Dowse #include <ctype.h>
438fae3551SRodney W. Grimes #include <err.h>
44318f2fb4SIan Dowse #include <errno.h>
458fae3551SRodney W. Grimes #include <fstab.h>
468fae3551SRodney W. Grimes #include <stdio.h>
478fae3551SRodney W. Grimes #include <stdlib.h>
488fae3551SRodney W. Grimes #include <string.h>
498fae3551SRodney W. Grimes #include <unistd.h>
508fae3551SRodney W. Grimes
51a69497d7SMatthew Dillon #include "mounttab.h"
52a69497d7SMatthew Dillon
53840a8027SRicardo Branco /* used by md_detach() */
54840a8027SRicardo Branco #include <sys/ioctl.h>
55840a8027SRicardo Branco #include <sys/mdioctl.h>
56840a8027SRicardo Branco #include <fcntl.h>
57840a8027SRicardo Branco #include <paths.h>
58840a8027SRicardo Branco
59840a8027SRicardo Branco #define DEV_MD _PATH_DEV MD_NAME
60840a8027SRicardo Branco #define DEV_MDCTL _PATH_DEV MDCTL_NAME
61840a8027SRicardo Branco
62eddb4805SIan Dowse typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
63bc70c172SBrian Feldman
641efe3c6bSEd Schouten static struct addrinfo *nfshost_ai = NULL;
65840a8027SRicardo Branco static int dflag, fflag, vflag;
66840a8027SRicardo Branco static int all;
671efe3c6bSEd Schouten static char *nfshost;
688fae3551SRodney W. Grimes
691add162cSIan Dowse struct statfs *checkmntlist(char *);
70bc70c172SBrian Feldman int checkvfsname (const char *, char **);
71eddb4805SIan Dowse struct statfs *getmntentry(const char *fromname, const char *onname,
72eddb4805SIan Dowse fsid_t *fsid, dowhat what);
73bc70c172SBrian Feldman char **makevfslist (const char *);
74bc70c172SBrian Feldman size_t mntinfo (struct statfs **);
758360efbdSAlfred Perlstein int namematch (struct addrinfo *);
76eddb4805SIan Dowse int parsehexfsid(const char *hex, fsid_t *fsid);
777f471a32SXin LI int sacmp (void *, void *);
78bc70c172SBrian Feldman int umountall (char **);
798360efbdSAlfred Perlstein int checkname (char *, char **);
80eddb4805SIan Dowse int umountfs(struct statfs *sfs);
81bc70c172SBrian Feldman void usage (void);
82bc70c172SBrian Feldman int xdr_dir (XDR *, char *);
83840a8027SRicardo Branco int md_detach(const char *);
848fae3551SRodney W. Grimes
858fae3551SRodney W. Grimes int
main(int argc,char * argv[])86bc70c172SBrian Feldman main(int argc, char *argv[])
878fae3551SRodney W. Grimes {
88840a8027SRicardo Branco int errs, ch, mntsize, error, nfsforce, ret;
89f73d495fSIan Dowse char **typelist = NULL;
90f73d495fSIan Dowse struct statfs *mntbuf, *sfs;
918360efbdSAlfred Perlstein struct addrinfo hints;
928fae3551SRodney W. Grimes
934261923eSRick Macklem nfsforce = all = errs = 0;
94840a8027SRicardo Branco while ((ch = getopt(argc, argv, "AadF:fh:Nnt:v")) != -1)
958fae3551SRodney W. Grimes switch (ch) {
96d499a0efSBruce Evans case 'A':
97d499a0efSBruce Evans all = 2;
98d499a0efSBruce Evans break;
998fae3551SRodney W. Grimes case 'a':
1008fae3551SRodney W. Grimes all = 1;
1018fae3551SRodney W. Grimes break;
102840a8027SRicardo Branco case 'd':
103840a8027SRicardo Branco dflag = 1;
104840a8027SRicardo Branco break;
105ef258dd9SMatthew N. Dodd case 'F':
106ef258dd9SMatthew N. Dodd setfstab(optarg);
107ef258dd9SMatthew N. Dodd break;
1088fae3551SRodney W. Grimes case 'f':
109debc480eSEdward Tomasz Napierala fflag |= MNT_FORCE;
1108fae3551SRodney W. Grimes break;
111d499a0efSBruce Evans case 'h': /* -h implies -A. */
112d499a0efSBruce Evans all = 2;
1138fae3551SRodney W. Grimes nfshost = optarg;
1148fae3551SRodney W. Grimes break;
1154261923eSRick Macklem case 'N':
1164261923eSRick Macklem nfsforce = 1;
1174261923eSRick Macklem break;
118debc480eSEdward Tomasz Napierala case 'n':
119debc480eSEdward Tomasz Napierala fflag |= MNT_NONBUSY;
120debc480eSEdward Tomasz Napierala break;
1218fae3551SRodney W. Grimes case 't':
122d499a0efSBruce Evans if (typelist != NULL)
123bc70c172SBrian Feldman err(1, "only one -t option may be specified");
124d499a0efSBruce Evans typelist = makevfslist(optarg);
1258fae3551SRodney W. Grimes break;
1268fae3551SRodney W. Grimes case 'v':
1278fae3551SRodney W. Grimes vflag = 1;
1288fae3551SRodney W. Grimes break;
1298fae3551SRodney W. Grimes default:
1308fae3551SRodney W. Grimes usage();
1318fae3551SRodney W. Grimes /* NOTREACHED */
1328fae3551SRodney W. Grimes }
1338fae3551SRodney W. Grimes argc -= optind;
1348fae3551SRodney W. Grimes argv += optind;
1358fae3551SRodney W. Grimes
136debc480eSEdward Tomasz Napierala if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0)
137debc480eSEdward Tomasz Napierala err(1, "-f and -n are mutually exclusive");
138debc480eSEdward Tomasz Napierala
139adb378ceSPhilippe Charnier if ((argc == 0 && !all) || (argc != 0 && all))
1408fae3551SRodney W. Grimes usage();
1418fae3551SRodney W. Grimes
1424261923eSRick Macklem if (nfsforce != 0 && (argc == 0 || nfshost != NULL || typelist != NULL))
1434261923eSRick Macklem usage();
1444261923eSRick Macklem
1458fae3551SRodney W. Grimes /* -h implies "-t nfs" if no -t flag. */
1468fae3551SRodney W. Grimes if ((nfshost != NULL) && (typelist == NULL))
147d499a0efSBruce Evans typelist = makevfslist("nfs");
1488fae3551SRodney W. Grimes
1498360efbdSAlfred Perlstein if (nfshost != NULL) {
1508360efbdSAlfred Perlstein memset(&hints, 0, sizeof hints);
1518360efbdSAlfred Perlstein error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
15221eff82fSIan Dowse if (error)
15313e2e1afSIan Dowse errx(1, "%s: %s", nfshost, gai_strerror(error));
1548360efbdSAlfred Perlstein }
1558360efbdSAlfred Perlstein
156d499a0efSBruce Evans switch (all) {
157d499a0efSBruce Evans case 2:
158bc70c172SBrian Feldman if ((mntsize = mntinfo(&mntbuf)) <= 0)
159d499a0efSBruce Evans break;
160bc70c172SBrian Feldman /*
161bc70c172SBrian Feldman * We unmount the nfs-mounts in the reverse order
162bc70c172SBrian Feldman * that they were mounted.
163bc70c172SBrian Feldman */
164bc70c172SBrian Feldman for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
165f73d495fSIan Dowse sfs = &mntbuf[mntsize];
166f73d495fSIan Dowse if (checkvfsname(sfs->f_fstypename, typelist))
167d499a0efSBruce Evans continue;
16880bbaeb5SKirk McKusick if (strcmp(sfs->f_mntonname, "/dev") == 0)
16980bbaeb5SKirk McKusick continue;
170eddb4805SIan Dowse if (umountfs(sfs) != 0)
171d499a0efSBruce Evans errs = 1;
172d499a0efSBruce Evans }
173bc70c172SBrian Feldman free(mntbuf);
174d499a0efSBruce Evans break;
175d499a0efSBruce Evans case 1:
1768fae3551SRodney W. Grimes if (setfsent() == 0)
177ef258dd9SMatthew N. Dodd err(1, "%s", getfstab());
178d499a0efSBruce Evans errs = umountall(typelist);
179d499a0efSBruce Evans break;
180d499a0efSBruce Evans case 0:
1818fae3551SRodney W. Grimes for (errs = 0; *argv != NULL; ++argv)
1824261923eSRick Macklem if (nfsforce != 0) {
1834261923eSRick Macklem /*
1844261923eSRick Macklem * First do the nfssvc() syscall to shut down
1854261923eSRick Macklem * the mount point and then do the forced
1864261923eSRick Macklem * dismount.
1874261923eSRick Macklem */
1884261923eSRick Macklem ret = nfssvc(NFSSVC_FORCEDISM, *argv);
1894261923eSRick Macklem if (ret >= 0)
1904261923eSRick Macklem ret = unmount(*argv, MNT_FORCE);
1914261923eSRick Macklem if (ret < 0) {
1924261923eSRick Macklem warn("%s", *argv);
1934261923eSRick Macklem errs = 1;
1944261923eSRick Macklem }
1954261923eSRick Macklem } else if (checkname(*argv, typelist) != 0)
196d499a0efSBruce Evans errs = 1;
197d499a0efSBruce Evans break;
198d499a0efSBruce Evans }
1998fae3551SRodney W. Grimes exit(errs);
2008fae3551SRodney W. Grimes }
2018fae3551SRodney W. Grimes
2028fae3551SRodney W. Grimes int
umountall(char ** typelist)203bc70c172SBrian Feldman umountall(char **typelist)
2048fae3551SRodney W. Grimes {
2055965373eSMaxime Henrion struct xvfsconf vfc;
2068fae3551SRodney W. Grimes struct fstab *fs;
207adb378ceSPhilippe Charnier int rval;
2088fae3551SRodney W. Grimes char *cp;
2090602ee7cSBrian Feldman static int firstcall = 1;
2108fae3551SRodney W. Grimes
21191a81678SBrian Feldman if ((fs = getfsent()) != NULL)
2120602ee7cSBrian Feldman firstcall = 0;
21391a81678SBrian Feldman else if (firstcall)
21491a81678SBrian Feldman errx(1, "fstab reading failure");
21591a81678SBrian Feldman else
21691a81678SBrian Feldman return (0);
217bc70c172SBrian Feldman do {
2188fae3551SRodney W. Grimes /* Ignore the root. */
2198fae3551SRodney W. Grimes if (strcmp(fs->fs_file, "/") == 0)
2208fae3551SRodney W. Grimes continue;
2218fae3551SRodney W. Grimes /*
2228fae3551SRodney W. Grimes * !!!
2238fae3551SRodney W. Grimes * Historic practice: ignore unknown FSTAB_* fields.
2248fae3551SRodney W. Grimes */
2258fae3551SRodney W. Grimes if (strcmp(fs->fs_type, FSTAB_RW) &&
2268fae3551SRodney W. Grimes strcmp(fs->fs_type, FSTAB_RO) &&
2278fae3551SRodney W. Grimes strcmp(fs->fs_type, FSTAB_RQ))
2288fae3551SRodney W. Grimes continue;
229b6e55a05SDag-Erling Smørgrav /* Ignore unknown file system types. */
23081667275SDag-Erling Smørgrav if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
2318fae3551SRodney W. Grimes continue;
232d499a0efSBruce Evans if (checkvfsname(fs->fs_vfstype, typelist))
2338fae3551SRodney W. Grimes continue;
2348fae3551SRodney W. Grimes
2358fae3551SRodney W. Grimes /*
2368fae3551SRodney W. Grimes * We want to unmount the file systems in the reverse order
2378fae3551SRodney W. Grimes * that they were mounted. So, we save off the file name
2388fae3551SRodney W. Grimes * in some allocated memory, and then call recursively.
2398fae3551SRodney W. Grimes */
2408fae3551SRodney W. Grimes if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
241bc70c172SBrian Feldman err(1, "malloc failed");
2428fae3551SRodney W. Grimes (void)strcpy(cp, fs->fs_file);
243d499a0efSBruce Evans rval = umountall(typelist);
2448360efbdSAlfred Perlstein rval = checkname(cp, typelist) || rval;
245bc70c172SBrian Feldman free(cp);
2460602ee7cSBrian Feldman return (rval);
2470602ee7cSBrian Feldman } while ((fs = getfsent()) != NULL);
2488fae3551SRodney W. Grimes return (0);
2498fae3551SRodney W. Grimes }
2508fae3551SRodney W. Grimes
2518360efbdSAlfred Perlstein /*
252eddb4805SIan Dowse * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
2538360efbdSAlfred Perlstein */
2548fae3551SRodney W. Grimes int
checkname(char * mntname,char ** typelist)2557f471a32SXin LI checkname(char *mntname, char **typelist)
2568fae3551SRodney W. Grimes {
257eddb4805SIan Dowse char buf[MAXPATHLEN];
258eddb4805SIan Dowse struct statfs sfsbuf;
259eddb4805SIan Dowse struct stat sb;
260f73d495fSIan Dowse struct statfs *sfs;
261eddb4805SIan Dowse char *delimp;
262eddb4805SIan Dowse dev_t dev;
263eddb4805SIan Dowse int len;
2648fae3551SRodney W. Grimes
265bc70c172SBrian Feldman /*
266bc70c172SBrian Feldman * 1. Check if the name exists in the mounttable.
267bc70c172SBrian Feldman */
2687f471a32SXin LI sfs = checkmntlist(mntname);
269bc70c172SBrian Feldman /*
270bc70c172SBrian Feldman * 2. Remove trailing slashes if there are any. After that
271bc70c172SBrian Feldman * we look up the name in the mounttable again.
272bc70c172SBrian Feldman */
273318f2fb4SIan Dowse if (sfs == NULL) {
2747f471a32SXin LI len = strlen(mntname);
2757f471a32SXin LI while (len > 1 && mntname[len - 1] == '/')
2767f471a32SXin LI mntname[--len] = '\0';
2777f471a32SXin LI sfs = checkmntlist(mntname);
278bc70c172SBrian Feldman }
279bc70c172SBrian Feldman /*
280eddb4805SIan Dowse * 3. Check if the deprecated NFS syntax with an '@' has been used
281eddb4805SIan Dowse * and translate it to the ':' syntax. Look up the name in the
282eddb4805SIan Dowse * mount table again.
283bc70c172SBrian Feldman */
2847f471a32SXin LI if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
2857f471a32SXin LI snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
2867f471a32SXin LI (int)(delimp - mntname), mntname);
287eddb4805SIan Dowse len = strlen(buf);
2885fff0914SIan Dowse while (len > 1 && buf[len - 1] == '/')
289eddb4805SIan Dowse buf[--len] = '\0';
290eddb4805SIan Dowse sfs = checkmntlist(buf);
291bc70c172SBrian Feldman }
292bc70c172SBrian Feldman /*
293eddb4805SIan Dowse * 4. Resort to a statfs(2) call. This is the last check so that
294eddb4805SIan Dowse * hung NFS filesystems for example can be unmounted without
295eddb4805SIan Dowse * potentially blocking forever in statfs() as long as the
296eddb4805SIan Dowse * filesystem is specified unambiguously. This covers all the
297eddb4805SIan Dowse * hard cases such as symlinks and mismatches between the
298eddb4805SIan Dowse * mount list and reality.
299eddb4805SIan Dowse * We also do this if an ambiguous mount point was specified.
300bc70c172SBrian Feldman */
3017f471a32SXin LI if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
3027f471a32SXin LI getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
3037f471a32SXin LI if (statfs(mntname, &sfsbuf) != 0) {
3047f471a32SXin LI warn("%s: statfs", mntname);
3057f471a32SXin LI } else if (stat(mntname, &sb) != 0) {
3067f471a32SXin LI warn("%s: stat", mntname);
307eddb4805SIan Dowse } else if (S_ISDIR(sb.st_mode)) {
3087f471a32SXin LI /* Check that `mntname' is the root directory. */
309eddb4805SIan Dowse dev = sb.st_dev;
3107f471a32SXin LI snprintf(buf, sizeof(buf), "%s/..", mntname);
311eddb4805SIan Dowse if (stat(buf, &sb) != 0) {
312eddb4805SIan Dowse warn("%s: stat", buf);
313eddb4805SIan Dowse } else if (sb.st_dev == dev) {
314eddb4805SIan Dowse warnx("%s: not a file system root directory",
3157f471a32SXin LI mntname);
316eddb4805SIan Dowse return (1);
3178360efbdSAlfred Perlstein } else
318eddb4805SIan Dowse sfs = &sfsbuf;
319eddb4805SIan Dowse }
320eddb4805SIan Dowse }
321eddb4805SIan Dowse if (sfs == NULL) {
3227f471a32SXin LI warnx("%s: unknown file system", mntname);
3238fae3551SRodney W. Grimes return (1);
3248fae3551SRodney W. Grimes }
3251add162cSIan Dowse if (checkvfsname(sfs->f_fstypename, typelist))
326d499a0efSBruce Evans return (1);
327eddb4805SIan Dowse return (umountfs(sfs));
3288360efbdSAlfred Perlstein }
3298360efbdSAlfred Perlstein
3308360efbdSAlfred Perlstein /*
3318360efbdSAlfred Perlstein * NFS stuff and unmount(2) call
3328360efbdSAlfred Perlstein */
3338360efbdSAlfred Perlstein int
umountfs(struct statfs * sfs)334eddb4805SIan Dowse umountfs(struct statfs *sfs)
3358360efbdSAlfred Perlstein {
336318f2fb4SIan Dowse char fsidbuf[64];
3378360efbdSAlfred Perlstein enum clnt_stat clnt_stat;
3388360efbdSAlfred Perlstein struct timeval try;
3398360efbdSAlfred Perlstein struct addrinfo *ai, hints;
3408360efbdSAlfred Perlstein int do_rpc;
3418360efbdSAlfred Perlstein CLIENT *clp;
3428360efbdSAlfred Perlstein char *nfsdirname, *orignfsdirname;
3438360efbdSAlfred Perlstein char *hostp, *delimp;
34449a8bad7SRick Macklem char buf[1024];
34549a8bad7SRick Macklem struct nfscl_dumpmntopts dumpmntopts;
34649a8bad7SRick Macklem const char *proto_ptr = NULL;
3478360efbdSAlfred Perlstein
3488360efbdSAlfred Perlstein ai = NULL;
34913e2e1afSIan Dowse do_rpc = 0;
35013e2e1afSIan Dowse hostp = NULL;
3518360efbdSAlfred Perlstein nfsdirname = delimp = orignfsdirname = NULL;
3528360efbdSAlfred Perlstein memset(&hints, 0, sizeof hints);
3538360efbdSAlfred Perlstein
354eddb4805SIan Dowse if (strcmp(sfs->f_fstypename, "nfs") == 0) {
355eddb4805SIan Dowse if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
3568360efbdSAlfred Perlstein err(1, "strdup");
3578360efbdSAlfred Perlstein orignfsdirname = nfsdirname;
358a505d435SHajimu UMEMOTO if (*nfsdirname == '[' &&
359a505d435SHajimu UMEMOTO (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
360a505d435SHajimu UMEMOTO *(delimp + 1) == ':') {
361a505d435SHajimu UMEMOTO hostp = nfsdirname + 1;
362a505d435SHajimu UMEMOTO nfsdirname = delimp + 2;
363a505d435SHajimu UMEMOTO } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
3648360efbdSAlfred Perlstein hostp = nfsdirname;
365a505d435SHajimu UMEMOTO nfsdirname = delimp + 1;
366a505d435SHajimu UMEMOTO }
367a505d435SHajimu UMEMOTO if (hostp != NULL) {
368a505d435SHajimu UMEMOTO *delimp = '\0';
3698360efbdSAlfred Perlstein getaddrinfo(hostp, NULL, &hints, &ai);
3708360efbdSAlfred Perlstein if (ai == NULL) {
3718360efbdSAlfred Perlstein warnx("can't get net id for host");
3728360efbdSAlfred Perlstein }
3738360efbdSAlfred Perlstein }
37413e2e1afSIan Dowse
375bc70c172SBrian Feldman /*
376bc70c172SBrian Feldman * Check if we have to start the rpc-call later.
377bc70c172SBrian Feldman * If there are still identical nfs-names mounted,
378bc70c172SBrian Feldman * we skip the rpc-call. Obviously this has to
379bc70c172SBrian Feldman * happen before unmount(2), but it should happen
380bc70c172SBrian Feldman * after the previous namecheck.
38113e2e1afSIan Dowse * A non-NULL return means that this is the last
38213e2e1afSIan Dowse * mount from mntfromname that is still mounted.
383bc70c172SBrian Feldman */
384eddb4805SIan Dowse if (getmntentry(sfs->f_mntfromname, NULL, NULL,
38549a8bad7SRick Macklem CHECKUNIQUE) != NULL) {
3860fe9a7daSBrian Feldman do_rpc = 1;
38749a8bad7SRick Macklem proto_ptr = "udp";
38849a8bad7SRick Macklem /*
38949a8bad7SRick Macklem * Try and find out whether this NFS mount is NFSv4 and
39049a8bad7SRick Macklem * what protocol is being used. If this fails, the
39149a8bad7SRick Macklem * default is NFSv2,3 and use UDP for the Unmount RPC.
39249a8bad7SRick Macklem */
39349a8bad7SRick Macklem dumpmntopts.ndmnt_fname = sfs->f_mntonname;
39449a8bad7SRick Macklem dumpmntopts.ndmnt_buf = buf;
39549a8bad7SRick Macklem dumpmntopts.ndmnt_blen = sizeof(buf);
39649a8bad7SRick Macklem if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) {
39749a8bad7SRick Macklem if (strstr(buf, "nfsv4,") != NULL)
39849a8bad7SRick Macklem do_rpc = 0;
39949a8bad7SRick Macklem else if (strstr(buf, ",tcp,") != NULL)
40049a8bad7SRick Macklem proto_ptr = "tcp";
40149a8bad7SRick Macklem }
40249a8bad7SRick Macklem }
40313e2e1afSIan Dowse }
4048360efbdSAlfred Perlstein
405c27063b9SEitan Adler if (!namematch(ai)) {
406c27063b9SEitan Adler free(orignfsdirname);
407d499a0efSBruce Evans return (1);
408c27063b9SEitan Adler }
409eddb4805SIan Dowse /* First try to unmount using the file system ID. */
410eddb4805SIan Dowse snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
411eddb4805SIan Dowse sfs->f_fsid.val[1]);
412318f2fb4SIan Dowse if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
4130ed25a9aSIan Dowse /* XXX, non-root users get a zero fsid, so don't warn. */
4140ed25a9aSIan Dowse if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
4150ed25a9aSIan Dowse sfs->f_fsid.val[1] != 0)
416eddb4805SIan Dowse warn("unmount of %s failed", sfs->f_mntonname);
417c27063b9SEitan Adler if (errno != ENOENT) {
418c27063b9SEitan Adler free(orignfsdirname);
419318f2fb4SIan Dowse return (1);
420c27063b9SEitan Adler }
4218b918187SIan Dowse /* Compatibility for old kernels. */
4228b918187SIan Dowse if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
423318f2fb4SIan Dowse warnx("retrying using path instead of file system ID");
424eddb4805SIan Dowse if (unmount(sfs->f_mntonname, fflag) != 0) {
425eddb4805SIan Dowse warn("unmount of %s failed", sfs->f_mntonname);
426c27063b9SEitan Adler free(orignfsdirname);
4278fae3551SRodney W. Grimes return (1);
4288fae3551SRodney W. Grimes }
429eddb4805SIan Dowse }
430efb45ad0SGordon Bergling /* Mark this file system as unmounted. */
431eddb4805SIan Dowse getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
432bc70c172SBrian Feldman if (vflag)
433eddb4805SIan Dowse (void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
434eddb4805SIan Dowse sfs->f_mntonname);
435bc70c172SBrian Feldman /*
436bc70c172SBrian Feldman * Report to mountd-server which nfsname
437bc70c172SBrian Feldman * has been unmounted.
438bc70c172SBrian Feldman */
4398360efbdSAlfred Perlstein if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
44049a8bad7SRick Macklem clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr);
4418360efbdSAlfred Perlstein if (clp == NULL) {
44213e2e1afSIan Dowse warnx("%s: %s", hostp,
4430775314bSDoug Rabson clnt_spcreateerror("MOUNTPROG"));
444c27063b9SEitan Adler free(orignfsdirname);
4458fae3551SRodney W. Grimes return (1);
4468fae3551SRodney W. Grimes }
4478360efbdSAlfred Perlstein clp->cl_auth = authsys_create_default();
4488fae3551SRodney W. Grimes try.tv_sec = 20;
4498fae3551SRodney W. Grimes try.tv_usec = 0;
4500775314bSDoug Rabson clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
4515c514aeeSMatthew N. Dodd nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
4528fae3551SRodney W. Grimes if (clnt_stat != RPC_SUCCESS) {
45313e2e1afSIan Dowse warnx("%s: %s", hostp,
45413e2e1afSIan Dowse clnt_sperror(clp, "RPCMNT_UMOUNT"));
455c27063b9SEitan Adler free(orignfsdirname);
4568fae3551SRodney W. Grimes return (1);
4578fae3551SRodney W. Grimes }
458a69497d7SMatthew Dillon /*
459a69497d7SMatthew Dillon * Remove the unmounted entry from /var/db/mounttab.
460a69497d7SMatthew Dillon */
461afe1ef24SIan Dowse if (read_mtab()) {
462afe1ef24SIan Dowse clean_mtab(hostp, nfsdirname, vflag);
463afe1ef24SIan Dowse if(!write_mtab(vflag))
46413e2e1afSIan Dowse warnx("cannot remove mounttab entry %s:%s",
465a69497d7SMatthew Dillon hostp, nfsdirname);
466a69497d7SMatthew Dillon free_mtab();
467a69497d7SMatthew Dillon }
4688fae3551SRodney W. Grimes auth_destroy(clp->cl_auth);
4698fae3551SRodney W. Grimes clnt_destroy(clp);
4708fae3551SRodney W. Grimes }
471c27063b9SEitan Adler free(orignfsdirname);
472840a8027SRicardo Branco
473840a8027SRicardo Branco if (dflag) {
474840a8027SRicardo Branco if (md_detach(sfs->f_mntfromname) == 0) {
475840a8027SRicardo Branco if (vflag)
476840a8027SRicardo Branco (void)printf("%s: detached\n",
477840a8027SRicardo Branco sfs->f_mntfromname);
478840a8027SRicardo Branco } else if (!all)
479840a8027SRicardo Branco return (-1);
480840a8027SRicardo Branco }
481840a8027SRicardo Branco
4828fae3551SRodney W. Grimes return (0);
4838fae3551SRodney W. Grimes }
4848fae3551SRodney W. Grimes
485318f2fb4SIan Dowse struct statfs *
getmntentry(const char * fromname,const char * onname,fsid_t * fsid,dowhat what)486eddb4805SIan Dowse getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
4878fae3551SRodney W. Grimes {
488d499a0efSBruce Evans static struct statfs *mntbuf;
489bc70c172SBrian Feldman static size_t mntsize = 0;
490e96092e8SUlrich Spörlein static int *mntcheck = NULL;
491eddb4805SIan Dowse struct statfs *sfs, *foundsfs;
492bc70c172SBrian Feldman int i, count;
4938fae3551SRodney W. Grimes
494bc70c172SBrian Feldman if (mntsize <= 0) {
495bc70c172SBrian Feldman if ((mntsize = mntinfo(&mntbuf)) <= 0)
4968fae3551SRodney W. Grimes return (NULL);
4978fae3551SRodney W. Grimes }
498bc70c172SBrian Feldman if (mntcheck == NULL) {
499eddb4805SIan Dowse if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
500bc70c172SBrian Feldman err(1, "calloc");
501bc70c172SBrian Feldman }
502bc70c172SBrian Feldman /*
503bc70c172SBrian Feldman * We want to get the file systems in the reverse order
504eddb4805SIan Dowse * that they were mounted. Unmounted file systems are marked
505eddb4805SIan Dowse * in a table called 'mntcheck'.
506bc70c172SBrian Feldman */
507eddb4805SIan Dowse count = 0;
508eddb4805SIan Dowse foundsfs = NULL;
509bc70c172SBrian Feldman for (i = mntsize - 1; i >= 0; i--) {
510eddb4805SIan Dowse if (mntcheck[i])
511eddb4805SIan Dowse continue;
512eddb4805SIan Dowse sfs = &mntbuf[i];
513eddb4805SIan Dowse if (fromname != NULL && strcmp(sfs->f_mntfromname,
514eddb4805SIan Dowse fromname) != 0)
515eddb4805SIan Dowse continue;
516eddb4805SIan Dowse if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
517eddb4805SIan Dowse continue;
518245bfd34SRyan Moeller if (fsid != NULL && fsidcmp(&sfs->f_fsid, fsid) != 0)
519eddb4805SIan Dowse continue;
520eddb4805SIan Dowse
52138f102c2SIan Dowse switch (what) {
522eddb4805SIan Dowse case CHECKUNIQUE:
523eddb4805SIan Dowse foundsfs = sfs;
524eddb4805SIan Dowse count++;
52538f102c2SIan Dowse continue;
526eddb4805SIan Dowse case REMOVE:
527eddb4805SIan Dowse mntcheck[i] = 1;
52838f102c2SIan Dowse break;
529eddb4805SIan Dowse default:
53038f102c2SIan Dowse break;
53138f102c2SIan Dowse }
532eddb4805SIan Dowse return (sfs);
5338fae3551SRodney W. Grimes }
534318f2fb4SIan Dowse
535eddb4805SIan Dowse if (what == CHECKUNIQUE && count == 1)
536eddb4805SIan Dowse return (foundsfs);
5378fae3551SRodney W. Grimes return (NULL);
5388fae3551SRodney W. Grimes }
5398fae3551SRodney W. Grimes
5408fae3551SRodney W. Grimes int
sacmp(void * sa1,void * sa2)5417f471a32SXin LI sacmp(void *sa1, void *sa2)
5428fae3551SRodney W. Grimes {
5438360efbdSAlfred Perlstein void *p1, *p2;
5448360efbdSAlfred Perlstein int len;
5458fae3551SRodney W. Grimes
5467f471a32SXin LI if (((struct sockaddr *)sa1)->sa_family !=
5477f471a32SXin LI ((struct sockaddr *)sa2)->sa_family)
5488fae3551SRodney W. Grimes return (1);
5498fae3551SRodney W. Grimes
5507f471a32SXin LI switch (((struct sockaddr *)sa1)->sa_family) {
5518360efbdSAlfred Perlstein case AF_INET:
5528360efbdSAlfred Perlstein p1 = &((struct sockaddr_in *)sa1)->sin_addr;
5538360efbdSAlfred Perlstein p2 = &((struct sockaddr_in *)sa2)->sin_addr;
5548360efbdSAlfred Perlstein len = 4;
5558360efbdSAlfred Perlstein break;
5568360efbdSAlfred Perlstein case AF_INET6:
5578360efbdSAlfred Perlstein p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
5588360efbdSAlfred Perlstein p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
5598360efbdSAlfred Perlstein len = 16;
5608360efbdSAlfred Perlstein if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
5618360efbdSAlfred Perlstein ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
5628360efbdSAlfred Perlstein return (1);
5638360efbdSAlfred Perlstein break;
5648360efbdSAlfred Perlstein default:
5658360efbdSAlfred Perlstein return (1);
5668360efbdSAlfred Perlstein }
5678360efbdSAlfred Perlstein
5688360efbdSAlfred Perlstein return memcmp(p1, p2, len);
5698360efbdSAlfred Perlstein }
5708360efbdSAlfred Perlstein
5718360efbdSAlfred Perlstein int
namematch(struct addrinfo * ai)5728360efbdSAlfred Perlstein namematch(struct addrinfo *ai)
5738360efbdSAlfred Perlstein {
5748360efbdSAlfred Perlstein struct addrinfo *aip;
5758360efbdSAlfred Perlstein
5768360efbdSAlfred Perlstein if (nfshost == NULL || nfshost_ai == NULL)
5778fae3551SRodney W. Grimes return (1);
5788fae3551SRodney W. Grimes
5798360efbdSAlfred Perlstein while (ai != NULL) {
5808360efbdSAlfred Perlstein aip = nfshost_ai;
5818360efbdSAlfred Perlstein while (aip != NULL) {
5828360efbdSAlfred Perlstein if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
5838fae3551SRodney W. Grimes return (1);
5848360efbdSAlfred Perlstein aip = aip->ai_next;
5858fae3551SRodney W. Grimes }
5868360efbdSAlfred Perlstein ai = ai->ai_next;
5878fae3551SRodney W. Grimes }
5888360efbdSAlfred Perlstein
5898fae3551SRodney W. Grimes return (0);
5908fae3551SRodney W. Grimes }
5918fae3551SRodney W. Grimes
592318f2fb4SIan Dowse struct statfs *
checkmntlist(char * mntname)5937f471a32SXin LI checkmntlist(char *mntname)
594bc70c172SBrian Feldman {
595318f2fb4SIan Dowse struct statfs *sfs;
596eddb4805SIan Dowse fsid_t fsid;
597bc70c172SBrian Feldman
598eddb4805SIan Dowse sfs = NULL;
5997f471a32SXin LI if (parsehexfsid(mntname, &fsid) == 0)
600eddb4805SIan Dowse sfs = getmntentry(NULL, NULL, &fsid, FIND);
60138f102c2SIan Dowse if (sfs == NULL)
6027f471a32SXin LI sfs = getmntentry(NULL, mntname, NULL, FIND);
603318f2fb4SIan Dowse if (sfs == NULL)
6047f471a32SXin LI sfs = getmntentry(mntname, NULL, NULL, FIND);
605318f2fb4SIan Dowse return (sfs);
606bc70c172SBrian Feldman }
607bc70c172SBrian Feldman
608bc70c172SBrian Feldman size_t
mntinfo(struct statfs ** mntbuf)609bc70c172SBrian Feldman mntinfo(struct statfs **mntbuf)
610bc70c172SBrian Feldman {
611bc70c172SBrian Feldman static struct statfs *origbuf;
612bc70c172SBrian Feldman size_t bufsize;
613bc70c172SBrian Feldman int mntsize;
614bc70c172SBrian Feldman
615a69497d7SMatthew Dillon mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
616bc70c172SBrian Feldman if (mntsize <= 0)
617bc70c172SBrian Feldman return (0);
618bc70c172SBrian Feldman bufsize = (mntsize + 1) * sizeof(struct statfs);
619bc70c172SBrian Feldman if ((origbuf = malloc(bufsize)) == NULL)
620bc70c172SBrian Feldman err(1, "malloc");
621bc70c172SBrian Feldman mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
622bc70c172SBrian Feldman *mntbuf = origbuf;
623bc70c172SBrian Feldman return (mntsize);
624bc70c172SBrian Feldman }
625bc70c172SBrian Feldman
626eddb4805SIan Dowse /*
6278b918187SIan Dowse * Convert a hexadecimal filesystem ID to an fsid_t.
628eddb4805SIan Dowse * Returns 0 on success.
629eddb4805SIan Dowse */
630eddb4805SIan Dowse int
parsehexfsid(const char * hex,fsid_t * fsid)631eddb4805SIan Dowse parsehexfsid(const char *hex, fsid_t *fsid)
632bc70c172SBrian Feldman {
633eddb4805SIan Dowse char hexbuf[3];
634eddb4805SIan Dowse int i;
635bc70c172SBrian Feldman
636eddb4805SIan Dowse if (strlen(hex) != sizeof(*fsid) * 2)
637eddb4805SIan Dowse return (-1);
638eddb4805SIan Dowse hexbuf[2] = '\0';
6395fff0914SIan Dowse for (i = 0; i < (int)sizeof(*fsid); i++) {
640eddb4805SIan Dowse hexbuf[0] = hex[i * 2];
641eddb4805SIan Dowse hexbuf[1] = hex[i * 2 + 1];
642eddb4805SIan Dowse if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
643eddb4805SIan Dowse return (-1);
644eddb4805SIan Dowse ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
645bc70c172SBrian Feldman }
646eddb4805SIan Dowse return (0);
647bc70c172SBrian Feldman }
648bc70c172SBrian Feldman
6498fae3551SRodney W. Grimes /*
6508fae3551SRodney W. Grimes * xdr routines for mount rpc's
6518fae3551SRodney W. Grimes */
6528fae3551SRodney W. Grimes int
xdr_dir(XDR * xdrsp,char * dirp)653bc70c172SBrian Feldman xdr_dir(XDR *xdrsp, char *dirp)
6548fae3551SRodney W. Grimes {
655bc70c172SBrian Feldman
6560775314bSDoug Rabson return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
6578fae3551SRodney W. Grimes }
6588fae3551SRodney W. Grimes
6598fae3551SRodney W. Grimes void
usage(void)660ca25fa25SEd Schouten usage(void)
6618fae3551SRodney W. Grimes {
662bc70c172SBrian Feldman
663210a5dc8SPhilippe Charnier (void)fprintf(stderr, "%s\n%s\n",
664840a8027SRicardo Branco "usage: umount [-dfNnv] special ... | node ... | fsid ...",
665840a8027SRicardo Branco " umount -a | -A [-F fstab] [-dfnv] [-h host] [-t type]");
6668fae3551SRodney W. Grimes exit(1);
6678fae3551SRodney W. Grimes }
668840a8027SRicardo Branco
669840a8027SRicardo Branco int
md_detach(const char * device)670840a8027SRicardo Branco md_detach(const char *device)
671840a8027SRicardo Branco {
672840a8027SRicardo Branco struct md_ioctl mdio;
673840a8027SRicardo Branco char *eptr;
674840a8027SRicardo Branco int fd;
675840a8027SRicardo Branco
676840a8027SRicardo Branco if (strncmp(device, DEV_MD, sizeof(DEV_MD) - 1)) {
677840a8027SRicardo Branco if (!all)
678840a8027SRicardo Branco warnx("invalid md device: %s", device);
679840a8027SRicardo Branco return (-1);
680840a8027SRicardo Branco }
681840a8027SRicardo Branco
682e96d0d74SRicardo Branco memset(&mdio, 0, sizeof(mdio));
683e96d0d74SRicardo Branco mdio.md_version = MDIOVERSION;
684e96d0d74SRicardo Branco mdio.md_options = fflag ? MD_FORCE : 0;
685840a8027SRicardo Branco mdio.md_unit = strtoul(device + sizeof(DEV_MD) - 1, &eptr, 0);
686e96d0d74SRicardo Branco if (mdio.md_unit == (unsigned)ULONG_MAX || eptr - device == sizeof(DEV_MD) - 1) {
687840a8027SRicardo Branco warnx("invalid md device: %s", device);
688840a8027SRicardo Branco return (-1);
689840a8027SRicardo Branco }
690840a8027SRicardo Branco
691840a8027SRicardo Branco fd = open(DEV_MDCTL, O_RDWR, 0);
692840a8027SRicardo Branco if (fd < 0) {
693840a8027SRicardo Branco warn("%s", DEV_MDCTL);
694840a8027SRicardo Branco return (-1);
695840a8027SRicardo Branco }
696840a8027SRicardo Branco
697840a8027SRicardo Branco if (ioctl(fd, MDIOCDETACH, &mdio) < 0) {
698840a8027SRicardo Branco warn("%s", DEV_MD);
699840a8027SRicardo Branco close(fd);
700840a8027SRicardo Branco return (-1);
701840a8027SRicardo Branco }
702840a8027SRicardo Branco
703840a8027SRicardo Branco close(fd);
704840a8027SRicardo Branco return (0);
705840a8027SRicardo Branco }
706