xref: /titanic_41/usr/src/cmd/fs.d/cachefs/mount/mount.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /*	    All Rights Reserved */
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  *
39  *			mount.c
40  *
41  * Cachefs mount program.
42  */
43 
44 #include <locale.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <strings.h>
49 #include <stdarg.h>
50 #include <unistd.h>
51 #include <limits.h>
52 #include <errno.h>
53 #include <wait.h>
54 #include <ctype.h>
55 #include <fcntl.h>
56 #include <fslib.h>
57 #include <sys/types.h>
58 #include <sys/time.h>
59 #include <sys/param.h>
60 #include <sys/stat.h>
61 #include <sys/fcntl.h>
62 #include <sys/mount.h>
63 #include <sys/mntent.h>
64 #include <sys/mnttab.h>
65 #include <sys/mntio.h>
66 #include <sys/fs/cachefs_fs.h>
67 #include <sys/utsname.h>
68 #include <rpc/rpc.h>
69 #include <kstat.h>
70 #undef MAX
71 #include <nfs/nfs.h>
72 #include <nfs/nfs_clnt.h>
73 #include <sys/mkdev.h>
74 #include "../common/subr.h"
75 #include "../common/cachefsd.h"
76 
77 char *cfs_opts[] = {
78 #define	CFSOPT_BACKFSTYPE	0
79 	"backfstype",
80 #define	CFSOPT_CACHEDIR		1
81 	"cachedir",
82 #define	CFSOPT_CACHEID		2
83 	"cacheid",
84 #define	CFSOPT_BACKPATH		3
85 	"backpath",
86 
87 #define	CFSOPT_WRITEAROUND	4
88 	"write-around",
89 #define	CFSOPT_NONSHARED	5
90 	"non-shared",
91 
92 #define	CFSOPT_DISCONNECTABLE	6
93 	"disconnectable",
94 #define	CFSOPT_SOFT		7
95 	"soft",
96 
97 #define	CFSOPT_NOCONST		8
98 	"noconst",
99 #define	CFSOPT_CODCONST		9
100 	"demandconst",
101 
102 #define	CFSOPT_LOCALACCESS	10
103 	"local-access",
104 #define	CFSOPT_LAZYMOUNT	11
105 	"lazy-mount",
106 
107 #define	CFSOPT_RW		12
108 	"rw",
109 #define	CFSOPT_RO		13
110 	"ro",
111 #define	CFSOPT_SUID		14
112 	"suid",
113 #define	CFSOPT_NOSUID		15
114 	"nosuid",
115 #define	CFSOPT_REMOUNT		16
116 	"remount",
117 #define	CFSOPT_FGSIZE		17
118 	"fgsize",
119 #define	CFSOPT_POPSIZE		18
120 	"popsize",
121 #define	CFSOPT_ACREGMIN		19
122 	"acregmin",
123 #define	CFSOPT_ACREGMAX		20
124 	"acregmax",
125 #define	CFSOPT_ACDIRMIN		21
126 	"acdirmin",
127 #define	CFSOPT_ACDIRMAX		22
128 	"acdirmax",
129 #define	CFSOPT_ACTIMEO		23
130 	"actimeo",
131 #define	CFSOPT_SLIDE		24
132 	"slide",
133 #define	CFSOPT_NOSETSEC		25
134 	"nosec",	/* XXX should we use MNTOPT_NOTSETSEC? */
135 #define	CFSOPT_LLOCK		26
136 	"llock",
137 #define	CFSOPT_NONOTIFY		27
138 	"nonotify",
139 #define	CFSOPT_SNR		28
140 	"snr",
141 #define	CFSOPT_NOFILL		29
142 	"nofill",
143 #ifdef CFS_NFSV3_PASSTHROUGH
144 #define	CFSOPT_NFSV3PASSTHROUGH	30
145 	"nfsv3pass",
146 #endif /* CFS_NFSV3_PASSTHROUGH */
147 	NULL
148 };
149 
150 #define	MNTTYPE_CFS	"cachefs"	/* XXX - to be added to mntent.h */
151 					/* XXX - and should be cachefs */
152 #define	CFS_DEF_DIR	"/cache"	/* XXX - should be added to cfs.h */
153 
154 #define	bad(val) (val == NULL || !isdigit(*val))
155 
156 #define	VFS_PATH	"/usr/lib/fs"
157 #define	ALT_PATH	"/etc/fs"
158 
159 /* forward references */
160 void usage(char *msgp);
161 void pr_err(char *fmt, ...);
162 int set_cfs_args(char *optionp, struct cachefs_mountargs *margsp, int *mflagp,
163     char **backfstypepp, char **reducepp, int *notifyp, int *nfsv3pass);
164 int get_mount_point(char *cachedirp, char *specp, char **pathpp);
165 int dobackmnt(struct cachefs_mountargs *margsp, char *reducep, char *specp,
166     char *backfstypep, char *mynamep, int readonly);
167 void doexec(char *fstype, char **newargv, char *myname);
168 char *get_back_fsid(char *specp);
169 char *get_cacheid(char *, char *);
170 void record_mount(char *mntp, char *specp, char *backfsp, char *backfstypep,
171     char *cachedirp, char *cacheidp, char *optionp, char *reducep);
172 int daemon_notify(char *cachedirp, char *cacheidp);
173 int pingserver(char *backmntp);
174 int check_cache(char *cachedirp);
175 uint32_t cachefs_get_back_nfsvers(char *cfs_backfs, int nomnttab);
176 int cfs_nfsv4_build_opts(char *optionp, char *cfs_nfsv4ops);
177 
178 int nomnttab;
179 int quiet;
180 /*
181  *
182  *			main
183  *
184  * Description:
185  *	Main routine for the cachefs mount program.
186  * Arguments:
187  *	argc	number of command line arguments
188  *	argv	list of command line arguments
189  * Returns:
190  *	Returns 0 for success, 1 an error was encountered.
191  * Preconditions:
192  */
193 
194 main(int argc, char **argv)
195 {
196 	char *myname;
197 	char *optionp;
198 	char *opigp;
199 	int mflag;
200 	int readonly;
201 	struct cachefs_mountargs margs;
202 	char *backfstypep;
203 	char *reducep;
204 	char *specp;
205 	int xx;
206 	int stat_loc;
207 	char *newargv[20];
208 	char *mntp;
209 	pid_t pid;
210 	int mounted;
211 	int c;
212 	int lockid;
213 	int Oflg;
214 	char *strp;
215 	char servname[33];
216 	int notify = 1;
217 	struct stat64 statb;
218 	struct mnttagdesc mtdesc;
219 	char mops[MAX_MNTOPT_STR];
220 	char cfs_nfsv4ops[MAX_MNTOPT_STR];
221 	uint32_t nfsvers = 0;
222 	uint32_t nfsvers_error = FALSE;
223 	int nfsv3pass = 0;
224 	(void) setlocale(LC_ALL, "");
225 #if !defined(TEXT_DOMAIN)
226 #define	TEXT_DOMAIN	"SYS_TEST"
227 #endif
228 	(void) textdomain(TEXT_DOMAIN);
229 
230 	if (argv[0]) {
231 		myname = strrchr(argv[0], '/');
232 		if (myname)
233 			myname++;
234 		else
235 			myname = argv[0];
236 	} else {
237 		myname = "path unknown";
238 	}
239 
240 	optionp = NULL;
241 	nomnttab = 0;
242 	quiet = 0;
243 	readonly = 0;
244 	Oflg = 0;
245 	cfs_nfsv4ops[0] = '\0';
246 
247 	/* process command line options */
248 	while ((c = getopt(argc, argv, "mo:Orq")) != EOF) {
249 		switch (c) {
250 		case 'm':	/* no entry in /etc/mnttab */
251 			nomnttab = 1;
252 			break;
253 
254 		case 'o':
255 			optionp = optarg;
256 			break;
257 
258 		case 'O':
259 			Oflg++;
260 			break;
261 
262 		case 'r':	/* read only mount */
263 			readonly = 1;
264 			break;
265 
266 		case 'q':
267 			quiet = 1;
268 			break;
269 
270 		default:
271 			usage("invalid option");
272 			return (1);
273 		}
274 	}
275 
276 	/* if -o not specified */
277 	if (optionp == NULL) {
278 		usage(gettext("\"-o backfstype\" must be specified"));
279 		return (1);
280 	}
281 
282 	/* verify special device and mount point are specified */
283 	if (argc - optind < 2) {
284 		usage(gettext("must specify special device and mount point"));
285 		return (1);
286 	}
287 
288 	/* Store mount point and special device. */
289 	specp = argv[argc - 2];
290 	mntp = argv[argc - 1];
291 
292 	/* Initialize default mount values */
293 	margs.cfs_options.opt_flags = CFS_ACCESS_BACKFS;
294 	margs.cfs_options.opt_popsize = DEF_POP_SIZE;
295 	margs.cfs_options.opt_fgsize = DEF_FILEGRP_SIZE;
296 	margs.cfs_fsid = NULL;
297 	memset(margs.cfs_cacheid, 0, sizeof (margs.cfs_cacheid));
298 	margs.cfs_cachedir = CFS_DEF_DIR;
299 	margs.cfs_backfs = NULL;
300 	margs.cfs_acregmin = 0;
301 	margs.cfs_acregmax = 0;
302 	margs.cfs_acdirmin = 0;
303 	margs.cfs_acdirmax = 0;
304 	mflag = MS_OPTIONSTR;
305 	if (nomnttab)
306 		mflag |= MS_NOMNTTAB;
307 	backfstypep = NULL;
308 
309 	/* process -o options */
310 	xx = set_cfs_args(optionp, &margs, &mflag, &backfstypep, &reducep,
311 	    &notify, &nfsv3pass);
312 	if (xx) {
313 		return (1);
314 	}
315 	strcpy(mops, optionp);
316 
317 	/* backfstype has to be specified */
318 	if (backfstypep == NULL) {
319 		usage(gettext("\"-o backfstype\" must be specified"));
320 		return (1);
321 	}
322 
323 	if ((strcmp(backfstypep, "nfs") != 0) &&
324 				(strcmp(backfstypep, "hsfs") != 0)) {
325 		pr_err(gettext("%s as backfstype is not supported."),
326 					backfstypep);
327 		return (1);
328 	}
329 
330 	/* set default write mode if not specified */
331 	if ((margs.cfs_options.opt_flags &
332 	    (CFS_WRITE_AROUND|CFS_NONSHARED)) == 0) {
333 		margs.cfs_options.opt_flags |= CFS_WRITE_AROUND;
334 		if (strcmp(backfstypep, "hsfs") == 0)
335 			mflag |= MS_RDONLY;
336 	}
337 
338 	/* if read-only was specified with the -r option */
339 	if (readonly) {
340 		mflag |= MS_RDONLY;
341 	}
342 
343 	/* if overlay was specified with -O option */
344 	if (Oflg) {
345 		mflag |= MS_OVERLAY;
346 	}
347 
348 	/* get the fsid of the backfs and the cacheid */
349 	margs.cfs_fsid = get_back_fsid(specp);
350 	if (margs.cfs_fsid == NULL) {
351 		pr_err(gettext("out of memory"));
352 		return (1);
353 	}
354 
355 	/*
356 	 * If using this cachedir to mount a file system for the first time
357 	 * after reboot, the ncheck for the sanity of the cachedir
358 	 */
359 	if (first_time_ab(margs.cfs_cachedir))
360 		if (check_cache(margs.cfs_cachedir))
361 			return (1);
362 
363 	/* get the front file system cache id if necessary */
364 	if (margs.cfs_cacheid[0] == '\0') {
365 		char *cacheid = get_cacheid(margs.cfs_fsid, mntp);
366 
367 		if (cacheid == NULL) {
368 			pr_err(gettext("default cacheid too long"));
369 			return (1);
370 		}
371 
372 		strcpy(margs.cfs_cacheid, cacheid);
373 	}
374 
375 	/* lock the cache directory shared */
376 	lockid = cachefs_dir_lock(margs.cfs_cachedir, 1);
377 	if (lockid == -1) {
378 		/* exit if could not get the lock */
379 		return (1);
380 	}
381 
382 	/* if no mount point was specified and we are not remounting */
383 	mounted = 0;
384 	if ((margs.cfs_backfs == NULL) &&
385 	    (((mflag & MS_REMOUNT) == 0) ||
386 	    (margs.cfs_options.opt_flags & CFS_SLIDE))) {
387 		/* if a disconnectable mount */
388 		xx = 0;
389 		if (margs.cfs_options.opt_flags & CFS_DISCONNECTABLE) {
390 			/* see if the server is alive */
391 			xx = pingserver(specp);
392 		}
393 
394 		/* attempt to mount the back file system */
395 		if (xx == 0) {
396 			xx = dobackmnt(&margs, reducep, specp, backfstypep,
397 			    myname, readonly);
398 			/*
399 			 * nfs mount exits with a value of 32 if a timeout
400 			 * error occurs trying the mount.
401 			 */
402 			if (xx && (xx != 32)) {
403 				cachefs_dir_unlock(lockid);
404 				rmdir(margs.cfs_backfs);
405 				return (1);
406 			}
407 			if (xx == 0)
408 				mounted = 1;
409 		}
410 	}
411 
412 	/*
413 	 * At this point the back file system should be mounted.
414 	 * Get NFS version information for the back filesystem if
415 	 * it is NFS. The version information is required
416 	 * because NFS version 4 is incompatible with cachefs
417 	 * and we provide pass-through support for NFS version 4
418 	 * with cachefs, aka the cachefs mount is installed but
419 	 * there is no caching. This is indicated to the kernel
420 	 * during the mount by setting the CFS_BACKFS_NFSV4 flag.
421 	 */
422 	if (margs.cfs_backfs != NULL && strcmp(backfstypep, "nfs") == 0) {
423 
424 		nfsvers = cachefs_get_back_nfsvers(margs.cfs_backfs, nomnttab);
425 		switch (nfsvers) {
426 		case 2:
427 			break;
428 
429 		case 3:
430 			if (nfsv3pass) {
431 				/* Force pass through (for debugging) */
432 				margs.cfs_options.opt_flags = CFS_BACKFS_NFSV4;
433 				if (cfs_nfsv4_build_opts(optionp,
434 						cfs_nfsv4ops) != 0) {
435 					nfsvers_error = TRUE;
436 					goto clean_backmnt;
437 				}
438 			}
439 			break;
440 
441 		case 4:
442 			/*
443 			 * overwrite old option flags with NFSv4 flag.
444 			 * Note that will also operate in strict
445 			 * consistency mode. Clean up the option string
446 			 * to get rid of the cachefs-specific options
447 			 * to be in sync with the opt flags, otherwise
448 			 * these can make it into the mnttab and cause
449 			 * problems (esp. the disconnected option).
450 			 */
451 			margs.cfs_options.opt_flags = CFS_BACKFS_NFSV4;
452 			if (cfs_nfsv4_build_opts(optionp, cfs_nfsv4ops) != 0) {
453 				nfsvers_error = TRUE;
454 				goto clean_backmnt;
455 			}
456 			break;
457 
458 		default:
459 			/* error, unknown version */
460 			nfsvers_error = TRUE;
461 			goto clean_backmnt;
462 		}
463 	}
464 
465 	/*
466 	 * Grab server name from special file arg if it is there or set
467 	 * server name to "server unknown".
468 	 */
469 	margs.cfs_hostname = servname;
470 	strncpy(servname, specp, sizeof (servname));
471 	servname[sizeof (servname) - 1] = '\0';
472 	strp = strchr(servname, ':');
473 	if (strp == NULL) {
474 		margs.cfs_hostname = "server unknown";
475 		margs.cfs_backfsname = specp;
476 	} else {
477 		*strp = '\0';
478 		/*
479 		 * The rest of the special file arg is the name of
480 		 * the back filesystem.
481 		 */
482 		strp++;
483 		margs.cfs_backfsname = strp;
484 	}
485 
486 	/* mount the cache file system */
487 	xx = mount((margs.cfs_backfs != NULL) ? margs.cfs_backfs : "nobackfs",
488 		mntp, mflag | MS_DATA, MNTTYPE_CFS,
489 		&margs, sizeof (margs),
490 		(cfs_nfsv4ops[0] == '\0' ? mops : cfs_nfsv4ops),
491 		MAX_MNTOPT_STR);
492 clean_backmnt:
493 	if (xx == -1 || nfsvers_error) {
494 		if (nfsvers_error) {
495 			pr_err(gettext("nfs version error."));
496 		} else if (errno == ESRCH) {
497 			pr_err(gettext("mount failed, options do not match."));
498 		} else if ((errno == EAGAIN) && (margs.cfs_backfs == NULL)) {
499 			pr_err(gettext("mount failed, server not responding."));
500 		} else {
501 			pr_err(gettext("mount failed %s"), strerror(errno));
502 		}
503 
504 		/* try to unmount the back file system if we mounted it */
505 		if (mounted) {
506 			xx = 1;
507 			newargv[xx++] = "umount";
508 			newargv[xx++] = margs.cfs_backfs;
509 			newargv[xx++] = NULL;
510 
511 			/* fork */
512 			if ((pid = fork()) == -1) {
513 				pr_err(gettext("could not fork: %s"),
514 				    strerror(errno));
515 				cachefs_dir_unlock(lockid);
516 				return (1);
517 			}
518 
519 			/* if the child */
520 			if (pid == 0) {
521 				/* do the unmount */
522 				doexec(backfstypep, newargv, "umount");
523 			}
524 
525 			/* else if the parent */
526 			else {
527 				wait(0);
528 			}
529 			rmdir(margs.cfs_backfs);
530 		}
531 
532 		cachefs_dir_unlock(lockid);
533 		return (1);
534 	}
535 
536 	/* release the lock on the cache directory */
537 	cachefs_dir_unlock(lockid);
538 
539 	/* record the mount information in the fscache directory */
540 	record_mount(mntp, specp, margs.cfs_backfs, backfstypep,
541 		margs.cfs_cachedir, margs.cfs_cacheid,
542 		(cfs_nfsv4ops[0] == '\0' ? optionp : cfs_nfsv4ops), reducep);
543 
544 	/* notify the daemon of the mount */
545 	if (notify)
546 		daemon_notify(margs.cfs_cachedir, margs.cfs_cacheid);
547 
548 	/* update mnttab file if necessary */
549 	if (!nomnttab) {
550 		/*
551 		 * If we added the back file system, tag it with ignore,
552 		 * however, don't fail the mount after its done
553 		 * if the tag can't be added (eg., this would cause
554 		 * automounter problems).
555 		 */
556 		if (mounted) {
557 			FILE *mt;
558 			struct extmnttab mnt;
559 
560 			if ((mt = fopen(MNTTAB, "r")) == NULL)
561 				return (1);
562 			while (getextmntent(mt, &mnt, sizeof (mnt)) != -1) {
563 				if (mnt.mnt_mountp != NULL &&
564 				    strcmp(margs.cfs_backfs,
565 					mnt.mnt_mountp) == 0) {
566 					/* found it, do tag ioctl */
567 					mtdesc.mtd_major = mnt.mnt_major;
568 					mtdesc.mtd_minor = mnt.mnt_minor;
569 					mtdesc.mtd_mntpt = margs.cfs_backfs;
570 					mtdesc.mtd_tag = MNTOPT_IGNORE;
571 
572 					(void) ioctl(fileno(mt),
573 						MNTIOC_SETTAG, &mtdesc);
574 					break;
575 				}
576 			}
577 			fclose(mt);
578 		}
579 	}
580 
581 	/* return success */
582 	return (0);
583 }
584 
585 
586 /*
587  *
588  *			usage
589  *
590  * Description:
591  *	Prints a short usage message.
592  * Arguments:
593  *	msgp	message to include with the usage message
594  * Returns:
595  * Preconditions:
596  */
597 
598 void
599 usage(char *msgp)
600 {
601 	if (msgp) {
602 		pr_err(gettext("%s"), msgp);
603 	}
604 
605 	fprintf(stderr,
606 	    gettext("Usage: mount -F cachefs [generic options] "
607 	    "-o backfstype=file_system_type[FSTypespecific_options] "
608 	    "special mount_point\n"));
609 }
610 
611 /*
612  *
613  *			pr_err
614  *
615  * Description:
616  *	Prints an error message to stderr.
617  * Arguments:
618  *	fmt	printf style format
619  *	...	arguments for fmt
620  * Returns:
621  * Preconditions:
622  *	precond(fmt)
623  */
624 
625 void
626 pr_err(char *fmt, ...)
627 {
628 	va_list ap;
629 
630 	va_start(ap, fmt);
631 	(void) fprintf(stderr, gettext("mount -F cachefs: "));
632 	(void) vfprintf(stderr, fmt, ap);
633 	(void) fprintf(stderr, "\n");
634 	va_end(ap);
635 }
636 
637 /*
638  *
639  *			set_cfs_args
640  *
641  * Description:
642  *	Parse the comma delimited set of options specified by optionp
643  *	and puts the results in margsp, mflagp, and backfstypepp.
644  *	A string is constructed of options which are not specific to
645  *	cfs and is placed in reducepp.
646  *	Pointers to strings are invalid if this routine is called again.
647  *	No initialization is done on margsp, mflagp, or backfstypepp.
648  * Arguments:
649  *	optionp		string of comma delimited options
650  *	margsp		option results for the mount dataptr arg
651  *	mflagp		option results for the mount mflag arg
652  *	backfstypepp	set to name of back file system type
653  *	reducepp	set to the option string without cfs specific options
654  * Returns:
655  *	Returns 0 for success, -1 for an error.
656  * Preconditions:
657  *	precond(optionp)
658  *	precond(margsp)
659  *	precond(mflagp)
660  *	precond(backfstypepp)
661  *	precond(reducepp)
662  */
663 
664 int
665 set_cfs_args(char *optionp, struct cachefs_mountargs *margsp, int *mflagp,
666     char **backfstypepp, char **reducepp, int *notifyp, int *nfsv3pass)
667 {
668 	static char *optstrp = NULL;
669 	static char *reducep = NULL;
670 	char *savep, *strp, *valp;
671 	int badopt;
672 	int ret;
673 	int o_backpath = 0;
674 	int o_writemode = 0;
675 	int xx;
676 	uint_t yy;
677 	struct stat64 sinfo;
678 	char *pbuf;
679 
680 	/* free up any previous options */
681 	free(optstrp);
682 	optstrp = NULL;
683 	free(reducep);
684 	reducep = NULL;
685 
686 	/* make a copy of the options so we can modify it */
687 	optstrp = strp = strdup(optionp);
688 	reducep = malloc(strlen(optionp) + 1000);
689 	if ((strp == NULL) || (reducep == NULL)) {
690 		pr_err(gettext("out of memory"));
691 		return (-1);
692 	}
693 	*reducep = '\0';
694 
695 	/* parse the options */
696 	badopt = 0;
697 	ret = 0;
698 	while (*strp) {
699 		savep = strp;
700 		switch (getsubopt(&strp, cfs_opts, &valp)) {
701 
702 		case CFSOPT_BACKFSTYPE:
703 			if (valp == NULL)
704 				badopt = 1;
705 			else
706 				*backfstypepp = valp;
707 			break;
708 
709 		case CFSOPT_CACHEDIR:
710 			if (valp == NULL)
711 				badopt = 1;
712 			else {
713 				margsp->cfs_cachedir = valp;
714 				if (valp[0] != '/') {
715 				    pbuf = (char *)malloc(MAXPATHLEN +
716 						strlen(valp) + 3);
717 				    if (pbuf == NULL) {
718 					pr_err(gettext("out of memory"));
719 					badopt = 1;
720 					break;
721 				    }
722 				    if (getcwd(pbuf, MAXPATHLEN+1) == NULL) {
723 					pr_err(gettext("cachedir too long"));
724 					badopt = 1;
725 					break;
726 				    }
727 				    if (pbuf[strlen(pbuf)-1] != '/')
728 					strcat(pbuf, "/");
729 				    strcat(pbuf, valp);
730 				    margsp->cfs_cachedir = pbuf;
731 				}
732 			}
733 			break;
734 
735 		case CFSOPT_CACHEID:
736 			if (valp == NULL) {
737 				badopt = 1;
738 				break;
739 			}
740 
741 			if (strlen(valp) >= (size_t)C_MAX_MOUNT_FSCDIRNAME) {
742 				pr_err(gettext("cacheid too long"));
743 				badopt = 1;
744 				break;
745 			}
746 
747 			memset(margsp->cfs_cacheid, 0, C_MAX_MOUNT_FSCDIRNAME);
748 			strcpy(margsp->cfs_cacheid, valp);
749 			break;
750 
751 		case CFSOPT_BACKPATH:
752 			if (valp == NULL)
753 				badopt = 1;
754 			else {
755 				margsp->cfs_backfs = valp;
756 				o_backpath = 1;
757 			}
758 			break;
759 
760 		case CFSOPT_WRITEAROUND:
761 			margsp->cfs_options.opt_flags |= CFS_WRITE_AROUND;
762 			o_writemode++;
763 			break;
764 
765 		case CFSOPT_NONSHARED:
766 			margsp->cfs_options.opt_flags |= CFS_NONSHARED;
767 			o_writemode++;
768 			break;
769 
770 		case CFSOPT_NOCONST:
771 			margsp->cfs_options.opt_flags |= CFS_NOCONST_MODE;
772 			break;
773 
774 		case CFSOPT_CODCONST:
775 			margsp->cfs_options.opt_flags |= CFS_CODCONST_MODE;
776 			break;
777 
778 		case CFSOPT_LOCALACCESS:
779 			margsp->cfs_options.opt_flags &= ~CFS_ACCESS_BACKFS;
780 			break;
781 
782 		case CFSOPT_NOSETSEC:
783 			margsp->cfs_options.opt_flags |= CFS_NOACL;
784 			break;
785 
786 		case CFSOPT_LLOCK:
787 			margsp->cfs_options.opt_flags |= CFS_LLOCK;
788 			strcat(reducep, ",");
789 			strcat(reducep, savep);
790 			break;
791 
792 		case CFSOPT_REMOUNT:
793 			*mflagp |= MS_REMOUNT;
794 			break;
795 
796 		case CFSOPT_SLIDE:
797 			margsp->cfs_options.opt_flags |= CFS_SLIDE;
798 			break;
799 
800 		case CFSOPT_FGSIZE:
801 			if (bad(valp))
802 				badopt = 1;
803 			else
804 				margsp->cfs_options.opt_fgsize = atoi(valp);
805 			break;
806 
807 		case CFSOPT_POPSIZE:
808 			if (bad(valp))
809 				badopt = 1;
810 			else
811 				margsp->cfs_options.opt_popsize =
812 				    atoi(valp) * 1024;
813 			break;
814 
815 		case CFSOPT_ACREGMIN:
816 			if (bad(valp))
817 				badopt = 1;
818 			else
819 				margsp->cfs_acregmin = atoi(valp);
820 			break;
821 
822 		case CFSOPT_ACREGMAX:
823 			if (bad(valp))
824 				badopt = 1;
825 			else
826 				margsp->cfs_acregmax = atoi(valp);
827 			break;
828 
829 		case CFSOPT_ACDIRMIN:
830 			if (bad(valp))
831 				badopt = 1;
832 			else
833 				margsp->cfs_acdirmin = atoi(valp);
834 			break;
835 
836 		case CFSOPT_ACDIRMAX:
837 			if (bad(valp))
838 				badopt = 1;
839 			else
840 				margsp->cfs_acdirmax = atoi(valp);
841 			break;
842 
843 		case CFSOPT_ACTIMEO:
844 			if (bad(valp))
845 				badopt = 1;
846 			else {
847 				yy = atoi(valp);
848 				margsp->cfs_acregmin = yy;
849 				margsp->cfs_acregmax = yy;
850 				margsp->cfs_acdirmin = yy;
851 				margsp->cfs_acdirmax = yy;
852 			}
853 			/*
854 			 * Note that we do not pass the actimeo options
855 			 * to the back file system.  This change was
856 			 * made for Chart.  Chart needs noac or actimeo=0
857 			 * so it makes no sense to pass these options on.
858 			 * In theory it should be okay to not pass these
859 			 * options on for regular cachefs mounts since
860 			 * cachefs perform the required attribute caching.
861 			 */
862 			break;
863 
864 #if 0
865 		case CFSOPT_LAZYMOUNT:
866 			margsp->cfs_options.opt_flags |= CFS_LAZYMOUNT;
867 			break;
868 #endif
869 
870 		case CFSOPT_DISCONNECTABLE:
871 		case CFSOPT_SNR:
872 			margsp->cfs_options.opt_flags |= CFS_DISCONNECTABLE;
873 			break;
874 
875 		case CFSOPT_NOFILL:
876 			margsp->cfs_options.opt_flags |= CFS_NOFILL;
877 			break;
878 
879 		case CFSOPT_SOFT:
880 			margsp->cfs_options.opt_flags |= CFS_SOFT;
881 			break;
882 
883 		case CFSOPT_NONOTIFY:
884 			*notifyp = 0;
885 			break;
886 
887 #ifdef CFS_NFSV3_PASSTHROUGH
888 		case CFSOPT_NFSV3PASSTHROUGH:
889 			*nfsv3pass = 1;
890 			break;
891 #endif /* CFS_NFSV3_PASSTHROUGH */
892 
893 		default:
894 			/*
895 			 * unknown or vfs layer option, save for the back
896 			 * file system
897 			 */
898 			strcat(reducep, ",");
899 			strcat(reducep, savep);
900 			break;
901 		}
902 
903 		/* if a lexical error occurred */
904 		if (badopt) {
905 			pr_err(gettext("invalid argument to option: \"%s\""),
906 			    savep);
907 			badopt = 0;
908 			ret = -1;
909 		}
910 	}
911 
912 	/*
913 	 * Should mount backfs soft if disconnectable & non-shared options
914 	 * are used. NFS soft option allows reads and writes to TIMEOUT
915 	 * when the server is not responding, which is crucial for
916 	 * disconnectable option to work all the time in non-shared mode.
917 	 *
918 	 * Should mount backfs semisoft if disconnectable & write-around
919 	 * are used. NFS semisoft option allows reads to TIMEOUT and
920 	 * write to block when the server is not responding, which is
921 	 * good for write around option because it is shared.
922 	 *
923 	 * Since disconnectable and strict options are conflicting,
924 	 * when disconnectable option is used, default option is set to
925 	 * demandconst.
926 	 */
927 
928 	if (margsp->cfs_options.opt_flags & (CFS_DISCONNECTABLE | CFS_SOFT))
929 		if (margsp->cfs_options.opt_flags & CFS_NONSHARED) {
930 			strcat(reducep, ",soft,noprint");
931 			margsp->cfs_options.opt_flags |= CFS_CODCONST_MODE;
932 		}
933 		else
934 			strcat(reducep, ",semisoft,noprint");
935 
936 	if (!(margsp->cfs_options.opt_flags & CFS_DISCONNECTABLE)) {
937 		/* not snr, no need to notify the cachefsd */
938 		*notifyp = 0;
939 	}
940 
941 	/* additional nfs options needed so disconnectable will work */
942 	if (margsp->cfs_options.opt_flags & CFS_DISCONNECTABLE) {
943 		/*
944 		 * retry=0 so cachefs can mount if nfs mount fails
945 		 *   even with this nfs takes 3 minutes to give up
946 		 * actimeo=0 because NFS does not pick up new ctime after
947 		 *	rename
948 		 */
949 		strcat(reducep, ",retry=0");
950 		if (margsp->cfs_options.opt_flags & CFS_NONSHARED)
951 			strcat(reducep, ",actimeo=0");
952 	}
953 
954 	/* check for conflicting options */
955 	xx = margsp->cfs_options.opt_flags;
956 	if (o_backpath & (xx & CFS_DISCONNECTABLE)) {
957 		pr_err(gettext("backpath cannot be used with disconnectable"));
958 		ret = -1;
959 	}
960 	if (margsp->cfs_acregmin > margsp->cfs_acregmax) {
961 		pr_err(gettext("acregmin cannot be greater than acregmax"));
962 		ret = -1;
963 	}
964 	if (margsp->cfs_acdirmin > margsp->cfs_acdirmax) {
965 		pr_err(gettext("acdirmin cannot be greater than acdirmax"));
966 		ret = -1;
967 	}
968 
969 	xx = CFS_NOCONST_MODE | CFS_CODCONST_MODE;
970 	if ((margsp->cfs_options.opt_flags & xx) == xx) {
971 		pr_err(gettext("only one of noconst and demandconst"
972 			" may be specified"));
973 		ret = -1;
974 	}
975 
976 	if (o_writemode > 1) {
977 		pr_err(gettext(
978 		    "only one of write-around or non-shared"
979 		    " may be specified"));
980 		ret = -1;
981 	}
982 
983 	/* if an error occured */
984 	if (ret)
985 		return (-1);
986 
987 	/* if there are any options which are not mount specific */
988 	if (*reducep)
989 		*reducepp = reducep + 1;
990 	else
991 		*reducepp = NULL;
992 
993 	/* return success */
994 	return (0);
995 }
996 
997 /*
998  *
999  *			get_mount_point
1000  *
1001  * Description:
1002  *	Makes a suitable mount point for the back file system.
1003  *	The name of the mount point created is stored in a malloced
1004  *	buffer in pathpp
1005  * Arguments:
1006  *	cachedirp	the name of the cache directory
1007  *	specp		the special name of the device for the file system
1008  *	pathpp		where to store the mount point
1009  * Returns:
1010  *	Returns 0 for success, -1 for an error.
1011  * Preconditions:
1012  *	precond(cachedirp)
1013  *	precond(specp)
1014  *	precond(pathpp)
1015  */
1016 
1017 int
1018 get_mount_point(char *cachedirp, char *specp, char **pathpp)
1019 {
1020 	char *strp;
1021 	char *namep;
1022 	struct stat64 stat1, stat2;
1023 	int xx;
1024 	int index;
1025 	int max;
1026 
1027 	/* make a copy of the special device name */
1028 	specp = strdup(specp);
1029 	if (specp == NULL) {
1030 		pr_err(gettext("out of memory"));
1031 		return (-1);
1032 	}
1033 
1034 	/* convert the special device name into a file name */
1035 	strp = specp;
1036 	while (strp = strchr(strp, '/')) {
1037 		*strp = '_';
1038 	}
1039 
1040 	/* get some space for the path name */
1041 	strp = malloc(MAXPATHLEN);
1042 	if (strp == NULL) {
1043 		pr_err(gettext("out of memory"));
1044 		return (-1);
1045 	}
1046 
1047 	/* see if the mount directory is valid */
1048 	/* backfs can contain large files */
1049 	sprintf(strp, "%s/%s", cachedirp, BACKMNT_NAME);
1050 	xx = stat64(strp, &stat1);
1051 	if ((xx == -1) || !S_ISDIR(stat1.st_mode)) {
1052 		pr_err(gettext("%s is not a valid cache."), strp);
1053 		return (-1);
1054 	}
1055 
1056 	/* find a directory name we can use */
1057 	max = 10000;
1058 	namep = strp + strlen(strp);
1059 	for (index = 1; index < max; index++) {
1060 
1061 		/* construct a directory name to consider */
1062 		if (index == 1)
1063 			sprintf(namep, "/%s", specp);
1064 		else
1065 			sprintf(namep, "/%s_%d", specp, index);
1066 
1067 		/* try to create the directory */
1068 		xx = mkdir(strp, 0755);
1069 		if (xx == 0) {
1070 			/* done if the create succeeded */
1071 			break;
1072 		}
1073 	}
1074 
1075 	/* if the search failed */
1076 	if (index >= max) {
1077 		pr_err(gettext("could not create a directory"));
1078 		return (-1);
1079 	}
1080 
1081 	/* return success */
1082 	*pathpp = strp;
1083 	return (0);
1084 }
1085 
1086 
1087 int
1088 dobackmnt(struct cachefs_mountargs *margsp, char *reducep, char *specp,
1089     char *backfstypep, char *mynamep, int readonly)
1090 {
1091 	int xx;
1092 	pid_t pid;
1093 	char *newargv[20];
1094 	int stat_loc;
1095 
1096 	/* get a suitable mount point */
1097 	xx = get_mount_point(margsp->cfs_cachedir, specp, &margsp->cfs_backfs);
1098 	if (xx)
1099 		return (1);
1100 
1101 	/* construct argument list for mounting the back file system */
1102 	xx = 1;
1103 	newargv[xx++] = "mount";
1104 	if (readonly)
1105 		newargv[xx++] = "-r";
1106 	if (nomnttab)
1107 		newargv[xx++] = "-m";
1108 	if (quiet)
1109 		newargv[xx++] = "-q";
1110 	if (reducep) {
1111 		newargv[xx++] = "-o";
1112 		newargv[xx++] = reducep;
1113 	}
1114 	newargv[xx++] = specp;
1115 	newargv[xx++] = margsp->cfs_backfs;
1116 	newargv[xx++] = NULL;
1117 
1118 	/* fork */
1119 	if ((pid = fork()) == -1) {
1120 		pr_err(gettext("could not fork %s"), strerror(errno));
1121 		return (1);
1122 	}
1123 
1124 	/* if the child */
1125 	if (pid == 0) {
1126 		/* do the mount */
1127 		doexec(backfstypep, newargv, mynamep);
1128 	}
1129 
1130 	/* else if the parent */
1131 	else {
1132 		/* wait for the child to exit */
1133 		if (wait(&stat_loc) == -1) {
1134 			pr_err(gettext("wait failed %s"), strerror(errno));
1135 			return (1);
1136 		}
1137 
1138 		if (!WIFEXITED(stat_loc)) {
1139 			pr_err(gettext("back mount did not exit"));
1140 			return (1);
1141 		}
1142 
1143 		xx = WEXITSTATUS(stat_loc);
1144 		if (xx) {
1145 			pr_err(gettext("back mount failed"));
1146 			return (xx);
1147 		}
1148 	}
1149 
1150 	return (0);
1151 }
1152 
1153 /*
1154  *
1155  *			doexec
1156  *
1157  * Description:
1158  *	Execs the specified program with the specified command line arguments.
1159  *	This function never returns.
1160  * Arguments:
1161  *	fstype		type of file system
1162  *	newargv		command line arguments
1163  *	progp		name of program to exec
1164  * Returns:
1165  * Preconditions:
1166  *	precond(fstype)
1167  *	precond(newargv)
1168  */
1169 
1170 void
1171 doexec(char *fstype, char *newargv[], char *progp)
1172 {
1173 	char	full_path[PATH_MAX];
1174 	char	alter_path[PATH_MAX];
1175 	char	*vfs_path = VFS_PATH;
1176 	char	*alt_path = ALT_PATH;
1177 
1178 	/* build the full pathname of the fstype dependent command. */
1179 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, progp);
1180 	sprintf(alter_path, "%s/%s/%s", alt_path, fstype, progp);
1181 
1182 	/* if the program exists */
1183 	if (access(full_path, 0) == 0) {
1184 		/* invoke the program */
1185 		execv(full_path, &newargv[1]);
1186 
1187 		/* if wrong permissions */
1188 		if (errno == EACCES) {
1189 			pr_err(gettext("cannot execute %s %s"),
1190 			    full_path, strerror(errno));
1191 		}
1192 
1193 		/* if it did not work and the shell might make it */
1194 		if (errno == ENOEXEC) {
1195 			newargv[0] = "sh";
1196 			newargv[1] = full_path;
1197 			execv("/sbin/sh", &newargv[0]);
1198 		}
1199 	}
1200 
1201 	/* try the alternate path */
1202 	execv(alter_path, &newargv[1]);
1203 
1204 	/* if wrong permissions */
1205 	if (errno == EACCES) {
1206 		pr_err(gettext("cannot execute %s %s"),
1207 		    alter_path, strerror(errno));
1208 	}
1209 
1210 	/* if it did not work and the shell might make it */
1211 	if (errno == ENOEXEC) {
1212 		newargv[0] = "sh";
1213 		newargv[1] = alter_path;
1214 		execv("/sbin/sh", &newargv[0]);
1215 	}
1216 
1217 	pr_err(gettext("operation not applicable to FSType %s"), fstype);
1218 	exit(1);
1219 }
1220 
1221 /*
1222  *
1223  *			get_back_fsid
1224  *
1225  * Description:
1226  *	Determines a unique identifier for the back file system.
1227  * Arguments:
1228  *	specp	the special file of the back fs
1229  * Returns:
1230  *	Returns a malloc string which is the unique identifer
1231  *	or NULL on failure.  NULL is only returned if malloc fails.
1232  * Preconditions:
1233  *	precond(specp)
1234  */
1235 
1236 char *
1237 get_back_fsid(char *specp)
1238 {
1239 	return (strdup(specp));
1240 }
1241 
1242 /*
1243  *
1244  *			get_cacheid
1245  *
1246  * Description:
1247  *	Determines an identifier for the front file system cache.
1248  *	The returned string points to a static buffer which is
1249  *	overwritten on each call.
1250  *	The length of the returned string is < C_MAX_MOUNT_FSCDIRNAME.
1251  * Arguments:
1252  *	fsidp	back file system id
1253  *	mntp	front file system mount point
1254  * Returns:
1255  *	Returns a pointer to the string identifier, or NULL if the
1256  *	identifier was overflowed.
1257  * Preconditions:
1258  *	precond(fsidp)
1259  *	precond(mntp)
1260  */
1261 
1262 char *
1263 get_cacheid(char *fsidp, char *mntp)
1264 {
1265 	char *c1;
1266 	static char buf[PATH_MAX];
1267 	char mnt_copy[PATH_MAX];
1268 
1269 	/* strip off trailing space in mountpoint -- autofs fallout */
1270 	if (strlen(mntp) >= sizeof (mnt_copy))
1271 		return (NULL);
1272 	(void) strcpy(mnt_copy, mntp);
1273 	c1 = mnt_copy + strlen(mnt_copy) - 1;
1274 	if (*c1 == ' ')
1275 		*c1 = '\0';
1276 
1277 	if ((strlen(fsidp) + strlen(mnt_copy) + 2) >=
1278 	    (size_t)C_MAX_MOUNT_FSCDIRNAME)
1279 		return (NULL);
1280 
1281 	strcpy(buf, fsidp);
1282 	strcat(buf, ":");
1283 	strcat(buf, mnt_copy);
1284 	c1 = buf;
1285 	while ((c1 = strpbrk(c1, "/")) != NULL)
1286 		*c1 = '_';
1287 	return (buf);
1288 }
1289 
1290 
1291 /*
1292  *
1293  *			check_cache
1294  *
1295  * Description:
1296  *	Checks the cache we are about to use.
1297  * Arguments:
1298  *	cachedirp	cachedirectory to check
1299  * Returns:
1300  *	Returns 0 for success, -1 for an error.
1301  * Preconditions:
1302  */
1303 int
1304 check_cache(cachedirp)
1305 	char *cachedirp;
1306 {
1307 	char *fsck_argv[4];
1308 	int status = 0;
1309 	pid_t pid;
1310 
1311 	fsck_argv[1] = "fsck";
1312 	fsck_argv[2] = cachedirp;
1313 	fsck_argv[3] = NULL;
1314 
1315 	/* fork */
1316 	if ((pid = fork()) == -1) {
1317 		pr_err(gettext("could not fork %s"),
1318 		    strerror(errno));
1319 		return (1);
1320 	}
1321 
1322 	if (pid == 0) {
1323 		/* do the fsck */
1324 		doexec("cachefs", fsck_argv, "fsck");
1325 	} else {
1326 		/* wait for the child to exit */
1327 		if (wait(&status) == -1) {
1328 			pr_err(gettext("wait failed %s"),
1329 			    strerror(errno));
1330 			return (1);
1331 		}
1332 
1333 		if (!WIFEXITED(status)) {
1334 			pr_err(gettext("cache fsck did not exit"));
1335 			return (1);
1336 		}
1337 
1338 		if (WEXITSTATUS(status) != 0) {
1339 			pr_err(gettext("cache fsck mount failed"));
1340 			return (1);
1341 		}
1342 	}
1343 	return (0);
1344 }
1345 
1346 /*
1347  *
1348  *			record_mount
1349  *
1350  * Description:
1351  *	Records mount information in a file in the fscache directory.
1352  * Arguments:
1353  * Returns:
1354  * Preconditions:
1355  */
1356 
1357 void
1358 record_mount(char *mntp, char *specp, char *backfsp, char *backfstypep,
1359     char *cachedirp, char *cacheidp, char *optionp, char *reducep)
1360 {
1361 	char buf[MAXPATHLEN*2];
1362 	FILE *fout;
1363 	time_t tval;
1364 
1365 	tval = time(NULL);
1366 
1367 	/* this file is < 2GB */
1368 	sprintf(buf, "%s/%s/%s", cachedirp, cacheidp, CACHEFS_MNT_FILE);
1369 	fout = fopen(buf, "w");
1370 	if (fout == NULL) {
1371 		pr_err(gettext("could not open %s, %d"), buf, errno);
1372 		return;
1373 	}
1374 
1375 	fprintf(fout, "cachedir: %s\n", cachedirp);
1376 	fprintf(fout, "mnt_point: %s\n", mntp);
1377 	if (specp) {
1378 		fprintf(fout, "special: %s\n", specp);
1379 	}
1380 	if (backfsp)
1381 		fprintf(fout, "backpath: %s\n", backfsp);
1382 	fprintf(fout, "backfstype: %s\n", backfstypep);
1383 	fprintf(fout, "cacheid: %s\n", cacheidp);
1384 	fprintf(fout, "cachefs_options: %s\n", optionp);
1385 	if (reducep)
1386 		fprintf(fout, "backfs_options: %s\n", reducep);
1387 	fprintf(fout, "mount_time: %u\n", tval);
1388 
1389 	fclose(fout);
1390 }
1391 
1392 int
1393 daemon_notify(char *cachedirp, char *cacheidp)
1394 {
1395 	CLIENT *clnt;
1396 	enum clnt_stat retval;
1397 	int ret;
1398 	int xx;
1399 	int result;
1400 	char *hostp;
1401 	struct utsname info;
1402 	struct cachefsd_fs_mounted args;
1403 
1404 	/* get the host name */
1405 	xx = uname(&info);
1406 	if (xx == -1) {
1407 		pr_err(gettext("cannot get host name, errno %d"), errno);
1408 		return (1);
1409 	}
1410 	hostp = info.nodename;
1411 
1412 	/* creat the connection to the daemon */
1413 	clnt = clnt_create(hostp, CACHEFSDPROG, CACHEFSDVERS, "local");
1414 	if (clnt == NULL) {
1415 		pr_err(gettext("cachefsd is not running"));
1416 		return (1);
1417 	}
1418 
1419 	args.mt_cachedir = cachedirp;
1420 	args.mt_cacheid = cacheidp;
1421 	retval = cachefsd_fs_mounted_1(&args, NULL, clnt);
1422 	if (retval != RPC_SUCCESS) {
1423 		clnt_perror(clnt, gettext("cachefsd is not responding"));
1424 		clnt_destroy(clnt);
1425 		return (1);
1426 	}
1427 
1428 	ret = 0;
1429 
1430 	clnt_destroy(clnt);
1431 
1432 	return (ret);
1433 }
1434 
1435 /* returns 0 if the server is alive, -1 if an error */
1436 int
1437 pingserver(char *backmntp)
1438 {
1439 	CLIENT *clnt;
1440 	static struct timeval TIMEOUT = { 25, 0 };
1441 	enum clnt_stat retval;
1442 	int ret;
1443 	int xx;
1444 	char *hostp;
1445 	char buf[MAXPATHLEN];
1446 	char *pc;
1447 
1448 	/* get the host name */
1449 	strcpy(buf, backmntp);
1450 	pc = strchr(buf, ':');
1451 	if (pc == NULL) {
1452 		/* no host name, pretend it works */
1453 		return (0);
1454 	}
1455 	*pc = '\0';
1456 	hostp = buf;
1457 
1458 	/* create the connection to the mount daemon */
1459 	clnt = clnt_create(hostp, NFS_PROGRAM, NFS_VERSION, "udp");
1460 	if (clnt == NULL) {
1461 		return (-1);
1462 	}
1463 
1464 	ret = 0;
1465 
1466 	/* see if the mountd responds */
1467 	retval = clnt_call(clnt, 0, xdr_void, NULL, xdr_void, NULL,
1468 	    TIMEOUT);
1469 	if (retval != RPC_SUCCESS) {
1470 		ret = -1;
1471 	}
1472 
1473 	clnt_destroy(clnt);
1474 
1475 	return (ret);
1476 }
1477 
1478 /*
1479  * first_time_ab  : first time after boot - returns non-zero value
1480  *                  if the cachedir is being used for the first time
1481  *                  after the system reboot, otherwise zero.
1482  */
1483 int
1484 first_time_ab(char *buf)
1485 {
1486 	struct stat sinfo;
1487 	char name[MAXPATHLEN];
1488 	int ufd;
1489 	time32_t btime;
1490 
1491 	sprintf(name, "%s/%s", buf, CACHEFS_UNMNT_FILE);
1492 	if (stat(name, &sinfo) != 0)
1493 		return (1);
1494 	if (sinfo.st_size == 0)
1495 		return (1);
1496 	if ((ufd = open(name, O_RDONLY)) == -1)
1497 		return (1);
1498 	if (read(ufd, &btime, sizeof (time32_t)) == -1)
1499 		return (1);
1500 	close(ufd);
1501 	if (get_boottime() != btime)
1502 		return (1);
1503 	return (0);
1504 }
1505 
1506 /*
1507  * cachefs_get_back_nfsvers
1508  *
1509  * Returns:	nfs version
1510  *
1511  * Params:
1512  *		cfs_backfs	- backfile system mountpoint
1513  *		nomnttab	- mnttab entry does not exist
1514  *
1515  * Uses the kstat interface to extract the nfs version for
1516  * the mount.
1517  */
1518 uint32_t
1519 cachefs_get_back_nfsvers(char *cfs_backfs, int nomnttab)
1520 {
1521 	kstat_ctl_t *kc = NULL;
1522 	FILE *mnttab = NULL;
1523 	struct extmnttab mnt;
1524 	kstat_t *ksp;
1525 	dev_t my_fsid = NODEV;
1526 	struct mntinfo_kstat mik;
1527 	uint32_t nfsvers = 0;
1528 	struct stat64 st;
1529 
1530 	/*
1531 	 * Initialize kernel statistics facility.
1532 	 */
1533 	if ((kc = kstat_open()) == NULL) {
1534 		pr_err(gettext("kstat_open() can't open /dev/kstat: %s"),
1535 			strerror(errno));
1536 		goto end;
1537 	}
1538 
1539 	/*
1540 	 * Locate the mount information in the mnttab if the nomnttab
1541 	 * flag is not set, otherwise look for the entry by doing
1542 	 * stat'ting the mountpoint.
1543 	 */
1544 	if (!nomnttab) {
1545 		if ((mnttab = fopen(MNTTAB, "r")) == NULL) {
1546 			pr_err(gettext("can't open /etc/mnttab: %s"),
1547 				strerror(errno));
1548 			goto end;
1549 		}
1550 
1551 		while (getextmntent(mnttab, &mnt, sizeof (mnt)) != -1) {
1552 			if (mnt.mnt_mountp == NULL ||
1553 			    strcmp(cfs_backfs, mnt.mnt_mountp) != 0) {
1554 				continue;
1555 			}
1556 			my_fsid = makedev(mnt.mnt_major, mnt.mnt_minor);
1557 			break;
1558 		}
1559 	}
1560 
1561 	if (my_fsid == NODEV) {
1562 		if (stat64(cfs_backfs, &st) == -1) {
1563 			pr_err(gettext("can't stat mountpoint: %s"),
1564 				strerror(errno));
1565 			goto end;
1566 		} else {
1567 			my_fsid = st.st_dev;
1568 		}
1569 
1570 	}
1571 
1572 	/*
1573 	 * Walk the kstat control structures to locate the
1574 	 * structure that describes the nfs module/mntinfo
1575 	 * statistics for the mounted backfilesystem.
1576 	 */
1577 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1578 
1579 		if (ksp->ks_type != KSTAT_TYPE_RAW)
1580 			continue;
1581 		if (strcmp(ksp->ks_module, "nfs") != 0)
1582 			continue;
1583 		if (strcmp(ksp->ks_name, "mntinfo") != 0)
1584 			continue;
1585 		if ((my_fsid & MAXMIN) != ksp->ks_instance)
1586 			continue;
1587 
1588 		/*
1589 		 * At this point we have located the
1590 		 * kstat info for the mount, read the
1591 		 * statistics and return version info.
1592 		 */
1593 		if (kstat_read(kc, ksp, &mik) == -1) {
1594 			pr_err(gettext("kstat_read() can't read %s/%s: %s"),
1595 				ksp->ks_module, ksp->ks_name, strerror(errno));
1596 			goto end;
1597 		}
1598 
1599 		nfsvers = mik.mik_vers;
1600 		break;
1601 	}
1602 
1603 end:
1604 	if (kc)
1605 		kstat_close(kc);
1606 	if (mnttab)
1607 		fclose(mnttab);
1608 
1609 	return (nfsvers);
1610 }
1611 
1612 /*
1613  * cfs_nfsv4_build_opts
1614  *
1615  * Returns: 0 on success, -1 on failure
1616  *
1617  * Params:
1618  *	optionp		- original option pointer
1619  *	cfs_nfsv4ops	- modified options for nfsv4 cachefs mount
1620  *
1621  * Parse the comma delimited set of options specified by optionp
1622  * and clean out options that we don't want to use with NFSv4.
1623  */
1624 int
1625 cfs_nfsv4_build_opts(char *optionp, char *cfs_nfsv4ops)
1626 {
1627 	char *optstrp;
1628 	char *strp;
1629 	char *savep;
1630 	char *valp;
1631 	uint32_t first = TRUE;
1632 
1633 	/* Make a copy of the options so we can modify it */
1634 	optstrp = strp = strdup(optionp);
1635 	if (strp == NULL) {
1636 		pr_err(gettext("out of memory"));
1637 		return (-1);
1638 	}
1639 
1640 	/* Parse the options, cfs_nfsv4ops is initialized in main */
1641 	while (*strp) {
1642 		savep = strp;
1643 		switch (getsubopt(&strp, cfs_opts, &valp)) {
1644 
1645 		/* Ignore options that set cfs option flags */
1646 		case CFSOPT_WRITEAROUND:
1647 		case CFSOPT_NONSHARED:
1648 		case CFSOPT_NOCONST:
1649 		case CFSOPT_CODCONST:
1650 		case CFSOPT_LOCALACCESS:
1651 		case CFSOPT_NOSETSEC:
1652 		case CFSOPT_LLOCK:
1653 		case CFSOPT_SLIDE:
1654 		case CFSOPT_DISCONNECTABLE:
1655 		case CFSOPT_SNR:
1656 		case CFSOPT_NOFILL:
1657 		case CFSOPT_SOFT:
1658 			break;
1659 
1660 		default:
1661 			/*
1662 			 * Copy in option for cachefs nfsv4 mount.
1663 			 */
1664 			snprintf(cfs_nfsv4ops, MAX_MNTOPT_STR,
1665 				"%s%s%s", cfs_nfsv4ops, first ? "" : ",",
1666 				savep);
1667 			first = FALSE;
1668 			break;
1669 		}
1670 	}
1671 	free(optstrp);
1672 
1673 	return (0);
1674 }
1675