/* * 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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ #include "lint.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tsd.h" #include #include static int getmntent_compat(FILE *fp, struct mnttab *mp); #define GETTOK_R(xx, ll, tmp)\ if ((mp->xx = (char *)strtok_r(ll, sepstr, tmp)) == NULL)\ return (MNT_TOOFEW);\ if (strcmp(mp->xx, dash) == 0)\ mp->xx = NULL #define DIFF(xx)\ (mrefp->xx != NULL && (mgetp->xx == NULL ||\ strcmp(mrefp->xx, mgetp->xx) != 0)) #define SDIFF(xx, typem, typer)\ ((mgetp->xx == NULL) || (stat64(mgetp->xx, &statb) == -1) ||\ ((statb.st_mode & S_IFMT) != typem) ||\ (statb.st_rdev != typer)) static const char sepstr[] = " \t\n"; static const char dash[] = "-"; typedef struct { size_t buflen; char *buf; } thread_data_t; static void destroy_thread_data(void *arg) { thread_data_t *thread_data = arg; if (thread_data->buf != NULL) { free(thread_data->buf); thread_data->buf = NULL; } thread_data->buflen = 0; } static char * getmntbuf(size_t size) { thread_data_t *thread_data; thread_data = tsdalloc(_T_GETMNTENT, sizeof (thread_data_t), destroy_thread_data); if (thread_data == NULL) return (NULL); if (thread_data->buf == NULL || thread_data->buflen < size) { if (thread_data->buf != NULL) free(thread_data->buf); thread_data->buflen = 0; if ((thread_data->buf = malloc(size)) == NULL) return (NULL); thread_data->buflen = size; } return (thread_data->buf); } static int getmntany_compat(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp) { int ret, bstat; mode_t bmode; dev_t brdev; struct stat64 statb; /* * Ignore specials that don't correspond to real devices to avoid doing * unnecessary lookups in stat64(). */ if (mrefp->mnt_special && mrefp->mnt_special[0] == '/' && stat64(mrefp->mnt_special, &statb) == 0 && ((bmode = (statb.st_mode & S_IFMT)) == S_IFBLK || bmode == S_IFCHR)) { bstat = 1; brdev = statb.st_rdev; } else { bstat = 0; } while ((ret = getmntent_compat(fp, mgetp)) == 0 && ((bstat == 0 && DIFF(mnt_special)) || (bstat == 1 && SDIFF(mnt_special, bmode, brdev)) || DIFF(mnt_mountp) || DIFF(mnt_fstype) || DIFF(mnt_mntopts) || DIFF(mnt_time))) ; return (ret); } int getmntany(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp) { struct mntentbuf embuf; char *copyp, *bufp; int ret; /* * We collect all of the text strings pointed to by members of the * user's preferences struct into a single buffer. At the same time * populate the members of the results struct to point to the * corresponding words. We then ask the kernel to figure out the * rest; if this is a non-mntfs file then we handover to * getmntany_compat(). */ if ((copyp = bufp = getmntbuf(MNT_LINE_MAX)) == NULL) { errno = ENOMEM; return (-1); } bzero(mgetp, sizeof (struct mnttab)); if (mrefp->mnt_special) { mgetp->mnt_special = copyp; copyp += snprintf(mgetp->mnt_special, MNT_LINE_MAX, "%s", mrefp->mnt_special) + 1; } if (mrefp->mnt_mountp) { mgetp->mnt_mountp = copyp; copyp += snprintf(mgetp->mnt_mountp, bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mountp) + 1; } if (mrefp->mnt_fstype) { mgetp->mnt_fstype = copyp; copyp += snprintf(mgetp->mnt_fstype, bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_fstype) + 1; } if (mrefp->mnt_mntopts) { mgetp->mnt_mntopts = copyp; copyp += snprintf(mgetp->mnt_mntopts, bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mntopts) + 1; } if (mrefp->mnt_time) { mgetp->mnt_time = copyp; (void) snprintf(mgetp->mnt_time, bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_time); } embuf.mbuf_emp = (struct extmnttab *)mgetp; embuf.mbuf_bufsize = MNT_LINE_MAX; embuf.mbuf_buf = bufp; switch (ret = ioctl(fileno(fp), MNTIOC_GETMNTANY, &embuf)) { case 0: /* Success. */ return (0); case MNTFS_EOF: return (-1); case MNTFS_TOOLONG: return (MNT_TOOLONG); default: /* A failure of some kind. */ if (errno == ENOTTY) return (getmntany_compat(fp, mgetp, mrefp)); else return (ret); } } /* * Common code for getmntent() and getextmntent(). * * These functions serve to populate a structure supplied by the user. Common * to both struct mnttab and struct extmnttab is a set of pointers to the * individual text fields that form an entry in /etc/mnttab. We arrange for the * text itself to be stored in some thread-local storage, and for the kernel to * populate both this buffer and the structure directly. * * If getmntent() passes a file that isn't provided by mntfs then we assume that * it is a simple text file and give it to getmntent_compat() to parse. For * getextmntent() we give up; it requires major and minor numbers that only the * kernel can provide. */ static int getmntent_common(FILE *fp, struct extmnttab *emp, int command) { struct mntentbuf embuf; static size_t bufsize = MNT_LINE_MAX; int ret; embuf.mbuf_emp = emp; embuf.mbuf_bufsize = bufsize; if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) { errno = ENOMEM; return (-1); } while ((ret = ioctl(fileno(fp), command, &embuf)) == MNTFS_TOOLONG) { /* The buffer wasn't large enough. */ (void) atomic_swap_ulong((unsigned long *)&bufsize, 2 * embuf.mbuf_bufsize); embuf.mbuf_bufsize = bufsize; if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) { errno = ENOMEM; return (-1); } } switch (ret) { case 0: /* * We were successful, but we may have to enforce getmntent()'s * documented limit on the line length. */ if (command == MNTIOC_GETMNTENT && (emp->mnt_time + strlen(emp->mnt_time) + 1 - emp->mnt_special > MNT_LINE_MAX)) return (MNT_TOOLONG); else return (0); case MNTFS_EOF: /* EOF. */ return (-1); default: /* A non-mntfs file. */ if (command == MNTIOC_GETMNTENT) return (getmntent_compat(fp, (struct mnttab *)emp)); else return (ret); } } int getmntent(FILE *fp, struct mnttab *mp) { return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT)); } int getextmntent(FILE *fp, struct extmnttab *emp, size_t len __unused) { return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT)); } char * mntopt(char **p) { char *cp = *p; char *retstr; while (*cp && isspace(*cp)) cp++; retstr = cp; while (*cp && *cp != ',') cp++; if (*cp) { *cp = '\0'; cp++; } *p = cp; return (retstr); } char * hasmntopt(struct mnttab *mnt, char *opt) { char tmpopts[MNT_LINE_MAX]; char *f, *opts = tmpopts; size_t len; if (mnt->mnt_mntopts == NULL) return (NULL); (void) strcpy(opts, mnt->mnt_mntopts); len = strlen(opt); f = mntopt(&opts); for (; *f; f = mntopt(&opts)) { /* * Match only complete substrings. For options * which use a delimiter (such as 'retry=3'), * treat the delimiter as the end of the substring. */ if (strncmp(opt, f, len) == 0 && (f[len] == '\0' || !isalnum(f[len]))) return (f - tmpopts + mnt->mnt_mntopts); } return (NULL); } void resetmnttab(FILE *fp) { rewind(fp); } /* * Compatibility for non-mntfs files. For backwards compatibility, we continue * to have to support this broken interface. Note that getextmntent() has * always failed when using a file other than /etc/mnttab, because it relies on * an ioctl() call. */ static int getaline(char *lp, FILE *fp) { char *cp; while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) { if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n') return (MNT_TOOLONG); for (cp = lp; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp != '#' && *cp != '\n') return (0); } return (-1); } static int getmntent_compat(FILE *fp, struct mnttab *mp) { int ret; char *tmp; char *line = getmntbuf(MNT_LINE_MAX); if (line == NULL) { errno = ENOMEM; return (-1); } /* skip leading spaces and comments */ if ((ret = getaline(line, fp)) != 0) return (ret); /* split up each field */ GETTOK_R(mnt_special, line, &tmp); GETTOK_R(mnt_mountp, NULL, &tmp); GETTOK_R(mnt_fstype, NULL, &tmp); GETTOK_R(mnt_mntopts, NULL, &tmp); GETTOK_R(mnt_time, NULL, &tmp); /* check for too many fields */ if (strtok_r(NULL, sepstr, &tmp) != NULL) return (MNT_TOOMANY); return (0); }