]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/umount/umount.c
MFC: r321689
[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  * 4. 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, nfsforce, ret;
90         char **typelist = NULL;
91         struct statfs *mntbuf, *sfs;
92         struct addrinfo hints;
93
94         nfsforce = all = errs = 0;
95         while ((ch = getopt(argc, argv, "AaF:fh:Nnt: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                         nfsforce = 1;
115                         break;
116                 case 'n':
117                         fflag |= MNT_NONBUSY;
118                         break;
119                 case 't':
120                         if (typelist != NULL)
121                                 err(1, "only one -t option may be specified");
122                         typelist = makevfslist(optarg);
123                         break;
124                 case 'v':
125                         vflag = 1;
126                         break;
127                 default:
128                         usage();
129                         /* NOTREACHED */
130                 }
131         argc -= optind;
132         argv += optind;
133
134         if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0)
135                 err(1, "-f and -n are mutually exclusive");
136
137         /* Start disks transferring immediately. */
138         if ((fflag & (MNT_FORCE | MNT_NONBUSY)) == 0 && nfsforce == 0)
139                 sync();
140
141         if ((argc == 0 && !all) || (argc != 0 && all))
142                 usage();
143
144         if (nfsforce != 0 && (argc == 0 || nfshost != NULL || typelist != NULL))
145                 usage();
146
147         /* -h implies "-t nfs" if no -t flag. */
148         if ((nfshost != NULL) && (typelist == NULL))
149                 typelist = makevfslist("nfs");
150
151         if (nfshost != NULL) {
152                 memset(&hints, 0, sizeof hints);
153                 error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
154                 if (error)
155                         errx(1, "%s: %s", nfshost, gai_strerror(error));
156         }
157
158         switch (all) {
159         case 2:
160                 if ((mntsize = mntinfo(&mntbuf)) <= 0)
161                         break;
162                 /*
163                  * We unmount the nfs-mounts in the reverse order
164                  * that they were mounted.
165                  */
166                 for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
167                         sfs = &mntbuf[mntsize];
168                         if (checkvfsname(sfs->f_fstypename, typelist))
169                                 continue;
170                         if (strcmp(sfs->f_mntonname, "/dev") == 0)
171                                 continue;
172                         if (umountfs(sfs) != 0)
173                                 errs = 1;
174                 }
175                 free(mntbuf);
176                 break;
177         case 1:
178                 if (setfsent() == 0)
179                         err(1, "%s", getfstab());
180                 errs = umountall(typelist);
181                 break;
182         case 0:
183                 for (errs = 0; *argv != NULL; ++argv)
184                         if (nfsforce != 0) {
185                                 /*
186                                  * First do the nfssvc() syscall to shut down
187                                  * the mount point and then do the forced
188                                  * dismount.
189                                  */
190                                 ret = nfssvc(NFSSVC_FORCEDISM, *argv);
191                                 if (ret >= 0)
192                                         ret = unmount(*argv, MNT_FORCE);
193                                 if (ret < 0) {
194                                         warn("%s", *argv);
195                                         errs = 1;
196                                 }
197                         } else if (checkname(*argv, typelist) != 0)
198                                 errs = 1;
199                 break;
200         }
201         exit(errs);
202 }
203
204 int
205 umountall(char **typelist)
206 {
207         struct xvfsconf vfc;
208         struct fstab *fs;
209         int rval;
210         char *cp;
211         static int firstcall = 1;
212
213         if ((fs = getfsent()) != NULL)
214                 firstcall = 0;
215         else if (firstcall)
216                 errx(1, "fstab reading failure");
217         else
218                 return (0);
219         do {
220                 /* Ignore the root. */
221                 if (strcmp(fs->fs_file, "/") == 0)
222                         continue;
223                 /*
224                  * !!!
225                  * Historic practice: ignore unknown FSTAB_* fields.
226                  */
227                 if (strcmp(fs->fs_type, FSTAB_RW) &&
228                     strcmp(fs->fs_type, FSTAB_RO) &&
229                     strcmp(fs->fs_type, FSTAB_RQ))
230                         continue;
231                 /* Ignore unknown file system types. */
232                 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
233                         continue;
234                 if (checkvfsname(fs->fs_vfstype, typelist))
235                         continue;
236
237                 /*
238                  * We want to unmount the file systems in the reverse order
239                  * that they were mounted.  So, we save off the file name
240                  * in some allocated memory, and then call recursively.
241                  */
242                 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
243                         err(1, "malloc failed");
244                 (void)strcpy(cp, fs->fs_file);
245                 rval = umountall(typelist);
246                 rval = checkname(cp, typelist) || rval;
247                 free(cp);
248                 return (rval);
249         } while ((fs = getfsent()) != NULL);
250         return (0);
251 }
252
253 /*
254  * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
255  */
256 int
257 checkname(char *mntname, char **typelist)
258 {
259         char buf[MAXPATHLEN];
260         struct statfs sfsbuf;
261         struct stat sb;
262         struct statfs *sfs;
263         char *delimp;
264         dev_t dev;
265         int len;
266
267         /*
268          * 1. Check if the name exists in the mounttable.
269          */
270         sfs = checkmntlist(mntname);
271         /*
272          * 2. Remove trailing slashes if there are any. After that
273          * we look up the name in the mounttable again.
274          */
275         if (sfs == NULL) {
276                 len = strlen(mntname);
277                 while (len > 1 && mntname[len - 1] == '/')
278                         mntname[--len] = '\0';
279                 sfs = checkmntlist(mntname);
280         }
281         /*
282          * 3. Check if the deprecated NFS syntax with an '@' has been used
283          * and translate it to the ':' syntax. Look up the name in the
284          * mount table again.
285          */
286         if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
287                 snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
288                     (int)(delimp - mntname), mntname);
289                 len = strlen(buf);
290                 while (len > 1 && buf[len - 1] == '/')
291                         buf[--len] = '\0';
292                 sfs = checkmntlist(buf);
293         }
294         /*
295          * 4. Resort to a statfs(2) call. This is the last check so that
296          * hung NFS filesystems for example can be unmounted without
297          * potentially blocking forever in statfs() as long as the
298          * filesystem is specified unambiguously. This covers all the
299          * hard cases such as symlinks and mismatches between the
300          * mount list and reality.
301          * We also do this if an ambiguous mount point was specified.
302          */
303         if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
304             getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
305                 if (statfs(mntname, &sfsbuf) != 0) {
306                         warn("%s: statfs", mntname);
307                 } else if (stat(mntname, &sb) != 0) {
308                         warn("%s: stat", mntname);
309                 } else if (S_ISDIR(sb.st_mode)) {
310                         /* Check that `mntname' is the root directory. */
311                         dev = sb.st_dev;
312                         snprintf(buf, sizeof(buf), "%s/..", mntname);
313                         if (stat(buf, &sb) != 0) {
314                                 warn("%s: stat", buf);
315                         } else if (sb.st_dev == dev) {
316                                 warnx("%s: not a file system root directory",
317                                     mntname);
318                                 return (1);
319                         } else
320                                 sfs = &sfsbuf;
321                 }
322         }
323         if (sfs == NULL) {
324                 warnx("%s: unknown file system", mntname);
325                 return (1);
326         }
327         if (checkvfsname(sfs->f_fstypename, typelist))
328                 return (1);
329         return (umountfs(sfs));
330 }
331
332 /*
333  * NFS stuff and unmount(2) call
334  */
335 int
336 umountfs(struct statfs *sfs)
337 {
338         char fsidbuf[64];
339         enum clnt_stat clnt_stat;
340         struct timeval try;
341         struct addrinfo *ai, hints;
342         int do_rpc;
343         CLIENT *clp;
344         char *nfsdirname, *orignfsdirname;
345         char *hostp, *delimp;
346         char buf[1024];
347         struct nfscl_dumpmntopts dumpmntopts;
348         const char *proto_ptr = NULL;
349
350         ai = NULL;
351         do_rpc = 0;
352         hostp = NULL;
353         nfsdirname = delimp = orignfsdirname = NULL;
354         memset(&hints, 0, sizeof hints);
355
356         if (strcmp(sfs->f_fstypename, "nfs") == 0) {
357                 if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
358                         err(1, "strdup");
359                 orignfsdirname = nfsdirname;
360                 if (*nfsdirname == '[' &&
361                     (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
362                     *(delimp + 1) == ':') {
363                         hostp = nfsdirname + 1;
364                         nfsdirname = delimp + 2;
365                 } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
366                         hostp = nfsdirname;
367                         nfsdirname = delimp + 1;
368                 }
369                 if (hostp != NULL) {
370                         *delimp = '\0';
371                         getaddrinfo(hostp, NULL, &hints, &ai);
372                         if (ai == NULL) {
373                                 warnx("can't get net id for host");
374                         }
375                 }
376
377                 /*
378                  * Check if we have to start the rpc-call later.
379                  * If there are still identical nfs-names mounted,
380                  * we skip the rpc-call. Obviously this has to
381                  * happen before unmount(2), but it should happen
382                  * after the previous namecheck.
383                  * A non-NULL return means that this is the last
384                  * mount from mntfromname that is still mounted.
385                  */
386                 if (getmntentry(sfs->f_mntfromname, NULL, NULL,
387                     CHECKUNIQUE) != NULL) {
388                         do_rpc = 1;
389                         proto_ptr = "udp";
390                         /*
391                          * Try and find out whether this NFS mount is NFSv4 and
392                          * what protocol is being used. If this fails, the
393                          * default is NFSv2,3 and use UDP for the Unmount RPC.
394                          */
395                         dumpmntopts.ndmnt_fname = sfs->f_mntonname;
396                         dumpmntopts.ndmnt_buf = buf;
397                         dumpmntopts.ndmnt_blen = sizeof(buf);
398                         if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) {
399                                 if (strstr(buf, "nfsv4,") != NULL)
400                                         do_rpc = 0;
401                                 else if (strstr(buf, ",tcp,") != NULL)
402                                         proto_ptr = "tcp";
403                         }
404                 }
405         }
406
407         if (!namematch(ai)) {
408                 free(orignfsdirname);
409                 return (1);
410         }
411         /* First try to unmount using the file system ID. */
412         snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
413             sfs->f_fsid.val[1]);
414         if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
415                 /* XXX, non-root users get a zero fsid, so don't warn. */
416                 if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
417                     sfs->f_fsid.val[1] != 0)
418                         warn("unmount of %s failed", sfs->f_mntonname);
419                 if (errno != ENOENT) {
420                         free(orignfsdirname);
421                         return (1);
422                 }
423                 /* Compatibility for old kernels. */
424                 if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
425                         warnx("retrying using path instead of file system ID");
426                 if (unmount(sfs->f_mntonname, fflag) != 0) {
427                         warn("unmount of %s failed", sfs->f_mntonname);
428                         free(orignfsdirname);
429                         return (1);
430                 }
431         }
432         /* Mark this this file system as unmounted. */
433         getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
434         if (vflag)
435                 (void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
436                     sfs->f_mntonname);
437         /*
438          * Report to mountd-server which nfsname
439          * has been unmounted.
440          */
441         if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
442                 clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr);
443                 if (clp  == NULL) {
444                         warnx("%s: %s", hostp,
445                             clnt_spcreateerror("MOUNTPROG"));
446                         free(orignfsdirname);
447                         return (1);
448                 }
449                 clp->cl_auth = authsys_create_default();
450                 try.tv_sec = 20;
451                 try.tv_usec = 0;
452                 clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
453                     nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
454                 if (clnt_stat != RPC_SUCCESS) {
455                         warnx("%s: %s", hostp,
456                             clnt_sperror(clp, "RPCMNT_UMOUNT"));
457                         free(orignfsdirname);
458                         return (1);
459                 }
460                 /*
461                  * Remove the unmounted entry from /var/db/mounttab.
462                  */
463                 if (read_mtab()) {
464                         clean_mtab(hostp, nfsdirname, vflag);
465                         if(!write_mtab(vflag))
466                                 warnx("cannot remove mounttab entry %s:%s",
467                                     hostp, nfsdirname);
468                         free_mtab();
469                 }
470                 auth_destroy(clp->cl_auth);
471                 clnt_destroy(clp);
472         }
473         free(orignfsdirname);
474         return (0);
475 }
476
477 struct statfs *
478 getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
479 {
480         static struct statfs *mntbuf;
481         static size_t mntsize = 0;
482         static int *mntcheck = NULL;
483         struct statfs *sfs, *foundsfs;
484         int i, count;
485
486         if (mntsize <= 0) {
487                 if ((mntsize = mntinfo(&mntbuf)) <= 0)
488                         return (NULL);
489         }
490         if (mntcheck == NULL) {
491                 if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
492                         err(1, "calloc");
493         }
494         /*
495          * We want to get the file systems in the reverse order
496          * that they were mounted. Unmounted file systems are marked
497          * in a table called 'mntcheck'.
498          */
499         count = 0;
500         foundsfs = NULL;
501         for (i = mntsize - 1; i >= 0; i--) {
502                 if (mntcheck[i])
503                         continue;
504                 sfs = &mntbuf[i];
505                 if (fromname != NULL && strcmp(sfs->f_mntfromname,
506                     fromname) != 0)
507                         continue;
508                 if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
509                         continue;
510                 if (fsid != NULL && bcmp(&sfs->f_fsid, fsid,
511                     sizeof(*fsid)) != 0)
512                         continue;
513
514                 switch (what) {
515                 case CHECKUNIQUE:
516                         foundsfs = sfs;
517                         count++;
518                         continue;
519                 case REMOVE:
520                         mntcheck[i] = 1;
521                         break;
522                 default:
523                         break;
524                 }
525                 return (sfs);
526         }
527
528         if (what == CHECKUNIQUE && count == 1)
529                 return (foundsfs);
530         return (NULL);
531 }
532
533 int
534 sacmp(void *sa1, void *sa2)
535 {
536         void *p1, *p2;
537         int len;
538
539         if (((struct sockaddr *)sa1)->sa_family !=
540             ((struct sockaddr *)sa2)->sa_family)
541                 return (1);
542
543         switch (((struct sockaddr *)sa1)->sa_family) {
544         case AF_INET:
545                 p1 = &((struct sockaddr_in *)sa1)->sin_addr;
546                 p2 = &((struct sockaddr_in *)sa2)->sin_addr;
547                 len = 4;
548                 break;
549         case AF_INET6:
550                 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
551                 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
552                 len = 16;
553                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
554                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
555                         return (1);
556                 break;
557         default:
558                 return (1);
559         }
560
561         return memcmp(p1, p2, len);
562 }
563
564 int
565 namematch(struct addrinfo *ai)
566 {
567         struct addrinfo *aip;
568
569         if (nfshost == NULL || nfshost_ai == NULL)
570                 return (1);
571
572         while (ai != NULL) {
573                 aip = nfshost_ai;
574                 while (aip != NULL) {
575                         if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
576                                 return (1);
577                         aip = aip->ai_next;
578                 }
579                 ai = ai->ai_next;
580         }
581
582         return (0);
583 }
584
585 struct statfs *
586 checkmntlist(char *mntname)
587 {
588         struct statfs *sfs;
589         fsid_t fsid;
590
591         sfs = NULL;
592         if (parsehexfsid(mntname, &fsid) == 0)
593                 sfs = getmntentry(NULL, NULL, &fsid, FIND);
594         if (sfs == NULL)
595                 sfs = getmntentry(NULL, mntname, NULL, FIND);
596         if (sfs == NULL)
597                 sfs = getmntentry(mntname, NULL, NULL, FIND);
598         return (sfs);
599 }
600
601 size_t
602 mntinfo(struct statfs **mntbuf)
603 {
604         static struct statfs *origbuf;
605         size_t bufsize;
606         int mntsize;
607
608         mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
609         if (mntsize <= 0)
610                 return (0);
611         bufsize = (mntsize + 1) * sizeof(struct statfs);
612         if ((origbuf = malloc(bufsize)) == NULL)
613                 err(1, "malloc");
614         mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
615         *mntbuf = origbuf;
616         return (mntsize);
617 }
618
619 /*
620  * Convert a hexadecimal filesystem ID to an fsid_t.
621  * Returns 0 on success.
622  */
623 int
624 parsehexfsid(const char *hex, fsid_t *fsid)
625 {
626         char hexbuf[3];
627         int i;
628
629         if (strlen(hex) != sizeof(*fsid) * 2)
630                 return (-1);
631         hexbuf[2] = '\0';
632         for (i = 0; i < (int)sizeof(*fsid); i++) {
633                 hexbuf[0] = hex[i * 2];
634                 hexbuf[1] = hex[i * 2 + 1];
635                 if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
636                         return (-1);
637                 ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
638         }
639         return (0);
640 }
641
642 /*
643  * xdr routines for mount rpc's
644  */
645 int
646 xdr_dir(XDR *xdrsp, char *dirp)
647 {
648
649         return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
650 }
651
652 void
653 usage(void)
654 {
655
656         (void)fprintf(stderr, "%s\n%s\n",
657             "usage: umount [-fNnv] special ... | node ... | fsid ...",
658             "       umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]");
659         exit(1);
660 }