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
77char *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 */
160void usage(char *msgp);
161void pr_err(char *fmt, ...);
162int set_cfs_args(char *optionp, struct cachefs_mountargs *margsp, int *mflagp,
163    char **backfstypepp, char **reducepp, int *notifyp, int *nfsv3pass);
164int get_mount_point(char *cachedirp, char *specp, char **pathpp);
165int dobackmnt(struct cachefs_mountargs *margsp, char *reducep, char *specp,
166    char *backfstypep, char *mynamep, int readonly);
167void doexec(char *fstype, char **newargv, char *myname);
168char *get_back_fsid(char *specp);
169char *get_cacheid(char *, char *);
170void record_mount(char *mntp, char *specp, char *backfsp, char *backfstypep,
171    char *cachedirp, char *cacheidp, char *optionp, char *reducep);
172int daemon_notify(char *cachedirp, char *cacheidp);
173int pingserver(char *backmntp);
174int check_cache(char *cachedirp);
175uint32_t cachefs_get_back_nfsvers(char *cfs_backfs, int nomnttab);
176int cfs_nfsv4_build_opts(char *optionp, char *cfs_nfsv4ops);
177
178int nomnttab;
179int 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
194int
195main(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);
493clean_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
599void
600usage(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
626void
627pr_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
665int
666set_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
1018int
1019get_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
1088int
1089dobackmnt(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
1171void
1172doexec(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
1237char *
1238get_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
1263char *
1264get_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 */
1304int
1305check_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
1358void
1359record_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
1393int
1394daemon_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 */
1437int
1438pingserver(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 */
1484int
1485first_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 */
1519uint32_t
1520cachefs_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
1604end:
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 */
1625int
1626cfs_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