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