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