1 /* $NetBSD: mount_nfs.c,v 1.76 2025/01/03 00:49:24 rillig 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.76 2025/01/03 00:49:24 rillig 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 105 static const struct mntopt mopts[] = { 106 MOPT_STDOPTS, 107 MOPT_FORCE, 108 MOPT_UPDATE, 109 MOPT_GETARGS, 110 { "bg", 0, ALTF_BG, 1 }, 111 { "conn", 0, ALTF_CONN, 1 }, 112 { "dumbtimer", 0, ALTF_DUMBTIMR, 1 }, 113 { "intr", 0, ALTF_INTR, 1 }, 114 { "ac", 1, ALTF_NOAC, 1 }, 115 { "nfsv3", 0, ALTF_NFSV3, 1 }, 116 { "rdirplus", 0, ALTF_RDIRPLUS, 1 }, 117 { "mntudp", 0, ALTF_MNTUDP, 1 }, 118 { "resport", 1, ALTF_NORESPORT, 1 }, 119 { "nqnfs", 0, ALTF_NQNFS, 1 }, 120 { "soft", 0, ALTF_SOFT, 1 }, 121 { "tcp", 0, ALTF_TCP, 1 }, 122 { "udp", 0, ALTF_UDP, 1 }, 123 { "nfsv2", 0, ALTF_NFSV2, 1 }, 124 { "port", 0, ALTF_PORT, 1 }, 125 { "rsize", 0, ALTF_RSIZE, 1 }, 126 { "wsize", 0, ALTF_WSIZE, 1 }, 127 { "rdirsize", 0, ALTF_RDIRSIZE, 1 }, 128 { "maxgrps", 0, ALTF_MAXGRPS, 1 }, 129 { "leaseterm", 0, ALTF_LEASETERM, 1 }, 130 { "readahead", 0, ALTF_READAHEAD, 1 }, 131 { "deadthresh", 0, ALTF_DEADTHRESH, 1 }, 132 { "timeo", 0, ALTF_TIMEO, 1 }, 133 MOPT_NULL, 134 135 }; 136 137 struct nfs_args nfsdefargs = { 138 .version = NFS_ARGSVERSION, 139 .addr = NULL, 140 .addrlen = sizeof(struct sockaddr_in), 141 .sotype = SOCK_STREAM, 142 .proto = 0, 143 .fh = NULL, 144 .fhsize = 0, 145 .flags = NFSMNT_NFSV3|NFSMNT_NOCONN|NFSMNT_RESVPORT, 146 .wsize = NFS_WSIZE, 147 .rsize = NFS_RSIZE, 148 .readdirsize = NFS_READDIRSIZE, 149 .timeo = 10, 150 .retrans = NFS_RETRANS, 151 .maxgrouplist = NFS_MAXGRPS, 152 .readahead = NFS_DEFRAHEAD, 153 .leaseterm = 0, /* Ignored; lease term */ 154 .deadthresh = NFS_DEFDEADTHRESH, 155 .hostname = NULL, 156 }; 157 158 static void shownfsargs(const struct nfs_args *); 159 int mount_nfs(int argc, char **argv); 160 /* void set_rpc_maxgrouplist(int); */ 161 __dead static void usage(void); 162 163 #ifndef MOUNT_NOMAIN 164 int 165 main(int argc, char **argv) 166 { 167 168 setprogname(argv[0]); 169 return mount_nfs(argc, argv); 170 } 171 #endif 172 173 void 174 mount_nfs_dogetargs(struct nfs_args *nfsargsp, int mntflags, const char *spec) 175 { 176 static struct sockaddr_storage sa; 177 char *tspec; 178 179 if ((mntflags & MNT_GETARGS) != 0) { 180 memset(&sa, 0, sizeof(sa)); 181 nfsargsp->addr = (struct sockaddr *)&sa; 182 nfsargsp->addrlen = sizeof(sa); 183 } else { 184 if ((tspec = strdup(spec)) == NULL) { 185 err(EXIT_FAILURE, "strdup"); 186 } 187 if (!getnfsargs(tspec, nfsargsp)) { 188 exit(EXIT_FAILURE); 189 } 190 free(tspec); 191 } 192 } 193 194 static int 195 getnum(const char *s, int c) 196 { 197 char *es; 198 long num = strtol(s, &es, 10); 199 if (*es || num <= 0 || num > INT_MAX) 200 errx(EXIT_FAILURE, "Illegal value `%s' for option -%c", s, c); 201 return (int)num; 202 } 203 204 static __dead void 205 conflicting(void) 206 { 207 errx(EXIT_FAILURE, "Conflicting version options"); 208 } 209 210 void 211 mount_nfs_parseargs(int argc, char *argv[], 212 struct nfs_args *nfsargsp, int *mntflags, 213 char *spec, char *name) 214 { 215 int altflags, num; 216 int c; 217 mntoptparse_t mp; 218 219 *mntflags = 0; 220 altflags = 0; 221 memset(nfsargsp, 0, sizeof(*nfsargsp)); 222 *nfsargsp = nfsdefargs; 223 while ((c = getopt(argc, argv, 224 "23Aa:bcCdD:g:I:iL:lo:PpqR:r:sTt:w:x:UuX")) != -1) 225 switch (c) { 226 case '3': 227 case 'q': 228 if (force2) 229 conflicting(); 230 force3 = 1; 231 break; 232 case '2': 233 if (force3) 234 conflicting(); 235 force2 = 1; 236 nfsargsp->flags &= ~NFSMNT_NFSV3; 237 break; 238 case 'A': 239 nfsargsp->flags |= NFSMNT_NOAC; 240 break; 241 case 'a': 242 nfsargsp->readahead = getnum(optarg, c); 243 nfsargsp->flags |= NFSMNT_READAHEAD; 244 break; 245 case 'b': 246 opflags |= BGRND; 247 break; 248 case 'c': 249 nfsargsp->flags |= NFSMNT_NOCONN; 250 break; 251 case 'C': 252 nfsargsp->flags &= ~NFSMNT_NOCONN; 253 break; 254 case 'D': 255 nfsargsp->deadthresh = getnum(optarg, c); 256 nfsargsp->flags |= NFSMNT_DEADTHRESH; 257 break; 258 case 'd': 259 nfsargsp->flags |= NFSMNT_DUMBTIMR; 260 break; 261 case 'g': 262 num = getnum(optarg, c); 263 set_rpc_maxgrouplist(num); 264 nfsargsp->maxgrouplist = num; 265 nfsargsp->flags |= NFSMNT_MAXGRPS; 266 break; 267 case 'I': 268 nfsargsp->readdirsize = getnum(optarg, c); 269 nfsargsp->flags |= NFSMNT_READDIRSIZE; 270 break; 271 case 'i': 272 nfsargsp->flags |= NFSMNT_INT; 273 break; 274 case 'L': 275 /* ignore */ 276 break; 277 case 'l': 278 nfsargsp->flags |= NFSMNT_RDIRPLUS; 279 break; 280 case 'o': 281 mp = getmntopts(optarg, mopts, mntflags, &altflags); 282 if (mp == NULL) 283 err(EXIT_FAILURE, "getmntopts"); 284 if (altflags & ALTF_BG) 285 opflags |= BGRND; 286 if (altflags & ALTF_CONN) 287 nfsargsp->flags &= ~NFSMNT_NOCONN; 288 if (altflags & ALTF_DUMBTIMR) 289 nfsargsp->flags |= NFSMNT_DUMBTIMR; 290 if (altflags & ALTF_INTR) 291 nfsargsp->flags |= NFSMNT_INT; 292 if (altflags & ALTF_NOAC) 293 nfsargsp->flags |= NFSMNT_NOAC; 294 if (altflags & (ALTF_NFSV3|ALTF_NQNFS)) { 295 if (force2) 296 conflicting(); 297 force3 = 1; 298 } 299 if (altflags & ALTF_NFSV2) { 300 if (force3) 301 conflicting(); 302 force2 = 1; 303 nfsargsp->flags &= ~NFSMNT_NFSV3; 304 } 305 if (altflags & ALTF_RDIRPLUS) 306 nfsargsp->flags |= NFSMNT_RDIRPLUS; 307 if (altflags & ALTF_MNTUDP) 308 mnttcp_ok = 0; 309 if (altflags & ALTF_NORESPORT) 310 nfsargsp->flags &= ~NFSMNT_RESVPORT; 311 if (altflags & ALTF_SOFT) 312 nfsargsp->flags |= NFSMNT_SOFT; 313 if (altflags & ALTF_UDP) { 314 nfsargsp->sotype = SOCK_DGRAM; 315 } 316 /* 317 * After UDP, because TCP overrides if both 318 * are present. 319 */ 320 if (altflags & ALTF_TCP) { 321 nfsargsp->sotype = SOCK_STREAM; 322 } 323 if (altflags & ALTF_PORT) { 324 port = (int)getmntoptnum(mp, "port"); 325 } 326 if (altflags & ALTF_RSIZE) { 327 nfsargsp->rsize = 328 (int)getmntoptnum(mp, "rsize"); 329 nfsargsp->flags |= NFSMNT_RSIZE; 330 } 331 if (altflags & ALTF_WSIZE) { 332 nfsargsp->wsize = 333 (int)getmntoptnum(mp, "wsize"); 334 nfsargsp->flags |= NFSMNT_WSIZE; 335 } 336 if (altflags & ALTF_RDIRSIZE) { 337 nfsargsp->rsize = 338 (int)getmntoptnum(mp, "rdirsize"); 339 nfsargsp->flags |= NFSMNT_READDIRSIZE; 340 } 341 #if 0 342 if (altflags & ALTF_MAXGRPS) { 343 set_rpc_maxgrouplist(num); 344 nfsargsp->maxgrouplist = 345 (int)getmntoptnum(mp, "maxgrps"); 346 nfsargsp->flags |= NFSMNT_MAXGRPS; 347 } 348 #endif 349 if (altflags & ALTF_LEASETERM) { 350 nfsargsp->leaseterm = 351 (int)getmntoptnum(mp, "leaseterm"); 352 nfsargsp->flags |= NFSMNT_LEASETERM; 353 } 354 if (altflags & ALTF_READAHEAD) { 355 nfsargsp->readahead = 356 (int)getmntoptnum(mp, "readahead"); 357 nfsargsp->flags |= NFSMNT_READAHEAD; 358 } 359 if (altflags & ALTF_DEADTHRESH) { 360 nfsargsp->deadthresh = 361 (int)getmntoptnum(mp, "deadthresh"); 362 nfsargsp->flags |= NFSMNT_DEADTHRESH; 363 } 364 if (altflags & ALTF_TIMEO) { 365 nfsargsp->timeo = 366 (int)getmntoptnum(mp, "timeo"); 367 nfsargsp->flags |= NFSMNT_TIMEO; 368 } 369 if (altflags & ALTF_RETRANS) { 370 nfsargsp->retrans = 371 (int)getmntoptnum(mp, "retrans"); 372 nfsargsp->flags |= NFSMNT_RETRANS; 373 } 374 altflags = 0; 375 freemntopts(mp); 376 break; 377 case 'P': 378 nfsargsp->flags |= NFSMNT_RESVPORT; 379 break; 380 case 'p': 381 nfsargsp->flags &= ~NFSMNT_RESVPORT; 382 break; 383 case 'R': 384 retrycnt = getnum(optarg, c); 385 break; 386 case 'r': 387 nfsargsp->rsize = getnum(optarg, c); 388 nfsargsp->flags |= NFSMNT_RSIZE; 389 break; 390 case 's': 391 nfsargsp->flags |= NFSMNT_SOFT; 392 break; 393 case 'T': 394 nfsargsp->sotype = SOCK_STREAM; 395 break; 396 case 't': 397 nfsargsp->timeo = getnum(optarg, c); 398 nfsargsp->flags |= NFSMNT_TIMEO; 399 break; 400 case 'w': 401 nfsargsp->wsize = getnum(optarg, c); 402 nfsargsp->flags |= NFSMNT_WSIZE; 403 break; 404 case 'x': 405 nfsargsp->retrans = getnum(optarg, c); 406 nfsargsp->flags |= NFSMNT_RETRANS; 407 break; 408 case 'X': 409 nfsargsp->flags |= NFSMNT_XLATECOOKIE; 410 break; 411 case 'u': 412 nfsargsp->sotype = SOCK_DGRAM; 413 break; 414 case 'U': 415 mnttcp_ok = 0; 416 break; 417 default: 418 usage(); 419 } 420 argc -= optind; 421 argv += optind; 422 423 if (argc != 2) 424 usage(); 425 426 strlcpy(spec, *argv++, MAXPATHLEN); 427 pathadj(*argv, name); 428 mount_nfs_dogetargs(nfsargsp, *mntflags, spec); 429 } 430 431 int 432 mount_nfs(int argc, char *argv[]) 433 { 434 char spec[MAXPATHLEN], name[MAXPATHLEN]; 435 struct nfs_args args; 436 int mntflags; 437 int retval; 438 439 mount_nfs_parseargs(argc, argv, &args, &mntflags, spec, name); 440 441 retry: 442 if ((retval = mount(MOUNT_NFS, name, mntflags, 443 &args, sizeof args)) == -1) { 444 /* Did we just default to v3 on a v2-only kernel? 445 * If so, default to v2 & try again */ 446 if (errno == EPROGMISMATCH && 447 (args.flags & NFSMNT_NFSV3) != 0 && !force3) { 448 /* 449 * fall back to v2. XXX lack of V3 umount. 450 */ 451 args.flags &= ~NFSMNT_NFSV3; 452 mount_nfs_dogetargs(&args, mntflags, spec); 453 goto retry; 454 } 455 } 456 if (retval == -1) 457 err(EXIT_FAILURE, "%s on %s", spec, name); 458 if (mntflags & MNT_GETARGS) { 459 shownfsargs(&args); 460 return EXIT_SUCCESS; 461 } 462 463 exit(EXIT_SUCCESS); 464 } 465 466 static void 467 shownfsargs(const struct nfs_args *nfsargsp) 468 { 469 char fbuf[2048]; 470 char host[NI_MAXHOST], serv[NI_MAXSERV]; 471 int error; 472 473 (void)snprintb(fbuf, sizeof(fbuf), NFSMNT_BITS, 474 (uint64_t)nfsargsp->flags); 475 if (nfsargsp->addr != NULL) { 476 error = getnameinfo(nfsargsp->addr, 477 (socklen_t)nfsargsp->addrlen, host, 478 sizeof(host), serv, sizeof(serv), 479 NI_NUMERICHOST | NI_NUMERICSERV); 480 if (error != 0) 481 warnx("getnameinfo: %s", gai_strerror(error)); 482 } else 483 error = -1; 484 485 if (error == 0) 486 printf("addr=%s, port=%s, addrlen=%d, ", 487 host, serv, nfsargsp->addrlen); 488 printf("sotype=%d, proto=%d, fhsize=%d, " 489 "flags=%s, wsize=%d, rsize=%d, readdirsize=%d, timeo=%d, " 490 "retrans=%d, maxgrouplist=%d, readahead=%d, leaseterm=%d, " 491 "deadthresh=%d\n", 492 nfsargsp->sotype, 493 nfsargsp->proto, 494 nfsargsp->fhsize, 495 fbuf, 496 nfsargsp->wsize, 497 nfsargsp->rsize, 498 nfsargsp->readdirsize, 499 nfsargsp->timeo, 500 nfsargsp->retrans, 501 nfsargsp->maxgrouplist, 502 nfsargsp->readahead, 503 nfsargsp->leaseterm, 504 nfsargsp->deadthresh); 505 } 506 507 static void 508 usage(void) 509 { 510 (void)fprintf(stderr, "Usage: %s %s\n%s\n%s\n%s\n%s\n", getprogname(), 511 "[-23bCcdilPpqsTUuX] [-a maxreadahead] [-D deadthresh]", 512 "\t[-g maxgroups] [-I readdirsize] [-L leaseterm]", 513 "\t[-o options] [-R retrycnt] [-r readsize] [-t timeout]", 514 "\t[-w writesize] [-x retrans]", 515 "\trhost:path node"); 516 exit(EXIT_FAILURE); 517 } 518