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#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 *
31 *			cfsadmin.c
32 *
33 * Cache FS admin utility.
34 */
35
36#include <assert.h>
37#include <locale.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <string.h>
42#include <errno.h>
43#include <limits.h>
44#include <dirent.h>
45#include <ftw.h>
46#include <fcntl.h>
47#include <ctype.h>
48#include <stdarg.h>
49#include <sys/param.h>
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <sys/statvfs.h>
53#include <sys/mman.h>
54#include <sys/mnttab.h>
55#include <sys/fs/cachefs_fs.h>
56#include <sys/fs/cachefs_dir.h>
57#include <sys/utsname.h>
58#include <rpc/rpc.h>
59#include <priv.h>
60#include "../common/subr.h"
61#include "../common/cachefsd.h"
62
63char *cfsadmin_opts[] = {
64#define		COPT_MAXBLOCKS		0
65		"maxblocks",
66#define		COPT_MINBLOCKS		1
67		"minblocks",
68#define		COPT_THRESHBLOCKS	2
69		"threshblocks",
70
71#define		COPT_MAXFILES 		3
72		"maxfiles",
73#define		COPT_MINFILES		4
74		"minfiles",
75#define		COPT_THRESHFILES	5
76		"threshfiles",
77
78#define		COPT_MAXFILESIZE	6
79		"maxfilesize",
80
81#define		COPT_HIBLOCKS		7
82		"hiblocks",
83#define		COPT_LOWBLOCKS		8
84		"lowblocks",
85#define		COPT_HIFILES		9
86		"hifiles",
87#define		COPT_LOWFILES		10
88		"lowfiles",
89		NULL
90};
91
92#define	bad(val)	((val) == NULL || !isdigit(*(val)))
93
94/* numbers must be valid percentages ranging from 0 to 100 */
95#define	badpercent(val) \
96	((val) == NULL || !isdigit(*(val)) || \
97	    atoi((val)) < 0 || atoi((val)) > 100)
98
99/* forward references */
100void usage(char *msg);
101void pr_err(char *fmt, ...);
102int cfs_get_opts(char *oarg, struct cachefs_user_values *uvp);
103int update_cachelabel(char *dirp, char *optionp);
104void user_values_defaults(struct cachefs_user_values *uvp);
105int check_user_values_for_sanity(const struct cachefs_user_values *uvp);
106int cache_stats(char *dirp);
107int resource_file_grow(char *dirp, int oldcnt, int newcnt);
108int resource_file_dirty(char *dirp);
109void simulate_disconnection(char *namep, int disconnect);
110
111/*
112 *
113 *			main
114 *
115 * Description:
116 *	Main routine for the cfsadmin program.
117 * Arguments:
118 *	argc	number of command line arguments
119 *	argv	command line arguments
120 * Returns:
121 *	Returns 0 for failure, > 0 for an error.
122 * Preconditions:
123 */
124
125int
126main(int argc, char **argv)
127{
128	int c;
129	int xx;
130	int lockid;
131
132	char *cacheid;
133	char *cachedir;
134
135	int cflag;
136	int uflag;
137	int dflag;
138	int sflag;
139	int allflag;
140	int lflag;
141	char *optionp;
142	int Cflag;
143	int Dflag;
144
145	priv_set_t *priv_needed, *priv_effective;
146
147	(void) setlocale(LC_ALL, "");
148
149#if !defined(TEXT_DOMAIN)
150#define	TEXT_DOMAIN	"SYS_TEST"
151#endif
152	(void) textdomain(TEXT_DOMAIN);
153
154	/* set defaults for command line options */
155	cflag = 0;
156	uflag = 0;
157	dflag = 0;
158	sflag = 0;
159	allflag = 0;
160	lflag = 0;
161	optionp = NULL;
162	Cflag = 0;
163	Dflag = 0;
164
165	/* parse the command line arguments */
166	while ((c = getopt(argc, argv, "cCDuo:d:sl")) != EOF) {
167		switch (c) {
168
169		case 'c':		/* create */
170			cflag = 1;
171			break;
172
173			/*
174			 * -C and -D are undocumented calls used
175			 * to simulate disconnection on a file system.
176			 */
177		case 'C':		/* connect file system */
178			Cflag = 1;
179			break;
180		case 'D':		/* disconnect file system */
181			Dflag = 1;
182			break;
183
184		case 'u':		/* update */
185			uflag = 1;
186			break;
187
188		case 'd':		/* delete */
189			dflag = 1;
190			if (strcmp(optarg, "all") == 0)
191				allflag = 1;
192			else
193				cacheid = optarg;
194			break;
195
196		case 's':		/* consistency on demand */
197			sflag = 1;
198			break;
199
200		case 'l':		/* list cache ids */
201			lflag = 1;
202			break;
203
204		case 'o':		/* options for update and create */
205			optionp = optarg;
206			break;
207
208		default:
209			usage(gettext("illegal option"));
210			return (1);
211		}
212	}
213
214	if ((cflag + dflag + lflag + sflag + uflag + Cflag + Dflag) == 0) {
215		usage(gettext("no options specified"));
216		return (1);
217	}
218
219	if (cflag || uflag || dflag || Cflag || Dflag)
220		priv_needed = priv_str_to_set("all", ",", NULL);
221	if ((cflag || uflag) && getuid() != 0) {
222		/* These options create files. We want them to be root owned */
223		pr_err(gettext("must be run by root"));
224		return (1);
225	}
226
227	else if (lflag)
228		priv_needed = priv_str_to_set("file_dac_search,file_dac_read",
229		    ",", NULL);
230
231	else if (sflag)
232		priv_needed = priv_str_to_set("sys_config", ",", NULL);
233
234	priv_effective = priv_allocset();
235	(void) getppriv(PRIV_EFFECTIVE, priv_effective);
236	if (priv_issubset(priv_needed, priv_effective) == 0) {
237		pr_err(gettext("Not privileged."));
238		return (1);
239	}
240	priv_freeset(priv_effective);
241	priv_freeset(priv_needed);
242
243	if ((sflag + Cflag + Dflag) == 0) {
244		/* make sure cachedir is specified */
245		if (argc - 1 != optind) {
246			usage(gettext("cache directory not specified"));
247			return (1);
248		}
249		cachedir = argv[argc-1];
250	} else {
251		/* make sure at least one mount point is specified */
252		if (argc - 1 < optind) {
253			usage(gettext("mount points not specified"));
254			return (1);
255		}
256	}
257
258	/* make sure a reasonable set of flags were specified */
259	if ((cflag + uflag + dflag + sflag + lflag + Cflag + Dflag) != 1) {
260		/* flags are mutually exclusive, at least one must be set */
261		usage(gettext(
262		    "exactly one of -c, -u, -d, -s, -l must be specified"));
263		return (1);
264	}
265
266	/* make sure -o specified with -c or -u */
267	if (optionp && !(cflag|uflag)) {
268		usage(gettext("-o can only be used with -c or -u"));
269		return (1);
270	}
271
272	/* if creating a cache */
273	if (cflag) {
274		struct cachefs_user_values uv;
275		struct cache_label clabel;
276
277		/* get default cache paramaters */
278		user_values_defaults(&uv);
279
280		/* parse the options if specified */
281		if (optionp) {
282			xx = cfs_get_opts(optionp, &uv);
283			if (xx)
284				return (1);
285		}
286
287		/* verify options are reasonable */
288		xx = check_user_values_for_sanity(&uv);
289		if (xx)
290			return (1);
291
292		/* lock the cache directory non-shared */
293		lockid = cachefs_dir_lock(cachedir, 0);
294		if (lockid == -1) {
295			/* quit if could not get the lock */
296			return (1);
297		}
298
299		/* create the cache */
300		xx = cachefs_create_cache(cachedir, &uv, &clabel);
301		if (xx != 0) {
302			if (xx == -2) {
303				/* remove a partially created cache dir */
304				(void) cachefs_delete_all_cache(cachedir);
305			}
306			cachefs_dir_unlock(lockid);
307			return (1);
308		}
309		cachefs_dir_unlock(lockid);
310	}
311
312	/* else if updating resource parameters */
313	else if (uflag) {
314		/* lock the cache directory non-shared */
315		lockid = cachefs_dir_lock(cachedir, 0);
316		if (lockid == -1) {
317			/* quit if could not get the lock */
318			return (1);
319		}
320
321		xx = update_cachelabel(cachedir, optionp);
322		cachefs_dir_unlock(lockid);
323		if (xx != 0) {
324			return (1);
325		}
326	}
327
328	/* else if deleting a specific cacheID (or all caches) */
329	else if (dflag) {
330		/* lock the cache directory non-shared */
331		lockid = cachefs_dir_lock(cachedir, 0);
332		if (lockid == -1) {
333			/* quit if could not get the lock */
334			return (1);
335		}
336
337		/* if the cache is in use */
338		if (cachefs_inuse(cachedir)) {
339			pr_err(gettext("Cache %s is in use and "
340			    "cannot be modified."), cachedir);
341			cachefs_dir_unlock(lockid);
342			return (1);
343		}
344
345		if (allflag)
346			xx = cachefs_delete_all_cache(cachedir);
347		else {
348			/* mark resource file as dirty */
349			xx = resource_file_dirty(cachedir);
350			if (xx == 0)
351				xx = cachefs_delete_cache(cachedir, cacheid);
352		}
353		cachefs_dir_unlock(lockid);
354		if (xx != 0) {
355			return (1);
356		}
357	}
358
359	/* else if listing cache statistics */
360	else if (lflag) {
361		xx = cache_stats(cachedir);
362		if (xx != 0)
363			return (1);
364	}
365
366	/* else if issuing a check event to cached file systems */
367	else if (sflag) {
368		for (xx = optind; xx < argc; xx++) {
369			issue_cod(argv[xx]);
370		}
371	}
372
373	/* else if simulating a disconnection */
374	else if (Dflag) {
375		for (xx = optind; xx < argc; xx++) {
376			simulate_disconnection(argv[xx], 1);
377		}
378	}
379
380	/* else if connection after a simulated disconnection */
381	else if (Cflag) {
382		for (xx = optind; xx < argc; xx++) {
383			simulate_disconnection(argv[xx], 0);
384		}
385	}
386
387	/* return success */
388	return (0);
389}
390
391
392/*
393 *
394 *			usage
395 *
396 * Description:
397 *	Prints a usage message for this utility.
398 * Arguments:
399 *	msgp	message to include with the usage message
400 * Returns:
401 * Preconditions:
402 *	precond(msgp)
403 */
404
405void
406usage(char *msgp)
407{
408	fprintf(stderr, gettext("cfsadmin: %s\n"), msgp);
409	fprintf(stderr, gettext(
410	    "usage: cfsadmin -[cu] [-o parameter-list] cachedir\n"));
411	fprintf(stderr, gettext("       cfsadmin -d [CacheID|all] cachedir\n"));
412	fprintf(stderr, gettext("       cfsadmin -l cachedir\n"));
413	fprintf(stderr, gettext("       cfsadmin -s [mntpnt1 ... | all]\n"));
414}
415
416/*
417 *
418 *			pr_err
419 *
420 * Description:
421 *	Prints an error message to stderr.
422 * Arguments:
423 *	fmt	printf style format
424 *	...	arguments for fmt
425 * Returns:
426 * Preconditions:
427 *	precond(fmt)
428 */
429
430void
431pr_err(char *fmt, ...)
432{
433	va_list ap;
434
435	va_start(ap, fmt);
436	(void) fprintf(stderr, gettext("cfsadmin: "));
437	(void) vfprintf(stderr, fmt, ap);
438	(void) fprintf(stderr, "\n");
439	va_end(ap);
440}
441
442/*
443 *
444 *			cfs_get_opts
445 *
446 * Description:
447 *	Decodes cfs options specified with -o.
448 *	Only the fields referenced by the options are modified.
449 * Arguments:
450 *	oarg	options from -o option
451 *	uvp	place to put options
452 * Returns:
453 *	Returns 0 for success, -1 for an error.
454 * Preconditions:
455 *	precond(oarg)
456 *	precond(uvp)
457 */
458
459int
460cfs_get_opts(char *oarg, struct cachefs_user_values *uvp)
461{
462	char *optstr, *opts, *val;
463	char *saveopts;
464	int badopt;
465
466	/* make a copy of the options because getsubopt modifies it */
467	optstr = opts = strdup(oarg);
468	if (opts == NULL) {
469		pr_err(gettext("no memory"));
470		return (-1);
471	}
472
473	/* process the options */
474	badopt = 0;
475	while (*opts && !badopt) {
476		saveopts = opts;
477		switch (getsubopt(&opts, cfsadmin_opts, &val)) {
478		case COPT_MAXBLOCKS:
479			if (badpercent(val))
480				badopt = 1;
481			else
482				uvp->uv_maxblocks = atoi(val);
483			break;
484		case COPT_MINBLOCKS:
485			if (badpercent(val))
486				badopt = 1;
487			else
488				uvp->uv_minblocks = atoi(val);
489			break;
490		case COPT_THRESHBLOCKS:
491			if (badpercent(val))
492				badopt = 1;
493			else
494				uvp->uv_threshblocks = atoi(val);
495			break;
496
497		case COPT_MAXFILES:
498			if (badpercent(val))
499				badopt = 1;
500			else
501				uvp->uv_maxfiles = atoi(val);
502			break;
503		case COPT_MINFILES:
504			if (badpercent(val))
505				badopt = 1;
506			else
507				uvp->uv_minfiles = atoi(val);
508			break;
509		case COPT_THRESHFILES:
510			if (badpercent(val))
511				badopt = 1;
512			else
513				uvp->uv_threshfiles = atoi(val);
514			break;
515
516		case COPT_MAXFILESIZE:
517			if (bad(val))
518				badopt = 1;
519			else
520				uvp->uv_maxfilesize = atoi(val);
521			break;
522
523		case COPT_HIBLOCKS:
524			if (badpercent(val))
525				badopt = 1;
526			else
527				uvp->uv_hiblocks = atoi(val);
528			break;
529		case COPT_LOWBLOCKS:
530			if (badpercent(val))
531				badopt = 1;
532			else
533				uvp->uv_lowblocks = atoi(val);
534			break;
535		case COPT_HIFILES:
536			if (badpercent(val))
537				badopt = 1;
538			else
539				uvp->uv_hifiles = atoi(val);
540			break;
541		case COPT_LOWFILES:
542			if (badpercent(val))
543				badopt = 1;
544			else
545				uvp->uv_lowfiles = atoi(val);
546			break;
547		default:
548			/* if a bad option argument */
549			pr_err(gettext("Invalid option %s"), saveopts);
550			return (-1);
551		}
552	}
553
554	/* if a bad value for an option, display an error message */
555	if (badopt) {
556		pr_err(gettext("invalid argument to option: \"%s\""),
557		    saveopts);
558	}
559
560	/* free the duplicated option string */
561	free(optstr);
562
563	/* return the result */
564	return (badopt ? -1 : 0);
565}
566
567/*
568 *
569 *			update_cachelabel
570 *
571 * Description:
572 *	Changes the parameters of the cache_label.
573 *	If optionp is NULL then the cache_label is set to
574 *	default values.
575 * Arguments:
576 *	dirp		the name of the cache directory
577 *	optionp		comma delimited options
578 * Returns:
579 *	Returns 0 for success and -1 for an error.
580 * Preconditions:
581 *	precond(dirp)
582 */
583
584int
585update_cachelabel(char *dirp, char *optionp)
586{
587	char path[CACHEFS_XMAXPATH];
588	struct cache_label clabel_new;
589	struct cache_label clabel_orig;
590	struct cachefs_user_values uv_orig, uv_new;
591	int xx;
592
593	/* if the cache is in use */
594	if (cachefs_inuse(dirp)) {
595		pr_err(gettext("Cache %s is in use and cannot be modified."),
596		    dirp);
597		return (-1);
598	}
599
600	/* make sure we don't overwrite path */
601	if (strlen(dirp) > (size_t)PATH_MAX) {
602		pr_err(gettext("name of label file %s is too long."),
603		    dirp);
604		return (-1);
605	}
606
607	/* construct the pathname to the cach_label file */
608	sprintf(path, "%s/%s", dirp, CACHELABEL_NAME);
609
610	/* read the current set of parameters */
611	xx = cachefs_label_file_get(path, &clabel_orig);
612	if (xx == -1) {
613		pr_err(gettext("reading %s failed"), path);
614		return (-1);
615	}
616	xx = cachefs_label_file_vcheck(path, &clabel_orig);
617	if (xx != 0) {
618		pr_err(gettext("version mismatch on %s"), path);
619		return (-1);
620	}
621
622	/* convert the cache_label to user values */
623	xx = cachefs_convert_cl2uv(&clabel_orig, &uv_orig, dirp);
624	if (xx) {
625		return (-1);
626	}
627
628	/* if options were specified */
629	if (optionp) {
630		/* start with the original values */
631		uv_new = uv_orig;
632
633		/* parse the options */
634		xx = cfs_get_opts(optionp, &uv_new);
635		if (xx) {
636			return (-1);
637		}
638
639		/* verify options are reasonable */
640		xx = check_user_values_for_sanity(&uv_new);
641		if (xx) {
642			return (-1);
643		}
644	}
645
646	/* else if options where not specified, get defaults */
647	else {
648		user_values_defaults(&uv_new);
649	}
650
651	/* convert user values to a cache_label */
652	xx = cachefs_convert_uv2cl(&uv_new, &clabel_new, dirp);
653	if (xx) {
654		return (-1);
655	}
656
657	/* do not allow the cache size to shrink */
658	if (uv_orig.uv_maxblocks > uv_new.uv_maxblocks) {
659		pr_err(gettext("Cache size cannot be reduced,"
660			" maxblocks current %d%%, requested %d%%"),
661			uv_orig.uv_maxblocks, uv_new.uv_maxblocks);
662		return (-1);
663	}
664	if (clabel_orig.cl_maxinodes > clabel_new.cl_maxinodes) {
665		pr_err(gettext("Cache size cannot be reduced,"
666			" maxfiles current %d%% requested %d%%"),
667			uv_orig.uv_maxfiles, uv_new.uv_maxfiles);
668		return (-1);
669	}
670
671	/* write back the new values */
672	xx = cachefs_label_file_put(path, &clabel_new);
673	if (xx == -1) {
674		pr_err(gettext("writing %s failed"), path);
675		return (-1);
676	}
677
678	/* put the new values in the duplicate cache label file also */
679	sprintf(path, "%s/%s.dup", dirp, CACHELABEL_NAME);
680	xx = cachefs_label_file_put(path, &clabel_new);
681	if (xx == -1) {
682		pr_err(gettext("writing %s failed"), path);
683		return (-1);
684	}
685
686	/* grow resouces file if necessary */
687	xx = 0;
688	if (clabel_orig.cl_maxinodes != clabel_new.cl_maxinodes) {
689		xx = resource_file_grow(dirp, clabel_orig.cl_maxinodes,
690			clabel_new.cl_maxinodes);
691	}
692
693	/* return status */
694	return (xx);
695}
696
697/*
698 *
699 *			user_values_defaults
700 *
701 * Description:
702 *	Sets default values in the cachefs_user_values object.
703 * Arguments:
704 *	uvp	cachefs_user_values object to set values for
705 * Returns:
706 * Preconditions:
707 *	precond(uvp)
708 */
709
710void
711user_values_defaults(struct cachefs_user_values *uvp)
712{
713	uvp->uv_maxblocks = 90;
714	uvp->uv_minblocks = 0;
715	uvp->uv_threshblocks = 85;
716	uvp->uv_maxfiles = 90;
717	uvp->uv_minfiles = 0;
718	uvp->uv_threshfiles = 85;
719	uvp->uv_maxfilesize = 3;
720	uvp->uv_hiblocks = 85;
721	uvp->uv_lowblocks = 75;
722	uvp->uv_hifiles = 85;
723	uvp->uv_lowfiles = 75;
724}
725
726/*
727 *
728 *			check_user_values_for_sanity
729 *
730 * Description:
731 *	Check the cachefs_user_values for sanity.
732 * Arguments:
733 *	uvp	cachefs_user_values object to check
734 * Returns:
735 *	Returns 0 if okay, -1 if not.
736 * Preconditions:
737 *	precond(uvp)
738 */
739
740int
741check_user_values_for_sanity(const struct cachefs_user_values *uvp)
742{
743	int ret;
744
745	ret = 0;
746
747	if (uvp->uv_lowblocks >= uvp->uv_hiblocks) {
748		pr_err(gettext("lowblocks can't be >= hiblocks."));
749		ret = -1;
750	}
751	if (uvp->uv_lowfiles >= uvp->uv_hifiles) {
752		pr_err(gettext("lowfiles can't be >= hifiles."));
753		ret = -1;
754	}
755
756	/* XXX more conditions to check here? */
757
758	/* XXX make sure thresh values are between min and max values */
759
760	/* return status */
761	return (ret);
762}
763
764/*
765 *
766 *			cache_stats
767 *
768 * Description:
769 *	Show each cache in the directory, cache resource statistics,
770 *	and, for each fs in the cache, the name of the fs, and the
771 *	cache resource parameters.
772 * Arguments:
773 *	dirp	name of the cache directory
774 * Returns:
775 *	Returns 0 for success, -1 for an error.
776 * Errors:
777 * Preconditions:
778 */
779
780int
781cache_stats(char *dirp)
782{
783	DIR *dp;
784	struct dirent64 *dep;
785	char path[CACHEFS_XMAXPATH];
786	struct stat64 statinfo;
787	int ret;
788	int xx;
789	struct cache_label clabel;
790	struct cachefs_user_values uv;
791
792	/* make sure cache dir name is not too long */
793	if (strlen(dirp) > (size_t)PATH_MAX) {
794		pr_err(gettext("path name %s is too long."), dirp);
795		return (-1);
796	}
797
798	/* read the cache label file */
799	sprintf(path, "%s/%s", dirp, CACHELABEL_NAME);
800	xx = cachefs_label_file_get(path, &clabel);
801	if (xx == -1) {
802		pr_err(gettext("Reading %s failed."), path);
803		return (-1);
804	}
805	xx = cachefs_label_file_vcheck(path, &clabel);
806	if (xx != 0) {
807		pr_err(gettext("Version mismatch on %s."), path);
808		return (-1);
809	}
810
811	/* convert the cache_label to user values */
812	xx = cachefs_convert_cl2uv(&clabel, &uv, dirp);
813	if (xx)
814		return (-1);
815
816	/* display the parameters */
817	printf(gettext("cfsadmin: list cache FS information\n"));
818#if 0
819	printf(gettext("   Version      %3d\n"), clabel.cl_cfsversion);
820#endif
821	printf(gettext("   maxblocks    %3d%%\n"), uv.uv_maxblocks);
822	printf(gettext("   minblocks    %3d%%\n"), uv.uv_minblocks);
823	printf(gettext("   threshblocks %3d%%\n"), uv.uv_threshblocks);
824	printf(gettext("   maxfiles     %3d%%\n"), uv.uv_maxfiles);
825	printf(gettext("   minfiles     %3d%%\n"), uv.uv_minfiles);
826	printf(gettext("   threshfiles  %3d%%\n"), uv.uv_threshfiles);
827	printf(gettext("   maxfilesize  %3dMB\n"), uv.uv_maxfilesize);
828
829	/* open the directory */
830	if ((dp = opendir(dirp)) == NULL) {
831		pr_err(gettext("opendir %s failed: %s"), dirp,
832		    strerror(errno));
833		return (-1);
834	}
835
836	/* loop reading the contents of the directory */
837	ret = 0;
838	while ((dep = readdir64(dp)) != NULL) {
839		/* ignore . and .. */
840		if ((strcmp(dep->d_name, ".") == 0) ||
841		    (strcmp(dep->d_name, "..") == 0))
842			continue;
843
844		/* stat the file */
845		sprintf(path, "%s/%s", dirp, dep->d_name);
846		xx = lstat64(path, &statinfo);
847		if (xx == -1) {
848			pr_err(gettext("lstat %s failed: %s"),
849			    path, strerror(errno));
850			closedir(dp);
851			return (-1);
852		}
853
854		/* ignore anything that is not a link */
855		if (!S_ISLNK(statinfo.st_mode))
856			continue;
857
858		/* print the file system cache directory name */
859		printf(gettext("  %s\n"), dep->d_name);
860
861		/* XXX anything else */
862	}
863
864	/* XXX what about stats */
865
866	/* return status */
867	return (ret);
868}
869
870/*
871 *
872 *			resource_file_grow
873 *
874 * Description:
875 *	Grows the resource file in the specified directory
876 *	to its new size.
877 * Arguments:
878 *	dirp	cache directory resource file is in
879 *	oldcnt	previous number of files in resource file
880 *	newcnt	new number of files in resource file
881 * Returns:
882 *	Returns 0 for success, -1 for an error.
883 * Preconditions:
884 *	precond(dirp)
885 *	precond(oldcnt <= newcnt)
886 *	precond(cache is locked exclusively)
887 *	precond(cache is not in use)
888 */
889
890int
891resource_file_grow(char *dirp, int oldcnt, int newcnt)
892{
893	int fd;
894	char path[CACHEFS_XMAXPATH];
895	int xx;
896	struct stat64 st;
897	static struct cachefs_rinfo rold, rnew;
898	struct cache_usage cusage, *cusagep;
899	char buf[MAXBSIZE];
900	int cnt;
901	caddr_t addrp;
902	int dirty;
903
904	/* get info about the resouce file for the various sizes */
905	cachefs_resource_size(oldcnt, &rold);
906	cachefs_resource_size(newcnt, &rnew);
907
908	/* open the resource file for writing */
909	/* this file is < 2GB */
910	sprintf(path, "%s/%s", dirp, RESOURCE_NAME);
911	fd = open(path, O_RDWR);
912	if (fd == -1) {
913		pr_err(gettext("Could not open %s: %s, run fsck"), path,
914		    strerror(errno));
915		return (-1);
916	}
917
918	/* get info on the file */
919	xx = fstat64(fd, &st);
920	if (xx == -1) {
921		pr_err(gettext("Could not stat %s: %s"), path,
922		    strerror(errno));
923		close(fd);
924		return (-1);
925	}
926
927	/* make sure the size is the correct */
928	if ((off_t)st.st_size != rold.r_fsize) {
929		pr_err(gettext("Resource file has wrong size %d %d, run fsck"),
930			(off_t)st.st_size, rold.r_fsize);
931		close(fd);
932		return (-1);
933	}
934
935	/* read the cache usage structure */
936	xx = read(fd, &cusage, sizeof (cusage));
937	if (xx != sizeof (cusage)) {
938		pr_err(gettext("Could not read cache_usage, %d, run fsck"),
939			xx);
940		close(fd);
941		return (-1);
942	}
943
944	/* rewind */
945	xx = lseek(fd, 0, SEEK_SET);
946	if (xx == -1) {
947		pr_err(gettext("Could not lseek %s: %s"), path,
948			strerror(errno));
949		close(fd);
950		return (-1);
951	}
952
953	/* indicate cache is dirty if necessary */
954	dirty = 1;
955	if ((cusage.cu_flags & CUSAGE_ACTIVE) == 0) {
956		dirty = 0;
957		cusage.cu_flags |= CUSAGE_ACTIVE;
958		xx = write(fd, &cusage, sizeof (cusage));
959		if (xx != sizeof (cusage)) {
960			pr_err(gettext(
961				"Could not write cache_usage, %d, run fsck"),
962				xx);
963			close(fd);
964			return (-1);
965		}
966	}
967
968	/* go to the end of the file */
969	xx = lseek(fd, 0, SEEK_END);
970	if (xx == -1) {
971		pr_err(gettext("Could not lseek %s: %s"), path,
972			strerror(errno));
973		close(fd);
974		return (-1);
975	}
976
977	/* grow the file to the new size */
978	memset(buf, 0, sizeof (buf));
979	cnt = rnew.r_fsize - rold.r_fsize;
980	assert((cnt % MAXBSIZE) == 0);
981	cnt /= MAXBSIZE;
982	while (cnt-- > 0) {
983		xx = write(fd, buf, sizeof (buf));
984		if (xx != sizeof (buf)) {
985			pr_err(gettext("Could not write file, %d, run fsck"),
986				xx);
987			close(fd);
988			return (-1);
989		}
990	}
991
992	/* mmap the file into our address space */
993	addrp = mmap(NULL, rnew.r_fsize, PROT_READ | PROT_WRITE, MAP_SHARED,
994		fd, 0);
995	if (addrp == (void *)-1) {
996		pr_err(gettext("Could not mmap file %s: %s"), path,
997			strerror(errno));
998		close(fd);
999		return (-1);
1000	}
1001
1002	/* close the file descriptor, we do not need it anymore */
1003	close(fd);
1004
1005	/* move the idents region to its new location */
1006	memmove(addrp + rnew.r_identoffset, addrp + rold.r_identoffset,
1007		rold.r_identsize);
1008
1009	/* zero out the old idents region that is now in the pointers region */
1010	memset(addrp + rold.r_identoffset, 0,
1011		rnew.r_identoffset - rold.r_identoffset);
1012
1013	/* sync the data to the file */
1014	xx = msync(addrp, rnew.r_fsize, MS_SYNC);
1015	if (xx == -1) {
1016		pr_err(gettext("Could not sync file %s: %s"), path,
1017			strerror(errno));
1018		munmap(addrp, rnew.r_fsize);
1019		return (-1);
1020	}
1021
1022	/* mark the file as clean if it was not dirty originally */
1023	if (!dirty) {
1024		cusagep = (struct cache_usage *)addrp;
1025		cusagep->cu_flags &= ~CUSAGE_ACTIVE;
1026
1027		/* sync the data to the file */
1028		xx = msync(addrp, rnew.r_fsize, MS_SYNC);
1029		if (xx == -1) {
1030			pr_err(gettext("Could not sync file %s: %s"), path,
1031				strerror(errno));
1032			munmap(addrp, rnew.r_fsize);
1033			return (-1);
1034		}
1035	}
1036
1037	/* unmap the file */
1038	munmap(addrp, rnew.r_fsize);
1039
1040	/* return success */
1041	return (0);
1042}
1043
1044/*
1045 *
1046 *			resource_file_dirty
1047 *
1048 * Description:
1049 *	Marks the resource file as dirty.
1050 *	This will cause fsck to fix it up the next time it
1051 *	is run.
1052 * Arguments:
1053 *	dirp	cache directory resource file is in
1054 * Returns:
1055 *	Returns 0 for success, -1 for an error.
1056 * Preconditions:
1057 *	precond(dirp)
1058 *	precond(cache is locked exclusively)
1059 *	precond(cache is not in use)
1060 */
1061
1062int
1063resource_file_dirty(char *dirp)
1064{
1065	int fd;
1066	char path[CACHEFS_XMAXPATH];
1067	int xx;
1068	struct cache_usage cusage;
1069
1070	/* open the resource file for writing */
1071	/* this file is < 2GB */
1072	sprintf(path, "%s/%s", dirp, RESOURCE_NAME);
1073	fd = open(path, O_RDWR);
1074	if (fd == -1) {
1075		pr_err(gettext("Could not open %s: %s, run fsck"), path,
1076		    strerror(errno));
1077		return (-1);
1078	}
1079
1080	/* read the cache usage structure */
1081	xx = read(fd, &cusage, sizeof (cusage));
1082	if (xx != sizeof (cusage)) {
1083		pr_err(gettext("Could not read cache_usage, %d, run fsck"),
1084			xx);
1085		close(fd);
1086		return (-1);
1087	}
1088
1089	/* rewind */
1090	xx = lseek(fd, 0, SEEK_SET);
1091	if (xx == -1) {
1092		pr_err(gettext("Could not lseek %s: %s"), path,
1093			strerror(errno));
1094		close(fd);
1095		return (-1);
1096	}
1097
1098	/* indicate cache is dirty if necessary */
1099	if ((cusage.cu_flags & CUSAGE_ACTIVE) == 0) {
1100		cusage.cu_flags |= CUSAGE_ACTIVE;
1101		xx = write(fd, &cusage, sizeof (cusage));
1102		if (xx != sizeof (cusage)) {
1103			pr_err(gettext(
1104				"Could not write cache_usage, %d, run fsck"),
1105				xx);
1106			close(fd);
1107			return (-1);
1108		}
1109	}
1110
1111	xx = close(fd);
1112	if (xx == -1) {
1113		pr_err(gettext("Could not successfully close %s: %s"), path,
1114			strerror(errno));
1115	}
1116	return (xx);
1117}
1118
1119/*
1120 *
1121 *			issue_cod
1122 *
1123 * Description:
1124 *	Executes the _FIOCOD ioctl on the specified file.
1125 * Arguments:
1126 *	name	filename to issue ioctl on (or "all")
1127 * Returns:
1128 *	Returns 0 for success, -1 for an error.
1129 * Preconditions:
1130 *	precond(dirp)
1131 */
1132
1133int
1134issue_cod(char *name)
1135{
1136	int fd;
1137	int xx;
1138	int arg;
1139	char *dirp;
1140	FILE *mfp;
1141	struct mnttab mt, mtpref;
1142
1143#ifndef MNTTYPE_CACHEFS
1144#define	MNTTYPE_CACHEFS	"cachefs"
1145#endif
1146
1147	arg = 0;
1148	if (strcmp(name, "all") == 0) {
1149		/*
1150		 * if "all" was specified rather than a mount point,
1151		 * we locate a cachefs mount in /etc/mnttab (any cachefs
1152		 * mount will do).  We issue the ioctl on this mount point,
1153		 * and specify a non-zero argument to the ioctl.  The non-zero
1154		 * arg tells the kernel to do demandconst on all relevant
1155		 * cachefs mounts
1156		 */
1157		if ((mfp = fopen(MNTTAB, "r")) == NULL) {
1158			pr_err(gettext("Could not open %s."), MNTTAB);
1159			return (-1);
1160		}
1161		mtpref.mnt_special = NULL;
1162		mtpref.mnt_mountp = NULL;
1163		mtpref.mnt_mntopts = NULL;
1164		mtpref.mnt_time = NULL;
1165		mtpref.mnt_fstype = MNTTYPE_CACHEFS;
1166		if (getmntany(mfp, &mt, &mtpref) != 0) {
1167			(void) fclose(mfp);
1168			return (-1);
1169		}
1170		(void) fclose(mfp);
1171		dirp = mt.mnt_mountp;
1172		arg = 1;
1173	} else {
1174		dirp = name;
1175	}
1176
1177	/* open the file */
1178	fd = open(dirp, O_RDONLY);
1179	if (fd == -1) {
1180		pr_err(gettext("Could not open %s, %s."),
1181			dirp, strerror(errno));
1182		return (-1);
1183	}
1184
1185	/* issue the ioctl */
1186	xx = ioctl(fd, _FIOCOD, arg);
1187	if (xx) {
1188		if (errno == ENOTTY) {
1189			pr_err(gettext("%s is not a CacheFS file system"),
1190				dirp);
1191		} else if (errno == EBUSY) {
1192			if (arg == 0)
1193				/* we're quiet if "all" was specified */
1194				pr_err(gettext("CacheFS file system %s is not"
1195					" mounted demandconst."), dirp);
1196		} else {
1197			pr_err(gettext("Could not issue consistency request"
1198				" on %s\n    %s."), dirp, strerror(errno));
1199		}
1200	}
1201	close(fd);
1202	return (xx);
1203}
1204
1205/*
1206 *
1207 *			simulate_disconnection
1208 *
1209 * Description:
1210 *	Sends the rpc message to the cachefsd to turn simulated
1211 *	disconnection on or off
1212 * Arguments:
1213 *	namep		name of file system or "all"
1214 *	disconnect	1 means disconnect, 0 means connect
1215 * Returns:
1216 * Preconditions:
1217 *	precond(name)
1218 */
1219
1220void
1221simulate_disconnection(char *namep, int disconnect)
1222{
1223	CLIENT *clnt;
1224	enum clnt_stat retval;
1225	int ret;
1226	int xx;
1227	int result;
1228	char *hostp;
1229	struct utsname info;
1230	struct cachefsd_disconnection_args args;
1231	char *msgp;
1232	struct timeval tval;
1233
1234	/* get the host name */
1235	xx = uname(&info);
1236	if (xx == -1) {
1237		pr_err(gettext("cannot get host name, errno %d"), errno);
1238		return;
1239	}
1240	hostp = info.nodename;
1241
1242	/* creat the connection to the daemon */
1243	clnt = clnt_create(hostp, CACHEFSDPROG, CACHEFSDVERS, "local");
1244	if (clnt == NULL) {
1245		pr_err(gettext("cachefsd is not running"));
1246		return;
1247	}
1248
1249	/* give it a chance to complete */
1250	tval.tv_sec = 60 * 60 * 24;
1251	tval.tv_usec = 0;
1252	clnt_control(clnt, CLSET_TIMEOUT, (char *)&tval);
1253
1254	/* perform the operation */
1255	args.cda_mntpt = namep;
1256	args.cda_disconnect = disconnect;
1257	retval = cachefsd_disconnection_1(&args, &ret, clnt);
1258	if (retval != RPC_SUCCESS) {
1259		clnt_perror(clnt, gettext("cachefsd is not responding"));
1260		clnt_destroy(clnt);
1261		return;
1262	}
1263
1264	/* check for error from daemon */
1265	if (ret != 0) {
1266		if (disconnect) {
1267			switch (ret) {
1268			default:
1269				msgp = "unknown error";
1270				break;
1271			case 1:
1272				msgp = "not mounted disconnectable";
1273				break;
1274			case 2:
1275				msgp = "already disconnected";
1276				break;
1277			case 3:
1278				msgp = "not a cached file system";
1279				break;
1280			}
1281			pr_err(gettext("Could not disconnect %s: %s"),
1282			    namep, msgp);
1283		} else {
1284			switch (ret) {
1285			default:
1286				msgp = "unknown error";
1287				break;
1288			case 1:
1289				msgp = "already connected";
1290				break;
1291			case 2:
1292				msgp = "not simulated disconnection";
1293				break;
1294			case 3:
1295				msgp = "not a cached file system";
1296				break;
1297			}
1298			pr_err(gettext("Could not reconnect %s: %s"),
1299			    namep, msgp);
1300		}
1301	}
1302
1303	ret = 0;
1304
1305	clnt_destroy(clnt);
1306}
1307