1 /* $NetBSD: mount_nfs.c,v 1.77 2026/05/28 13:38:06 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Rick Macklem at The University of Guelph. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ 38 The Regents of the University of California. All rights reserved."); 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 44 #else 45 __RCSID("$NetBSD: mount_nfs.c,v 1.77 2026/05/28 13:38:06 christos Exp $"); 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/mount.h> 51 #include <sys/socket.h> 52 #include <sys/stat.h> 53 #include <syslog.h> 54 55 #include <rpc/rpc.h> 56 #include <nfs/rpcv2.h> /* XXX: redefines enums */ 57 #include <nfs/nfsproto.h> 58 #include <nfs/nfs.h> 59 #include <nfs/nfsmount.h> 60 61 #include <arpa/inet.h> 62 63 #include <err.h> 64 #include <errno.h> 65 #include <fcntl.h> 66 #include <netdb.h> 67 #include <signal.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <unistd.h> 72 #include <util.h> 73 74 #include <mntopts.h> 75 76 #include "mountprog.h" 77 #include "mount_nfs.h" 78 79 #define ALTF_BG 0x00000001 80 #define ALTF_CONN 0x00000002 81 #define ALTF_DUMBTIMR 0x00000004 82 #define ALTF_INTR 0x00000008 83 #define ALTF_NOAC 0x00000010 84 #define ALTF_NFSV3 0x00000020 85 #define ALTF_RDIRPLUS 0x00000040 86 #define ALTF_MNTUDP 0x00000080 87 #define ALTF_NORESPORT 0x00000100 88 #define ALTF_SEQPACKET 0x00000200 89 #define ALTF_NQNFS 0x00000400 90 #define ALTF_SOFT 0x00000800 91 #define ALTF_TCP 0x00001000 92 #define ALTF_NFSV2 0x00002000 93 #define ALTF_PORT 0x00004000 94 #define ALTF_RSIZE 0x00008000 95 #define ALTF_WSIZE 0x00010000 96 #define ALTF_RDIRSIZE 0x00020000 97 #define ALTF_MAXGRPS 0x00040000 98 #define ALTF_LEASETERM 0x00080000 99 #define ALTF_READAHEAD 0x00100000 100 #define ALTF_DEADTHRESH 0x00200000 101 #define ALTF_TIMEO 0x00400000 102 #define ALTF_RETRANS 0x00800000 103 #define ALTF_UDP 0x01000000 104 #define ALTF_NOWCCMSG 0x02000000 105 106 static const struct mntopt mopts[] = { 107 MOPT_STDOPTS, 108 MOPT_FORCE, 109 MOPT_UPDATE, 110 MOPT_GETARGS, 111 { "bg", 0, ALTF_BG, 1 }, 112 { "conn", 0, ALTF_CONN, 1 }, 113 { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 114 { "intr", 0, ALTF_INTR, 1 }, 115 { "ac", 1, ALTF_NOAC, 1 }, 116 { "nfsv3", 0, ALTF_NFSV3, 1 }, 117 { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 118 { "mntudp", 0, ALTF_MNTUDP, 1 }, 119 { "resport", 1, ALTF_NORESPORT, 1 }, 120 { "nqnfs", 0, ALTF_NQNFS, 1 }, 121 { "soft", 0, ALTF_SOFT, 1 }, 122 { "tcp", 0, ALTF_TCP, 1 }, 123 { "udp", 0, ALTF_UDP, 1 }, 124 { "nfsv2", 0, ALTF_NFSV2, 1 }, 125 { "port", 0, ALTF_PORT, 1 }, 126 { "rsize", 0, ALTF_RSIZE, 1 }, 127 { "wsize", 0, ALTF_WSIZE, 1 }, 128 { "rdirsize", 0, ALTF_RDIRSIZE, 1 }, 129 { "maxgrps", 0, ALTF_MAXGRPS, 1 }, 130 { "leaseterm", 0, ALTF_LEASETERM, 1 }, 131 { "readahead", 0, ALTF_READAHEAD, 1 }, 132 { "deadthresh", 0, ALTF_DEADTHRESH, 1 }, 133 { "timeo", 0, ALTF_TIMEO, 1 }, 134 { "wccmsg", 1, ALTF_NOWCCMSG, 1 }, 135 MOPT_NULL, 136 137 }; 138 139 struct nfs_args nfsdefargs = { 140 .version = NFS_ARGSVERSION, 141 .addr = NULL, 142 .addrlen = sizeof(struct sockaddr_in), 143 .sotype = SOCK_STREAM, 144 .proto = 0, 145 .fh = NULL, 146 .fhsize = 0, 147 .flags = NFSMNT_NFSV3|NFSMNT_NOCONN|NFSMNT_RESVPORT, 148 .wsize = NFS_WSIZE, 149 .rsize = NFS_RSIZE, 150 .readdirsize = NFS_READDIRSIZE, 151 .timeo = 10, 152 .retrans = NFS_RETRANS, 153 .maxgrouplist = NFS_MAXGRPS, 154 .readahead = NFS_DEFRAHEAD, 155 .leaseterm = 0, /* Ignored; lease term */ 156 .deadthresh = NFS_DEFDEADTHRESH, 157 .hostname = NULL, 158 }; 159 160 static void shownfsargs(const struct nfs_args *); 161 int mount_nfs(int argc, char **argv); 162 /* void set_rpc_maxgrouplist(int); */ 163 __dead static void usage(void); 164 165 #ifndef MOUNT_NOMAIN 166 int 167 main(int argc, char **argv) 168 { 169 170 setprogname(argv[0]); 171 return mount_nfs(argc, argv); 172 } 173 #endif 174 175 void 176 mount_nfs_dogetargs(struct nfs_args *nfsargsp, int mntflags, const char *spec) 177 { 178 static struct sockaddr_storage sa; 179 char *tspec; 180 181 if ((mntflags & MNT_GETARGS) != 0) { 182 memset(&sa, 0, sizeof(sa)); 183 nfsargsp->addr = (struct sockaddr *)&sa; 184 nfsargsp->addrlen = sizeof(sa); 185 } else { 186 if ((tspec = strdup(spec)) == NULL) { 187 err(EXIT_FAILURE, "strdup"); 188 } 189 if (!getnfsargs(tspec, nfsargsp)) { 190 exit(EXIT_FAILURE); 191 } 192 free(tspec); 193 } 194 } 195 196 static int 197 getnum(const char *s, int c) 198 { 199 char *es; 200 long num = strtol(s, &es, 10); 201 if (*es || num <= 0 || num > INT_MAX) 202 errx(EXIT_FAILURE, "Illegal value `%s' for option -%c", s, c); 203 return (int)num; 204 } 205 206 static __dead void 207 conflicting(void) 208 { 209 errx(EXIT_FAILURE, "Conflicting version options"); 210 } 211 212 void 213 mount_nfs_parseargs(int argc, char *argv[], 214 struct nfs_args *nfsargsp, int *mntflags, 215 char *spec, char *name) 216 { 217 int altflags, num; 218 int c; 219 mntoptparse_t mp; 220 221 *mntflags = 0; 222 altflags = 0; 223 memset(nfsargsp, 0, sizeof(*nfsargsp)); 224 *nfsargsp = nfsdefargs; 225 while ((c = getopt(argc, argv, 226 "23Aa:bcCdD:g:I:iL:lo:PpqR:r:sTt:w:x:UuX")) != -1) 227 switch (c) { 228 case '3': 229 case 'q': 230 if (force2) 231 conflicting(); 232 force3 = 1; 233 break; 234 case '2': 235 if (force3) 236 conflicting(); 237 force2 = 1; 238 nfsargsp->flags &= ~NFSMNT_NFSV3; 239 break; 240 case 'A': 241 nfsargsp->flags |= NFSMNT_NOAC; 242 break; 243 case 'a': 244 nfsargsp->readahead = getnum(optarg, c); 245 nfsargsp->flags |= NFSMNT_READAHEAD; 246 break; 247 case 'b': 248 opflags |= BGRND; 249 break; 250 case 'c': 251 nfsargsp->flags |= NFSMNT_NOCONN; 252 break; 253 case 'C': 254 nfsargsp->flags &= ~NFSMNT_NOCONN; 255 break; 256 case 'D': 257 nfsargsp->deadthresh = getnum(optarg, c); 258 nfsargsp->flags |= NFSMNT_DEADTHRESH; 259 break; 260 case 'd': 261 nfsargsp->flags |= NFSMNT_DUMBTIMR; 262 break; 263 case 'g': 264 num = getnum(optarg, c); 265 set_rpc_maxgrouplist(num); 266 nfsargsp->maxgrouplist = num; 267 nfsargsp->flags |= NFSMNT_MAXGRPS; 268 break; 269 case 'I': 270 nfsargsp->readdirsize = getnum(optarg, c); 271 nfsargsp->flags |= NFSMNT_READDIRSIZE; 272 break; 273 case 'i': 274 nfsargsp->flags |= NFSMNT_INT; 275 break; 276 case 'L': 277 /* ignore */ 278 break; 279 case 'l': 280 nfsargsp->flags |= NFSMNT_RDIRPLUS; 281 break; 282 case 'o': 283 mp = getmntopts(optarg, mopts, mntflags, &altflags); 284 if (mp == NULL) 285 err(EXIT_FAILURE, "getmntopts"); 286 if (altflags & ALTF_BG) 287 opflags |= BGRND; 288 if (altflags & ALTF_CONN) 289 nfsargsp->flags &= ~NFSMNT_NOCONN; 290 if (altflags & ALTF_DUMBTIMR) 291 nfsargsp->flags |= NFSMNT_DUMBTIMR; 292 if (altflags & ALTF_INTR) 293 nfsargsp->flags |= NFSMNT_INT; 294 if (altflags & ALTF_NOAC) 295 nfsargsp->flags |= NFSMNT_NOAC; 296 if (altflags & ALTF_NOWCCMSG) 297 nfsargsp->flags |= NFSMNT_NOWCCMSG; 298 if (altflags & (ALTF_NFSV3|ALTF_NQNFS)) { 299 if (force2) 300 conflicting(); 301 force3 = 1; 302 } 303 if (altflags & ALTF_NFSV2) { 304 if (force3) 305 conflicting(); 306 force2 = 1; 307 nfsargsp->flags &= ~NFSMNT_NFSV3; 308 } 309 if (altflags & ALTF_RDIRPLUS) 310 nfsargsp->flags |= NFSMNT_RDIRPLUS; 311 if (altflags & ALTF_MNTUDP) 312 mnttcp_ok = 0; 313 if (altflags & ALTF_NORESPORT) 314 nfsargsp->flags &= ~NFSMNT_RESVPORT; 315 if (altflags & ALTF_SOFT) 316 nfsargsp->flags |= NFSMNT_SOFT; 317 if (altflags & ALTF_UDP) { 318 nfsargsp->sotype = SOCK_DGRAM; 319 } 320 /* 321 * After UDP, because TCP overrides if both 322 * are present. 323 */ 324 if (altflags & ALTF_TCP) { 325 nfsargsp->sotype = SOCK_STREAM; 326 } 327 if (altflags & ALTF_PORT) { 328 port = (int)getmntoptnum(mp, "port"); 329 } 330 if (altflags & ALTF_RSIZE) { 331 nfsargsp->rsize = 332 (int)getmntoptnum(mp, "rsize"); 333 nfsargsp->flags |= NFSMNT_RSIZE; 334 } 335 if (altflags & ALTF_WSIZE) { 336 nfsargsp->wsize = 337 (int)getmntoptnum(mp, "wsize"); 338 nfsargsp->flags |= NFSMNT_WSIZE; 339 } 340 if (altflags & ALTF_RDIRSIZE) { 341 nfsargsp->rsize = 342 (int)getmntoptnum(mp, "rdirsize"); 343 nfsargsp->flags |= NFSMNT_READDIRSIZE; 344 } 345 #if 0 346 if (altflags & ALTF_MAXGRPS) { 347 set_rpc_maxgrouplist(num); 348 nfsargsp->maxgrouplist = 349 (int)getmntoptnum(mp, "maxgrps"); 350 nfsargsp->flags |= NFSMNT_MAXGRPS; 351 } 352 #endif 353 if (altflags & ALTF_LEASETERM) { 354 nfsargsp->leaseterm = 355 (int)getmntoptnum(mp, "leaseterm"); 356 nfsargsp->flags |= NFSMNT_LEASETERM; 357 } 358 if (altflags & ALTF_READAHEAD) { 359 nfsargsp->readahead = 360 (int)getmntoptnum(mp, "readahead"); 361 nfsargsp->flags |= NFSMNT_READAHEAD; 362 } 363 if (altflags & ALTF_DEADTHRESH) { 364 nfsargsp->deadthresh = 365 (int)getmntoptnum(mp, "deadthresh"); 366 nfsargsp->flags |= NFSMNT_DEADTHRESH; 367 } 368 if (altflags & ALTF_TIMEO) { 369 nfsargsp->timeo = 370 (int)getmntoptnum(mp, "timeo"); 371 nfsargsp->flags |= NFSMNT_TIMEO; 372 } 373 if (altflags & ALTF_RETRANS) { 374 nfsargsp->retrans = 375 (int)getmntoptnum(mp, "retrans"); 376 nfsargsp->flags |= NFSMNT_RETRANS; 377 } 378 altflags = 0; 379 freemntopts(mp); 380 break; 381 case 'P': 382 nfsargsp->flags |= NFSMNT_RESVPORT; 383 break; 384 case 'p': 385 nfsargsp->flags &= ~NFSMNT_RESVPORT; 386 break; 387 case 'R': 388 retrycnt = getnum(optarg, c); 389 break; 390 case 'r': 391 nfsargsp->rsize = getnum(optarg, c); 392 nfsargsp->flags |= NFSMNT_RSIZE; 393 break; 394 case 's': 395 nfsargsp->flags |= NFSMNT_SOFT; 396 break; 397 case 'T': 398 nfsargsp->sotype = SOCK_STREAM; 399 break; 400 case 't': 401 nfsargsp->timeo = getnum(optarg, c); 402 nfsargsp->flags |= NFSMNT_TIMEO; 403 break; 404 case 'w': 405 nfsargsp->wsize = getnum(optarg, c); 406 nfsargsp->flags |= NFSMNT_WSIZE; 407 break; 408 case 'x': 409 nfsargsp->retrans = getnum(optarg, c); 410 nfsargsp->flags |= NFSMNT_RETRANS; 411 break; 412 case 'X': 413 nfsargsp->flags |= NFSMNT_XLATECOOKIE; 414 break; 415 case 'u': 416 nfsargsp->sotype = SOCK_DGRAM; 417 break; 418 case 'U': 419 mnttcp_ok = 0; 420 break; 421 default: 422 usage(); 423 } 424 argc -= optind; 425 argv += optind; 426 427 if (argc != 2) 428 usage(); 429 430 strlcpy(spec, *argv++, MAXPATHLEN); 431 pathadj(*argv, name); 432 mount_nfs_dogetargs(nfsargsp, *mntflags, spec); 433 } 434 435 int 436 mount_nfs(int argc, char *argv[]) 437 { 438 char spec[MAXPATHLEN], name[MAXPATHLEN]; 439 struct nfs_args args; 440 int mntflags; 441 int retval; 442 443 mount_nfs_parseargs(argc, argv, &args, &mntflags, spec, name); 444 445 retry: 446 if ((retval = mount(MOUNT_NFS, name, mntflags, 447 &args, sizeof args)) == -1) { 448 /* Did we just default to v3 on a v2-only kernel? 449 * If so, default to v2 & try again */ 450 if (errno == EPROGMISMATCH && 451 (args.flags & NFSMNT_NFSV3) != 0 && !force3) { 452 /* 453 * fall back to v2. XXX lack of V3 umount. 454 */ 455 args.flags &= ~NFSMNT_NFSV3; 456 mount_nfs_dogetargs(&args, mntflags, spec); 457 goto retry; 458 } 459 } 460 if (retval == -1) 461 err(EXIT_FAILURE, "%s on %s", spec, name); 462 if (mntflags & MNT_GETARGS) { 463 shownfsargs(&args); 464 return EXIT_SUCCESS; 465 } 466 467 exit(EXIT_SUCCESS); 468 } 469 470 static void 471 shownfsargs(const struct nfs_args *nfsargsp) 472 { 473 char fbuf[2048]; 474 char host[NI_MAXHOST], serv[NI_MAXSERV]; 475 int error; 476 477 (void)snprintb(fbuf, sizeof(fbuf), NFSMNT_BITS, 478 (uint64_t)nfsargsp->flags); 479 if (nfsargsp->addr != NULL) { 480 error = getnameinfo(nfsargsp->addr, 481 (socklen_t)nfsargsp->addrlen, host, 482 sizeof(host), serv, sizeof(serv), 483 NI_NUMERICHOST | NI_NUMERICSERV); 484 if (error != 0) 485 warnx("getnameinfo: %s", gai_strerror(error)); 486 } else 487 error = -1; 488 489 if (error == 0) 490 printf("addr=%s, port=%s, addrlen=%d, ", 491 host, serv, nfsargsp->addrlen); 492 printf("sotype=%d, proto=%d, fhsize=%d, " 493 "flags=%s, wsize=%d, rsize=%d, readdirsize=%d, timeo=%d, " 494 "retrans=%d, maxgrouplist=%d, readahead=%d, leaseterm=%d, " 495 "deadthresh=%d\n", 496 nfsargsp->sotype, 497 nfsargsp->proto, 498 nfsargsp->fhsize, 499 fbuf, 500 nfsargsp->wsize, 501 nfsargsp->rsize, 502 nfsargsp->readdirsize, 503 nfsargsp->timeo, 504 nfsargsp->retrans, 505 nfsargsp->maxgrouplist, 506 nfsargsp->readahead, 507 nfsargsp->leaseterm, 508 nfsargsp->deadthresh); 509 } 510 511 static void 512 usage(void) 513 { 514 (void)fprintf(stderr, "Usage: %s %s\n%s\n%s\n%s\n%s\n", getprogname(), 515 "[-23bCcdilPpqsTUuX] [-a maxreadahead] [-D deadthresh]", 516 "\t[-g maxgroups] [-I readdirsize] [-L leaseterm]", 517 "\t[-o options] [-R retrycnt] [-r readsize] [-t timeout]", 518 "\t[-w writesize] [-x retrans]", 519 "\trhost:path node"); 520 exit(EXIT_FAILURE); 521 } 522