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