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