OpenSolaris_b135/cmd/allocate/allocate3.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <auth_attr.h>
#include <auth_list.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <bsm/devices.h>
#include <sys/acl.h>
#include <tsol/label.h>
#include <syslog.h>
#include <limits.h>
#include <user_attr.h>
#include <secdb.h>
#include <sys/mkdev.h>
#include <sys/acl.h>
#include <sys/file.h>
#include <sys/procfs.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <utime.h>
#include <libgen.h>
#include <zone.h>
#include <nss_dbdefs.h>
#include <bsm/devalloc.h>
#include <libdevinfo.h>
#include "allocate.h"

extern void print_error(int, char *);

#if	defined(DEBUG) || defined(lint)
#define	dprintf(s, a) (void) fprintf(stderr, s, a)
#define	dperror(s) perror(s)
#else	/* !DEBUG */
#define	dprintf(s, a)	0
#define	dperror(s)	0
#endif	/* DEBUG */

#define	DEV_ERRORED(sbuf)	(((sbuf).st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
#define	DEV_ALLOCATED(sbuf)	((sbuf).st_uid != DA_UID || \
			!(((sbuf).st_mode & ~S_IFMT) == DEALLOC_MODE || \
			DEV_ERRORED(sbuf)))

#define	ALLOC_CLEAN		"-A"
#define	DEALLOC_CLEAN		"-D"
#define	DAC_DIR			"/etc/security/dev"
#define	DEVICE_AUTH_SEPARATOR	","
#define	LOCALDEVICE		"/dev/console"
#define	PROCFS			"/proc/"
#define	SFF_NO_ERROR		0x1

#define	ALLOC_BY_NONE		-1
#define	CHECK_DRANGE		1
#define	CHECK_URANGE		2
#define	CHECK_ZLABEL		3

extern void audit_allocate_list(char *);
extern void audit_allocate_device(char *);

extern int	system_labeled;
extern char	*newenv[];

struct state_file {
	int	sf_flags;
	char	sf_path[MAXPATHLEN];
};

struct file_info {
	struct stat	fi_stat;
	char		*fi_message;
};

struct zone_path {
	int	count;
	char	**path;
};

struct dev_names {
	char **dnames;
};

static int _dev_file_name(struct state_file *, devmap_t *);
static int lock_dev(char *, struct stat *);
static int _check_label(devalloc_t *, char *, uid_t, int);
static int create_znode(char *, struct zone_path *, devmap_t *);
static int remove_znode(char *, devmap_t *);
static int update_device(char **, char *, int);

/*
 * checks if the invoking user is local to the device
 */
/*ARGSUSED*/
int
_is_local(uid_t uid)
{
	struct stat	statbuf;

	if (stat(LOCALDEVICE, &statbuf) == 0 &&
	    statbuf.st_uid == uid)
		return (1);

	return (0);
}

/*
 * Checks if the user with the specified uid has the specified authorization
 */
int
_is_authorized(char *auths, uid_t uid)
{
	char		*dcp, *authlist, *lasts;
	char		pw_buf[NSS_BUFLEN_PASSWD];
	struct passwd	pw_ent;

	/*
	 * first, the easy cases
	 */
	if (strcmp(auths, "@") == 0)
		return (1);
	if (strcmp(auths, "*") == 0)
		return (ALLOC_BY_NONE);
	if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
		return (0);
	if (strpbrk(auths, DEVICE_AUTH_SEPARATOR) == NULL)
		return (chkauthattr(auths, pw_ent.pw_name));
	authlist = strdup(auths);
	if (authlist == NULL)
		return (0);
	for (dcp = authlist;
	    (dcp = strtok_r(dcp, DEVICE_AUTH_SEPARATOR, &lasts)) != NULL;
	    dcp = NULL) {
		if (chkauthattr(dcp, pw_ent.pw_name))
			break;
	}
	free(authlist);

	return (dcp != NULL);
}

/*
 * Checks if the specified user has authorization for the device
 */
int
_is_dev_authorized(devalloc_t *da, uid_t uid)
{
	int	ares;
	char	*auth_list, *dcp, *subauth = NULL;

	auth_list = da->da_devauth;
	if (auth_list == NULL)
		return (0);
	dcp = strpbrk(auth_list, KV_TOKEN_DELIMIT);
	if (dcp == NULL)
		return (_is_authorized(auth_list, uid));
	if (_is_local(uid)) {
		/* the local authorization is before the separator */
		ares = dcp - auth_list;
		subauth = malloc(ares + 1);
		if (subauth == NULL)
			return (0);
		(void) strlcpy(subauth, auth_list, (ares + 1));
		auth_list = subauth;
	} else
		auth_list = dcp + 1;
	ares = _is_authorized(auth_list, uid);
	if (subauth != NULL)
		free(subauth);

	return (ares);
}

int
check_devs(devmap_t *dm)
{
	int	status = 0;
	char	**file;

	if (dm->dmap_devarray == NULL)
		return (NODMAPERR);
	for (file = dm->dmap_devarray; *file != NULL; file++) {
		if ((status = access(*file, F_OK)) == -1) {
			dprintf("Unable to access file %s\n", *file);
			break;
		}
	}

	return (status);
}

int
print_da_defs(da_defs_t *da_defs)
{
	char	optbuf[BUFSIZ];
	char	*p = NULL;

	if (da_defs->devopts == NULL) {
		dprintf("No default attributes for %s\n", da_defs->devtype);
		return (DEFATTRSERR);
	}
	(void) printf("dev_type=%s\n", da_defs->devtype);
	if (_kva2str(da_defs->devopts, optbuf, sizeof (optbuf), KV_ASSIGN,
	    KV_TOKEN_DELIMIT) == 0) {
		if (p = rindex(optbuf, ':'))
			*p = '\0';
		(void) printf("\t%s\n", optbuf);
	}

	return (0);
}

void
print_dev_attrs(int optflag, devalloc_t *da, devmap_t *dm,
    struct file_info *fip)
{
	char	*p = NULL;
	char	optbuf[BUFSIZ];

	(void) printf("device=%s%s", dm->dmap_devname, KV_DELIMITER);
	(void) printf("type=%s%s", dm->dmap_devtype, KV_DELIMITER);
	(void) printf("auths=%s%s",
	    (da->da_devauth ? da->da_devauth : ""), KV_DELIMITER);
	(void) printf("clean=%s%s",
	    (da->da_devexec ? da->da_devexec : ""), KV_DELIMITER);
	if (da->da_devopts != NULL) {
		if (_kva2str(da->da_devopts, optbuf, sizeof (optbuf),
		    KV_ASSIGN, KV_TOKEN_DELIMIT) == 0) {
			if (p = rindex(optbuf, ':'))
				*p = '\0';
			(void) printf("%s", optbuf);
		}
	}
	(void) printf("%s", KV_DELIMITER);
	if (optflag & WINDOWING) {
		if ((fip->fi_message != NULL) &&
		    (strcmp(fip->fi_message, DAOPT_CLASS) == 0))
			(void) printf("owner=/FREE%s", KV_DELIMITER);
		else if (DEV_ERRORED(fip->fi_stat))
			(void) printf("owner=/ERROR%s", KV_DELIMITER);
		else if (!DEV_ALLOCATED(fip->fi_stat))
			(void) printf("owner=/FREE%s", KV_DELIMITER);
		else
			(void) printf("owner=%u%s", fip->fi_stat.st_uid,
			    KV_DELIMITER);
	}
	(void) printf("files=%s", dm->dmap_devlist);
	(void) printf("\n");
}

