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 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  28 /*          All Rights Reserved */
  29 
  30 /*
  31  * Portions of this source code were derived from Berkeley 4.3 BSD
  32  * under license from the Regents of the University of California.
  33  */
  34 
  35 #pragma ident   "%Z%%M% %I%     %E% SMI"
  36 
  37 /*
  38  *
  39  *                      mount.c
  40  *
  41  * Cachefs mount program.
  42  */
  43 
  44 #include <locale.h>
  45 #include <stdio.h>
  46 #include <stdlib.h>
  47 #include <string.h>
  48 #include <strings.h>
  49 #include <stdarg.h>
  50 #include <unistd.h>
  51 #include <limits.h>
  52 #include <errno.h>
  53 #include <wait.h>
  54 #include <ctype.h>
  55 #include <fcntl.h>
  56 #include <fslib.h>
  57 #include <sys/types.h>
  58 #include <sys/time.h>
  59 #include <sys/param.h>
  60 #include <sys/stat.h>
  61 #include <sys/fcntl.h>
  62 #include <sys/mount.h>
  63 #include <sys/mntent.h>
  64 #include <sys/mnttab.h>
  65 #include <sys/mntio.h>
  66 #include <sys/fs/cachefs_fs.h>
  67 #include <sys/utsname.h>
  68 #include <rpc/rpc.h>
  69 #include <kstat.h>
  70 #undef MAX
  71 #include <nfs/nfs.h>
  72 #include <nfs/nfs_clnt.h>
  73 #include <sys/mkdev.h>
  74 #include "../common/subr.h"
  75 #include "../common/cachefsd.h"
  76 
  77 char *cfs_opts[] = {
  78 #define CFSOPT_BACKFSTYPE       0
  79         "backfstype",
  80 #define CFSOPT_CACHEDIR         1
  81         "cachedir",
  82 #define CFSOPT_CACHEID          2
  83         "cacheid",
  84 #define CFSOPT_BACKPATH         3
  85         "backpath",
  86 
  87 #define CFSOPT_WRITEAROUND      4
  88         "write-around",
  89 #define CFSOPT_NONSHARED        5
  90         "non-shared",
  91 
  92 #define CFSOPT_DISCONNECTABLE   6
  93         "disconnectable",
  94 #define CFSOPT_SOFT             7
  95         "soft",
  96 
  97 #define CFSOPT_NOCONST          8
  98         "noconst",
  99 #define CFSOPT_CODCONST         9
 100         "demandconst",
 101 
 102 #define CFSOPT_LOCALACCESS      10
 103         "local-access",
 104 #define CFSOPT_LAZYMOUNT        11
 105         "lazy-mount",
 106 
 107 #define CFSOPT_RW               12
 108         "rw",
 109 #define CFSOPT_RO               13
 110         "ro",
 111 #define CFSOPT_SUID             14
 112         "suid",
 113 #define CFSOPT_NOSUID           15
 114         "nosuid",
 115 #define CFSOPT_REMOUNT          16
 116         "remount",
 117 #define CFSOPT_FGSIZE           17
 118         "fgsize",
 119 #define CFSOPT_POPSIZE          18
 120         "popsize",
 121 #define CFSOPT_ACREGMIN         19
 122         "acregmin",
 123 #define CFSOPT_ACREGMAX         20
 124         "acregmax",
 125 #define CFSOPT_ACDIRMIN         21
 126         "acdirmin",
 127 #define CFSOPT_ACDIRMAX         22
 128         "acdirmax",
 129 #define CFSOPT_ACTIMEO          23
 130         "actimeo",
 131 #define CFSOPT_SLIDE            24
 132         "slide",
 133 #define CFSOPT_NOSETSEC         25
 134         "nosec",        /* XXX should we use MNTOPT_NOTSETSEC? */
 135 #define CFSOPT_LLOCK            26
 136         "llock",
 137 #define CFSOPT_NONOTIFY         27
 138         "nonotify",
 139 #define CFSOPT_SNR              28
 140         "snr",
 141 #define CFSOPT_NOFILL           29
 142         "nofill",
 143 #ifdef CFS_NFSV3_PASSTHROUGH
 144 #define CFSOPT_NFSV3PASSTHROUGH 30
 145         "nfsv3pass",
 146 #endif /* CFS_NFSV3_PASSTHROUGH */
 147         NULL
 148 };
 149 
 150 #define MNTTYPE_CFS     "cachefs"       /* XXX - to be added to mntent.h */
 151                                         /* XXX - and should be cachefs */
 152 #define CFS_DEF_DIR     "/cache"        /* XXX - should be added to cfs.h */
 153 
 154 #define bad(val) (val == NULL || !isdigit(*val))
 155 
 156 #define VFS_PATH        "/usr/lib/fs"
 157 #define ALT_PATH        "/etc/fs"
 158 
 159 /* forward references */
 160 void usage(char *msgp);
 161 void pr_err(char *fmt, ...);
 162 int set_cfs_args(char *optionp, struct cachefs_mountargs *margsp, int *mflagp,
 163     char **backfstypepp, char **reducepp, int *notifyp, int *nfsv3pass);
 164 int get_mount_point(char *cachedirp, char *specp, char **pathpp);
 165 int dobackmnt(struct cachefs_mountargs *margsp, char *reducep, char *specp,
 166     char *backfstypep, char *mynamep, int readonly);
 167 void doexec(char *fstype, char **newargv, char *myname);
 168 char *get_back_fsid(char *specp);
 169 char *get_cacheid(char *, char *);
 170 void record_mount(char *mntp, char *specp, char *backfsp, char *backfstypep,
 171     char *cachedirp, char *cacheidp, char *optionp, char *reducep);
 172 int daemon_notify(char *cachedirp, char *cacheidp);
 173 int pingserver(char *backmntp);
 174 int check_cache(char *cachedirp);
 175 uint32_t cachefs_get_back_nfsvers(char *cfs_backfs, int nomnttab);
 176 int cfs_nfsv4_build_opts(char *optionp, char *cfs_nfsv4ops);
 177 
 178 int nomnttab;
 179 int quiet;
 180 /*
 181  *
 182  *                      main
 183  *
 184  * Description:
 185  *      Main routine for the cachefs mount program.
 186  * Arguments:
 187  *      argc    number of command line arguments
 188  *      argv    list of command line arguments
 189  * Returns:
 190  *      Returns 0 for success, 1 an error was encountered.
 191  * Preconditions:
 192  */
 193 
 194 int
 195 main(int argc, char **argv)
 196 {
 197         char *myname;
 198         char *optionp;
 199         char *opigp;
 200         int mflag;
 201         int readonly;
 202         struct cachefs_mountargs margs;
 203         char *backfstypep;
 204         char *reducep;
 205         char *specp;
 206         int xx;
 207         int stat_loc;
 208         char *newargv[20];
 209         char *mntp;
 210         pid_t pid;
 211         int mounted;
 212         int c;
 213         int lockid;
 214         int Oflg;
 215         char *strp;
 216         char servname[33];
 217         int notify = 1;
 218         struct stat64 statb;
 219         struct mnttagdesc mtdesc;
 220         char mops[MAX_MNTOPT_STR];
 221         char cfs_nfsv4ops[MAX_MNTOPT_STR];
 222         uint32_t nfsvers = 0;
 223         uint32_t nfsvers_error = FALSE;
 224         int nfsv3pass = 0;
 225         (void) setlocale(LC_ALL, "");
 226 #if !defined(TEXT_DOMAIN)
 227 #define TEXT_DOMAIN     "SYS_TEST"
 228 #endif
 229         (void) textdomain(TEXT_DOMAIN);
 230 
 231         if (argv[0]) {
 232                 myname = strrchr(argv[0], '/');
 233                 if (myname)
 234                         myname++;
 235                 else
 236                         myname = argv[0];
 237         } else {
 238                 myname = "path unknown";
 239         }
 240 
 241         optionp = NULL;
 242         nomnttab = 0;
 243         quiet = 0;
 244         readonly = 0;
 245         Oflg = 0;
 246         cfs_nfsv4ops[0] = '\0';
 247 
 248         /* process command line options */
 249         while ((c = getopt(argc, argv, "mo:Orq")) != EOF) {
 250                 switch (c) {
 251                 case 'm':       /* no entry in /etc/mnttab */
 252                         nomnttab = 1;
 253                         break;
 254 
 255                 case 'o':
 256                         optionp = optarg;
 257                         break;
 258 
 259                 case 'O':
 260                         Oflg++;
 261                         break;
 262 
 263                 case 'r':       /* read only mount */
 264                         readonly = 1;
 265                         break;
 266 
 267                 case 'q':
 268                         quiet = 1;
 269                         break;
 270 
 271                 default:
 272                         usage("invalid option");
 273                         return (1);
 274                 }
 275         }
 276 
 277         /* if -o not specified */
 278         if (optionp == NULL) {
 279                 usage(gettext("\"-o backfstype\" must be specified"));
 280                 return (1);
 281         }
 282 
 283         /* verify special device and mount point are specified */
 284         if (argc - optind < 2) {
 285                 usage(gettext("must specify special device and mount point"));
 286                 return (1);
 287         }
 288 
 289         /* Store mount point and special device. */
 290         specp = argv[argc - 2];
 291         mntp = argv[argc - 1];
 292 
 293         /* Initialize default mount values */
 294         margs.cfs_options.opt_flags = CFS_ACCESS_BACKFS;
 295         margs.cfs_options.opt_popsize = DEF_POP_SIZE;
 296         margs.cfs_options.opt_fgsize = DEF_FILEGRP_SIZE;
 297         margs.cfs_fsid = NULL;
 298         memset(margs.cfs_cacheid, 0, sizeof (margs.cfs_cacheid));
 299         margs.cfs_cachedir = CFS_DEF_DIR;
 300         margs.cfs_backfs = NULL;
 301         margs.cfs_acregmin = 0;
 302         margs.cfs_acregmax = 0;
 303         margs.cfs_acdirmin = 0;
 304         margs.cfs_acdirmax = 0;
 305         mflag = MS_OPTIONSTR;
 306         if (nomnttab)
 307                 mflag |= MS_NOMNTTAB;
 308         backfstypep = NULL;
 309 
 310         /* process -o options */
 311         xx = set_cfs_args(optionp, &margs, &mflag, &backfstypep, &reducep,
 312             &notify, &nfsv3pass);
 313         if (xx) {
 314                 return (1);
 315         }
 316         strcpy(mops, optionp);
 317 
 318         /* backfstype has to be specified */
 319         if (backfstypep == NULL) {
 320                 usage(gettext("\"-o backfstype\" must be specified"));
 321                 return (1);
 322         }
 323 
 324         if ((strcmp(backfstypep, "nfs") != 0) &&
 325                                 (strcmp(backfstypep, "hsfs") != 0)) {
 326                 pr_err(gettext("%s as backfstype is not supported."),
 327                                         backfstypep);
 328                 return (1);
 329         }
 330 
 331         /* set default write mode if not specified */
 332         if ((margs.cfs_options.opt_flags &
 333             (CFS_WRITE_AROUND|CFS_NONSHARED)) == 0) {
 334                 margs.cfs_options.opt_flags |= CFS_WRITE_AROUND;
 335                 if (strcmp(backfstypep, "hsfs") == 0)
 336                         mflag |= MS_RDONLY;
 337         }
 338 
 339         /* if read-only was specified with the -r option */
 340         if (readonly) {
 341                 mflag |= MS_RDONLY;
 342         }
 343 
 344         /* if overlay was specified with -O option */
 345         if (Oflg) {
 346                 mflag |= MS_OVERLAY;
 347         }
 348 
 349         /* get the fsid of the backfs and the cacheid */
 350         margs.cfs_fsid = get_back_fsid(specp);
 351         if (margs.cfs_fsid == NULL) {
 352                 pr_err(gettext("out of memory"));
 353                 return (1);
 354         }
 355 
 356         /*
 357          * If using this cachedir to mount a file system for the first time
 358          * after reboot, the ncheck for the sanity of the cachedir
 359          */
 360         if (first_time_ab(margs.cfs_cachedir))
 361                 if (check_cache(margs.cfs_cachedir))
 362                         return (1);
 363 
 364         /* get the front file system cache id if necessary */
 365         if (margs.cfs_cacheid[0] == '\0') {
 366                 char *cacheid = get_cacheid(margs.cfs_fsid, mntp);
 367 
 368                 if (cacheid == NULL) {
 369                         pr_err(gettext("default cacheid too long"));
 370                         return (1);
 371                 }
 372 
 373                 strcpy(margs.cfs_cacheid, cacheid);
 374         }
 375 
 376         /* lock the cache directory shared */
 377         lockid = cachefs_dir_lock(margs.cfs_cachedir, 1);
 378         if (lockid == -1) {
 379                 /* exit if could not get the lock */
 380                 return (1);
 381         }
 382 
 383         /* if no mount point was specified and we are not remounting */
 384         mounted = 0;
 385         if ((margs.cfs_backfs == NULL) &&
 386             (((mflag & MS_REMOUNT) == 0) ||
 387             (margs.cfs_options.opt_flags & CFS_SLIDE))) {
 388                 /* if a disconnectable mount */
 389                 xx = 0;
 390                 if (margs.cfs_options.opt_flags & CFS_DISCONNECTABLE) {
 391                         /* see if the server is alive */
 392                         xx = pingserver(specp);
 393                 }
 394 
 395                 /* attempt to mount the back file system */
 396                 if (xx == 0) {
 397                         xx = dobackmnt(&margs, reducep, specp, backfstypep,
 398                             myname, readonly);
 399                         /*
 400                          * nfs mount exits with a value of 32 if a timeout
 401                          * error occurs trying the mount.
 402                          */
 403                         if (xx && (xx != 32)) {
 404                                 cachefs_dir_unlock(lockid);
 405                                 rmdir(margs.cfs_backfs);
 406                                 return (1);
 407                         }
 408                         if (xx == 0)
 409                                 mounted = 1;
 410                 }
 411         }
 412 
 413         /*
 414          * At this point the back file system should be mounted.
 415          * Get NFS version information for the back filesystem if
 416          * it is NFS. The version information is required
 417          * because NFS version 4 is incompatible with cachefs
 418          * and we provide pass-through support for NFS version 4
 419          * with cachefs, aka the cachefs mount is installed but
 420          * there is no caching. This is indicated to the kernel
 421          * during the mount by setting the CFS_BACKFS_NFSV4 flag.
 422          */
 423         if (margs.cfs_backfs != NULL && strcmp(backfstypep, "nfs") == 0) {
 424 
 425                 nfsvers = cachefs_get_back_nfsvers(margs.cfs_backfs, nomnttab);
 426                 switch (nfsvers) {
 427                 case 2:
 428                         break;
 429 
 430                 case 3:
 431                         if (nfsv3pass) {
 432                                 /* Force pass through (for debugging) */
 433                                 margs.cfs_options.opt_flags = CFS_BACKFS_NFSV4;
 434                                 if (cfs_nfsv4_build_opts(optionp,
 435                                                 cfs_nfsv4ops) != 0) {
 436                                         nfsvers_error = TRUE;
 437                                         goto clean_backmnt;
 438                                 }
 439                         }
 440                         break;
 441 
 442                 case 4:
 443                         /*
 444                          * overwrite old option flags with NFSv4 flag.
 445                          * Note that will also operate in strict
 446                          * consistency mode. Clean up the option string
 447                          * to get rid of the cachefs-specific options
 448                          * to be in sync with the opt flags, otherwise
 449                          * these can make it into the mnttab and cause
 450                          * problems (esp. the disconnected option).
 451                          */
 452                         margs.cfs_options.opt_flags = CFS_BACKFS_NFSV4;
 453                         if (cfs_nfsv4_build_opts(optionp, cfs_nfsv4ops) != 0) {
 454                                 nfsvers_error = TRUE;
 455                                 goto clean_backmnt;
 456                         }
 457                         break;
 458 
 459                 default:
 460                         /* error, unknown version */
 461                         nfsvers_error = TRUE;
 462                         goto clean_backmnt;
 463                 }
 464         }
 465 
 466         /*
 467          * Grab server name from special file arg if it is there or set
 468          * server name to "server unknown".
 469          */
 470         margs.cfs_hostname = servname;
 471         strncpy(servname, specp, sizeof (servname));
 472         servname[sizeof (servname) - 1] = '\0';
 473         strp = strchr(servname, ':');
 474         if (strp == NULL) {
 475                 margs.cfs_hostname = "server unknown";
 476                 margs.cfs_backfsname = specp;
 477         } else {
 478                 *strp = '\0';
 479                 /*
 480                  * The rest of the special file arg is the name of
 481                  * the back filesystem.
 482                  */
 483                 strp++;
 484                 margs.cfs_backfsname = strp;
 485         }
 486 
 487         /* mount the cache file system */
 488         xx = mount((margs.cfs_backfs != NULL) ? margs.cfs_backfs : "nobackfs",
 489                 mntp, mflag | MS_DATA, MNTTYPE_CFS,
 490                 &margs, sizeof (margs),
 491                 (cfs_nfsv4ops[0] == '\0' ? mops : cfs_nfsv4ops),
 492                 MAX_MNTOPT_STR);
 493 clean_backmnt:
 494         if (xx == -1 || nfsvers_error) {
 495                 if (nfsvers_error) {
 496                         pr_err(gettext("nfs version error."));
 497                 } else if (errno == ESRCH) {
 498                         pr_err(gettext("mount failed, options do not match."));
 499                 } else if ((errno == EAGAIN) && (margs.cfs_backfs == NULL)) {
 500                         pr_err(gettext("mount failed, server not responding."));
 501                 } else {
 502                         pr_err(gettext("mount failed %s"), strerror(errno));
 503                 }
 504 
 505                 /* try to unmount the back file system if we mounted it */
 506                 if (mounted) {
 507                         xx = 1;
 508                         newargv[xx++] = "umount";
 509                         newargv[xx++] = margs.cfs_backfs;
 510                         newargv[xx++] = NULL;
 511 
 512                         /* fork */
 513                         if ((pid = fork()) == -1) {
 514                                 pr_err(gettext("could not fork: %s"),
 515                                     strerror(errno));
 516                                 cachefs_dir_unlock(lockid);
 517                                 return (1);
 518                         }
 519 
 520                         /* if the child */
 521                         if (pid == 0) {
 522                                 /* do the unmount */
 523                                 doexec(backfstypep, newargv, "umount");
 524                         }
 525 
 526                         /* else if the parent */
 527                         else {
 528                                 wait(0);
 529                         }
 530                         rmdir(margs.cfs_backfs);
 531                 }
 532 
 533                 cachefs_dir_unlock(lockid);
 534                 return (1);
 535         }
 536 
 537         /* release the lock on the cache directory */
 538         cachefs_dir_unlock(lockid);
 539 
 540         /* record the mount information in the fscache directory */
 541         record_mount(mntp, specp, margs.cfs_backfs, backfstypep,
 542                 margs.cfs_cachedir, margs.cfs_cacheid,
 543                 (cfs_nfsv4ops[0] == '\0' ? optionp : cfs_nfsv4ops), reducep);
 544 
 545         /* notify the daemon of the mount */
 546         if (notify)
 547                 daemon_notify(margs.cfs_cachedir, margs.cfs_cacheid);
 548 
 549         /* update mnttab file if necessary */
 550         if (!nomnttab) {
 551                 /*
 552                  * If we added the back file system, tag it with ignore,
 553                  * however, don't fail the mount after its done
 554                  * if the tag can't be added (eg., this would cause
 555                  * automounter problems).
 556                  */
 557                 if (mounted) {
 558                         FILE *mt;
 559                         struct extmnttab mnt;
 560 
 561                         if ((mt = fopen(MNTTAB, "r")) == NULL)
 562                                 return (1);
 563                         while (getextmntent(mt, &mnt, sizeof (mnt)) != -1) {
 564                                 if (mnt.mnt_mountp != NULL &&
 565                                     strcmp(margs.cfs_backfs,
 566                                         mnt.mnt_mountp) == 0) {
 567                                         /* found it, do tag ioctl */
 568                                         mtdesc.mtd_major = mnt.mnt_major;
 569                                         mtdesc.mtd_minor = mnt.mnt_minor;
 570                                         mtdesc.mtd_mntpt = margs.cfs_backfs;
 571                                         mtdesc.mtd_tag = MNTOPT_IGNORE;
 572 
 573                                         (void) ioctl(fileno(mt),
 574                                                 MNTIOC_SETTAG, &mtdesc);
 575                                         break;
 576                                 }
 577                         }
 578                         fclose(mt);
 579                 }
 580         }
 581 
 582         /* return success */
 583         return (0);
 584 }
 585 
 586 
 587 /*
 588  *
 589  *                      usage
 590  *
 591  * Description:
 592  *      Prints a short usage message.
 593  * Arguments:
 594  *      msgp    message to include with the usage message
 595  * Returns:
 596  * Preconditions:
 597  */
 598 
 599 void
 600 usage(char *msgp)
 601 {
 602         if (msgp) {
 603                 pr_err(gettext("%s"), msgp);
 604         }
 605 
 606         fprintf(stderr,
 607             gettext("Usage: mount -F cachefs [generic options] "
 608             "-o backfstype=file_system_type[FSTypespecific_options] "
 609             "special mount_point\n"));
 610 }
 611 
 612 /*
 613  *
 614  *                      pr_err
 615  *
 616  * Description:
 617  *      Prints an error message to stderr.
 618  * Arguments:
 619  *      fmt     printf style format
 620  *      ...     arguments for fmt
 621  * Returns:
 622  * Preconditions:
 623  *      precond(fmt)
 624  */
 625 
 626 void
 627 pr_err(char *fmt, ...)
 628 {
 629         va_list ap;
 630 
 631         va_start(ap, fmt);
 632         (void) fprintf(stderr, gettext("mount -F cachefs: "));
 633         (void) vfprintf(stderr, fmt, ap);
 634         (void) fprintf(stderr, "\n");
 635         va_end(ap);
 636 }
 637 
 638 /*
 639  *
 640  *                      set_cfs_args
 641  *
 642  * Description:
 643  *      Parse the comma delimited set of options specified by optionp
 644  *      and puts the results in margsp, mflagp, and backfstypepp.
 645  *      A string is constructed of options which are not specific to
 646  *      cfs and is placed in reducepp.
 647  *      Pointers to strings are invalid if this routine is called again.
 648  *      No initialization is done on margsp, mflagp, or backfstypepp.
 649  * Arguments:
 650  *      optionp         string of comma delimited options
 651  *      margsp          option results for the mount dataptr arg
 652  *      mflagp          option results for the mount mflag arg
 653  *      backfstypepp    set to name of back file system type
 654  *      reducepp        set to the option string without cfs specific options
 655  * Returns:
 656  *      Returns 0 for success, -1 for an error.
 657  * Preconditions:
 658  *      precond(optionp)
 659  *      precond(margsp)
 660  *      precond(mflagp)
 661  *      precond(backfstypepp)
 662  *      precond(reducepp)
 663  */
 664 
 665 int
 666 set_cfs_args(char *optionp, struct cachefs_mountargs *margsp, int *mflagp,
 667     char **backfstypepp, char **reducepp, int *notifyp, int *nfsv3pass)
 668 {
 669         static char *optstrp = NULL;
 670         static char *reducep = NULL;
 671         char *savep, *strp, *valp;
 672         int badopt;
 673         int ret;
 674         int o_backpath = 0;
 675         int o_writemode = 0;
 676         int xx;
 677         uint_t yy;
 678         struct stat64 sinfo;
 679         char *pbuf;
 680 
 681         /* free up any previous options */
 682         free(optstrp);
 683         optstrp = NULL;
 684         free(reducep);
 685         reducep = NULL;
 686 
 687         /* make a copy of the options so we can modify it */
 688         optstrp = strp = strdup(optionp);
 689         reducep = malloc(strlen(optionp) + 1000);
 690         if ((strp == NULL) || (reducep == NULL)) {
 691                 pr_err(gettext("out of memory"));
 692                 return (-1);
 693         }
 694         *reducep = '\0';
 695 
 696         /* parse the options */
 697         badopt = 0;
 698         ret = 0;
 699         while (*strp) {
 700                 savep = strp;
 701                 switch (getsubopt(&strp, cfs_opts, &valp)) {
 702 
 703                 case CFSOPT_BACKFSTYPE:
 704                         if (valp == NULL)
 705                                 badopt = 1;
 706                         else
 707                                 *backfstypepp = valp;
 708                         break;
 709 
 710                 case CFSOPT_CACHEDIR:
 711                         if (valp == NULL)
 712                                 badopt = 1;
 713                         else {
 714                                 margsp->cfs_cachedir = valp;
 715                                 if (valp[0] != '/') {
 716                                     pbuf = (char *)malloc(MAXPATHLEN +
 717                                                 strlen(valp) + 3);
 718                                     if (pbuf == NULL) {
 719                                         pr_err(gettext("out of memory"));
 720                                         badopt = 1;
 721                                         break;
 722                                     }
 723                                     if (getcwd(pbuf, MAXPATHLEN+1) == NULL) {
 724                                         pr_err(gettext("cachedir too long"));
 725                                         badopt = 1;
 726                                         break;
 727                                     }
 728                                     if (pbuf[strlen(pbuf)-1] != '/')
 729                                         strcat(pbuf, "/");
 730                                     strcat(pbuf, valp);
 731                                     margsp->cfs_cachedir = pbuf;
 732                                 }
 733                         }
 734                         break;
 735 
 736                 case CFSOPT_CACHEID:
 737                         if (valp == NULL) {
 738                                 badopt = 1;
 739                                 break;
 740                         }
 741 
 742                         if (strlen(valp) >= (size_t)C_MAX_MOUNT_FSCDIRNAME) {
 743                                 pr_err(gettext("cacheid too long"));
 744                                 badopt = 1;
 745                                 break;
 746                         }
 747 
 748                         memset(margsp->cfs_cacheid, 0, C_MAX_MOUNT_FSCDIRNAME);
 749                         strcpy(margsp->cfs_cacheid, valp);
 750                         break;
 751 
 752                 case CFSOPT_BACKPATH:
 753                         if (valp == NULL)
 754                                 badopt = 1;
 755                         else {
 756                                 margsp->cfs_backfs = valp;
 757                                 o_backpath = 1;
 758                         }
 759                         break;
 760 
 761                 case CFSOPT_WRITEAROUND:
 762                         margsp->cfs_options.opt_flags |= CFS_WRITE_AROUND;
 763                         o_writemode++;
 764                         break;
 765 
 766                 case CFSOPT_NONSHARED:
 767                         margsp->cfs_options.opt_flags |= CFS_NONSHARED;
 768                         o_writemode++;
 769                         break;
 770 
 771                 case CFSOPT_NOCONST:
 772                         margsp->cfs_options.opt_flags |= CFS_NOCONST_MODE;
 773                         break;
 774 
 775                 case CFSOPT_CODCONST:
 776                         margsp->cfs_options.opt_flags |= CFS_CODCONST_MODE;
 777                         break;
 778 
 779                 case CFSOPT_LOCALACCESS:
 780                         margsp->cfs_options.opt_flags &= ~CFS_ACCESS_BACKFS;
 781                         break;
 782 
 783                 case CFSOPT_NOSETSEC:
 784                         margsp->cfs_options.opt_flags |= CFS_NOACL;
 785                         break;
 786 
 787                 case CFSOPT_LLOCK:
 788                         margsp->cfs_options.opt_flags |= CFS_LLOCK;
 789                         strcat(reducep, ",");
 790                         strcat(reducep, savep);
 791                         break;
 792 
 793                 case CFSOPT_REMOUNT:
 794                         *mflagp |= MS_REMOUNT;
 795                         break;
 796 
 797                 case CFSOPT_SLIDE:
 798                         margsp->cfs_options.opt_flags |= CFS_SLIDE;
 799                         break;
 800 
 801                 case CFSOPT_FGSIZE:
 802                         if (bad(valp))
 803                                 badopt = 1;
 804                         else
 805                                 margsp->cfs_options.opt_fgsize = atoi(valp);
 806                         break;
 807 
 808                 case CFSOPT_POPSIZE:
 809                         if (bad(valp))
 810                                 badopt = 1;
 811                         else
 812                                 margsp->cfs_options.opt_popsize =
 813                                     atoi(valp) * 1024;
 814                         break;
 815 
 816                 case CFSOPT_ACREGMIN:
 817                         if (bad(valp))
 818                                 badopt = 1;
 819                         else
 820                                 margsp->cfs_acregmin = atoi(valp);
 821                         break;
 822 
 823                 case CFSOPT_ACREGMAX:
 824                         if (bad(valp))
 825                                 badopt = 1;
 826                         else
 827                                 margsp->cfs_acregmax = atoi(valp);
 828                         break;
 829 
 830                 case CFSOPT_ACDIRMIN:
 831                         if (bad(valp))
 832                                 badopt = 1;
 833                         else
 834                                 margsp->cfs_acdirmin = atoi(valp);
 835                         break;
 836 
 837                 case CFSOPT_ACDIRMAX:
 838                         if (bad(valp))
 839                                 badopt = 1;
 840                         else
 841                                 margsp->cfs_acdirmax = atoi(valp);
 842                         break;
 843 
 844                 case CFSOPT_ACTIMEO:
 845                         if (bad(valp))
 846                                 badopt = 1;
 847                         else {
 848                                 yy = atoi(valp);
 849                                 margsp->cfs_acregmin = yy;
 850                                 margsp->cfs_acregmax = yy;
 851                                 margsp->cfs_acdirmin = yy;
 852                                 margsp->cfs_acdirmax = yy;
 853                         }
 854                         /*
 855                          * Note that we do not pass the actimeo options
 856                          * to the back file system.  This change was
 857                          * made for Chart.  Chart needs noac or actimeo=0
 858                          * so it makes no sense to pass these options on.
 859                          * In theory it should be okay to not pass these
 860                          * options on for regular cachefs mounts since
 861                          * cachefs perform the required attribute caching.
 862                          */
 863                         break;
 864 
 865 #if 0
 866                 case CFSOPT_LAZYMOUNT:
 867                         margsp->cfs_options.opt_flags |= CFS_LAZYMOUNT;
 868                         break;
 869 #endif
 870 
 871                 case CFSOPT_DISCONNECTABLE:
 872                 case CFSOPT_SNR:
 873                         margsp->cfs_options.opt_flags |= CFS_DISCONNECTABLE;
 874                         break;
 875 
 876                 case CFSOPT_NOFILL:
 877                         margsp->cfs_options.opt_flags |= CFS_NOFILL;
 878                         break;
 879 
 880                 case CFSOPT_SOFT:
 881                         margsp->cfs_options.opt_flags |= CFS_SOFT;
 882                         break;
 883 
 884                 case CFSOPT_NONOTIFY:
 885                         *notifyp = 0;
 886                         break;
 887 
 888 #ifdef CFS_NFSV3_PASSTHROUGH
 889                 case CFSOPT_NFSV3PASSTHROUGH:
 890                         *nfsv3pass = 1;
 891                         break;
 892 #endif /* CFS_NFSV3_PASSTHROUGH */
 893 
 894                 default:
 895                         /*
 896                          * unknown or vfs layer option, save for the back
 897                          * file system
 898                          */
 899                         strcat(reducep, ",");
 900                         strcat(reducep, savep);
 901                         break;
 902                 }
 903 
 904                 /* if a lexical error occurred */
 905                 if (badopt) {
 906                         pr_err(gettext("invalid argument to option: \"%s\""),
 907                             savep);
 908                         badopt = 0;
 909                         ret = -1;
 910                 }
 911         }
 912 
 913         /*
 914          * Should mount backfs soft if disconnectable & non-shared options
 915          * are used. NFS soft option allows reads and writes to TIMEOUT
 916          * when the server is not responding, which is crucial for
 917          * disconnectable option to work all the time in non-shared mode.
 918          *
 919          * Should mount backfs semisoft if disconnectable & write-around
 920          * are used. NFS semisoft option allows reads to TIMEOUT and
 921          * write to block when the server is not responding, which is
 922          * good for write around option because it is shared.
 923          *
 924          * Since disconnectable and strict options are conflicting,
 925          * when disconnectable option is used, default option is set to
 926          * demandconst.
 927          */
 928 
 929         if (margsp->cfs_options.opt_flags & (CFS_DISCONNECTABLE | CFS_SOFT))
 930                 if (margsp->cfs_options.opt_flags & CFS_NONSHARED) {
 931                         strcat(reducep, ",soft,noprint");
 932                         margsp->cfs_options.opt_flags |= CFS_CODCONST_MODE;
 933                 }
 934                 else
 935                         strcat(reducep, ",semisoft,noprint");
 936 
 937         if (!(margsp->cfs_options.opt_flags & CFS_DISCONNECTABLE)) {
 938                 /* not snr, no need to notify the cachefsd */
 939                 *notifyp = 0;
 940         }
 941 
 942         /* additional nfs options needed so disconnectable will work */
 943         if (margsp->cfs_options.opt_flags & CFS_DISCONNECTABLE) {
 944                 /*
 945                  * retry=0 so cachefs can mount if nfs mount fails
 946                  *   even with this nfs takes 3 minutes to give up
 947                  * actimeo=0 because NFS does not pick up new ctime after
 948                  *      rename
 949                  */
 950                 strcat(reducep, ",retry=0");
 951                 if (margsp->cfs_options.opt_flags & CFS_NONSHARED)
 952                         strcat(reducep, ",actimeo=0");
 953         }
 954 
 955         /* check for conflicting options */
 956         xx = margsp->cfs_options.opt_flags;
 957         if (o_backpath & (xx & CFS_DISCONNECTABLE)) {
 958                 pr_err(gettext("backpath cannot be used with disconnectable"));
 959                 ret = -1;
 960         }
 961         if (margsp->cfs_acregmin > margsp->cfs_acregmax) {
 962                 pr_err(gettext("acregmin cannot be greater than acregmax"));
 963                 ret = -1;
 964         }
 965         if (margsp->cfs_acdirmin > margsp->cfs_acdirmax) {
 966                 pr_err(gettext("acdirmin cannot be greater than acdirmax"));
 967                 ret = -1;
 968         }
 969 
 970         xx = CFS_NOCONST_MODE | CFS_CODCONST_MODE;
 971         if ((margsp->cfs_options.opt_flags & xx) == xx) {
 972                 pr_err(gettext("only one of noconst and demandconst"
 973                         " may be specified"));
 974                 ret = -1;
 975         }
 976 
 977         if (o_writemode > 1) {
 978                 pr_err(gettext(
 979                     "only one of write-around or non-shared"
 980                     " may be specified"));
 981                 ret = -1;
 982         }
 983 
 984         /* if an error occured */
 985         if (ret)
 986                 return (-1);
 987 
 988         /* if there are any options which are not mount specific */
 989         if (*reducep)
 990                 *reducepp = reducep + 1;
 991         else
 992                 *reducepp = NULL;
 993 
 994         /* return success */
 995         return (0);
 996 }
 997 
 998 /*
 999  *
1000  *                      get_mount_point
1001  *
1002  * Description:
1003  *      Makes a suitable mount point for the back file system.
1004  *      The name of the mount point created is stored in a malloced
1005  *      buffer in pathpp
1006  * Arguments:
1007  *      cachedirp       the name of the cache directory
1008  *      specp           the special name of the device for the file system
1009  *      pathpp          where to store the mount point
1010  * Returns:
1011  *      Returns 0 for success, -1 for an error.
1012  * Preconditions:
1013  *      precond(cachedirp)
1014  *      precond(specp)
1015  *      precond(pathpp)
1016  */
1017 
1018 int
1019 get_mount_point(char *cachedirp, char *specp, char **pathpp)
1020 {
1021         char *strp;
1022         char *namep;
1023         struct stat64 stat1, stat2;
1024         int xx;
1025         int index;
1026         int max;
1027 
1028         /* make a copy of the special device name */
1029         specp = strdup(specp);
1030         if (specp == NULL) {
1031                 pr_err(gettext("out of memory"));
1032                 return (-1);
1033         }
1034 
1035         /* convert the special device name into a file name */
1036         strp = specp;
1037         while (strp = strchr(strp, '/')) {
1038                 *strp = '_';
1039         }
1040 
1041         /* get some space for the path name */
1042         strp = malloc(MAXPATHLEN);
1043         if (strp == NULL) {
1044                 pr_err(gettext("out of memory"));
1045                 return (-1);
1046         }
1047 
1048         /* see if the mount directory is valid */
1049         /* backfs can contain large files */
1050         sprintf(strp, "%s/%s", cachedirp, BACKMNT_NAME);
1051         xx = stat64(strp, &stat1);
1052         if ((xx == -1) || !S_ISDIR(stat1.st_mode)) {
1053                 pr_err(gettext("%s is not a valid cache."), strp);
1054                 return (-1);
1055         }
1056 
1057         /* find a directory name we can use */
1058         max = 10000;
1059         namep = strp + strlen(strp);
1060         for (index = 1; index < max; index++) {
1061 
1062                 /* construct a directory name to consider */
1063                 if (index == 1)
1064                         sprintf(namep, "/%s", specp);
1065                 else
1066                         sprintf(namep, "/%s_%d", specp, index);
1067 
1068                 /* try to create the directory */
1069                 xx = mkdir(strp, 0755);
1070                 if (xx == 0) {
1071                         /* done if the create succeeded */
1072                         break;
1073                 }
1074         }
1075 
1076         /* if the search failed */
1077         if (index >= max) {
1078                 pr_err(gettext("could not create a directory"));
1079                 return (-1);
1080         }
1081 
1082         /* return success */
1083         *pathpp = strp;
1084         return (0);
1085 }
1086 
1087 
1088 int
1089 dobackmnt(struct cachefs_mountargs *margsp, char *reducep, char *specp,
1090     char *backfstypep, char *mynamep, int readonly)
1091 {
1092         int xx;
1093         pid_t pid;
1094         char *newargv[20];
1095         int stat_loc;
1096 
1097         /* get a suitable mount point */
1098         xx = get_mount_point(margsp->cfs_cachedir, specp, &margsp->cfs_backfs);
1099         if (xx)
1100                 return (1);
1101 
1102         /* construct argument list for mounting the back file system */
1103         xx = 1;
1104         newargv[xx++] = "mount";
1105         if (readonly)
1106                 newargv[xx++] = "-r";
1107         if (nomnttab)
1108                 newargv[xx++] = "-m";
1109         if (quiet)
1110                 newargv[xx++] = "-q";
1111         if (reducep) {
1112                 newargv[xx++] = "-o";
1113                 newargv[xx++] = reducep;
1114         }
1115         newargv[xx++] = specp;
1116         newargv[xx++] = margsp->cfs_backfs;
1117         newargv[xx++] = NULL;
1118 
1119         /* fork */
1120         if ((pid = fork()) == -1) {
1121                 pr_err(gettext("could not fork %s"), strerror(errno));
1122                 return (1);
1123         }
1124 
1125         /* if the child */
1126         if (pid == 0) {
1127                 /* do the mount */
1128                 doexec(backfstypep, newargv, mynamep);
1129         }
1130 
1131         /* else if the parent */
1132         else {
1133                 /* wait for the child to exit */
1134                 if (wait(&stat_loc) == -1) {
1135                         pr_err(gettext("wait failed %s"), strerror(errno));
1136                         return (1);
1137                 }
1138 
1139                 if (!WIFEXITED(stat_loc)) {
1140                         pr_err(gettext("back mount did not exit"));
1141                         return (1);
1142                 }
1143 
1144                 xx = WEXITSTATUS(stat_loc);
1145                 if (xx) {
1146                         pr_err(gettext("back mount failed"));
1147                         return (xx);
1148                 }
1149         }
1150 
1151         return (0);
1152 }
1153 
1154 /*
1155  *
1156  *                      doexec
1157  *
1158  * Description:
1159  *      Execs the specified program with the specified command line arguments.
1160  *      This function never returns.
1161  * Arguments:
1162  *      fstype          type of file system
1163  *      newargv         command line arguments
1164  *      progp           name of program to exec
1165  * Returns:
1166  * Preconditions:
1167  *      precond(fstype)
1168  *      precond(newargv)
1169  */
1170 
1171 void
1172 doexec(char *fstype, char *newargv[], char *progp)
1173 {
1174         char    full_path[PATH_MAX];
1175         char    alter_path[PATH_MAX];
1176         char    *vfs_path = VFS_PATH;
1177         char    *alt_path = ALT_PATH;
1178 
1179         /* build the full pathname of the fstype dependent command. */
1180         sprintf(full_path, "%s/%s/%s", vfs_path, fstype, progp);
1181         sprintf(alter_path, "%s/%s/%s", alt_path, fstype, progp);
1182 
1183         /* if the program exists */
1184         if (access(full_path, 0) == 0) {
1185                 /* invoke the program */
1186                 execv(full_path, &newargv[1]);
1187 
1188                 /* if wrong permissions */
1189                 if (errno == EACCES) {
1190                         pr_err(gettext("cannot execute %s %s"),
1191                             full_path, strerror(errno));
1192                 }
1193 
1194                 /* if it did not work and the shell might make it */
1195                 if (errno == ENOEXEC) {
1196                         newargv[0] = "sh";
1197                         newargv[1] = full_path;
1198                         execv("/sbin/sh", &newargv[0]);
1199                 }
1200         }
1201 
1202         /* try the alternate path */
1203         execv(alter_path, &newargv[1]);
1204 
1205         /* if wrong permissions */
1206         if (errno == EACCES) {
1207                 pr_err(gettext("cannot execute %s %s"),
1208                     alter_path, strerror(errno));
1209         }
1210 
1211         /* if it did not work and the shell might make it */
1212         if (errno == ENOEXEC) {
1213                 newargv[0] = "sh";
1214                 newargv[1] = alter_path;
1215                 execv("/sbin/sh", &newargv[0]);
1216         }
1217 
1218         pr_err(gettext("operation not applicable to FSType %s"), fstype);
1219         exit(1);
1220 }
1221 
1222 /*
1223  *
1224  *                      get_back_fsid
1225  *
1226  * Description:
1227  *      Determines a unique identifier for the back file system.
1228  * Arguments:
1229  *      specp   the special file of the back fs
1230  * Returns:
1231  *      Returns a malloc string which is the unique identifer
1232  *      or NULL on failure.  NULL is only returned if malloc fails.
1233  * Preconditions:
1234  *      precond(specp)
1235  */
1236 
1237 char *
1238 get_back_fsid(char *specp)
1239 {
1240         return (strdup(specp));
1241 }
1242 
1243 /*
1244  *
1245  *                      get_cacheid
1246  *
1247  * Description:
1248  *      Determines an identifier for the front file system cache.
1249  *      The returned string points to a static buffer which is
1250  *      overwritten on each call.
1251  *      The length of the returned string is < C_MAX_MOUNT_FSCDIRNAME.
1252  * Arguments:
1253  *      fsidp   back file system id
1254  *      mntp    front file system mount point
1255  * Returns:
1256  *      Returns a pointer to the string identifier, or NULL if the
1257  *      identifier was overflowed.
1258  * Preconditions:
1259  *      precond(fsidp)
1260  *      precond(mntp)
1261  */
1262 
1263 char *
1264 get_cacheid(char *fsidp, char *mntp)
1265 {
1266         char *c1;
1267         static char buf[PATH_MAX];
1268         char mnt_copy[PATH_MAX];
1269 
1270         /* strip off trailing space in mountpoint -- autofs fallout */
1271         if (strlen(mntp) >= sizeof (mnt_copy))
1272                 return (NULL);
1273         (void) strcpy(mnt_copy, mntp);
1274         c1 = mnt_copy + strlen(mnt_copy) - 1;
1275         if (*c1 == ' ')
1276                 *c1 = '\0';
1277 
1278         if ((strlen(fsidp) + strlen(mnt_copy) + 2) >=
1279             (size_t)C_MAX_MOUNT_FSCDIRNAME)
1280                 return (NULL);
1281 
1282         strcpy(buf, fsidp);
1283         strcat(buf, ":");
1284         strcat(buf, mnt_copy);
1285         c1 = buf;
1286         while ((c1 = strpbrk(c1, "/")) != NULL)
1287                 *c1 = '_';
1288         return (buf);
1289 }
1290 
1291 
1292 /*
1293  *
1294  *                      check_cache
1295  *
1296  * Description:
1297  *      Checks the cache we are about to use.
1298  * Arguments:
1299  *      cachedirp       cachedirectory to check
1300  * Returns:
1301  *      Returns 0 for success, -1 for an error.
1302  * Preconditions:
1303  */
1304 int
1305 check_cache(cachedirp)
1306         char *cachedirp;
1307 {
1308         char *fsck_argv[4];
1309         int status = 0;
1310         pid_t pid;
1311 
1312         fsck_argv[1] = "fsck";
1313         fsck_argv[2] = cachedirp;
1314         fsck_argv[3] = NULL;
1315 
1316         /* fork */
1317         if ((pid = fork()) == -1) {
1318                 pr_err(gettext("could not fork %s"),
1319                     strerror(errno));
1320                 return (1);
1321         }
1322 
1323         if (pid == 0) {
1324                 /* do the fsck */
1325                 doexec("cachefs", fsck_argv, "fsck");
1326         } else {
1327                 /* wait for the child to exit */
1328                 if (wait(&status) == -1) {
1329                         pr_err(gettext("wait failed %s"),
1330                             strerror(errno));
1331                         return (1);
1332                 }
1333 
1334                 if (!WIFEXITED(status)) {
1335                         pr_err(gettext("cache fsck did not exit"));
1336                         return (1);
1337                 }
1338 
1339                 if (WEXITSTATUS(status) != 0) {
1340                         pr_err(gettext("cache fsck mount failed"));
1341                         return (1);
1342                 }
1343         }
1344         return (0);
1345 }
1346 
1347 /*
1348  *
1349  *                      record_mount
1350  *
1351  * Description:
1352  *      Records mount information in a file in the fscache directory.
1353  * Arguments:
1354  * Returns:
1355  * Preconditions:
1356  */
1357 
1358 void
1359 record_mount(char *mntp, char *specp, char *backfsp, char *backfstypep,
1360     char *cachedirp, char *cacheidp, char *optionp, char *reducep)
1361 {
1362         char buf[MAXPATHLEN*2];
1363         FILE *fout;
1364         time_t tval;
1365 
1366         tval = time(NULL);
1367 
1368         /* this file is < 2GB */
1369         sprintf(buf, "%s/%s/%s", cachedirp, cacheidp, CACHEFS_MNT_FILE);
1370         fout = fopen(buf, "w");
1371         if (fout == NULL) {
1372                 pr_err(gettext("could not open %s, %d"), buf, errno);
1373                 return;
1374         }
1375 
1376         fprintf(fout, "cachedir: %s\n", cachedirp);
1377         fprintf(fout, "mnt_point: %s\n", mntp);
1378         if (specp) {
1379                 fprintf(fout, "special: %s\n", specp);
1380         }
1381         if (backfsp)
1382                 fprintf(fout, "backpath: %s\n", backfsp);
1383         fprintf(fout, "backfstype: %s\n", backfstypep);
1384         fprintf(fout, "cacheid: %s\n", cacheidp);
1385         fprintf(fout, "cachefs_options: %s\n", optionp);
1386         if (reducep)
1387                 fprintf(fout, "backfs_options: %s\n", reducep);
1388         fprintf(fout, "mount_time: %u\n", tval);
1389 
1390         fclose(fout);
1391 }
1392 
1393 int
1394 daemon_notify(char *cachedirp, char *cacheidp)
1395 {
1396         CLIENT *clnt;
1397         enum clnt_stat retval;
1398         int ret;
1399         int xx;
1400         int result;
1401         char *hostp;
1402         struct utsname info;
1403         struct cachefsd_fs_mounted args;
1404 
1405         /* get the host name */
1406         xx = uname(&info);
1407         if (xx == -1) {
1408                 pr_err(gettext("cannot get host name, errno %d"), errno);
1409                 return (1);
1410         }
1411         hostp = info.nodename;
1412 
1413         /* creat the connection to the daemon */
1414         clnt = clnt_create(hostp, CACHEFSDPROG, CACHEFSDVERS, "local");
1415         if (clnt == NULL) {
1416                 pr_err(gettext("cachefsd is not running"));
1417                 return (1);
1418         }
1419 
1420         args.mt_cachedir = cachedirp;
1421         args.mt_cacheid = cacheidp;
1422         retval = cachefsd_fs_mounted_1(&args, NULL, clnt);
1423         if (retval != RPC_SUCCESS) {
1424                 clnt_perror(clnt, gettext("cachefsd is not responding"));
1425                 clnt_destroy(clnt);
1426                 return (1);
1427         }
1428 
1429         ret = 0;
1430 
1431         clnt_destroy(clnt);
1432 
1433         return (ret);
1434 }
1435 
1436 /* returns 0 if the server is alive, -1 if an error */
1437 int
1438 pingserver(char *backmntp)
1439 {
1440         CLIENT *clnt;
1441         static struct timeval TIMEOUT = { 25, 0 };
1442         enum clnt_stat retval;
1443         int ret;
1444         int xx;
1445         char *hostp;
1446         char buf[MAXPATHLEN];
1447         char *pc;
1448 
1449         /* get the host name */
1450         strcpy(buf, backmntp);
1451         pc = strchr(buf, ':');
1452         if (pc == NULL) {
1453                 /* no host name, pretend it works */
1454                 return (0);
1455         }
1456         *pc = '\0';
1457         hostp = buf;
1458 
1459         /* create the connection to the mount daemon */
1460         clnt = clnt_create(hostp, NFS_PROGRAM, NFS_VERSION, "udp");
1461         if (clnt == NULL) {
1462                 return (-1);
1463         }
1464 
1465         ret = 0;
1466 
1467         /* see if the mountd responds */
1468         retval = clnt_call(clnt, 0, xdr_void, NULL, xdr_void, NULL,
1469             TIMEOUT);
1470         if (retval != RPC_SUCCESS) {
1471                 ret = -1;
1472         }
1473 
1474         clnt_destroy(clnt);
1475 
1476         return (ret);
1477 }
1478 
1479 /*
1480  * first_time_ab  : first time after boot - returns non-zero value
1481  *                  if the cachedir is being used for the first time
1482  *                  after the system reboot, otherwise zero.
1483  */
1484 int
1485 first_time_ab(char *buf)
1486 {
1487         struct stat sinfo;
1488         char name[MAXPATHLEN];
1489         int ufd;
1490         time32_t btime;
1491 
1492         sprintf(name, "%s/%s", buf, CACHEFS_UNMNT_FILE);
1493         if (stat(name, &sinfo) != 0)
1494                 return (1);
1495         if (sinfo.st_size == 0)
1496                 return (1);
1497         if ((ufd = open(name, O_RDONLY)) == -1)
1498                 return (1);
1499         if (read(ufd, &btime, sizeof (time32_t)) == -1)
1500                 return (1);
1501         close(ufd);
1502         if (get_boottime() != btime)
1503                 return (1);
1504         return (0);
1505 }
1506 
1507 /*
1508  * cachefs_get_back_nfsvers
1509  *
1510  * Returns:     nfs version
1511  *
1512  * Params:
1513  *              cfs_backfs      - backfile system mountpoint
1514  *              nomnttab        - mnttab entry does not exist
1515  *
1516  * Uses the kstat interface to extract the nfs version for
1517  * the mount.
1518  */
1519 uint32_t
1520 cachefs_get_back_nfsvers(char *cfs_backfs, int nomnttab)
1521 {
1522         kstat_ctl_t *kc = NULL;
1523         FILE *mnttab = NULL;
1524         struct extmnttab mnt;
1525         kstat_t *ksp;
1526         dev_t my_fsid = NODEV;
1527         struct mntinfo_kstat mik;
1528         uint32_t nfsvers = 0;
1529         struct stat64 st;
1530 
1531         /*
1532          * Initialize kernel statistics facility.
1533          */
1534         if ((kc = kstat_open()) == NULL) {
1535                 pr_err(gettext("kstat_open() can't open /dev/kstat: %s"),
1536                         strerror(errno));
1537                 goto end;
1538         }
1539 
1540         /*
1541          * Locate the mount information in the mnttab if the nomnttab
1542          * flag is not set, otherwise look for the entry by doing
1543          * stat'ting the mountpoint.
1544          */
1545         if (!nomnttab) {
1546                 if ((mnttab = fopen(MNTTAB, "r")) == NULL) {
1547                         pr_err(gettext("can't open /etc/mnttab: %s"),
1548                                 strerror(errno));
1549                         goto end;
1550                 }
1551 
1552                 while (getextmntent(mnttab, &mnt, sizeof (mnt)) != -1) {
1553                         if (mnt.mnt_mountp == NULL ||
1554                             strcmp(cfs_backfs, mnt.mnt_mountp) != 0) {
1555                                 continue;
1556                         }
1557                         my_fsid = makedev(mnt.mnt_major, mnt.mnt_minor);
1558                         break;
1559                 }
1560         }
1561 
1562         if (my_fsid == NODEV) {
1563                 if (stat64(cfs_backfs, &st) == -1) {
1564                         pr_err(gettext("can't stat mountpoint: %s"),
1565                                 strerror(errno));
1566                         goto end;
1567                 } else {
1568                         my_fsid = st.st_dev;
1569                 }
1570 
1571         }
1572 
1573         /*
1574          * Walk the kstat control structures to locate the
1575          * structure that describes the nfs module/mntinfo
1576          * statistics for the mounted backfilesystem.
1577          */
1578         for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1579 
1580                 if (ksp->ks_type != KSTAT_TYPE_RAW)
1581                         continue;
1582                 if (strcmp(ksp->ks_module, "nfs") != 0)
1583                         continue;
1584                 if (strcmp(ksp->ks_name, "mntinfo") != 0)
1585                         continue;
1586                 if ((my_fsid & MAXMIN) != ksp->ks_instance)
1587                         continue;
1588 
1589                 /*
1590                  * At this point we have located the
1591                  * kstat info for the mount, read the
1592                  * statistics and return version info.
1593                  */
1594                 if (kstat_read(kc, ksp, &mik) == -1) {
1595                         pr_err(gettext("kstat_read() can't read %s/%s: %s"),
1596                                 ksp->ks_module, ksp->ks_name, strerror(errno));
1597                         goto end;
1598                 }
1599 
1600                 nfsvers = mik.mik_vers;
1601                 break;
1602         }
1603 
1604 end:
1605         if (kc)
1606                 kstat_close(kc);
1607         if (mnttab)
1608                 fclose(mnttab);
1609 
1610         return (nfsvers);
1611 }
1612 
1613 /*
1614  * cfs_nfsv4_build_opts
1615  *
1616  * Returns: 0 on success, -1 on failure
1617  *
1618  * Params:
1619  *      optionp         - original option pointer
1620  *      cfs_nfsv4ops    - modified options for nfsv4 cachefs mount
1621  *
1622  * Parse the comma delimited set of options specified by optionp
1623  * and clean out options that we don't want to use with NFSv4.
1624  */
1625 int
1626 cfs_nfsv4_build_opts(char *optionp, char *cfs_nfsv4ops)
1627 {
1628         char *optstrp;
1629         char *strp;
1630         char *savep;
1631         char *valp;
1632         uint32_t first = TRUE;
1633 
1634         /* Make a copy of the options so we can modify it */
1635         optstrp = strp = strdup(optionp);
1636         if (strp == NULL) {
1637                 pr_err(gettext("out of memory"));
1638                 return (-1);
1639         }
1640 
1641         /* Parse the options, cfs_nfsv4ops is initialized in main */
1642         while (*strp) {
1643                 savep = strp;
1644                 switch (getsubopt(&strp, cfs_opts, &valp)) {
1645 
1646                 /* Ignore options that set cfs option flags */
1647                 case CFSOPT_WRITEAROUND:
1648                 case CFSOPT_NONSHARED:
1649                 case CFSOPT_NOCONST:
1650                 case CFSOPT_CODCONST:
1651                 case CFSOPT_LOCALACCESS:
1652                 case CFSOPT_NOSETSEC:
1653                 case CFSOPT_LLOCK:
1654                 case CFSOPT_SLIDE:
1655                 case CFSOPT_DISCONNECTABLE:
1656                 case CFSOPT_SNR:
1657                 case CFSOPT_NOFILL:
1658                 case CFSOPT_SOFT:
1659                         break;
1660 
1661                 default:
1662                         /*
1663                          * Copy in option for cachefs nfsv4 mount.
1664                          */
1665                         snprintf(cfs_nfsv4ops, MAX_MNTOPT_STR,
1666                                 "%s%s%s", cfs_nfsv4ops, first ? "" : ",",
1667                                 savep);
1668                         first = FALSE;
1669                         break;
1670                 }
1671         }
1672         free(optstrp);
1673 
1674         return (0);
1675 }