1 /* $NetBSD: umount.c,v 1.56 2025/07/01 20:11:13 kre Exp $ */ 2 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1989, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95"; 41 #else 42 __RCSID("$NetBSD: umount.c,v 1.56 2025/07/01 20:11:13 kre Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/mount.h> 49 #include <sys/time.h> 50 #ifndef SMALL 51 #include <sys/socket.h> 52 53 #include <netdb.h> 54 #include <rpc/rpc.h> 55 #include <rpc/pmap_clnt.h> 56 #include <rpc/pmap_prot.h> 57 #include <nfs/rpcv2.h> 58 #include <nfs/nfsmount.h> 59 60 #include <dev/vndvar.h> 61 #include <sys/ioctl.h> 62 #include <fcntl.h> 63 #endif /* !SMALL */ 64 65 #include <err.h> 66 #include <errno.h> 67 #include <fstab.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <unistd.h> 72 #include <util.h> 73 74 typedef enum { MNTANY, MNTON, MNTFROM } mntwhat; 75 76 #ifndef SMALL 77 #include "mountprog.h" 78 79 static int dflag, fake, verbose; 80 static char *nfshost; 81 static struct addrinfo *nfshost_ai = NULL; 82 83 static int namematch(const struct addrinfo *); 84 static int sacmp(const struct sockaddr *, const struct sockaddr *); 85 static int xdr_dir(XDR *, char *); 86 static const char *getmntproto(const char *); 87 static int vn_detach(const char *); 88 #endif /* !SMALL */ 89 90 static int all, fflag; 91 static char *getmntname(const char *, mntwhat, char **); 92 static int umountfs(const char *, const char **, int); 93 static void usage(void) __dead; 94 95 int 96 main(int argc, char *argv[]) 97 { 98 int ch, errs, raw = 0; 99 char mntfromname[MAXPATHLEN]; 100 #ifndef SMALL 101 int mnts; 102 struct statvfs *mntbuf; 103 struct addrinfo hints; 104 #endif /* SMALL */ 105 const char **typelist = NULL; 106 107 #ifdef SMALL 108 #define OPTS "fR" 109 #else 110 #define OPTS "AadFfh:Rt:v" 111 #endif 112 while ((ch = getopt(argc, argv, OPTS)) != -1) 113 switch (ch) { 114 case 'f': 115 fflag = MNT_FORCE; 116 break; 117 case 'R': 118 raw = 1; 119 break; 120 #ifndef SMALL 121 case 'A': 122 case 'a': 123 all = 1; 124 break; 125 case 'd': 126 dflag = 1; 127 break; 128 case 'F': 129 fake = 1; 130 break; 131 case 'h': /* -h implies -A. */ 132 all = 1; 133 nfshost = optarg; 134 break; 135 case 't': 136 if (typelist != NULL) 137 errx(1, "only one -t option may be specified."); 138 typelist = makevfslist(optarg); 139 break; 140 case 'v': 141 verbose++; 142 break; 143 #endif /* !SMALL */ 144 default: 145 usage(); 146 /* NOTREACHED */ 147 } 148 argc -= optind; 149 argv += optind; 150 151 if ((argc == 0 && !all) || (argc != 0 && all) || (all && raw)) 152 usage(); 153 154 #ifndef SMALL 155 /* -h implies "-t nfs" if no -t flag. */ 156 if ((nfshost != NULL) && (typelist == NULL)) 157 typelist = makevfslist("nfs"); 158 159 if (nfshost != NULL) { 160 memset(&hints, 0, sizeof hints); 161 if (getaddrinfo(nfshost, NULL, &hints, &nfshost_ai) != 0) { 162 nfshost_ai = NULL; 163 } 164 } 165 166 errs = 0; 167 if (all) { 168 if ((mnts = getmntinfo(&mntbuf, ST_NOWAIT)) == 0) { 169 warn("getmntinfo"); 170 errs = 1; 171 } 172 for (errs = 0, mnts--; mnts > 0; mnts--) { 173 if (checkvfsname(mntbuf[mnts].f_fstypename, typelist)) 174 continue; 175 if (umountfs(mntbuf[mnts].f_mntonname, typelist, 176 1) != 0) 177 errs = 1; 178 } 179 } else 180 #endif /* !SMALL */ 181 for (errs = 0; *argv != NULL; ++argv) { 182 if (getfsspecname(mntfromname, sizeof(mntfromname), 183 *argv) == NULL) 184 err(EXIT_FAILURE, "%s", mntfromname); 185 if (umountfs(mntfromname, typelist, raw) != 0) 186 errs = 1; 187 } 188 return errs; 189 } 190 191 static int 192 umountfs(const char *name, const char **typelist, int raw) 193 { 194 #ifndef SMALL 195 enum clnt_stat clnt_stat; 196 struct timeval try; 197 CLIENT *clp; 198 char *hostp = NULL; 199 struct addrinfo *ai = NULL, hints; 200 const char *proto = NULL; 201 struct statvfs sfs; 202 #endif /* !SMALL */ 203 const char *mntpt; 204 char *type, rname[MAXPATHLEN], umountprog[MAXPATHLEN]; 205 mntwhat what; 206 struct stat sb; 207 208 if (raw) { 209 mntpt = name; 210 } else { 211 212 what = MNTANY; 213 if (realpath(name, rname) != NULL) { 214 name = rname; 215 216 if (stat(name, &sb) == 0) { 217 if (S_ISBLK(sb.st_mode)) 218 what = MNTON; 219 else if (S_ISDIR(sb.st_mode)) 220 what = MNTFROM; 221 } 222 } 223 #ifdef SMALL 224 else { 225 warn("%s", name); 226 return 1; 227 } 228 #endif /* SMALL */ 229 mntpt = name; 230 231 switch (what) { 232 case MNTON: 233 if ((mntpt = getmntname(name, MNTON, &type)) == NULL) { 234 warnx("%s: not currently mounted", name); 235 return (1); 236 } 237 break; 238 case MNTFROM: 239 if ((name = getmntname(mntpt, MNTFROM, &type)) == NULL) { 240 warnx("%s: not currently mounted", mntpt); 241 return (1); 242 } 243 break; 244 default: 245 if ((name = getmntname(mntpt, MNTFROM, &type)) == NULL) { 246 name = mntpt; 247 if ((mntpt = getmntname(name, MNTON, &type)) == NULL) { 248 warnx("%s: not currently mounted", name); 249 return 1; 250 } 251 } 252 } 253 254 #ifndef SMALL 255 if (checkvfsname(type, typelist)) 256 return 1; 257 258 (void)memset(&hints, 0, sizeof hints); 259 if (!strncmp(type, MOUNT_NFS, 260 sizeof(((struct statvfs *)NULL)->f_fstypename))) { 261 char *delimp; 262 proto = getmntproto(mntpt); 263 /* look for host:mountpoint */ 264 if ((delimp = strrchr(name, ':')) != NULL) { 265 int len = delimp - name; 266 hostp = malloc(len + 1); 267 if (hostp == NULL) 268 return 1; 269 memcpy(hostp, name, len); 270 hostp[len] = 0; 271 name += len + 1; 272 if (getaddrinfo(hostp, NULL, &hints, &ai) != 0) 273 ai = NULL; 274 } 275 } 276 277 if (!namematch(ai)) 278 return 1; 279 #endif /* ! SMALL */ 280 snprintf(umountprog, sizeof(umountprog), "umount_%s", type); 281 } 282 283 #ifndef SMALL 284 if (verbose) { 285 (void)printf("%s: %sunmount from %s\n", 286 name, fake ? "fake " : "", mntpt); 287 /* put this before the test of FAKE */ 288 if (!raw && verbose > 1) { 289 int OK = 1; 290 if (fake) { 291 OK = faccessat(AT_FDCWD, umountprog, 292 X_OK, AT_EACCESS); 293 } 294 (void)printf("Trying unmount program %s%s\n", 295 umountprog, 296 (fake && OK < 0) ? ": would fail" : ""); 297 } 298 } 299 if (fake) 300 return 0; 301 #endif /* ! SMALL */ 302 303 if (!raw) { 304 /* 305 * The only options that need to be passed on are -f 306 * and -v. 307 */ 308 char *args[3]; 309 unsigned nargs = 0; 310 311 args[nargs++] = umountprog; 312 if (fflag == MNT_FORCE) { 313 args[nargs++] = __UNCONST("-f"); 314 } 315 #ifndef SMALL 316 if (verbose) { 317 args[nargs++] = __UNCONST("-v"); 318 } 319 #endif 320 execvp(umountprog, args); 321 if (errno != ENOENT) { 322 warn("%s: execvp", umountprog); 323 } 324 } 325 326 #ifndef SMALL 327 if (verbose > 1) 328 (void)printf("(No separate unmount program.)\n"); 329 330 if (dflag && statvfs(mntpt, &sfs) == -1) { 331 warn("%s: statvfs", mntpt); 332 return 1; 333 } 334 #endif 335 336 if (unmount(mntpt, fflag) == -1) { 337 warn("%s", mntpt); 338 return 1; 339 } 340 341 #ifndef SMALL 342 if (ai != NULL && !(fflag & MNT_FORCE)) { 343 clp = clnt_create(hostp, RPCPROG_MNT, RPCMNT_VER1, proto); 344 if (clp == NULL) { 345 clnt_pcreateerror("Cannot MNT PRC"); 346 return 1; 347 } 348 clp->cl_auth = authsys_create_default(); 349 try.tv_sec = 20; 350 try.tv_usec = 0; 351 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, 352 __UNCONST(name), xdr_void, NULL, try); 353 if (clnt_stat != RPC_SUCCESS) { 354 clnt_perror(clp, "Bad MNT RPC"); 355 return 1; 356 } 357 auth_destroy(clp->cl_auth); 358 clnt_destroy(clp); 359 } 360 361 if (dflag) { 362 if (vn_detach(sfs.f_mntfromname) == 0) { 363 if (verbose) 364 (void)printf("%s: detached\n", 365 sfs.f_mntfromname); 366 } else if (!all) 367 return (-1); 368 } 369 #endif /* ! SMALL */ 370 return 0; 371 } 372 373 static char * 374 getmntname(const char *name, mntwhat what, char **type) 375 { 376 static struct statvfs *mntbuf; 377 static int mntsize; 378 static char mntfromname[MAXPATHLEN]; 379 int i; 380 381 if (mntbuf == NULL && 382 (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 383 warn("getmntinfo"); 384 return (NULL); 385 } 386 for (i = mntsize - 1; i >= 0; i--) { 387 if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) { 388 if (type) 389 *type = mntbuf[i].f_fstypename; 390 return (mntbuf[i].f_mntonname); 391 } 392 if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) { 393 if (type) 394 *type = mntbuf[i].f_fstypename; 395 if (getfsspecname(mntfromname, sizeof(mntfromname), 396 mntbuf[i].f_mntfromname) == NULL) 397 err(EXIT_FAILURE, "%s", mntfromname); 398 return mntfromname; 399 } 400 } 401 return (NULL); 402 } 403 404 #ifndef SMALL 405 static int 406 sacmp(const struct sockaddr *sa1, const struct sockaddr *sa2) 407 { 408 const void *p1, *p2; 409 size_t len; 410 411 if (sa1->sa_family != sa2->sa_family) 412 return 1; 413 414 switch (sa1->sa_family) { 415 case AF_INET: 416 p1 = &((const struct sockaddr_in *)sa1)->sin_addr; 417 p2 = &((const struct sockaddr_in *)sa2)->sin_addr; 418 len = 4; 419 break; 420 case AF_INET6: 421 p1 = &((const struct sockaddr_in6 *)sa1)->sin6_addr; 422 p2 = &((const struct sockaddr_in6 *)sa2)->sin6_addr; 423 len = 16; 424 if (((const struct sockaddr_in6 *)sa1)->sin6_scope_id != 425 ((const struct sockaddr_in6 *)sa2)->sin6_scope_id) 426 return 1; 427 break; 428 default: 429 return 1; 430 } 431 432 return memcmp(p1, p2, len); 433 } 434 435 static int 436 namematch(const struct addrinfo *ai) 437 { 438 struct addrinfo *aip; 439 440 if (nfshost == NULL || nfshost_ai == NULL) 441 return (1); 442 443 while (ai != NULL) { 444 aip = nfshost_ai; 445 while (aip != NULL) { 446 if (sacmp(ai->ai_addr, aip->ai_addr) == 0) 447 return 1; 448 aip = aip->ai_next; 449 } 450 ai = ai->ai_next; 451 } 452 453 return 0; 454 } 455 456 /* 457 * xdr routines for mount rpc's 458 */ 459 static int 460 xdr_dir(XDR *xdrsp, char *dirp) 461 { 462 return xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN); 463 } 464 465 static const char * 466 getmntproto(const char *name) 467 { 468 struct nfs_args nfsargs; 469 struct sockaddr_storage ss; 470 471 nfsargs.sotype = SOCK_DGRAM; 472 nfsargs.addr = (struct sockaddr *)&ss; 473 nfsargs.addrlen = sizeof(ss); 474 (void)mount("nfs", name, MNT_GETARGS, &nfsargs, sizeof(nfsargs)); 475 return nfsargs.sotype == SOCK_STREAM ? "tcp" : "udp"; 476 } 477 478 int 479 vn_detach(const char *dev) 480 { 481 struct vnd_ioctl vndio; 482 char rdev[MAXPATHLEN + 1]; 483 int fd; 484 485 if (strncmp(dev, "/dev/vnd", sizeof("/dev/vnd") - 1)) { 486 if (!all) 487 warnx("invalid vnd device: %s", dev); 488 return -1; 489 } 490 491 if ((fd = opendisk(dev, O_RDWR, rdev, sizeof(rdev), 0)) == -1) { 492 warn("%s: opendisk", rdev); 493 return -1; 494 } 495 496 memset(&vndio, 0, sizeof(vndio)); 497 vndio.vnd_flags = fflag ? VNDIOF_FORCE : 0; 498 499 if (ioctl(fd, VNDIOCCLR, &vndio) == -1) { 500 warn("%s: VNDIOCCLR", rdev); 501 close(fd); 502 return -1; 503 } 504 close(fd); 505 506 return 0; 507 } 508 #endif /* !SMALL */ 509 510 static void 511 usage(void) 512 { 513 #ifdef SMALL 514 (void)fprintf(stderr, 515 "Usage: %s [-fR] special | node\n", getprogname()); 516 #else 517 (void)fprintf(stderr, 518 "Usage: %s [-dfvFR] [-t fstypelist] special | node\n" 519 "\t %s -a[dfvF] [-h host] [-t fstypelist]\n", getprogname(), 520 getprogname()); 521 #endif /* SMALL */ 522 exit(1); 523 } 524