void
print_dev(devmap_t *dm)
{
	char	**file;

	(void) printf(gettext("device: %s "), dm->dmap_devname);
	(void) printf(gettext("type: %s "), dm->dmap_devtype);
	(void) printf(gettext("files:"));
	file = dm->dmap_devarray;
	if (file != NULL) {
		for (; *file != NULL; file++)
			(void) printf(" %s", *file);
	}
	(void) printf("\n");
}

/* ARGSUSED */
int
_list_device(int optflag, uid_t uid, devalloc_t *da, char *zonename)
{
	int			bytes = 0;
	int			error = 0;
	int			is_authorized = 0;
	char			*fname = NULL;
	char			file_name[MAXPATHLEN];
	devmap_t		*dm;
	struct file_info	fi;
	struct state_file	sf;

	fi.fi_message = NULL;
	setdmapent();
	if ((dm = getdmapnam(da->da_devname)) == NULL) {
		enddmapent();
		dprintf("Unable to find %s in the maps database\n",
		    da->da_devname);
		return (NODMAPERR);
	}
	enddmapent();

	if ((optflag & CLASS) &&
	    (!(optflag & (LISTALL | LISTFREE | LISTALLOC)))) {
		fi.fi_message = DAOPT_CLASS;
		if (optflag & LISTATTRS)
			print_dev_attrs(optflag, da, dm, &fi);
		else
			print_dev(dm);
		goto out;
	}

	if (system_labeled) {
		if ((error = _dev_file_name(&sf, dm)) != 0) {
			freedmapent(dm);
			dprintf("Unable to find %s device files\n",
			    da->da_devname);
			error = NODMAPERR;
			goto out;
		}
		fname = sf.sf_path;
	} else {
		bytes = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
		    da->da_devname);
		if (bytes <= 0) {
			error = DEVNAMEERR;
			goto out;
		} else if (bytes >= MAXPATHLEN) {
			dprintf("device name %s is too long.\n",
			    da->da_devname);
			error = DEVLONGERR;
			goto out;
		}
		fname = file_name;
	}
	if (stat(fname, &fi.fi_stat) != 0) {
		dprintf("Unable to stat %s\n", fname);
		dperror("Error:");
		error = DACACCERR;
		goto out;
	}
	is_authorized = _is_dev_authorized(da, uid);
	if (optflag & LISTFREE) {	/* list_devices -n */
		/*
		 * list all free devices
		 */
		if (DEV_ALLOCATED(fi.fi_stat)) {
				error = PREALLOCERR;
				goto out;
		}
		if (system_labeled) {
			/*
			 * for this free device, check if -
			 * 1. user has authorization to allocate
			 * 2. the zone label is within the label range of the
			 *    device
			 */
			if (is_authorized == ALLOC_BY_NONE) {
				error = DAUTHERR;
				goto out;
			} else if (is_authorized == 0) {
				error = UAUTHERR;
				goto out;
			}
			if (_check_label(da, zonename, uid,
			    CHECK_DRANGE) != 0) {
				error = LABELRNGERR;
				goto out;
			}
		}
	} else if (optflag & LISTALLOC) {	/*  list_devices -u */
		/*
		 * list all allocated devices
		 */
		if (!DEV_ALLOCATED(fi.fi_stat)) {
			error = DEVNALLOCERR;
			goto out;
		}
		if (fi.fi_stat.st_uid != uid) {
			error = DEVSTATEERR;
			goto out;
		}
		if (system_labeled) {
			/*
			 * check if the zone label equals the label at which
			 * the device is allocated.
			 */
			if (_check_label(da, zonename, uid,
			    CHECK_ZLABEL) != 0) {
				error = LABELRNGERR;
				goto out;
			}
		}
	} else if (optflag & LISTALL) {		/* list_devices -l */
		/*
		 * list all devices - free and allocated - available
		 */
		if (DEV_ALLOCATED(fi.fi_stat)) {
			if (optflag & WINDOWING &&
			    (is_authorized == ALLOC_BY_NONE)) {
				/*
				 * don't complain if we're here for the GUI.
				 */
				error = 0;
			} else if (fi.fi_stat.st_uid != uid) {
				if (!(optflag & WINDOWING)) {
					error = ALLOCUERR;
					goto out;
				}
			}
			if (system_labeled && !(optflag & WINDOWING)) {
				/*
				 * if we're not displaying in the GUI,
				 * check if the zone label equals the label
				 * at which the device is allocated.
				 */
				if (_check_label(da, zonename, uid,
				    CHECK_ZLABEL) != 0) {
					error = LABELRNGERR;
					goto out;
				}
			}
		} else if (system_labeled && !(optflag & WINDOWING)) {
			/*
			 * if we're not displaying in the GUI,
			 * for this free device, check if -
			 * 1. user has authorization to allocate
			 * 2. the zone label is within the label range of the
			 *    device
			 */
			if (is_authorized == ALLOC_BY_NONE) {
				error = DAUTHERR;
				goto out;
			} else if (is_authorized == 0) {
				error = UAUTHERR;
				goto out;
			}
			if (_check_label(da, zonename, uid,
			    CHECK_DRANGE) != 0) {
				error = LABELRNGERR;
				goto out;
			}
		}
	}
	if (system_labeled && DEV_ERRORED(fi.fi_stat) && !(optflag & LISTALL)) {
		error = DEVSTATEERR;
		goto out;
	}
	if (check_devs(dm) == -1) {
		error = DSPMISSERR;
		goto out;
	}
	if (optflag & LISTATTRS)
		print_dev_attrs(optflag, da, dm, &fi);
	else
		print_dev(dm);

	error = 0;

out:
	freedmapent(dm);
	return (error);
}

