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