usr/src/lib/libc/port/gen/getmntent.c
author Jon Tibble <meths@btinternet.com>
Thu, 09 Dec 2010 22:32:39 +0100
changeset 13255 4afa820d78b9
parent 13105 48f2dbca79a2
permissions -rw-r--r--
298 SPARC build fails in smt_pause.o 478 Build needs fixing for pkgdepend flag day Reviewed by: [email protected] Reviewed by: [email protected] Reviewed by: [email protected] Approved by: [email protected]

/*
 * 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 <mtlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <thread.h>
#include <synch.h>
#include <libc.h>
#include <unistd.h>
#include "tsd.h"
#include <atomic.h>
#include <strings.h>

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));
}

/*ARGSUSED*/
int
getextmntent(FILE *fp, struct extmnttab *emp, size_t len)
{
	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);
}