1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * Common subroutines used by the programs in these subdirectories.
  31  */
  32 
  33 #include <locale.h>
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <string.h>
  37 #include <assert.h>
  38 #include <unistd.h>
  39 #include <limits.h>
  40 #include <errno.h>
  41 #include <wait.h>
  42 #include <ctype.h>
  43 #include <fcntl.h>
  44 #include <ftw.h>
  45 #include <dirent.h>
  46 #include <sys/types.h>
  47 #include <sys/time.h>
  48 #include <utmpx.h>
  49 #include <sys/uio.h>
  50 #include <sys/param.h>
  51 #include <sys/stat.h>
  52 #include <sys/fcntl.h>
  53 #include <sys/mount.h>
  54 #include <sys/mntent.h>
  55 #include <sys/mnttab.h>
  56 #include <sys/mman.h>
  57 #include <sys/fs/cachefs_fs.h>
  58 #include "subr.h"
  59 
  60 /*
  61  *
  62  *                      cachefs_dir_lock
  63  *
  64  * Description:
  65  *      Gets a lock on the cache directory.
  66  *      To release the lock, call cachefs_dir_unlock
  67  *      with the returned value.
  68  * Arguments:
  69  *      cachedirp       name of the cache directory
  70  *      shared          1 if shared, 0 if not
  71  * Returns:
  72  *      Returns -1 if the lock cannot be obtained immediatly.
  73  *      If the lock is obtained, returns a value >= 0.
  74  * Preconditions:
  75  *      precond(cachedirp)
  76  */
  77 
  78 int
  79 cachefs_dir_lock(const char *cachedirp, int shared)
  80 {
  81         int fd;
  82         int xx;
  83         int len;
  84         char buf[MAXPATHLEN];
  85         struct flock fl;
  86         char *strp;
  87         struct stat statb;
  88 
  89         /* make a path prefix to the cache directory lock file */
  90         strp = CACHEFS_ROOTRUN;
  91         xx = stat(strp, &statb);
  92         if ((xx < 0) || ((statb.st_mode & S_IFMT) != S_IFDIR))
  93                 strp = "/tmp";
  94 
  95         /* won't overflow */
  96         len = snprintf(buf, sizeof (buf), "%s/%s", strp, CACHEFS_LOCKDIR_PRE);
  97 
  98         if (strlcat(buf, cachedirp, sizeof (buf)) >= sizeof (buf)) {
  99                 pr_err(gettext("Cache directory name %s is too long"),
 100                         cachedirp);
 101                 return (-1);
 102         }
 103 
 104         strp = &buf[len];
 105 
 106         while (strp = strchr(strp, '/')) {      /* convert path to a file */
 107                 *strp = '_';
 108         }
 109 
 110         /*
 111          * Create and open the cache directory lock file.
 112          * This file will be <2G.
 113          */
 114         fd = open(buf, O_RDWR | O_CREAT, 0700);
 115         if (fd == -1) {
 116                 pr_err(gettext("Cannot open lock file %s"), buf);
 117                 return (-1);
 118         }
 119 
 120         /* try to set the lock */
 121         fl.l_type = (shared == 1) ? F_RDLCK : F_WRLCK;
 122         fl.l_whence = 0;
 123         fl.l_start = 1024;
 124         fl.l_len = 1024;
 125         fl.l_sysid = 0;
 126         fl.l_pid = 0;
 127         /* CACHEFS_LOCK_FILE will be <2GB */
 128         xx = fcntl(fd, F_SETLKW, &fl);
 129         if (xx == -1) {
 130                 if (errno == EAGAIN) {
 131                         pr_err(gettext("Cannot gain access to the "
 132                             "cache directory %s."), cachedirp);
 133                 } else {
 134                         pr_err(gettext("Unexpected failure on lock file %s %s"),
 135                             buf, strerror(errno));
 136                 }
 137                 close(fd);
 138                 return (-1);
 139         }
 140 
 141         /* return the file descriptor which can be used to release the lock */
 142         return (fd);
 143 }
 144 
 145 /*
 146  *
 147  *                      cachefs_dir_unlock
 148  *
 149  * Description:
 150  *      Releases an advisory lock on the cache directory.
 151  * Arguments:
 152  *      fd      cookie returned by cachefs_dir_lock
 153  * Returns:
 154  *      Returns -1 if the lock cannot be released or 0 for success.
 155  * Preconditions:
 156  */
 157 
 158 int
 159 cachefs_dir_unlock(int fd)
 160 {
 161         struct flock fl;
 162         int error = 0;
 163         int xx;
 164 
 165         /* release the lock */
 166         fl.l_type = F_UNLCK;
 167         fl.l_whence = 0;
 168         fl.l_start = 1024;
 169         fl.l_len = 1024;
 170         fl.l_sysid = 0;
 171         fl.l_pid = 0;
 172         /* fd will be <2GB */
 173         xx = fcntl(fd, F_SETLK, &fl);
 174         if (xx == -1) {
 175                 pr_err(gettext("Unexpected failure releasing lock file %s"),
 176                         strerror(errno));
 177                 error = -1;
 178         }
 179 
 180         /* close the lock file */
 181         close(fd);
 182 
 183         return (error);
 184 }
 185 
 186 /*
 187  *
 188  *                      cachefs_label_file_get
 189  *
 190  * Description:
 191  *      Gets the contents of a cache label file.
 192  *      Performs error checking on the file.
 193  * Arguments:
 194  *      filep   name of the cache label file
 195  *      clabelp where to put the file contents
 196  * Returns:
 197  *      Returns 0 for success or -1 if an error occurs.
 198  * Preconditions:
 199  *      precond(filep)
 200  *      precond(clabelp)
 201  */
 202 
 203 int
 204 cachefs_label_file_get(const char *filep, struct cache_label *clabelp)
 205 {
 206         int xx;
 207         int fd;
 208         struct stat64 statinfo;
 209 
 210         /* get info on the file */
 211         xx = lstat64(filep, &statinfo);
 212         if (xx == -1) {
 213                 if (errno != ENOENT) {
 214                         pr_err(gettext("Cannot stat file %s: %s"),
 215                             filep, strerror(errno));
 216                 } else {
 217                         pr_err(gettext("File %s does not exist."), filep);
 218                 }
 219 
 220                 return (-1);
 221         }
 222 
 223         /* if the file is the wrong type */
 224         if (!S_ISREG(statinfo.st_mode)) {
 225                 pr_err(gettext("Cache label file %s corrupted"), filep);
 226                 return (-1);
 227         }
 228 
 229         /* if the file is the wrong size; it will be <2GB */
 230         if (statinfo.st_size != (offset_t)sizeof (struct cache_label)) {
 231                 pr_err(gettext("Cache label file %s wrong size"), filep);
 232                 return (-1);
 233         }
 234 
 235         /* open the cache label file */
 236         fd = open(filep, O_RDONLY);
 237         if (fd == -1) {
 238                 pr_err(gettext("Error opening %s: %s"), filep,
 239                     strerror(errno));
 240                 return (-1);
 241         }
 242 
 243         /* read the current set of parameters */
 244         xx = read(fd, clabelp, sizeof (struct cache_label));
 245         if (xx != sizeof (struct cache_label)) {
 246                 pr_err(gettext("Reading %s failed: %s\n"), filep,
 247                     strerror(errno));
 248                 close(fd);
 249                 return (-1);
 250         }
 251         close(fd);
 252 
 253         /* return success */
 254         return (0);
 255 }
 256 
 257 /*
 258  *
 259  *                      cachefs_label_file_put
 260  *
 261  * Description:
 262  *      Outputs the contents of a cache label object to a file.
 263  * Arguments:
 264  *      filep   name of the cache label file
 265  *      clabelp where to get the file contents
 266  * Returns:
 267  *      Returns 0 for success or -1 if an error occurs.
 268  * Preconditions:
 269  *      precond(filep)
 270  *      precond(clabelp)
 271  */
 272 
 273 int
 274 cachefs_label_file_put(const char *filep, struct cache_label *clabelp)
 275 {
 276         int xx;
 277         int fd;
 278 
 279         /* get rid of the file if it already exists */
 280         xx = unlink(filep);
 281         if ((xx == -1) && (errno != ENOENT)) {
 282                 pr_err(gettext("Could not remove %s: %s"), filep,
 283                     strerror(errno));
 284                 return (-1);
 285         }
 286 
 287         /* open the cache label file; this file will be <2GB */
 288         fd = open(filep, O_CREAT | O_RDWR, 0600);
 289         if (fd == -1) {
 290                 pr_err(gettext("Error creating %s: %s"), filep,
 291                     strerror(errno));
 292                 return (-1);
 293         }
 294 
 295         /* write out the cache label object */
 296         xx = write(fd, clabelp, sizeof (struct cache_label));
 297         if (xx != sizeof (struct cache_label)) {
 298                 pr_err(gettext("Writing %s failed: %s"), filep,
 299                     strerror(errno));
 300                 close(fd);
 301                 return (-1);
 302         }
 303 
 304         /* make sure the contents get to disk */
 305         if (fsync(fd) != 0) {
 306                 pr_err(gettext("Writing %s failed on sync: %s"), filep,
 307                     strerror(errno));
 308                 close(fd);
 309                 return (-1);
 310         }
 311 
 312         close(fd);
 313 
 314         /* return success */
 315         return (0);
 316 }
 317 
 318 int
 319 cachefs_label_file_vcheck(char *filep, struct cache_label *clabelp)
 320 {
 321         /* check for an invalid version number */
 322         if (clabelp->cl_cfsversion != CFSVERSION) {
 323                 pr_err(gettext("Cache label file %s corrupted"), filep);
 324                 return (-1);
 325         }
 326 
 327         return (0);
 328 }
 329 
 330 /*
 331  *
 332  *                      cachefs_inuse
 333  *
 334  * Description:
 335  *      Tests whether or not the cache directory is in use by
 336  *      the cache file system.
 337  * Arguments:
 338  *      cachedirp       name of the file system cache directory
 339  * Returns:
 340  *      Returns 1 if the cache is in use or an error, 0 if not.
 341  * Preconditions:
 342  *      precond(cachedirp)
 343  */
 344 
 345 int
 346 cachefs_inuse(const char *cachedirp)
 347 {
 348         int fd;
 349         int xx;
 350         char buf[MAXPATHLEN];
 351         char *lockp = CACHEFS_LOCK_FILE;
 352         struct flock fl;
 353 
 354         /* see if path name is too long */
 355         xx = strlen(cachedirp) + strlen(lockp) + 3;
 356         if (xx >= MAXPATHLEN) {
 357                 pr_err(gettext("Cache directory name %s is too long"),
 358                     cachedirp);
 359                 return (1);
 360         }
 361 
 362         /* make a path to the cache directory lock file */
 363         snprintf(buf, sizeof (buf), "%s/%s", cachedirp, lockp);
 364 
 365         /* Open the kernel in use lock file.  This file will be <2GB. */
 366         fd = open(buf, O_RDWR, 0700);
 367         if (fd == -1) {
 368                 pr_err(gettext("Cannot open lock file %s"), buf);
 369                 return (1);
 370         }
 371 
 372         /* test the lock status */
 373         fl.l_type = F_WRLCK;
 374         fl.l_whence = 0;
 375         fl.l_start = 0;
 376         fl.l_len = 1024;
 377         fl.l_sysid = 0;
 378         fl.l_pid = 0;
 379         xx = fcntl(fd, F_GETLK, &fl);
 380         if (xx == -1) {
 381                 pr_err(gettext("Unexpected failure on lock file %s %s"),
 382                     buf, strerror(errno));
 383                 close(fd);
 384                 return (1);
 385         }
 386         close(fd);
 387 
 388         if (fl.l_type == F_UNLCK)
 389                 xx = 0;
 390         else
 391                 xx = 1;
 392 
 393         /* return whether or not the cache is in use */
 394         return (xx);
 395 }
 396 
 397 /*
 398  *
 399  *                      cachefs_resouce_size
 400  *
 401  * Description:
 402  *      Returns information about a resource file.
 403  * Arguments:
 404  *      maxinodes       number of inodes to be managed by the resource file
 405  *      rinfop          set to info about the resource file
 406  * Returns:
 407  * Preconditions:
 408  *      precond(rinfop)
 409  */
 410 
 411 void
 412 cachefs_resource_size(int maxinodes, struct cachefs_rinfo *rinfop)
 413 {
 414         int fsize;
 415 
 416         fsize = MAXBSIZE;
 417 
 418         rinfop->r_ptroffset = fsize;
 419 
 420         fsize += MAXBSIZE * (maxinodes / CACHEFS_RLPMBS);
 421         if ((maxinodes % CACHEFS_RLPMBS) != 0)
 422                 fsize += MAXBSIZE;
 423 
 424         rinfop->r_fsize = fsize;
 425 }
 426 
 427 /*
 428  *
 429  *                      cachefs_create_cache
 430  *
 431  * Description:
 432  *      Creates the specified cache directory and populates it as
 433  *      needed by CFS.
 434  * Arguments:
 435  *      dirp            the name of the cache directory
 436  *      uv              user values (may be NULL)
 437  *      clabel          label file contents, or placeholder for this
 438  * Returns:
 439  *      Returns 0 for success or:
 440  *              -1 for an error
 441  *              -2 for an error and cache directory partially created
 442  * Preconditions:
 443  *      precond(dirp)
 444  */
 445 
 446 int
 447 cachefs_create_cache(char *dirp, struct cachefs_user_values *uv,
 448     struct cache_label *clabel)
 449 {
 450         int xx;
 451         char path[CACHEFS_XMAXPATH];
 452         int fd;
 453         void *bufp;
 454         int cnt;
 455         struct cache_usage cu;
 456         FILE *fp;
 457         char *parent;
 458         struct statvfs64 svfs;
 459 
 460         cu.cu_blksused = 0;
 461         cu.cu_filesused = 0;
 462         cu.cu_flags = 0;
 463 
 464         /* make sure cache dir name is not too long */
 465         if (strlen(dirp) > (size_t)PATH_MAX) {
 466                 pr_err(gettext("path name %s is too long."), dirp);
 467                 return (-1);
 468         }
 469 
 470         /* ensure the path isn't in cachefs */
 471         parent = cachefs_file_to_dir(dirp);
 472         if (parent == NULL) {
 473                 pr_err(gettext("Out of memory"));
 474                 return (-1);
 475         }
 476         if (statvfs64(parent, &svfs) != 0) {
 477                 pr_err(gettext("%s: %s"), parent, strerror(errno));
 478                 free(parent);
 479                 return (-1);
 480         }
 481         if (strcmp(svfs.f_basetype, CACHEFS_BASETYPE) == 0) {
 482                 pr_err(gettext("Cannot create cache in cachefs filesystem"));
 483                 free(parent);
 484                 return (-1);
 485         }
 486         free(parent);
 487 
 488         /* make the directory */
 489         if (mkdir(dirp, 0) == -1) {
 490                 switch (errno) {
 491                 case EEXIST:
 492                         pr_err(gettext("%s already exists."), dirp);
 493                         break;
 494 
 495                 default:
 496                         pr_err(gettext("mkdir %s failed: %s"),
 497                             dirp, strerror(errno));
 498                 }
 499                 return (-1);
 500         }
 501         cu.cu_filesused += 1;
 502         cu.cu_blksused += 1;
 503 
 504         /* convert user values to a cache_label */
 505         if (uv != NULL) {
 506                 xx = cachefs_convert_uv2cl(uv, clabel, dirp);
 507                 if (xx)
 508                         return (-2);
 509         }
 510 
 511         /*
 512          * Create the cache directory lock file.
 513          * Used by the kernel module to indicate the cache is in use.
 514          * This file will be <2G.
 515          */
 516         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOCK_FILE);
 517         fd = open(path, O_RDWR | O_CREAT, 0700);
 518         if (fd == -1) {
 519                 pr_err(gettext("Cannot create lock file %s"), path);
 520                 return (-1);
 521         }
 522         close(fd);
 523 
 524         /* make the directory for the back file system mount points */
 525         /* note: we do not count this directory in the resources */
 526         snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
 527         if (mkdir(path, 0700) == -1) {
 528                 pr_err(gettext("mkdir %s failed: %s"), path,
 529                     strerror(errno));
 530                 return (-2);
 531         }
 532 
 533         /* make the directory for lost+found */
 534         /* note: we do not count this directory in the resources */
 535         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOSTFOUND_NAME);
 536         if (mkdir(path, 0700) == -1) {
 537                 pr_err(gettext("mkdir %s failed: %s"), path,
 538                     strerror(errno));
 539                 return (-2);
 540         }
 541 
 542         /* make the networker "don't back up" file; this file is <2GB */
 543         xx = 0;
 544         snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
 545         if ((fp = fopen(path, "w")) != NULL) {
 546                 if (realpath(dirp, path) != NULL) {
 547                         fprintf(fp, "<< ./ >>\n");
 548                         fprintf(fp, "+skip: .?* *\n");
 549                         if (fclose(fp) == 0)
 550                                 xx = 1;
 551                 }
 552         }
 553         if (xx == 0) {
 554                 snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
 555                 pr_err(gettext("can't create %s"), path);
 556                 (void) unlink(path);
 557         } else {
 558                 cu.cu_filesused += 1;
 559                 cu.cu_blksused += 1;
 560         }
 561 
 562         /* create the unmount file */
 563         xx = 0;
 564         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_UNMNT_FILE);
 565         if ((fp = fopen(path, "w")) != NULL) {
 566                 time32_t btime;
 567 
 568                 btime = get_boottime();
 569                 fwrite((void *)&btime, sizeof (btime), 1, fp);
 570                 if (fclose(fp) == 0)
 571                         xx = 1;
 572         }
 573         if (xx == 0)
 574                 pr_err(gettext("can't create .cfs_unmnt file"));
 575 
 576         /* create the cache label file */
 577         snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
 578         xx = cachefs_label_file_put(path, clabel);
 579         if (xx == -1) {
 580                 pr_err(gettext("creating %s failed."), path);
 581                 return (-2);
 582         }
 583         cu.cu_filesused += 1;
 584         cu.cu_blksused += 1;
 585 
 586         /* create the cache label duplicate file */
 587         snprintf(path, sizeof (path), "%s/%s.dup", dirp, CACHELABEL_NAME);
 588         xx = cachefs_label_file_put(path, clabel);
 589         if (xx == -1) {
 590                 pr_err(gettext("creating %s failed."), path);
 591                 return (-2);
 592         }
 593         cu.cu_filesused += 1;
 594         cu.cu_blksused += 1;
 595 
 596         /* create the resouce file; this file will be <2GB */
 597         snprintf(path, sizeof (path), "%s/%s", dirp, RESOURCE_NAME);
 598         fd = open(path, O_CREAT | O_RDWR, 0600);
 599         if (fd == -1) {
 600                 pr_err(gettext("create %s failed: %s"), path,
 601                     strerror(errno));
 602                 return (-2);
 603         }
 604         cu.cu_filesused += 1;
 605 
 606         /* allocate a zeroed buffer for filling the resouce file */
 607         bufp = calloc(1, MAXBSIZE);
 608         if (bufp == NULL) {
 609                 pr_err(gettext("out of space %d."), MAXBSIZE);
 610                 close(fd);
 611                 return (-2);
 612         }
 613 
 614         /* determine number of MAXBSIZE chunks to make the file */
 615         cnt = 1;        /* for the header */
 616         cnt += clabel->cl_maxinodes / CACHEFS_RLPMBS;
 617         if ((clabel->cl_maxinodes % CACHEFS_RLPMBS) != 0)
 618                 ++cnt;
 619 
 620         /* fill up the file with zeros */
 621         for (xx = 0; xx < cnt; xx++) {
 622                 if (write(fd, bufp, MAXBSIZE) != MAXBSIZE) {
 623                         pr_err(gettext("write %s failed: %s"), path,
 624                             strerror(errno));
 625                         close(fd);
 626                         free(bufp);
 627                         return (-2);
 628                 }
 629         }
 630         free(bufp);
 631         cu.cu_blksused += cnt;
 632 
 633         /* position to the begining of the file */
 634         if (lseek(fd, 0, SEEK_SET) == -1) {
 635                 pr_err(gettext("lseek %s failed: %s"), path,
 636                     strerror(errno));
 637                 close(fd);
 638                 return (-2);
 639         }
 640 
 641         /* write the cache usage structure */
 642         xx = sizeof (struct cache_usage);
 643         if (write(fd, &cu, xx) != xx) {
 644                 pr_err(gettext("cu write %s failed: %s"), path,
 645                     strerror(errno));
 646                 close(fd);
 647                 return (-2);
 648         }
 649 
 650         /* make sure the contents get to disk */
 651         if (fsync(fd) != 0) {
 652                 pr_err(gettext("fsync %s failed: %s"), path,
 653                     strerror(errno));
 654                 close(fd);
 655                 return (-2);
 656         }
 657         close(fd);
 658 
 659         /* return success */
 660         return (0);
 661 }
 662 
 663 /*
 664  *
 665  *                      cachefs_delete_all_cache
 666  *
 667  * Description:
 668  *      Delete all caches in cache directory.
 669  * Arguments:
 670  *      dirp    the pathname of of the cache directory to delete
 671  * Returns:
 672  *      Returns 0 for success or -1 for an error.
 673  * Preconditions:
 674  *      precond(dirp)
 675  */
 676 
 677 int
 678 cachefs_delete_all_cache(char *dirp)
 679 {
 680         DIR *dp;
 681         struct dirent64 *dep;
 682         int xx;
 683         char path[CACHEFS_XMAXPATH];
 684         struct stat64 statinfo;
 685 
 686         /* make sure cache dir name is not too long */
 687         if (strlen(dirp) > (size_t)PATH_MAX) {
 688                 pr_err(gettext("path name %s is too long."),
 689                     dirp);
 690                 return (-1);
 691         }
 692 
 693         /* check that dirp is probably a cachefs directory */
 694         snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
 695         xx = access(path, R_OK | W_OK | X_OK);
 696 
 697         snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
 698         xx |= access(path, R_OK | W_OK);
 699 
 700         if (xx) {
 701                 pr_err(gettext("%s does not appear to be a "
 702                     "cachefs cache directory."), dirp);
 703                 return (-1);
 704         }
 705 
 706         /* remove the lost+found directory if it exists and is empty */
 707         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOSTFOUND_NAME);
 708         xx = rmdir(path);
 709         if (xx == -1) {
 710                 if (errno == EEXIST) {
 711                         pr_err(gettext("Cannot delete cache '%s'.  "
 712                             "First move files in '%s' to a safe location."),
 713                             dirp, path);
 714                         return (-1);
 715                 } else if (errno != ENOENT) {
 716                         pr_err(gettext("rmdir %s failed: %s"), path,
 717                             strerror(errno));
 718                         return (-1);
 719                 }
 720         }
 721 
 722         /* delete the back FS mount point directory if it exists */
 723         snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
 724         xx = lstat64(path, &statinfo);
 725         if (xx == -1) {
 726                 if (errno != ENOENT) {
 727                         pr_err(gettext("lstat %s failed: %s"), path,
 728                             strerror(errno));
 729                         return (-1);
 730                 }
 731         } else {
 732                 xx = nftw64(path, cachefs_delete_file, 16,
 733                     FTW_PHYS | FTW_DEPTH | FTW_MOUNT);
 734                 if (xx == -1) {
 735                         pr_err(gettext("unable to delete %s"), path);
 736                         return (-1);
 737                 }
 738         }
 739 
 740         /* open the cache directory specified */
 741         if ((dp = opendir(dirp)) == NULL) {
 742                 pr_err(gettext("cannot open cache directory %s: %s"),
 743                     dirp, strerror(errno));
 744                 return (-1);
 745         }
 746 
 747         /* read the file names in the cache directory */
 748         while ((dep = readdir64(dp)) != NULL) {
 749                 /* ignore . and .. */
 750                 if (strcmp(dep->d_name, ".") == 0 ||
 751                                 strcmp(dep->d_name, "..") == 0)
 752                         continue;
 753 
 754                 /* stat the file */
 755                 snprintf(path, sizeof (path), "%s/%s", dirp, dep->d_name);
 756                 xx = lstat64(path, &statinfo);
 757                 if (xx == -1) {
 758                         if (errno == ENOENT) {
 759                                 /* delete_cache may have nuked a directory */
 760                                 continue;
 761                         }
 762 
 763                         pr_err(gettext("lstat %s failed: %s"),
 764                             path, strerror(errno));
 765                         closedir(dp);
 766                         return (-1);
 767                 }
 768 
 769                 /* ignore anything that is not a link */
 770                 if (!S_ISLNK(statinfo.st_mode))
 771                         continue;
 772 
 773                 /* delete the file system cache directory */
 774                 xx = cachefs_delete_cache(dirp, dep->d_name);
 775                 if (xx) {
 776                         closedir(dp);
 777                         return (-1);
 778                 }
 779         }
 780         closedir(dp);
 781 
 782         /* remove the cache dir unmount file */
 783         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_UNMNT_FILE);
 784         xx = unlink(path);
 785         if ((xx == -1) && (errno != ENOENT)) {
 786                 pr_err(gettext("unlink %s failed: %s"), path,
 787                     strerror(errno));
 788                 return (-1);
 789         }
 790 
 791         /* remove the cache label file */
 792         snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
 793         xx = unlink(path);
 794         if ((xx == -1) && (errno != ENOENT)) {
 795                 pr_err(gettext("unlink %s failed: %s"), path,
 796                     strerror(errno));
 797                 return (-1);
 798         }
 799 
 800         /* remove the cache label duplicate file */
 801         snprintf(path, sizeof (path), "%s/%s.dup", dirp, CACHELABEL_NAME);
 802         xx = unlink(path);
 803         if ((xx == -1) && (errno != ENOENT)) {
 804                 pr_err(gettext("unlink %s failed: %s"), path,
 805                     strerror(errno));
 806                 return (-1);
 807         }
 808 
 809         /* remove the resource file */
 810         snprintf(path, sizeof (path), "%s/%s", dirp, RESOURCE_NAME);
 811         xx = unlink(path);
 812         if ((xx == -1) && (errno != ENOENT)) {
 813                 pr_err(gettext("unlink %s failed: %s"), path,
 814                     strerror(errno));
 815                 return (-1);
 816         }
 817 
 818         /* remove the cachefslog file if it exists */
 819         snprintf(path, sizeof (path), "%s/%s", dirp, LOG_STATUS_NAME);
 820         (void) unlink(path);
 821 
 822         /* remove the networker "don't back up" file if it exists */
 823         snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
 824         (void) unlink(path);
 825 
 826         /* remove the lock file */
 827         snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOCK_FILE);
 828         xx = unlink(path);
 829         if ((xx == -1) && (errno != ENOENT)) {
 830                 pr_err(gettext("unlink %s failed: %s"), path,
 831                     strerror(errno));
 832                 return (-1);
 833         }
 834 
 835         /* remove the directory */
 836         xx = rmdir(dirp);
 837         if (xx == -1) {
 838                 pr_err(gettext("rmdir %s failed: %s"), dirp,
 839                     strerror(errno));
 840                 return (-1);
 841         }
 842 
 843         /* return success */
 844         return (0);
 845 }
 846 
 847 /*
 848  *
 849  *                      cachefs_delete_cache
 850  *
 851  * Description:
 852  *      Deletes the specified file system cache.
 853  * Arguments:
 854  *      dirp    cache directory name
 855  *      namep   file system cache directory to delete
 856  * Returns:
 857  *      Returns 0 for success, -1 for failure.
 858  * Preconditions:
 859  *      precond(dirp)
 860  *      precond(namep)
 861  */
 862 
 863 int
 864 cachefs_delete_cache(char *dirp, char *namep)
 865 {
 866         char path[CACHEFS_XMAXPATH];
 867         char buf[CACHEFS_XMAXPATH];
 868         int xx;
 869         struct stat64 statinfo;
 870 
 871         /* make sure cache dir name is not too long */
 872         if (strlen(dirp) > (size_t)PATH_MAX) {
 873                 pr_err(gettext("path name %s is too long."),
 874                     dirp);
 875                 return (-1);
 876         }
 877 
 878         /* construct the path name of the file system cache directory */
 879         snprintf(path, sizeof (path), "%s/%s", dirp, namep);
 880 
 881         /* stat the specified file */
 882         xx = lstat64(path, &statinfo);
 883         if (xx == -1) {
 884                 pr_err(gettext("lstat %s failed: %s"), path,
 885                     strerror(errno));
 886                 return (-1);
 887         }
 888 
 889         /* make sure name is a symbolic link */
 890         if (!S_ISLNK(statinfo.st_mode)) {
 891                 pr_err(gettext("\"%s\" is not a valid cache id."), namep);
 892                 return (-1);
 893         }
 894 
 895         /* read the contents of the symbolic link */
 896         xx = readlink(path, buf, sizeof (buf));
 897         if (xx == -1) {
 898                 pr_err(gettext("Readlink of %s failed: %s"), path,
 899                     strerror(errno));
 900                 return (-1);
 901         }
 902         buf[xx] = '\0';
 903 
 904         /* remove the directory */
 905         snprintf(path, sizeof (path), "%s/%s", dirp, buf);
 906         xx = nftw64(path, cachefs_delete_file, 16,
 907             FTW_PHYS | FTW_DEPTH | FTW_MOUNT);
 908         if (xx == -1) {
 909                 pr_err(gettext("directory walk of %s failed."), dirp);
 910                 return (-1);
 911         }
 912 
 913         /* delete the link */
 914         snprintf(path, sizeof (path), "%s/%s", dirp, namep);
 915         if (unlink(path) == -1) {
 916                 pr_err(gettext("unlink %s failed: %s"), path,
 917                     strerror(errno));
 918                 return (-1);
 919         }
 920 
 921         /* return success */
 922         return (0);
 923 }
 924 
 925 /*
 926  *
 927  *                      cachefs_delete_file
 928  *
 929  * Description:
 930  *      Remove a file or directory; called by nftw64().
 931  * Arguments:
 932  *      namep   pathname of the file
 933  *      statp   stat info about the file
 934  *      flg     info about file
 935  *      ftwp    depth information
 936  * Returns:
 937  *      Returns 0 for success, -1 for failure.
 938  * Preconditions:
 939  *      precond(namep)
 940  */
 941 
 942 int
 943 cachefs_delete_file(const char *namep, const struct stat64 *statp, int flg,
 944     struct FTW *ftwp)
 945 {
 946         /* ignore . and .. */
 947         if (strcmp(namep, ".") == 0 || strcmp(namep, "..") == 0)
 948                 return (0);
 949 
 950         switch (flg) {
 951         case FTW_F:     /* files */
 952         case FTW_SL:
 953                 if (unlink(namep) == -1) {
 954                         pr_err(gettext("unlink %s failed: %s"),
 955                             namep, strerror(errno));
 956                         return (-1);
 957                 }
 958                 break;
 959 
 960         case FTW_DP:    /* directories that have their children processed */
 961                 if (rmdir(namep) == -1) {
 962                         pr_err(gettext("rmdir %s failed: %s"),
 963                             namep, strerror(errno));
 964                         return (-1);
 965                 }
 966                 break;
 967 
 968         case FTW_D:     /* ignore directories if children not processed */
 969                 break;
 970 
 971         default:
 972                 pr_err(gettext("failure on file %s, flg %d."),
 973                     namep, flg);
 974                 return (-1);
 975         }
 976 
 977         /* return success */
 978         return (0);
 979 }
 980 
 981 /*
 982  *
 983  *                      cachefs_convert_uv2cl
 984  *
 985  * Description:
 986  *      Copies the contents of a cachefs_user_values object into a
 987  *      cache_label object, performing the necessary conversions.
 988  * Arguments:
 989  *      uvp     cachefs_user_values to copy from
 990  *      clp     cache_label to copy into
 991  *      dirp    cache directory
 992  * Returns:
 993  *      Returns 0 for success, -1 for an error.
 994  * Preconditions:
 995  *      precond(uvp)
 996  *      precond(clp)
 997  *      precond(dirp)
 998  */
 999 