/* ARGSUSED */
int
list_devices(int optflag, uid_t uid, char *device, char *zonename)
{
	int		error = 0;
	char		*class = NULL;
	da_defs_t	*da_defs;
	devalloc_t	*da;

	if (system_labeled && optflag & WINDOWING && !(optflag & LISTATTRS)) {
		/*
		 * Private interface for GUI.
		 */
		(void) puts(DA_DB_LOCK);
		return (0);
	}
	if (optflag & USERID) {
		/*
		 * we need device.revoke to list someone else's devices
		 */
		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
			return (UAUTHERR);
	}
	if (system_labeled) {
		if (!(optflag & USERID) &&
		    !_is_authorized(DEFAULT_DEV_ALLOC_AUTH, uid))
			/*
			 * we need device.allocate to list our devices
			 */
			return (UAUTHERR);
		if (optflag & LISTDEFS) {
			/*
			 * list default attrs from devalloc_defaults
			 */
			setdadefent();
			if (device) {
				/*
				 * list default attrs for this device type
				 */
				da_defs = getdadeftype(device);
				if (da_defs == NULL) {
					enddadefent();
					dprintf("No default attributes for "
					    "%s\n", device);
					return (DEFATTRSERR);
				}
				error = print_da_defs(da_defs);
				freedadefent(da_defs);
			} else {
				/*
				 * list everything in devalloc_defaults
				 */
				while ((da_defs = getdadefent()) != NULL) {
					(void) print_da_defs(da_defs);
					freedadefent(da_defs);
				}
			}
			enddadefent();
			return (error);
		}
	}
	/*
	 * Lock the database to make sure no body writes to it while we are
	 * reading.
	 */
	(void) lock_dev(NULL, NULL);
	setdaent();
	if (device) {
		if (optflag & CLASS) {
			/*
			 * list all devices of this class.
			 */
			while ((da = getdaent()) != NULL) {
				class =	 kva_match(da->da_devopts, DAOPT_CLASS);
				if (class && (strcmp(class, device) == 0)) {
					(void) _list_device(optflag, uid, da,
					    zonename);
				}
				freedaent(da);
			}
		} else {
			/*
			 * list this device
			 */
			if ((da = getdanam(device)) == NULL) {
				enddaent();
				return (NODAERR);
			}
			error = _list_device(optflag, uid, da, zonename);
			freedaent(da);
		}
	} else {
		/*
		 * list all devices
		 */
		while ((da = getdaent()) != NULL) {
			(void) _list_device(optflag, uid, da, zonename);
			freedaent(da);
		}
	}
	enddaent();

	return (error);
}

/*
 * Set the DAC characteristics of the file.
 * This uses a fancy chmod() by setting a minimal ACL which sets the mode
 * and discards any existing ACL.
 */
int
_newdac(char *file, uid_t owner, gid_t group, o_mode_t mode)
{
	int	err = 0;

	if (mode == ALLOC_MODE) {
		if (chown(file, owner, group) == -1) {
			dperror("newdac: unable to chown");
			err = CHOWNERR;
		}
	} else do {
		if (chown(file, owner, group) == -1) {
			dperror("newdac: unable to chown");
			err = CHOWNERR;
		}
	} while (fdetach(file) == 0);

	if (err)
		return (err);

	if (strncmp(file, "/dev/", strlen("/dev/")) != 0) {
		/*
		 * This could be a SunRay device that is in /tmp.
		 */
		if (chmod(file, mode) == -1) {
			dperror("newdac: unable to chmod");
			err = SETACLERR;
		}
	} else {
		err = acl_strip(file, owner, group, (mode_t)mode);
	}

	if (err != 0) {
		dperror("newdac: unable to setacl");
		err = SETACLERR;
	}

	return (err);
}

/*
 * lock_dev -
 *	locks a section of DA_DB_LOCK.
 *	returns lock fd if successful, else -1 on error.
 */
