]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/umount/umount.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r306956, and update
[FreeBSD/FreeBSD.git] / sbin / umount / umount.c
1 /*-
2  * Copyright (c) 1980, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #ifndef lint
31 static const char copyright[] =
32 "@(#) Copyright (c) 1980, 1989, 1993\n\
33         The Regents of the University of California.  All rights reserved.\n";
34 #endif /* not lint */
35
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)umount.c    8.8 (Berkeley) 5/8/95";
39 #endif
40 static const char rcsid[] =
41   "$FreeBSD$";
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/mount.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48
49 #include <netdb.h>
50 #include <rpc/rpc.h>
51 #include <rpcsvc/mount.h>
52 #include <nfs/nfssvc.h>
53
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <fstab.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62
63 #include "mounttab.h"
64
65 typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
66
67 static struct addrinfo *nfshost_ai = NULL;
68 static int      fflag, vflag;
69 static char     *nfshost;
70
71 struct statfs *checkmntlist(char *);
72 int      checkvfsname (const char *, char **);
73 struct statfs *getmntentry(const char *fromname, const char *onname,
74              fsid_t *fsid, dowhat what);
75 char   **makevfslist (const char *);
76 size_t   mntinfo (struct statfs **);
77 int      namematch (struct addrinfo *);
78 int      parsehexfsid(const char *hex, fsid_t *fsid);
79 int      sacmp (void *, void *);
80 int      umountall (char **);
81 int      checkname (char *, char **);
82 int      umountfs(struct statfs *sfs);
83 void     usage (void);
84 int      xdr_dir (XDR *, char *);
85
86 int
87 main(int argc, char *argv[])
88 {
89         int all, errs, ch, mntsize, error;
90         char **typelist = NULL;
91         struct statfs *mntbuf, *sfs;
92         struct addrinfo hints;
93
94         all = errs = 0;
95         while ((ch = getopt(argc, argv, "AaF:fh:nt:v")) != -1)
96                 switch (ch) {
97                 case 'A':
98                         all = 2;
99                         break;
100                 case 'a':
101                         all = 1;
102                         break;
103                 case 'F':
104                         setfstab(optarg);
105                         break;
106                 case 'f':
107                         fflag |= MNT_FORCE;
108                         break;
109                 case 'h':       /* -h implies -A. */
110                         all = 2;
111                         nfshost = optarg;
112                         break;
113                 case 'n':
114                         fflag |= MNT_NONBUSY;
115                         break;
116                 case 't':
117                         if (typelist != NULL)
118                                 err(1, "only one -t option may be specified");
119                         typelist = makevfslist(optarg);
120                         break;
121                 case 'v':
122                         vflag = 1;
123                         break;
124                 default:
125                         usage();
126                         /* NOTREACHED */
127                 }
128         argc -= optind;
129         argv += optind;
130
131         if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0)
132                 err(1, "-f and -n are mutually exclusive");
133
134         /* Start disks transferring immediately. */
135         if ((fflag & (MNT_FORCE | MNT_NONBUSY)) == 0)
136                 sync();
137
138         if ((argc == 0 && !all) || (argc != 0 && all))
139                 usage();
140
141         /* -h implies "-t nfs" if no -t flag. */
142         if ((nfshost != NULL) && (typelist == NULL))
143                 typelist = makevfslist("nfs");
144
145         if (nfshost != NULL) {
146                 memset(&hints, 0, sizeof hints);
147                 error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
148                 if (error)
149                         errx(1, "%s: %s", nfshost, gai_strerror(error));
150         }
151
152         switch (all) {
153         case 2:
154                 if ((mntsize = mntinfo(&mntbuf)) <= 0)
155                         break;
156                 /*
157                  * We unmount the nfs-mounts in the reverse order
158                  * that they were mounted.
159                  */
160                 for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
161                         sfs = &mntbuf[mntsize];
162                         if (checkvfsname(sfs->f_fstypename, typelist))
163                                 continue;
164                         if (strcmp(sfs->f_mntonname, "/dev") == 0)
165                                 continue;
166                         if (umountfs(sfs) != 0)
167                                 errs = 1;
168                 }
169                 free(mntbuf);
170                 break;
171         case 1:
172                 if (setfsent() == 0)
173                         err(1, "%s", getfstab());
174                 errs = umountall(typelist);
175                 break;
176         case 0:
177                 for (errs = 0; *argv != NULL; ++argv)
178                         if (checkname(*argv, typelist) != 0)
179                                 errs = 1;
180                 break;
181         }
182         exit(errs);
183 }
184
185 int
186 umountall(char **typelist)
187 {
188         struct xvfsconf vfc;
189         struct fstab *fs;
190         int rval;
191         char *cp;
192         static int firstcall = 1;
193
194         if ((fs = getfsent()) != NULL)
195                 firstcall = 0;
196         else if (firstcall)
197                 errx(1, "fstab reading failure");
198         else
199                 return (0);
200         do {
201                 /* Ignore the root. */
202                 if (strcmp(fs->fs_file, "/") == 0)
203                         continue;
204                 /*
205                  * !!!
206                  * Historic practice: ignore unknown FSTAB_* fields.
207                  */
208                 if (strcmp(fs->fs_type, FSTAB_RW) &&
209                     strcmp(fs->fs_type, FSTAB_RO) &&
210                     strcmp(fs->fs_type, FSTAB_RQ))
211                         continue;
212                 /* Ignore unknown file system types. */
213                 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
214                         continue;
215                 if (checkvfsname(fs->fs_vfstype, typelist))
216                         continue;
217
218                 /*
219                  * We want to unmount the file systems in the reverse order
220                  * that they were mounted.  So, we save off the file name
221                  * in some allocated memory, and then call recursively.
222                  */
223                 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
224                         err(1, "malloc failed");
225                 (void)strcpy(cp, fs->fs_file);
226                 rval = umountall(typelist);
227                 rval = checkname(cp, typelist) || rval;
228                 free(cp);
229                 return (rval);
230         } while ((fs = getfsent()) != NULL);
231         return (0);
232 }
233
234 /*
235  * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
236  */
237 int
238 checkname(char *mntname, char **typelist)
239 {
240         char buf[MAXPATHLEN];
241         struct statfs sfsbuf;
242         struct stat sb;
243         struct statfs *sfs;
244         char *delimp;
245         dev_t dev;
246         int len;
247
248         /*
249          * 1. Check if the name exists in the mounttable.
250          */
251         sfs = checkmntlist(mntname);
252         /*
253          * 2. Remove trailing slashes if there are any. After that
254          * we look up the name in the mounttable again.
255          */
256         if (sfs == NULL) {
257                 len = strlen(mntname);
258                 while (len > 1 && mntname[len - 1] == '/')
259                         mntname[--len] = '\0';
260                 sfs = checkmntlist(mntname);
261         }
262         /*
263          * 3. Check if the deprecated NFS syntax with an '@' has been used
264          * and translate it to the ':' syntax. Look up the name in the
265          * mount table again.
266          */
267         if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
268                 snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
269                     (int)(delimp - mntname), mntname);
270                 len = strlen(buf);
271                 while (len > 1 && buf[len - 1] == '/')
272                         buf[--len] = '\0';
273                 sfs = checkmntlist(buf);
274         }
275         /*
276          * 4. Resort to a statfs(2) call. This is the last check so that
277          * hung NFS filesystems for example can be unmounted without
278          * potentially blocking forever in statfs() as long as the
279          * filesystem is specified unambiguously. This covers all the
280          * hard cases such as symlinks and mismatches between the
281          * mount list and reality.
282          * We also do this if an ambiguous mount point was specified.
283          */
284         if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
285             getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
286                 if (statfs(mntname, &sfsbuf) != 0) {
287                         warn("%s: statfs", mntname);
288                 } else if (stat(mntname, &sb) != 0) {
289                         warn("%s: stat", mntname);
290                 } else if (S_ISDIR(sb.st_mode)) {
291                         /* Check that `mntname' is the root directory. */
292                         dev = sb.st_dev;
293                         snprintf(buf, sizeof(buf), "%s/..", mntname);
294                         if (stat(buf, &sb) != 0) {
295                                 warn("%s: stat", buf);
296                         } else if (sb.st_dev == dev) {
297                                 warnx("%s: not a file system root directory",
298                                     mntname);
299                                 return (1);
300                         } else
301                                 sfs = &sfsbuf;
302                 }
303         }
304         if (sfs == NULL) {
305                 warnx("%s: unknown file system", mntname);
306                 return (1);
307         }
308         if (checkvfsname(sfs->f_fstypename, typelist))
309                 return (1);
310         return (umountfs(sfs));
311 }
312
313 /*
314  * NFS stuff and unmount(2) call
315  */
316 int
317 umountfs(struct statfs *sfs)
318 {
319         char fsidbuf[64];
320         enum clnt_stat clnt_stat;
321         struct timeval try;
322         struct addrinfo *ai, hints;
323         int do_rpc;
324         CLIENT *clp;
325         char *nfsdirname, *orignfsdirname;
326         char *hostp, *delimp;
327         char buf[1024];
328         struct nfscl_dumpmntopts dumpmntopts;
329         const char *proto_ptr = NULL;
330
331         ai = NULL;
332         do_rpc = 0;
333         hostp = NULL;
334         nfsdirname = delimp = orignfsdirname = NULL;
335         memset(&hints, 0, sizeof hints);
336
337         if (strcmp(sfs->f_fstypename, "nfs") == 0) {
338                 if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
339                         err(1, "strdup");
340                 orignfsdirname = nfsdirname;
341                 if (*nfsdirname == '[' &&
342                     (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
343                     *(delimp + 1) == ':') {
344                         hostp = nfsdirname + 1;
345                         nfsdirname = delimp + 2;
346                 } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
347                         hostp = nfsdirname;
348                         nfsdirname = delimp + 1;
349                 }
350                 if (hostp != NULL) {
351                         *delimp = '\0';
352                         getaddrinfo(hostp, NULL, &hints, &ai);
353                         if (ai == NULL) {
354                                 warnx("can't get net id for host");
355                         }
356                 }
357
358                 /*
359                  * Check if we have to start the rpc-call later.
360                  * If there are still identical nfs-names mounted,
361                  * we skip the rpc-call. Obviously this has to
362                  * happen before unmount(2), but it should happen
363                  * after the previous namecheck.
364                  * A non-NULL return means that this is the last
365                  * mount from mntfromname that is still mounted.
366                  */
367                 if (getmntentry(sfs->f_mntfromname, NULL, NULL,
368                     CHECKUNIQUE) != NULL) {
369                         do_rpc = 1;
370                         proto_ptr = "udp";
371                         /*
372                          * Try and find out whether this NFS mount is NFSv4 and
373                          * what protocol is being used. If this fails, the
374                          * default is NFSv2,3 and use UDP for the Unmount RPC.
375                          */
376                         dumpmntopts.ndmnt_fname = sfs->f_mntonname;
377                         dumpmntopts.ndmnt_buf = buf;
378                         dumpmntopts.ndmnt_blen = sizeof(buf);
379                         if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) {
380                                 if (strstr(buf, "nfsv4,") != NULL)
381                                         do_rpc = 0;
382                                 else if (strstr(buf, ",tcp,") != NULL)
383                                         proto_ptr = "tcp";
384                         }
385                 }
386         }
387
388         if (!namematch(ai)) {
389                 free(orignfsdirname);
390                 return (1);
391         }
392         /* First try to unmount using the file system ID. */
393         snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
394             sfs->f_fsid.val[1]);
395         if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
396                 /* XXX, non-root users get a zero fsid, so don't warn. */
397                 if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
398                     sfs->f_fsid.val[1] != 0)
399                         warn("unmount of %s failed", sfs->f_mntonname);
400                 if (errno != ENOENT) {
401                         free(orignfsdirname);
402                         return (1);
403                 }
404                 /* Compatibility for old kernels. */
405                 if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
406                         warnx("retrying using path instead of file system ID");
407                 if (unmount(sfs->f_mntonname, fflag) != 0) {
408                         warn("unmount of %s failed", sfs->f_mntonname);
409                         free(orignfsdirname);
410                         return (1);
411                 }
412         }
413         /* Mark this this file system as unmounted. */
414         getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
415         if (vflag)
416                 (void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
417                     sfs->f_mntonname);
418         /*
419          * Report to mountd-server which nfsname
420          * has been unmounted.
421          */
422         if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
423                 clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr);
424                 if (clp  == NULL) {
425                         warnx("%s: %s", hostp,
426                             clnt_spcreateerror("MOUNTPROG"));
427                         free(orignfsdirname);
428                         return (1);
429                 }
430                 clp->cl_auth = authsys_create_default();
431                 try.tv_sec = 20;
432                 try.tv_usec = 0;
433                 clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
434                     nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
435                 if (clnt_stat != RPC_SUCCESS) {
436                         warnx("%s: %s", hostp,
437                             clnt_sperror(clp, "RPCMNT_UMOUNT"));
438                         free(orignfsdirname);
439                         return (1);
440                 }
441                 /*
442                  * Remove the unmounted entry from /var/db/mounttab.
443                  */
444                 if (read_mtab()) {
445                         clean_mtab(hostp, nfsdirname, vflag);
446                         if(!write_mtab(vflag))
447                                 warnx("cannot remove mounttab entry %s:%s",
448                                     hostp, nfsdirname);
449                         free_mtab();
450                 }
451                 auth_destroy(clp->cl_auth);
452                 clnt_destroy(clp);
453         }
454         free(orignfsdirname);
455         return (0);
456 }
457
458 struct statfs *
459 getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
460 {
461         static struct statfs *mntbuf;
462         static size_t mntsize = 0;
463         static int *mntcheck = NULL;
464         struct statfs *sfs, *foundsfs;
465         int i, count;
466
467         if (mntsize <= 0) {
468                 if ((mntsize = mntinfo(&mntbuf)) <= 0)
469                         return (NULL);
470         }
471         if (mntcheck == NULL) {
472                 if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
473                         err(1, "calloc");
474         }
475         /*
476          * We want to get the file systems in the reverse order
477          * that they were mounted. Unmounted file systems are marked
478          * in a table called 'mntcheck'.
479          */
480         count = 0;
481         foundsfs = NULL;
482         for (i = mntsize - 1; i >= 0; i--) {
483                 if (mntcheck[i])
484                         continue;
485                 sfs = &mntbuf[i];
486                 if (fromname != NULL && strcmp(sfs->f_mntfromname,
487                     fromname) != 0)
488                         continue;
489                 if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
490                         continue;
491                 if (fsid != NULL && bcmp(&sfs->f_fsid, fsid,
492                     sizeof(*fsid)) != 0)
493                         continue;
494
495                 switch (what) {
496                 case CHECKUNIQUE:
497                         foundsfs = sfs;
498                         count++;
499                         continue;
500                 case REMOVE:
501                         mntcheck[i] = 1;
502                         break;
503                 default:
504                         break;
505                 }
506                 return (sfs);
507         }
508
509         if (what == CHECKUNIQUE && count == 1)
510                 return (foundsfs);
511         return (NULL);
512 }
513
514 int
515 sacmp(void *sa1, void *sa2)
516 {
517         void *p1, *p2;
518         int len;
519
520         if (((struct sockaddr *)sa1)->sa_family !=
521             ((struct sockaddr *)sa2)->sa_family)
522                 return (1);
523
524         switch (((struct sockaddr *)sa1)->sa_family) {
525         case AF_INET:
526                 p1 = &((struct sockaddr_in *)sa1)->sin_addr;
527                 p2 = &((struct sockaddr_in *)sa2)->sin_addr;
528                 len = 4;
529                 break;
530         case AF_INET6:
531                 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
532                 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
533                 len = 16;
534                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
535                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
536                         return (1);
537                 break;
538         default:
539                 return (1);
540         }
541
542         return memcmp(p1, p2, len);
543 }
544
545 int
546 namematch(struct addrinfo *ai)
547 {
548         struct addrinfo *aip;
549
550         if (nfshost == NULL || nfshost_ai == NULL)
551                 return (1);
552
553         while (ai != NULL) {
554                 aip = nfshost_ai;
555                 while (aip != NULL) {
556                         if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
557                                 return (1);
558                         aip = aip->ai_next;
559                 }
560                 ai = ai->ai_next;
561         }
562
563         return (0);
564 }
565
566 struct statfs *
567 checkmntlist(char *mntname)
568 {
569         struct statfs *sfs;
570         fsid_t fsid;
571
572         sfs = NULL;
573         if (parsehexfsid(mntname, &fsid) == 0)
574                 sfs = getmntentry(NULL, NULL, &fsid, FIND);
575         if (sfs == NULL)
576                 sfs = getmntentry(NULL, mntname, NULL, FIND);
577         if (sfs == NULL)
578                 sfs = getmntentry(mntname, NULL, NULL, FIND);
579         return (sfs);
580 }
581
582 size_t
583 mntinfo(struct statfs **mntbuf)
584 {
585         static struct statfs *origbuf;
586         size_t bufsize;
587         int mntsize;
588
589         mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
590         if (mntsize <= 0)
591                 return (0);
592         bufsize = (mntsize + 1) * sizeof(struct statfs);
593         if ((origbuf = malloc(bufsize)) == NULL)
594                 err(1, "malloc");
595         mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
596         *mntbuf = origbuf;
597         return (mntsize);
598 }
599
600 /*
601  * Convert a hexadecimal filesystem ID to an fsid_t.
602  * Returns 0 on success.
603  */
604 int
605 parsehexfsid(const char *hex, fsid_t *fsid)
606 {
607         char hexbuf[3];
608         int i;
609
610         if (strlen(hex) != sizeof(*fsid) * 2)
611                 return (-1);
612         hexbuf[2] = '\0';
613         for (i = 0; i < (int)sizeof(*fsid); i++) {
614                 hexbuf[0] = hex[i * 2];
615                 hexbuf[1] = hex[i * 2 + 1];
616                 if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
617                         return (-1);
618                 ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
619         }
620         return (0);
621 }
622
623 /*
624  * xdr routines for mount rpc's
625  */
626 int
627 xdr_dir(XDR *xdrsp, char *dirp)
628 {
629
630         return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
631 }
632
633 void
634 usage(void)
635 {
636
637         (void)fprintf(stderr, "%s\n%s\n",
638             "usage: umount [-fnv] special ... | node ... | fsid ...",
639             "       umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]");
640         exit(1);
641 }