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 }