static int
lock_dev(char *file, struct stat *statbuf)
{
	static int	lockfd = -1;
	int		ret;
	int		count = 0;
	int		retry = 10;
	off_t		size = 0;
	off_t		offset;
	char		*lockfile;

	if (system_labeled)
		lockfile = DA_DB_LOCK;
	else
		lockfile = file;

	if (statbuf) {
		offset = statbuf->st_rdev;
		dprintf("locking %s\n", file);
	} else {
		offset = 0;
		dprintf("locking %s\n", lockfile);
	}
	if ((lockfd == -1) &&
	    (lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1) {
		dperror("lock_dev: cannot open lock file");
		return (-1);
	}
	if (system_labeled) {
		(void) _newdac(lockfile, DA_UID, DA_GID, 0600);
		if (lseek(lockfd, offset, SEEK_SET) == -1) {
			dperror("lock_dev: cannot position lock file");
			return (-1);
		}
		size = 1;
	}
	errno = 0;
	while (retry) {
		count++;
		ret = lockf(lockfd, F_TLOCK, size);
		if (ret == 0)
			return (lockfd);
		if ((errno != EACCES) && (errno != EAGAIN)) {
			dperror("lock_dev: cannot set lock");
			return (-1);
		}
		retry--;
		(void) sleep(count);
		errno = 0;
	}

	return (-1);
}

int
mk_alloc(devmap_t *list, uid_t uid, struct zone_path *zpath)
{
	int	i;
	int	error = 0;
	char	**file;
	gid_t	gid = getgid();
	mode_t	mode = ALLOC_MODE;

	file = list->dmap_devarray;
	if (file == NULL)
		return (NODMAPERR);
	for (; *file != NULL; file++) {
		dprintf("Allocating %s\n", *file);
		if ((error = _newdac(*file, uid, gid, mode)) != 0) {
			(void) _newdac(*file, ALLOC_ERRID, DA_GID,
			    ALLOC_ERR_MODE);
			break;
		}
	}
	if (system_labeled && zpath->count && (error == 0)) {
		/*
		 * mark as allocated any new device nodes that we
		 * created in local zone
		 */
		for (i = 0; i < zpath->count; i++) {
			dprintf("Allocating %s\n", zpath->path[i]);
			if ((error = _newdac(zpath->path[i], uid, gid,
			    mode)) != 0) {
				(void) _newdac(zpath->path[i], ALLOC_ERRID,
				    DA_GID, ALLOC_ERR_MODE);
				break;
			}
		}
	}

	return (error);
}

/*
 * mk_revoke() is used instead of system("/usr/sbin/fuser -k file")
 * because "/usr/sbin/fuser -k file" kills all processes
 * working with the file, even "vold" (bug #4095152).
 */
int
mk_revoke(int optflag, char *file)
{
	int		r = 0, p[2], fp, lock;
	int		fuserpid;
	char		buf[MAXPATHLEN];
	FILE		*ptr;
	pid_t		c_pid;
	prpsinfo_t	info;

	(void) strcpy(buf, PROCFS);
	/*
	 * vfork() and execl() just to make the same output
	 * as before fixing of bug #4095152.
	 * The problem is that the "fuser" command prints
	 * one part of output into stderr and another into stdout,
	 * but user sees them mixed. Of course, better to change "fuser"
	 * or to intercept and not to print its output.
	 */
	if (!(optflag & SILENT)) {
		c_pid = vfork();
		if (c_pid == -1)
			return (-1);
		if (c_pid == 0) {
			dprintf("first exec fuser %s\n", file);
			(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
			dperror("first exec fuser");
			_exit(1);
		}

		(void) waitpid(c_pid, &lock, 0);
		dprintf("exit status %x\n", lock);
		if (WEXITSTATUS(lock) != 0)
			return (-1);
	}
	dprintf("first continuing c_pid=%d\n", (int)c_pid);
	if (pipe(p)) {
		dperror("pipe");
		return (-1);
	}
	/* vfork() and execl() to catch output and to process it */
	c_pid = vfork();
	if (c_pid == -1) {
		dperror("second vfork");
		return (-1);
	}
	dprintf("second continuing c_pid=%d\n", (int)c_pid);
	if (c_pid == 0) {
		(void) close(p[0]);
		(void) close(1);
		(void) fcntl(p[1], F_DUPFD, 1);
		(void) close(p[1]);
		(void) close(2);
		dprintf("second exec fuser %s\n", file);
		(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
		dperror("second exec fuser");
		_exit(1);
	}
	(void) close(p[1]);
	if ((ptr = fdopen(p[0], "r")) != NULL) {
		while (!feof(ptr)) {
			if (fscanf(ptr, "%d", &fuserpid) > 0) {
				(void) sprintf(buf + strlen(PROCFS), "%d",
				    fuserpid);
				if ((fp = open(buf, O_RDONLY)) == -1) {
					dperror(buf);
					continue;
				}
				if (ioctl(fp, PIOCPSINFO,
				    (char *)&info) == -1) {
					dprintf("%d psinfo failed", fuserpid);
					dperror("");
					(void) close(fp);
					continue;
				}
				(void) close(fp);
				if (strcmp(info.pr_fname, "vold") == NULL) {
					dprintf("%d matched vold name\n",
					    fuserpid);
					continue;
				}
				dprintf("killing %s", info.pr_fname);
				dprintf("(%d)\n", fuserpid);
				if ((r =
				    kill((pid_t)fuserpid, SIGKILL)) == -1) {
					dprintf("kill %d", fuserpid);
					dperror("");
					break;
				}
			}
		}
	} else {
		dperror("fdopen(p[0], r)");
		r = -1;
	}
	(void) fclose(ptr);

	return (r);
}

int
mk_unalloc(int optflag, devmap_t *list)
{
	int	error = 0;
	int	status;
	char	**file;

	audit_allocate_list(list->dmap_devlist);
	file = list->dmap_devarray;
	if (file == NULL)
		return (NODMAPERR);
	for (; *file != NULL; file++) {
		dprintf("Deallocating %s\n", *file);
		if (mk_revoke(optflag, *file) < 0) {
			dprintf("mk_unalloc: unable to revoke %s\n", *file);
			dperror("");
			error = CNTFRCERR;
		}
		status = _newdac(*file, DA_UID, DA_GID, DEALLOC_MODE);
		if (error == 0)
			error = status;

	}

	return (error);
}

int
mk_error(devmap_t *list)
{
	int	status = 0;
	char	**file;

	audit_allocate_list(list->dmap_devlist);
	file = list->dmap_devarray;
	if (file == NULL)
		return (NODMAPERR);
	for (; *file != NULL; file++) {
		dprintf("Putting %s in error state\n", *file);
		status = _newdac(*file, ALLOC_ERRID, DA_GID, ALLOC_ERR_MODE);
	}

	return (status);
}

int
exec_clean(int optflag, char *devname, char *path, uid_t uid, char *zonename,
    char *clean_arg)
{
	int		c;
	int		status = 0, exit_status;
	char		*mode, *cmd, *wdwcmd, *zoneroot;
	char		*devzone = zonename;
	char		wdwpath[PATH_MAX];
	char		zonepath[MAXPATHLEN];
	char		title[100];
	char		pw_buf[NSS_BUFLEN_PASSWD];
	struct passwd	pw_ent;

	zonepath[0] = '\0';
	if (system_labeled) {
		if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
			if (strcmp(clean_arg, ALLOC_CLEAN) == 0) {
				return (-1);
			} else if (optflag & FORCE) {
				(void) strcpy(zonepath, "/");
				devzone = GLOBAL_ZONENAME;
			} else {
				dprintf("unable to get label for %s zone\n",
				    zonename);
				return (-1);
			}
		} else {
			(void) strcpy(zonepath, zoneroot);
			free(zoneroot);
		}
	}
	if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
		return (-1);
	if (optflag & FORCE_ALL)
		mode = "-I";
	else if (optflag & FORCE)
		mode = "-f";
	else
		mode = "-s";
	if (path == NULL)
		return (0);
	if ((cmd = strrchr(path, '/')) == NULL)
		cmd = path;
	else
		cmd++;	/* skip leading '/' */
	c = vfork();
	switch (c) {
	case -1:
		return (-1);
	case 0:
		(void) setuid(0);
		if (system_labeled && (optflag & WINDOWING)) {
			/* First try .windowing version of script */
			(void) strncpy(wdwpath, path, PATH_MAX);
			(void) strncat(wdwpath, ".windowing", PATH_MAX);
			if ((wdwcmd = strrchr(wdwpath, '/')) == NULL)
				wdwcmd = wdwpath;
			(void) execl(wdwpath, wdwcmd, mode, devname, clean_arg,
			    pw_ent.pw_name, devzone, zonepath, NULL);
			/* If that failed, run regular version via dtterm */
			(void) snprintf(title, sizeof (title),
			    "Device %s for %s",
			    strcmp(clean_arg, ALLOC_CLEAN) == 0 ?
			    "allocation" : "deallocation", devname);
			(void) execl("/usr/dt/bin/dtterm", "dtterm",
			    "-title", title, "-geometry", "x10+100+400",
			    "-e", "/etc/security/lib/wdwwrapper",
			    path, mode, devname, clean_arg, pw_ent.pw_name,
			    devzone, zonepath, NULL);
			/*
			 * And if that failed, continue on to try
			 * running regular version directly.
			 */
		}
		dprintf("clean script: %s, ", path);
		dprintf("cmd=%s, ", cmd);
		dprintf("mode=%s, ", mode);
		if (system_labeled) {
			dprintf("devname=%s ", devname);
			dprintf("zonename=%s ", devzone);
			dprintf("zonepath=%s ", zonepath);
			dprintf("username=%s\n", pw_ent.pw_name);
			(void) execl(path, cmd, mode, devname, clean_arg,
			    pw_ent.pw_name, devzone, zonepath, NULL);
		} else {
			dprintf("devname=%s\n", devname);
			(void) execle(path, cmd, mode, devname, NULL, newenv);
		}
		dprintf("Unable to execute clean up script %s\n", path);
		dperror("");
		exit(CNTDEXECERR);
	default:
		(void) waitpid(c, &status, 0);
		dprintf("Child %d", c);
		if (WIFEXITED(status)) {
			exit_status = WEXITSTATUS(status);
			dprintf(" exited, status: %d\n", exit_status);
			return (exit_status);
		} else if (WIFSIGNALED(status)) {
			dprintf(" killed, signal %d\n", WTERMSIG(status));
		} else {
			dprintf(": exit status %d\n", status);
		}
		return (-1);
	}
}

int
_deallocate_dev(int optflag, devalloc_t *da, devmap_t *dm_in, uid_t uid,
    char *zonename, int *lock_fd)
{
	int			bytes = 0;
	int			error = 0;
	int			is_authorized = 0;
	uid_t			nuid;
	char			*fname = NULL;
	char			file_name[MAXPATHLEN];
	char			*devzone = NULL;
	devmap_t		*dm = NULL, *dm_new = NULL;
	struct stat		stat_buf;
	struct state_file	sf;

	if (dm_in == NULL) {
		setdmapent();
		if ((dm_new = getdmapnam(da->da_devname)) == NULL) {
			enddmapent();
			dprintf("Unable to find %s in device map database\n",
			    da->da_devname);
			return (NODMAPERR);
		}
		enddmapent();
		dm = dm_new;
	} else {
		dm = dm_in;
	}
	if (system_labeled) {
		if (_dev_file_name(&sf, dm) != 0) {
			if (dm_new)
				freedmapent(dm_new);
			dprintf("Unable to find %s device files\n",
			    da->da_devname);
			error = NODMAPERR;
			goto out;
		}
		fname = sf.sf_path;
	} else {
		bytes = snprintf(file_name,  MAXPATHLEN, "%s/%s", DAC_DIR,
		    da->da_devname);
		if (bytes <= 0) {
			error = DEVNAMEERR;
			goto out;
		} else if (bytes >= MAXPATHLEN) {
			dprintf("device name %s is too long.\n",
			    da->da_devname);
			error = DEVLONGERR;
			goto out;
		}
		fname = file_name;
	}

	audit_allocate_device(fname);

	if (stat(fname, &stat_buf) != 0) {
		dprintf("Unable to stat %s\n", fname);
		error = DACACCERR;
		goto out;
	}
	is_authorized = _is_dev_authorized(da, uid);
	if (!(optflag & (FORCE | FORCE_ALL)) && !is_authorized) {
		dprintf("User %d is unauthorized to deallocate\n", (int)uid);
		error = UAUTHERR;
		goto out;
	}
	if (system_labeled) {
		/*
		 * unless we're here to deallocate by force, check if the
		 * label at which the device is currently allocated is
		 * within the user label range.
		 */
		if (!(optflag & FORCE) &&
		    _check_label(da, zonename, uid, CHECK_URANGE) != 0) {
			error = LABELRNGERR;
			goto out;
		}
	}
	if (!(optflag & FORCE) && stat_buf.st_uid != uid &&
	    DEV_ALLOCATED(stat_buf)) {
		error = ALLOCUERR;
		goto out;
	}
	if (!DEV_ALLOCATED(stat_buf)) {
		if (DEV_ERRORED(stat_buf)) {
			if (!(optflag & FORCE)) {
				error = DEVSTATEERR;
				goto out;
			}
		} else {
			error = DEVNALLOCERR;
			goto out;
		}
	}
	/* All checks passed, time to lock and deallocate */
	if ((*lock_fd = lock_dev(fname, &stat_buf)) == -1) {
		error = DEVLKERR;
		goto out;
	}
	if (system_labeled) {
		devzone = kva_match(da->da_devopts, DAOPT_ZONE);
		if (devzone == NULL) {
			devzone = GLOBAL_ZONENAME;
		} else if (strcmp(devzone, GLOBAL_ZONENAME) != 0) {
			if ((remove_znode(devzone, dm) != 0) &&
			    !(optflag & FORCE)) {
				error = ZONEERR;
				goto out;
			}
		}
	}
	if ((error = mk_unalloc(optflag, dm)) != 0) {
		if (!(optflag & FORCE))
			goto out;
	}
	if (system_labeled == 0) {
		if ((error = _newdac(fname, DA_UID, DA_GID,
		    DEALLOC_MODE)) != 0) {
			(void) _newdac(file_name, DA_UID, DA_GID,
			    ALLOC_ERR_MODE);
			goto out;
		}
	}
	/*
	 * if we are deallocating device owned by someone else,
	 * pass the owner's uid to the cleaning script.
	 */
	nuid = (stat_buf.st_uid == uid) ? uid : stat_buf.st_uid;
	error = exec_clean(optflag, da->da_devname, da->da_devexec, nuid,
	    devzone, DEALLOC_CLEAN);
	if (error != 0) {
		if (!(optflag & (FORCE | FORCE_ALL))) {
			error = CLEANERR;
			(void) mk_error(dm);
		} else {
			error = 0;
		}
	}

out:
	if (dm_new)
		freedmapent(dm_new);
	return (error);
}

int
_allocate_dev(int optflag, uid_t uid, devalloc_t *da, char *zonename,
	int *lock_fd)
{
	int			i;
	int			bytes = 0;
	int			error = 0;
	int			is_authorized = 0;
	int			dealloc_optflag = 0;
	char			*fname = NULL;
	char			file_name[MAXPATHLEN];
	devmap_t 		*dm;
	struct stat 		stat_buf;
	struct state_file	sf;
	struct zone_path	zpath;

	zpath.count = 0;
	zpath.path = NULL;
	setdmapent();
	if ((dm = getdmapnam(da->da_devname)) == NULL) {
		enddmapent();
		dprintf("Unable to find %s in device map database\n",
		    da->da_devname);
		return (NODMAPERR);
	}
	enddmapent();
	if (system_labeled) {
		if (_dev_file_name(&sf, dm) != 0) {
			freedmapent(dm);
			dprintf("Unable to find %s device files\n",
			    da->da_devname);
			error = NODMAPERR;
			goto out;
		}
		fname = sf.sf_path;
	} else {
		bytes = snprintf(file_name,  MAXPATHLEN, "%s/%s", DAC_DIR,
		    da->da_devname);
		if (bytes <= 0) {
			error = DEVNAMEERR;
			goto out;
		} else if (bytes >= MAXPATHLEN) {
			dprintf("device name %s is too long.\n",
			    da->da_devname);
			error = DEVLONGERR;
			goto out;
		}
		fname = file_name;
	}

	(void) audit_allocate_device(fname);

	if (stat(fname, &stat_buf) != 0) {
		dprintf("Unable to stat %s\n", fname);
		dperror("Error:");
		error = DACACCERR;
		goto out;
	}
	if (DEV_ERRORED(stat_buf)) {
		error = DEVSTATEERR;
		goto out;
	}
	is_authorized = _is_dev_authorized(da, uid);
	if (is_authorized == ALLOC_BY_NONE) {
		dprintf("Device %s is not allocatable\n", da->da_devname);
		error = UAUTHERR;
		goto out;
	} else if (!is_authorized && !(optflag & USERNAME)) {
		dprintf("User %d is unauthorized to allocate\n", (int)uid);
		error = UAUTHERR;
		goto out;
	}
	if (system_labeled) {
		/*
		 * check if label of the zone to which the device is being
		 * allocated is within the device label range.
		 */
		if (_check_label(da, zonename, uid, CHECK_DRANGE) != 0) {
			error = LABELRNGERR;
			goto out;
		}
	}
	if (check_devs(dm) == -1) {
		error = DSPMISSERR;
		goto out;
	}
	if (DEV_ALLOCATED(stat_buf)) {
		if (optflag & FORCE) {
			if (optflag & SILENT)
				dealloc_optflag = FORCE|SILENT;
			else
				dealloc_optflag = FORCE;
			if (_deallocate_dev(dealloc_optflag, da, dm, uid,
			    zonename, lock_fd)) {
				dprintf("Couldn't force deallocate device %s\n",
				    da->da_devname);
				error = CNTFRCERR;
				goto out;
			}
		} else if (stat_buf.st_uid == uid) {
			error = PREALLOCERR;
			goto out;
		} else {
			error = ALLOCUERR;
			goto out;
		}
	}
	/* All checks passed, time to lock and allocate */
	if ((*lock_fd = lock_dev(fname, &stat_buf)) == -1) {
		error = DEVLKERR;
		goto out;
	}
	if (system_labeled) {
		/*
		 * Run the cleaning program; it also mounts allocated
		 * device if required.
		 */
		error = exec_clean(optflag, da->da_devname, da->da_devexec, uid,
		    zonename, ALLOC_CLEAN);
		if (error != DEVCLEAN_OK) {
			switch (error) {
			case DEVCLEAN_ERROR:
			case DEVCLEAN_SYSERR:
				dprintf("allocate: "
				    "Error in device clean program %s\n",
				    da->da_devexec);
				error = CLEANERR;
				(void) mk_error(dm);
				goto out;
			case DEVCLEAN_BADMOUNT:
				dprintf("allocate: Failed to mount device %s\n",
				    da->da_devexec);
				goto out;
			case DEVCLEAN_MOUNTOK:
				break;
			default:
				error = 0;
				goto out;
			}
		}
		/*
		 * If not mounted, create zonelinks, if this is not the
		 * global zone.
		 */
		if ((strcmp(zonename, GLOBAL_ZONENAME) != 0) &&
		    (error != DEVCLEAN_MOUNTOK)) {
			if (create_znode(zonename, &zpath, dm) != 0) {
				error = ZONEERR;
				goto out;
			}
		}
	}

	(void) audit_allocate_list(dm->dmap_devlist);

	if ((error = mk_alloc(dm, uid, &zpath)) != 0) {
		(void) mk_unalloc(optflag, dm);
		goto out;
	}

	if (system_labeled == 0) {
		if ((error = _newdac(file_name, uid, getgid(),
		    ALLOC_MODE)) != 0) {
			(void) _newdac(file_name, DA_UID, DA_GID,
			    ALLOC_ERR_MODE);
			goto out;
		}
	}
	error = 0;
out:
	if (zpath.count) {
		for (i = 0; i < zpath.count; i++)
			free(zpath.path[i]);
		free(zpath.path);
	}
	freedmapent(dm);
	return (error);
}

void
_store_devnames(int *count, struct dev_names *dnms, char *zonename,
    devalloc_t *da, int flag)
{
	int i;

	dnms->dnames = (char **)realloc(dnms->dnames,
	    (*count + 1) * sizeof (char *));
	if (da) {
		dnms->dnames[*count] = strdup(da->da_devname);
		(*count)++;
	} else {
		dnms->dnames[*count] = NULL;
		if (flag == DA_ADD_ZONE)
			(void) update_device(dnms->dnames, zonename,
			    DA_ADD_ZONE);
		else if (flag == DA_REMOVE_ZONE)
			(void) update_device(dnms->dnames, NULL,
			    DA_REMOVE_ZONE);
		for (i = 0; i < *count; i++)
			free(dnms->dnames[i]);
		free(dnms->dnames);
	}
}

int
allocate(int optflag, uid_t uid, char *device, char *zonename)
{
	int		count = 0;
	int		error = 0;
	int		lock_fd = -1;
	devalloc_t	*da;
	struct dev_names dnms;

	if (optflag & (FORCE | USERID | USERNAME)) {
		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
			return (UAUTHERR);
	}
	dnms.dnames = NULL;
	setdaent();
	if (optflag & TYPE) {
		/*
		 * allocate devices of this type
		 */
		while ((da = getdatype(device)) != NULL) {
			if (system_labeled &&
			    da_check_logindevperm(da->da_devname)) {
				freedaent(da);
				continue;
			}
			dprintf("trying to allocate %s\n", da->da_devname);
			error = _allocate_dev(optflag, uid, da, zonename,
			    &lock_fd);
			if (system_labeled && (error == 0)) {
				/*
				 * we need to record in device_allocate the
				 * label (zone name) at which this device is
				 * being allocated. store this device entry.
				 */
				_store_devnames(&count, &dnms, zonename, da, 0);
			}
			freedaent(da);
			error = 0;
		}
	} else {
		/*
		 * allocate this device
		 */
		if ((da = getdanam(device)) == NULL) {
			enddaent();
			return (NODAERR);
		}
		if (system_labeled && da_check_logindevperm(device)) {
			freedaent(da);
			return (LOGINDEVPERMERR);
		}
		dprintf("trying to allocate %s\n", da->da_devname);
		error = _allocate_dev(optflag, uid, da, zonename, &lock_fd);
		/*
		 * we need to record in device_allocate the label (zone name)
		 * at which this device is being allocated. store this device
		 * entry.
		 */
		if (system_labeled && (error == 0))
			_store_devnames(&count, &dnms, zonename, da, 0);
		freedaent(da);
		if (error == DEVCLEAN_BADMOUNT)
			error = 0;
	}
	enddaent();
	if (lock_fd != -1)
		(void) close(lock_fd);
	/*
	 * add to device_allocate labels (zone names) for the devices we
	 * allocated.
	 */
	if (dnms.dnames)
		_store_devnames(&count, &dnms, zonename, NULL, DA_ADD_ZONE);

	return (error);
}

/* ARGSUSED */
int
deallocate(int optflag, uid_t uid, char *device, char *zonename)
{
	int		count = 0;
	int		error = 0;
	int		lock_fd = -1;
	char		*class = NULL;
	devalloc_t	*da;
	struct dev_names dnms;

	if (optflag & (FORCE | FORCE_ALL)) {
		if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
		return (UAUTHERR);
	}
	if (optflag & FORCE_ALL)
		optflag |= FORCE;
	dnms.dnames = NULL;
	setdaent();
	if (optflag & FORCE_ALL) {
		/*
		 * deallocate all devices
		 */
		while ((da = getdaent()) != NULL) {
			if (system_labeled &&
			    da_check_logindevperm(da->da_devname)) {
				freedaent(da);
				continue;
			}
			dprintf("trying to deallocate %s\n", da->da_devname);
			error = _deallocate_dev(optflag, da, NULL, uid,
			    zonename, &lock_fd);
			if (system_labeled && (error == 0)) {
				/*
				 * we need to remove this device's allocation
				 * label (zone name) from device_allocate.
				 * store this device name.
				 */
				_store_devnames(&count, &dnms, zonename, da, 0);
			}
			freedaent(da);
			error = 0;
		}
	} else if (system_labeled && (optflag & TYPE)) {
		/*
		 * deallocate all devices of this type
		 */
		while ((da = getdatype(device)) != NULL) {
			if (da_check_logindevperm(da->da_devname)) {
				freedaent(da);
				continue;
			}
			dprintf("trying to deallocate %s\n", da->da_devname);
			error = _deallocate_dev(optflag, da, NULL, uid,
			    zonename, &lock_fd);
			if (error == 0) {
				/*
				 * we need to remove this device's allocation
				 * label (zone name) from device_allocate.
				 * store this device name.
				 */
				_store_devnames(&count, &dnms, zonename, da, 0);
			}
			freedaent(da);
			error = 0;
		}
	} else if (system_labeled && (optflag & CLASS)) {
		/*
		 * deallocate all devices of this class (for sunray)
		 */
		while ((da = getdaent()) != NULL) {
			class =  kva_match(da->da_devopts, DAOPT_CLASS);
			if (class && (strcmp(class, device) == 0)) {
				dprintf("trying to deallocate %s\n",
				    da->da_devname);
				error = _deallocate_dev(optflag, da, NULL, uid,
				    zonename, &lock_fd);
				if (error == 0) {
					/*
					 * we need to remove this device's
					 * allocation label (zone name) from
					 * device_allocate. store this device
					 * name.
					 */
					_store_devnames(&count, &dnms, zonename,
					    da, 0);
				}
				error = 0;
			}
			freedaent(da);
		}
	} else if (!(optflag & TYPE)) {
		/*
		 * deallocate this device
		 */
		if ((da = getdanam(device)) == NULL) {
			enddaent();
			return (NODAERR);
		}
		if (system_labeled && da_check_logindevperm(da->da_devname)) {
			freedaent(da);
			return (LOGINDEVPERMERR);
		}
		dprintf("trying to deallocate %s\n", da->da_devname);
		error = _deallocate_dev(optflag, da, NULL, uid, zonename,
		    &lock_fd);
		if (system_labeled && (error == 0)) {
			/*
			 * we need to remove this device's allocation label
			 * (zone name) from device_allocate. store this
			 * device name.
			 */
			_store_devnames(&count, &dnms, zonename, da, 0);
		}
		freedaent(da);
		if (error == DEVCLEAN_BADMOUNT)
			error = 0;
	}
	enddaent();
	if (lock_fd != -1)
		(void) close(lock_fd);
	/*
	 * remove from device_allocate labels (zone names) for the devices we
	 * deallocated.
	 */
	if (dnms.dnames)
		_store_devnames(&count, &dnms, zonename, NULL, DA_REMOVE_ZONE);

	return (error);
}

static int
_dev_file_name(struct state_file *sfp, devmap_t *dm)
{
	sfp->sf_flags = 0;
	/* if devlist is generated, never leave device in error state */
	if (dm->dmap_devlist[0] == '`')
		sfp->sf_flags |= SFF_NO_ERROR;
	if (dm->dmap_devarray == NULL ||
	    dm->dmap_devarray[0] == NULL)
		return (NODMAPERR);
	(void) strncpy(sfp->sf_path, dm->dmap_devarray[0],
	    sizeof (sfp->sf_path));
	sfp->sf_path[sizeof (sfp->sf_path) - 1] = '\0';
	if (sfp->sf_path[0] == '\0') {
		dprintf("dev_file_name: no device list for %s\n",
		    dm->dmap_devname);
		return (NODMAPERR);
	}

	return (0);
}

/*
 * _check_label -
 *	checks the device label range against zone label, which is also
 *	user's current label.
 *	returns 0 if in range, -1 for all other conditions.
 *
 */

static int
_check_label(devalloc_t *da, char *zonename, uid_t uid, int flag)
{
	int		err;
	int		in_range = 0;
	char		*alloczone, *lstr;
	char		pw_buf[NSS_BUFLEN_PASSWD];
	blrange_t	*range;
	m_label_t	*zlabel;
	struct passwd	pw_ent;

	if ((da == NULL) || (zonename == NULL))
		return (-1);

	if ((zlabel = getzonelabelbyname(zonename)) == NULL) {
		dprintf("unable to get label for %s zone\n", zonename);
		return (-1);
	}
	if (flag == CHECK_DRANGE) {
		blrange_t	drange;

		drange.lower_bound = blabel_alloc();
		lstr = kva_match(da->da_devopts, DAOPT_MINLABEL);
		if (lstr == NULL) {
			bsllow(drange.lower_bound);
		} else if (stobsl(lstr, drange.lower_bound, NO_CORRECTION,
		    &err) == 0) {
			dprintf("bad min_label for device %s\n",
			    da->da_devname);
			free(zlabel);
			blabel_free(drange.lower_bound);
			return (-1);
		}
		drange.upper_bound = blabel_alloc();
		lstr = kva_match(da->da_devopts, DAOPT_MAXLABEL);
		if (lstr == NULL) {
			bslhigh(drange.upper_bound);
		} else if (stobsl(lstr, drange.upper_bound, NO_CORRECTION,
		    &err) == 0) {
			dprintf("bad max_label for device %s\n",
			    da->da_devname);
			free(zlabel);
			blabel_free(drange.lower_bound);
			blabel_free(drange.upper_bound);
			return (-1);
		}
		if (blinrange(zlabel, &drange) == 0) {
			char	*zlbl = NULL, *min = NULL, *max = NULL;

			(void) bsltos(zlabel, &zlbl, 0, 0);
			(void) bsltos(drange.lower_bound, &min, 0, 0);
			(void) bsltos(drange.upper_bound, &max, 0, 0);
			dprintf("%s zone label ", zonename);
			dprintf("%s outside device label range: ", zlbl);
			dprintf("min - %s, ", min);
			dprintf("max - %s\n", max);
			free(zlabel);
			blabel_free(drange.lower_bound);
			blabel_free(drange.upper_bound);
			return (-1);
		}
	} else if (flag == CHECK_URANGE) {
		if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) {
			dprintf("Unable to get passwd entry for userid %d\n",
			    (int)uid);
			free(zlabel);
			return (-1);
		}
		if ((range = getuserrange(pw_ent.pw_name)) == NULL) {
			dprintf("Unable to get label range for userid %d\n",
			    (int)uid);
			free(zlabel);
			return (-1);
		}
		in_range = blinrange(zlabel, range);
		free(zlabel);
		blabel_free(range->lower_bound);
		blabel_free(range->upper_bound);
		free(range);
		if (in_range == 0) {
			dprintf("%s device label ", da->da_devname);
			dprintf("out of user %d label range\n", (int)uid);
			return (-1);
		}
	} else if (flag == CHECK_ZLABEL) {
		alloczone = kva_match(da->da_devopts, DAOPT_ZONE);
		if (alloczone == NULL) {
			free(zlabel);
			return (-1);
		}
		if (strcmp(zonename, alloczone) != 0) {
			dprintf("%s zone is different than ", zonename);
			dprintf("%s zone to which the device ", alloczone);
			dprintf("%s is allocated\n", da->da_devname);
			free(zlabel);
			return (-1);
		}
	}
	free(zlabel);

	return (0);
}

int
create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
{
	int		size;
	int		len = 0;
	int		fcount = 0;
	char		*p, *tmpfile, *zoneroot;
	char		**file;
	char		zonepath[MAXPATHLEN];
	di_prof_t	prof = NULL;

	file = list->dmap_devarray;
	if (file == NULL)
		return (NODMAPERR);
	if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
		dprintf("unable to get label for %s zone\n", zonename);
		return (1);
	}
	(void) strcpy(zonepath, zoneroot);
	free(zoneroot);
	len = strlen(zonepath);
	size = sizeof (zonepath);
	(void) strlcat(zonepath, "/dev", size);
	if (di_prof_init(zonepath, &prof)) {
		dprintf("failed to initialize dev profile at %s\n", zonepath);
		return (1);
	}
	zonepath[len] = '\0';
	for (; *file != NULL; file++) {
		/*
		 * First time initialization
		 */
		tmpfile = strdup(*file);

		/*
		 * Most devices have pathnames starting in /dev
		 * but SunRay devices do not. In SRRS 3.1 they use /tmp.
		 *
		 * If the device pathname is not in /dev then create
		 * a symbolic link to it and put the device in /dev
		 */
		if (strncmp(tmpfile, "/dev/", strlen("/dev/")) != 0) {
			char	*linkdir;
			char	srclinkdir[MAXPATHLEN];
			char	dstlinkdir[MAXPATHLEN];

			linkdir = strchr(tmpfile + 1, '/');
			p = strchr(linkdir + 1, '/');
			*p = '\0';
			(void) strcpy(dstlinkdir, "/dev");
			(void) strncat(dstlinkdir, linkdir, MAXPATHLEN);
			(void) snprintf(srclinkdir, MAXPATHLEN, "%s/root%s",
			    zonepath, tmpfile);
			(void) symlink(dstlinkdir, srclinkdir);
			*p = '/';
			(void) strncat(dstlinkdir, p, MAXPATHLEN);
			free(tmpfile);
			tmpfile = strdup(dstlinkdir);
		}
		if (di_prof_add_dev(prof, tmpfile)) {
			dprintf("failed to add %s to profile\n", tmpfile);
			di_prof_fini(prof);
			return (1);
		}
		if (strlcat(zonepath, tmpfile, size) >= size) {
			dprintf("Buffer overflow in create_znode for %s\n",
			    *file);
			free(tmpfile);
			di_prof_fini(prof);
			return (1);
		}
		free(tmpfile);
		fcount++;
		if ((zpath->path = (char **)realloc(zpath->path,
		    (fcount * sizeof (char *)))) == NULL) {
			di_prof_fini(prof);
			return (1);
		}
		zpath->path[zpath->count] = strdup(zonepath);
		zpath->count = fcount;
		zonepath[len] = '\0';
	}

	if (di_prof_commit(prof))
		dprintf("failed to add devices to zone %s\n", zonename);
	di_prof_fini(prof);

	return (0);
}

