OpenSolaris_b135/cmd/fs.d/fslib.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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	<stdio.h>
#include	<stdarg.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<libintl.h>
#include	<string.h>
#include	<fcntl.h>
#include	<errno.h>
#include	<syslog.h>
#include	<alloca.h>
#include	<sys/vfstab.h>
#include	<sys/mnttab.h>
#include	<sys/mntent.h>
#include	<sys/mount.h>
#include	<sys/filio.h>
#include	<sys/fs/ufs_filio.h>
#include	<sys/stat.h>
#include	<sys/param.h>
#include	<zone.h>
#include	<signal.h>
#include	<strings.h>
#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 "=<value>" 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 "=<value>", 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,
};

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