/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fslib.h" /* LINTLIBRARY */ #define BUFLEN 256 #define TIME_MAX 16 /* * Reads all of the entries from the in-kernel mnttab, and returns the * linked list of the entries. */ mntlist_t * fsgetmntlist(void) { FILE *mfp; mntlist_t *mntl; char buf[BUFLEN]; if ((mfp = fopen(MNTTAB, "r")) == NULL) { (void) snprintf(buf, BUFLEN, "fsgetmntlist: fopen %s", MNTTAB); perror(buf); return (NULL); } mntl = fsmkmntlist(mfp); (void) fclose(mfp); return (mntl); } static struct extmnttab zmnttab = { 0 }; struct extmnttab * fsdupmnttab(struct extmnttab *mnt) { struct extmnttab *new; new = (struct extmnttab *)malloc(sizeof (*new)); if (new == NULL) goto alloc_failed; *new = zmnttab; /* * Allocate an extra byte for the mountpoint * name in case a space needs to be added. */ new->mnt_mountp = (char *)malloc(strlen(mnt->mnt_mountp) + 2); if (new->mnt_mountp == NULL) goto alloc_failed; (void) strcpy(new->mnt_mountp, mnt->mnt_mountp); if ((new->mnt_special = strdup(mnt->mnt_special)) == NULL) goto alloc_failed; if ((new->mnt_fstype = strdup(mnt->mnt_fstype)) == NULL) goto alloc_failed; if (mnt->mnt_mntopts != NULL) if ((new->mnt_mntopts = strdup(mnt->mnt_mntopts)) == NULL) goto alloc_failed; if (mnt->mnt_time != NULL) if ((new->mnt_time = strdup(mnt->mnt_time)) == NULL) goto alloc_failed; new->mnt_major = mnt->mnt_major; new->mnt_minor = mnt->mnt_minor; return (new); alloc_failed: (void) fprintf(stderr, gettext("fsdupmnttab: Out of memory\n")); fsfreemnttab(new); return (NULL); } /* * Free a single mnttab structure */ void fsfreemnttab(struct extmnttab *mnt) { if (mnt) { if (mnt->mnt_special) free(mnt->mnt_special); if (mnt->mnt_mountp) free(mnt->mnt_mountp); if (mnt->mnt_fstype) free(mnt->mnt_fstype); if (mnt->mnt_mntopts) free(mnt->mnt_mntopts); if (mnt->mnt_time) free(mnt->mnt_time); free(mnt); } } void fsfreemntlist(mntlist_t *mntl) { mntlist_t *mntl_tmp; while (mntl) { fsfreemnttab(mntl->mntl_mnt); mntl_tmp = mntl; mntl = mntl->mntl_next; free(mntl_tmp); } } /* * Read the mnttab file and return it as a list of mnttab structs. * Returns NULL if there was a memory failure. */ mntlist_t * fsmkmntlist(FILE *mfp) { struct extmnttab mnt; mntlist_t *mhead, *mtail; int ret; mhead = mtail = NULL; resetmnttab(mfp); while ((ret = getextmntent(mfp, &mnt, sizeof (struct extmnttab))) != -1) { mntlist_t *mp; if (ret != 0) /* bad entry */ continue; mp = (mntlist_t *)malloc(sizeof (*mp)); if (mp == NULL) goto alloc_failed; if (mhead == NULL) mhead = mp; else mtail->mntl_next = mp; mtail = mp; mp->mntl_next = NULL; mp->mntl_flags = 0; if ((mp->mntl_mnt = fsdupmnttab(&mnt)) == NULL) goto alloc_failed; } return (mhead); alloc_failed: fsfreemntlist(mhead); return (NULL); } /* * Return the last entry that matches mntin's special * device and/or mountpt. * Helps to be robust here, so we check for NULL pointers. */ mntlist_t * fsgetmlast(mntlist_t *ml, struct mnttab *mntin) { mntlist_t *delete = NULL; for (; ml; ml = ml->mntl_next) { if (mntin->mnt_mountp && mntin->mnt_special) { /* * match if and only if both are equal. */ if ((strcmp(ml->mntl_mnt->mnt_mountp, mntin->mnt_mountp) == 0) && (strcmp(ml->mntl_mnt->mnt_special, mntin->mnt_special) == 0)) delete = ml; } else if (mntin->mnt_mountp) { if (strcmp(ml->mntl_mnt->mnt_mountp, mntin->mnt_mountp) == 0) delete = ml; } else if (mntin->mnt_special) { if (strcmp(ml->mntl_mnt->mnt_special, mntin->mnt_special) == 0) delete = ml; } } return (delete); } /* * Returns the mountlevel of the pathname in cp. As examples, * / => 1, /bin => 2, /bin/ => 2, ////bin////ls => 3, sdf => 0, etc... */ int fsgetmlevel(char *cp) { int mlevel; char *cp1; if (cp == NULL || *cp == NULL || *cp != '/') return (0); /* this should never happen */ mlevel = 1; /* root (/) is the minimal case */ for (cp1 = cp + 1; *cp1; cp++, cp1++) if (*cp == '/' && *cp1 != '/') /* "///" counts as 1 */ mlevel++; return (mlevel); } /* * Returns non-zero if string s is a member of the strings in ps. */ int fsstrinlist(const char *s, const char **ps) { const char *cp; cp = *ps; while (cp) { if (strcmp(s, cp) == 0) return (1); ps++; cp = *ps; } return (0); } static char *empty_opt_vector[] = { NULL }; /* * Compare the mount options that were requested by the caller to * the options actually supported by the file system. If any requested * options are not supported, print a warning message. * * WARNING: this function modifies the string pointed to by * the requested_opts argument. * * Arguments: * requested_opts - the string containing the requested options. * actual_opts - the string returned by mount(2), which lists the * options actually supported. It is normal for this * string to contain more options than the requested options. * (The actual options may contain the default options, which * may not have been included in the requested options.) * special - device being mounted (only used in error messages). * mountp - mount point (only used in error messages). */ void cmp_requested_to_actual_options(char *requested_opts, char *actual_opts, char *special, char *mountp) { char *option_ptr, *actopt, *equalptr; int found; char *actual_opt_hold, *bufp; if (requested_opts == NULL) return; bufp = alloca(strlen(actual_opts) + 1); while (*requested_opts != '\0') { (void) getsubopt(&requested_opts, empty_opt_vector, &option_ptr); /* * Truncate any "=" string from the end of * the option. */ if ((equalptr = strchr(option_ptr, '=')) != NULL) *equalptr = '\0'; if (*option_ptr == '\0') continue; /* * Whilst we don't need this option to perform a lofi * mount, let's not be mendacious enough to complain * about it. */ if (strcmp(option_ptr, "loop") == 0) continue; /* * Search for the requested option in the list of options * actually supported. */ found = 0; /* * Need to use a copy of actual_opts because getsubopt * is destructive and we need to scan the actual_opts * string more than once. * * We also need to reset actual_opt_hold to the * beginning of the buffer because getsubopt changes * actual_opt_hold (the pointer). */ actual_opt_hold = bufp; if (actual_opts != NULL) (void) strcpy(actual_opt_hold, actual_opts); else *actual_opt_hold = '\0'; while (*actual_opt_hold != '\0') { (void) getsubopt(&actual_opt_hold, empty_opt_vector, &actopt); /* Truncate the "=", if any. */ if ((equalptr = strchr(actopt, '=')) != NULL) *equalptr = '\0'; if ((strcmp(option_ptr, actopt)) == 0) { found = 1; break; } } if (found == 0) { /* * That we're ignoring the option is always * truthful; the old message that the option * was unknown is often not correct. */ (void) fprintf(stderr, gettext( "mount: %s on %s - WARNING ignoring option " "\"%s\"\n"), special, mountp, option_ptr); } } } /* * FUNCTION: fsgetmaxphys(int *, int *) * * INPUT: int *maxphys - a pointer to an integer that will hold * the value for the system maxphys value. * int *error - 0 means completed successfully * otherwise this indicates the errno value. * * RETURNS: int - 0 if maxphys not found * - 1 if maxphys is found */ int fsgetmaxphys(int *maxphys, int *error) { int gotit = 0; int fp = open("/", O_RDONLY); *error = 0; /* * For some reason cannot open root as read only. Need a valid file * descriptor to call the ufs private ioctl. If this open failes, * just assume we cannot get maxphys in this case. */ if (fp == -1) { return (gotit); } if (ioctl(fp, _FIOGETMAXPHYS, maxphys) == -1) { *error = errno; (void) close(fp); return (gotit); } (void) close(fp); gotit = 1; return (gotit); } /* * The below is limited support for zone-aware commands. */ struct zone_summary { zoneid_t zoneid; char rootpath[MAXPATHLEN]; size_t rootpathlen; }; struct zone_summary * fs_get_zone_summaries(void) { uint_t numzones = 0, oldnumzones = 0; uint_t i, j; zoneid_t *ids = NULL; struct zone_summary *summaries; zoneid_t myzoneid = getzoneid(); for (;;) { if (zone_list(ids, &numzones) < 0) { perror("unable to retrieve list of zones"); if (ids != NULL) free(ids); return (NULL); } if (numzones <= oldnumzones) break; if (ids != NULL) free(ids); ids = malloc(numzones * sizeof (*ids)); if (ids == NULL) { perror("malloc failed"); return (NULL); } oldnumzones = numzones; } summaries = malloc((numzones + 1) * sizeof (*summaries)); if (summaries == NULL) { free(ids); perror("malloc failed"); return (NULL); } for (i = 0, j = 0; i < numzones; i++) { ssize_t len; if (ids[i] == myzoneid) continue; len = zone_getattr(ids[i], ZONE_ATTR_ROOT, summaries[j].rootpath, sizeof (summaries[j].rootpath)); if (len < 0) { /* * Zone must have gone away. Skip. */ continue; } /* * Adding a trailing '/' to the zone's rootpath allows us to * use strncmp() to see if a given path resides within that * zone. * * As an example, if the zone's rootpath is "/foo/root", * "/foo/root/usr" resides within the zone, while * "/foo/rootpath" doesn't. */ (void) strlcat(summaries[j].rootpath, "/", sizeof (summaries[j].rootpath)); summaries[j].rootpathlen = len; summaries[j].zoneid = ids[i]; j++; } summaries[j].zoneid = -1; free(ids); return (summaries); } static zoneid_t fs_find_zone(const struct zone_summary *summaries, const char *mntpt) { uint_t i; for (i = 0; summaries[i].zoneid != -1; i++) { if (strncmp(mntpt, summaries[i].rootpath, summaries[i].rootpathlen) == 0) return (summaries[i].zoneid); } /* * (-1) is the special token we return to the caller if the mount * wasn't found in any other mounts on the system. This means it's * only visible to our zone. * * Odd choice of constant, I know, but it beats calling getzoneid() a * million times. */ return (-1); } boolean_t fs_mount_in_other_zone(const struct zone_summary *summaries, const char *mntpt) { return (fs_find_zone(summaries, mntpt) != -1); } /* * List of standard options. */ static const char *stdopts[] = { MNTOPT_RO, MNTOPT_RW, MNTOPT_SUID, MNTOPT_NOSUID, MNTOPT_DEVICES, MNTOPT_NODEVICES, MNTOPT_SETUID, MNTOPT_NOSETUID, MNTOPT_NBMAND, MNTOPT_NONBMAND, MNTOPT_EXEC, MNTOPT_NOEXEC, MNTOPT_FOLLOW, MNTOPT_NOFOLLOW, }; #define NSTDOPT (sizeof (stdopts) / sizeof (stdopts[0])) static int optindx(const char *opt) { int i; for (i = 0; i < NSTDOPT; i++) { if (strcmp(opt, stdopts[i]) == 0) return (i); } return (-1); } /* * INPUT: filesystem option not recognized by the fs specific option * parsing code. * OUTPUT: True if and only if the option is one of the standard VFS * layer options. */ boolean_t fsisstdopt(const char *opt) { return (optindx(opt) != -1); }