1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
25 */
26
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <syslog.h>
32 #include <string.h>
33 #include <deflt.h>
34 #include <kstat.h>
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <signal.h>
43 #include <sys/signal.h>
44 #include <rpc/rpc.h>
45 #include <rpc/pmap_clnt.h>
46 #include <sys/mount.h>
47 #include <sys/mntent.h>
48 #include <sys/mnttab.h>
49 #include <sys/fstyp.h>
50 #include <sys/fsid.h>
51 #include <arpa/inet.h>
52 #include <netdb.h>
53 #include <netconfig.h>
54 #include <netdir.h>
55 #include <errno.h>
56 #define NFSCLIENT
57 #include <nfs/nfs.h>
58 #include <nfs/mount.h>
59 #include <rpcsvc/mount.h>
60 #include <rpc/nettype.h>
61 #include <locale.h>
62 #include <setjmp.h>
63 #include <sys/socket.h>
64 #include <thread.h>
65 #include <limits.h>
66 #include <nss_dbdefs.h> /* for NSS_BUFLEN_HOSTS */
67 #include <nfs/nfs_sec.h>
68 #include <sys/sockio.h>
69 #include <net/if.h>
70 #include <assert.h>
71 #include <nfs/nfs_clnt.h>
72 #include <rpcsvc/nfs4_prot.h>
73 #include <nfs/nfs4.h>
74 #define NO_RDDIR_CACHE
75 #include "automount.h"
76 #include "replica.h"
77 #include "nfs_subr.h"
78 #include "webnfs.h"
79 #include "nfs_resolve.h"
80 #include <sys/sockio.h>
81 #include <net/if.h>
82 #include <rpcsvc/daemon_utils.h>
83 #include <pwd.h>
84 #include <strings.h>
85 #include <tsol/label.h>
86 #include <zone.h>
87 #include <limits.h>
88 #include <libscf.h>
89 #include <libshare.h>
90 #include "smfcfg.h"
91
92 extern void set_nfsv4_ephemeral_mount_to(void);
93
94 extern char *nfs_get_qop_name();
95 extern AUTH *nfs_create_ah();
96 extern enum snego_stat nfs_sec_nego();
97
98 #define MAXHOSTS 512
99
100 /*
101 * host cache states
102 */
103 #define NOHOST 0
104 #define GOODHOST 1
105 #define DEADHOST 2
106
107 #define NFS_ARGS_EXTB_secdata(args, secdata) \
108 { (args).nfs_args_ext = NFS_ARGS_EXTB, \
109 (args).nfs_ext_u.nfs_extB.secdata = secdata; }
110
111 struct cache_entry {
112 struct cache_entry *cache_next;
113 char *cache_host;
114 time_t cache_time;
115 int cache_state;
116 rpcvers_t cache_reqvers;
117 rpcvers_t cache_outvers;
118 char *cache_proto;
119 };
120
121 struct mfs_snego_t {
122 int sec_opt;
123 bool_t snego_done;
124 char *nfs_flavor;
125 seconfig_t nfs_sec;
126 };
127 typedef struct mfs_snego_t mfs_snego_t;
128
129 static struct cache_entry *cache_head = NULL;
130 rwlock_t cache_lock; /* protect the cache chain */
131
132 static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, uid_t,
133 action_list *);
134 static int is_nfs_port(char *);
135
136 static void netbuf_free(struct netbuf *);
137 static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int);
138 static struct mapfs *enum_servers(struct mapent *, char *);
139 static struct mapfs *get_mysubnet_servers(struct mapfs *);
140 static int subnet_test(int af, struct sioc_addrreq *);
141 static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
142 struct netconfig **, char *, ushort_t, struct t_info *);
143
144 static struct netbuf *get_pubfh(char *, rpcvers_t, mfs_snego_t *,
145 struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *,
146 bool_t, char *);
147
148 static int create_homedir(const char *, const char *);
149
150 enum type_of_stuff {
151 SERVER_ADDR = 0,
152 SERVER_PING = 1,
153 SERVER_FH = 2
154 };
155
156 static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t,
157 rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t,
158 struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *);
159 static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t,
160 rpcvers_t, struct netconfig *, ushort_t, struct t_info *,
161 struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *,
162 mfs_snego_t *);
163 static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t,
164 struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
165 caddr_t *, bool_t, char *, enum clnt_stat *, int);
166 static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *,
167 struct netconfig *, ushort_t, struct t_info *, struct t_bind *,
168 caddr_t *, bool_t, char *, enum clnt_stat *);
169
170 struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **);
171 void free_mfs(struct mapfs *);
172 static void dump_mfs(struct mapfs *, char *, int);
173 static char *dump_distance(struct mapfs *);
174 static void cache_free(struct cache_entry *);
175 static int cache_check(char *, rpcvers_t *, char *);
176 static void cache_enter(char *, rpcvers_t, rpcvers_t, char *, int);
177 void destroy_auth_client_handle(CLIENT *cl);
178
179 #ifdef CACHE_DEBUG
180 static void trace_host_cache();
181 static void trace_portmap_cache();
182 #endif /* CACHE_DEBUG */
183
184 static int rpc_timeout = 20;
185
186 #ifdef CACHE_DEBUG
187 /*
188 * host cache counters. These variables do not need to be protected
189 * by mutex's. They have been added to measure the utility of the
190 * goodhost/deadhost cache in the lazy hierarchical mounting scheme.
191 */
192 static int host_cache_accesses = 0;
193 static int host_cache_lookups = 0;
194 static int deadhost_cache_hits = 0;
195 static int goodhost_cache_hits = 0;
196
197 /*
198 * portmap cache counters. These variables do not need to be protected
199 * by mutex's. They have been added to measure the utility of the portmap
200 * cache in the lazy hierarchical mounting scheme.
201 */
202 static int portmap_cache_accesses = 0;
203 static int portmap_cache_lookups = 0;
204 static int portmap_cache_hits = 0;
205 #endif /* CACHE_DEBUG */
206
207 /*
208 * There are the defaults (range) for the client when determining
209 * which NFS version to use when probing the server (see above).
210 * These will only be used when the vers mount option is not used and
211 * these may be reset if /etc/default/nfs is configured to do so.
212 */
213 static rpcvers_t vers_max_default = NFS_VERSMAX_DEFAULT;
214 static rpcvers_t vers_min_default = NFS_VERSMIN_DEFAULT;
215
216 /*
217 * list of support services needed
218 */
219 static char *service_list[] = { STATD, LOCKD, NULL };
220 static char *service_list_v4[] = { STATD, LOCKD, NFS4CBD, NFSMAPID, NULL };
221
222 static void read_default_nfs(void);
223 static int is_v4_mount(char *);
224 static void start_nfs4cbd(void);
225
226 int
mount_nfs(struct mapent * me,char * mntpnt,char * prevhost,int overlay,uid_t uid,action_list ** alpp)227 mount_nfs(
228 struct mapent *me,
229 char *mntpnt,
230 char *prevhost,
231 int overlay,
232 uid_t uid,
233 action_list **alpp)
234 {
235 struct mapfs *mfs, *mp;
236 int err = -1;
237 action_list *alp;
238 char *dir;
239
240
241 alp = *alpp;
242
243 read_default_nfs();
244
245 mfs = enum_servers(me, prevhost);
246 if (mfs == NULL)
247 return (ENOENT);
248
249 /*
250 * Try loopback if we have something on localhost; if nothing
251 * works, we will fall back to NFS
252 */
253 if (is_nfs_port(me->map_mntopts)) {
254 for (mp = mfs; mp; mp = mp->mfs_next) {
255 if (self_check(mp->mfs_host)) {
256 err = loopbackmount(mp->mfs_dir,
257 mntpnt, me->map_mntopts, overlay);
258 if (err) {
259 mp->mfs_ignore = 1;
260 } else {
261 /*
262 * Free action_list if there
263 * is one as it is not needed.
264 * Make sure to set alpp to null
265 * so caller doesn't try to free it
266 * again.
267 */
268 if (*alpp) {
269 free(*alpp);
270 *alpp = NULL;
271 }
272 break;
273 }
274 }
275 }
276 }
277 if (err) {
278 dir = strdup(mfs->mfs_dir);
279 err = nfsmount(mfs, mntpnt, me->map_mntopts,
280 overlay, uid, alp);
281 if (err && trace > 1) {
282 trace_prt(1, " Couldn't mount %s:%s, err=%d\n",
283 mfs->mfs_host ? mfs->mfs_host : "",
284 mfs->mfs_dir ? mfs->mfs_dir : dir, err);
285 }
286 free(dir);
287 }
288 free_mfs(mfs);
289 return (err);
290 }
291
292
293 /*
294 * Using the new ioctl SIOCTONLINK to determine if a host is on the same
295 * subnet. Remove the old network, subnet check.
296 */
297
298 static struct mapfs *
get_mysubnet_servers(struct mapfs * mfs_in)299 get_mysubnet_servers(struct mapfs *mfs_in)
300 {
301 int s;
302 struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
303
304 struct netconfig *nconf;
305 NCONF_HANDLE *nc = NULL;
306 struct nd_hostserv hs;
307 struct nd_addrlist *retaddrs;
308 struct netbuf *nb;
309 struct sioc_addrreq areq;
310 int res;
311 int af;
312 int i;
313 int sa_size;
314
315 hs.h_serv = "rpcbind";
316
317 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
318 nc = setnetconfig();
319
320 while (nconf = getnetconfig(nc)) {
321
322 /*
323 * Care about INET family only. proto_done flag
324 * indicates if we have already covered this
325 * protocol family. If so skip it
326 */
327 if (((strcmp(nconf->nc_protofmly, NC_INET6) == 0) ||
328 (strcmp(nconf->nc_protofmly, NC_INET) == 0)) &&
329 (nconf->nc_semantics == NC_TPI_CLTS)) {
330 } else
331 continue;
332
333 hs.h_host = mfs->mfs_host;
334
335 if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK)
336 continue;
337
338 /*
339 * For each host address see if it's on our
340 * local subnet.
341 */
342
343 if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
344 af = AF_INET6;
345 else
346 af = AF_INET;
347 nb = retaddrs->n_addrs;
348 for (i = 0; i < retaddrs->n_cnt; i++, nb++) {
349 memset(&areq.sa_addr, 0, sizeof (areq.sa_addr));
350 memcpy(&areq.sa_addr, nb->buf, MIN(nb->len,
351 sizeof (areq.sa_addr)));
352 if (res = subnet_test(af, &areq)) {
353 p = add_mfs(mfs, DIST_MYNET,
354 &mfs_head, &mfs_tail);
355 if (!p) {
356 netdir_free(retaddrs,
357 ND_ADDRLIST);
358 endnetconfig(nc);
359 return (NULL);
360 }
361 break;
362 }
363 } /* end of every host */
364 if (trace > 2) {
365 trace_prt(1, "get_mysubnet_servers: host=%s "
366 "netid=%s res=%s\n", mfs->mfs_host,
367 nconf->nc_netid, res == 1?"SUC":"FAIL");
368 }
369
370 netdir_free(retaddrs, ND_ADDRLIST);
371 } /* end of while */
372
373 endnetconfig(nc);
374
375 } /* end of every map */
376
377 return (mfs_head);
378
379 }
380
381 int
subnet_test(int af,struct sioc_addrreq * areq)382 subnet_test(int af, struct sioc_addrreq *areq)
383 {
384 int s;
385
386 if ((s = socket(af, SOCK_DGRAM, 0)) < 0) {
387 return (0);
388 }
389
390 areq->sa_res = -1;
391
392 if (ioctl(s, SIOCTONLINK, (caddr_t)areq) < 0) {
393 syslog(LOG_ERR, "subnet_test:SIOCTONLINK failed");
394 return (0);
395 }
396 close(s);
397 if (areq->sa_res == 1)
398 return (1);
399 else
400 return (0);
401
402
403 }
404
405 /*
406 * ping a bunch of hosts at once and sort by who responds first
407 */
408 static struct mapfs *
sort_servers(struct mapfs * mfs_in,int timeout)409 sort_servers(struct mapfs *mfs_in, int timeout)
410 {
411 struct mapfs *m1 = NULL;
412 enum clnt_stat clnt_stat;
413
414 if (!mfs_in)
415 return (NULL);
416
417 clnt_stat = nfs_cast(mfs_in, &m1, timeout);
418
419 if (!m1) {
420 char buff[2048] = {'\0'};
421
422 for (m1 = mfs_in; m1; m1 = m1->mfs_next) {
423 (void) strcat(buff, m1->mfs_host);
424 if (m1->mfs_next)
425 (void) strcat(buff, ",");
426 }
427
428 syslog(LOG_ERR, "servers %s not responding: %s",
429 buff, clnt_sperrno(clnt_stat));
430 }
431
432 return (m1);
433 }
434
435 /*
436 * Add a mapfs entry to the list described by *mfs_head and *mfs_tail,
437 * provided it is not marked "ignored" and isn't a dupe of ones we've
438 * already seen.
439 */
440 struct mapfs *
add_mfs(struct mapfs * mfs,int distance,struct mapfs ** mfs_head,struct mapfs ** mfs_tail)441 add_mfs(struct mapfs *mfs, int distance, struct mapfs **mfs_head,
442 struct mapfs **mfs_tail)
443 {
444 struct mapfs *tmp, *new;
445
446 for (tmp = *mfs_head; tmp; tmp = tmp->mfs_next)
447 if ((strcmp(tmp->mfs_host, mfs->mfs_host) == 0 &&
448 strcmp(tmp->mfs_dir, mfs->mfs_dir) == 0) ||
449 mfs->mfs_ignore)
450 return (*mfs_head);
451 new = (struct mapfs *)malloc(sizeof (struct mapfs));
452 if (!new) {
453 syslog(LOG_ERR, "Memory allocation failed: %m");
454 return (NULL);
455 }
456 bcopy(mfs, new, sizeof (struct mapfs));
457 new->mfs_next = NULL;
458 if (distance)
459 new->mfs_distance = distance;
460 if (!*mfs_head)
461 *mfs_tail = *mfs_head = new;
462 else {
463 (*mfs_tail)->mfs_next = new;
464 *mfs_tail = new;
465 }
466 return (*mfs_head);
467 }
468
469 static void
dump_mfs(struct mapfs * mfs,char * message,int level)470 dump_mfs(struct mapfs *mfs, char *message, int level)
471 {
472 struct mapfs *m1;
473
474 if (trace <= level)
475 return;
476
477 trace_prt(1, "%s", message);
478 if (!mfs) {
479 trace_prt(0, "mfs is null\n");
480 return;
481 }
482 for (m1 = mfs; m1; m1 = m1->mfs_next)
483 trace_prt(0, "%s[%s] ", m1->mfs_host, dump_distance(m1));
484 trace_prt(0, "\n");
485 }
486
487 static char *
dump_distance(struct mapfs * mfs)488 dump_distance(struct mapfs *mfs)
489 {
490 switch (mfs->mfs_distance) {
491 case 0: return ("zero");
492 case DIST_SELF: return ("self");
493 case DIST_MYSUB: return ("mysub");
494 case DIST_MYNET: return ("mynet");
495 case DIST_OTHER: return ("other");
496 default: return ("other");
497 }
498 }
499
500 /*
501 * Walk linked list "raw", building a new list consisting of members
502 * NOT found in list "filter", returning the result.
503 */
504 static struct mapfs *
filter_mfs(struct mapfs * raw,struct mapfs * filter)505 filter_mfs(struct mapfs *raw, struct mapfs *filter)
506 {
507 struct mapfs *mfs, *p, *mfs_head = NULL, *mfs_tail = NULL;
508 int skip;
509
510 if (!raw)
511 return (NULL);
512 for (mfs = raw; mfs; mfs = mfs->mfs_next) {
513 for (skip = 0, p = filter; p; p = p->mfs_next) {
514 if (strcmp(p->mfs_host, mfs->mfs_host) == 0 &&
515 strcmp(p->mfs_dir, mfs->mfs_dir) == 0) {
516 skip = 1;
517 break;
518 }
519 }
520 if (skip)
521 continue;
522 p = add_mfs(mfs, 0, &mfs_head, &mfs_tail);
523 if (!p)
524 return (NULL);
525 }
526 return (mfs_head);
527 }
528
529 /*
530 * Walk a linked list of mapfs structs, freeing each member.
531 */
532 void
free_mfs(struct mapfs * mfs)533 free_mfs(struct mapfs *mfs)
534 {
535 struct mapfs *tmp;
536
537 while (mfs) {
538 tmp = mfs->mfs_next;
539 free(mfs);
540 mfs = tmp;
541 }
542 }
543
544 /*
545 * New code for NFS client failover: we need to carry and sort
546 * lists of server possibilities rather than return a single
547 * entry. It preserves previous behaviour of sorting first by
548 * locality (loopback-or-preferred/subnet/net/other) and then
549 * by ping times. We'll short-circuit this process when we
550 * have ENOUGH or more entries.
551 */
552 static struct mapfs *
enum_servers(struct mapent * me,char * preferred)553 enum_servers(struct mapent *me, char *preferred)
554 {
555 struct mapfs *p, *m1, *m2, *mfs_head = NULL, *mfs_tail = NULL;
556
557 /*
558 * Short-circuit for simple cases.
559 */
560 if (!me->map_fs->mfs_next) {
561 p = add_mfs(me->map_fs, DIST_OTHER, &mfs_head, &mfs_tail);
562 if (!p)
563 return (NULL);
564 return (mfs_head);
565 }
566
567 dump_mfs(me->map_fs, " enum_servers: mapent: ", 2);
568
569 /*
570 * get addresses & see if any are myself
571 * or were mounted from previously in a
572 * hierarchical mount.
573 */
574 if (trace > 2)
575 trace_prt(1, " enum_servers: looking for pref/self\n");
576 for (m1 = me->map_fs; m1; m1 = m1->mfs_next) {
577 if (m1->mfs_ignore)
578 continue;
579 if (self_check(m1->mfs_host) ||
580 strcmp(m1->mfs_host, preferred) == 0) {
581 p = add_mfs(m1, DIST_SELF, &mfs_head, &mfs_tail);
582 if (!p)
583 return (NULL);
584 }
585 }
586 if (trace > 2 && m1)
587 trace_prt(1, " enum_servers: pref/self found, %s\n",
588 m1->mfs_host);
589
590 /*
591 * look for entries on this subnet
592 */
593 dump_mfs(m1, " enum_servers: input of get_mysubnet_servers: ", 2);
594 m1 = get_mysubnet_servers(me->map_fs);
595 dump_mfs(m1, " enum_servers: output of get_mysubnet_servers: ", 3);
596 if (m1 && m1->mfs_next) {
597 m2 = sort_servers(m1, rpc_timeout / 2);
598 dump_mfs(m2, " enum_servers: output of sort_servers: ", 3);
599 free_mfs(m1);
600 m1 = m2;
601 }
602
603 for (m2 = m1; m2; m2 = m2->mfs_next) {
604 p = add_mfs(m2, 0, &mfs_head, &mfs_tail);
605 if (!p)
606 return (NULL);
607 }
608 if (m1)
609 free_mfs(m1);
610
611 /*
612 * add the rest of the entries at the end
613 */
614 m1 = filter_mfs(me->map_fs, mfs_head);
615 dump_mfs(m1, " enum_servers: etc: output of filter_mfs: ", 3);
616 m2 = sort_servers(m1, rpc_timeout / 2);
617 dump_mfs(m2, " enum_servers: etc: output of sort_servers: ", 3);
618 if (m1)
619 free_mfs(m1);
620 m1 = m2;
621 for (m2 = m1; m2; m2 = m2->mfs_next) {
622 p = add_mfs(m2, DIST_OTHER, &mfs_head, &mfs_tail);
623 if (!p)
624 return (NULL);
625 }
626 if (m1)
627 free_mfs(m1);
628
629 done:
630 dump_mfs(mfs_head, " enum_servers: output: ", 1);
631 return (mfs_head);
632 }
633
634 static enum nfsstat
nfsmount(struct mapfs * mfs_in,char * mntpnt,char * opts,int overlay,uid_t uid,action_list * alp)635 nfsmount(
636 struct mapfs *mfs_in,
637 char *mntpnt, char *opts,
638 int overlay,
639 uid_t uid,
640 action_list *alp)
641 {
642 CLIENT *cl;
643 char remname[MAXPATHLEN], *mnttabtext = NULL;
644 char mopts[MAX_MNTOPT_STR];
645 char netname[MAXNETNAMELEN+1];
646 char *mntopts = NULL;
647 int mnttabcnt = 0;
648 int loglevel;
649 struct mnttab m;
650 struct nfs_args *argp = NULL, *head = NULL, *tail = NULL,
651 *prevhead, *prevtail;
652 int flags;
653 struct fhstatus fhs;
654 struct timeval timeout;
655 enum clnt_stat rpc_stat;
656 enum nfsstat status;
657 struct stat stbuf;
658 struct netconfig *nconf;
659 rpcvers_t vers, versmin; /* used to negotiate nfs version in pingnfs */
660 /* and mount version with mountd */
661 rpcvers_t outvers; /* final version to be used during mount() */
662 rpcvers_t nfsvers; /* version in map options, 0 if not there */
663 rpcvers_t mountversmax; /* tracks the max mountvers during retries */
664
665 /* used to negotiate nfs version using webnfs */
666 rpcvers_t pubvers, pubversmin, pubversmax;
667 int posix;
668 struct nd_addrlist *retaddrs;
669 struct mountres3 res3;
670 nfs_fh3 fh3;
671 char *fstype;
672 int count, i;
673 char scerror_msg[MAXMSGLEN];
674 int *auths;
675 int delay;
676 int retries;
677 char *nfs_proto = NULL;
678 uint_t nfs_port = 0;
679 char *p, *host, *rhost, *dir;
680 struct mapfs *mfs = NULL;
681 int error, last_error = 0;
682 int replicated;
683 int entries = 0;
684 int v2cnt = 0, v3cnt = 0, v4cnt = 0;
685 int v2near = 0, v3near = 0, v4near = 0;
686 int skipentry = 0;
687 char *nfs_flavor;
688 seconfig_t nfs_sec;
689 int sec_opt, scerror;
690 struct sec_data *secdata;
691 int secflags;
692 struct netbuf *syncaddr;
693 bool_t use_pubfh;
694 ushort_t thisport;
695 int got_val;
696 mfs_snego_t mfssnego_init, mfssnego;
697
698 dump_mfs(mfs_in, " nfsmount: input: ", 2);
699 replicated = (mfs_in->mfs_next != NULL);
700 m.mnt_mntopts = opts;
701 if (replicated && hasmntopt(&m, MNTOPT_SOFT)) {
702 if (verbose)
703 syslog(LOG_WARNING,
704 "mount on %s is soft and will not be replicated.", mntpnt);
705 replicated = 0;
706 }
707 if (replicated && !hasmntopt(&m, MNTOPT_RO)) {
708 if (verbose)
709 syslog(LOG_WARNING,
710 "mount on %s is not read-only and will not be replicated.",
711 mntpnt);
712 replicated = 0;
713 }
714 if (replicated)
715 loglevel = LOG_WARNING;
716 else
717 loglevel = LOG_ERR;
718
719 if (trace > 1) {
720 if (replicated)
721 trace_prt(1, " nfsmount: replicated mount on %s %s:\n",
722 mntpnt, opts);
723 else
724 trace_prt(1, " nfsmount: standard mount on %s %s:\n",
725 mntpnt, opts);
726 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next)
727 trace_prt(1, " %s:%s\n",
728 mfs->mfs_host, mfs->mfs_dir);
729 }
730
731 /*
732 * Make sure mountpoint is safe to mount on
733 */
734 if (lstat(mntpnt, &stbuf) < 0) {
735 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
736 return (NFSERR_NOENT);
737 }
738
739 /*
740 * Get protocol specified in options list, if any.
741 */
742 if ((str_opt(&m, "proto", &nfs_proto)) == -1) {
743 return (NFSERR_NOENT);
744 }
745
746 /*
747 * Get port specified in options list, if any.
748 */
749 got_val = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
750 if (!got_val)
751 nfs_port = 0; /* "unspecified" */
752 if (nfs_port > USHRT_MAX) {
753 syslog(LOG_ERR, "%s: invalid port number %d", mntpnt, nfs_port);
754 return (NFSERR_NOENT);
755 }
756
757 /*
758 * Set mount(2) flags here, outside of the loop.
759 */
760 flags = MS_OPTIONSTR;
761 flags |= (hasmntopt(&m, MNTOPT_RO) == NULL) ? 0 : MS_RDONLY;
762 flags |= (hasmntopt(&m, MNTOPT_NOSUID) == NULL) ? 0 : MS_NOSUID;
763 flags |= overlay ? MS_OVERLAY : 0;
764 if (mntpnt[strlen(mntpnt) - 1] != ' ')
765 /* direct mount point without offsets */
766 flags |= MS_OVERLAY;
767
768 use_pubfh = (hasmntopt(&m, MNTOPT_PUBLIC) == NULL) ? FALSE : TRUE;
769
770 (void) memset(&mfssnego_init, 0, sizeof (mfs_snego_t));
771 if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
772 if (++mfssnego_init.sec_opt > 1) {
773 syslog(loglevel,
774 "conflicting security options");
775 return (NFSERR_IO);
776 }
777 if (nfs_getseconfig_byname("dh", &mfssnego_init.nfs_sec)) {
778 syslog(loglevel,
779 "error getting dh information from %s",
780 NFSSEC_CONF);
781 return (NFSERR_IO);
782 }
783 }
784
785 if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
786 if ((str_opt(&m, MNTOPT_SEC,
787 &mfssnego_init.nfs_flavor)) == -1) {
788 syslog(LOG_ERR, "nfsmount: no memory");
789 return (NFSERR_IO);
790 }
791 }
792
793 if (mfssnego_init.nfs_flavor) {
794 if (++mfssnego_init.sec_opt > 1) {
795 syslog(loglevel,
796 "conflicting security options");
797 free(mfssnego_init.nfs_flavor);
798 return (NFSERR_IO);
799 }
800 if (nfs_getseconfig_byname(mfssnego_init.nfs_flavor,
801 &mfssnego_init.nfs_sec)) {
802 syslog(loglevel,
803 "error getting %s information from %s",
804 mfssnego_init.nfs_flavor, NFSSEC_CONF);
805 free(mfssnego_init.nfs_flavor);
806 return (NFSERR_IO);
807 }
808 free(mfssnego_init.nfs_flavor);
809 }
810
811 nextentry:
812 skipentry = 0;
813
814 got_val = nopt(&m, MNTOPT_VERS, (int *)&nfsvers);
815 if (!got_val)
816 nfsvers = 0; /* "unspecified" */
817 if (set_versrange(nfsvers, &vers, &versmin) != 0) {
818 syslog(LOG_ERR, "Incorrect NFS version specified for %s",
819 mntpnt);
820 last_error = NFSERR_NOENT;
821 goto ret;
822 }
823
824 if (nfsvers != 0) {
825 pubversmax = pubversmin = nfsvers;
826 } else {
827 pubversmax = vers;
828 pubversmin = versmin;
829 }
830
831 /*
832 * Walk the whole list, pinging and collecting version
833 * info so that we can make sure the mount will be
834 * homogeneous with respect to version.
835 *
836 * If we have a version preference, this is easy; we'll
837 * just reject anything that doesn't match.
838 *
839 * If not, we want to try to provide the best compromise
840 * that considers proximity, preference for a higher version,
841 * sorted order, and number of replicas. We will count
842 * the number of V2 and V3 replicas and also the number
843 * which are "near", i.e. the localhost or on the same
844 * subnet.
845 */
846 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
847
848
849 if (mfs->mfs_ignore)
850 continue;
851
852 /*
853 * If the host is '[a:d:d:r:e:s:s'],
854 * only use 'a:d:d:r:e:s:s' for communication
855 */
856 host = strdup(mfs->mfs_host);
857 if (host == NULL) {
858 syslog(LOG_ERR, "nfsmount: no memory");
859 last_error = NFSERR_IO;
860 goto out;
861 }
862 unbracket(&host);
863
864 (void) memcpy(&mfssnego, &mfssnego_init, sizeof (mfs_snego_t));
865
866 if (use_pubfh == TRUE || mfs->mfs_flags & MFS_URL) {
867 char *path;
868
869 if (nfs_port != 0 && mfs->mfs_port != 0 &&
870 nfs_port != mfs->mfs_port) {
871
872 syslog(LOG_ERR, "nfsmount: port (%u) in nfs URL"
873 " not the same as port (%d) in port "
874 "option\n", mfs->mfs_port, nfs_port);
875 last_error = NFSERR_IO;
876 goto out;
877
878 } else if (nfs_port != 0)
879 thisport = nfs_port;
880 else
881 thisport = mfs->mfs_port;
882
883 dir = mfs->mfs_dir;
884
885 if ((mfs->mfs_flags & MFS_URL) == 0) {
886 path = malloc(strlen(dir) + 2);
887 if (path == NULL) {
888 syslog(LOG_ERR, "nfsmount: no memory");
889 last_error = NFSERR_IO;
890 goto out;
891 }
892 path[0] = (char)WNL_NATIVEPATH;
893 (void) strcpy(&path[1], dir);
894 } else {
895 path = dir;
896 }
897
898 argp = (struct nfs_args *)
899 malloc(sizeof (struct nfs_args));
900
901 if (!argp) {
902 if (path != dir)
903 free(path);
904 syslog(LOG_ERR, "nfsmount: no memory");
905 last_error = NFSERR_IO;
906 goto out;
907 }
908 (void) memset(argp, 0, sizeof (*argp));
909
910 /*
911 * RDMA support
912 * By now Mount argument struct has been allocated,
913 * either a pub_fh path will be taken or the regular
914 * one. So here if a protocol was specified and it
915 * was not rdma we let it be, else we set DO_RDMA.
916 * If no proto was there we advise on trying RDMA.
917 */
918 if (nfs_proto) {
919 if (strcmp(nfs_proto, "rdma") == 0) {
920 free(nfs_proto);
921 nfs_proto = NULL;
922 argp->flags |= NFSMNT_DORDMA;
923 }
924 } else
925 argp->flags |= NFSMNT_TRYRDMA;
926
927 for (pubvers = pubversmax; pubvers >= pubversmin;
928 pubvers--) {
929
930 nconf = NULL;
931 argp->addr = get_pubfh(host, pubvers, &mfssnego,
932 &nconf, nfs_proto, thisport, NULL,
933 &argp->fh, TRUE, path);
934
935 if (argp->addr != NULL)
936 break;
937
938 if (nconf != NULL)
939 freenetconfigent(nconf);
940 }
941
942 if (path != dir)
943 free(path);
944
945 if (argp->addr != NULL) {
946
947 /*
948 * The use of llock option for NFSv4
949 * mounts is not required since file
950 * locking is included within the protocol
951 */
952 if (pubvers != NFS_V4)
953 argp->flags |= NFSMNT_LLOCK;
954
955 argp->flags |= NFSMNT_PUBLIC;
956
957 vers = pubvers;
958 mfs->mfs_args = argp;
959 mfs->mfs_version = pubvers;
960 mfs->mfs_nconf = nconf;
961 mfs->mfs_flags |= MFS_FH_VIA_WEBNFS;
962
963 } else {
964 free(argp);
965
966 /*
967 * If -public was specified, give up
968 * on this entry now.
969 */
970 if (use_pubfh == TRUE) {
971 syslog(loglevel,
972 "%s: no public file handle support",
973 host);
974 last_error = NFSERR_NOENT;
975 mfs->mfs_ignore = 1;
976 continue;
977 }
978
979 /*
980 * Back off to a conventional mount.
981 *
982 * URL's can contain escape characters. Get
983 * rid of them.
984 */
985 path = malloc(strlen(dir) + 2);
986
987 if (path == NULL) {
988 syslog(LOG_ERR, "nfsmount: no memory");
989 last_error = NFSERR_IO;
990 goto out;
991 }
992
993 strcpy(path, dir);
994 URLparse(path);
995 mfs->mfs_dir = path;
996 mfs->mfs_flags |= MFS_ALLOC_DIR;
997 mfs->mfs_flags &= ~MFS_URL;
998 }
999 }
1000
1001 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1002 i = pingnfs(host, get_retry(opts) + 1, &vers, versmin,
1003 0, FALSE, NULL, nfs_proto);
1004 if (i != RPC_SUCCESS) {
1005 if (i == RPC_PROGVERSMISMATCH) {
1006 syslog(loglevel, "server %s: NFS "
1007 "protocol version mismatch",
1008 host);
1009 } else {
1010 syslog(loglevel, "server %s not "
1011 "responding", host);
1012 }
1013 mfs->mfs_ignore = 1;
1014 last_error = NFSERR_NOENT;
1015 continue;
1016 }
1017 if (nfsvers != 0 && nfsvers != vers) {
1018 if (nfs_proto == NULL)
1019 syslog(loglevel,
1020 "NFS version %d "
1021 "not supported by %s",
1022 nfsvers, host);
1023 else
1024 syslog(loglevel,
1025 "NFS version %d "
1026 "with proto %s "
1027 "not supported by %s",
1028 nfsvers, nfs_proto, host);
1029 mfs->mfs_ignore = 1;
1030 last_error = NFSERR_NOENT;
1031 continue;
1032 }
1033 }
1034
1035 free(host);
1036
1037 switch (vers) {
1038 case NFS_V4: v4cnt++; break;
1039 case NFS_V3: v3cnt++; break;
1040 case NFS_VERSION: v2cnt++; break;
1041 default: break;
1042 }
1043
1044 /*
1045 * It's not clear how useful this stuff is if
1046 * we are using webnfs across the internet, but it
1047 * can't hurt.
1048 */
1049 if (mfs->mfs_distance &&
1050 mfs->mfs_distance <= DIST_MYSUB) {
1051 switch (vers) {
1052 case NFS_V4: v4near++; break;
1053 case NFS_V3: v3near++; break;
1054 case NFS_VERSION: v2near++; break;
1055 default: break;
1056 }
1057 }
1058
1059 /*
1060 * If the mount is not replicated, we don't want to
1061 * ping every entry, so we'll stop here. This means
1062 * that we may have to go back to "nextentry" above
1063 * to consider another entry if we can't get
1064 * all the way to mount(2) with this one.
1065 */
1066 if (!replicated)
1067 break;
1068
1069 }
1070
1071 if (nfsvers == 0) {
1072 /*
1073 * Choose the NFS version.
1074 * We prefer higher versions, but will choose a one-
1075 * version downgrade in service if we can use a local
1076 * network interface and avoid a router.
1077 */
1078 if (v4cnt && v4cnt >= v3cnt && (v4near || !v3near))
1079 nfsvers = NFS_V4;
1080 else if (v3cnt && v3cnt >= v2cnt && (v3near || !v2near))
1081 nfsvers = NFS_V3;
1082 else
1083 nfsvers = NFS_VERSION;
1084 if (trace > 2)
1085 trace_prt(1,
1086 " nfsmount: v4=%d[%d]v3=%d[%d],v2=%d[%d] => v%d.\n",
1087 v4cnt, v4near, v3cnt, v3near,
1088 v2cnt, v2near, nfsvers);
1089 }
1090
1091 /*
1092 * Since we don't support different NFS versions in replicated
1093 * mounts, set fstype now.
1094 * Also take the opportunity to set
1095 * the mount protocol version as appropriate.
1096 */
1097 switch (nfsvers) {
1098 case NFS_V4:
1099 fstype = MNTTYPE_NFS4;
1100 break;
1101 case NFS_V3:
1102 fstype = MNTTYPE_NFS3;
1103 if (use_pubfh == FALSE) {
1104 mountversmax = MOUNTVERS3;
1105 versmin = MOUNTVERS3;
1106 }
1107 break;
1108 case NFS_VERSION:
1109 fstype = MNTTYPE_NFS;
1110 if (use_pubfh == FALSE) {
1111 mountversmax = MOUNTVERS_POSIX;
1112 versmin = MOUNTVERS;
1113 }
1114 break;
1115 }
1116
1117 /*
1118 * Our goal here is to evaluate each of several possible
1119 * replicas and try to come up with a list we can hand
1120 * to mount(2). If we don't have a valid "head" at the
1121 * end of this process, it means we have rejected all
1122 * potential server:/path tuples. We will fail quietly
1123 * in front of mount(2), and will have printed errors
1124 * where we found them.
1125 * XXX - do option work outside loop w careful design
1126 * XXX - use macro for error condition free handling
1127 */
1128 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
1129
1130 /*
1131 * Initialize retry and delay values on a per-server basis.
1132 */
1133 retries = get_retry(opts);
1134 delay = INITDELAY;
1135 retry:
1136 if (mfs->mfs_ignore)
1137 continue;
1138
1139 /*
1140 * If we don't have a fh yet, and if this is not a replicated
1141 * mount, we haven't done a pingnfs() on the next entry,
1142 * so we don't know if the next entry is up or if it
1143 * supports an NFS version we like. So if we had a problem
1144 * with an entry, we need to go back and run through some new
1145 * code.
1146 */
1147 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1148 !replicated && skipentry)
1149 goto nextentry;
1150
1151 vers = mountversmax;
1152 host = mfs->mfs_host;
1153 dir = mfs->mfs_dir;
1154
1155 /*
1156 * Remember the possible '[a:d:d:r:e:s:s]' as the address to be
1157 * later passed to mount(2) and used in the mnttab line, but
1158 * only use 'a:d:d:r:e:s:s' for communication
1159 */
1160 rhost = strdup(host);
1161 if (rhost == NULL) {
1162 syslog(LOG_ERR, "nfsmount: no memory");
1163 last_error = NFSERR_IO;
1164 goto out;
1165 }
1166 unbracket(&host);
1167
1168 (void) sprintf(remname, "%s:%s", rhost, dir);
1169 if (trace > 4 && replicated)
1170 trace_prt(1, " nfsmount: examining %s\n", remname);
1171
1172 if (mfs->mfs_args == NULL) {
1173
1174 /*
1175 * Allocate nfs_args structure
1176 */
1177 argp = (struct nfs_args *)
1178 malloc(sizeof (struct nfs_args));
1179
1180 if (!argp) {
1181 syslog(LOG_ERR, "nfsmount: no memory");
1182 last_error = NFSERR_IO;
1183 goto out;
1184 }
1185
1186 (void) memset(argp, 0, sizeof (*argp));
1187
1188 /*
1189 * RDMA support
1190 * By now Mount argument struct has been allocated,
1191 * either a pub_fh path will be taken or the regular
1192 * one. So here if a protocol was specified and it
1193 * was not rdma we let it be, else we set DO_RDMA.
1194 * If no proto was there we advise on trying RDMA.
1195 */
1196 if (nfs_proto) {
1197 if (strcmp(nfs_proto, "rdma") == 0) {
1198 free(nfs_proto);
1199 nfs_proto = NULL;
1200 argp->flags |= NFSMNT_DORDMA;
1201 }
1202 } else
1203 argp->flags |= NFSMNT_TRYRDMA;
1204 } else {
1205 argp = mfs->mfs_args;
1206 mfs->mfs_args = NULL;
1207
1208 /*
1209 * Skip entry if we already have file handle but the
1210 * NFS version is wrong.
1211 */
1212 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) &&
1213 mfs->mfs_version != nfsvers) {
1214
1215 free(argp);
1216 skipentry = 1;
1217 mfs->mfs_ignore = 1;
1218 continue;
1219 }
1220 }
1221
1222 prevhead = head;
1223 prevtail = tail;
1224 if (!head)
1225 head = tail = argp;
1226 else
1227 tail = tail->nfs_ext_u.nfs_extB.next = argp;
1228
1229 /*
1230 * WebNFS and NFSv4 behave similarly in that they
1231 * don't use the mount protocol. Therefore, avoid
1232 * mount protocol like things when version 4 is being
1233 * used.
1234 */
1235 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1236 nfsvers != NFS_V4) {
1237 timeout.tv_usec = 0;
1238 timeout.tv_sec = rpc_timeout;
1239 rpc_stat = RPC_TIMEDOUT;
1240
1241 /* Create the client handle. */
1242
1243 if (trace > 1) {
1244 trace_prt(1,
1245 " nfsmount: Get mount version: request "
1246 "vers=%d min=%d\n", vers, versmin);
1247 }
1248
1249 while ((cl = clnt_create_vers(host, MOUNTPROG, &outvers,
1250 versmin, vers, "udp")) == NULL) {
1251 if (trace > 4) {
1252 trace_prt(1,
1253 " nfsmount: Can't get mount "
1254 "version: rpcerr=%d\n",
1255 rpc_createerr.cf_stat);
1256 }
1257 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
1258 rpc_createerr.cf_stat == RPC_TIMEDOUT)
1259 break;
1260
1261 /*
1262 * backoff and return lower version to retry the ping.
1263 * XXX we should be more careful and handle
1264 * RPC_PROGVERSMISMATCH here, because that error
1265 * is handled in clnt_create_vers(). It's not done to
1266 * stay in sync with the nfs mount command.
1267 */
1268 vers--;
1269 if (vers < versmin)
1270 break;
1271 if (trace > 4) {
1272 trace_prt(1,
1273 " nfsmount: Try version=%d\n",
1274 vers);
1275 }
1276 }
1277
1278 if (cl == NULL) {
1279 free(argp);
1280 head = prevhead;
1281 tail = prevtail;
1282 if (tail)
1283 tail->nfs_ext_u.nfs_extB.next = NULL;
1284 last_error = NFSERR_NOENT;
1285
1286 if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST &&
1287 rpc_createerr.cf_stat !=
1288 RPC_PROGVERSMISMATCH &&
1289 retries-- > 0) {
1290 DELAY(delay);
1291 goto retry;
1292 }
1293
1294 syslog(loglevel, "%s %s", host,
1295 clnt_spcreateerror(
1296 "server not responding"));
1297 skipentry = 1;
1298 mfs->mfs_ignore = 1;
1299 continue;
1300 }
1301 if (trace > 1) {
1302 trace_prt(1,
1303 " nfsmount: mount version=%d\n", outvers);
1304 }
1305 #ifdef MALLOC_DEBUG
1306 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
1307 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1308 __FILE__, __LINE__);
1309 #endif
1310
1311 if (__clnt_bindresvport(cl) < 0) {
1312 free(argp);
1313 head = prevhead;
1314 tail = prevtail;
1315 if (tail)
1316 tail->nfs_ext_u.nfs_extB.next = NULL;
1317 last_error = NFSERR_NOENT;
1318
1319 if (retries-- > 0) {
1320 destroy_auth_client_handle(cl);
1321 DELAY(delay);
1322 goto retry;
1323 }
1324
1325 syslog(loglevel, "mount %s: %s", host,
1326 "Couldn't bind to reserved port");
1327 destroy_auth_client_handle(cl);
1328 skipentry = 1;
1329 mfs->mfs_ignore = 1;
1330 continue;
1331 }
1332
1333 #ifdef MALLOC_DEBUG
1334 drop_alloc("AUTH_HANDLE", cl->cl_auth,
1335 __FILE__, __LINE__);
1336 #endif
1337 AUTH_DESTROY(cl->cl_auth);
1338 if ((cl->cl_auth = authsys_create_default()) == NULL) {
1339 free(argp);
1340 head = prevhead;
1341 tail = prevtail;
1342 if (tail)
1343 tail->nfs_ext_u.nfs_extB.next = NULL;
1344 last_error = NFSERR_NOENT;
1345
1346 if (retries-- > 0) {
1347 destroy_auth_client_handle(cl);
1348 DELAY(delay);
1349 goto retry;
1350 }
1351
1352 syslog(loglevel, "mount %s: %s", host,
1353 "Failed creating default auth handle");
1354 destroy_auth_client_handle(cl);
1355 skipentry = 1;
1356 mfs->mfs_ignore = 1;
1357 continue;
1358 }
1359 #ifdef MALLOC_DEBUG
1360 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
1361 __FILE__, __LINE__);
1362 #endif
1363 } else
1364 cl = NULL;
1365
1366 /*
1367 * set security options
1368 */
1369 sec_opt = 0;
1370 (void) memset(&nfs_sec, 0, sizeof (nfs_sec));
1371 if (hasmntopt(&m, MNTOPT_SECURE) != NULL) {
1372 if (++sec_opt > 1) {
1373 syslog(loglevel,
1374 "conflicting security options for %s",
1375 remname);
1376 free(argp);
1377 head = prevhead;
1378 tail = prevtail;
1379 if (tail)
1380 tail->nfs_ext_u.nfs_extB.next = NULL;
1381 last_error = NFSERR_IO;
1382 destroy_auth_client_handle(cl);
1383 skipentry = 1;
1384 mfs->mfs_ignore = 1;
1385 continue;
1386 }
1387 if (nfs_getseconfig_byname("dh", &nfs_sec)) {
1388 syslog(loglevel,
1389 "error getting dh information from %s",
1390 NFSSEC_CONF);
1391 free(argp);
1392 head = prevhead;
1393 tail = prevtail;
1394 if (tail)
1395 tail->nfs_ext_u.nfs_extB.next = NULL;
1396 last_error = NFSERR_IO;
1397 destroy_auth_client_handle(cl);
1398 skipentry = 1;
1399 mfs->mfs_ignore = 1;
1400 continue;
1401 }
1402 }
1403
1404 nfs_flavor = NULL;
1405 if (hasmntopt(&m, MNTOPT_SEC) != NULL) {
1406 if ((str_opt(&m, MNTOPT_SEC, &nfs_flavor)) == -1) {
1407 syslog(LOG_ERR, "nfsmount: no memory");
1408 last_error = NFSERR_IO;
1409 destroy_auth_client_handle(cl);
1410 goto out;
1411 }
1412 }
1413
1414 if (nfs_flavor) {
1415 if (++sec_opt > 1) {
1416 syslog(loglevel,
1417 "conflicting security options for %s",
1418 remname);
1419 free(nfs_flavor);
1420 free(argp);
1421 head = prevhead;
1422 tail = prevtail;
1423 if (tail)
1424 tail->nfs_ext_u.nfs_extB.next = NULL;
1425 last_error = NFSERR_IO;
1426 destroy_auth_client_handle(cl);
1427 skipentry = 1;
1428 mfs->mfs_ignore = 1;
1429 continue;
1430 }
1431 if (nfs_getseconfig_byname(nfs_flavor, &nfs_sec)) {
1432 syslog(loglevel,
1433 "error getting %s information from %s",
1434 nfs_flavor, NFSSEC_CONF);
1435 free(nfs_flavor);
1436 free(argp);
1437 head = prevhead;
1438 tail = prevtail;
1439 if (tail)
1440 tail->nfs_ext_u.nfs_extB.next = NULL;
1441 last_error = NFSERR_IO;
1442 destroy_auth_client_handle(cl);
1443 skipentry = 1;
1444 mfs->mfs_ignore = 1;
1445 continue;
1446 }
1447 free(nfs_flavor);
1448 }
1449
1450 posix = (nfsvers != NFS_V4 &&
1451 hasmntopt(&m, MNTOPT_POSIX) != NULL) ? 1 : 0;
1452
1453 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1454 nfsvers != NFS_V4) {
1455 bool_t give_up_on_mnt;
1456 bool_t got_mnt_error;
1457 /*
1458 * If we started with a URL, if first byte of path is not "/",
1459 * then the mount will likely fail, so we should try again
1460 * with a prepended "/".
1461 */
1462 if (mfs->mfs_flags & MFS_ALLOC_DIR && *dir != '/')
1463 give_up_on_mnt = FALSE;
1464 else
1465 give_up_on_mnt = TRUE;
1466
1467 got_mnt_error = FALSE;
1468
1469 try_mnt_slash:
1470 if (got_mnt_error == TRUE) {
1471 int i, l;
1472
1473 give_up_on_mnt = TRUE;
1474 l = strlen(dir);
1475
1476 /*
1477 * Insert a "/" to front of mfs_dir.
1478 */
1479 for (i = l; i > 0; i--)
1480 dir[i] = dir[i-1];
1481
1482 dir[0] = '/';
1483 }
1484
1485 /* Get fhandle of remote path from server's mountd */
1486
1487 switch (outvers) {
1488 case MOUNTVERS:
1489 if (posix) {
1490 free(argp);
1491 head = prevhead;
1492 tail = prevtail;
1493 if (tail)
1494 tail->nfs_ext_u.nfs_extB.next =
1495 NULL;
1496 last_error = NFSERR_NOENT;
1497 syslog(loglevel,
1498 "can't get posix info for %s",
1499 host);
1500 destroy_auth_client_handle(cl);
1501 skipentry = 1;
1502 mfs->mfs_ignore = 1;
1503 continue;
1504 }
1505 /* FALLTHRU */
1506 case MOUNTVERS_POSIX:
1507 if (nfsvers == NFS_V3) {
1508 free(argp);
1509 head = prevhead;
1510 tail = prevtail;
1511 if (tail)
1512 tail->nfs_ext_u.nfs_extB.next =
1513 NULL;
1514 last_error = NFSERR_NOENT;
1515 syslog(loglevel,
1516 "%s doesn't support NFS Version 3",
1517 host);
1518 destroy_auth_client_handle(cl);
1519 skipentry = 1;
1520 mfs->mfs_ignore = 1;
1521 continue;
1522 }
1523 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1524 xdr_dirpath, (caddr_t)&dir,
1525 xdr_fhstatus, (caddr_t)&fhs, timeout);
1526 if (rpc_stat != RPC_SUCCESS) {
1527
1528 if (give_up_on_mnt == FALSE) {
1529 got_mnt_error = TRUE;
1530 goto try_mnt_slash;
1531 }
1532
1533 /*
1534 * Given the way "clnt_sperror" works, the "%s"
1535 * immediately following the "not responding"
1536 * is correct.
1537 */
1538 free(argp);
1539 head = prevhead;
1540 tail = prevtail;
1541 if (tail)
1542 tail->nfs_ext_u.nfs_extB.next =
1543 NULL;
1544 last_error = NFSERR_NOENT;
1545
1546 if (retries-- > 0) {
1547 destroy_auth_client_handle(cl);
1548 DELAY(delay);
1549 goto retry;
1550 }
1551
1552 if (trace > 3) {
1553 trace_prt(1,
1554 " nfsmount: mount RPC "
1555 "failed for %s\n",
1556 host);
1557 }
1558 syslog(loglevel,
1559 "%s server not responding%s",
1560 host, clnt_sperror(cl, ""));
1561 destroy_auth_client_handle(cl);
1562 skipentry = 1;
1563 mfs->mfs_ignore = 1;
1564 continue;
1565 }
1566 if ((errno = fhs.fhs_status) != MNT_OK) {
1567
1568 if (give_up_on_mnt == FALSE) {
1569 got_mnt_error = TRUE;
1570 goto try_mnt_slash;
1571 }
1572
1573 free(argp);
1574 head = prevhead;
1575 tail = prevtail;
1576 if (tail)
1577 tail->nfs_ext_u.nfs_extB.next =
1578 NULL;
1579 if (errno == EACCES) {
1580 status = NFSERR_ACCES;
1581 } else {
1582 syslog(loglevel, "%s: %m",
1583 host);
1584 status = NFSERR_IO;
1585 }
1586 if (trace > 3) {
1587 trace_prt(1,
1588 " nfsmount: mount RPC gave"
1589 " %d for %s:%s\n",
1590 errno, host, dir);
1591 }
1592 last_error = status;
1593 destroy_auth_client_handle(cl);
1594 skipentry = 1;
1595 mfs->mfs_ignore = 1;
1596 continue;
1597 }
1598 argp->fh = malloc((sizeof (fhandle)));
1599 if (!argp->fh) {
1600 syslog(LOG_ERR, "nfsmount: no memory");
1601 last_error = NFSERR_IO;
1602 destroy_auth_client_handle(cl);
1603 goto out;
1604 }
1605 (void) memcpy(argp->fh,
1606 &fhs.fhstatus_u.fhs_fhandle,
1607 sizeof (fhandle));
1608 break;
1609 case MOUNTVERS3:
1610 posix = 0;
1611 (void) memset((char *)&res3, '\0',
1612 sizeof (res3));
1613 rpc_stat = clnt_call(cl, MOUNTPROC_MNT,
1614 xdr_dirpath, (caddr_t)&dir,
1615 xdr_mountres3, (caddr_t)&res3, timeout);
1616 if (rpc_stat != RPC_SUCCESS) {
1617
1618 if (give_up_on_mnt == FALSE) {
1619 got_mnt_error = TRUE;
1620 goto try_mnt_slash;
1621 }
1622
1623 /*
1624 * Given the way "clnt_sperror" works, the "%s"
1625 * immediately following the "not responding"
1626 * is correct.
1627 */
1628 free(argp);
1629 head = prevhead;
1630 tail = prevtail;
1631 if (tail)
1632 tail->nfs_ext_u.nfs_extB.next =
1633 NULL;
1634 last_error = NFSERR_NOENT;
1635
1636 if (retries-- > 0) {
1637 destroy_auth_client_handle(cl);
1638 DELAY(delay);
1639 goto retry;
1640 }
1641
1642 if (trace > 3) {
1643 trace_prt(1,
1644 " nfsmount: mount RPC "
1645 "failed for %s\n",
1646 host);
1647 }
1648 syslog(loglevel,
1649 "%s server not responding%s",
1650 remname, clnt_sperror(cl, ""));
1651 destroy_auth_client_handle(cl);
1652 skipentry = 1;
1653 mfs->mfs_ignore = 1;
1654 continue;
1655 }
1656 if ((errno = res3.fhs_status) != MNT_OK) {
1657
1658 if (give_up_on_mnt == FALSE) {
1659 got_mnt_error = TRUE;
1660 goto try_mnt_slash;
1661 }
1662
1663 free(argp);
1664 head = prevhead;
1665 tail = prevtail;
1666 if (tail)
1667 tail->nfs_ext_u.nfs_extB.next =
1668 NULL;
1669 if (errno == EACCES) {
1670 status = NFSERR_ACCES;
1671 } else {
1672 syslog(loglevel, "%s: %m",
1673 remname);
1674 status = NFSERR_IO;
1675 }
1676 if (trace > 3) {
1677 trace_prt(1,
1678 " nfsmount: mount RPC gave"
1679 " %d for %s:%s\n",
1680 errno, host, dir);
1681 }
1682 last_error = status;
1683 destroy_auth_client_handle(cl);
1684 skipentry = 1;
1685 mfs->mfs_ignore = 1;
1686 continue;
1687 }
1688
1689 /*
1690 * Negotiate the security flavor for nfs_mount
1691 */
1692 auths = res3.mountres3_u.mountinfo.
1693 auth_flavors.auth_flavors_val;
1694 count = res3.mountres3_u.mountinfo.
1695 auth_flavors.auth_flavors_len;
1696
1697 if (sec_opt) {
1698 for (i = 0; i < count; i++)
1699 if (auths[i] ==
1700 nfs_sec.sc_nfsnum) {
1701 break;
1702 }
1703 if (i >= count) {
1704 syslog(LOG_ERR,
1705 "%s: does not support "
1706 "security \"%s\"\n",
1707 remname, nfs_sec.sc_name);
1708 clnt_freeres(cl, xdr_mountres3,
1709 (caddr_t)&res3);
1710 free(argp);
1711 head = prevhead;
1712 tail = prevtail;
1713 if (tail)
1714 tail->nfs_ext_u.
1715 nfs_extB.next =
1716 NULL;
1717 last_error = NFSERR_IO;
1718 destroy_auth_client_handle(cl);
1719 skipentry = 1;
1720 mfs->mfs_ignore = 1;
1721 continue;
1722 }
1723 } else if (count > 0) {
1724 for (i = 0; i < count; i++) {
1725 if (!(scerror =
1726 nfs_getseconfig_bynumber(
1727 auths[i], &nfs_sec))) {
1728 sec_opt++;
1729 break;
1730 }
1731 }
1732 if (i >= count) {
1733 if (nfs_syslog_scerr(scerror,
1734 scerror_msg)
1735 != -1) {
1736 syslog(LOG_ERR,
1737 "%s cannot be "
1738 "mounted because it"
1739 " is shared with "
1740 "security flavor %d"
1741 " which %s",
1742 remname,
1743 auths[i-1],
1744 scerror_msg);
1745 }
1746 clnt_freeres(cl, xdr_mountres3,
1747 (caddr_t)&res3);
1748 free(argp);
1749 head = prevhead;
1750 tail = prevtail;
1751 if (tail)
1752 tail->nfs_ext_u.
1753 nfs_extB.next =
1754 NULL;
1755 last_error = NFSERR_IO;
1756 destroy_auth_client_handle(cl);
1757 skipentry = 1;
1758 mfs->mfs_ignore = 1;
1759 continue;
1760 }
1761 }
1762
1763 fh3.fh3_length =
1764 res3.mountres3_u.mountinfo.fhandle.
1765 fhandle3_len;
1766 (void) memcpy(fh3.fh3_u.data,
1767 res3.mountres3_u.mountinfo.fhandle.
1768 fhandle3_val,
1769 fh3.fh3_length);
1770 clnt_freeres(cl, xdr_mountres3,
1771 (caddr_t)&res3);
1772 argp->fh = malloc(sizeof (nfs_fh3));
1773 if (!argp->fh) {
1774 syslog(LOG_ERR, "nfsmount: no memory");
1775 last_error = NFSERR_IO;
1776 destroy_auth_client_handle(cl);
1777 goto out;
1778 }
1779 (void) memcpy(argp->fh, &fh3, sizeof (nfs_fh3));
1780 break;
1781 default:
1782 free(argp);
1783 head = prevhead;
1784 tail = prevtail;
1785 if (tail)
1786 tail->nfs_ext_u.nfs_extB.next = NULL;
1787 last_error = NFSERR_NOENT;
1788 syslog(loglevel,
1789 "unknown MOUNT version %ld on %s",
1790 vers, remname);
1791 destroy_auth_client_handle(cl);
1792 skipentry = 1;
1793 mfs->mfs_ignore = 1;
1794 continue;
1795 } /* switch */
1796 }
1797 if (nfsvers == NFS_V4) {
1798 argp->fh = strdup(dir);
1799 if (argp->fh == NULL) {
1800 syslog(LOG_ERR, "nfsmount: no memory");
1801 last_error = NFSERR_IO;
1802 goto out;
1803 }
1804 }
1805
1806 if (trace > 4)
1807 trace_prt(1, " nfsmount: have %s filehandle for %s\n",
1808 fstype, remname);
1809
1810 argp->flags |= NFSMNT_NEWARGS;
1811 argp->flags |= NFSMNT_INT; /* default is "intr" */
1812 argp->flags |= NFSMNT_HOSTNAME;
1813 argp->hostname = strdup(host);
1814 if (argp->hostname == NULL) {
1815 syslog(LOG_ERR, "nfsmount: no memory");
1816 last_error = NFSERR_IO;
1817 goto out;
1818 }
1819
1820 /*
1821 * In this case, we want NFSv4 to behave like
1822 * non-WebNFS so that we get the server address.
1823 */
1824 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0) {
1825 nconf = NULL;
1826
1827 if (nfs_port != 0)
1828 thisport = nfs_port;
1829 else
1830 thisport = mfs->mfs_port;
1831
1832 /*
1833 * For NFSv4, we want to avoid rpcbind, so call
1834 * get_server_netinfo() directly to tell it that
1835 * we want to go "direct_to_server". Otherwise,
1836 * do what has always been done.
1837 */
1838 if (nfsvers == NFS_V4) {
1839 enum clnt_stat cstat;
1840
1841 argp->addr = get_server_netinfo(SERVER_ADDR,
1842 host, NFS_PROGRAM, nfsvers, NULL,
1843 &nconf, nfs_proto, thisport, NULL,
1844 NULL, TRUE, NULL, &cstat);
1845 } else {
1846 argp->addr = get_addr(host, NFS_PROGRAM,
1847 nfsvers, &nconf, nfs_proto,
1848 thisport, NULL);
1849 }
1850
1851 if (argp->addr == NULL) {
1852 if (argp->hostname)
1853 free(argp->hostname);
1854 free(argp->fh);
1855 free(argp);
1856 head = prevhead;
1857 tail = prevtail;
1858 if (tail)
1859 tail->nfs_ext_u.nfs_extB.next = NULL;
1860 last_error = NFSERR_NOENT;
1861
1862 if (retries-- > 0) {
1863 destroy_auth_client_handle(cl);
1864 DELAY(delay);
1865 goto retry;
1866 }
1867
1868 syslog(loglevel, "%s: no NFS service", host);
1869 destroy_auth_client_handle(cl);
1870 skipentry = 1;
1871 mfs->mfs_ignore = 1;
1872 continue;
1873 }
1874 if (trace > 4)
1875 trace_prt(1,
1876 "\tnfsmount: have net address for %s\n",
1877 remname);
1878
1879 } else {
1880 nconf = mfs->mfs_nconf;
1881 mfs->mfs_nconf = NULL;
1882 }
1883
1884 argp->flags |= NFSMNT_KNCONF;
1885 argp->knconf = get_knconf(nconf);
1886 if (argp->knconf == NULL) {
1887 netbuf_free(argp->addr);
1888 freenetconfigent(nconf);
1889 if (argp->hostname)
1890 free(argp->hostname);
1891 free(argp->fh);
1892 free(argp);
1893 head = prevhead;
1894 tail = prevtail;
1895 if (tail)
1896 tail->nfs_ext_u.nfs_extB.next = NULL;
1897 last_error = NFSERR_NOSPC;
1898 destroy_auth_client_handle(cl);
1899 skipentry = 1;
1900 mfs->mfs_ignore = 1;
1901 continue;
1902 }
1903 if (trace > 4)
1904 trace_prt(1,
1905 "\tnfsmount: have net config for %s\n",
1906 remname);
1907
1908 if (hasmntopt(&m, MNTOPT_SOFT) != NULL) {
1909 argp->flags |= NFSMNT_SOFT;
1910 }
1911 if (hasmntopt(&m, MNTOPT_NOINTR) != NULL) {
1912 argp->flags &= ~(NFSMNT_INT);
1913 }
1914 if (hasmntopt(&m, MNTOPT_NOAC) != NULL) {
1915 argp->flags |= NFSMNT_NOAC;
1916 }
1917 if (hasmntopt(&m, MNTOPT_NOCTO) != NULL) {
1918 argp->flags |= NFSMNT_NOCTO;
1919 }
1920 if (hasmntopt(&m, MNTOPT_FORCEDIRECTIO) != NULL) {
1921 argp->flags |= NFSMNT_DIRECTIO;
1922 }
1923 if (hasmntopt(&m, MNTOPT_NOFORCEDIRECTIO) != NULL) {
1924 argp->flags &= ~(NFSMNT_DIRECTIO);
1925 }
1926
1927 /*
1928 * Set up security data for argp->nfs_ext_u.nfs_extB.secdata.
1929 */
1930 if (mfssnego.snego_done) {
1931 memcpy(&nfs_sec, &mfssnego.nfs_sec,
1932 sizeof (seconfig_t));
1933 } else if (!sec_opt) {
1934 /*
1935 * Get default security mode.
1936 */
1937 if (nfs_getseconfig_default(&nfs_sec)) {
1938 syslog(loglevel,
1939 "error getting default security entry\n");
1940 free_knconf(argp->knconf);
1941 netbuf_free(argp->addr);
1942 freenetconfigent(nconf);
1943 if (argp->hostname)
1944 free(argp->hostname);
1945 free(argp->fh);
1946 free(argp);
1947 head = prevhead;
1948 tail = prevtail;
1949 if (tail)
1950 tail->nfs_ext_u.nfs_extB.next = NULL;
1951 last_error = NFSERR_NOSPC;
1952 destroy_auth_client_handle(cl);
1953 skipentry = 1;
1954 mfs->mfs_ignore = 1;
1955 continue;
1956 }
1957 argp->flags |= NFSMNT_SECDEFAULT;
1958 }
1959
1960 /*
1961 * For AUTH_DH
1962 * get the network address for the time service on
1963 * the server. If an RPC based time service is
1964 * not available then try the IP time service.
1965 *
1966 * Eventurally, we want to move this code to nfs_clnt_secdata()
1967 * when autod_nfs.c and mount.c can share the same
1968 * get_the_addr/get_netconfig_info routine.
1969 */
1970 secflags = 0;
1971 syncaddr = NULL;
1972 retaddrs = NULL;
1973
1974 if (nfs_sec.sc_rpcnum == AUTH_DH || nfsvers == NFS_V4) {
1975 /*
1976 * If not using the public fh and not NFS_V4, we can try
1977 * talking RPCBIND. Otherwise, assume that firewalls
1978 * prevent us from doing that.
1979 */
1980 if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 &&
1981 nfsvers != NFS_V4) {
1982 enum clnt_stat cstat;
1983 syncaddr = get_server_netinfo(SERVER_ADDR,
1984 host, RPCBPROG, RPCBVERS, NULL, &nconf,
1985 NULL, 0, NULL, NULL, FALSE, NULL, &cstat);
1986 }
1987
1988 if (syncaddr != NULL) {
1989 /* for flags in sec_data */
1990 secflags |= AUTH_F_RPCTIMESYNC;
1991 } else {
1992 struct nd_hostserv hs;
1993 int error;
1994
1995 hs.h_host = host;
1996 hs.h_serv = "timserver";
1997 error = netdir_getbyname(nconf, &hs, &retaddrs);
1998
1999 if (error != ND_OK &&
2000 nfs_sec.sc_rpcnum == AUTH_DH) {
2001 syslog(loglevel,
2002 "%s: secure: no time service\n",
2003 host);
2004 free_knconf(argp->knconf);
2005 netbuf_free(argp->addr);
2006 freenetconfigent(nconf);
2007 if (argp->hostname)
2008 free(argp->hostname);
2009 free(argp->fh);
2010 free(argp);
2011 head = prevhead;
2012 tail = prevtail;
2013 if (tail)
2014 tail->nfs_ext_u.nfs_extB.next =
2015 NULL;
2016 last_error = NFSERR_IO;
2017 destroy_auth_client_handle(cl);
2018 skipentry = 1;
2019 mfs->mfs_ignore = 1;
2020 continue;
2021 }
2022
2023 if (error == ND_OK)
2024 syncaddr = retaddrs->n_addrs;
2025
2026 /*
2027 * For potential usage by NFS V4 when AUTH_DH
2028 * is negotiated via SECINFO in the kernel.
2029 */
2030 if (nfsvers == NFS_V4 && syncaddr &&
2031 host2netname(netname, host, NULL)) {
2032 argp->syncaddr =
2033 malloc(sizeof (struct netbuf));
2034 argp->syncaddr->buf =
2035 malloc(syncaddr->len);
2036 (void) memcpy(argp->syncaddr->buf,
2037 syncaddr->buf, syncaddr->len);
2038 argp->syncaddr->len = syncaddr->len;
2039 argp->syncaddr->maxlen =
2040 syncaddr->maxlen;
2041 argp->netname = strdup(netname);
2042 argp->flags |= NFSMNT_SECURE;
2043 }
2044 } /* syncaddr */
2045 } /* AUTH_DH */
2046
2047 /*
2048 * TSOL notes: automountd in tsol extension
2049 * has "read down" capability, i.e. we allow
2050 * a user to trigger an nfs mount into a lower
2051 * labeled zone. We achieve this by always having
2052 * root issue the mount request so that the
2053 * lookup ops can go past /zone/<zone_name>
2054 * on the server side.
2055 */
2056 if (is_system_labeled())
2057 nfs_sec.sc_uid = (uid_t)0;
2058 else
2059 nfs_sec.sc_uid = uid;
2060 /*
2061 * If AUTH_DH is a chosen flavor now, its data will be stored
2062 * in the sec_data structure via nfs_clnt_secdata().
2063 */
2064 if (!(secdata = nfs_clnt_secdata(&nfs_sec, host, argp->knconf,
2065 syncaddr, secflags))) {
2066 syslog(LOG_ERR,
2067 "errors constructing security related data\n");
2068 if (secflags & AUTH_F_RPCTIMESYNC)
2069 netbuf_free(syncaddr);
2070 else if (retaddrs)
2071 netdir_free(retaddrs, ND_ADDRLIST);
2072 if (argp->syncaddr)
2073 netbuf_free(argp->syncaddr);
2074 if (argp->netname)
2075 free(argp->netname);
2076 if (argp->hostname)
2077 free(argp->hostname);
2078 free_knconf(argp->knconf);
2079 netbuf_free(argp->addr);
2080 freenetconfigent(nconf);
2081 free(argp->fh);
2082 free(argp);
2083 head = prevhead;
2084 tail = prevtail;
2085 if (tail)
2086 tail->nfs_ext_u.nfs_extB.next = NULL;
2087 last_error = NFSERR_IO;
2088 destroy_auth_client_handle(cl);
2089 skipentry = 1;
2090 mfs->mfs_ignore = 1;
2091 continue;
2092 }
2093 NFS_ARGS_EXTB_secdata(*argp, secdata);
2094 /* end of security stuff */
2095
2096 if (trace > 4)
2097 trace_prt(1,
2098 " nfsmount: have secure info for %s\n", remname);
2099
2100 if (hasmntopt(&m, MNTOPT_GRPID) != NULL) {
2101 argp->flags |= NFSMNT_GRPID;
2102 }
2103 if (nopt(&m, MNTOPT_RSIZE, &argp->rsize)) {
2104 argp->flags |= NFSMNT_RSIZE;
2105 }
2106 if (nopt(&m, MNTOPT_WSIZE, &argp->wsize)) {
2107 argp->flags |= NFSMNT_WSIZE;
2108 }
2109 if (nopt(&m, MNTOPT_TIMEO, &argp->timeo)) {
2110 argp->flags |= NFSMNT_TIMEO;
2111 }
2112 if (nopt(&m, MNTOPT_RETRANS, &argp->retrans)) {
2113 argp->flags |= NFSMNT_RETRANS;
2114 }
2115 if (nopt(&m, MNTOPT_ACTIMEO, &argp->acregmax)) {
2116 argp->flags |= NFSMNT_ACREGMAX;
2117 argp->flags |= NFSMNT_ACDIRMAX;
2118 argp->flags |= NFSMNT_ACDIRMIN;
2119 argp->flags |= NFSMNT_ACREGMIN;
2120 argp->acdirmin = argp->acregmin = argp->acdirmax
2121 = argp->acregmax;
2122 } else {
2123 if (nopt(&m, MNTOPT_ACREGMIN, &argp->acregmin)) {
2124 argp->flags |= NFSMNT_ACREGMIN;
2125 }
2126 if (nopt(&m, MNTOPT_ACREGMAX, &argp->acregmax)) {
2127 argp->flags |= NFSMNT_ACREGMAX;
2128 }
2129 if (nopt(&m, MNTOPT_ACDIRMIN, &argp->acdirmin)) {
2130 argp->flags |= NFSMNT_ACDIRMIN;
2131 }
2132 if (nopt(&m, MNTOPT_ACDIRMAX, &argp->acdirmax)) {
2133 argp->flags |= NFSMNT_ACDIRMAX;
2134 }
2135 }
2136
2137 if (posix) {
2138 argp->pathconf = NULL;
2139 if (error = get_pathconf(cl, dir, remname,
2140 &argp->pathconf, retries)) {
2141 if (secflags & AUTH_F_RPCTIMESYNC)
2142 netbuf_free(syncaddr);
2143 else if (retaddrs)
2144 netdir_free(retaddrs, ND_ADDRLIST);
2145 free_knconf(argp->knconf);
2146 netbuf_free(argp->addr);
2147 freenetconfigent(nconf);
2148 nfs_free_secdata(
2149 argp->nfs_ext_u.nfs_extB.secdata);
2150 if (argp->syncaddr)
2151 netbuf_free(argp->syncaddr);
2152 if (argp->netname)
2153 free(argp->netname);
2154 if (argp->hostname)
2155 free(argp->hostname);
2156 free(argp->fh);
2157 free(argp);
2158 head = prevhead;
2159 tail = prevtail;
2160 if (tail)
2161 tail->nfs_ext_u.nfs_extB.next = NULL;
2162 last_error = NFSERR_IO;
2163
2164 if (error == RET_RETRY && retries-- > 0) {
2165 destroy_auth_client_handle(cl);
2166 DELAY(delay);
2167 goto retry;
2168 }
2169
2170 destroy_auth_client_handle(cl);
2171 skipentry = 1;
2172 mfs->mfs_ignore = 1;
2173 continue;
2174 }
2175 argp->flags |= NFSMNT_POSIX;
2176 if (trace > 4)
2177 trace_prt(1,
2178 " nfsmount: have pathconf for %s\n",
2179 remname);
2180 }
2181
2182 /*
2183 * free loop-specific data structures
2184 */
2185 destroy_auth_client_handle(cl);
2186 freenetconfigent(nconf);
2187 if (secflags & AUTH_F_RPCTIMESYNC)
2188 netbuf_free(syncaddr);
2189 else if (retaddrs)
2190 netdir_free(retaddrs, ND_ADDRLIST);
2191
2192 /*
2193 * Decide whether to use remote host's lockd or local locking.
2194 * If we are using the public fh, we've already turned
2195 * LLOCK on.
2196 */
2197 if (hasmntopt(&m, MNTOPT_LLOCK))
2198 argp->flags |= NFSMNT_LLOCK;
2199 if (!(argp->flags & NFSMNT_LLOCK) && nfsvers == NFS_VERSION &&
2200 remote_lock(host, argp->fh)) {
2201 syslog(loglevel, "No network locking on %s : "
2202 "contact admin to install server change", host);
2203 argp->flags |= NFSMNT_LLOCK;
2204 }
2205
2206 /*
2207 * Build a string for /etc/mnttab.
2208 * If possible, coalesce strings with same 'dir' info.
2209 */
2210 if ((mfs->mfs_flags & MFS_URL) == 0) {
2211 char *tmp;
2212
2213 if (mnttabcnt) {
2214 p = strrchr(mnttabtext, (int)':');
2215 if (!p || strcmp(p+1, dir) != 0) {
2216 mnttabcnt += strlen(remname) + 2;
2217 } else {
2218 *p = '\0';
2219 mnttabcnt += strlen(rhost) + 2;
2220 }
2221 if ((tmp = realloc(mnttabtext,
2222 mnttabcnt)) != NULL) {
2223 mnttabtext = tmp;
2224 strcat(mnttabtext, ",");
2225 } else {
2226 free(mnttabtext);
2227 mnttabtext = NULL;
2228 }
2229 } else {
2230 mnttabcnt = strlen(remname) + 1;
2231 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2232 mnttabtext[0] = '\0';
2233 }
2234
2235 if (mnttabtext != NULL)
2236 strcat(mnttabtext, remname);
2237
2238 } else {
2239 char *tmp;
2240 int more_cnt = 0;
2241 char sport[16];
2242
2243 more_cnt += strlen("nfs://");
2244 more_cnt += strlen(mfs->mfs_host);
2245
2246 if (mfs->mfs_port != 0) {
2247 (void) sprintf(sport, ":%u", mfs->mfs_port);
2248 } else
2249 sport[0] = '\0';
2250
2251 more_cnt += strlen(sport);
2252 more_cnt += 1; /* "/" */
2253 more_cnt += strlen(mfs->mfs_dir);
2254
2255 if (mnttabcnt) {
2256 more_cnt += 1; /* "," */
2257 mnttabcnt += more_cnt;
2258
2259 if ((tmp = realloc(mnttabtext,
2260 mnttabcnt)) != NULL) {
2261 mnttabtext = tmp;
2262 strcat(mnttabtext, ",");
2263 } else {
2264 free(mnttabtext);
2265 mnttabtext = NULL;
2266 }
2267 } else {
2268 mnttabcnt = more_cnt + 1;
2269 if ((mnttabtext = malloc(mnttabcnt)) != NULL)
2270 mnttabtext[0] = '\0';
2271 }
2272
2273 if (mnttabtext != NULL) {
2274 strcat(mnttabtext, "nfs://");
2275 strcat(mnttabtext, mfs->mfs_host);
2276 strcat(mnttabtext, sport);
2277 strcat(mnttabtext, "/");
2278 strcat(mnttabtext, mfs->mfs_dir);
2279 }
2280 }
2281
2282 if (!mnttabtext) {
2283 syslog(LOG_ERR, "nfsmount: no memory");
2284 last_error = NFSERR_IO;
2285 goto out;
2286 }
2287
2288 /*
2289 * At least one entry, can call mount(2).
2290 */
2291 entries++;
2292
2293 /*
2294 * If replication was defeated, don't do more work
2295 */
2296 if (!replicated)
2297 break;
2298 }
2299
2300
2301 /*
2302 * Did we get through all possibilities without success?
2303 */
2304 if (!entries)
2305 goto out;
2306
2307 /* Make "xattr" the default if "noxattr" is not specified. */
2308 strcpy(mopts, opts);
2309 if (!hasmntopt(&m, MNTOPT_NOXATTR) && !hasmntopt(&m, MNTOPT_XATTR)) {
2310 if (strlen(mopts) > 0)
2311 strcat(mopts, ",");
2312 strcat(mopts, "xattr");
2313 }
2314
2315 /*
2316 * enable services as needed.
2317 */
2318 {
2319 char **sl;
2320
2321 if (strcmp(fstype, MNTTYPE_NFS4) == 0)
2322 sl = service_list_v4;
2323 else
2324 sl = service_list;
2325
2326 (void) _check_services(sl);
2327 }
2328
2329 /*
2330 * Whew; do the mount, at last.
2331 */
2332 if (trace > 1) {
2333 trace_prt(1, " mount %s %s (%s)\n", mnttabtext, mntpnt, mopts);
2334 }
2335
2336 /*
2337 * About to do a nfs mount, make sure the mount_to is set for
2338 * potential ephemeral mounts with NFSv4.
2339 */
2340 set_nfsv4_ephemeral_mount_to();
2341
2342 /*
2343 * If no action list pointer then do the mount, otherwise
2344 * build the actions list pointer with the mount information.
2345 * so the mount can be done in the kernel.
2346 */
2347 if (alp == NULL) {
2348 if (mount(mnttabtext, mntpnt, flags | MS_DATA, fstype,
2349 head, sizeof (*head), mopts, MAX_MNTOPT_STR) < 0) {
2350 if (trace > 1)
2351 trace_prt(1, " Mount of %s on %s: %d\n",
2352 mnttabtext, mntpnt, errno);
2353 if (errno != EBUSY || verbose)
2354 syslog(LOG_ERR,
2355 "Mount of %s on %s: %m", mnttabtext, mntpnt);
2356 last_error = NFSERR_IO;
2357 goto out;
2358 }
2359
2360 last_error = NFS_OK;
2361 if (stat(mntpnt, &stbuf) == 0) {
2362 if (trace > 1) {
2363 trace_prt(1, " mount %s dev=%x rdev=%x OK\n",
2364 mnttabtext, stbuf.st_dev, stbuf.st_rdev);
2365 }
2366 } else {
2367 if (trace > 1) {
2368 trace_prt(1, " mount %s OK\n", mnttabtext);
2369 trace_prt(1, " stat of %s failed\n", mntpnt);
2370 }
2371
2372 }
2373 } else {
2374 alp->action.action = AUTOFS_MOUNT_RQ;
2375 alp->action.action_list_entry_u.mounta.spec =
2376 strdup(mnttabtext);
2377 alp->action.action_list_entry_u.mounta.dir = strdup(mntpnt);
2378 alp->action.action_list_entry_u.mounta.flags =
2379 flags | MS_DATA;
2380 alp->action.action_list_entry_u.mounta.fstype =
2381 strdup(fstype);
2382 alp->action.action_list_entry_u.mounta.dataptr = (char *)head;
2383 alp->action.action_list_entry_u.mounta.datalen =
2384 sizeof (*head);
2385 mntopts = malloc(strlen(mopts) + 1);
2386 strcpy(mntopts, mopts);
2387 mntopts[strlen(mopts)] = '\0';
2388 alp->action.action_list_entry_u.mounta.optptr = mntopts;
2389 alp->action.action_list_entry_u.mounta.optlen =
2390 strlen(mntopts) + 1;
2391 last_error = NFS_OK;
2392 goto ret;
2393 }
2394
2395 out:
2396 argp = head;
2397 while (argp) {
2398 if (argp->pathconf)
2399 free(argp->pathconf);
2400 free_knconf(argp->knconf);
2401 netbuf_free(argp->addr);
2402 if (argp->syncaddr)
2403 netbuf_free(argp->syncaddr);
2404 if (argp->netname) {
2405 free(argp->netname);
2406 }
2407 if (argp->hostname)
2408 free(argp->hostname);
2409 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
2410 free(argp->fh);
2411 head = argp;
2412 argp = argp->nfs_ext_u.nfs_extB.next;
2413 free(head);
2414 }
2415 ret:
2416 if (nfs_proto)
2417 free(nfs_proto);
2418 if (mnttabtext)
2419 free(mnttabtext);
2420
2421 for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) {
2422
2423 if (mfs->mfs_flags & MFS_ALLOC_DIR) {
2424 free(mfs->mfs_dir);
2425 mfs->mfs_dir = NULL;
2426 mfs->mfs_flags &= ~MFS_ALLOC_DIR;
2427 }
2428
2429 if (mfs->mfs_args != NULL && alp == NULL) {
2430 free(mfs->mfs_args);
2431 mfs->mfs_args = NULL;
2432 }
2433
2434 if (mfs->mfs_nconf != NULL) {
2435 freenetconfigent(mfs->mfs_nconf);
2436 mfs->mfs_nconf = NULL;
2437 }
2438 }
2439
2440 return (last_error);
2441 }
2442
2443 /*
2444 * get_pathconf(cl, path, fsname, pcnf, cretries)
2445 * ugliness that requires that ppathcnf and pathcnf stay consistent
2446 * cretries is a copy of retries used to determine when to syslog
2447 * on retry situations.
2448 */
2449 static int
get_pathconf(CLIENT * cl,char * path,char * fsname,struct pathcnf ** pcnf,int cretries)2450 get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf,
2451 int cretries)
2452 {
2453 struct ppathcnf *p = NULL;
2454 enum clnt_stat rpc_stat;
2455 struct timeval timeout;
2456
2457 p = (struct ppathcnf *)malloc(sizeof (struct ppathcnf));
2458 if (p == NULL) {
2459 syslog(LOG_ERR, "get_pathconf: Out of memory");
2460 return (RET_ERR);
2461 }
2462 memset((caddr_t)p, 0, sizeof (struct ppathcnf));
2463
2464 timeout.tv_sec = 10;
2465 timeout.tv_usec = 0;
2466 rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
2467 xdr_dirpath, (caddr_t)&path, xdr_ppathcnf, (caddr_t)p, timeout);
2468 if (rpc_stat != RPC_SUCCESS) {
2469 if (cretries-- <= 0) {
2470 syslog(LOG_ERR,
2471 "get_pathconf: %s: server not responding: %s",
2472 fsname, clnt_sperror(cl, ""));
2473 }
2474 free(p);
2475 return (RET_RETRY);
2476 }
2477 if (_PC_ISSET(_PC_ERROR, p->pc_mask)) {
2478 syslog(LOG_ERR, "get_pathconf: no info for %s", fsname);
2479 free(p);
2480 return (RET_ERR);
2481 }
2482 *pcnf = (struct pathcnf *)p;
2483 return (RET_OK);
2484 }
2485
2486 void
netbuf_free(nb)2487 netbuf_free(nb)
2488 struct netbuf *nb;
2489 {
2490 if (nb == NULL)
2491 return;
2492 if (nb->buf)
2493 free(nb->buf);
2494 free(nb);
2495 }
2496
2497 #define SMALL_HOSTNAME 20
2498 #define SMALL_PROTONAME 10
2499 #define SMALL_PROTOFMLYNAME 10
2500
2501 struct portmap_cache {
2502 int cache_prog;
2503 int cache_vers;
2504 time_t cache_time;
2505 char cache_small_hosts[SMALL_HOSTNAME + 1];
2506 char *cache_hostname;
2507 char *cache_proto;
2508 char *cache_protofmly;
2509 char cache_small_protofmly[SMALL_PROTOFMLYNAME + 1];
2510 char cache_small_proto[SMALL_PROTONAME + 1];
2511 struct netbuf cache_srv_addr;
2512 struct portmap_cache *cache_prev, *cache_next;
2513 };
2514
2515 rwlock_t portmap_cache_lock;
2516 static int portmap_cache_valid_time = 30;
2517 struct portmap_cache *portmap_cache_head, *portmap_cache_tail;
2518
2519 #ifdef MALLOC_DEBUG
2520 void
portmap_cache_flush()2521 portmap_cache_flush()
2522 {
2523 struct portmap_cache *next = NULL, *cp;
2524
2525 (void) rw_wrlock(&portmap_cache_lock);
2526 for (cp = portmap_cache_head; cp; cp = cp->cache_next) {
2527 if (cp->cache_hostname != NULL &&
2528 cp->cache_hostname !=
2529 cp->cache_small_hosts)
2530 free(cp->cache_hostname);
2531 if (cp->cache_proto != NULL &&
2532 cp->cache_proto !=
2533 cp->cache_small_proto)
2534 free(cp->cache_proto);
2535 if (cp->cache_srv_addr.buf != NULL)
2536 free(cp->cache_srv_addr.buf);
2537 next = cp->cache_next;
2538 free(cp);
2539 }
2540 portmap_cache_head = NULL;
2541 portmap_cache_tail = NULL;
2542 (void) rw_unlock(&portmap_cache_lock);
2543 }
2544 #endif
2545
2546 /*
2547 * Returns 1 if the entry is found in the cache, 0 otherwise.
2548 */
2549 static int
portmap_cache_lookup(hostname,prog,vers,nconf,addrp)2550 portmap_cache_lookup(hostname, prog, vers, nconf, addrp)
2551 char *hostname;
2552 rpcprog_t prog;
2553 rpcvers_t vers;
2554 struct netconfig *nconf;
2555 struct netbuf *addrp;
2556 {
2557 struct portmap_cache *cachep, *prev, *next = NULL, *cp;
2558 int retval = 0;
2559
2560 timenow = time(NULL);
2561
2562 (void) rw_rdlock(&portmap_cache_lock);
2563
2564 /*
2565 * Increment the portmap cache counters for # accesses and lookups
2566 * Use a smaller factor (100 vs 1000 for the host cache) since
2567 * initial analysis shows this cache is looked up 10% that of the
2568 * host cache.
2569 */
2570 #ifdef CACHE_DEBUG
2571 portmap_cache_accesses++;
2572 portmap_cache_lookups++;
2573 if ((portmap_cache_lookups%100) == 0)
2574 trace_portmap_cache();
2575 #endif /* CACHE_DEBUG */
2576
2577 for (cachep = portmap_cache_head; cachep;
2578 cachep = cachep->cache_next) {
2579 if (timenow > cachep->cache_time) {
2580 /*
2581 * We stumbled across an entry in the cache which
2582 * has timed out. Free up all the entries that
2583 * were added before it, which will positionally
2584 * be after this entry. And adjust neighboring
2585 * pointers.
2586 * When we drop the lock and re-acquire it, we
2587 * need to start from the beginning.
2588 */
2589 (void) rw_unlock(&portmap_cache_lock);
2590 (void) rw_wrlock(&portmap_cache_lock);
2591 for (cp = portmap_cache_head;
2592 cp && (cp->cache_time >= timenow);
2593 cp = cp->cache_next)
2594 ;
2595 if (cp == NULL)
2596 goto done;
2597 /*
2598 * Adjust the link of the predecessor.
2599 * Make the tail point to the new last entry.
2600 */
2601 prev = cp->cache_prev;
2602 if (prev == NULL) {
2603 portmap_cache_head = NULL;
2604 portmap_cache_tail = NULL;
2605 } else {
2606 prev->cache_next = NULL;
2607 portmap_cache_tail = prev;
2608 }
2609 for (; cp; cp = next) {
2610 if (cp->cache_hostname != NULL &&
2611 cp->cache_hostname !=
2612 cp->cache_small_hosts)
2613 free(cp->cache_hostname);
2614 if (cp->cache_proto != NULL &&
2615 cp->cache_proto !=
2616 cp->cache_small_proto)
2617 free(cp->cache_proto);
2618 if (cp->cache_srv_addr.buf != NULL)
2619 free(cp->cache_srv_addr.buf);
2620 next = cp->cache_next;
2621 free(cp);
2622 }
2623 goto done;
2624 }
2625 if (cachep->cache_hostname == NULL ||
2626 prog != cachep->cache_prog || vers != cachep->cache_vers ||
2627 strcmp(nconf->nc_proto, cachep->cache_proto) != 0 ||
2628 strcmp(nconf->nc_protofmly, cachep->cache_protofmly) != 0 ||
2629 strcmp(hostname, cachep->cache_hostname) != 0)
2630 continue;
2631 /*
2632 * Cache Hit.
2633 */
2634 #ifdef CACHE_DEBUG
2635 portmap_cache_hits++; /* up portmap cache hit counter */
2636 #endif /* CACHE_DEBUG */
2637 addrp->len = cachep->cache_srv_addr.len;
2638 memcpy(addrp->buf, cachep->cache_srv_addr.buf, addrp->len);
2639 retval = 1;
2640 break;
2641 }
2642 done:
2643 (void) rw_unlock(&portmap_cache_lock);
2644 return (retval);
2645 }
2646
2647 static void
portmap_cache_enter(hostname,prog,vers,nconf,addrp)2648 portmap_cache_enter(hostname, prog, vers, nconf, addrp)
2649 char *hostname;
2650 rpcprog_t prog;
2651 rpcvers_t vers;
2652 struct netconfig *nconf;
2653 struct netbuf *addrp;
2654 {
2655 struct portmap_cache *cachep;
2656 int protofmlylen;
2657 int protolen, hostnamelen;
2658
2659 timenow = time(NULL);
2660
2661 cachep = malloc(sizeof (struct portmap_cache));
2662 if (cachep == NULL)
2663 return;
2664 memset((char *)cachep, 0, sizeof (*cachep));
2665
2666 hostnamelen = strlen(hostname);
2667 if (hostnamelen <= SMALL_HOSTNAME)
2668 cachep->cache_hostname = cachep->cache_small_hosts;
2669 else {
2670 cachep->cache_hostname = malloc(hostnamelen + 1);
2671 if (cachep->cache_hostname == NULL)
2672 goto nomem;
2673 }
2674 strcpy(cachep->cache_hostname, hostname);
2675 protolen = strlen(nconf->nc_proto);
2676 if (protolen <= SMALL_PROTONAME)
2677 cachep->cache_proto = cachep->cache_small_proto;
2678 else {
2679 cachep->cache_proto = malloc(protolen + 1);
2680 if (cachep->cache_proto == NULL)
2681 goto nomem;
2682 }
2683 protofmlylen = strlen(nconf->nc_protofmly);
2684 if (protofmlylen <= SMALL_PROTOFMLYNAME)
2685 cachep->cache_protofmly = cachep->cache_small_protofmly;
2686 else {
2687 cachep->cache_protofmly = malloc(protofmlylen + 1);
2688 if (cachep->cache_protofmly == NULL)
2689 goto nomem;
2690 }
2691
2692 strcpy(cachep->cache_proto, nconf->nc_proto);
2693 cachep->cache_prog = prog;
2694 cachep->cache_vers = vers;
2695 cachep->cache_time = timenow + portmap_cache_valid_time;
2696 cachep->cache_srv_addr.len = addrp->len;
2697 cachep->cache_srv_addr.buf = malloc(addrp->len);
2698 if (cachep->cache_srv_addr.buf == NULL)
2699 goto nomem;
2700 memcpy(cachep->cache_srv_addr.buf, addrp->buf, addrp->maxlen);
2701 cachep->cache_prev = NULL;
2702 (void) rw_wrlock(&portmap_cache_lock);
2703 /*
2704 * There's a window in which we could have multiple threads making
2705 * the same cache entry. This can be avoided by walking the cache
2706 * once again here to check and see if there are duplicate entries
2707 * (after grabbing the write lock). This isn't fatal and I'm not
2708 * going to bother with this.
2709 */
2710 #ifdef CACHE_DEBUG
2711 portmap_cache_accesses++; /* up portmap cache access counter */
2712 #endif /* CACHE_DEBUG */
2713 cachep->cache_next = portmap_cache_head;
2714 if (portmap_cache_head != NULL)
2715 portmap_cache_head->cache_prev = cachep;
2716 portmap_cache_head = cachep;
2717 (void) rw_unlock(&portmap_cache_lock);
2718 return;
2719
2720 nomem:
2721 syslog(LOG_ERR, "portmap_cache_enter: Memory allocation failed");
2722 if (cachep->cache_srv_addr.buf)
2723 free(cachep->cache_srv_addr.buf);
2724 if (cachep->cache_proto && protolen > SMALL_PROTONAME)
2725 free(cachep->cache_proto);
2726 if (cachep->cache_hostname && hostnamelen > SMALL_HOSTNAME)
2727 free(cachep->cache_hostname);
2728 if (cachep->cache_protofmly && protofmlylen > SMALL_PROTOFMLYNAME)
2729 free(cachep->cache_protofmly);
2730 if (cachep)
2731 free(cachep);
2732 cachep = NULL;
2733 }
2734
2735 static int
get_cached_srv_addr(char * hostname,rpcprog_t prog,rpcvers_t vers,struct netconfig * nconf,struct netbuf * addrp)2736 get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2737 struct netconfig *nconf, struct netbuf *addrp)
2738 {
2739 if (portmap_cache_lookup(hostname, prog, vers, nconf, addrp))
2740 return (1);
2741 if (rpcb_getaddr(prog, vers, nconf, addrp, hostname) == 0)
2742 return (0);
2743 portmap_cache_enter(hostname, prog, vers, nconf, addrp);
2744 return (1);
2745 }
2746
2747 /*
2748 * Get a network address on "hostname" for program "prog"
2749 * with version "vers". If the port number is specified (non zero)
2750 * then try for a TCP/UDP transport and set the port number of the
2751 * resulting IP address.
2752 *
2753 * If the address of a netconfig pointer was passed and
2754 * if it's not null, use it as the netconfig otherwise
2755 * assign the address of the netconfig that was used to
2756 * establish contact with the service.
2757 *
2758 * tinfo argument is for matching the get_addr() defined in
2759 * ../nfs/mount/mount.c
2760 */
2761
2762 static struct netbuf *
get_addr(char * hostname,rpcprog_t prog,rpcvers_t vers,struct netconfig ** nconfp,char * proto,ushort_t port,struct t_info * tinfo)2763 get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
2764 struct netconfig **nconfp, char *proto, ushort_t port,
2765 struct t_info *tinfo)
2766
2767 {
2768 enum clnt_stat cstat;
2769
2770 return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL,
2771 nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat));
2772 }
2773
2774 static struct netbuf *
get_pubfh(char * hostname,rpcvers_t vers,mfs_snego_t * mfssnego,struct netconfig ** nconfp,char * proto,ushort_t port,struct t_info * tinfo,caddr_t * fhp,bool_t get_pubfh,char * fspath)2775 get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego,
2776 struct netconfig **nconfp, char *proto, ushort_t port,
2777 struct t_info *tinfo, caddr_t *fhp, bool_t get_pubfh, char *fspath)
2778 {
2779 enum clnt_stat cstat;
2780
2781 return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers,
2782 mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath,
2783 &cstat));
2784 }
2785
2786 static enum clnt_stat
get_ping(char * hostname,rpcprog_t prog,rpcvers_t vers,struct netconfig ** nconfp,ushort_t port,bool_t direct_to_server)2787 get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers,
2788 struct netconfig **nconfp, ushort_t port, bool_t direct_to_server)
2789 {
2790 enum clnt_stat cstat;
2791
2792 (void) get_server_netinfo(SERVER_PING, hostname, prog,
2793 vers, NULL, nconfp, NULL, port, NULL, NULL,
2794 direct_to_server, NULL, &cstat);
2795
2796 return (cstat);
2797 }
2798
2799 void *
get_server_netinfo(enum type_of_stuff type_of_stuff,char * hostname,rpcprog_t prog,rpcvers_t vers,mfs_snego_t * mfssnego,struct netconfig ** nconfp,char * proto,ushort_t port,struct t_info * tinfo,caddr_t * fhp,bool_t direct_to_server,char * fspath,enum clnt_stat * cstatp)2800 get_server_netinfo(
2801 enum type_of_stuff type_of_stuff,
2802 char *hostname,
2803 rpcprog_t prog,
2804 rpcvers_t vers,
2805 mfs_snego_t *mfssnego,
2806 struct netconfig **nconfp,
2807 char *proto,
2808 ushort_t port, /* may be zero */
2809 struct t_info *tinfo,
2810 caddr_t *fhp,
2811 bool_t direct_to_server,
2812 char *fspath,
2813 enum clnt_stat *cstatp)
2814 {
2815 struct netbuf *nb = NULL;
2816 struct netconfig *nconf = NULL;
2817 NCONF_HANDLE *nc = NULL;
2818 int error = 0;
2819 int fd = 0;
2820 struct t_bind *tbind = NULL;
2821 int nthtry = FIRST_TRY;
2822
2823 if (nconfp && *nconfp) {
2824 return (get_netconfig_info(type_of_stuff, hostname,
2825 prog, vers, nconf, port, tinfo, tbind, fhp,
2826 direct_to_server, fspath, cstatp, mfssnego));
2827 }
2828
2829 /*
2830 * No nconf passed in.
2831 *
2832 * Try to get a nconf from /etc/netconfig.
2833 * First choice is COTS, second is CLTS unless proto
2834 * is specified. When we retry, we reset the
2835 * netconfig list, so that we search the whole list
2836 * for the next choice.
2837 */
2838 if ((nc = setnetpath()) == NULL)
2839 goto done;
2840
2841 /*
2842 * If proto is specified, then only search for the match,
2843 * otherwise try COTS first, if failed, then try CLTS.
2844 */
2845 if (proto) {
2846 while ((nconf = getnetpath(nc)) != NULL) {
2847 if (strcmp(nconf->nc_proto, proto))
2848 continue;
2849 /*
2850 * If the port number is specified then TCP/UDP
2851 * is needed. Otherwise any cots/clts will do.
2852 */
2853 if (port) {
2854 if ((strcmp(nconf->nc_protofmly, NC_INET) &&
2855 strcmp(nconf->nc_protofmly, NC_INET6)) ||
2856 (strcmp(nconf->nc_proto, NC_TCP) &&
2857 strcmp(nconf->nc_proto, NC_UDP)))
2858 continue;
2859 }
2860 nb = get_netconfig_info(type_of_stuff, hostname,
2861 prog, vers, nconf, port, tinfo, tbind, fhp,
2862 direct_to_server, fspath, cstatp, mfssnego);
2863 if (*cstatp == RPC_SUCCESS)
2864 break;
2865
2866 assert(nb == NULL);
2867
2868 }
2869 if (nconf == NULL)
2870 goto done;
2871 } else {
2872 retry:
2873 while ((nconf = getnetpath(nc)) != NULL) {
2874 if (nconf->nc_flag & NC_VISIBLE) {
2875 if (nthtry == FIRST_TRY) {
2876 if ((nconf->nc_semantics ==
2877 NC_TPI_COTS_ORD) ||
2878 (nconf->nc_semantics ==
2879 NC_TPI_COTS)) {
2880 if (port == 0)
2881 break;
2882 if ((strcmp(nconf->nc_protofmly,
2883 NC_INET) == 0 ||
2884 strcmp(nconf->nc_protofmly,
2885 NC_INET6) == 0) &&
2886 (strcmp(nconf->nc_proto,
2887 NC_TCP) == 0))
2888 break;
2889 }
2890 }
2891 if (nthtry == SECOND_TRY) {
2892 if (nconf->nc_semantics ==
2893 NC_TPI_CLTS) {
2894 if (port == 0)
2895 break;
2896 if ((strcmp(nconf->nc_protofmly,
2897 NC_INET) == 0 ||
2898 strcmp(nconf->nc_protofmly,
2899 NC_INET6) == 0) &&
2900 (strcmp(nconf->nc_proto,
2901 NC_UDP) == 0))
2902 break;
2903 }
2904 }
2905 }
2906 }
2907
2908 if (nconf == NULL) {
2909 if (++nthtry <= MNT_PREF_LISTLEN) {
2910 endnetpath(nc);
2911 if ((nc = setnetpath()) == NULL)
2912 goto done;
2913 goto retry;
2914 } else
2915 goto done;
2916 } else {
2917 nb = get_netconfig_info(type_of_stuff, hostname,
2918 prog, vers, nconf, port, tinfo, tbind, fhp,
2919 direct_to_server, fspath, cstatp, mfssnego);
2920 if (*cstatp != RPC_SUCCESS)
2921 /*
2922 * Continue the same search path in the
2923 * netconfig db until no more matched nconf
2924 * (nconf == NULL).
2925 */
2926 goto retry;
2927 }
2928 }
2929
2930 /*
2931 * Got nconf and nb. Now dup the netconfig structure (nconf)
2932 * and return it thru nconfp.
2933 */
2934 if (nconf != NULL) {
2935 if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) {
2936 syslog(LOG_ERR, "no memory\n");
2937 free(nb);
2938 nb = NULL;
2939 }
2940 } else {
2941 *nconfp = NULL;
2942 }
2943 done:
2944 if (nc)
2945 endnetpath(nc);
2946 return (nb);
2947 }
2948
2949 void *
get_server_fh(char * hostname,rpcprog_t prog,rpcvers_t vers,mfs_snego_t * mfssnego,struct netconfig * nconf,ushort_t port,struct t_info * tinfo,struct t_bind * tbind,caddr_t * fhp,bool_t direct_to_server,char * fspath,enum clnt_stat * cstat)2950 get_server_fh(char *hostname, rpcprog_t prog, rpcvers_t vers,
2951 mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port,
2952 struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp,
2953 bool_t direct_to_server, char *fspath, enum clnt_stat *cstat)
2954 {
2955 AUTH *ah = NULL;
2956 AUTH *new_ah = NULL;
2957 struct snego_t snego;
2958 enum clnt_stat cs = RPC_TIMEDOUT;
2959 struct timeval tv;
2960 bool_t file_handle = 1;
2961 enum snego_stat sec;
2962 CLIENT *cl = NULL;
2963 int fd = -1;
2964 struct netbuf *nb = NULL;
2965
2966 if (direct_to_server != TRUE)
2967 return (NULL);
2968
2969 if (prog == NFS_PROGRAM && vers == NFS_V4)
2970 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
2971 goto done;
2972
2973 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0)
2974 goto done;
2975
2976 /* LINTED pointer alignment */
2977 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
2978 goto done;
2979
2980 if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd,
2981 direct_to_server, port, prog, vers, file_handle) < 0) {
2982 goto done;
2983 }
2984
2985 cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
2986 if (cl == NULL)
2987 goto done;
2988
2989 ah = authsys_create_default();
2990 if (ah != NULL) {
2991 #ifdef MALLOC_DEBUG
2992 drop_alloc("AUTH_HANDLE", cl->cl_auth,
2993 __FILE__, __LINE__);
2994 #endif
2995 AUTH_DESTROY(cl->cl_auth);
2996 cl->cl_auth = ah;
2997 #ifdef MALLOC_DEBUG
2998 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
2999 __FILE__, __LINE__);
3000 #endif
3001 }
3002
3003 if (!mfssnego->snego_done && vers != NFS_V4) {
3004 /*
3005 * negotiate sec flavor.
3006 */
3007 snego.cnt = 0;
3008 if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) ==
3009 SNEGO_SUCCESS) {
3010 int jj;
3011
3012 /*
3013 * check if server supports the one
3014 * specified in the sec= option.
3015 */
3016 if (mfssnego->sec_opt) {
3017 for (jj = 0; jj < snego.cnt; jj++) {
3018 if (snego.array[jj] ==
3019 mfssnego->nfs_sec.sc_nfsnum) {
3020 mfssnego->snego_done = TRUE;
3021 break;
3022 }
3023 }
3024 }
3025
3026 /*
3027 * find a common sec flavor
3028 */
3029 if (!mfssnego->snego_done) {
3030 for (jj = 0; jj < snego.cnt; jj++) {
3031 if (!nfs_getseconfig_bynumber(
3032 snego.array[jj],
3033 &mfssnego->nfs_sec)) {
3034 mfssnego->snego_done = TRUE;
3035 break;
3036 }
3037 }
3038 }
3039 if (!mfssnego->snego_done)
3040 goto done;
3041 /*
3042 * Now that the flavor has been
3043 * negotiated, get the fh.
3044 *
3045 * First, create an auth handle using the negotiated
3046 * sec flavor in the next lookup to
3047 * fetch the filehandle.
3048 */
3049 new_ah = nfs_create_ah(cl, hostname,
3050 &mfssnego->nfs_sec);
3051 if (new_ah == NULL)
3052 goto done;
3053 #ifdef MALLOC_DEBUG
3054 drop_alloc("AUTH_HANDLE", cl->cl_auth,
3055 __FILE__, __LINE__);
3056 #endif
3057 AUTH_DESTROY(cl->cl_auth);
3058 cl->cl_auth = new_ah;
3059 #ifdef MALLOC_DEBUG
3060 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3061 __FILE__, __LINE__);
3062 #endif
3063 } else if (sec == SNEGO_ARRAY_TOO_SMALL ||
3064 sec == SNEGO_FAILURE) {
3065 goto done;
3066 }
3067 }
3068
3069 switch (vers) {
3070 case NFS_VERSION:
3071 {
3072 wnl_diropargs arg;
3073 wnl_diropres res;
3074
3075 memset((char *)&arg.dir, 0, sizeof (wnl_fh));
3076 memset((char *)&res, 0, sizeof (wnl_diropres));
3077 arg.name = fspath;
3078 if (wnlproc_lookup_2(&arg, &res, cl) !=
3079 RPC_SUCCESS || res.status != WNL_OK)
3080 goto done;
3081 *fhp = malloc(sizeof (wnl_fh));
3082
3083 if (*fhp == NULL) {
3084 syslog(LOG_ERR, "no memory\n");
3085 goto done;
3086 }
3087
3088 memcpy((char *)*fhp,
3089 (char *)&res.wnl_diropres_u.wnl_diropres.file,
3090 sizeof (wnl_fh));
3091 cs = RPC_SUCCESS;
3092 }
3093 break;
3094 case NFS_V3:
3095 {
3096 WNL_LOOKUP3args arg;
3097 WNL_LOOKUP3res res;
3098 nfs_fh3 *fh3p;
3099
3100 memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3));
3101 memset((char *)&res, 0, sizeof (WNL_LOOKUP3res));
3102 arg.what.name = fspath;
3103 if (wnlproc3_lookup_3(&arg, &res, cl) !=
3104 RPC_SUCCESS || res.status != WNL3_OK)
3105 goto done;
3106
3107 fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
3108
3109 if (fh3p == NULL) {
3110 syslog(LOG_ERR, "no memory\n");
3111 goto done;
3112 }
3113
3114 fh3p->fh3_length =
3115 res.WNL_LOOKUP3res_u.res_ok.object.data.data_len;
3116 memcpy(fh3p->fh3_u.data,
3117 res.WNL_LOOKUP3res_u.res_ok.object.data.data_val,
3118 fh3p->fh3_length);
3119
3120 *fhp = (caddr_t)fh3p;
3121
3122 cs = RPC_SUCCESS;
3123 }
3124 break;
3125 case NFS_V4:
3126 tv.tv_sec = 10;
3127 tv.tv_usec = 0;
3128 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
3129 xdr_void, 0, tv);
3130 if (cs != RPC_SUCCESS)
3131 goto done;
3132
3133 *fhp = strdup(fspath);
3134 if (fhp == NULL) {
3135 cs = RPC_SYSTEMERROR;
3136 goto done;
3137 }
3138 break;
3139 }
3140 nb = (struct netbuf *)malloc(sizeof (struct netbuf));
3141 if (nb == NULL) {
3142 syslog(LOG_ERR, "no memory\n");
3143 cs = RPC_SYSTEMERROR;
3144 goto done;
3145 }
3146 nb->buf = (char *)malloc(tbind->addr.maxlen);
3147 if (nb->buf == NULL) {
3148 syslog(LOG_ERR, "no memory\n");
3149 free(nb);
3150 nb = NULL;
3151 cs = RPC_SYSTEMERROR;
3152 goto done;
3153 }
3154 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
3155 nb->len = tbind->addr.len;
3156 nb->maxlen = tbind->addr.maxlen;
3157 done:
3158 if (cstat != NULL)
3159 *cstat = cs;
3160 destroy_auth_client_handle(cl);
3161 cleanup_tli_parms(tbind, fd);
3162 return (nb);
3163 }
3164
3165 /*
3166 * Sends a null call to the remote host's (NFS program, versp). versp
3167 * may be "NULL" in which case the default maximum version is used.
3168 * Upon return, versp contains the maximum version supported iff versp!= NULL.
3169 */
3170 enum clnt_stat
pingnfs(char * hostpart,int attempts,rpcvers_t * versp,rpcvers_t versmin,ushort_t port,bool_t usepub,char * path,char * proto)3171 pingnfs(
3172 char *hostpart,
3173 int attempts,
3174 rpcvers_t *versp,
3175 rpcvers_t versmin,
3176 ushort_t port, /* may be zero */
3177 bool_t usepub,
3178 char *path,
3179 char *proto)
3180 {
3181 CLIENT *cl = NULL;
3182 struct timeval rpc_to_new = {15, 0};
3183 static struct timeval rpc_rtrans_new = {-1, -1};
3184 enum clnt_stat clnt_stat;
3185 int i, j;
3186 rpcvers_t versmax; /* maximum version to try against server */
3187 rpcvers_t outvers; /* version supported by host on last call */
3188 rpcvers_t vers_to_try; /* to try different versions against host */
3189 char *hostname;
3190 struct netconfig *nconf;
3191
3192 hostname = strdup(hostpart);
3193 if (hostname == NULL) {
3194 return (RPC_SYSTEMERROR);
3195 }
3196 unbracket(&hostname);
3197
3198 if (path != NULL && strcmp(hostname, "nfs") == 0 &&
3199 strncmp(path, "//", 2) == 0) {
3200 char *sport;
3201
3202 hostname = strdup(path+2);
3203
3204 if (hostname == NULL)
3205 return (RPC_SYSTEMERROR);
3206
3207 path = strchr(hostname, '/');
3208
3209 /*
3210 * This cannot happen. If it does, give up
3211 * on the ping as this is obviously a corrupt
3212 * entry.
3213 */
3214 if (path == NULL) {
3215 free(hostname);
3216 return (RPC_SUCCESS);
3217 }
3218
3219 /*
3220 * Probable end point of host string.
3221 */
3222 *path = '\0';
3223
3224 sport = strchr(hostname, ':');
3225
3226 if (sport != NULL && sport < path) {
3227
3228 /*
3229 * Actual end point of host string.
3230 */
3231 *sport = '\0';
3232 port = htons((ushort_t)atoi(sport+1));
3233 }
3234
3235 usepub = TRUE;
3236 }
3237
3238 /* Pick up the default versions and then set them appropriately */
3239 if (versp) {
3240 versmax = *versp;
3241 /* use versmin passed in */
3242 } else {
3243 read_default_nfs();
3244 set_versrange(0, &versmax, &versmin);
3245 }
3246
3247 if (proto &&
3248 strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0 &&
3249 versmax == NFS_V4) {
3250 if (versmin == NFS_V4) {
3251 if (versp) {
3252 *versp = versmax - 1;
3253 return (RPC_SUCCESS);
3254 }
3255 return (RPC_PROGUNAVAIL);
3256 } else {
3257 versmax--;
3258 }
3259 }
3260
3261 if (versp)
3262 *versp = versmax;
3263
3264 switch (cache_check(hostname, versp, proto)) {
3265 case GOODHOST:
3266 if (hostname != hostpart)
3267 free(hostname);
3268 return (RPC_SUCCESS);
3269 case DEADHOST:
3270 if (hostname != hostpart)
3271 free(hostname);
3272 return (RPC_TIMEDOUT);
3273 case NOHOST:
3274 default:
3275 break;
3276 }
3277
3278 /*
3279 * XXX The retransmission time rpcbrmttime is a global defined
3280 * in the rpc library (rpcb_clnt.c). We use (and like) the default
3281 * value of 15 sec in the rpc library. The code below is to protect
3282 * us in case it changes. This need not be done under a lock since
3283 * any # of threads entering this function will get the same
3284 * retransmission value.
3285 */
3286 if (rpc_rtrans_new.tv_sec == -1 && rpc_rtrans_new.tv_usec == -1) {
3287 __rpc_control(CLCR_GET_RPCB_RMTTIME, (char *)&rpc_rtrans_new);
3288 if (rpc_rtrans_new.tv_sec != 15 && rpc_rtrans_new.tv_sec != 0)
3289 if (trace > 1)
3290 trace_prt(1, "RPC library rttimer changed\n");
3291 }
3292
3293 /*
3294 * XXX Manipulate the total timeout to get the number of
3295 * desired retransmissions. This code is heavily dependant on
3296 * the RPC backoff mechanism in clnt_dg_call (clnt_dg.c).
3297 */
3298 for (i = 0, j = rpc_rtrans_new.tv_sec; i < attempts-1; i++) {
3299 if (j < RPC_MAX_BACKOFF)
3300 j *= 2;
3301 else
3302 j = RPC_MAX_BACKOFF;
3303 rpc_to_new.tv_sec += j;
3304 }
3305
3306 vers_to_try = versmax;
3307
3308 /*
3309 * check the host's version within the timeout
3310 */
3311 if (trace > 1)
3312 trace_prt(1, " ping: %s timeout=%ld request vers=%d min=%d\n",
3313 hostname, rpc_to_new.tv_sec, versmax, versmin);
3314
3315 if (usepub == FALSE) {
3316 do {
3317 /*
3318 * If NFSv4, then we do the same thing as is used
3319 * for public filehandles so that we avoid rpcbind
3320 */
3321 if (vers_to_try == NFS_V4) {
3322 if (trace > 4) {
3323 trace_prt(1, " pingnfs: Trying ping via "
3324 "\"circuit_v\"\n");
3325 }
3326
3327 cl = clnt_create_service_timed(hostname, "nfs",
3328 NFS_PROGRAM, vers_to_try,
3329 port, "circuit_v", &rpc_to_new);
3330 if (cl != NULL) {
3331 outvers = vers_to_try;
3332 break;
3333 }
3334 if (trace > 4) {
3335 trace_prt(1,
3336 " pingnfs: Can't ping via "
3337 "\"circuit_v\" %s: RPC error=%d\n",
3338 hostname, rpc_createerr.cf_stat);
3339 }
3340
3341 } else {
3342 cl = clnt_create_vers_timed(hostname,
3343 NFS_PROGRAM, &outvers, versmin, vers_to_try,
3344 "datagram_v", &rpc_to_new);
3345 if (cl != NULL)
3346 break;
3347 if (trace > 4) {
3348 trace_prt(1,
3349 " pingnfs: Can't ping via "
3350 "\"datagram_v\"%s: RPC error=%d\n",
3351 hostname, rpc_createerr.cf_stat);
3352 }
3353 if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST ||
3354 rpc_createerr.cf_stat == RPC_TIMEDOUT)
3355 break;
3356 if (rpc_createerr.cf_stat ==
3357 RPC_PROGNOTREGISTERED) {
3358 if (trace > 4) {
3359 trace_prt(1,
3360 " pingnfs: Trying ping "
3361 "via \"circuit_v\"\n");
3362 }
3363 cl = clnt_create_vers_timed(hostname,
3364 NFS_PROGRAM, &outvers,
3365 versmin, vers_to_try,
3366 "circuit_v", &rpc_to_new);
3367 if (cl != NULL)
3368 break;
3369 if (trace > 4) {
3370 trace_prt(1,
3371 " pingnfs: Can't ping "
3372 "via \"circuit_v\" %s: "
3373 "RPC error=%d\n",
3374 hostname,
3375 rpc_createerr.cf_stat);
3376 }
3377 }
3378 }
3379
3380 /*
3381 * backoff and return lower version to retry the ping.
3382 * XXX we should be more careful and handle
3383 * RPC_PROGVERSMISMATCH here, because that error is handled
3384 * in clnt_create_vers(). It's not done to stay in sync
3385 * with the nfs mount command.
3386 */
3387 vers_to_try--;
3388 if (vers_to_try < versmin)
3389 break;
3390 if (versp != NULL) { /* recheck the cache */
3391 *versp = vers_to_try;
3392 if (trace > 4) {
3393 trace_prt(1,
3394 " pingnfs: check cache: vers=%d\n",
3395 *versp);
3396 }
3397 switch (cache_check(hostname, versp, proto)) {
3398 case GOODHOST:
3399 if (hostname != hostpart)
3400 free(hostname);
3401 return (RPC_SUCCESS);
3402 case DEADHOST:
3403 if (hostname != hostpart)
3404 free(hostname);
3405 return (RPC_TIMEDOUT);
3406 case NOHOST:
3407 default:
3408 break;
3409 }
3410 }
3411 if (trace > 4) {
3412 trace_prt(1, " pingnfs: Try version=%d\n",
3413 vers_to_try);
3414 }
3415 } while (cl == NULL);
3416
3417
3418 if (cl == NULL) {
3419 if (verbose)
3420 syslog(LOG_ERR, "pingnfs: %s%s",
3421 hostname, clnt_spcreateerror(""));
3422 clnt_stat = rpc_createerr.cf_stat;
3423 } else {
3424 clnt_destroy(cl);
3425 clnt_stat = RPC_SUCCESS;
3426 }
3427
3428 } else {
3429 for (vers_to_try = versmax; vers_to_try >= versmin;
3430 vers_to_try--) {
3431
3432 nconf = NULL;
3433
3434 if (trace > 4) {
3435 trace_prt(1, " pingnfs: Try version=%d "
3436 "using get_ping()\n", vers_to_try);
3437 }
3438
3439 clnt_stat = get_ping(hostname, NFS_PROGRAM,
3440 vers_to_try, &nconf, port, TRUE);
3441
3442 if (nconf != NULL)
3443 freenetconfigent(nconf);
3444
3445 if (clnt_stat == RPC_SUCCESS) {
3446 outvers = vers_to_try;
3447 break;
3448 }
3449 }
3450 }
3451
3452 if (trace > 1)
3453 clnt_stat == RPC_SUCCESS ?
3454 trace_prt(1, " pingnfs OK: nfs version=%d\n", outvers):
3455 trace_prt(1, " pingnfs FAIL: can't get nfs version\n");
3456
3457 if (clnt_stat == RPC_SUCCESS) {
3458 cache_enter(hostname, versmax, outvers, proto, GOODHOST);
3459 if (versp != NULL)
3460 *versp = outvers;
3461 } else
3462 cache_enter(hostname, versmax, versmax, proto, DEADHOST);
3463
3464 if (hostpart != hostname)
3465 free(hostname);
3466
3467 return (clnt_stat);
3468 }
3469
3470 #define MNTTYPE_LOFS "lofs"
3471
3472 int
loopbackmount(fsname,dir,mntopts,overlay)3473 loopbackmount(fsname, dir, mntopts, overlay)
3474 char *fsname; /* Directory being mounted */
3475 char *dir; /* Directory being mounted on */
3476 char *mntopts;
3477 int overlay;
3478 {
3479 struct mnttab mnt;
3480 int flags = 0;
3481 char fstype[] = MNTTYPE_LOFS;
3482 int dirlen;
3483 struct stat st;
3484 char optbuf[MAX_MNTOPT_STR];
3485
3486 dirlen = strlen(dir);
3487 if (dir[dirlen-1] == ' ')
3488 dirlen--;
3489
3490 if (dirlen == strlen(fsname) &&
3491 strncmp(fsname, dir, dirlen) == 0) {
3492 syslog(LOG_ERR,
3493 "Mount of %s on %s would result in deadlock, aborted\n",
3494 fsname, dir);
3495 return (RET_ERR);
3496 }
3497 mnt.mnt_mntopts = mntopts;
3498 if (hasmntopt(&mnt, MNTOPT_RO) != NULL)
3499 flags |= MS_RDONLY;
3500
3501 (void) strlcpy(optbuf, mntopts, sizeof (optbuf));
3502
3503 if (overlay)
3504 flags |= MS_OVERLAY;
3505
3506 if (trace > 1)
3507 trace_prt(1,
3508 " loopbackmount: fsname=%s, dir=%s, flags=%d\n",
3509 fsname, dir, flags);
3510
3511 if (is_system_labeled()) {
3512 if (create_homedir((const char *)fsname,
3513 (const char *)dir) == 0) {
3514 return (NFSERR_NOENT);
3515 }
3516 }
3517
3518 if (mount(fsname, dir, flags | MS_DATA | MS_OPTIONSTR, fstype,
3519 NULL, 0, optbuf, sizeof (optbuf)) < 0) {
3520 syslog(LOG_ERR, "Mount of %s on %s: %m", fsname, dir);
3521 return (RET_ERR);
3522 }
3523
3524 if (stat(dir, &st) == 0) {
3525 if (trace > 1) {
3526 trace_prt(1,
3527 " loopbackmount of %s on %s dev=%x rdev=%x OK\n",
3528 fsname, dir, st.st_dev, st.st_rdev);
3529 }
3530 } else {
3531 if (trace > 1) {
3532 trace_prt(1,
3533 " loopbackmount of %s on %s OK\n", fsname, dir);
3534 trace_prt(1, " stat of %s failed\n", dir);
3535 }
3536 }
3537
3538 return (0);
3539 }
3540
3541 /*
3542 * Look for the value of a numeric option of the form foo=x. If found, set
3543 * *valp to the value and return non-zero. If not found or the option is
3544 * malformed, return zero.
3545 */
3546
3547 int
nopt(mnt,opt,valp)3548 nopt(mnt, opt, valp)
3549 struct mnttab *mnt;
3550 char *opt;
3551 int *valp; /* OUT */
3552 {
3553 char *equal;
3554 char *str;
3555
3556 /*
3557 * We should never get a null pointer, but if we do, it's better to
3558 * ignore the option than to dump core.
3559 */
3560
3561 if (valp == NULL) {
3562 syslog(LOG_DEBUG, "null pointer for %s option", opt);
3563 return (0);
3564 }
3565
3566 if (str = hasmntopt(mnt, opt)) {
3567 if (equal = strchr(str, '=')) {
3568 *valp = atoi(&equal[1]);
3569 return (1);
3570 } else {
3571 syslog(LOG_ERR, "Bad numeric option '%s'", str);
3572 }
3573 }
3574 return (0);
3575 }
3576
3577 int
nfsunmount(mnt)3578 nfsunmount(mnt)
3579 struct mnttab *mnt;
3580 {
3581 struct timeval timeout;
3582 CLIENT *cl;
3583 enum clnt_stat rpc_stat;
3584 char *host, *path;
3585 struct replica *list;
3586 int i, count = 0;
3587 int isv4mount = is_v4_mount(mnt->mnt_mountp);
3588
3589 if (trace > 1)
3590 trace_prt(1, " nfsunmount: umount %s\n", mnt->mnt_mountp);
3591
3592 if (umount(mnt->mnt_mountp) < 0) {
3593 if (trace > 1)
3594 trace_prt(1, " nfsunmount: umount %s FAILED\n",
3595 mnt->mnt_mountp);
3596 if (errno)
3597 return (errno);
3598 }
3599
3600 /*
3601 * If this is a NFSv4 mount, the mount protocol was not used
3602 * so we just return.
3603 */
3604 if (isv4mount) {
3605 if (trace > 1)
3606 trace_prt(1, " nfsunmount: umount %s OK\n",
3607 mnt->mnt_mountp);
3608 return (0);
3609 }
3610
3611 /*
3612 * If mounted with -o public, then no need to contact server
3613 * because mount protocol was not used.
3614 */
3615 if (hasmntopt(mnt, MNTOPT_PUBLIC) != NULL) {
3616 return (0);
3617 }
3618
3619 /*
3620 * The rest of this code is advisory to the server.
3621 * If it fails return success anyway.
3622 */
3623
3624 list = parse_replica(mnt->mnt_special, &count);
3625 if (!list) {
3626 if (count >= 0)
3627 syslog(LOG_ERR,
3628 "Memory allocation failed: %m");
3629 return (ENOMEM);
3630 }
3631
3632 for (i = 0; i < count; i++) {
3633
3634 host = list[i].host;
3635 path = list[i].path;
3636
3637 /*
3638 * Skip file systems mounted using WebNFS, because mount
3639 * protocol was not used.
3640 */
3641 if (strcmp(host, "nfs") == 0 && strncmp(path, "//", 2) == 0)
3642 continue;
3643
3644 cl = clnt_create(host, MOUNTPROG, MOUNTVERS, "datagram_v");
3645 if (cl == NULL)
3646 break;
3647 #ifdef MALLOC_DEBUG
3648 add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__);
3649 add_alloc("AUTH_HANDLE", cl->cl_auth, 0,
3650 __FILE__, __LINE__);
3651 #endif
3652 if (__clnt_bindresvport(cl) < 0) {
3653 if (verbose)
3654 syslog(LOG_ERR, "umount %s:%s: %s",
3655 host, path,
3656 "Couldn't bind to reserved port");
3657 destroy_auth_client_handle(cl);
3658 continue;
3659 }
3660 #ifdef MALLOC_DEBUG
3661 drop_alloc("AUTH_HANDLE", cl->cl_auth, __FILE__, __LINE__);
3662 #endif
3663 AUTH_DESTROY(cl->cl_auth);
3664 if ((cl->cl_auth = authsys_create_default()) == NULL) {
3665 if (verbose)
3666 syslog(LOG_ERR, "umount %s:%s: %s",
3667 host, path,
3668 "Failed creating default auth handle");
3669 destroy_auth_client_handle(cl);
3670 continue;
3671 }
3672 #ifdef MALLOC_DEBUG
3673 add_alloc("AUTH_HANDLE", cl->cl_auth, 0, __FILE__, __LINE__);
3674 #endif
3675 timeout.tv_usec = 0;
3676 timeout.tv_sec = 5;
3677 rpc_stat = clnt_call(cl, MOUNTPROC_UMNT, xdr_dirpath,
3678 (caddr_t)&path, xdr_void, (char *)NULL, timeout);
3679 if (verbose && rpc_stat != RPC_SUCCESS)
3680 syslog(LOG_ERR, "%s: %s",
3681 host, clnt_sperror(cl, "unmount"));
3682 destroy_auth_client_handle(cl);
3683 }
3684
3685 free_replica(list, count);
3686
3687 if (trace > 1)
3688 trace_prt(1, " nfsunmount: umount %s OK\n", mnt->mnt_mountp);
3689
3690 done:
3691 return (0);
3692 }
3693
3694 /*
3695 * Put a new entry in the cache chain by prepending it to the front.
3696 * If there isn't enough memory then just give up.
3697 */
3698 static void
cache_enter(host,reqvers,outvers,proto,state)3699 cache_enter(host, reqvers, outvers, proto, state)
3700 char *host;
3701 rpcvers_t reqvers;
3702 rpcvers_t outvers;
3703 char *proto;
3704 int state;
3705 {
3706 struct cache_entry *entry;
3707 int cache_time = 30; /* sec */
3708
3709 timenow = time(NULL);
3710
3711 entry = (struct cache_entry *)malloc(sizeof (struct cache_entry));
3712 if (entry == NULL)
3713 return;
3714 (void) memset((caddr_t)entry, 0, sizeof (struct cache_entry));
3715 entry->cache_host = strdup(host);
3716 if (entry->cache_host == NULL) {
3717 cache_free(entry);
3718 return;
3719 }
3720 entry->cache_reqvers = reqvers;
3721 entry->cache_outvers = outvers;
3722 entry->cache_proto = (proto == NULL ? NULL : strdup(proto));
3723 entry->cache_state = state;
3724 entry->cache_time = timenow + cache_time;
3725 (void) rw_wrlock(&cache_lock);
3726 #ifdef CACHE_DEBUG
3727 host_cache_accesses++; /* up host cache access counter */
3728 #endif /* CACHE DEBUG */
3729 entry->cache_next = cache_head;
3730 cache_head = entry;
3731 (void) rw_unlock(&cache_lock);
3732 }
3733
3734 static int
cache_check(host,versp,proto)3735 cache_check(host, versp, proto)
3736 char *host;
3737 rpcvers_t *versp;
3738 char *proto;
3739 {
3740 int state = NOHOST;
3741 struct cache_entry *ce, *prev;
3742
3743 timenow = time(NULL);
3744
3745 (void) rw_rdlock(&cache_lock);
3746
3747 #ifdef CACHE_DEBUG
3748 /* Increment the lookup and access counters for the host cache */
3749 host_cache_accesses++;
3750 host_cache_lookups++;
3751 if ((host_cache_lookups%1000) == 0)
3752 trace_host_cache();
3753 #endif /* CACHE DEBUG */
3754
3755 for (ce = cache_head; ce; ce = ce->cache_next) {
3756 if (timenow > ce->cache_time) {
3757 (void) rw_unlock(&cache_lock);
3758 (void) rw_wrlock(&cache_lock);
3759 for (prev = NULL, ce = cache_head; ce;
3760 prev = ce, ce = ce->cache_next) {
3761 if (timenow > ce->cache_time) {
3762 cache_free(ce);
3763 if (prev)
3764 prev->cache_next = NULL;
3765 else
3766 cache_head = NULL;
3767 break;
3768 }
3769 }
3770 (void) rw_unlock(&cache_lock);
3771 return (state);
3772 }
3773 if (strcmp(host, ce->cache_host) != 0)
3774 continue;
3775 if ((proto == NULL && ce->cache_proto != NULL) ||
3776 (proto != NULL && ce->cache_proto == NULL))
3777 continue;
3778 if (proto != NULL &&
3779 strcmp(proto, ce->cache_proto) != 0)
3780 continue;
3781
3782 if (versp == NULL ||
3783 (versp != NULL && *versp == ce->cache_reqvers) ||
3784 (versp != NULL && *versp == ce->cache_outvers)) {
3785 if (versp != NULL)
3786 *versp = ce->cache_outvers;
3787 state = ce->cache_state;
3788
3789 /* increment the host cache hit counters */
3790 #ifdef CACHE_DEBUG
3791 if (state == GOODHOST)
3792 goodhost_cache_hits++;
3793 if (state == DEADHOST)
3794 deadhost_cache_hits++;
3795 #endif /* CACHE_DEBUG */
3796 (void) rw_unlock(&cache_lock);
3797 return (state);
3798 }
3799 }
3800 (void) rw_unlock(&cache_lock);
3801 return (state);
3802 }
3803
3804 /*
3805 * Free a cache entry and all entries
3806 * further down the chain since they
3807 * will also be expired.
3808 */
3809 static void
cache_free(entry)3810 cache_free(entry)
3811 struct cache_entry *entry;
3812 {
3813 struct cache_entry *ce, *next = NULL;
3814
3815 for (ce = entry; ce; ce = next) {
3816 if (ce->cache_host)
3817 free(ce->cache_host);
3818 if (ce->cache_proto)
3819 free(ce->cache_proto);
3820 next = ce->cache_next;
3821 free(ce);
3822 }
3823 }
3824
3825 #ifdef MALLOC_DEBUG
3826 void
cache_flush()3827 cache_flush()
3828 {
3829 (void) rw_wrlock(&cache_lock);
3830 cache_free(cache_head);
3831 cache_head = NULL;
3832 (void) rw_unlock(&cache_lock);
3833 }
3834
3835 void
flush_caches()3836 flush_caches()
3837 {
3838 mutex_lock(&cleanup_lock);
3839 cond_signal(&cleanup_start_cv);
3840 (void) cond_wait(&cleanup_done_cv, &cleanup_lock);
3841 mutex_unlock(&cleanup_lock);
3842 cache_flush();
3843 portmap_cache_flush();
3844 }
3845 #endif
3846
3847 /*
3848 * Returns 1, if port option is NFS_PORT or
3849 * nfsd is running on the port given
3850 * Returns 0, if both port is not NFS_PORT and nfsd is not
3851 * running on the port.
3852 */
3853
3854 static int
is_nfs_port(char * opts)3855 is_nfs_port(char *opts)
3856 {
3857 struct mnttab m;
3858 uint_t nfs_port = 0;
3859 struct servent sv;
3860 char buf[256];
3861 int got_port;
3862
3863 m.mnt_mntopts = opts;
3864
3865 /*
3866 * Get port specified in options list, if any.
3867 */
3868 got_port = nopt(&m, MNTOPT_PORT, (int *)&nfs_port);
3869
3870 /*
3871 * if no port specified or it is same as NFS_PORT return nfs
3872 * To use any other daemon the port number should be different
3873 */
3874 if (!got_port || nfs_port == NFS_PORT)
3875 return (1);
3876 /*
3877 * If daemon is nfsd, return nfs
3878 */
3879 if (getservbyport_r(nfs_port, NULL, &sv, buf, 256) == &sv &&
3880 strcmp(sv.s_name, "nfsd") == 0)
3881 return (1);
3882
3883 /*
3884 * daemon is not nfs
3885 */
3886 return (0);
3887 }
3888
3889
3890 /*
3891 * destroy_auth_client_handle(cl)
3892 * destroys the created client handle
3893 */
3894 void
destroy_auth_client_handle(CLIENT * cl)3895 destroy_auth_client_handle(CLIENT *cl)
3896 {
3897 if (cl) {
3898 if (cl->cl_auth) {
3899 #ifdef MALLOC_DEBUG
3900 drop_alloc("AUTH_HANDLE", cl->cl_auth,
3901 __FILE__, __LINE__);
3902 #endif
3903 AUTH_DESTROY(cl->cl_auth);
3904 cl->cl_auth = NULL;
3905 }
3906 #ifdef MALLOC_DEBUG
3907 drop_alloc("CLNT_HANDLE", cl,
3908 __FILE__, __LINE__);
3909 #endif
3910 clnt_destroy(cl);
3911 }
3912 }
3913
3914
3915 /*
3916 * Attempt to figure out which version of NFS to use in pingnfs(). If
3917 * the version number was specified (i.e., non-zero), then use it.
3918 * Otherwise, default to the compiled-in default or the default as set
3919 * by the /etc/default/nfs configuration (as read by read_default().
3920 */
3921 int
set_versrange(rpcvers_t nfsvers,rpcvers_t * vers,rpcvers_t * versmin)3922 set_versrange(rpcvers_t nfsvers, rpcvers_t *vers, rpcvers_t *versmin)
3923 {
3924 switch (nfsvers) {
3925 case 0:
3926 *vers = vers_max_default;
3927 *versmin = vers_min_default;
3928 break;
3929 case NFS_V4:
3930 *vers = NFS_V4;
3931 *versmin = NFS_V4;
3932 break;
3933 case NFS_V3:
3934 *vers = NFS_V3;
3935 *versmin = NFS_V3;
3936 break;
3937 case NFS_VERSION:
3938 *vers = NFS_VERSION; /* version 2 */
3939 *versmin = NFS_VERSMIN; /* version 2 */
3940 break;
3941 default:
3942 return (-1);
3943 }
3944 return (0);
3945 }
3946
3947 #ifdef CACHE_DEBUG
3948 /*
3949 * trace_portmap_cache()
3950 * traces the portmap cache values at desired points
3951 */
3952 static void
trace_portmap_cache()3953 trace_portmap_cache()
3954 {
3955 syslog(LOG_ERR, "portmap_cache: accesses=%d lookups=%d hits=%d\n",
3956 portmap_cache_accesses, portmap_cache_lookups,
3957 portmap_cache_hits);
3958 }
3959
3960 /*
3961 * trace_host_cache()
3962 * traces the host cache values at desired points
3963 */
3964 static void
trace_host_cache()3965 trace_host_cache()
3966 {
3967 syslog(LOG_ERR,
3968 "host_cache: accesses=%d lookups=%d deadhits=%d goodhits=%d\n",
3969 host_cache_accesses, host_cache_lookups, deadhost_cache_hits,
3970 goodhost_cache_hits);
3971 }
3972 #endif /* CACHE_DEBUG */
3973
3974 /*
3975 * Read the NFS SMF properties to determine if the
3976 * client has been configured for a new min/max for the NFS version to
3977 * use.
3978 */
3979
3980 #define SVC_NFS_CLIENT "svc:/network/nfs/client"
3981
3982 static void
read_default_nfs(void)3983 read_default_nfs(void)
3984 {
3985 static time_t lastread = 0;
3986 struct stat buf;
3987 char defval[4];
3988 int errno, bufsz;
3989 int tmp, ret = 0;
3990
3991 bufsz = 4;
3992 ret = nfs_smf_get_prop("client_versmin", defval, DEFAULT_INSTANCE,
3993 SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
3994 if (ret == SA_OK) {
3995 errno = 0;
3996 tmp = strtol(defval, (char **)NULL, 10);
3997 if (errno == 0) {
3998 vers_min_default = tmp;
3999 }
4000 }
4001
4002 bufsz = 4;
4003 ret = nfs_smf_get_prop("client_versmax", defval, DEFAULT_INSTANCE,
4004 SCF_TYPE_INTEGER, SVC_NFS_CLIENT, &bufsz);
4005 if (ret == SA_OK) {
4006 errno = 0;
4007 tmp = strtol(defval, (char **)NULL, 10);
4008 if (errno == 0) {
4009 vers_max_default = tmp;
4010 }
4011 }
4012
4013 lastread = buf.st_mtime;
4014
4015 /*
4016 * Quick sanity check on the values picked up from the
4017 * defaults file. Make sure that a mistake wasn't
4018 * made that will confuse things later on.
4019 * If so, reset to compiled-in defaults
4020 */
4021 if (vers_min_default > vers_max_default ||
4022 vers_min_default < NFS_VERSMIN ||
4023 vers_max_default > NFS_VERSMAX) {
4024 if (trace > 1) {
4025 trace_prt(1,
4026 " read_default: version minimum/maximum incorrectly configured\n");
4027 trace_prt(1,
4028 " read_default: config is min=%d, max%d. Resetting to min=%d, max%d\n",
4029 vers_min_default, vers_max_default,
4030 NFS_VERSMIN_DEFAULT,
4031 NFS_VERSMAX_DEFAULT);
4032 }
4033 vers_min_default = NFS_VERSMIN_DEFAULT;
4034 vers_max_default = NFS_VERSMAX_DEFAULT;
4035 }
4036 }
4037
4038 /*
4039 * Find the mnttab entry that corresponds to "name".
4040 * We're not sure what the name represents: either
4041 * a mountpoint name, or a special name (server:/path).
4042 * Return the last entry in the file that matches.
4043 */
4044 static struct extmnttab *
mnttab_find(dirname)4045 mnttab_find(dirname)
4046 char *dirname;
4047 {
4048 FILE *fp;
4049 struct extmnttab mnt;
4050 struct extmnttab *res = NULL;
4051
4052 fp = fopen(MNTTAB, "r");
4053 if (fp == NULL) {
4054 if (trace > 1)
4055 trace_prt(1, " mnttab_find: unable to open mnttab\n");
4056 return (NULL);
4057 }
4058 while (getextmntent(fp, &mnt, sizeof (struct extmnttab)) == 0) {
4059 if (strcmp(mnt.mnt_mountp, dirname) == 0 ||
4060 strcmp(mnt.mnt_special, dirname) == 0) {
4061 if (res)
4062 fsfreemnttab(res);
4063 res = fsdupmnttab(&mnt);
4064 }
4065 }
4066
4067 resetmnttab(fp);
4068 fclose(fp);
4069 if (res == NULL) {
4070 if (trace > 1)
4071 trace_prt(1, " mnttab_find: unable to find %s\n",
4072 dirname);
4073 }
4074 return (res);
4075 }
4076
4077 /*
4078 * This function's behavior is taken from nfsstat.
4079 * Trying to determine what NFS version was used for the mount.
4080 */
4081 static int
is_v4_mount(char * mntpath)4082 is_v4_mount(char *mntpath)
4083 {
4084 kstat_ctl_t *kc = NULL; /* libkstat cookie */
4085 kstat_t *ksp;
4086 ulong_t fsid;
4087 struct mntinfo_kstat mik;
4088 struct extmnttab *mntp;
4089 uint_t mnt_minor;
4090
4091 if ((mntp = mnttab_find(mntpath)) == NULL)
4092 return (FALSE);
4093
4094 /* save the minor number and free the struct so we don't forget */
4095 mnt_minor = mntp->mnt_minor;
4096 fsfreemnttab(mntp);
4097
4098 if ((kc = kstat_open()) == NULL)
4099 return (FALSE);
4100
4101 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
4102 if (ksp->ks_type != KSTAT_TYPE_RAW)
4103 continue;
4104 if (strcmp(ksp->ks_module, "nfs") != 0)
4105 continue;
4106 if (strcmp(ksp->ks_name, "mntinfo") != 0)
4107 continue;
4108 if (mnt_minor != ksp->ks_instance)
4109 continue;
4110
4111 if (kstat_read(kc, ksp, &mik) == -1)
4112 continue;
4113
4114 (void) kstat_close(kc);
4115 if (mik.mik_vers == 4)
4116 return (TRUE);
4117 else
4118 return (FALSE);
4119 }
4120 (void) kstat_close(kc);
4121
4122 return (FALSE);
4123 }
4124
4125 static int
create_homedir(const char * src,const char * dst)4126 create_homedir(const char *src, const char *dst) {
4127
4128 struct stat stbuf;
4129 char *dst_username;
4130 struct passwd *pwd, pwds;
4131 char buf_pwd[NSS_BUFLEN_PASSWD];
4132 int homedir_len;
4133 int dst_dir_len;
4134 int src_dir_len;
4135
4136 if (trace > 1)
4137 trace_prt(1, "entered create_homedir\n");
4138
4139 if (stat(src, &stbuf) == 0) {
4140 if (trace > 1)
4141 trace_prt(1, "src exists\n");
4142 return (1);
4143 }
4144
4145 dst_username = strrchr(dst, '/');
4146 if (dst_username) {
4147 dst_username++; /* Skip over slash */
4148 pwd = getpwnam_r(dst_username, &pwds, buf_pwd,
4149 sizeof (buf_pwd));
4150 if (pwd == NULL) {
4151 return (0);
4152 }
4153 } else {
4154 return (0);
4155 }
4156
4157 homedir_len = strlen(pwd->pw_dir);
4158 dst_dir_len = strlen(dst) - homedir_len;
4159 src_dir_len = strlen(src) - homedir_len;
4160
4161 /* Check that the paths are in the same zone */
4162 if (src_dir_len < dst_dir_len ||
4163 (strncmp(dst, src, dst_dir_len) != 0)) {
4164 if (trace > 1)
4165 trace_prt(1, " paths don't match\n");
4166 return (0);
4167 }
4168 /* Check that mountpoint is an auto_home entry */
4169 if (dst_dir_len < 0 ||
4170 (strcmp(pwd->pw_dir, dst + dst_dir_len) != 0)) {
4171 return (0);
4172 }
4173
4174 /* Check that source is an home directory entry */
4175 if (src_dir_len < 0 ||
4176 (strcmp(pwd->pw_dir, src + src_dir_len) != 0)) {
4177 if (trace > 1)
4178 trace_prt(1, " homedir (2) doesn't match %s\n",
4179 src+src_dir_len);
4180 return (0);
4181 }
4182
4183 if (mkdir(src,
4184 S_IRUSR | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH) == -1) {
4185 if (trace > 1) {
4186 trace_prt(1, " Couldn't mkdir %s\n", src);
4187 }
4188 return (0);
4189 }
4190
4191 if (chown(src, pwd->pw_uid, pwd->pw_gid) == -1) {
4192 unlink(src);
4193 return (0);
4194 }
4195
4196 /* Created new home directory for the user */
4197 return (1);
4198 }
4199
4200 void
free_nfs_args(struct nfs_args * argp)4201 free_nfs_args(struct nfs_args *argp)
4202 {
4203 struct nfs_args *oldp;
4204 while (argp) {
4205 if (argp->pathconf)
4206 free(argp->pathconf);
4207 if (argp->knconf)
4208 free_knconf(argp->knconf);
4209 if (argp->addr)
4210 netbuf_free(argp->addr);
4211 if (argp->syncaddr)
4212 netbuf_free(argp->syncaddr);
4213 if (argp->netname)
4214 free(argp->netname);
4215 if (argp->hostname)
4216 free(argp->hostname);
4217 if (argp->nfs_ext_u.nfs_extB.secdata)
4218 nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
4219 if (argp->fh)
4220 free(argp->fh);
4221 if (argp->nfs_ext_u.nfs_extA.secdata) {
4222 sec_data_t *sd;
4223 sd = argp->nfs_ext_u.nfs_extA.secdata;
4224 if (sd == NULL)
4225 break;
4226 switch (sd->rpcflavor) {
4227 case AUTH_NONE:
4228 case AUTH_UNIX:
4229 case AUTH_LOOPBACK:
4230 break;
4231 case AUTH_DES:
4232 {
4233 dh_k4_clntdata_t *dhk4;
4234 dhk4 = (dh_k4_clntdata_t *)sd->data;
4235 if (dhk4 == NULL)
4236 break;
4237 if (dhk4->syncaddr.buf)
4238 free(dhk4->syncaddr.buf);
4239 if (dhk4->knconf->knc_protofmly)
4240 free(dhk4->knconf->knc_protofmly);
4241 if (dhk4->knconf->knc_proto)
4242 free(dhk4->knconf->knc_proto);
4243 if (dhk4->knconf)
4244 free(dhk4->knconf);
4245 if (dhk4->netname)
4246 free(dhk4->netname);
4247 free(dhk4);
4248 break;
4249 }
4250 case RPCSEC_GSS:
4251 {
4252 gss_clntdata_t *gss;
4253 gss = (gss_clntdata_t *)sd->data;
4254 if (gss == NULL)
4255 break;
4256 if (gss->mechanism.elements)
4257 free(gss->mechanism.elements);
4258 free(gss);
4259 break;
4260 }
4261 }
4262 }
4263 oldp = argp;
4264 if (argp->nfs_args_ext == NFS_ARGS_EXTB)
4265 argp = argp->nfs_ext_u.nfs_extB.next;
4266 else
4267 argp = NULL;
4268 free(oldp);
4269 }
4270 }
4271
4272 void *
get_netconfig_info(enum type_of_stuff type_of_stuff,char * hostname,rpcprog_t prog,rpcvers_t vers,struct netconfig * nconf,ushort_t port,struct t_info * tinfo,struct t_bind * tbind,caddr_t * fhp,bool_t direct_to_server,char * fspath,enum clnt_stat * cstat,mfs_snego_t * mfssnego)4273 get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname,
4274 rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
4275 ushort_t port, struct t_info *tinfo, struct t_bind *tbind,
4276 caddr_t *fhp, bool_t direct_to_server, char *fspath,
4277 enum clnt_stat *cstat, mfs_snego_t *mfssnego)
4278 {
4279 struct netconfig *nb = NULL;
4280 int ping_server = 0;
4281
4282
4283 if (nconf == NULL)
4284 return (NULL);
4285
4286 switch (type_of_stuff) {
4287 case SERVER_FH:
4288 nb = get_server_fh(hostname, prog, vers, mfssnego,
4289 nconf, port, tinfo, tbind, fhp, direct_to_server,
4290 fspath, cstat);
4291 break;
4292 case SERVER_PING:
4293 ping_server = 1;
4294 case SERVER_ADDR:
4295 nb = get_server_addrorping(hostname, prog, vers,
4296 nconf, port, tinfo, tbind, fhp, direct_to_server,
4297 fspath, cstat, ping_server);
4298 break;
4299 default:
4300 assert(nb != NULL);
4301 }
4302 return (nb);
4303 }
4304
4305 /*
4306 * Get the server address or can we ping it or not.
4307 * Check the portmap cache first for server address.
4308 * If no entries there, ping the server with a NULLPROC rpc.
4309 */
4310 void *
get_server_addrorping(char * hostname,rpcprog_t prog,rpcvers_t vers,struct netconfig * nconf,ushort_t port,struct t_info * tinfo,struct t_bind * tbind,caddr_t * fhp,bool_t direct_to_server,char * fspath,enum clnt_stat * cstat,int ping_server)4311 get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers,
4312 struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
4313 struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server,
4314 char *fspath, enum clnt_stat *cstat, int ping_server)
4315 {
4316 struct timeval tv;
4317 enum clnt_stat cs = RPC_TIMEDOUT;
4318 struct netbuf *nb = NULL;
4319 CLIENT *cl = NULL;
4320 int fd = -1;
4321
4322 if (prog == NFS_PROGRAM && vers == NFS_V4)
4323 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0)
4324 goto done;
4325
4326 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) {
4327 goto done;
4328 }
4329
4330 /* LINTED pointer alignment */
4331 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR))
4332 == NULL) {
4333 goto done;
4334 }
4335
4336 if (direct_to_server != TRUE) {
4337 if (!ping_server) {
4338 if (get_cached_srv_addr(hostname, prog, vers,
4339 nconf, &tbind->addr) == 0)
4340 goto done;
4341 } else {
4342 if (port == 0)
4343 goto done;
4344 }
4345 }
4346 if (setup_nb_parms(nconf, tbind, tinfo, hostname,
4347 fd, direct_to_server, port, prog, vers, 0) < 0)
4348 goto done;
4349
4350 if (port || (direct_to_server == TRUE)) {
4351 tv.tv_sec = 10;
4352 tv.tv_usec = 0;
4353 cl = clnt_tli_create(fd, nconf, &tbind->addr,
4354 prog, vers, 0, 0);
4355 if (cl == NULL)
4356 goto done;
4357
4358 cs = clnt_call(cl, NULLPROC, xdr_void, 0,
4359 xdr_void, 0, tv);
4360 if (cs != RPC_SUCCESS) {
4361 syslog(LOG_ERR, "error is %d", cs);
4362 goto done;
4363 }
4364 }
4365 if (!ping_server) {
4366 nb = (struct netbuf *)malloc(sizeof (struct netbuf));
4367 if (nb == NULL) {
4368 syslog(LOG_ERR, "no memory\n");
4369 goto done;
4370 }
4371 nb->buf = (char *)malloc(tbind->addr.maxlen);
4372 if (nb->buf == NULL) {
4373 syslog(LOG_ERR, "no memory\n");
4374 free(nb);
4375 nb = NULL;
4376 goto done;
4377 }
4378 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
4379 nb->len = tbind->addr.len;
4380 nb->maxlen = tbind->addr.maxlen;
4381 cs = RPC_SUCCESS;
4382 }
4383 done:
4384 destroy_auth_client_handle(cl);
4385 cleanup_tli_parms(tbind, fd);
4386 *cstat = cs;
4387 return (nb);
4388 }
4389