xref: /titanic_41/usr/src/cmd/fs.d/cachefs/cfsadmin/cfsadmin.c (revision cde2885fdf538266ee2a3b08dee2d5075ce8fa2b)
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 
63 char *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 */
100 void usage(char *msg);
101 void pr_err(char *fmt, ...);
102 int cfs_get_opts(char *oarg, struct cachefs_user_values *uvp);
103 int update_cachelabel(char *dirp, char *optionp);
104 void user_values_defaults(struct cachefs_user_values *uvp);
105 int check_user_values_for_sanity(const struct cachefs_user_values *uvp);
106 int cache_stats(char *dirp);
107 int resource_file_grow(char *dirp, int oldcnt, int newcnt);
108 int resource_file_dirty(char *dirp);
109 void 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 
125 int
126 main(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 
405 void
406 usage(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 
430 void
431 pr_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 
459 int
460 cfs_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 
584 int
585 update_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 
710 void
711 user_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 
740 int
741 check_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 
780 int
781 cache_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 
890 int
891 resource_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 
1062 int
1063 resource_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 
1133 int
1134 issue_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 
1220 void
1221 simulate_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