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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "@(#)fslib.c    1.26    08/05/07 SMI"
  27 
  28 #include        <stdio.h>
  29 #include        <stdarg.h>
  30 #include        <stdlib.h>
  31 #include        <unistd.h>
  32 #include        <libintl.h>
  33 #include        <string.h>
  34 #include        <fcntl.h>
  35 #include        <errno.h>
  36 #include        <syslog.h>
  37 #include        <alloca.h>
  38 #include        <sys/vfstab.h>
  39 #include        <sys/mnttab.h>
  40 #include        <sys/mntent.h>
  41 #include        <sys/mount.h>
  42 #include        <sys/filio.h>
  43 #include        <sys/fs/ufs_filio.h>
  44 #include        <sys/stat.h>
  45 #include        <sys/param.h>
  46 #include        <zone.h>
  47 #include        <signal.h>
  48 #include        <strings.h>
  49 #include        "fslib.h"
  50 
  51 /* LINTLIBRARY */
  52 
  53 #define BUFLEN          256
  54 
  55 #define TIME_MAX 16
  56 
  57 /*
  58  * Reads all of the entries from the in-kernel mnttab, and returns the
  59  * linked list of the entries.
  60  */
  61 mntlist_t *
  62 fsgetmntlist(void)
  63 {
  64         FILE *mfp;
  65         mntlist_t *mntl;
  66         char buf[BUFLEN];
  67 
  68         if ((mfp = fopen(MNTTAB, "r")) == NULL) {
  69                 (void) snprintf(buf, BUFLEN, "fsgetmntlist: fopen %s", MNTTAB);
  70                 perror(buf);
  71                 return (NULL);
  72         }
  73 
  74         mntl = fsmkmntlist(mfp);
  75 
  76         (void) fclose(mfp);
  77         return (mntl);
  78 }
  79 
  80 
  81 static struct extmnttab zmnttab = { 0 };
  82 
  83 struct extmnttab *
  84 fsdupmnttab(struct extmnttab *mnt)
  85 {
  86         struct extmnttab *new;
  87 
  88         new = (struct extmnttab *)malloc(sizeof (*new));
  89         if (new == NULL)
  90                 goto alloc_failed;
  91 
  92         *new = zmnttab;
  93         /*
  94          * Allocate an extra byte for the mountpoint
  95          * name in case a space needs to be added.
  96          */
  97         new->mnt_mountp = (char *)malloc(strlen(mnt->mnt_mountp) + 2);
  98         if (new->mnt_mountp == NULL)
  99                 goto alloc_failed;
 100         (void) strcpy(new->mnt_mountp, mnt->mnt_mountp);
 101 
 102         if ((new->mnt_special = strdup(mnt->mnt_special)) == NULL)
 103                 goto alloc_failed;
 104 
 105         if ((new->mnt_fstype = strdup(mnt->mnt_fstype)) == NULL)
 106                 goto alloc_failed;
 107 
 108         if (mnt->mnt_mntopts != NULL)
 109                 if ((new->mnt_mntopts = strdup(mnt->mnt_mntopts)) == NULL)
 110                         goto alloc_failed;
 111 
 112         if (mnt->mnt_time != NULL)
 113                 if ((new->mnt_time = strdup(mnt->mnt_time)) == NULL)
 114                         goto alloc_failed;
 115 
 116         new->mnt_major = mnt->mnt_major;
 117         new->mnt_minor = mnt->mnt_minor;
 118         return (new);
 119 
 120 alloc_failed:
 121         (void) fprintf(stderr, gettext("fsdupmnttab: Out of memory\n"));
 122         fsfreemnttab(new);
 123         return (NULL);
 124 }
 125 
 126 /*
 127  * Free a single mnttab structure
 128  */
 129 void
 130 fsfreemnttab(struct extmnttab *mnt)
 131 {
 132 
 133         if (mnt) {
 134                 if (mnt->mnt_special)
 135                         free(mnt->mnt_special);
 136                 if (mnt->mnt_mountp)
 137                         free(mnt->mnt_mountp);
 138                 if (mnt->mnt_fstype)
 139                         free(mnt->mnt_fstype);
 140                 if (mnt->mnt_mntopts)
 141                         free(mnt->mnt_mntopts);
 142                 if (mnt->mnt_time)
 143                         free(mnt->mnt_time);
 144                 free(mnt);
 145         }
 146 }
 147 
 148 void
 149 fsfreemntlist(mntlist_t *mntl)
 150 {
 151         mntlist_t *mntl_tmp;
 152 
 153         while (mntl) {
 154                 fsfreemnttab(mntl->mntl_mnt);
 155                 mntl_tmp = mntl;
 156                 mntl = mntl->mntl_next;
 157                 free(mntl_tmp);
 158         }
 159 }
 160 
 161 /*
 162  * Read the mnttab file and return it as a list of mnttab structs.
 163  * Returns NULL if there was a memory failure.
 164  */
 165 mntlist_t *
 166 fsmkmntlist(FILE *mfp)
 167 {
 168         struct extmnttab        mnt;
 169         mntlist_t       *mhead, *mtail;
 170         int             ret;
 171 
 172         mhead = mtail = NULL;
 173 
 174         resetmnttab(mfp);
 175         while ((ret = getextmntent(mfp, &mnt, sizeof (struct extmnttab)))
 176             != -1) {
 177                 mntlist_t       *mp;
 178 
 179                 if (ret != 0)           /* bad entry */
 180                         continue;
 181 
 182                 mp = (mntlist_t *)malloc(sizeof (*mp));
 183                 if (mp == NULL)
 184                         goto alloc_failed;
 185                 if (mhead == NULL)
 186                         mhead = mp;
 187                 else
 188                         mtail->mntl_next = mp;
 189                 mtail = mp;
 190                 mp->mntl_next = NULL;
 191                 mp->mntl_flags = 0;
 192                 if ((mp->mntl_mnt = fsdupmnttab(&mnt)) == NULL)
 193                         goto alloc_failed;
 194         }
 195         return (mhead);
 196 
 197 alloc_failed:
 198         fsfreemntlist(mhead);
 199         return (NULL);
 200 }
 201 
 202 /*
 203  * Return the last entry that matches mntin's special
 204  * device and/or mountpt.
 205  * Helps to be robust here, so we check for NULL pointers.
 206  */
 207 mntlist_t *
 208 fsgetmlast(mntlist_t *ml, struct mnttab *mntin)
 209 {
 210         mntlist_t       *delete = NULL;
 211 
 212         for (; ml; ml = ml->mntl_next) {
 213                 if (mntin->mnt_mountp && mntin->mnt_special) {
 214                         /*
 215                          * match if and only if both are equal.
 216                          */
 217                         if ((strcmp(ml->mntl_mnt->mnt_mountp,
 218                             mntin->mnt_mountp) == 0) &&
 219                             (strcmp(ml->mntl_mnt->mnt_special,
 220                             mntin->mnt_special) == 0))
 221                                 delete = ml;
 222                 } else if (mntin->mnt_mountp) {
 223                         if (strcmp(ml->mntl_mnt->mnt_mountp,
 224                             mntin->mnt_mountp) == 0)
 225                                 delete = ml;
 226                 } else if (mntin->mnt_special) {
 227                         if (strcmp(ml->mntl_mnt->mnt_special,
 228                             mntin->mnt_special) == 0)
 229                                 delete = ml;
 230                 }
 231         }
 232         return (delete);
 233 }
 234 
 235 
 236 /*
 237  * Returns the mountlevel of the pathname in cp.  As examples,
 238  * / => 1, /bin => 2, /bin/ => 2, ////bin////ls => 3, sdf => 0, etc...
 239  */
 240 int
 241 fsgetmlevel(char *cp)
 242 {
 243         int     mlevel;
 244         char    *cp1;
 245 
 246         if (cp == NULL || *cp == NULL || *cp != '/')
 247                 return (0);     /* this should never happen */
 248 
 249         mlevel = 1;                     /* root (/) is the minimal case */
 250 
 251         for (cp1 = cp + 1; *cp1; cp++, cp1++)
 252                 if (*cp == '/' && *cp1 != '/')  /* "///" counts as 1 */
 253                         mlevel++;
 254 
 255         return (mlevel);
 256 }
 257 
 258 /*
 259  * Returns non-zero if string s is a member of the strings in ps.
 260  */
 261 int
 262 fsstrinlist(const char *s, const char **ps)
 263 {
 264         const char *cp;
 265         cp = *ps;
 266         while (cp) {
 267                 if (strcmp(s, cp) == 0)
 268                         return (1);
 269                 ps++;
 270                 cp = *ps;
 271         }
 272         return (0);
 273 }
 274 
 275 static char *empty_opt_vector[] = {
 276         NULL
 277 };
 278 /*
 279  * Compare the mount options that were requested by the caller to
 280  * the options actually supported by the file system.  If any requested
 281  * options are not supported, print a warning message.
 282  *
 283  * WARNING: this function modifies the string pointed to by
 284  *      the requested_opts argument.
 285  *
 286  * Arguments:
 287  *      requested_opts - the string containing the requested options.
 288  *      actual_opts - the string returned by mount(2), which lists the
 289  *              options actually supported.  It is normal for this
 290  *              string to contain more options than the requested options.
 291  *              (The actual options may contain the default options, which
 292  *              may not have been included in the requested options.)
 293  *      special - device being mounted (only used in error messages).
 294  *      mountp - mount point (only used in error messages).
 295  */
 296 void
 297 cmp_requested_to_actual_options(char *requested_opts, char *actual_opts,
 298         char *special, char *mountp)
 299 {
 300         char    *option_ptr, *actopt, *equalptr;
 301         int     found;
 302         char    *actual_opt_hold, *bufp;
 303 
 304         if (requested_opts == NULL)
 305                 return;
 306 
 307         bufp = alloca(strlen(actual_opts) + 1);
 308 
 309         while (*requested_opts != '\0') {
 310                 (void) getsubopt(&requested_opts, empty_opt_vector,
 311                     &option_ptr);
 312 
 313                 /*
 314                  * Truncate any "=<value>" string from the end of
 315                  * the option.
 316                  */
 317                 if ((equalptr = strchr(option_ptr, '=')) != NULL)
 318                         *equalptr = '\0';
 319 
 320                 if (*option_ptr == '\0')
 321                         continue;
 322 
 323                 /*
 324                  * Whilst we don't need this option to perform a lofi
 325                  * mount, let's not be mendacious enough to complain
 326                  * about it.
 327                  */
 328                 if (strcmp(option_ptr, "loop") == 0)
 329                         continue;
 330 
 331                 /*
 332                  * Search for the requested option in the list of options
 333                  * actually supported.
 334                  */
 335                 found = 0;
 336 
 337                 /*
 338                  * Need to use a copy of actual_opts because getsubopt
 339                  * is destructive and we need to scan the actual_opts
 340                  * string more than once.
 341                  *
 342                  * We also need to reset actual_opt_hold to the
 343                  * beginning of the buffer because getsubopt changes
 344                  * actual_opt_hold (the pointer).
 345                  */
 346                 actual_opt_hold = bufp;
 347                 if (actual_opts != NULL)
 348                         (void) strcpy(actual_opt_hold, actual_opts);
 349                 else
 350                         *actual_opt_hold = '\0';
 351 
 352                 while (*actual_opt_hold != '\0') {
 353                         (void) getsubopt(&actual_opt_hold, empty_opt_vector,
 354                             &actopt);
 355 
 356                         /* Truncate the "=<value>", if any. */
 357                         if ((equalptr = strchr(actopt, '=')) != NULL)
 358                                 *equalptr = '\0';
 359 
 360                         if ((strcmp(option_ptr, actopt)) == 0) {
 361                                 found = 1;
 362                                 break;
 363                         }
 364                 }
 365 
 366                 if (found == 0) {
 367                         /*
 368                          * That we're ignoring the option is always
 369                          * truthful; the old message that the option
 370                          * was unknown is often not correct.
 371                          */
 372                         (void) fprintf(stderr, gettext(
 373                             "mount: %s on %s - WARNING ignoring option "
 374                             "\"%s\"\n"), special, mountp, option_ptr);
 375                 }
 376         }
 377 }
 378 /*
 379  * FUNCTION:    fsgetmaxphys(int *, int *)
 380  *
 381  * INPUT:       int *maxphys - a pointer to an integer that will hold
 382  *                      the value for the system maxphys value.
 383  *              int *error - 0 means completed successfully
 384  *                           otherwise this indicates the errno value.
 385  *
 386  * RETURNS:     int     - 0 if maxphys not found
 387  *                      - 1 if maxphys is found
 388  */
 389 int
 390 fsgetmaxphys(int *maxphys, int *error) {
 391 
 392         int     gotit = 0;
 393         int     fp = open("/", O_RDONLY);
 394 
 395         *error = 0;
 396 
 397         /*
 398          * For some reason cannot open root as read only. Need a valid file
 399          * descriptor to call the ufs private ioctl. If this open failes,
 400          * just assume we cannot get maxphys in this case.
 401          */
 402         if (fp == -1) {
 403                 return (gotit);
 404         }
 405 
 406         if (ioctl(fp, _FIOGETMAXPHYS, maxphys) == -1) {
 407                 *error = errno;
 408                 (void) close(fp);
 409                 return (gotit);
 410         }
 411 
 412         (void) close(fp);
 413         gotit = 1;
 414         return (gotit);
 415 
 416 }
 417 
 418 /*
 419  * The below is limited support for zone-aware commands.
 420  */
 421 struct zone_summary {
 422         zoneid_t        zoneid;
 423         char            rootpath[MAXPATHLEN];
 424         size_t          rootpathlen;
 425 };
 426 
 427 struct zone_summary *
 428 fs_get_zone_summaries(void)
 429 {
 430         uint_t numzones = 0, oldnumzones = 0;
 431         uint_t i, j;
 432         zoneid_t *ids = NULL;
 433         struct zone_summary *summaries;
 434         zoneid_t myzoneid = getzoneid();
 435 
 436         for (;;) {
 437                 if (zone_list(ids, &numzones) < 0) {
 438                         perror("unable to retrieve list of zones");
 439                         if (ids != NULL)
 440                                 free(ids);
 441                         return (NULL);
 442                 }
 443                 if (numzones <= oldnumzones)
 444                         break;
 445                 if (ids != NULL)
 446                         free(ids);
 447                 ids = malloc(numzones * sizeof (*ids));
 448                 if (ids == NULL) {
 449                         perror("malloc failed");
 450                         return (NULL);
 451                 }
 452                 oldnumzones = numzones;
 453         }
 454 
 455         summaries = malloc((numzones + 1) * sizeof (*summaries));
 456         if (summaries == NULL) {
 457                 free(ids);
 458                 perror("malloc failed");
 459                 return (NULL);
 460         }
 461 
 462 
 463         for (i = 0, j = 0; i < numzones; i++) {
 464                 ssize_t len;
 465 
 466                 if (ids[i] == myzoneid)
 467                         continue;
 468                 len = zone_getattr(ids[i], ZONE_ATTR_ROOT,
 469                     summaries[j].rootpath, sizeof (summaries[j].rootpath));
 470                 if (len < 0) {
 471                         /*
 472                          * Zone must have gone away. Skip.
 473                          */
 474                         continue;
 475                 }
 476                 /*
 477                  * Adding a trailing '/' to the zone's rootpath allows us to
 478                  * use strncmp() to see if a given path resides within that
 479                  * zone.
 480                  *
 481                  * As an example, if the zone's rootpath is "/foo/root",
 482                  * "/foo/root/usr" resides within the zone, while
 483                  * "/foo/rootpath" doesn't.
 484                  */
 485                 (void) strlcat(summaries[j].rootpath, "/",
 486                     sizeof (summaries[j].rootpath));
 487                 summaries[j].rootpathlen = len;
 488                 summaries[j].zoneid = ids[i];
 489                 j++;
 490         }
 491         summaries[j].zoneid = -1;
 492         free(ids);
 493         return (summaries);
 494 }
 495 
 496 static zoneid_t
 497 fs_find_zone(const struct zone_summary *summaries, const char *mntpt)
 498 {
 499         uint_t i;
 500 
 501         for (i = 0; summaries[i].zoneid != -1; i++) {
 502                 if (strncmp(mntpt, summaries[i].rootpath,
 503                     summaries[i].rootpathlen) == 0)
 504                         return (summaries[i].zoneid);
 505         }
 506         /*
 507          * (-1) is the special token we return to the caller if the mount
 508          * wasn't found in any other mounts on the system.  This means it's
 509          * only visible to our zone.
 510          *
 511          * Odd choice of constant, I know, but it beats calling getzoneid() a
 512          * million times.
 513          */
 514         return (-1);
 515 }
 516 
 517 boolean_t
 518 fs_mount_in_other_zone(const struct zone_summary *summaries, const char *mntpt)
 519 {
 520         return (fs_find_zone(summaries, mntpt) != -1);
 521 }
 522 
 523 /*
 524  * List of standard options.
 525  */
 526 static const char *stdopts[] = {
 527         MNTOPT_RO,                      MNTOPT_RW,
 528         MNTOPT_SUID,                    MNTOPT_NOSUID,
 529         MNTOPT_DEVICES,                 MNTOPT_NODEVICES,
 530         MNTOPT_SETUID,                  MNTOPT_NOSETUID,
 531         MNTOPT_NBMAND,                  MNTOPT_NONBMAND,
 532         MNTOPT_EXEC,                    MNTOPT_NOEXEC,
 533 };
 534 
 535 #define NSTDOPT         (sizeof (stdopts) / sizeof (stdopts[0]))
 536 
 537 static int
 538 optindx(const char *opt)
 539 {
 540         int i;
 541 
 542         for (i = 0; i < NSTDOPT; i++) {
 543                 if (strcmp(opt, stdopts[i]) == 0)
 544                         return (i);
 545         }
 546         return (-1);
 547 }
 548 
 549 /*
 550  * INPUT:       filesystem option not recognized by the fs specific option
 551  *              parsing code.
 552  * OUTPUT:      True if and only if the option is one of the standard VFS
 553  *              layer options.
 554  */
 555 boolean_t
 556 fsisstdopt(const char *opt)
 557 {
 558         return (optindx(opt) != -1);
 559 }