1000 int
1001 cachefs_convert_uv2cl(const struct cachefs_user_values *uvp,
1002     struct cache_label *clp, const char *dirp)
1003 {
1004         struct statvfs64 fs;
1005         int xx;
1006         double ftmp;
1007         double temp;
1008 
1009         /* get file system information */
1010         xx = statvfs64(dirp, &fs);
1011         if (xx == -1) {
1012                 pr_err(gettext("statvfs %s failed: %s"), dirp,
1013                     strerror(errno));
1014                 return (-1);
1015         }
1016 
1017         ftmp = (double)fs.f_frsize / (double)MAXBSIZE;
1018 
1019         /* front fs is less than 1 terabyte */
1020         temp = (double)uvp->uv_maxblocks / 100.0 *
1021             (double)fs.f_blocks * ftmp + .5;
1022         clp->cl_maxblks = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1023 
1024         temp = (double)uvp->uv_minblocks / 100.0 *
1025             (double)fs.f_blocks * ftmp + .5;
1026         clp->cl_blockmin = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1027 
1028         temp = (double)uvp->uv_threshblocks / 100.0 *
1029             (double)fs.f_blocks * ftmp + .5;
1030         clp->cl_blocktresh = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1031 
1032         temp = (double)uvp->uv_maxfiles / 100.0 * (double)fs.f_files + .5;
1033         clp->cl_maxinodes = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1034 
1035         temp = (double)uvp->uv_minfiles / 100.0 * (double)fs.f_files + .5;
1036         clp->cl_filemin = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1037 
1038         temp = (double)uvp->uv_threshfiles / 100.0 * (double)fs.f_files +.5;
1039         clp->cl_filetresh = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1040 
1041         ftmp = (double)(1024 * 1024) / (double)MAXBSIZE;
1042         clp->cl_maxfiles = uvp->uv_maxfilesize * ftmp + .5;
1043 
1044         clp->cl_blkhiwat = uvp->uv_hiblocks / 100.0 * clp->cl_maxblks + .5;
1045         clp->cl_blklowat = uvp->uv_lowblocks / 100.0 * clp->cl_maxblks + .5;
1046 
1047         clp->cl_filehiwat = uvp->uv_hifiles / 100.0 * clp->cl_maxinodes + .5;
1048         clp->cl_filelowat = uvp->uv_lowfiles / 100.0 * clp->cl_maxinodes + .5;
1049 
1050         clp->cl_cfsversion = CFSVERSION;
1051 
1052         /* return success */
1053         return (0);
1054 }
1055 
1056 /*
1057  *
1058  *                      cachefs_convert_cl2uv
1059  *
1060  * Description:
1061  *      Copies the contents of a cache_label object into a
1062  *      cachefs_user_values object, performing the necessary conversions.
1063  * Arguments:
1064  *      clp     cache_label to copy from
1065  *      uvp     cachefs_user_values to copy into
1066  *      dirp    cache directory
1067  * Returns:
1068  *      Returns 0 for success, -1 for an error.
1069  * Preconditions:
1070  *      precond(clp)
1071  *      precond(uvp)
1072  *      precond(dirp)
1073  */
1074 
1075 int
1076 cachefs_convert_cl2uv(const struct cache_label *clp,
1077     struct cachefs_user_values *uvp, const char *dirp)
1078 {
1079         struct statvfs64 fs;
1080         int xx;
1081         double temp;
1082         double ftmp;
1083         long long ltmp;
1084 
1085         /* get file system information */
1086         xx = statvfs64(dirp, &fs);
1087         if (xx == -1) {
1088                 pr_err(gettext("statvfs %s failed: %s"), dirp,
1089                     strerror(errno));
1090                 return (-1);
1091         }
1092 
1093 #define BOUND(yy) \
1094         yy = (yy < 0) ? 0 : yy; \
1095         yy = (yy > 100) ? 100 : yy;
1096 
1097         ftmp = (double)MAXBSIZE / (double)fs.f_frsize;
1098 
1099         temp = (double)clp->cl_maxblks * ftmp /
1100             (double)fs.f_blocks * 100. + .5;
1101         BOUND(temp);
1102         uvp->uv_maxblocks = (int)temp;
1103 
1104         temp = (double)clp->cl_blockmin * ftmp /
1105             (double)fs.f_blocks * 100. + .5;
1106         BOUND(temp);
1107         uvp->uv_minblocks = (int)temp;
1108 
1109         temp = (double)clp->cl_blocktresh * ftmp /
1110             (double)fs.f_blocks * 100. + .5;
1111         BOUND(temp);
1112         uvp->uv_threshblocks = (int)temp;
1113 
1114         temp = ((double)clp->cl_maxinodes / fs.f_files) * 100. + .5;
1115         BOUND(temp);
1116         uvp->uv_maxfiles = (int)temp;
1117 
1118         temp = ((double)clp->cl_filemin / fs.f_files) * 100. + .5;
1119         BOUND(temp);
1120         uvp->uv_minfiles = (int)temp;
1121 
1122         temp = ((double)clp->cl_filetresh / fs.f_files) * 100. + .5;
1123         BOUND(temp);
1124         uvp->uv_threshfiles = (int)temp;
1125 
1126         ltmp = ((long long)clp->cl_maxfiles * MAXBSIZE);
1127         uvp->uv_maxfilesize = (ltmp + (MAXBSIZE / 2)) / (1024 * 1024);
1128 
1129         xx = ((double)clp->cl_blkhiwat / clp->cl_maxblks) * 100. + .5;
1130         BOUND(xx);
1131         uvp->uv_hiblocks = xx;
1132 
1133         xx = ((double)clp->cl_blklowat / clp->cl_maxblks) * 100. + .5;
1134         BOUND(xx);
1135         uvp->uv_lowblocks = xx;
1136 
1137         xx = ((double)clp->cl_filehiwat / clp->cl_maxinodes) * 100. + .5;
1138         BOUND(xx);
1139         uvp->uv_hifiles = xx;
1140 
1141         xx = ((double)clp->cl_filelowat / clp->cl_maxinodes) * 100. + .5;
1142         BOUND(xx);
1143         uvp->uv_lowfiles = xx;
1144 
1145         /* return success */
1146         return (0);
1147 }
1148 
1149 /*
1150  * cachefs_file_to_dir
1151  *
1152  * takes in a path, and returns the parent directory of that path.
1153  *
1154  * it's the caller's responsibility to free the pointer returned by
1155  * this function.
1156  */
1157 
1158 char *
1159 cachefs_file_to_dir(const char *path)
1160 {
1161         char *rc, *cp;
1162 
1163         if (path == NULL)
1164                 return (NULL);
1165 
1166         rc = strdup(path);
1167         if (rc == NULL)
1168                 return (NULL);
1169 
1170         if ((cp = strrchr(rc, '/')) == NULL) {
1171 
1172                 /*
1173                  * if no slashes at all, return "." (current directory).
1174                  */
1175 
1176                 (void) free(rc);
1177                 rc = strdup(".");
1178 
1179         } else if (cp == rc) {
1180 
1181                 /*
1182                  * else, if the last '/' is the first character, chop
1183                  * off from there (i.e. return "/").
1184                  */
1185 
1186                 rc[1] = '\0';
1187 
1188         } else {
1189 
1190                 /*
1191                  * else, we have a path like /foo/bar or foo/bar.
1192                  * chop off from the last '/'.
1193                  */
1194 
1195                 *cp = '\0';
1196 
1197         }
1198 
1199         return (rc);
1200 }
1201 
1202 /*
1203  *                      cachefs_clean_flag_test
1204  *
1205  * Description:
1206  *      Tests whether or not the clean flag on the file system
1207  *      is set.
1208  * Arguments:
1209  *      cachedirp       name of the the file system cache directory
1210  * Returns:
1211  *      Returns 1 if the cache was shut down cleanly, 0 if not.
1212  * Preconditions:
1213  *      precond(cachedirp)
1214  */
1215 
1216 int
1217 cachefs_clean_flag_test(const char *cachedirp)
1218 {
1219         char *namep;
1220         int xx;
1221         char buf[MAXPATHLEN];
1222         int fd;
1223         struct cache_usage cu;
1224 
1225         /* construct the path name of the resource file */
1226         namep = RESOURCE_NAME;
1227         xx = strlen(cachedirp) + strlen(namep) + 3;
1228         if (xx >= MAXPATHLEN) {
1229                 pr_err(gettext("Path name too long %s/%s"),
1230                     cachedirp, namep);
1231                 return (39);
1232         }
1233         snprintf(buf, sizeof (buf), "%s/%s", cachedirp, namep);
1234 
1235         /* open the file; it will be <2GB */
1236         fd = open(buf, O_RDONLY);
1237         if (fd == -1) {
1238                 pr_err(gettext("Cannot open %s: %s"), buf, strerror(errno));
1239                 return (0);
1240         }
1241 
1242         /* read the cache_usage structure */
1243         xx = read(fd, &cu, sizeof (cu));
1244         if (xx != sizeof (cu)) {
1245                 pr_err(gettext("Error reading %s: %d %s"), buf,
1246                     xx, strerror(errno));
1247                 close(fd);
1248                 return (0);
1249         }
1250         close(fd);
1251 
1252         /* return state of the cache */
1253         return ((cu.cu_flags & CUSAGE_ACTIVE) == 0);
1254 }
1255 
1256 time32_t
1257 get_boottime()
1258 {
1259         struct utmpx id, *putmp;
1260 
1261         id.ut_type = BOOT_TIME;
1262         setutxent();
1263         if ((putmp = getutxid(&id)) != NULL)
1264                 return ((time32_t)putmp->ut_tv.tv_sec);
1265         return (-1);
1266 }