int
remove_znode(char *zonename, devmap_t *dm)
{
	int		len = 0;
	char		*zoneroot;
	char		**file;
	char		zonepath[MAXPATHLEN];
	di_prof_t	prof = NULL;

	file = dm->dmap_devarray;
	if (file == NULL)
		return (NODMAPERR);
	if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
		(void) snprintf(zonepath, MAXPATHLEN, "/zone/%s", zonename);
	} else {
		(void)  strcpy(zonepath, zoneroot);
		free(zoneroot);
	}
	/*
	 * To support SunRay we will just deal with the
	 * file in /dev, not the symlinks.
	 */
	(void) strncat(zonepath, "/dev", MAXPATHLEN);
	len = strlen(zonepath);
	if (di_prof_init(zonepath, &prof)) {
		dprintf("failed to initialize dev profile at %s\n", zonepath);
		return (1);
	}
	for (; *file != NULL; file++) {
		char *devrelpath;

		/*
		 * remove device node from zone.
		 *
		 * SunRay devices don't start with /dev
		 * so skip over first directory to make
		 * sure it is /dev. SunRay devices in zones
		 * will have a symlink into /dev but
		 * we don't ever delete it.
		 */
		devrelpath = strchr(*file + 1, '/');

		if (di_prof_add_exclude(prof, devrelpath + 1)) {
			dprintf("Failed exclude %s in dev profile\n", *file);
			di_prof_fini(prof);
			return (1);
		}
		zonepath[len] = '\0';
	}

	if (di_prof_commit(prof))
		dprintf("failed to remove devices from zone %s\n", zonename);
	di_prof_fini(prof);
	return (0);
}

int
update_device(char **devnames, char *zonename, int flag)
{
	int		len, rc;
	char		*optstr = NULL;
	da_args		dargs;
	devinfo_t	devinfo;

	dargs.optflag = flag;
	dargs.optflag |= DA_UPDATE|DA_ALLOC_ONLY;
	dargs.rootdir = NULL;
	dargs.devnames = devnames;
	devinfo.devname = devinfo.devtype = devinfo.devauths = devinfo.devexec =
	    devinfo.devlist = NULL;
	if (dargs.optflag & DA_ADD_ZONE) {
		len = strlen(DAOPT_ZONE) + strlen(zonename) + 3;
		if ((optstr = (char *)malloc(len)) == NULL)
			return (-1);
		(void) snprintf(optstr, len, "%s%s%s", DAOPT_ZONE, KV_ASSIGN,
		    zonename);
		devinfo.devopts = optstr;
	}
	dargs.devinfo = &devinfo;

	rc = da_update_device(&dargs);

	if (optstr)
		free(optstr);

	return (rc);
}