1/* $NetBSD: mount_nfs.c,v 1.75 2022/12/21 19:00:52 chs 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 43static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; 44#else 45__RCSID("$NetBSD: mount_nfs.c,v 1.75 2022/12/21 19:00:52 chs 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 105static 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 137struct 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 158static void shownfsargs(const struct nfs_args *); 159int mount_nfs(int argc, char **argv); 160/* void set_rpc_maxgrouplist(int); */ 161__dead static void usage(void); 162 163#ifndef MOUNT_NOMAIN 164int 165main(int argc, char **argv) 166{ 167 168 setprogname(argv[0]); 169 return mount_nfs(argc, argv); 170} 171#endif 172 173void 174mount_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 194static int 195getnum(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 204static __dead void 205conflicting(void) 206{ 207 errx(EXIT_FAILURE, "Conflicting version options"); 208} 209 210void 211mount_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:iKL:lm:o: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 break; 420 } 421 argc -= optind; 422 argv += optind; 423 424 if (argc != 2) 425 usage(); 426 427 strlcpy(spec, *argv++, MAXPATHLEN); 428 pathadj(*argv, name); 429 mount_nfs_dogetargs(nfsargsp, *mntflags, spec); 430} 431 432int 433mount_nfs(int argc, char *argv[]) 434{ 435 char spec[MAXPATHLEN], name[MAXPATHLEN]; 436 struct nfs_args args; 437 int mntflags; 438 int retval; 439 440 mount_nfs_parseargs(argc, argv, &args, &mntflags, spec, name); 441 442 retry: 443 if ((retval = mount(MOUNT_NFS, name, mntflags, 444 &args, sizeof args)) == -1) { 445 /* Did we just default to v3 on a v2-only kernel? 446 * If so, default to v2 & try again */ 447 if (errno == EPROGMISMATCH && 448 (args.flags & NFSMNT_NFSV3) != 0 && !force3) { 449 /* 450 * fall back to v2. XXX lack of V3 umount. 451 */ 452 args.flags &= ~NFSMNT_NFSV3; 453 mount_nfs_dogetargs(&args, mntflags, spec); 454 goto retry; 455 } 456 } 457 if (retval == -1) 458 err(EXIT_FAILURE, "%s on %s", spec, name); 459 if (mntflags & MNT_GETARGS) { 460 shownfsargs(&args); 461 return EXIT_SUCCESS; 462 } 463 464 exit(EXIT_SUCCESS); 465} 466 467static void 468shownfsargs(const struct nfs_args *nfsargsp) 469{ 470 char fbuf[2048]; 471 char host[NI_MAXHOST], serv[NI_MAXSERV]; 472 int error; 473 474 (void)snprintb(fbuf, sizeof(fbuf), NFSMNT_BITS, 475 (uint64_t)nfsargsp->flags); 476 if (nfsargsp->addr != NULL) { 477 error = getnameinfo(nfsargsp->addr, 478 (socklen_t)nfsargsp->addrlen, host, 479 sizeof(host), serv, sizeof(serv), 480 NI_NUMERICHOST | NI_NUMERICSERV); 481 if (error != 0) 482 warnx("getnameinfo: %s", gai_strerror(error)); 483 } else 484 error = -1; 485 486 if (error == 0) 487 printf("addr=%s, port=%s, addrlen=%d, ", 488 host, serv, nfsargsp->addrlen); 489 printf("sotype=%d, proto=%d, fhsize=%d, " 490 "flags=%s, wsize=%d, rsize=%d, readdirsize=%d, timeo=%d, " 491 "retrans=%d, maxgrouplist=%d, readahead=%d, leaseterm=%d, " 492 "deadthresh=%d\n", 493 nfsargsp->sotype, 494 nfsargsp->proto, 495 nfsargsp->fhsize, 496 fbuf, 497 nfsargsp->wsize, 498 nfsargsp->rsize, 499 nfsargsp->readdirsize, 500 nfsargsp->timeo, 501 nfsargsp->retrans, 502 nfsargsp->maxgrouplist, 503 nfsargsp->readahead, 504 nfsargsp->leaseterm, 505 nfsargsp->deadthresh); 506} 507 508static void 509usage(void) 510{ 511 (void)fprintf(stderr, "Usage: %s %s\n%s\n%s\n%s\n%s\n", getprogname(), 512"[-23bCcdilPpqsTUuX] [-a maxreadahead] [-D deadthresh]", 513"\t[-g maxgroups] [-I readdirsize] [-L leaseterm]", 514"\t[-o options] [-R retrycnt] [-r readsize] [-t timeout]", 515"\t[-w writesize] [-x retrans]", 516"\trhost:path node"); 517 exit(EXIT_FAILURE); 518} 519