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