46 47#include <sys/param.h> 48#include <sys/linker.h> 49#include <sys/module.h> 50#include <sys/mount.h> 51#include <sys/socket.h> 52#include <sys/stat.h> 53#include <sys/syslog.h> 54#include <sys/uio.h> 55 56#include <rpc/rpc.h> 57#include <rpc/pmap_clnt.h> 58#include <rpc/pmap_prot.h> 59#include <rpcsvc/nfs_prot.h> 60#include <rpcsvc/mount.h> 61 62#include <nfsclient/nfs.h> 63 64#include <arpa/inet.h> 65 66#include <ctype.h> 67#include <err.h> 68#include <errno.h> 69#include <fcntl.h> 70#include <netdb.h> 71#include <stdio.h> 72#include <stdlib.h> 73#include <string.h> 74#include <strings.h> 75#include <sysexits.h> 76#include <unistd.h> 77 78#include "mntopts.h" 79#include "mounttab.h" 80 81/* Table for af,sotype -> netid conversions. */ 82struct nc_protos { 83 const char *netid; 84 int af; 85 int sotype; 86} nc_protos[] = { 87 {"udp", AF_INET, SOCK_DGRAM}, 88 {"tcp", AF_INET, SOCK_STREAM}, 89 {"udp6", AF_INET6, SOCK_DGRAM}, 90 {"tcp6", AF_INET6, SOCK_STREAM}, 91 {NULL, 0, 0} 92}; 93 94struct nfhret { 95 u_long stat; 96 long vers; 97 long auth; 98 long fhsize; 99 u_char nfh[NFS3_FHSIZE]; 100}; 101#define BGRND 1 102#define ISBGRND 2 103#define OF_NOINET4 4 104#define OF_NOINET6 8 105int retrycnt = -1; 106int opflags = 0; 107int nfsproto = IPPROTO_TCP; 108int mnttcp_ok = 1; 109int noconn = 0; 110char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 111struct sockaddr *addr; 112int addrlen = 0; 113u_char *fh = NULL; 114int fhsize = 0; 115int secflavor = -1; 116int got_principal = 0; 117 118enum mountmode { 119 ANY, 120 V2, 121 V3, 122 V4 123} mountmode = ANY; 124 125/* Return codes for nfs_tryproto. */ 126enum tryret { 127 TRYRET_SUCCESS, 128 TRYRET_TIMEOUT, /* No response received. */ 129 TRYRET_REMOTEERR, /* Error received from remote server. */ 130 TRYRET_LOCALERR /* Local failure. */ 131}; 132 133static int fallback_mount(struct iovec *iov, int iovlen, int mntflags); 134static int sec_name_to_num(char *sec); 135static char *sec_num_to_name(int num); 136static int getnfsargs(char *, struct iovec **iov, int *iovlen); 137/* void set_rpc_maxgrouplist(int); */ 138static struct netconfig *getnetconf_cached(const char *netid); 139static const char *netidbytype(int af, int sotype); 140static void usage(void) __dead2; 141static int xdr_dir(XDR *, char *); 142static int xdr_fh(XDR *, struct nfhret *); 143static enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 144 char **errstr, struct iovec **iov, int *iovlen); 145static enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 146 147int 148main(int argc, char *argv[]) 149{ 150 int c; 151 struct iovec *iov; 152 int mntflags, num, iovlen; 153 int osversion; 154 char *name, *p, *spec, *fstype; 155 char mntpath[MAXPATHLEN], errmsg[255]; 156 char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50]; 157 158 mntflags = 0; 159 iov = NULL; 160 iovlen = 0; 161 memset(errmsg, 0, sizeof(errmsg)); 162 gssname = NULL; 163 164 fstype = strrchr(argv[0], '_'); 165 if (fstype == NULL) 166 errx(EX_USAGE, "argv[0] must end in _fstype"); 167 168 ++fstype; 169 170 while ((c = getopt(argc, argv, 171 "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 172 switch (c) { 173 case '2': 174 mountmode = V2; 175 break; 176 case '3': 177 mountmode = V3; 178 break; 179 case 'a': 180 printf("-a deprecated, use -o readahead=<value>\n"); 181 build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 182 break; 183 case 'b': 184 opflags |= BGRND; 185 break; 186 case 'c': 187 printf("-c deprecated, use -o noconn\n"); 188 build_iovec(&iov, &iovlen, "noconn", NULL, 0); 189 noconn = 1; 190 break; 191 case 'D': 192 printf("-D deprecated, use -o deadthresh=<value>\n"); 193 build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 194 break; 195 case 'd': 196 printf("-d deprecated, use -o dumbtimer"); 197 build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 198 break; 199 case 'g': 200 printf("-g deprecated, use -o maxgroups"); 201 num = strtol(optarg, &p, 10); 202 if (*p || num <= 0) 203 errx(1, "illegal -g value -- %s", optarg); 204 //set_rpc_maxgrouplist(num); 205 build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 206 break; 207 case 'I': 208 printf("-I deprecated, use -o readdirsize=<value>\n"); 209 build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 210 break; 211 case 'i': 212 printf("-i deprecated, use -o intr\n"); 213 build_iovec(&iov, &iovlen, "intr", NULL, 0); 214 break; 215 case 'L': 216 printf("-L deprecated, use -o nolockd\n"); 217 build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 218 break; 219 case 'l': 220 printf("-l deprecated, -o rdirplus\n"); 221 build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 222 break; 223 case 'N': 224 printf("-N deprecated, do not specify -o resvport\n"); 225 break; 226 case 'o': { 227 int pass_flag_to_nmount; 228 char *opt = optarg; 229 while (opt) { 230 char *pval = NULL; 231 char *pnextopt = NULL; 232 char *val = ""; 233 pass_flag_to_nmount = 1; 234 pnextopt = strchr(opt, ','); 235 if (pnextopt != NULL) { 236 *pnextopt = '\0'; 237 pnextopt++; 238 } 239 pval = strchr(opt, '='); 240 if (pval != NULL) { 241 *pval = '\0'; 242 val = pval + 1; 243 } 244 if (strcmp(opt, "bg") == 0) { 245 opflags |= BGRND; 246 pass_flag_to_nmount=0; 247 } else if (strcmp(opt, "fg") == 0) { 248 /* same as not specifying -o bg */ 249 pass_flag_to_nmount=0; 250 } else if (strcmp(opt, "gssname") == 0) { 251 pass_flag_to_nmount = 0; 252 gssname = val; 253 } else if (strcmp(opt, "mntudp") == 0) { 254 mnttcp_ok = 0; 255 nfsproto = IPPROTO_UDP; 256 } else if (strcmp(opt, "udp") == 0) { 257 nfsproto = IPPROTO_UDP; 258 } else if (strcmp(opt, "tcp") == 0) { 259 nfsproto = IPPROTO_TCP; 260 } else if (strcmp(opt, "noinet4") == 0) { 261 pass_flag_to_nmount=0; 262 opflags |= OF_NOINET4; 263 } else if (strcmp(opt, "noinet6") == 0) { 264 pass_flag_to_nmount=0; 265 opflags |= OF_NOINET6; 266 } else if (strcmp(opt, "noconn") == 0) { 267 noconn = 1; 268 } else if (strcmp(opt, "nfsv2") == 0) { 269 pass_flag_to_nmount=0; 270 mountmode = V2; 271 } else if (strcmp(opt, "nfsv3") == 0) { 272 mountmode = V3; 273 } else if (strcmp(opt, "nfsv4") == 0) { 274 pass_flag_to_nmount=0; 275 mountmode = V4; 276 fstype = "nfs"; 277 nfsproto = IPPROTO_TCP; 278 if (portspec == NULL) 279 portspec = "2049"; 280 } else if (strcmp(opt, "port") == 0) { 281 pass_flag_to_nmount=0; 282 asprintf(&portspec, "%d", 283 atoi(val)); 284 if (portspec == NULL) 285 err(1, "asprintf"); 286 } else if (strcmp(opt, "principal") == 0) { 287 got_principal = 1; 288 } else if (strcmp(opt, "sec") == 0) { 289 /* 290 * Don't add this option to 291 * the iovec yet - we will 292 * negotiate which sec flavor 293 * to use with the remote 294 * mountd. 295 */ 296 pass_flag_to_nmount=0; 297 secflavor = sec_name_to_num(val); 298 if (secflavor < 0) { 299 errx(1, 300 "illegal sec value -- %s", 301 val); 302 } 303 } else if (strcmp(opt, "retrycnt") == 0) { 304 pass_flag_to_nmount=0; 305 num = strtol(val, &p, 10); 306 if (*p || num < 0) 307 errx(1, "illegal retrycnt value -- %s", val); 308 retrycnt = num; 309 } else if (strcmp(opt, "maxgroups") == 0) { 310 num = strtol(val, &p, 10); 311 if (*p || num <= 0) 312 errx(1, "illegal maxgroups value -- %s", val); 313 //set_rpc_maxgrouplist(num); 314 } 315 if (pass_flag_to_nmount) 316 build_iovec(&iov, &iovlen, opt, val, 317 strlen(val) + 1); 318 opt = pnextopt; 319 } 320 } 321 break; 322 case 'P': 323 /* obsolete for -o noresvport now default */ 324 printf("-P deprecated, use -o noresvport\n"); 325 build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 326 break; 327 case 'R': 328 printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 329 num = strtol(optarg, &p, 10); 330 if (*p || num < 0) 331 errx(1, "illegal -R value -- %s", optarg); 332 retrycnt = num; 333 break; 334 case 'r': 335 printf("-r deprecated, use -o rsize=<rsize>\n"); 336 build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 337 break; 338 case 's': 339 printf("-s deprecated, use -o soft\n"); 340 build_iovec(&iov, &iovlen, "soft", NULL, 0); 341 break; 342 case 'T': 343 nfsproto = IPPROTO_TCP; 344 printf("-T deprecated, use -o tcp\n"); 345 break; 346 case 't': 347 printf("-t deprecated, use -o timeout=<value>\n"); 348 build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 349 break; 350 case 'w': 351 printf("-w deprecated, use -o wsize=<value>\n"); 352 build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 353 break; 354 case 'x': 355 printf("-x deprecated, use -o retrans=<value>\n"); 356 build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 357 break; 358 case 'U': 359 printf("-U deprecated, use -o mntudp\n"); 360 mnttcp_ok = 0; 361 nfsproto = IPPROTO_UDP; 362 build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 363 break; 364 default: 365 usage(); 366 break; 367 } 368 argc -= optind; 369 argv += optind; 370 371 if (argc != 2) { 372 usage(); 373 /* NOTREACHED */ 374 } 375 376 spec = *argv++; 377 name = *argv; 378 379 if (retrycnt == -1) 380 /* The default is to keep retrying forever. */ 381 retrycnt = 0; 382 383 /* 384 * If the fstye is "oldnfs", run the old NFS client unless the 385 * "nfsv4" option was specified. 386 */ 387 if (strcmp(fstype, "nfs") == 0) { 388 if (modfind("nfscl") < 0) { 389 /* Not present in kernel, try loading it */ 390 if (kldload("nfscl") < 0 || 391 modfind("nfscl") < 0) 392 errx(1, "nfscl is not available"); 393 } 394 } 395 396 /* 397 * Add the fqdn to the gssname, as required. 398 */ 399 if (gssname != NULL) { 400 if (strchr(gssname, '@') == NULL && 401 gethostname(hostname, MAXHOSTNAMELEN) == 0) { 402 snprintf(gssn, sizeof (gssn), "%s@%s", gssname, 403 hostname); 404 gssname = gssn; 405 } 406 build_iovec(&iov, &iovlen, "gssname", gssname, 407 strlen(gssname) + 1); 408 } 409 410 if (!getnfsargs(spec, &iov, &iovlen)) 411 exit(1); 412 413 /* resolve the mountpoint with realpath(3) */
| 46 47#include <sys/param.h> 48#include <sys/linker.h> 49#include <sys/module.h> 50#include <sys/mount.h> 51#include <sys/socket.h> 52#include <sys/stat.h> 53#include <sys/syslog.h> 54#include <sys/uio.h> 55 56#include <rpc/rpc.h> 57#include <rpc/pmap_clnt.h> 58#include <rpc/pmap_prot.h> 59#include <rpcsvc/nfs_prot.h> 60#include <rpcsvc/mount.h> 61 62#include <nfsclient/nfs.h> 63 64#include <arpa/inet.h> 65 66#include <ctype.h> 67#include <err.h> 68#include <errno.h> 69#include <fcntl.h> 70#include <netdb.h> 71#include <stdio.h> 72#include <stdlib.h> 73#include <string.h> 74#include <strings.h> 75#include <sysexits.h> 76#include <unistd.h> 77 78#include "mntopts.h" 79#include "mounttab.h" 80 81/* Table for af,sotype -> netid conversions. */ 82struct nc_protos { 83 const char *netid; 84 int af; 85 int sotype; 86} nc_protos[] = { 87 {"udp", AF_INET, SOCK_DGRAM}, 88 {"tcp", AF_INET, SOCK_STREAM}, 89 {"udp6", AF_INET6, SOCK_DGRAM}, 90 {"tcp6", AF_INET6, SOCK_STREAM}, 91 {NULL, 0, 0} 92}; 93 94struct nfhret { 95 u_long stat; 96 long vers; 97 long auth; 98 long fhsize; 99 u_char nfh[NFS3_FHSIZE]; 100}; 101#define BGRND 1 102#define ISBGRND 2 103#define OF_NOINET4 4 104#define OF_NOINET6 8 105int retrycnt = -1; 106int opflags = 0; 107int nfsproto = IPPROTO_TCP; 108int mnttcp_ok = 1; 109int noconn = 0; 110char *portspec = NULL; /* Server nfs port; NULL means look up via rpcbind. */ 111struct sockaddr *addr; 112int addrlen = 0; 113u_char *fh = NULL; 114int fhsize = 0; 115int secflavor = -1; 116int got_principal = 0; 117 118enum mountmode { 119 ANY, 120 V2, 121 V3, 122 V4 123} mountmode = ANY; 124 125/* Return codes for nfs_tryproto. */ 126enum tryret { 127 TRYRET_SUCCESS, 128 TRYRET_TIMEOUT, /* No response received. */ 129 TRYRET_REMOTEERR, /* Error received from remote server. */ 130 TRYRET_LOCALERR /* Local failure. */ 131}; 132 133static int fallback_mount(struct iovec *iov, int iovlen, int mntflags); 134static int sec_name_to_num(char *sec); 135static char *sec_num_to_name(int num); 136static int getnfsargs(char *, struct iovec **iov, int *iovlen); 137/* void set_rpc_maxgrouplist(int); */ 138static struct netconfig *getnetconf_cached(const char *netid); 139static const char *netidbytype(int af, int sotype); 140static void usage(void) __dead2; 141static int xdr_dir(XDR *, char *); 142static int xdr_fh(XDR *, struct nfhret *); 143static enum tryret nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, 144 char **errstr, struct iovec **iov, int *iovlen); 145static enum tryret returncode(enum clnt_stat stat, struct rpc_err *rpcerr); 146 147int 148main(int argc, char *argv[]) 149{ 150 int c; 151 struct iovec *iov; 152 int mntflags, num, iovlen; 153 int osversion; 154 char *name, *p, *spec, *fstype; 155 char mntpath[MAXPATHLEN], errmsg[255]; 156 char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50]; 157 158 mntflags = 0; 159 iov = NULL; 160 iovlen = 0; 161 memset(errmsg, 0, sizeof(errmsg)); 162 gssname = NULL; 163 164 fstype = strrchr(argv[0], '_'); 165 if (fstype == NULL) 166 errx(EX_USAGE, "argv[0] must end in _fstype"); 167 168 ++fstype; 169 170 while ((c = getopt(argc, argv, 171 "23a:bcdD:g:I:iLlNo:PR:r:sTt:w:x:U")) != -1) 172 switch (c) { 173 case '2': 174 mountmode = V2; 175 break; 176 case '3': 177 mountmode = V3; 178 break; 179 case 'a': 180 printf("-a deprecated, use -o readahead=<value>\n"); 181 build_iovec(&iov, &iovlen, "readahead", optarg, (size_t)-1); 182 break; 183 case 'b': 184 opflags |= BGRND; 185 break; 186 case 'c': 187 printf("-c deprecated, use -o noconn\n"); 188 build_iovec(&iov, &iovlen, "noconn", NULL, 0); 189 noconn = 1; 190 break; 191 case 'D': 192 printf("-D deprecated, use -o deadthresh=<value>\n"); 193 build_iovec(&iov, &iovlen, "deadthresh", optarg, (size_t)-1); 194 break; 195 case 'd': 196 printf("-d deprecated, use -o dumbtimer"); 197 build_iovec(&iov, &iovlen, "dumbtimer", NULL, 0); 198 break; 199 case 'g': 200 printf("-g deprecated, use -o maxgroups"); 201 num = strtol(optarg, &p, 10); 202 if (*p || num <= 0) 203 errx(1, "illegal -g value -- %s", optarg); 204 //set_rpc_maxgrouplist(num); 205 build_iovec(&iov, &iovlen, "maxgroups", optarg, (size_t)-1); 206 break; 207 case 'I': 208 printf("-I deprecated, use -o readdirsize=<value>\n"); 209 build_iovec(&iov, &iovlen, "readdirsize", optarg, (size_t)-1); 210 break; 211 case 'i': 212 printf("-i deprecated, use -o intr\n"); 213 build_iovec(&iov, &iovlen, "intr", NULL, 0); 214 break; 215 case 'L': 216 printf("-L deprecated, use -o nolockd\n"); 217 build_iovec(&iov, &iovlen, "nolockd", NULL, 0); 218 break; 219 case 'l': 220 printf("-l deprecated, -o rdirplus\n"); 221 build_iovec(&iov, &iovlen, "rdirplus", NULL, 0); 222 break; 223 case 'N': 224 printf("-N deprecated, do not specify -o resvport\n"); 225 break; 226 case 'o': { 227 int pass_flag_to_nmount; 228 char *opt = optarg; 229 while (opt) { 230 char *pval = NULL; 231 char *pnextopt = NULL; 232 char *val = ""; 233 pass_flag_to_nmount = 1; 234 pnextopt = strchr(opt, ','); 235 if (pnextopt != NULL) { 236 *pnextopt = '\0'; 237 pnextopt++; 238 } 239 pval = strchr(opt, '='); 240 if (pval != NULL) { 241 *pval = '\0'; 242 val = pval + 1; 243 } 244 if (strcmp(opt, "bg") == 0) { 245 opflags |= BGRND; 246 pass_flag_to_nmount=0; 247 } else if (strcmp(opt, "fg") == 0) { 248 /* same as not specifying -o bg */ 249 pass_flag_to_nmount=0; 250 } else if (strcmp(opt, "gssname") == 0) { 251 pass_flag_to_nmount = 0; 252 gssname = val; 253 } else if (strcmp(opt, "mntudp") == 0) { 254 mnttcp_ok = 0; 255 nfsproto = IPPROTO_UDP; 256 } else if (strcmp(opt, "udp") == 0) { 257 nfsproto = IPPROTO_UDP; 258 } else if (strcmp(opt, "tcp") == 0) { 259 nfsproto = IPPROTO_TCP; 260 } else if (strcmp(opt, "noinet4") == 0) { 261 pass_flag_to_nmount=0; 262 opflags |= OF_NOINET4; 263 } else if (strcmp(opt, "noinet6") == 0) { 264 pass_flag_to_nmount=0; 265 opflags |= OF_NOINET6; 266 } else if (strcmp(opt, "noconn") == 0) { 267 noconn = 1; 268 } else if (strcmp(opt, "nfsv2") == 0) { 269 pass_flag_to_nmount=0; 270 mountmode = V2; 271 } else if (strcmp(opt, "nfsv3") == 0) { 272 mountmode = V3; 273 } else if (strcmp(opt, "nfsv4") == 0) { 274 pass_flag_to_nmount=0; 275 mountmode = V4; 276 fstype = "nfs"; 277 nfsproto = IPPROTO_TCP; 278 if (portspec == NULL) 279 portspec = "2049"; 280 } else if (strcmp(opt, "port") == 0) { 281 pass_flag_to_nmount=0; 282 asprintf(&portspec, "%d", 283 atoi(val)); 284 if (portspec == NULL) 285 err(1, "asprintf"); 286 } else if (strcmp(opt, "principal") == 0) { 287 got_principal = 1; 288 } else if (strcmp(opt, "sec") == 0) { 289 /* 290 * Don't add this option to 291 * the iovec yet - we will 292 * negotiate which sec flavor 293 * to use with the remote 294 * mountd. 295 */ 296 pass_flag_to_nmount=0; 297 secflavor = sec_name_to_num(val); 298 if (secflavor < 0) { 299 errx(1, 300 "illegal sec value -- %s", 301 val); 302 } 303 } else if (strcmp(opt, "retrycnt") == 0) { 304 pass_flag_to_nmount=0; 305 num = strtol(val, &p, 10); 306 if (*p || num < 0) 307 errx(1, "illegal retrycnt value -- %s", val); 308 retrycnt = num; 309 } else if (strcmp(opt, "maxgroups") == 0) { 310 num = strtol(val, &p, 10); 311 if (*p || num <= 0) 312 errx(1, "illegal maxgroups value -- %s", val); 313 //set_rpc_maxgrouplist(num); 314 } 315 if (pass_flag_to_nmount) 316 build_iovec(&iov, &iovlen, opt, val, 317 strlen(val) + 1); 318 opt = pnextopt; 319 } 320 } 321 break; 322 case 'P': 323 /* obsolete for -o noresvport now default */ 324 printf("-P deprecated, use -o noresvport\n"); 325 build_iovec(&iov, &iovlen, "noresvport", NULL, 0); 326 break; 327 case 'R': 328 printf("-R deprecated, use -o retrycnt=<retrycnt>\n"); 329 num = strtol(optarg, &p, 10); 330 if (*p || num < 0) 331 errx(1, "illegal -R value -- %s", optarg); 332 retrycnt = num; 333 break; 334 case 'r': 335 printf("-r deprecated, use -o rsize=<rsize>\n"); 336 build_iovec(&iov, &iovlen, "rsize", optarg, (size_t)-1); 337 break; 338 case 's': 339 printf("-s deprecated, use -o soft\n"); 340 build_iovec(&iov, &iovlen, "soft", NULL, 0); 341 break; 342 case 'T': 343 nfsproto = IPPROTO_TCP; 344 printf("-T deprecated, use -o tcp\n"); 345 break; 346 case 't': 347 printf("-t deprecated, use -o timeout=<value>\n"); 348 build_iovec(&iov, &iovlen, "timeout", optarg, (size_t)-1); 349 break; 350 case 'w': 351 printf("-w deprecated, use -o wsize=<value>\n"); 352 build_iovec(&iov, &iovlen, "wsize", optarg, (size_t)-1); 353 break; 354 case 'x': 355 printf("-x deprecated, use -o retrans=<value>\n"); 356 build_iovec(&iov, &iovlen, "retrans", optarg, (size_t)-1); 357 break; 358 case 'U': 359 printf("-U deprecated, use -o mntudp\n"); 360 mnttcp_ok = 0; 361 nfsproto = IPPROTO_UDP; 362 build_iovec(&iov, &iovlen, "mntudp", NULL, 0); 363 break; 364 default: 365 usage(); 366 break; 367 } 368 argc -= optind; 369 argv += optind; 370 371 if (argc != 2) { 372 usage(); 373 /* NOTREACHED */ 374 } 375 376 spec = *argv++; 377 name = *argv; 378 379 if (retrycnt == -1) 380 /* The default is to keep retrying forever. */ 381 retrycnt = 0; 382 383 /* 384 * If the fstye is "oldnfs", run the old NFS client unless the 385 * "nfsv4" option was specified. 386 */ 387 if (strcmp(fstype, "nfs") == 0) { 388 if (modfind("nfscl") < 0) { 389 /* Not present in kernel, try loading it */ 390 if (kldload("nfscl") < 0 || 391 modfind("nfscl") < 0) 392 errx(1, "nfscl is not available"); 393 } 394 } 395 396 /* 397 * Add the fqdn to the gssname, as required. 398 */ 399 if (gssname != NULL) { 400 if (strchr(gssname, '@') == NULL && 401 gethostname(hostname, MAXHOSTNAMELEN) == 0) { 402 snprintf(gssn, sizeof (gssn), "%s@%s", gssname, 403 hostname); 404 gssname = gssn; 405 } 406 build_iovec(&iov, &iovlen, "gssname", gssname, 407 strlen(gssname) + 1); 408 } 409 410 if (!getnfsargs(spec, &iov, &iovlen)) 411 exit(1); 412 413 /* resolve the mountpoint with realpath(3) */
|
415 416 build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 417 build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 418 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 419 420 /* 421 * XXX: 422 * Backwards compatibility routines for older kernels. 423 * Remove this and fallback_mount() code when we do not need to support 424 * NFS mounts against older kernels which still need 425 * struct nfs_args to be passed in via nmount(). 426 */ 427 osversion = getosreldate(); 428 if (osversion >= 702100) { 429 if (nmount(iov, iovlen, mntflags)) 430 err(1, "%s, %s", mntpath, errmsg); 431 } else { 432 if (fallback_mount(iov, iovlen, mntflags)) 433 err(1, "%s, %s", mntpath, errmsg); 434 } 435 436 exit(0); 437} 438 439static int 440findopt(struct iovec *iov, int iovlen, const char *name, 441 char **valuep, int *lenp) 442{ 443 int i; 444 445 for (i = 0; i < iovlen/2; i++, iov += 2) { 446 if (strcmp(name, iov[0].iov_base) == 0) { 447 if (valuep) 448 *valuep = iov[1].iov_base; 449 if (lenp) 450 *lenp = iov[1].iov_len; 451 return (0); 452 } 453 } 454 return (ENOENT); 455} 456 457static void 458copyopt(struct iovec **newiov, int *newiovlen, 459 struct iovec *iov, int iovlen, const char *name) 460{ 461 char *value; 462 int len; 463 464 if (findopt(iov, iovlen, name, &value, &len) == 0) 465 build_iovec(newiov, newiovlen, name, value, len); 466} 467 468/* 469 * XXX: This function is provided for backwards 470 * compatibility with older kernels which did not support 471 * passing NFS mount options to nmount() as individual 472 * parameters. It should be eventually be removed. 473 */ 474static int 475fallback_mount(struct iovec *iov, int iovlen, int mntflags) 476{ 477 struct nfs_args args = { 478 .version = NFS_ARGSVERSION, 479 .addr = NULL, 480 .addrlen = sizeof (struct sockaddr_in), 481 .sotype = SOCK_STREAM, 482 .proto = 0, 483 .fh = NULL, 484 .fhsize = 0, 485 .flags = NFSMNT_RESVPORT, 486 .wsize = NFS_WSIZE, 487 .rsize = NFS_RSIZE, 488 .readdirsize = NFS_READDIRSIZE, 489 .timeo = 10, 490 .retrans = NFS_RETRANS, 491 .maxgrouplist = NFS_MAXGRPS, 492 .readahead = NFS_DEFRAHEAD, 493 .wcommitsize = 0, /* was: NQ_DEFLEASE */ 494 .deadthresh = NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 495 .hostname = NULL, 496 /* args version 4 */ 497 .acregmin = NFS_MINATTRTIMO, 498 .acregmax = NFS_MAXATTRTIMO, 499 .acdirmin = NFS_MINDIRATTRTIMO, 500 .acdirmax = NFS_MAXDIRATTRTIMO, 501 }; 502 int ret; 503 char *opt; 504 struct iovec *newiov; 505 int newiovlen; 506 507 if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0) 508 args.flags |= NFSMNT_DUMBTIMR; 509 if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0) 510 args.flags |= NFSMNT_NOCONN; 511 if (findopt(iov, iovlen, "conn", NULL, NULL) == 0) 512 args.flags |= NFSMNT_NOCONN; 513 if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0) 514 args.flags |= NFSMNT_NOLOCKD; 515 if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0) 516 args.flags &= ~NFSMNT_NOLOCKD; 517 if (findopt(iov, iovlen, "intr", NULL, NULL) == 0) 518 args.flags |= NFSMNT_INT; 519 if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0) 520 args.flags |= NFSMNT_RDIRPLUS; 521 if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0) 522 args.flags |= NFSMNT_RESVPORT; 523 if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0) 524 args.flags &= ~NFSMNT_RESVPORT; 525 if (findopt(iov, iovlen, "soft", NULL, NULL) == 0) 526 args.flags |= NFSMNT_SOFT; 527 if (findopt(iov, iovlen, "hard", NULL, NULL) == 0) 528 args.flags &= ~NFSMNT_SOFT; 529 if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0) 530 args.sotype = SOCK_DGRAM; 531 if (findopt(iov, iovlen, "udp", NULL, NULL) == 0) 532 args.sotype = SOCK_DGRAM; 533 if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0) 534 args.sotype = SOCK_STREAM; 535 if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0) 536 args.flags |= NFSMNT_NFSV3; 537 if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) { 538 if (opt == NULL) { 539 errx(1, "illegal readdirsize"); 540 } 541 ret = sscanf(opt, "%d", &args.readdirsize); 542 if (ret != 1 || args.readdirsize <= 0) { 543 errx(1, "illegal readdirsize: %s", opt); 544 } 545 args.flags |= NFSMNT_READDIRSIZE; 546 } 547 if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) { 548 if (opt == NULL) { 549 errx(1, "illegal readahead"); 550 } 551 ret = sscanf(opt, "%d", &args.readahead); 552 if (ret != 1 || args.readahead <= 0) { 553 errx(1, "illegal readahead: %s", opt); 554 } 555 args.flags |= NFSMNT_READAHEAD; 556 } 557 if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) { 558 if (opt == NULL) { 559 errx(1, "illegal wsize"); 560 } 561 ret = sscanf(opt, "%d", &args.wsize); 562 if (ret != 1 || args.wsize <= 0) { 563 errx(1, "illegal wsize: %s", opt); 564 } 565 args.flags |= NFSMNT_WSIZE; 566 } 567 if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) { 568 if (opt == NULL) { 569 errx(1, "illegal rsize"); 570 } 571 ret = sscanf(opt, "%d", &args.rsize); 572 if (ret != 1 || args.rsize <= 0) { 573 errx(1, "illegal wsize: %s", opt); 574 } 575 args.flags |= NFSMNT_RSIZE; 576 } 577 if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) { 578 if (opt == NULL) { 579 errx(1, "illegal retrans"); 580 } 581 ret = sscanf(opt, "%d", &args.retrans); 582 if (ret != 1 || args.retrans <= 0) { 583 errx(1, "illegal retrans: %s", opt); 584 } 585 args.flags |= NFSMNT_RETRANS; 586 } 587 if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) { 588 ret = sscanf(opt, "%d", &args.acregmin); 589 if (ret != 1 || args.acregmin < 0) { 590 errx(1, "illegal acregmin: %s", opt); 591 } 592 args.flags |= NFSMNT_ACREGMIN; 593 } 594 if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) { 595 ret = sscanf(opt, "%d", &args.acregmax); 596 if (ret != 1 || args.acregmax < 0) { 597 errx(1, "illegal acregmax: %s", opt); 598 } 599 args.flags |= NFSMNT_ACREGMAX; 600 } 601 if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) { 602 ret = sscanf(opt, "%d", &args.acdirmin); 603 if (ret != 1 || args.acdirmin < 0) { 604 errx(1, "illegal acdirmin: %s", opt); 605 } 606 args.flags |= NFSMNT_ACDIRMIN; 607 } 608 if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) { 609 ret = sscanf(opt, "%d", &args.acdirmax); 610 if (ret != 1 || args.acdirmax < 0) { 611 errx(1, "illegal acdirmax: %s", opt); 612 } 613 args.flags |= NFSMNT_ACDIRMAX; 614 } 615 if (findopt(iov, iovlen, "wcommitsize", &opt, NULL) == 0) { 616 ret = sscanf(opt, "%d", &args.wcommitsize); 617 if (ret != 1 || args.wcommitsize < 0) { 618 errx(1, "illegal wcommitsize: %s", opt); 619 } 620 args.flags |= NFSMNT_WCOMMITSIZE; 621 } 622 if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) { 623 ret = sscanf(opt, "%d", &args.deadthresh); 624 if (ret != 1 || args.deadthresh <= 0) { 625 errx(1, "illegal deadthresh: %s", opt); 626 } 627 args.flags |= NFSMNT_DEADTHRESH; 628 } 629 if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) { 630 ret = sscanf(opt, "%d", &args.timeo); 631 if (ret != 1 || args.timeo <= 0) { 632 errx(1, "illegal timeout: %s", opt); 633 } 634 args.flags |= NFSMNT_TIMEO; 635 } 636 if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) { 637 ret = sscanf(opt, "%d", &args.maxgrouplist); 638 if (ret != 1 || args.timeo <= 0) { 639 errx(1, "illegal maxgroups: %s", opt); 640 } 641 args.flags |= NFSMNT_MAXGRPS; 642 } 643 if (findopt(iov, iovlen, "addr", &opt, 644 &args.addrlen) == 0) { 645 args.addr = (struct sockaddr *) opt; 646 } 647 if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) { 648 args.fh = opt; 649 } 650 if (findopt(iov, iovlen, "hostname", &args.hostname, 651 NULL) == 0) { 652 } 653 if (args.hostname == NULL) { 654 errx(1, "Invalid hostname"); 655 } 656 657 newiov = NULL; 658 newiovlen = 0; 659 660 build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args)); 661 copyopt(&newiov, &newiovlen, iov, iovlen, "fstype"); 662 copyopt(&newiov, &newiovlen, iov, iovlen, "fspath"); 663 copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg"); 664 665 return nmount(newiov, newiovlen, mntflags); 666} 667 668static int 669sec_name_to_num(char *sec) 670{ 671 if (!strcmp(sec, "krb5")) 672 return (RPCSEC_GSS_KRB5); 673 if (!strcmp(sec, "krb5i")) 674 return (RPCSEC_GSS_KRB5I); 675 if (!strcmp(sec, "krb5p")) 676 return (RPCSEC_GSS_KRB5P); 677 if (!strcmp(sec, "sys")) 678 return (AUTH_SYS); 679 return (-1); 680} 681 682static char * 683sec_num_to_name(int flavor) 684{ 685 switch (flavor) { 686 case RPCSEC_GSS_KRB5: 687 return ("krb5"); 688 case RPCSEC_GSS_KRB5I: 689 return ("krb5i"); 690 case RPCSEC_GSS_KRB5P: 691 return ("krb5p"); 692 case AUTH_SYS: 693 return ("sys"); 694 } 695 return (NULL); 696} 697 698static int 699getnfsargs(char *spec, struct iovec **iov, int *iovlen) 700{ 701 struct addrinfo hints, *ai_nfs, *ai; 702 enum tryret ret; 703 int ecode, speclen, remoteerr, offset, have_bracket = 0; 704 char *hostp, *delimp, *errstr; 705 size_t len; 706 static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 707 708 if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 709 *(delimp + 1) == ':') { 710 hostp = spec + 1; 711 spec = delimp + 2; 712 have_bracket = 1; 713 } else if ((delimp = strrchr(spec, ':')) != NULL) { 714 hostp = spec; 715 spec = delimp + 1; 716 } else if ((delimp = strrchr(spec, '@')) != NULL) { 717 warnx("path@server syntax is deprecated, use server:path"); 718 hostp = delimp + 1; 719 } else { 720 warnx("no <host>:<dirpath> nfs-name"); 721 return (0); 722 } 723 *delimp = '\0'; 724 725 /* 726 * If there has been a trailing slash at mounttime it seems 727 * that some mountd implementations fail to remove the mount 728 * entries from their mountlist while unmounting. 729 */ 730 for (speclen = strlen(spec); 731 speclen > 1 && spec[speclen - 1] == '/'; 732 speclen--) 733 spec[speclen - 1] = '\0'; 734 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 735 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 736 return (0); 737 } 738 /* Make both '@' and ':' notations equal */ 739 if (*hostp != '\0') { 740 len = strlen(hostp); 741 offset = 0; 742 if (have_bracket) 743 nam[offset++] = '['; 744 memmove(nam + offset, hostp, len); 745 if (have_bracket) 746 nam[len + offset++] = ']'; 747 nam[len + offset++] = ':'; 748 memmove(nam + len + offset, spec, speclen); 749 nam[len + speclen + offset] = '\0'; 750 } 751 752 /* 753 * Handle an internet host address. 754 */ 755 memset(&hints, 0, sizeof hints); 756 hints.ai_flags = AI_NUMERICHOST; 757 if (nfsproto == IPPROTO_TCP) 758 hints.ai_socktype = SOCK_STREAM; 759 else if (nfsproto == IPPROTO_UDP) 760 hints.ai_socktype = SOCK_DGRAM; 761 762 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 763 hints.ai_flags = AI_CANONNAME; 764 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 765 != 0) { 766 if (portspec == NULL) 767 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 768 else 769 errx(1, "%s:%s: %s", hostp, portspec, 770 gai_strerror(ecode)); 771 return (0); 772 } 773 774 /* 775 * For a Kerberized nfs mount where the "principal" 776 * argument has not been set, add it here. 777 */ 778 if (got_principal == 0 && secflavor >= 0 && 779 secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) { 780 snprintf(pname, sizeof (pname), "nfs@%s", 781 ai_nfs->ai_canonname); 782 build_iovec(iov, iovlen, "principal", pname, 783 strlen(pname) + 1); 784 } 785 } 786 787 ret = TRYRET_LOCALERR; 788 for (;;) { 789 /* 790 * Try each entry returned by getaddrinfo(). Note the 791 * occurrence of remote errors by setting `remoteerr'. 792 */ 793 remoteerr = 0; 794 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 795 if ((ai->ai_family == AF_INET6) && 796 (opflags & OF_NOINET6)) 797 continue; 798 if ((ai->ai_family == AF_INET) && 799 (opflags & OF_NOINET4)) 800 continue; 801 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 802 iovlen); 803 if (ret == TRYRET_SUCCESS) 804 break; 805 if (ret != TRYRET_LOCALERR) 806 remoteerr = 1; 807 if ((opflags & ISBGRND) == 0) 808 fprintf(stderr, "%s\n", errstr); 809 } 810 if (ret == TRYRET_SUCCESS) 811 break; 812 813 /* Exit if all errors were local. */ 814 if (!remoteerr) 815 exit(1); 816 817 /* 818 * If retrycnt == 0, we are to keep retrying forever. 819 * Otherwise decrement it, and exit if it hits zero. 820 */ 821 if (retrycnt != 0 && --retrycnt == 0) 822 exit(1); 823 824 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 825 warnx("Cannot immediately mount %s:%s, backgrounding", 826 hostp, spec); 827 opflags |= ISBGRND; 828 if (daemon(0, 0) != 0) 829 err(1, "daemon"); 830 } 831 sleep(60); 832 } 833 freeaddrinfo(ai_nfs); 834 835 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 836 /* Add mounted file system to PATH_MOUNTTAB */ 837 if (!add_mtab(hostp, spec)) 838 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 839 return (1); 840} 841 842/* 843 * Try to set up the NFS arguments according to the address 844 * family, protocol (and possibly port) specified in `ai'. 845 * 846 * Returns TRYRET_SUCCESS if successful, or: 847 * TRYRET_TIMEOUT The server did not respond. 848 * TRYRET_REMOTEERR The server reported an error. 849 * TRYRET_LOCALERR Local failure. 850 * 851 * In all error cases, *errstr will be set to a statically-allocated string 852 * describing the error. 853 */ 854static enum tryret 855nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 856 struct iovec **iov, int *iovlen) 857{ 858 static char errbuf[256]; 859 struct sockaddr_storage nfs_ss; 860 struct netbuf nfs_nb; 861 struct nfhret nfhret; 862 struct timeval try; 863 struct rpc_err rpcerr; 864 CLIENT *clp; 865 struct netconfig *nconf, *nconf_mnt; 866 const char *netid, *netid_mnt; 867 char *secname; 868 int doconnect, nfsvers, mntvers, sotype; 869 enum clnt_stat stat; 870 enum mountmode trymntmode; 871 872 sotype = 0; 873 trymntmode = mountmode; 874 errbuf[0] = '\0'; 875 *errstr = errbuf; 876 877 if (nfsproto == IPPROTO_TCP) 878 sotype = SOCK_STREAM; 879 else if (nfsproto == IPPROTO_UDP) 880 sotype = SOCK_DGRAM; 881 882 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 883 snprintf(errbuf, sizeof errbuf, 884 "af %d sotype %d not supported", ai->ai_family, sotype); 885 return (TRYRET_LOCALERR); 886 } 887 if ((nconf = getnetconf_cached(netid)) == NULL) { 888 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 889 return (TRYRET_LOCALERR); 890 } 891 /* The RPCPROG_MNT netid may be different. */ 892 if (mnttcp_ok) { 893 netid_mnt = netid; 894 nconf_mnt = nconf; 895 } else { 896 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 897 == NULL) { 898 snprintf(errbuf, sizeof errbuf, 899 "af %d sotype SOCK_DGRAM not supported", 900 ai->ai_family); 901 return (TRYRET_LOCALERR); 902 } 903 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 904 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 905 nc_sperror()); 906 return (TRYRET_LOCALERR); 907 } 908 } 909 910tryagain: 911 if (trymntmode == V4) { 912 nfsvers = 4; 913 } else if (trymntmode == V2) { 914 nfsvers = 2; 915 mntvers = 1; 916 } else { 917 nfsvers = 3; 918 mntvers = 3; 919 } 920 921 if (portspec != NULL) { 922 /* `ai' contains the complete nfsd sockaddr. */ 923 nfs_nb.buf = ai->ai_addr; 924 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 925 } else { 926 /* Ask the remote rpcbind. */ 927 nfs_nb.buf = &nfs_ss; 928 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 929 930 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 931 hostp)) { 932 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 933 trymntmode == ANY) { 934 trymntmode = V2; 935 goto tryagain; 936 } 937 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 938 netid, hostp, spec, 939 clnt_spcreateerror("RPCPROG_NFS")); 940 return (returncode(rpc_createerr.cf_stat, 941 &rpc_createerr.cf_error)); 942 } 943 } 944 945 /* Check that the server (nfsd) responds on the port we have chosen. */ 946 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 947 0, 0); 948 if (clp == NULL) { 949 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 950 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 951 return (returncode(rpc_createerr.cf_stat, 952 &rpc_createerr.cf_error)); 953 } 954 if (sotype == SOCK_DGRAM && noconn == 0) { 955 /* 956 * Use connect(), to match what the kernel does. This 957 * catches cases where the server responds from the 958 * wrong source address. 959 */ 960 doconnect = 1; 961 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 962 clnt_destroy(clp); 963 snprintf(errbuf, sizeof errbuf, 964 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 965 spec); 966 return (TRYRET_LOCALERR); 967 } 968 } 969 970 try.tv_sec = 10; 971 try.tv_usec = 0; 972 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 973 (xdrproc_t)xdr_void, NULL, try); 974 if (stat != RPC_SUCCESS) { 975 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 976 clnt_destroy(clp); 977 trymntmode = V2; 978 goto tryagain; 979 } 980 clnt_geterr(clp, &rpcerr); 981 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 982 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 983 clnt_destroy(clp); 984 return (returncode(stat, &rpcerr)); 985 } 986 clnt_destroy(clp); 987 988 /* 989 * For NFSv4, there is no mount protocol. 990 */ 991 if (trymntmode == V4) { 992 /* 993 * Store the server address in nfsargsp, making 994 * sure to copy any locally allocated structures. 995 */ 996 addrlen = nfs_nb.len; 997 addr = malloc(addrlen); 998 if (addr == NULL) 999 err(1, "malloc"); 1000 bcopy(nfs_nb.buf, addr, addrlen); 1001 1002 build_iovec(iov, iovlen, "addr", addr, addrlen); 1003 secname = sec_num_to_name(secflavor); 1004 if (secname != NULL) 1005 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1006 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 1007 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 1008 1009 return (TRYRET_SUCCESS); 1010 } 1011 1012 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 1013 try.tv_sec = 10; 1014 try.tv_usec = 0; 1015 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 1016 if (clp == NULL) { 1017 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1018 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 1019 return (returncode(rpc_createerr.cf_stat, 1020 &rpc_createerr.cf_error)); 1021 } 1022 clp->cl_auth = authsys_create_default(); 1023 nfhret.auth = secflavor; 1024 nfhret.vers = mntvers; 1025 stat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 1026 (xdrproc_t)xdr_fh, &nfhret, 1027 try); 1028 auth_destroy(clp->cl_auth); 1029 if (stat != RPC_SUCCESS) { 1030 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1031 clnt_destroy(clp); 1032 trymntmode = V2; 1033 goto tryagain; 1034 } 1035 clnt_geterr(clp, &rpcerr); 1036 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1037 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 1038 clnt_destroy(clp); 1039 return (returncode(stat, &rpcerr)); 1040 } 1041 clnt_destroy(clp); 1042 1043 if (nfhret.stat != 0) { 1044 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1045 hostp, spec, strerror(nfhret.stat)); 1046 return (TRYRET_REMOTEERR); 1047 } 1048 1049 /* 1050 * Store the filehandle and server address in nfsargsp, making 1051 * sure to copy any locally allocated structures. 1052 */ 1053 addrlen = nfs_nb.len; 1054 addr = malloc(addrlen); 1055 fhsize = nfhret.fhsize; 1056 fh = malloc(fhsize); 1057 if (addr == NULL || fh == NULL) 1058 err(1, "malloc"); 1059 bcopy(nfs_nb.buf, addr, addrlen); 1060 bcopy(nfhret.nfh, fh, fhsize); 1061 1062 build_iovec(iov, iovlen, "addr", addr, addrlen); 1063 build_iovec(iov, iovlen, "fh", fh, fhsize); 1064 secname = sec_num_to_name(nfhret.auth); 1065 if (secname) 1066 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1067 if (nfsvers == 3) 1068 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1069 1070 return (TRYRET_SUCCESS); 1071} 1072 1073/* 1074 * Catagorise a RPC return status and error into an `enum tryret' 1075 * return code. 1076 */ 1077static enum tryret 1078returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1079{ 1080 switch (stat) { 1081 case RPC_TIMEDOUT: 1082 return (TRYRET_TIMEOUT); 1083 case RPC_PMAPFAILURE: 1084 case RPC_PROGNOTREGISTERED: 1085 case RPC_PROGVERSMISMATCH: 1086 /* XXX, these can be local or remote. */ 1087 case RPC_CANTSEND: 1088 case RPC_CANTRECV: 1089 return (TRYRET_REMOTEERR); 1090 case RPC_SYSTEMERROR: 1091 switch (rpcerr->re_errno) { 1092 case ETIMEDOUT: 1093 return (TRYRET_TIMEOUT); 1094 case ENOMEM: 1095 break; 1096 default: 1097 return (TRYRET_REMOTEERR); 1098 } 1099 /* FALLTHROUGH */ 1100 default: 1101 break; 1102 } 1103 return (TRYRET_LOCALERR); 1104} 1105 1106/* 1107 * Look up a netid based on an address family and socket type. 1108 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1109 * 1110 * XXX there should be a library function for this. 1111 */ 1112static const char * 1113netidbytype(int af, int sotype) 1114{ 1115 struct nc_protos *p; 1116 1117 for (p = nc_protos; p->netid != NULL; p++) { 1118 if (af != p->af || sotype != p->sotype) 1119 continue; 1120 return (p->netid); 1121 } 1122 return (NULL); 1123} 1124 1125/* 1126 * Look up a netconfig entry based on a netid, and cache the result so 1127 * that we don't need to remember to call freenetconfigent(). 1128 * 1129 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1130 * work on failure. 1131 */ 1132static struct netconfig * 1133getnetconf_cached(const char *netid) 1134{ 1135 static struct nc_entry { 1136 struct netconfig *nconf; 1137 struct nc_entry *next; 1138 } *head; 1139 struct nc_entry *p; 1140 struct netconfig *nconf; 1141 1142 for (p = head; p != NULL; p = p->next) 1143 if (strcmp(netid, p->nconf->nc_netid) == 0) 1144 return (p->nconf); 1145 1146 if ((nconf = getnetconfigent(netid)) == NULL) 1147 return (NULL); 1148 if ((p = malloc(sizeof(*p))) == NULL) 1149 err(1, "malloc"); 1150 p->nconf = nconf; 1151 p->next = head; 1152 head = p; 1153 1154 return (p->nconf); 1155} 1156 1157/* 1158 * xdr routines for mount rpc's 1159 */ 1160static int 1161xdr_dir(XDR *xdrsp, char *dirp) 1162{ 1163 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1164} 1165 1166static int 1167xdr_fh(XDR *xdrsp, struct nfhret *np) 1168{ 1169 int i; 1170 long auth, authcnt, authfnd = 0; 1171 1172 if (!xdr_u_long(xdrsp, &np->stat)) 1173 return (0); 1174 if (np->stat) 1175 return (1); 1176 switch (np->vers) { 1177 case 1: 1178 np->fhsize = NFS_FHSIZE; 1179 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1180 case 3: 1181 if (!xdr_long(xdrsp, &np->fhsize)) 1182 return (0); 1183 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1184 return (0); 1185 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1186 return (0); 1187 if (!xdr_long(xdrsp, &authcnt)) 1188 return (0); 1189 for (i = 0; i < authcnt; i++) { 1190 if (!xdr_long(xdrsp, &auth)) 1191 return (0); 1192 if (np->auth == -1) { 1193 np->auth = auth; 1194 authfnd++; 1195 } else if (auth == np->auth) { 1196 authfnd++; 1197 } 1198 } 1199 /* 1200 * Some servers, such as DEC's OSF/1 return a nil authenticator 1201 * list to indicate RPCAUTH_UNIX. 1202 */ 1203 if (authcnt == 0 && np->auth == -1) 1204 np->auth = AUTH_SYS; 1205 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1206 np->stat = EAUTH; 1207 return (1); 1208 }; 1209 return (0); 1210} 1211 1212static void 1213usage(void) 1214{ 1215 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1216"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1217" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1218" [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1219" rhost:path node"); 1220 exit(1); 1221}
| 416 417 build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 418 build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 419 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 420 421 /* 422 * XXX: 423 * Backwards compatibility routines for older kernels. 424 * Remove this and fallback_mount() code when we do not need to support 425 * NFS mounts against older kernels which still need 426 * struct nfs_args to be passed in via nmount(). 427 */ 428 osversion = getosreldate(); 429 if (osversion >= 702100) { 430 if (nmount(iov, iovlen, mntflags)) 431 err(1, "%s, %s", mntpath, errmsg); 432 } else { 433 if (fallback_mount(iov, iovlen, mntflags)) 434 err(1, "%s, %s", mntpath, errmsg); 435 } 436 437 exit(0); 438} 439 440static int 441findopt(struct iovec *iov, int iovlen, const char *name, 442 char **valuep, int *lenp) 443{ 444 int i; 445 446 for (i = 0; i < iovlen/2; i++, iov += 2) { 447 if (strcmp(name, iov[0].iov_base) == 0) { 448 if (valuep) 449 *valuep = iov[1].iov_base; 450 if (lenp) 451 *lenp = iov[1].iov_len; 452 return (0); 453 } 454 } 455 return (ENOENT); 456} 457 458static void 459copyopt(struct iovec **newiov, int *newiovlen, 460 struct iovec *iov, int iovlen, const char *name) 461{ 462 char *value; 463 int len; 464 465 if (findopt(iov, iovlen, name, &value, &len) == 0) 466 build_iovec(newiov, newiovlen, name, value, len); 467} 468 469/* 470 * XXX: This function is provided for backwards 471 * compatibility with older kernels which did not support 472 * passing NFS mount options to nmount() as individual 473 * parameters. It should be eventually be removed. 474 */ 475static int 476fallback_mount(struct iovec *iov, int iovlen, int mntflags) 477{ 478 struct nfs_args args = { 479 .version = NFS_ARGSVERSION, 480 .addr = NULL, 481 .addrlen = sizeof (struct sockaddr_in), 482 .sotype = SOCK_STREAM, 483 .proto = 0, 484 .fh = NULL, 485 .fhsize = 0, 486 .flags = NFSMNT_RESVPORT, 487 .wsize = NFS_WSIZE, 488 .rsize = NFS_RSIZE, 489 .readdirsize = NFS_READDIRSIZE, 490 .timeo = 10, 491 .retrans = NFS_RETRANS, 492 .maxgrouplist = NFS_MAXGRPS, 493 .readahead = NFS_DEFRAHEAD, 494 .wcommitsize = 0, /* was: NQ_DEFLEASE */ 495 .deadthresh = NFS_MAXDEADTHRESH, /* was: NQ_DEADTHRESH */ 496 .hostname = NULL, 497 /* args version 4 */ 498 .acregmin = NFS_MINATTRTIMO, 499 .acregmax = NFS_MAXATTRTIMO, 500 .acdirmin = NFS_MINDIRATTRTIMO, 501 .acdirmax = NFS_MAXDIRATTRTIMO, 502 }; 503 int ret; 504 char *opt; 505 struct iovec *newiov; 506 int newiovlen; 507 508 if (findopt(iov, iovlen, "dumbtimer", NULL, NULL) == 0) 509 args.flags |= NFSMNT_DUMBTIMR; 510 if (findopt(iov, iovlen, "noconn", NULL, NULL) == 0) 511 args.flags |= NFSMNT_NOCONN; 512 if (findopt(iov, iovlen, "conn", NULL, NULL) == 0) 513 args.flags |= NFSMNT_NOCONN; 514 if (findopt(iov, iovlen, "nolockd", NULL, NULL) == 0) 515 args.flags |= NFSMNT_NOLOCKD; 516 if (findopt(iov, iovlen, "lockd", NULL, NULL) == 0) 517 args.flags &= ~NFSMNT_NOLOCKD; 518 if (findopt(iov, iovlen, "intr", NULL, NULL) == 0) 519 args.flags |= NFSMNT_INT; 520 if (findopt(iov, iovlen, "rdirplus", NULL, NULL) == 0) 521 args.flags |= NFSMNT_RDIRPLUS; 522 if (findopt(iov, iovlen, "resvport", NULL, NULL) == 0) 523 args.flags |= NFSMNT_RESVPORT; 524 if (findopt(iov, iovlen, "noresvport", NULL, NULL) == 0) 525 args.flags &= ~NFSMNT_RESVPORT; 526 if (findopt(iov, iovlen, "soft", NULL, NULL) == 0) 527 args.flags |= NFSMNT_SOFT; 528 if (findopt(iov, iovlen, "hard", NULL, NULL) == 0) 529 args.flags &= ~NFSMNT_SOFT; 530 if (findopt(iov, iovlen, "mntudp", NULL, NULL) == 0) 531 args.sotype = SOCK_DGRAM; 532 if (findopt(iov, iovlen, "udp", NULL, NULL) == 0) 533 args.sotype = SOCK_DGRAM; 534 if (findopt(iov, iovlen, "tcp", NULL, NULL) == 0) 535 args.sotype = SOCK_STREAM; 536 if (findopt(iov, iovlen, "nfsv3", NULL, NULL) == 0) 537 args.flags |= NFSMNT_NFSV3; 538 if (findopt(iov, iovlen, "readdirsize", &opt, NULL) == 0) { 539 if (opt == NULL) { 540 errx(1, "illegal readdirsize"); 541 } 542 ret = sscanf(opt, "%d", &args.readdirsize); 543 if (ret != 1 || args.readdirsize <= 0) { 544 errx(1, "illegal readdirsize: %s", opt); 545 } 546 args.flags |= NFSMNT_READDIRSIZE; 547 } 548 if (findopt(iov, iovlen, "readahead", &opt, NULL) == 0) { 549 if (opt == NULL) { 550 errx(1, "illegal readahead"); 551 } 552 ret = sscanf(opt, "%d", &args.readahead); 553 if (ret != 1 || args.readahead <= 0) { 554 errx(1, "illegal readahead: %s", opt); 555 } 556 args.flags |= NFSMNT_READAHEAD; 557 } 558 if (findopt(iov, iovlen, "wsize", &opt, NULL) == 0) { 559 if (opt == NULL) { 560 errx(1, "illegal wsize"); 561 } 562 ret = sscanf(opt, "%d", &args.wsize); 563 if (ret != 1 || args.wsize <= 0) { 564 errx(1, "illegal wsize: %s", opt); 565 } 566 args.flags |= NFSMNT_WSIZE; 567 } 568 if (findopt(iov, iovlen, "rsize", &opt, NULL) == 0) { 569 if (opt == NULL) { 570 errx(1, "illegal rsize"); 571 } 572 ret = sscanf(opt, "%d", &args.rsize); 573 if (ret != 1 || args.rsize <= 0) { 574 errx(1, "illegal wsize: %s", opt); 575 } 576 args.flags |= NFSMNT_RSIZE; 577 } 578 if (findopt(iov, iovlen, "retrans", &opt, NULL) == 0) { 579 if (opt == NULL) { 580 errx(1, "illegal retrans"); 581 } 582 ret = sscanf(opt, "%d", &args.retrans); 583 if (ret != 1 || args.retrans <= 0) { 584 errx(1, "illegal retrans: %s", opt); 585 } 586 args.flags |= NFSMNT_RETRANS; 587 } 588 if (findopt(iov, iovlen, "acregmin", &opt, NULL) == 0) { 589 ret = sscanf(opt, "%d", &args.acregmin); 590 if (ret != 1 || args.acregmin < 0) { 591 errx(1, "illegal acregmin: %s", opt); 592 } 593 args.flags |= NFSMNT_ACREGMIN; 594 } 595 if (findopt(iov, iovlen, "acregmax", &opt, NULL) == 0) { 596 ret = sscanf(opt, "%d", &args.acregmax); 597 if (ret != 1 || args.acregmax < 0) { 598 errx(1, "illegal acregmax: %s", opt); 599 } 600 args.flags |= NFSMNT_ACREGMAX; 601 } 602 if (findopt(iov, iovlen, "acdirmin", &opt, NULL) == 0) { 603 ret = sscanf(opt, "%d", &args.acdirmin); 604 if (ret != 1 || args.acdirmin < 0) { 605 errx(1, "illegal acdirmin: %s", opt); 606 } 607 args.flags |= NFSMNT_ACDIRMIN; 608 } 609 if (findopt(iov, iovlen, "acdirmax", &opt, NULL) == 0) { 610 ret = sscanf(opt, "%d", &args.acdirmax); 611 if (ret != 1 || args.acdirmax < 0) { 612 errx(1, "illegal acdirmax: %s", opt); 613 } 614 args.flags |= NFSMNT_ACDIRMAX; 615 } 616 if (findopt(iov, iovlen, "wcommitsize", &opt, NULL) == 0) { 617 ret = sscanf(opt, "%d", &args.wcommitsize); 618 if (ret != 1 || args.wcommitsize < 0) { 619 errx(1, "illegal wcommitsize: %s", opt); 620 } 621 args.flags |= NFSMNT_WCOMMITSIZE; 622 } 623 if (findopt(iov, iovlen, "deadthresh", &opt, NULL) == 0) { 624 ret = sscanf(opt, "%d", &args.deadthresh); 625 if (ret != 1 || args.deadthresh <= 0) { 626 errx(1, "illegal deadthresh: %s", opt); 627 } 628 args.flags |= NFSMNT_DEADTHRESH; 629 } 630 if (findopt(iov, iovlen, "timeout", &opt, NULL) == 0) { 631 ret = sscanf(opt, "%d", &args.timeo); 632 if (ret != 1 || args.timeo <= 0) { 633 errx(1, "illegal timeout: %s", opt); 634 } 635 args.flags |= NFSMNT_TIMEO; 636 } 637 if (findopt(iov, iovlen, "maxgroups", &opt, NULL) == 0) { 638 ret = sscanf(opt, "%d", &args.maxgrouplist); 639 if (ret != 1 || args.timeo <= 0) { 640 errx(1, "illegal maxgroups: %s", opt); 641 } 642 args.flags |= NFSMNT_MAXGRPS; 643 } 644 if (findopt(iov, iovlen, "addr", &opt, 645 &args.addrlen) == 0) { 646 args.addr = (struct sockaddr *) opt; 647 } 648 if (findopt(iov, iovlen, "fh", &opt, &args.fhsize) == 0) { 649 args.fh = opt; 650 } 651 if (findopt(iov, iovlen, "hostname", &args.hostname, 652 NULL) == 0) { 653 } 654 if (args.hostname == NULL) { 655 errx(1, "Invalid hostname"); 656 } 657 658 newiov = NULL; 659 newiovlen = 0; 660 661 build_iovec(&newiov, &newiovlen, "nfs_args", &args, sizeof(args)); 662 copyopt(&newiov, &newiovlen, iov, iovlen, "fstype"); 663 copyopt(&newiov, &newiovlen, iov, iovlen, "fspath"); 664 copyopt(&newiov, &newiovlen, iov, iovlen, "errmsg"); 665 666 return nmount(newiov, newiovlen, mntflags); 667} 668 669static int 670sec_name_to_num(char *sec) 671{ 672 if (!strcmp(sec, "krb5")) 673 return (RPCSEC_GSS_KRB5); 674 if (!strcmp(sec, "krb5i")) 675 return (RPCSEC_GSS_KRB5I); 676 if (!strcmp(sec, "krb5p")) 677 return (RPCSEC_GSS_KRB5P); 678 if (!strcmp(sec, "sys")) 679 return (AUTH_SYS); 680 return (-1); 681} 682 683static char * 684sec_num_to_name(int flavor) 685{ 686 switch (flavor) { 687 case RPCSEC_GSS_KRB5: 688 return ("krb5"); 689 case RPCSEC_GSS_KRB5I: 690 return ("krb5i"); 691 case RPCSEC_GSS_KRB5P: 692 return ("krb5p"); 693 case AUTH_SYS: 694 return ("sys"); 695 } 696 return (NULL); 697} 698 699static int 700getnfsargs(char *spec, struct iovec **iov, int *iovlen) 701{ 702 struct addrinfo hints, *ai_nfs, *ai; 703 enum tryret ret; 704 int ecode, speclen, remoteerr, offset, have_bracket = 0; 705 char *hostp, *delimp, *errstr; 706 size_t len; 707 static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; 708 709 if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 710 *(delimp + 1) == ':') { 711 hostp = spec + 1; 712 spec = delimp + 2; 713 have_bracket = 1; 714 } else if ((delimp = strrchr(spec, ':')) != NULL) { 715 hostp = spec; 716 spec = delimp + 1; 717 } else if ((delimp = strrchr(spec, '@')) != NULL) { 718 warnx("path@server syntax is deprecated, use server:path"); 719 hostp = delimp + 1; 720 } else { 721 warnx("no <host>:<dirpath> nfs-name"); 722 return (0); 723 } 724 *delimp = '\0'; 725 726 /* 727 * If there has been a trailing slash at mounttime it seems 728 * that some mountd implementations fail to remove the mount 729 * entries from their mountlist while unmounting. 730 */ 731 for (speclen = strlen(spec); 732 speclen > 1 && spec[speclen - 1] == '/'; 733 speclen--) 734 spec[speclen - 1] = '\0'; 735 if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 736 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG)); 737 return (0); 738 } 739 /* Make both '@' and ':' notations equal */ 740 if (*hostp != '\0') { 741 len = strlen(hostp); 742 offset = 0; 743 if (have_bracket) 744 nam[offset++] = '['; 745 memmove(nam + offset, hostp, len); 746 if (have_bracket) 747 nam[len + offset++] = ']'; 748 nam[len + offset++] = ':'; 749 memmove(nam + len + offset, spec, speclen); 750 nam[len + speclen + offset] = '\0'; 751 } 752 753 /* 754 * Handle an internet host address. 755 */ 756 memset(&hints, 0, sizeof hints); 757 hints.ai_flags = AI_NUMERICHOST; 758 if (nfsproto == IPPROTO_TCP) 759 hints.ai_socktype = SOCK_STREAM; 760 else if (nfsproto == IPPROTO_UDP) 761 hints.ai_socktype = SOCK_DGRAM; 762 763 if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { 764 hints.ai_flags = AI_CANONNAME; 765 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) 766 != 0) { 767 if (portspec == NULL) 768 errx(1, "%s: %s", hostp, gai_strerror(ecode)); 769 else 770 errx(1, "%s:%s: %s", hostp, portspec, 771 gai_strerror(ecode)); 772 return (0); 773 } 774 775 /* 776 * For a Kerberized nfs mount where the "principal" 777 * argument has not been set, add it here. 778 */ 779 if (got_principal == 0 && secflavor >= 0 && 780 secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) { 781 snprintf(pname, sizeof (pname), "nfs@%s", 782 ai_nfs->ai_canonname); 783 build_iovec(iov, iovlen, "principal", pname, 784 strlen(pname) + 1); 785 } 786 } 787 788 ret = TRYRET_LOCALERR; 789 for (;;) { 790 /* 791 * Try each entry returned by getaddrinfo(). Note the 792 * occurrence of remote errors by setting `remoteerr'. 793 */ 794 remoteerr = 0; 795 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) { 796 if ((ai->ai_family == AF_INET6) && 797 (opflags & OF_NOINET6)) 798 continue; 799 if ((ai->ai_family == AF_INET) && 800 (opflags & OF_NOINET4)) 801 continue; 802 ret = nfs_tryproto(ai, hostp, spec, &errstr, iov, 803 iovlen); 804 if (ret == TRYRET_SUCCESS) 805 break; 806 if (ret != TRYRET_LOCALERR) 807 remoteerr = 1; 808 if ((opflags & ISBGRND) == 0) 809 fprintf(stderr, "%s\n", errstr); 810 } 811 if (ret == TRYRET_SUCCESS) 812 break; 813 814 /* Exit if all errors were local. */ 815 if (!remoteerr) 816 exit(1); 817 818 /* 819 * If retrycnt == 0, we are to keep retrying forever. 820 * Otherwise decrement it, and exit if it hits zero. 821 */ 822 if (retrycnt != 0 && --retrycnt == 0) 823 exit(1); 824 825 if ((opflags & (BGRND | ISBGRND)) == BGRND) { 826 warnx("Cannot immediately mount %s:%s, backgrounding", 827 hostp, spec); 828 opflags |= ISBGRND; 829 if (daemon(0, 0) != 0) 830 err(1, "daemon"); 831 } 832 sleep(60); 833 } 834 freeaddrinfo(ai_nfs); 835 836 build_iovec(iov, iovlen, "hostname", nam, (size_t)-1); 837 /* Add mounted file system to PATH_MOUNTTAB */ 838 if (!add_mtab(hostp, spec)) 839 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec); 840 return (1); 841} 842 843/* 844 * Try to set up the NFS arguments according to the address 845 * family, protocol (and possibly port) specified in `ai'. 846 * 847 * Returns TRYRET_SUCCESS if successful, or: 848 * TRYRET_TIMEOUT The server did not respond. 849 * TRYRET_REMOTEERR The server reported an error. 850 * TRYRET_LOCALERR Local failure. 851 * 852 * In all error cases, *errstr will be set to a statically-allocated string 853 * describing the error. 854 */ 855static enum tryret 856nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, 857 struct iovec **iov, int *iovlen) 858{ 859 static char errbuf[256]; 860 struct sockaddr_storage nfs_ss; 861 struct netbuf nfs_nb; 862 struct nfhret nfhret; 863 struct timeval try; 864 struct rpc_err rpcerr; 865 CLIENT *clp; 866 struct netconfig *nconf, *nconf_mnt; 867 const char *netid, *netid_mnt; 868 char *secname; 869 int doconnect, nfsvers, mntvers, sotype; 870 enum clnt_stat stat; 871 enum mountmode trymntmode; 872 873 sotype = 0; 874 trymntmode = mountmode; 875 errbuf[0] = '\0'; 876 *errstr = errbuf; 877 878 if (nfsproto == IPPROTO_TCP) 879 sotype = SOCK_STREAM; 880 else if (nfsproto == IPPROTO_UDP) 881 sotype = SOCK_DGRAM; 882 883 if ((netid = netidbytype(ai->ai_family, sotype)) == NULL) { 884 snprintf(errbuf, sizeof errbuf, 885 "af %d sotype %d not supported", ai->ai_family, sotype); 886 return (TRYRET_LOCALERR); 887 } 888 if ((nconf = getnetconf_cached(netid)) == NULL) { 889 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror()); 890 return (TRYRET_LOCALERR); 891 } 892 /* The RPCPROG_MNT netid may be different. */ 893 if (mnttcp_ok) { 894 netid_mnt = netid; 895 nconf_mnt = nconf; 896 } else { 897 if ((netid_mnt = netidbytype(ai->ai_family, SOCK_DGRAM)) 898 == NULL) { 899 snprintf(errbuf, sizeof errbuf, 900 "af %d sotype SOCK_DGRAM not supported", 901 ai->ai_family); 902 return (TRYRET_LOCALERR); 903 } 904 if ((nconf_mnt = getnetconf_cached(netid_mnt)) == NULL) { 905 snprintf(errbuf, sizeof errbuf, "%s: %s", netid_mnt, 906 nc_sperror()); 907 return (TRYRET_LOCALERR); 908 } 909 } 910 911tryagain: 912 if (trymntmode == V4) { 913 nfsvers = 4; 914 } else if (trymntmode == V2) { 915 nfsvers = 2; 916 mntvers = 1; 917 } else { 918 nfsvers = 3; 919 mntvers = 3; 920 } 921 922 if (portspec != NULL) { 923 /* `ai' contains the complete nfsd sockaddr. */ 924 nfs_nb.buf = ai->ai_addr; 925 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen; 926 } else { 927 /* Ask the remote rpcbind. */ 928 nfs_nb.buf = &nfs_ss; 929 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss; 930 931 if (!rpcb_getaddr(NFS_PROGRAM, nfsvers, nconf, &nfs_nb, 932 hostp)) { 933 if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH && 934 trymntmode == ANY) { 935 trymntmode = V2; 936 goto tryagain; 937 } 938 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", 939 netid, hostp, spec, 940 clnt_spcreateerror("RPCPROG_NFS")); 941 return (returncode(rpc_createerr.cf_stat, 942 &rpc_createerr.cf_error)); 943 } 944 } 945 946 /* Check that the server (nfsd) responds on the port we have chosen. */ 947 clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, NFS_PROGRAM, nfsvers, 948 0, 0); 949 if (clp == NULL) { 950 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 951 hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS")); 952 return (returncode(rpc_createerr.cf_stat, 953 &rpc_createerr.cf_error)); 954 } 955 if (sotype == SOCK_DGRAM && noconn == 0) { 956 /* 957 * Use connect(), to match what the kernel does. This 958 * catches cases where the server responds from the 959 * wrong source address. 960 */ 961 doconnect = 1; 962 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) { 963 clnt_destroy(clp); 964 snprintf(errbuf, sizeof errbuf, 965 "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp, 966 spec); 967 return (TRYRET_LOCALERR); 968 } 969 } 970 971 try.tv_sec = 10; 972 try.tv_usec = 0; 973 stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, 974 (xdrproc_t)xdr_void, NULL, try); 975 if (stat != RPC_SUCCESS) { 976 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 977 clnt_destroy(clp); 978 trymntmode = V2; 979 goto tryagain; 980 } 981 clnt_geterr(clp, &rpcerr); 982 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid, 983 hostp, spec, clnt_sperror(clp, "NFSPROC_NULL")); 984 clnt_destroy(clp); 985 return (returncode(stat, &rpcerr)); 986 } 987 clnt_destroy(clp); 988 989 /* 990 * For NFSv4, there is no mount protocol. 991 */ 992 if (trymntmode == V4) { 993 /* 994 * Store the server address in nfsargsp, making 995 * sure to copy any locally allocated structures. 996 */ 997 addrlen = nfs_nb.len; 998 addr = malloc(addrlen); 999 if (addr == NULL) 1000 err(1, "malloc"); 1001 bcopy(nfs_nb.buf, addr, addrlen); 1002 1003 build_iovec(iov, iovlen, "addr", addr, addrlen); 1004 secname = sec_num_to_name(secflavor); 1005 if (secname != NULL) 1006 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1007 build_iovec(iov, iovlen, "nfsv4", NULL, 0); 1008 build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); 1009 1010 return (TRYRET_SUCCESS); 1011 } 1012 1013 /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ 1014 try.tv_sec = 10; 1015 try.tv_usec = 0; 1016 clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); 1017 if (clp == NULL) { 1018 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1019 hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); 1020 return (returncode(rpc_createerr.cf_stat, 1021 &rpc_createerr.cf_error)); 1022 } 1023 clp->cl_auth = authsys_create_default(); 1024 nfhret.auth = secflavor; 1025 nfhret.vers = mntvers; 1026 stat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, 1027 (xdrproc_t)xdr_fh, &nfhret, 1028 try); 1029 auth_destroy(clp->cl_auth); 1030 if (stat != RPC_SUCCESS) { 1031 if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { 1032 clnt_destroy(clp); 1033 trymntmode = V2; 1034 goto tryagain; 1035 } 1036 clnt_geterr(clp, &rpcerr); 1037 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1038 hostp, spec, clnt_sperror(clp, "RPCPROG_MNT")); 1039 clnt_destroy(clp); 1040 return (returncode(stat, &rpcerr)); 1041 } 1042 clnt_destroy(clp); 1043 1044 if (nfhret.stat != 0) { 1045 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, 1046 hostp, spec, strerror(nfhret.stat)); 1047 return (TRYRET_REMOTEERR); 1048 } 1049 1050 /* 1051 * Store the filehandle and server address in nfsargsp, making 1052 * sure to copy any locally allocated structures. 1053 */ 1054 addrlen = nfs_nb.len; 1055 addr = malloc(addrlen); 1056 fhsize = nfhret.fhsize; 1057 fh = malloc(fhsize); 1058 if (addr == NULL || fh == NULL) 1059 err(1, "malloc"); 1060 bcopy(nfs_nb.buf, addr, addrlen); 1061 bcopy(nfhret.nfh, fh, fhsize); 1062 1063 build_iovec(iov, iovlen, "addr", addr, addrlen); 1064 build_iovec(iov, iovlen, "fh", fh, fhsize); 1065 secname = sec_num_to_name(nfhret.auth); 1066 if (secname) 1067 build_iovec(iov, iovlen, "sec", secname, (size_t)-1); 1068 if (nfsvers == 3) 1069 build_iovec(iov, iovlen, "nfsv3", NULL, 0); 1070 1071 return (TRYRET_SUCCESS); 1072} 1073 1074/* 1075 * Catagorise a RPC return status and error into an `enum tryret' 1076 * return code. 1077 */ 1078static enum tryret 1079returncode(enum clnt_stat stat, struct rpc_err *rpcerr) 1080{ 1081 switch (stat) { 1082 case RPC_TIMEDOUT: 1083 return (TRYRET_TIMEOUT); 1084 case RPC_PMAPFAILURE: 1085 case RPC_PROGNOTREGISTERED: 1086 case RPC_PROGVERSMISMATCH: 1087 /* XXX, these can be local or remote. */ 1088 case RPC_CANTSEND: 1089 case RPC_CANTRECV: 1090 return (TRYRET_REMOTEERR); 1091 case RPC_SYSTEMERROR: 1092 switch (rpcerr->re_errno) { 1093 case ETIMEDOUT: 1094 return (TRYRET_TIMEOUT); 1095 case ENOMEM: 1096 break; 1097 default: 1098 return (TRYRET_REMOTEERR); 1099 } 1100 /* FALLTHROUGH */ 1101 default: 1102 break; 1103 } 1104 return (TRYRET_LOCALERR); 1105} 1106 1107/* 1108 * Look up a netid based on an address family and socket type. 1109 * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM. 1110 * 1111 * XXX there should be a library function for this. 1112 */ 1113static const char * 1114netidbytype(int af, int sotype) 1115{ 1116 struct nc_protos *p; 1117 1118 for (p = nc_protos; p->netid != NULL; p++) { 1119 if (af != p->af || sotype != p->sotype) 1120 continue; 1121 return (p->netid); 1122 } 1123 return (NULL); 1124} 1125 1126/* 1127 * Look up a netconfig entry based on a netid, and cache the result so 1128 * that we don't need to remember to call freenetconfigent(). 1129 * 1130 * Otherwise it behaves just like getnetconfigent(), so nc_*error() 1131 * work on failure. 1132 */ 1133static struct netconfig * 1134getnetconf_cached(const char *netid) 1135{ 1136 static struct nc_entry { 1137 struct netconfig *nconf; 1138 struct nc_entry *next; 1139 } *head; 1140 struct nc_entry *p; 1141 struct netconfig *nconf; 1142 1143 for (p = head; p != NULL; p = p->next) 1144 if (strcmp(netid, p->nconf->nc_netid) == 0) 1145 return (p->nconf); 1146 1147 if ((nconf = getnetconfigent(netid)) == NULL) 1148 return (NULL); 1149 if ((p = malloc(sizeof(*p))) == NULL) 1150 err(1, "malloc"); 1151 p->nconf = nconf; 1152 p->next = head; 1153 head = p; 1154 1155 return (p->nconf); 1156} 1157 1158/* 1159 * xdr routines for mount rpc's 1160 */ 1161static int 1162xdr_dir(XDR *xdrsp, char *dirp) 1163{ 1164 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 1165} 1166 1167static int 1168xdr_fh(XDR *xdrsp, struct nfhret *np) 1169{ 1170 int i; 1171 long auth, authcnt, authfnd = 0; 1172 1173 if (!xdr_u_long(xdrsp, &np->stat)) 1174 return (0); 1175 if (np->stat) 1176 return (1); 1177 switch (np->vers) { 1178 case 1: 1179 np->fhsize = NFS_FHSIZE; 1180 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFS_FHSIZE)); 1181 case 3: 1182 if (!xdr_long(xdrsp, &np->fhsize)) 1183 return (0); 1184 if (np->fhsize <= 0 || np->fhsize > NFS3_FHSIZE) 1185 return (0); 1186 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize)) 1187 return (0); 1188 if (!xdr_long(xdrsp, &authcnt)) 1189 return (0); 1190 for (i = 0; i < authcnt; i++) { 1191 if (!xdr_long(xdrsp, &auth)) 1192 return (0); 1193 if (np->auth == -1) { 1194 np->auth = auth; 1195 authfnd++; 1196 } else if (auth == np->auth) { 1197 authfnd++; 1198 } 1199 } 1200 /* 1201 * Some servers, such as DEC's OSF/1 return a nil authenticator 1202 * list to indicate RPCAUTH_UNIX. 1203 */ 1204 if (authcnt == 0 && np->auth == -1) 1205 np->auth = AUTH_SYS; 1206 if (!authfnd && (authcnt > 0 || np->auth != AUTH_SYS)) 1207 np->stat = EAUTH; 1208 return (1); 1209 }; 1210 return (0); 1211} 1212 1213static void 1214usage(void) 1215{ 1216 (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", 1217"usage: mount_nfs [-23bcdiLlNPsTU] [-a maxreadahead] [-D deadthresh]", 1218" [-g maxgroups] [-I readdirsize] [-o options] [-R retrycnt]", 1219" [-r readsize] [-t timeout] [-w writesize] [-x retrans]", 1220" rhost:path node"); 1221 exit(1); 1222}
|