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
main(int argc,char ** argv)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
usage(char * msgp)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
pr_err(char * fmt,...)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
cfs_get_opts(char * oarg,struct cachefs_user_values * uvp)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
update_cachelabel(char * dirp,char * optionp)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
user_values_defaults(struct cachefs_user_values * uvp)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
check_user_values_for_sanity(const struct cachefs_user_values * uvp)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
cache_stats(char * dirp)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
resource_file_grow(char * dirp,int oldcnt,int newcnt)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
resource_file_dirty(char * dirp)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
issue_cod(char * name)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
simulate_disconnection(char * namep,int disconnect)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