Home | History | Annotate | Download | only in gen
      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 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #include "lint.h"
     31 #include <mtlib.h>
     32 #include <stdio.h>
     33 #include <sys/types.h>
     34 #include <sys/stat.h>
     35 #include <sys/mnttab.h>
     36 #include <sys/mntio.h>
     37 #include <string.h>
     38 #include <ctype.h>
     39 #include <errno.h>
     40 #include <stdlib.h>
     41 #include <thread.h>
     42 #include <synch.h>
     43 #include <libc.h>
     44 #include <unistd.h>
     45 #include "tsd.h"
     46 #include <atomic.h>
     47 #include <strings.h>
     48 
     49 static int getmntent_compat(FILE *fp, struct mnttab *mp);
     50 
     51 #define	GETTOK_R(xx, ll, tmp)\
     52 	if ((mp->xx = (char *)strtok_r(ll, sepstr, tmp)) == NULL)\
     53 		return (MNT_TOOFEW);\
     54 	if (strcmp(mp->xx, dash) == 0)\
     55 		mp->xx = NULL
     56 
     57 #define	DIFF(xx)\
     58 	(mrefp->xx != NULL && (mgetp->xx == NULL ||\
     59 	    strcmp(mrefp->xx, mgetp->xx) != 0))
     60 
     61 #define	SDIFF(xx, typem, typer)\
     62 	((mgetp->xx == NULL) || (stat64(mgetp->xx, &statb) == -1) ||\
     63 	((statb.st_mode & S_IFMT) != typem) ||\
     64 	    (statb.st_rdev != typer))
     65 
     66 static const char	sepstr[] = " \t\n";
     67 static const char	dash[] = "-";
     68 
     69 typedef struct {
     70 	size_t	buflen;
     71 	char	*buf;
     72 } thread_data_t;
     73 
     74 static void
     75 destroy_thread_data(void *arg)
     76 {
     77 	thread_data_t *thread_data = arg;
     78 
     79 	if (thread_data->buf != NULL) {
     80 		free(thread_data->buf);
     81 		thread_data->buf = NULL;
     82 	}
     83 	thread_data->buflen = 0;
     84 }
     85 
     86 static char *
     87 getmntbuf(size_t size)
     88 {
     89 	thread_data_t *thread_data;
     90 
     91 	thread_data = tsdalloc(_T_GETMNTENT,
     92 	    sizeof (thread_data_t), destroy_thread_data);
     93 	if (thread_data == NULL)
     94 		return (NULL);
     95 	if (thread_data->buf == NULL ||
     96 	    thread_data->buflen < size) {
     97 		if (thread_data->buf != NULL)
     98 			free(thread_data->buf);
     99 		thread_data->buflen = 0;
    100 		if ((thread_data->buf = malloc(size)) == NULL)
    101 			return (NULL);
    102 		thread_data->buflen = size;
    103 	}
    104 	return (thread_data->buf);
    105 }
    106 
    107 static int
    108 getmntany_compat(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
    109 {
    110 	int	ret, bstat;
    111 	mode_t	bmode;
    112 	dev_t	brdev;
    113 	struct stat64	statb;
    114 
    115 	/*
    116 	 * Ignore specials that don't correspond to real devices to avoid doing
    117 	 * unnecessary lookups in stat64().
    118 	 */
    119 	if (mrefp->mnt_special && mrefp->mnt_special[0] == '/' &&
    120 	    stat64(mrefp->mnt_special, &statb) == 0 &&
    121 	    ((bmode = (statb.st_mode & S_IFMT)) == S_IFBLK ||
    122 	    bmode == S_IFCHR)) {
    123 		bstat = 1;
    124 		brdev = statb.st_rdev;
    125 	} else {
    126 		bstat = 0;
    127 	}
    128 
    129 	while ((ret = getmntent_compat(fp, mgetp)) == 0 &&
    130 	    ((bstat == 0 && DIFF(mnt_special)) ||
    131 	    (bstat == 1 && SDIFF(mnt_special, bmode, brdev)) ||
    132 	    DIFF(mnt_mountp) ||
    133 	    DIFF(mnt_fstype) ||
    134 	    DIFF(mnt_mntopts) ||
    135 	    DIFF(mnt_time)))
    136 		;
    137 
    138 	return (ret);
    139 }
    140 
    141 int
    142 getmntany(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
    143 {
    144 	struct mntentbuf embuf;
    145 	char *copyp, *bufp;
    146 	int ret;
    147 
    148 
    149 	/*
    150 	 * We collect all of the text strings pointed to by members of the
    151 	 * user's preferences struct into a single buffer. At the same time
    152 	 * populate the members of the results struct to point to the
    153 	 * corresponding words. We then ask the kernel to figure out the
    154 	 * rest; if this is a non-mntfs file then we handover to
    155 	 * getmntany_compat().
    156 	 */
    157 	if ((copyp = bufp = getmntbuf(MNT_LINE_MAX)) == NULL) {
    158 		errno = ENOMEM;
    159 		return (-1);
    160 	}
    161 	bzero(mgetp, sizeof (struct mnttab));
    162 	if (mrefp->mnt_special) {
    163 		mgetp->mnt_special = copyp;
    164 		copyp += snprintf(mgetp->mnt_special, MNT_LINE_MAX, "%s",
    165 		    mrefp->mnt_special) + 1;
    166 	}
    167 	if (mrefp->mnt_mountp) {
    168 		mgetp->mnt_mountp = copyp;
    169 		copyp += snprintf(mgetp->mnt_mountp,
    170 		    bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mountp) + 1;
    171 	}
    172 	if (mrefp->mnt_fstype) {
    173 		mgetp->mnt_fstype = copyp;
    174 		copyp += snprintf(mgetp->mnt_fstype,
    175 		    bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_fstype) + 1;
    176 	}
    177 	if (mrefp->mnt_mntopts) {
    178 		mgetp->mnt_mntopts = copyp;
    179 		copyp += snprintf(mgetp->mnt_mntopts,
    180 		    bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mntopts) + 1;
    181 	}
    182 	if (mrefp->mnt_time) {
    183 		mgetp->mnt_time = copyp;
    184 		(void) snprintf(mgetp->mnt_time, bufp + MNT_LINE_MAX - copyp,
    185 		    "%s", mrefp->mnt_time);
    186 	}
    187 
    188 	embuf.mbuf_emp = (struct extmnttab *)mgetp;
    189 	embuf.mbuf_bufsize = MNT_LINE_MAX;
    190 	embuf.mbuf_buf = bufp;
    191 
    192 	switch (ret = ioctl(fileno(fp), MNTIOC_GETMNTANY, &embuf)) {
    193 	case 0:
    194 		/* Success. */
    195 		return (0);
    196 	case MNTFS_EOF:
    197 		return (-1);
    198 	case MNTFS_TOOLONG:
    199 		return (MNT_TOOLONG);
    200 	default:
    201 		/* A failure of some kind. */
    202 		if (errno == ENOTTY)
    203 			return (getmntany_compat(fp, mgetp, mrefp));
    204 		else
    205 			return (ret);
    206 	}
    207 }
    208 
    209 /*
    210  * Common code for getmntent() and getextmntent().
    211  *
    212  * These functions serve to populate a structure supplied by the user. Common
    213  * to both struct mnttab and struct extmnttab is a set of pointers to the
    214  * individual text fields that form an entry in /etc/mnttab. We arrange for the
    215  * text itself to be stored in some thread-local storage, and for the kernel to
    216  * populate both this buffer and the structure directly.
    217  *
    218  * If getmntent() passes a file that isn't provided by mntfs then we assume that
    219  * it is a simple text file and give it to getmntent_compat() to parse. For
    220  * getextmntent() we give up; it requires major and minor numbers that only the
    221  * kernel can provide.
    222  */
    223 static int
    224 getmntent_common(FILE *fp, struct extmnttab *emp, int command)
    225 {
    226 	struct mntentbuf embuf;
    227 	static size_t bufsize = MNT_LINE_MAX;
    228 	int ret;
    229 
    230 	embuf.mbuf_emp = emp;
    231 	embuf.mbuf_bufsize = bufsize;
    232 	if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
    233 		errno = ENOMEM;
    234 		return (-1);
    235 	}
    236 
    237 	while ((ret = ioctl(fileno(fp), command, &embuf)) == MNTFS_TOOLONG) {
    238 		/* The buffer wasn't large enough. */
    239 		(void) atomic_swap_ulong((unsigned long *)&bufsize,
    240 		    2 * embuf.mbuf_bufsize);
    241 		embuf.mbuf_bufsize = bufsize;
    242 		if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
    243 			errno = ENOMEM;
    244 			return (-1);
    245 		}
    246 	}
    247 
    248 	switch (ret) {
    249 	case 0:
    250 		/*
    251 		 * We were successful, but we may have to enforce getmntent()'s
    252 		 * documented limit on the line length.
    253 		 */
    254 		if (command == MNTIOC_GETMNTENT &&
    255 		    (emp->mnt_time + strlen(emp->mnt_time) + 1 -
    256 		    emp->mnt_special > MNT_LINE_MAX))
    257 			return (MNT_TOOLONG);
    258 		else
    259 			return (0);
    260 	case MNTFS_EOF:
    261 		/* EOF. */
    262 		return (-1);
    263 	default:
    264 		/* A non-mntfs file. */
    265 		if (command == MNTIOC_GETMNTENT)
    266 			return (getmntent_compat(fp, (struct mnttab *)emp));
    267 		else
    268 			return (ret);
    269 	}
    270 }
    271 
    272 int
    273 getmntent(FILE *fp, struct mnttab *mp)
    274 {
    275 	return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT));
    276 }
    277 
    278 /*ARGSUSED*/
    279 int
    280 getextmntent(FILE *fp, struct extmnttab *emp, size_t len)
    281 {
    282 	return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT));
    283 }
    284 
    285 char *
    286 mntopt(char **p)
    287 {
    288 	char *cp = *p;
    289 	char *retstr;
    290 
    291 	while (*cp && isspace(*cp))
    292 		cp++;
    293 
    294 	retstr = cp;
    295 	while (*cp && *cp != ',')
    296 		cp++;
    297 
    298 	if (*cp) {
    299 		*cp = '\0';
    300 		cp++;
    301 	}
    302 
    303 	*p = cp;
    304 	return (retstr);
    305 }
    306 
    307 char *
    308 hasmntopt(struct mnttab *mnt, char *opt)
    309 {
    310 	char tmpopts[MNT_LINE_MAX];
    311 	char *f, *opts = tmpopts;
    312 	size_t	len;
    313 
    314 	if (mnt->mnt_mntopts == NULL)
    315 		return (NULL);
    316 	(void) strcpy(opts, mnt->mnt_mntopts);
    317 	len = strlen(opt);
    318 	f = mntopt(&opts);
    319 	for (; *f; f = mntopt(&opts)) {
    320 		/*
    321 		 * Match only complete substrings. For options
    322 		 * which use a delimiter (such as 'retry=3'),
    323 		 * treat the delimiter as the end of the substring.
    324 		 */
    325 		if (strncmp(opt, f, len) == 0 &&
    326 		    (f[len] == '\0' || !isalnum(f[len])))
    327 			return (f - tmpopts + mnt->mnt_mntopts);
    328 	}
    329 	return (NULL);
    330 }
    331 
    332 void
    333 resetmnttab(FILE *fp)
    334 {
    335 	rewind(fp);
    336 }
    337 
    338 /*
    339  * Compatibility for non-mntfs files.  For backwards compatibility, we continue
    340  * to have to support this broken interface.  Note that getextmntent() has
    341  * always failed when using a file other than /etc/mnttab, because it relies on
    342  * an ioctl() call.
    343  */
    344 static int
    345 getline(char *lp, FILE *fp)
    346 {
    347 	char	*cp;
    348 
    349 	while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) {
    350 		if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n')
    351 			return (MNT_TOOLONG);
    352 
    353 		for (cp = lp; *cp == ' ' || *cp == '\t'; cp++)
    354 			;
    355 
    356 		if (*cp != '#' && *cp != '\n')
    357 			return (0);
    358 	}
    359 	return (-1);
    360 }
    361 
    362 static int
    363 getmntent_compat(FILE *fp, struct mnttab *mp)
    364 {
    365 	int	ret;
    366 	char	*tmp;
    367 	char	*line = getmntbuf(MNT_LINE_MAX);
    368 
    369 	if (line == NULL) {
    370 		errno = ENOMEM;
    371 		return (-1);
    372 	}
    373 
    374 	/* skip leading spaces and comments */
    375 	if ((ret = getline(line, fp)) != 0)
    376 		return (ret);
    377 
    378 	/* split up each field */
    379 	GETTOK_R(mnt_special, line, &tmp);
    380 	GETTOK_R(mnt_mountp, NULL, &tmp);
    381 	GETTOK_R(mnt_fstype, NULL, &tmp);
    382 	GETTOK_R(mnt_mntopts, NULL, &tmp);
    383 	GETTOK_R(mnt_time, NULL, &tmp);
    384 
    385 	/* check for too many fields */
    386 	if (strtok_r(NULL, sepstr, &tmp) != NULL)
    387 		return (MNT_TOOMANY);
    388 
    389 	return (0);
    390 }
    391