changeset 0:1f6f95793da4

ckpt
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Mon, 25 Jul 2011 03:56:14 +0100
parents
children 9934e5e0bbd7
files lx lx-fixes series
diffstat 3 files changed, 50644 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lx	Mon Jul 25 03:56:14 2011 +0100
@@ -0,0 +1,50591 @@
+diff -r 4a1868d4ae91 usr/src/Makefile.lint
+--- a/usr/src/Makefile.lint	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/Makefile.lint	Thu Jul 21 20:14:17 2011 -0400
+@@ -483,6 +483,7 @@
+ 	cmd/biosdev \
+ 	cmd/rtc \
+ 	cmd/ucodeadm \
++	lib/brand/lx \
+ 	lib/cfgadm_plugins/sata \
+ 	lib/cfgadm_plugins/sbd \
+ 	lib/libfdisk
+diff -r 4a1868d4ae91 usr/src/Targetdirs
+--- a/usr/src/Targetdirs	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/Targetdirs	Thu Jul 21 20:14:17 2011 -0400
+@@ -47,6 +47,9 @@
+ 	/boot/grub		\
+ 	/boot/grub/bin		\
+ 	/platform/i86pc		\
++	/usr/lib/brand/lx 	\
++	/usr/lib/brand/lx/amd64	\
++	/usr/lib/brand/lx/distros \
+ 	/usr/lib/xen		\
+ 	/usr/lib/xen/bin
+ 
+@@ -599,6 +602,9 @@
+ 	/usr/ucblib/32 \
+ 	/var/ld/32
+ 
++i386_SYM.DIRS64= \
++	/usr/lib/brand/lx/64
++
+ sparc_SYM.DIRS64=
+ 
+ SYM.DIRS64= \
+@@ -715,6 +721,7 @@
+ $(BUILD64) $(ROOT)/lib/secure/64:=	LINKDEST=$(MACH64)
+ $(BUILD64) $(ROOT)/usr/lib/64:=		LINKDEST=$(MACH64)
+ $(BUILD64) $(ROOT)/usr/lib/elfedit/64:=	LINKDEST=$(MACH64)
++$(BUILD64) $(ROOT)/usr/lib/brand/lx/64:=	LINKDEST=$(MACH64)
+ $(BUILD64) $(ROOT)/usr/lib/brand/sn1/64:=	LINKDEST=$(MACH64)
+ $(BUILD64) $(ROOT)/usr/lib/brand/solaris10/64:=	LINKDEST=$(MACH64)
+ $(BUILD64) $(ROOT)/usr/lib/libp/64:=	LINKDEST=$(MACH64)
+diff -r 4a1868d4ae91 usr/src/cmd/devfsadm/i386/Makefile
+--- a/usr/src/cmd/devfsadm/i386/Makefile	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/cmd/devfsadm/i386/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -24,8 +24,11 @@
+ 
+ LINK_OBJS_i386 = \
+ 	misc_link_i386.o \
++	lx_link_i386.o \
+ 	xen_link.o
+ 
++lx_link_i386.o lx_link_i386.po lx_link_i386.ln := CPPFLAGS += -I$(UTSBASE)/common/brand/lx
++
+ xen_link.o xen_link.ln xen_link.po := CPPFLAGS += -I$(UTSBASE)/i86xpv
+ 
+ include ../Makefile.com
+diff -r 4a1868d4ae91 usr/src/cmd/devfsadm/i386/lx_link_i386.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/cmd/devfsadm/i386/lx_link_i386.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,86 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <devfsadm.h>
++#include <strings.h>
++#include <stdio.h>
++#include <sys/lx_ptm.h>
++#include <sys/lx_audio.h>
++
++static int lx_ptm(di_minor_t minor, di_node_t node);
++static int lx_audio(di_minor_t minor, di_node_t node);
++static int lx_systrace(di_minor_t minor, di_node_t node);
++
++static devfsadm_create_t lx_create_cbt[] = {
++	{ "pseudo", "ddi_pseudo", LX_PTM_DRV,
++	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_ptm },
++	{ "pseudo", "ddi_pseudo", LX_AUDIO_DRV,
++	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_audio },
++	{ "pseudo", "ddi_pseudo", "lx_systrace",
++	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_systrace },
++};
++
++DEVFSADM_CREATE_INIT_V0(lx_create_cbt);
++
++static int
++lx_ptm(di_minor_t minor, di_node_t node)
++{
++	char *mname = di_minor_name(minor);
++
++	if (strcmp(LX_PTM_MINOR_NODE, mname) == 0)
++		(void) devfsadm_mklink("brand/lx/ptmx", node, minor, 0);
++
++	return (DEVFSADM_CONTINUE);
++}
++
++static int
++lx_audio(di_minor_t minor, di_node_t node)
++{
++	char *mname = di_minor_name(minor);
++
++	if (strcmp(LXA_MINORNAME_DEVCTL, mname) == 0)
++		(void) devfsadm_mklink("brand/lx/audio_devctl", node, minor, 0);
++	if (strcmp(LXA_MINORNAME_DSP, mname) == 0)
++		(void) devfsadm_mklink("brand/lx/dsp", node, minor, 0);
++	if (strcmp(LXA_MINORNAME_MIXER, mname) == 0)
++		(void) devfsadm_mklink("brand/lx/mixer", node, minor, 0);
++
++	return (DEVFSADM_CONTINUE);
++}
++
++static int
++lx_systrace(di_minor_t minor, di_node_t node)
++{
++	char *mname = di_minor_name(minor);
++	char path[MAXPATHLEN];
++
++	(void) snprintf(path, sizeof (path), "dtrace/provider/%s", mname);
++	(void) devfsadm_mklink(path, node, minor, 0);
++
++	return (DEVFSADM_CONTINUE);
++}
+diff -r 4a1868d4ae91 usr/src/cmd/truss/codes.c
+--- a/usr/src/cmd/truss/codes.c	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/cmd/truss/codes.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -383,6 +383,9 @@
+ 	{ (uint_t)TIOCSILOOP,	"TIOCSILOOP",	NULL },
+ 	{ (uint_t)TIOCCILOOP,	"TIOCSILOOP",	NULL },
+ 
++	{ (uint_t)TIOCSETLD,	"TIOCSETLD",	NULL },
++	{ (uint_t)TIOCGETLD,	"TIOCGETLD",	NULL },
++
+ 	{ (uint_t)TIOCGPPS,	"TIOCGPPS",	NULL },
+ 	{ (uint_t)TIOCSPPS,	"TIOCSPPS",	NULL },
+ 	{ (uint_t)TIOCGPPSEV,	"TIOCGPPSEV",	NULL },
+diff -r 4a1868d4ae91 usr/src/cmd/zlogin/zlogin.c
+--- a/usr/src/cmd/zlogin/zlogin.c	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/cmd/zlogin/zlogin.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -2181,8 +2181,18 @@
+ 		/*
+ 		 * In failsafe mode, we don't use login(1), so don't try
+ 		 * setting up a utmpx entry.
++		 *
++		 * A branded zone may have very different utmpx semantics.
++		 * At the moment, we only have two brand types:
++		 * Solaris-like (native, sn1) and Linux.  In the Solaris
++		 * case, we know exactly how to do the necessary utmpx
++		 * setup.  Fortunately for us, the Linux /bin/login is
++		 * prepared to deal with a non-initialized utmpx entry, so
++		 * we can simply skip it.  If future brands don't fall into
++		 * either category, we'll have to add a per-brand utmpx
++		 * setup hook.
+ 		 */
+-		if (!failsafe)
++		if (!failsafe && (strcmp(zonebrand, "lx") != 0))
+ 			if (setup_utmpx(slaveshortname) == -1)
+ 				return (1);
+ 
+diff -r 4a1868d4ae91 usr/src/cmd/zoneadm/svc-zones
+--- a/usr/src/cmd/zoneadm/svc-zones	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/cmd/zoneadm/svc-zones	Thu Jul 21 20:14:17 2011 -0400
+@@ -28,10 +28,12 @@
+ # Return a list of running, non-global zones for which a shutdown via
+ # "/sbin/init 0" may work (typically only Solaris zones.)
+ #
++# At present, this means any running "lx" zones don't qualify.
++#
+ shutdown_zones()
+ {
+ 	zoneadm list -p | nawk -F: '{
+-		if ($2 != "global") {
++		if (($5 != "lx") && ($2 != "global")) {
+ 			print $2
+ 		}
+ 	}'
+diff -r 4a1868d4ae91 usr/src/common/brand/lx/lx_signum.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/common/brand/lx/lx_signum.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,242 @@
++/*
++ * 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 2010 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#include <sys/signal.h>
++#include <lx_signum.h>
++
++/*
++ * Delivering signals to a Linux process is complicated by differences in
++ * signal numbering, stack structure and contents, and the action taken when a
++ * signal handler exits.  In addition, many signal-related structures, such as
++ * sigset_ts, vary between Solaris and Linux.
++ *
++ * The simplest transformation that must be done when sending signals is to
++ * translate between Linux and Solaris signal numbers.
++ *
++ * These are the major signal number differences between Linux and Solaris:
++ *
++ * 	====================================
++ * 	| Number |   Linux    |  Solaris   |
++ * 	| ====== | =========  | ========== |
++ *	|    7   | SIGBUS     | SIGEMT     |
++ *	|   10   | SIGUSR1    | SIGBUS     |
++ *	|   12   | SIGUSR2    | SIGSYS     |
++ *	|   16   | SIGSTKFLT  | SIGUSR1    |
++ *	|   17   | SIGCHLD    | SIGUSR2    |
++ * 	|   18   | SIGCONT    | SIGCHLD    |
++ *	|   19   | SIGSTOP    | SIGPWR     |
++ * 	|   20   | SIGTSTP    | SIGWINCH   |
++ * 	|   21   | SIGTTIN    | SIGURG     |
++ * 	|   22   | SIGTTOU    | SIGPOLL    |
++ *	|   23   | SIGURG     | SIGSTOP    |
++ * 	|   24   | SIGXCPU    | SIGTSTP    |
++ *	|   25   | SIGXFSZ    | SIGCONT    |
++ *	|   26   | SIGVTALARM | SIGTTIN    |
++ *	|   27   | SIGPROF    | SIGTTOU    |
++ *	|   28   | SIGWINCH   | SIGVTALARM |
++ *	|   29   | SIGPOLL    | SIGPROF    |
++ *	|   30   | SIGPWR     | SIGXCPU    |
++ *	|   31   | SIGSYS     | SIGXFSZ    |
++ * 	====================================
++ *
++ * Not every Linux signal maps to a Solaris signal, nor does every Solaris
++ * signal map to a Linux counterpart. However, when signals do map, the
++ * mapping is unique.
++ *
++ * One mapping issue is that Linux supports 33 real time signals, with SIGRTMIN
++ * typically starting at or near 32 (SIGRTMIN) and proceeding to 64 (SIGRTMAX)
++ * (SIGRTMIN is "at or near" 32 because glibc usually "steals" one ore more of
++ * these signals for its own internal use, adjusting SIGRTMIN and SIGRTMAX as
++ * needed.)  Conversely, Solaris actively uses signals 32-40 for other purposes
++ * and supports exactly 32 real time signals, in the range 41 (SIGRTMIN)
++ * to 72 (SIGRTMAX).
++ *
++ * At present, attempting to translate a Linux signal equal to 63
++ * will generate an error (we allow SIGRTMAX because a program
++ * should be able to send SIGRTMAX without getting an EINVAL, though obviously
++ * anything that loops through the signals from SIGRTMIN to SIGRTMAX will
++ * fail.)
++ *
++ * Similarly, attempting to translate a native Solaris signal in the range
++ * 32-40 will also generate an error as we don't want to support the receipt of
++ * those signals from the Solaris global zone.
++ */
++
++/*
++ * Linux to Solaris signal map
++ *
++ * Usage:  solaris_signal = ltos_signum[lx_signal];
++ */
++const int
++ltos_signo[LX_NSIG] = {
++	0,
++	SIGHUP,
++	SIGINT,
++	SIGQUIT,
++	SIGILL,
++	SIGTRAP,
++	SIGABRT,
++	SIGBUS,
++	SIGFPE,
++	SIGKILL,
++	SIGUSR1,
++	SIGSEGV,
++	SIGUSR2,
++	SIGPIPE,
++	SIGALRM,
++	SIGTERM,
++	SIGEMT,			/* 16:  Linux SIGSTKFLT; use Solaris SIGEMT */
++	SIGCHLD,
++	SIGCONT,
++	SIGSTOP,
++	SIGTSTP,
++	SIGTTIN,
++	SIGTTOU,
++	SIGURG,
++	SIGXCPU,
++	SIGXFSZ,
++	SIGVTALRM,
++	SIGPROF,
++	SIGWINCH,
++	SIGPOLL,
++	SIGPWR,
++	SIGSYS,
++	_SIGRTMIN,		/* 32:  Linux SIGRTMIN */
++	_SIGRTMIN + 1,
++	_SIGRTMIN + 2,
++	_SIGRTMIN + 3,
++	_SIGRTMIN + 4,
++	_SIGRTMIN + 5,
++	_SIGRTMIN + 6,
++	_SIGRTMIN + 7,
++	_SIGRTMIN + 8,
++	_SIGRTMIN + 9,
++	_SIGRTMIN + 10,
++	_SIGRTMIN + 11,
++	_SIGRTMIN + 12,
++	_SIGRTMIN + 13,
++	_SIGRTMIN + 14,
++	_SIGRTMIN + 15,
++	_SIGRTMIN + 16,
++	_SIGRTMIN + 17,
++	_SIGRTMIN + 18,
++	_SIGRTMIN + 19,
++	_SIGRTMIN + 20,
++	_SIGRTMIN + 21,
++	_SIGRTMIN + 22,
++	_SIGRTMIN + 23,
++	_SIGRTMIN + 24,
++	_SIGRTMIN + 25,
++	_SIGRTMIN + 26,
++	_SIGRTMIN + 27,
++	_SIGRTMIN + 28,
++	_SIGRTMIN + 29,
++	_SIGRTMIN + 30,
++	-1,			/* 63:  Linux SIGRTMIN + 31, or SIGRTMAX - 1 */
++	_SIGRTMAX,		/* 64:  Linux SIGRTMAX */
++};
++
++/*
++ * Solaris to Linux signal map
++ *
++ * Usage:  lx_signal = stol_signo[solaris_signal];
++ */
++const int
++stol_signo[NSIG] = {
++	0,
++	LX_SIGHUP,
++	LX_SIGINT,
++	LX_SIGQUIT,
++	LX_SIGILL,
++	LX_SIGTRAP,
++	LX_SIGABRT,
++	LX_SIGSTKFLT,		/* 7:  Solaris SIGEMT; use for LX_SIGSTKFLT */
++	LX_SIGFPE,
++	LX_SIGKILL,
++	LX_SIGBUS,
++	LX_SIGSEGV,
++	LX_SIGSYS,
++	LX_SIGPIPE,
++	LX_SIGALRM,
++	LX_SIGTERM,
++	LX_SIGUSR1,
++	LX_SIGUSR2,
++	LX_SIGCHLD,
++	LX_SIGPWR,
++	LX_SIGWINCH,
++	LX_SIGURG,
++	LX_SIGPOLL,
++	LX_SIGSTOP,
++	LX_SIGTSTP,
++	LX_SIGCONT,
++	LX_SIGTTIN,
++	LX_SIGTTOU,
++	LX_SIGVTALRM,
++	LX_SIGPROF,
++	LX_SIGXCPU,
++	LX_SIGXFSZ,
++	-1,			/* 32:  Solaris SIGWAITING */
++	-1,			/* 33:  Solaris SIGLWP */
++	-1,			/* 34:  Solaris SIGFREEZE */
++	-1,			/* 35:  Solaris SIGTHAW */
++	-1,			/* 36:  Solaris SIGCANCEL */
++	-1,			/* 37:  Solaris SIGLOST */
++	-1,			/* 38:  Solaris SIGXRES */
++	-1,			/* 39:  Solaris SIGJVM1 */
++	-1,			/* 40:  Solaris SIGJVM2 */
++	LX_SIGRTMIN,		/* 41:  Solaris _SIGRTMIN */
++	LX_SIGRTMIN + 1,
++	LX_SIGRTMIN + 2,
++	LX_SIGRTMIN + 3,
++	LX_SIGRTMIN + 4,
++	LX_SIGRTMIN + 5,
++	LX_SIGRTMIN + 6,
++	LX_SIGRTMIN + 7,
++	LX_SIGRTMIN + 8,
++	LX_SIGRTMIN + 9,
++	LX_SIGRTMIN + 10,
++	LX_SIGRTMIN + 11,
++	LX_SIGRTMIN + 12,
++	LX_SIGRTMIN + 13,
++	LX_SIGRTMIN + 14,
++	LX_SIGRTMIN + 15,
++	LX_SIGRTMIN + 16,
++	LX_SIGRTMIN + 17,
++	LX_SIGRTMIN + 18,
++	LX_SIGRTMIN + 19,
++	LX_SIGRTMIN + 20,
++	LX_SIGRTMIN + 21,
++	LX_SIGRTMIN + 22,
++	LX_SIGRTMIN + 23,
++	LX_SIGRTMIN + 24,
++	LX_SIGRTMIN + 25,
++	LX_SIGRTMIN + 26,
++	LX_SIGRTMIN + 27,
++	LX_SIGRTMIN + 28,
++	LX_SIGRTMIN + 29,
++	LX_SIGRTMIN + 30,
++	LX_SIGRTMAX,		/* 72: Solaris _SIGRTMAX */
++};
+diff -r 4a1868d4ae91 usr/src/common/brand/lx/lx_signum.h
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/common/brand/lx/lx_signum.h	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,84 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#ifndef _LX_SIGNUM_H
++#define	_LX_SIGNUM_H
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#ifdef	__cplusplus
++extern "C" {
++#endif
++
++#define	LX_SIGHUP	1
++#define	LX_SIGINT	2
++#define	LX_SIGQUIT	3
++#define	LX_SIGILL	4
++#define	LX_SIGTRAP	5
++#define	LX_SIGABRT	6
++#define	LX_SIGIOT	6
++#define	LX_SIGBUS	7
++#define	LX_SIGFPE	8
++#define	LX_SIGKILL	9
++#define	LX_SIGUSR1	10
++#define	LX_SIGSEGV	11
++#define	LX_SIGUSR2	12
++#define	LX_SIGPIPE	13
++#define	LX_SIGALRM	14
++#define	LX_SIGTERM	15
++#define	LX_SIGSTKFLT	16
++#define	LX_SIGCHLD	17
++#define	LX_SIGCONT	18
++#define	LX_SIGSTOP	19
++#define	LX_SIGTSTP	20
++#define	LX_SIGTTIN	21
++#define	LX_SIGTTOU	22
++#define	LX_SIGURG	23
++#define	LX_SIGXCPU	24
++#define	LX_SIGXFSZ	25
++#define	LX_SIGVTALRM	26
++#define	LX_SIGPROF	27
++#define	LX_SIGWINCH	28
++#define	LX_SIGIO	29
++#define	LX_SIGPOLL	LX_SIGIO
++#define	LX_SIGPWR	30
++#define	LX_SIGSYS	31
++#define	LX_SIGUNUSED	31
++
++#define	LX_NSIG_WORDS	2
++#define	LX_NBPW		32
++#define	LX_NSIG		((LX_NBPW * LX_NSIG_WORDS) + 1)
++
++#define	LX_SIGRTMIN	32
++#define	LX_SIGRTMAX	LX_NSIG - 1
++
++extern const int ltos_signo[];
++extern const int stol_signo[];
++
++#ifdef	__cplusplus
++}
++#endif
++
++#endif	/* _LX_SIGNUM_H */
+diff -r 4a1868d4ae91 usr/src/lib/brand/Makefile
+--- a/usr/src/lib/brand/Makefile	Wed Jun 22 10:37:21 2011 -0700
++++ b/usr/src/lib/brand/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -30,6 +30,9 @@
+ # Build everything in parallel; use .WAIT for dependencies
+ .PARALLEL:
+ 
++i386_SUBDIRS= lx
++i386_MSGSUBDIRS= lx
++
+ SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled $($(MACH)_SUBDIRS)
+ MSGSUBDIRS= solaris10 shared $($(MACH)_MSGSUBDIRS)
+ 
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,56 @@
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++default: all
++
++include Makefile.lx
++
++# Build everything in parallel; use .WAIT for dependencies
++.PARALLEL:
++
++SUBDIRS=	cmd librtld_db lx_support lx_brand lx_thunk netfiles zone \
++		.WAIT lx_nametoaddr
++MSGSUBDIRS=	lx_brand lx_support zone
++
++all :=		TARGET= all
++install :=	TARGET= install
++clean :=	TARGET= clean
++clobber :=	TARGET= clobber
++lint :=		TARGET= lint
++_msg :=		TARGET= _msg
++
++.KEEP_STATE:
++
++all install clean clobber lint: $(SUBDIRS)
++
++_msg: $(MSGSUBDIRS)
++
++$(SUBDIRS): FRC
++	@cd $@; pwd; $(MAKE) $(TARGET)
++
++FRC:
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/Makefile.lx
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/Makefile.lx	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,34 @@
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++# lib/brand/lx/Makefile.lx
++#
++# include global definitions
++
++BRAND=	lx
++
++include $(SRC)/lib/brand/Makefile.brand
++
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/cmd/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/cmd/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,48 @@
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++#ident	"%Z%%M%	%I%	%E% SMI"
++
++PROGS =		lx_lockd lx_native lx_statd lx_thunk
++
++include ../Makefile.lx
++
++# override the install directory
++ROOTBIN =	$(ROOTBRANDDIR)
++CLOBBERFILES =	$(ROOTPROGS)
++
++.KEEP_STATE:
++
++lint:
++
++all:		$(PROGS)
++
++install:	all $(ROOTPROGS)
++
++clean:
++	$(RM) $(PROGS)
++
++clobber: clean
++	$(RM) $(ROOTPROGS)
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/cmd/lx_lockd.sh
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/cmd/lx_lockd.sh	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,36 @@
++#!/bin/sh
++#
++# 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.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++LD_LIBRARY_PATH=/usr/lib/brand/lx
++LD_PRELOAD=/native/usr/lib/brand/lx/lx_thunk.so.1
++LD_BIND_NOW=1
++export LD_LIBRARY_PATH LD_PRELOAD LD_BIND_NOW
++
++exec /native/usr/lib/brand/lx/lx_native \
++	/native/usr/lib/nfs/lockd -P -U 29 -G 29
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/cmd/lx_native.sh
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/cmd/lx_native.sh	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,29 @@
++#!/bin/sh
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++exit 0
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/cmd/lx_statd.sh
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/cmd/lx_statd.sh	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,36 @@
++#!/bin/sh
++#
++# 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.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++LD_LIBRARY_PATH=/usr/lib/brand/lx
++LD_PRELOAD=/native/usr/lib/brand/lx/lx_thunk.so.1
++LD_BIND_NOW=1
++export LD_LIBRARY_PATH LD_PRELOAD LD_BIND_NOW
++
++exec /native/usr/lib/brand/lx/lx_native \
++	/native/usr/lib/nfs/statd -P -U 29 -G 29
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/cmd/lx_thunk.sh
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/cmd/lx_thunk.sh	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,29 @@
++#!/bin/sh
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++exec /native/usr/lib/brand/lx/lx_thunk
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,54 @@
++#
++# 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.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++default: all
++
++include $(SRC)/lib/Makefile.lib
++
++SUBDIRS = $(MACH)
++$(BUILD64)SUBDIRS += $(MACH64)
++
++LINT_SUBDIRS=	$(MACH)
++$(BUILD64)LINT_SUBDIRS += $(MACH64)
++
++all :=          TARGET= all
++clean :=        TARGET= clean
++clobber :=      TARGET= clobber
++install :=      TARGET= install
++lint :=         TARGET= lint
++
++.KEEP_STATE:
++
++all install clean clobber: $(SUBDIRS)
++
++lint: $(LINT_SUBDIRS)
++
++$(SUBDIRS): FRC
++	@cd $@; pwd; $(MAKE) $(TARGET)
++
++FRC:
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/Makefile.com
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/Makefile.com	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,83 @@
++#
++# 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.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++LIBRARY =	lx_librtld_db.a
++VERS	=	.1
++COBJS	=	lx_librtld_db.o
++OBJECTS	=	$(COBJS) $(COBJS64)
++
++include $(SRC)/lib/Makefile.lib
++include ../../Makefile.lx
++
++CSRCS =       $(COBJS:%o=../common/%c)
++SRCS  =       $(CSRCS)
++
++SRCDIR =	../common
++UTSBASE	=	$(SRC)/uts
++
++#
++# ATTENTION:
++#	Librtl_db brand plugin libraries should NOT directly invoke any
++#	libproc.so interfaces or be linked against libproc.  If a librtl_db
++#	brand plugin library uses libproc.so interfaces then it may break
++#	any other librtld_db consumers (like mdb) that tries to attach
++#	to a branded process.  The only safe interfaces that the a librtld_db
++#	brand plugin library can use to access a target process are the
++#	proc_service(3PROC) apis.
++#
++DYNFLAGS +=	$(VERSREF) -M../common/mapfile-vers
++LIBS =		$(DYNLIB)
++LDLIBS +=	-lc -lrtld_db
++CFLAGS +=	$(CCVERBOSE)
++CPPFLAGS +=	-D_REENTRANT -I../ -I$(UTSBASE)/common/brand/lx \
++			-I$(SRC)/cmd/sgs/librtld_db/common \
++			-I$(SRC)/cmd/sgs/include \
++			-I$(SRC)/cmd/sgs/include/$(MACH)
++
++ROOTLIBDIR =	$(ROOT)/usr/lib/brand/lx
++ROOTLIBDIR64 =	$(ROOT)/usr/lib/brand/lx/$(MACH64)
++
++#
++# The top level Makefiles define define TEXT_DOMAIN.  But librtld_db.so.1
++# isn't internationalized and this library won't be either.  The only
++# messages that this library can generate are messages used for debugging
++# the operation of the library itself.
++#
++DTEXTDOM =
++
++.KEEP_STATE:
++
++all: $(LIBS)
++
++lint: lintcheck
++
++pics/%64.o:	../common/%.c
++		$(COMPILE.c) -D_ELF64 $(PICFLAGS) -o $@ $<
++		$(POST_PROCESS_O)
++
++include $(SRC)/lib/Makefile.targ
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/amd64/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/amd64/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,38 @@
++#
++# 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.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++COBJS64 =	lx_librtld_db64.o
++
++include ../Makefile.com
++include $(SRC)/lib/Makefile.lib.64
++
++DYNFLAGS +=	-Mmapfile-vers
++
++CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB)
++
++install: all $(ROOTLIBS64)
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,44 @@
++#
++# 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.
++#
++
++#
++# MAPFILE HEADER START
++#
++# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
++# Object versioning must comply with the rules detailed in
++#
++#	usr/src/lib/README.mapfiles
++#
++# You should not be making modifications here until you've read the most current
++# copy of that file. If you need help, contact a gatekeeper for guidance.
++#
++# MAPFILE HEADER END
++#
++
++SUNWprivate_1.1 {
++	global:
++		rtld_db_brand_ops64;
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,575 @@
++/*
++ * 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 <stdlib.h>
++#include <string.h>
++#include <strings.h>
++#include <sys/types.h>
++#include <sys/link.h>
++#include <libproc.h>
++#include <proc_service.h>
++#include <rtld_db.h>
++#include <synch.h>
++
++#include <sys/lx_brand.h>
++
++/*
++ * ATTENTION:
++ *	Librtl_db brand plugin libraries should NOT directly invoke any
++ *	libproc.so interfaces or be linked against libproc.  If a librtl_db
++ *	brand plugin library uses libproc.so interfaces then it may break
++ *	any other librtld_db consumers (like mdb) that tries to attach
++ *	to a branded process.  The only safe interfaces that the a librtld_db
++ *	brand plugin library can use to access a target process are the
++ *	proc_service(3PROC) apis.
++ */
++
++/*
++ * M_DATA comes from some streams header file but is also redifined in
++ * _rtld_db.h, so nuke the old streams definition here.
++ */
++#ifdef M_DATA
++#undef M_DATA
++#endif /* M_DATA */
++
++/*
++ * For 32-bit versions of this library, this file get's compiled once.
++ * For 64-bit versions of this library, this file get's compiled twice,
++ * once with _ELF64 defined and once without.  The expectation is that
++ * the 64-bit version of the library can properly deal with both 32-bit
++ * and 64-bit elf files, hence in the 64-bit library there are two copies
++ * of all the interfaces in this file, one set named *32 and one named *64.
++ *
++ * This also means that we need to be careful when declaring local pointers
++ * that point to objects in another processes address space, since these
++ * pointers may not match the current processes pointer width.  Basically,
++ * we should avoid using data types that change size between 32 and 64 bit
++ * modes like: long, void *, uintprt_t, caddr_t, psaddr_t, size_t, etc.
++ * Instead we should declare all pointers as uint32_t.  Then when we
++ * are compiled to deal with 64-bit targets we'll re-define uint32_t
++ * to be a uint64_t.
++ *
++ * Finally, one last importante note.  All the 64-bit elf file code
++ * is never used and can't be tested.  This is because we don't actually
++ * support 64-bit Linux processes yet.  The reason that we have it here
++ * is because we want to support debugging 32-bit elf targets with the
++ * 64-bit version of this library, so we need to have a 64-bit version
++ * of this library.  But a 64-bit version of this library is expected
++ * to provide debugging interfaces for both 32 and 64-bit elf targets.
++ * So we provide the 64-bit elf target interfaces, but they will never
++ * be invoked and are untested.  If we ever add support for 64-bit elf
++ * Linux processes, we'll need to verify that this code works correctly
++ * for those targets.
++ */
++#ifdef _LP64
++#ifdef _ELF64
++#define	lx_ldb_get_dyns32		lx_ldb_get_dyns64
++#define	lx_ldb_init32			lx_ldb_init64
++#define	lx_ldb_fini32			lx_ldb_fini64
++#define	lx_ldb_loadobj_iter32		lx_ldb_loadobj_iter64
++#define	lx_ldb_getauxval32		lx_ldb_getauxval64
++#define	lx_elf_props32			lx_elf_props64
++#define	_rd_get_dyns32			_rd_get_dyns64
++#define	_rd_get_ehdr32			_rd_get_ehdr64
++#define	uint32_t			uint64_t
++#define	Elf32_Dyn			Elf64_Dyn
++#define	Elf32_Ehdr			Elf64_Ehdr
++#define	Elf32_Phdr			Elf64_Phdr
++#endif /* _ELF64 */
++#endif /* _LP64 */
++
++/* Included from usr/src/cmd/sgs/librtld_db/common */
++#include <_rtld_db.h>
++
++typedef struct lx_rd {
++	rd_agent_t		*lr_rap;
++	struct ps_prochandle	*lr_php;	/* proc handle pointer */
++	uint32_t		lr_rdebug;	/* address of lx r_debug */
++	uint32_t		lr_exec;	/* base address of executable */
++} lx_rd_t;
++
++typedef struct lx_link_map {
++	uint32_t lxm_addr;	/* Base address shared object is loaded at.  */
++	uint32_t lxm_name;	/* Absolute file name object was found in.  */
++	uint32_t lxm_ld;	/* Dynamic section of the shared object.  */
++	uint32_t lxm_next;	/* Chain of loaded objects.  */
++} lx_link_map_t;
++
++typedef struct lx_r_debug {
++	int r_version;		/* Version number for this protocol.  */
++	uint32_t	r_map;	/* Head of the chain of loaded objects. */
++
++	/*
++	 * This is the address of a function internal to the run-time linker,
++	 * that will always be called when the linker begins to map in a
++	 * library or unmap it, and again when the mapping change is complete.
++	 * The debugger can set a breakpoint at this address if it wants to
++	 * notice shared object mapping changes.
++	 */
++	uint32_t	r_brk;
++	r_state_e	r_state; /* defined the same way between lx/solaris */
++	uint32_t	r_ldbase; /* Base address the linker is loaded at. */
++} lx_r_debug_t;
++
++static uint32_t
++lx_ldb_getauxval32(struct ps_prochandle *php, int type)
++{
++	const auxv_t		*auxvp = NULL;
++
++	if (ps_pauxv(php, &auxvp) != PS_OK)
++		return ((uint32_t)-1);
++
++	while (auxvp->a_type != AT_NULL) {
++		if (auxvp->a_type == type)
++			return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
++		auxvp++;
++	}
++	return ((uint32_t)-1);
++}
++
++/*
++ * A key difference between the linux linker and ours' is that the linux
++ * linker adds the base address of segments to certain values in the
++ * segments' ELF header. As an example, look at the address of the
++ * DT_HASH hash table in a Solaris section - it is a relative address
++ * which locates the start of the hash table, relative to the beginning
++ * of the ELF file. However, when the linux linker loads a section, it
++ * modifies the in-memory ELF image by changing address of the hash
++ * table to be an absolute address. This is only done for libraries - not for
++ * executables.
++ *
++ * Solaris tools expect the relative address to remain relative, so
++ * here we will modify the in-memory ELF image so that it once again
++ * contains relative addresses.
++ *
++ * To accomplish this, we walk through all sections in the target.
++ * Linux sections are identified by pointing to the linux linker or libc in the
++ * DT_NEEDED section. For all matching sections, we subtract the segment
++ * base address to get back to relative addresses.
++ */
++static rd_err_e
++lx_ldb_get_dyns32(rd_helper_data_t rhd,
++    psaddr_t addr, void **dynpp, size_t *dynpp_sz)
++{
++	lx_rd_t			*lx_rd = (lx_rd_t *)rhd;
++	rd_agent_t		*rap = lx_rd->lr_rap;
++	Elf32_Ehdr		ehdr;
++	Elf32_Dyn		*dynp = NULL;
++	size_t			dynp_sz;
++	uint_t			ndyns;
++	int			i;
++
++	ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr);
++
++	/* Read in a copy of the ehdr */
++	if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) {
++		ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed");
++		return (RD_ERR);
++	}
++
++	/* read out the PT_DYNAMIC elements for this object */
++	if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) {
++		ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed");
++		return (RD_ERR);
++	}
++
++	/*
++	 * From here on out if we encounter an error we'll just return
++	 * success and pass back the unmolested dynamic elements that
++	 * we've already obtained.
++	 */
++	*dynpp = dynp;
++	*dynpp_sz = dynp_sz;
++	ndyns = dynp_sz / sizeof (Elf32_Dyn);
++
++	/* If this isn't a dynamic object, there's nothing left todo */
++	if (ehdr.e_type != ET_DYN) {
++		ps_plog("lx_ldb_get_dyns: done: not a shared object");
++		return (RD_OK);
++	}
++
++	/*
++	 * Before we blindly start changing dynamic section addresses
++	 * we need to figure out if the current object that we're looking
++	 * at is a linux object or a solaris object.  To do this first
++	 * we need to find the string tab dynamic section element.
++	 */
++	for (i = 0; i < ndyns; i++) {
++		if (dynp[i].d_tag == DT_STRTAB)
++			break;
++	}
++	if (i == ndyns) {
++		ps_plog("lx_ldb_get_dyns: "
++		    "failed to find string tab in the dynamic section");
++		return (RD_OK);
++	}
++
++	/*
++	 * Check if the strtab value looks like an offset or an address.
++	 * It's an offset if the value is less then the base address that
++	 * the object is loaded at, or if the value is less than the offset
++	 * of the section headers in the same elf object.  This check isn't
++	 * perfect, but in practice it's good enough.
++	 */
++	if ((dynp[i].d_un.d_ptr < addr) ||
++	    (dynp[i].d_un.d_ptr < ehdr.e_shoff)) {
++		ps_plog("lx_ldb_get_dyns: "
++		    "doesn't appear to be an lx object");
++		return (RD_OK);
++	}
++
++	/*
++	 * This seems to be a a linux object, so we'll patch up the dynamic
++	 * section addresses
++	 */
++	ps_plog("lx_ldb_get_dyns: "
++	    "patching up lx object dynamic section addresses");
++	for (i = 0; i < ndyns; i++) {
++		switch (dynp[i].d_tag) {
++		case DT_PLTGOT:
++		case DT_HASH:
++		case DT_STRTAB:
++		case DT_SYMTAB:
++		case DT_RELA:
++		case DT_REL:
++		case DT_DEBUG:
++		case DT_JMPREL:
++		case DT_VERSYM:
++			if (dynp[i].d_un.d_val > addr) {
++				dynp[i].d_un.d_ptr -= addr;
++			}
++			break;
++		default:
++			break;
++		}
++	}
++	return (RD_OK);
++}
++
++static void
++lx_ldb_fini32(rd_helper_data_t rhd)
++{
++	lx_rd_t *lx_rd = (lx_rd_t *)rhd;
++	ps_plog("lx_ldb_fini: cleaning up lx helper");
++	free(lx_rd);
++}
++
++/*
++ * The linux linker has an r_debug structure somewhere in its data section that
++ * contains the address of the head of the link map list. To find this, we will
++ * use the DT_DEBUG token in the executable's dynamic section. The linux linker
++ * wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We
++ * get the address of the executable's program headers from the
++ * AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the
++ * address of the Elf header, and from there we can easily get to the DT_DEBUG
++ * entry.
++ */
++static rd_helper_data_t
++lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
++{
++	lx_rd_t		*lx_rd;
++	uint32_t	addr, phdr_addr, dyn_addr;
++	Elf32_Dyn	*dyn;
++	Elf32_Phdr	phdr, *ph, *phdrs;
++	Elf32_Ehdr	ehdr;
++	int		i, dyn_count;
++
++	lx_rd = calloc(sizeof (lx_rd_t), 1);
++	if (lx_rd == NULL) {
++		ps_plog("lx_ldb_init: cannot allocate memory");
++		return (NULL);
++	}
++	lx_rd->lr_rap = rap;
++	lx_rd->lr_php = php;
++
++	phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR);
++	if (phdr_addr == (uint32_t)-1) {
++		ps_plog("lx_ldb_init: no LX_PHDR found in aux vector");
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p",
++	    phdr_addr);
++
++	if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) {
++		ps_plog("lx_ldb_init: couldn't read phdr at 0x%p",
++		    phdr_addr);
++		free(lx_rd);
++		return (NULL);
++	}
++
++	/* The ELF headher should be before the program header in memory */
++	lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset;
++	if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
++		ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p",
++		    lx_rd->lr_exec);
++		free(lx_rd);
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr);
++
++	if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
++		ps_plog("lx_ldb_init: couldn't alloc phdrs memory");
++		free(lx_rd);
++		return (NULL);
++	}
++
++	if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) !=
++	    PS_OK) {
++		ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p",
++		    phdr_addr);
++		free(lx_rd);
++		free(phdrs);
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: read %d phdrs at: 0x%p",
++	    ehdr.e_phnum, phdr_addr);
++
++	for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
++	    /*LINTED */
++	    ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
++		if (ph->p_type == PT_DYNAMIC)
++			break;
++	}
++	if (i == ehdr.e_phnum) {
++		ps_plog("lx_ldb_init: no PT_DYNAMIC in executable");
++		free(lx_rd);
++		free(phdrs);
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p",
++	    i, (phdr_addr + ((char *)ph - (char *)phdrs)));
++
++	if ((dyn = malloc(ph->p_filesz)) == NULL) {
++		ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC");
++		free(lx_rd);
++		free(phdrs);
++		return (NULL);
++	}
++
++	dyn_addr = addr + ph->p_offset;
++	dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
++	if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
++		ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p",
++		    dyn_addr);
++		free(lx_rd);
++		free(phdrs);
++		free(dyn);
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p",
++	    dyn_count, dyn_addr);
++
++	for (i = 0; i < dyn_count; i++) {
++		if (dyn[i].d_tag == DT_DEBUG) {
++			lx_rd->lr_rdebug = dyn[i].d_un.d_ptr;
++			break;
++		}
++	}
++	free(phdrs);
++	free(dyn);
++
++	if (lx_rd->lr_rdebug == 0) {
++		ps_plog("lx_ldb_init: no DT_DEBUG found in exe");
++		free(lx_rd);
++		return (NULL);
++	}
++	ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug);
++
++	return ((rd_helper_data_t)lx_rd);
++}
++
++/*
++ * Given the address of an ELF object in the target, return its size and
++ * the proper link map ID.
++ */
++static size_t
++lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr)
++{
++	Elf32_Ehdr	ehdr;
++	Elf32_Phdr	*phdrs, *ph;
++	int		i;
++	uint32_t	min = (uint32_t)-1;
++	uint32_t	max = 0;
++	size_t		sz;
++
++	if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
++		ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p",
++		    addr);
++		return (0);
++	}
++
++	if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL)
++		return (0);
++
++	if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum *
++	    ehdr.e_phentsize) != PS_OK) {
++		ps_plog("lx_elf_props: Couldn't read program headers at 0x%p",
++		    addr + ehdr.e_phoff);
++		return (0);
++	}
++
++	for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
++	    /*LINTED */
++	    ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
++
++		if (ph->p_type != PT_LOAD)
++			continue;
++
++		if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) {
++			*data_addr = ph->p_vaddr;
++			if (ehdr.e_type == ET_DYN)
++				*data_addr += addr;
++			if (*data_addr & (ph->p_align - 1))
++				*data_addr = *data_addr & (~(ph->p_align -1));
++		}
++
++		if (ph->p_vaddr < min)
++			min = ph->p_vaddr;
++
++		if (ph->p_vaddr > max) {
++			max = ph->p_vaddr;
++			sz = ph->p_memsz + max - min;
++			if (sz & (ph->p_align - 1))
++				sz = (sz & (~(ph->p_align - 1))) + ph->p_align;
++		}
++	}
++
++	free(phdrs);
++	return (sz);
++}
++
++static int
++lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
++{
++	lx_rd_t			*lx_rd = (lx_rd_t *)rhd;
++	struct ps_prochandle	*php = lx_rd->lr_php;
++	lx_r_debug_t		r_debug;
++	lx_link_map_t		map;
++	uint32_t		p = NULL;
++	int			rc;
++	rd_loadobj_t		exec;
++
++	if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug,
++	    sizeof (r_debug))) != PS_OK) {
++		ps_plog("lx_ldb_loadobj_iter: "
++		    "Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug);
++		return (rc);
++	}
++
++	p = r_debug.r_map;
++
++	/*
++	 * The first item on the link map list is for the executable, but it
++	 * doesn't give us any useful information about it. We need to
++	 * synthesize a rd_loadobj_t for the client.
++	 *
++	 * Linux doesn't give us the executable name, so we'll get it from
++	 * the AT_EXECNAME entry instead.
++	 */
++	if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) {
++		ps_plog("lx_ldb_loadobj_iter: "
++		    "Couldn't read linux link map at 0x%p", p);
++		return (rc);
++	}
++
++	bzero(&exec, sizeof (exec));
++	exec.rl_base = lx_rd->lr_exec;
++	exec.rl_dynamic = map.lxm_ld;
++	exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME);
++	exec.rl_lmident = LM_ID_BASE;
++
++	exec.rl_bend = exec.rl_base +
++	    lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base);
++
++	if ((*cb)(&exec, client_data) == 0) {
++		ps_plog("lx_ldb_loadobj_iter: "
++		    "client callb failed for executable");
++		return (PS_ERR);
++	}
++
++	for (p = map.lxm_next; p != NULL; p = map.lxm_next) {
++		rd_loadobj_t	obj;
++
++		if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) !=
++		    PS_OK) {
++			ps_plog("lx_ldb_loadobj_iter: "
++			    "Couldn't read lk map at %p", p);
++			return (rc);
++		}
++
++		/*
++		 * The linux link map has less information than the Solaris one.
++		 * We need to go fetch the missing information from the ELF
++		 * headers.
++		 */
++
++		obj.rl_nameaddr = (psaddr_t)map.lxm_name;
++		obj.rl_base = map.lxm_addr;
++		obj.rl_refnameaddr = (psaddr_t)map.lxm_name;
++		obj.rl_plt_base = NULL;
++		obj.rl_plt_size = 0;
++		obj.rl_lmident = LM_ID_BASE;
++
++		/*
++		 * Ugh - we have to walk the ELF stuff, find the PT_LOAD
++		 * sections, and calculate the end of the file's mappings
++		 * ourselves.
++		 */
++
++		obj.rl_bend = map.lxm_addr +
++		    lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base);
++		obj.rl_padstart = obj.rl_base;
++		obj.rl_padend = obj.rl_bend;
++		obj.rl_dynamic = map.lxm_ld;
++		obj.rl_tlsmodid = 0;
++
++		ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p",
++		    obj.rl_base, obj.rl_bend);
++
++		if ((*cb)(&obj, client_data) == 0) {
++			ps_plog("lx_ldb_loadobj_iter: "
++			    "Client callback failed on %s", map.lxm_name);
++			return (rc);
++		}
++	}
++	return (RD_OK);
++}
++
++/*
++ * Librtld_db plugin linkage struct.
++ *
++ * When we get loaded by librtld_db, it will look for the symbol below
++ * to find our plugin entry points.
++ */
++rd_helper_ops_t RTLD_DB_BRAND_OPS = {
++	LM_ID_BRAND,
++	lx_ldb_init32,
++	lx_ldb_fini32,
++	lx_ldb_loadobj_iter32,
++	lx_ldb_get_dyns32
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/common/mapfile-vers
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/common/mapfile-vers	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,58 @@
++#
++# 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.
++#
++
++#
++# MAPFILE HEADER START
++#
++# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
++# Object versioning must comply with the rules detailed in
++#
++#	usr/src/lib/README.mapfiles
++#
++# You should not be making modifications here until you've read the most current
++# copy of that file. If you need help, contact a gatekeeper for guidance.
++#
++# MAPFILE HEADER END
++#
++
++{
++	global:
++		rtld_db_brand_ops32;
++	local:
++		*;
++};
++
++#Externally defined symbols
++{
++    global:
++	ps_pauxv =		NODIRECT PARENT;
++	ps_pdmodel =		NODIRECT PARENT;
++	ps_pglobal_lookup =	NODIRECT PARENT;
++	ps_pglobal_sym =	NODIRECT PARENT;
++	ps_plog =		NODIRECT PARENT;
++	ps_pread =		NODIRECT PARENT;
++	ps_pwrite =		NODIRECT PARENT;
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/librtld_db/i386/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/librtld_db/i386/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,33 @@
++#
++# 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.
++#
++#ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++include ../Makefile.com
++
++CLOBBERFILES =	$(ROOTLIBDIR)/$(DYNLIB)
++
++install: all $(ROOTLIBS)
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/Makefile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/Makefile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,53 @@
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++include ../../../Makefile.lib
++
++default:	all
++
++SUBDIRS=	$(MACH)
++
++LINT_SUBDIRS=	$(MACH)
++
++all :=          TARGET= all
++clean :=        TARGET= clean
++clobber :=      TARGET= clobber
++install :=      TARGET= install
++lint :=         TARGET= lint
++_msg :=		TARGET= _msg
++
++.KEEP_STATE:
++
++all install clean clobber _msg:	$(SUBDIRS)
++
++lint: $(LINT_SUBDIRS)
++
++$(SUBDIRS): FRC
++	@cd $@; pwd; $(MAKE) $(TARGET)
++
++FRC:
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/Makefile.com
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/Makefile.com	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,102 @@
++#
++# 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 2006 Sun Microsystems, Inc.  All rights reserved.
++# Use is subject to license terms.
++#
++# ident	"%Z%%M%	%I%	%E% SMI"
++#
++
++LX_CMN  =	$(SRC)/common/brand/lx
++
++LIBRARY =	lx_brand.a
++VERS	=	.1
++COBJS	=	clock.o			\
++		clone.o			\
++		debug.o			\
++		dir.o			\
++		file.o			\
++		fcntl.o			\
++		fork.o			\
++		id.o			\
++		ioctl.o			\
++		iovec.o			\
++		lx_brand.o		\
++		lx_thunk_server.o	\
++		mem.o			\
++		misc.o			\
++		module.o		\
++		mount.o			\
++		open.o			\
++		pgrp.o			\
++		poll_select.o		\
++		priority.o		\
++		ptrace.o		\
++		rlimit.o		\
++		sched.o			\
++		sendfile.o		\
++		signal.o		\
++		socket.o		\
++		stat.o			\
++		statfs.o		\
++		sysctl.o		\
++		sysv_ipc.o		\
++		time.o			\
++		truncate.o		\
++		wait.o
++
++CMNOBJS =	lx_signum.o
++ASOBJS	=	lx_handler.o lx_runexe.o lx_crt.o
++OBJECTS	=	$(CMNOBJS) $(COBJS) $(ASOBJS)
++
++include ../../Makefile.lx
++include ../../../../Makefile.lib
++
++CSRCS   =	$(COBJS:%o=../common/%c) $(CMNOBJS:%o=$(LX_CMN)/%c)
++ASSRCS  =	$(ASOBJS:%o=$(ISASRCDIR)/%s)
++SRCS    =	$(CSRCS) $(ASSRCS)
++
++SRCDIR =	../common
++UTSBASE	=	../../../../../uts
++
++LIBS =		$(DYNLIB)
++LDLIBS +=	-lc -lsocket -lmapmalloc -lproc -lrtld_db
++DYNFLAGS +=	-Wl,-e_start -Wl,-I/native/lib/ld.so.1 -M../common/mapfile
++CFLAGS +=	$(CCVERBOSE)
++CPPFLAGS +=	-D_REENTRANT -I../ -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
++ASFLAGS =	-P $(ASFLAGS_$(CURTYPE)) -D_ASM -I../	\
++			-I$(UTSBASE)/common/brand/lx
++
++.KEEP_STATE:
++
++all: $(LIBS)
++
++lint: lintcheck
++
++include ../../../../Makefile.targ
++
++pics/%.o: $(ISASRCDIR)/%.s
++	$(COMPILE.s) -o $@ $<
++	$(POST_PROCESS_O)
++
++pics/%.o: $(LX_CMN)/%.c
++	$(COMPILE.c) -o $@ $<
++	$(POST_PROCESS_O)
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/clock.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,116 @@
++/*
++ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <errno.h>
++#include <string.h>
++#include <time.h>
++#include <sys/lx_misc.h>
++
++/*
++ * Linux uses different values for it clock identifiers, so we have to do basic
++ * translations between the two.  Thankfully, both Linux and Solaris implement
++ * the same POSIX SUSv3 clock types, so the semantics should be identical.
++ */
++
++static int ltos_clock[] = {
++	CLOCK_REALTIME,
++	CLOCK_MONOTONIC,
++	CLOCK_PROCESS_CPUTIME_ID,
++	CLOCK_THREAD_CPUTIME_ID
++};
++
++#define	LX_CLOCK_MAX	(sizeof (ltos_clock) / sizeof (ltos_clock[0]))
++
++int
++lx_clock_gettime(int clock, struct timespec *tp)
++{
++	struct timespec ts;
++
++	if (clock < 0 || clock > LX_CLOCK_MAX)
++		return (-EINVAL);
++
++	if (clock_gettime(ltos_clock[clock], &ts) < 0)
++		return (-errno);
++
++	return ((uucopy(&ts, tp, sizeof (struct timespec)) < 0) ? -EFAULT : 0);
++}
++
++int
++lx_clock_settime(int clock, struct timespec *tp)
++{
++	struct timespec ts;
++
++	if (clock < 0 || clock > LX_CLOCK_MAX)
++		return (-EINVAL);
++
++	if (uucopy(tp, &ts, sizeof (struct timespec)) < 0)
++		return (-EFAULT);
++
++	return ((clock_settime(ltos_clock[clock], &ts) < 0) ? -errno : 0);
++}
++
++int
++lx_clock_getres(int clock, struct timespec *tp)
++{
++	struct timespec ts;
++
++	if (clock < 0 || clock > LX_CLOCK_MAX)
++		return (-EINVAL);
++
++	if (clock_getres(ltos_clock[clock], &ts) < 0)
++		return (-errno);
++
++	return ((uucopy(&ts, tp, sizeof (struct timespec)) < 0) ? -EFAULT : 0);
++}
++
++int
++lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
++    struct timespec *rmtp)
++{
++	struct timespec rqt, rmt;
++
++	if (clock < 0 || clock > LX_CLOCK_MAX)
++		return (-EINVAL);
++
++	if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0)
++		return (-EFAULT);
++
++	/* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */
++	if (clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt) < 0)
++		return (-errno);
++
++	/*
++	 * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is
++	 * non-NULL.
++	 */
++	if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) &&
++	    (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0))
++		return (-EFAULT);
++
++	return (0);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/clone.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/clone.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,546 @@
++/*
++ * 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 <assert.h>
++#include <errno.h>
++#include <stdlib.h>
++#include <signal.h>
++#include <unistd.h>
++#include <ucontext.h>
++#include <thread.h>
++#include <strings.h>
++#include <libintl.h>
++#include <sys/regset.h>
++#include <sys/syscall.h>
++#include <sys/inttypes.h>
++#include <sys/param.h>
++#include <sys/types.h>
++#include <sys/segments.h>
++#include <signal.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_types.h>
++#include <sys/lx_signal.h>
++#include <sys/lx_syscall.h>
++#include <sys/lx_brand.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_thread.h>
++
++#define	LX_CSIGNAL		0x000000ff
++#define	LX_CLONE_VM		0x00000100
++#define	LX_CLONE_FS		0x00000200
++#define	LX_CLONE_FILES		0x00000400
++#define	LX_CLONE_SIGHAND	0x00000800
++#define	LX_CLONE_PID		0x00001000
++#define	LX_CLONE_PTRACE		0x00002000
++#define	LX_CLONE_VFORK		0x00004000
++#define	LX_CLONE_PARENT		0x00008000
++#define	LX_CLONE_THREAD		0x00010000
++#define	LX_CLONE_SYSVSEM	0x00040000
++#define	LX_CLONE_SETTLS		0x00080000
++#define	LX_CLONE_PARENT_SETTID	0x00100000
++#define	LX_CLONE_CHILD_CLEARTID	0x00200000
++#define	LX_CLONE_DETACH		0x00400000
++#define	LX_CLONE_CHILD_SETTID	0x01000000
++
++#define	SHARED_AS	\
++	(LX_CLONE_VM | LX_CLONE_FS | LX_CLONE_FILES | LX_CLONE_SIGHAND)
++#define	CLONE_VFORK (LX_CLONE_VM | LX_CLONE_VFORK)
++#define	CLONE_TD (LX_CLONE_THREAD|LX_CLONE_DETACH)
++
++#define	IS_FORK(f)	(((f) & SHARED_AS) == 0)
++#define	IS_VFORK(f)	(((f) & CLONE_VFORK) == CLONE_VFORK)
++
++#define	LX_EXIT		1
++#define	LX_EXIT_GROUP	2
++
++/*
++ * This is dicey.  This seems to be an internal glibc structure, and not
++ * part of any external interface.  Thus, it is subject to change without
++ * notice.  FWIW, clone(2) itself seems to be an internal (or at least
++ * unstable) interface, since strace(1) shows it differently than the man
++ * page.
++ */
++struct lx_desc
++{
++	uint32_t entry_number;
++	uint32_t base_addr;
++	uint32_t limit;
++	uint32_t seg_32bit:1;
++	uint32_t contents:2;
++	uint32_t read_exec_only:1;
++	uint32_t limit_in_pages:1;
++	uint32_t seg_not_present:1;
++	uint32_t useable:1;
++	uint32_t empty:25;
++};
++
++struct clone_state {
++	void		*c_retaddr;	/* instr after clone()'s int80 */
++	int		c_flags;	/* flags to clone(2) */
++	int 		c_sig;		/* signal to send on thread exit */
++	void 		*c_stk;		/* %esp of new thread */
++	void 		*c_ptidp;
++	struct lx_desc	*c_ldtinfo;	/* thread-specific segment */
++	void		*c_ctidp;
++	uintptr_t	c_gs;		/* Linux's %gs */
++	sigset_t	c_sigmask;	/* signal mask */
++	lx_affmask_t	c_affmask;	/* CPU affinity mask */
++	volatile int	*c_clone_res;	/* pid/error returned to cloner */
++};
++
++extern void lx_setup_clone(uintptr_t, void *, void *);
++
++/*
++ * Counter incremented when we vfork(2) ourselves, and decremented when the
++ * vfork(2)ed child exit(2)s or exec(2)s.
++ */
++static int is_vforked = 0;
++
++int
++lx_exit(uintptr_t p1)
++{
++	int		ret, status = (int)p1;
++	lx_tsd_t	*lx_tsd;
++
++	/*
++	 * If we are a vfork(2)ed child, we need to exit as quickly and
++	 * cleanly as possible to avoid corrupting our parent.
++	 */
++	if (is_vforked != 0) {
++		is_vforked--;
++		_exit(status);
++	}
++
++	if ((ret = thr_getspecific(lx_tsd_key, (void **)&lx_tsd)) != 0)
++		lx_err_fatal(gettext(
++		    "%s: unable to read thread-specific data: %s"),
++		    "exit", strerror(ret));
++
++	assert(lx_tsd != 0);
++
++	lx_tsd->lxtsd_exit = LX_EXIT;
++	lx_tsd->lxtsd_exit_status = status;
++
++	/*
++	 * Block all signals in the exit context to avoid taking any signals
++	 * (to the degree possible) while exiting.
++	 */
++	(void) sigfillset(&lx_tsd->lxtsd_exit_context.uc_sigmask);
++
++	/*
++	 * This thread is exiting.  Restore the state of the thread to
++	 * what it was before we started running linux code.
++	 */
++	(void) setcontext(&lx_tsd->lxtsd_exit_context);
++
++	/*
++	 * If we returned from the setcontext(2), something is very wrong.
++	 */
++	lx_err_fatal(gettext("%s: unable to set exit context: %s"),
++	    "exit", strerror(errno));
++
++	/*NOTREACHED*/
++	return (0);
++}
++
++int
++lx_group_exit(uintptr_t p1)
++{
++	int		ret, status = (int)p1;
++	lx_tsd_t	*lx_tsd;
++
++	/*
++	 * If we are a vfork(2)ed child, we need to exit as quickly and
++	 * cleanly as possible to avoid corrupting our parent.
++	 */
++	if (is_vforked != 0) {
++		is_vforked--;
++		_exit(status);
++	}
++
++	if ((ret = thr_getspecific(lx_tsd_key, (void **)&lx_tsd)) != 0)
++		lx_err_fatal(gettext(
++		    "%s: unable to read thread-specific data: %s"),
++		    "group_exit", strerror(ret));
++
++	assert(lx_tsd != 0);
++
++	lx_tsd->lxtsd_exit = LX_EXIT_GROUP;
++	lx_tsd->lxtsd_exit_status = status;
++
++	/*
++	 * Block all signals in the exit context to avoid taking any signals
++	 * (to the degree possible) while exiting.
++	 */
++	(void) sigfillset(&lx_tsd->lxtsd_exit_context.uc_sigmask);
++
++	/*
++	 * This thread is exiting.  Restore the state of the thread to
++	 * what it was before we started running linux code.
++	 */
++	(void) setcontext(&lx_tsd->lxtsd_exit_context);
++
++	/*
++	 * If we returned from the setcontext(2), something is very wrong.
++	 */
++	lx_err_fatal(gettext("%s: unable to set exit context: %s"),
++	    "group_exit", strerror(errno));
++
++	/*NOTREACHED*/
++	return (0);
++}
++
++static void *
++clone_start(void *arg)
++{
++	int rval;
++	struct clone_state *cs = (struct clone_state *)arg;
++	lx_tsd_t lx_tsd;
++
++	/*
++	 * Let the kernel finish setting up all the needed state for this
++	 * new thread.
++	 *
++	 * We already created the thread using the thr_create(3C) library
++	 * call, so most of the work required to emulate lx_clone(2) has
++	 * been done by the time we get to this point.  Instead of creating
++	 * a new brandsys(2) subcommand to perform the last few bits of
++	 * bookkeeping, we just use the lx_clone() slot in the syscall
++	 * table.
++	 */
++	lx_debug("\tre-vectoring to lx kernel module to complete lx_clone()");
++	lx_debug("\tLX_SYS_clone(0x%x, 0x%p, 0x%p, 0x%p, 0x%p)",
++	    cs->c_flags, cs->c_stk, cs->c_ptidp, cs->c_ldtinfo, cs->c_ctidp);
++
++	rval = syscall(SYS_brand, B_EMULATE_SYSCALL + LX_SYS_clone,
++	    cs->c_flags, cs->c_stk, cs->c_ptidp, cs->c_ldtinfo, cs->c_ctidp,
++	    NULL);
++
++	/*
++	 * At this point the parent is waiting for cs->c_clone_res to go
++	 * non-zero to indicate the thread has been cloned.  The value set
++	 * in cs->c_clone_res will be used for the return value from
++	 * clone().
++	 */
++	if (rval < 0) {
++		*(cs->c_clone_res) = -errno;
++		lx_debug("\tkernel clone failed, errno %d\n", errno);
++		return (NULL);
++	}
++
++	if (lx_sched_setaffinity(0, sizeof (cs->c_affmask),
++	    (uintptr_t)&cs->c_affmask) != 0) {
++		*(cs->c_clone_res) = -errno;
++
++		lx_err_fatal(gettext(
++		    "Unable to set affinity mask in child thread: %s"),
++		    strerror(errno));
++	}
++
++	/* Initialize the thread specific data for this thread. */
++	bzero(&lx_tsd, sizeof (lx_tsd));
++	lx_tsd.lxtsd_gs = cs->c_gs;
++
++	/*
++	 * Use the address of the stack-allocated lx_tsd as the
++	 * per-thread storage area to cache various values for later
++	 * use.
++	 *
++	 * This address is only used by this thread, so there is no
++	 * danger of other threads using this storage area, nor of it
++	 * being accessed once this stack frame has been freed.
++	 */
++	if (thr_setspecific(lx_tsd_key, &lx_tsd) != 0) {
++		*(cs->c_clone_res) = -errno;
++		lx_err_fatal(
++		    gettext("Unable to set thread-specific ptr for clone: %s"),
++		    strerror(rval));
++	}
++
++	/*
++	 * Save the current context of this thread.
++	 *
++	 * We'll restore this context when this thread attempts to exit.
++	 */
++	if (getcontext(&lx_tsd.lxtsd_exit_context) != 0) {
++		*(cs->c_clone_res) = -errno;
++
++		lx_err_fatal(gettext(
++		    "Unable to initialize thread-specific exit context: %s"),
++		    strerror(errno));
++	}
++
++	/*
++	 * Do the final stack twiddling, reset %gs, and return to the
++	 * clone(2) path.
++	 */
++	if (lx_tsd.lxtsd_exit == 0) {
++		if (sigprocmask(SIG_SETMASK, &cs->c_sigmask, NULL) < 0) {
++			*(cs->c_clone_res) = -errno;
++
++			lx_err_fatal(gettext(
++			    "Unable to release held signals for child "
++			    "thread: %s"), strerror(errno));
++		}
++
++		/*
++		 * Let the parent know that the clone has (effectively) been
++		 * completed.
++		 */
++		*(cs->c_clone_res) = rval;
++
++		lx_setup_clone(cs->c_gs, cs->c_retaddr, cs->c_stk);
++
++		/* lx_setup_clone() should never return. */
++		assert(0);
++	}
++
++	/*
++	 * We are here because the Linux application called the exit() or
++	 * exit_group() system call.  In turn the brand library did a
++	 * setcontext() to jump to the thread context state saved in
++	 * getcontext(), above.
++	 */
++	if (lx_tsd.lxtsd_exit == LX_EXIT)
++		thr_exit((void *)lx_tsd.lxtsd_exit_status);
++	else
++		exit(lx_tsd.lxtsd_exit_status);
++
++	assert(0);
++	/*NOTREACHED*/
++}
++
++int
++lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++	uintptr_t p5)
++{
++	struct clone_state *cs;
++	int flags = (int)p1;
++	void *cldstk = (void *)p2;
++	void *ptidp = (void *)p3;
++	struct lx_desc *ldtinfo = (void *)p4;
++	void *ctidp = (void *)p5;
++	thread_t tid;
++	volatile int clone_res;
++	int sig;
++	int rval;
++	int pid;
++	lx_regs_t *rp;
++	sigset_t sigmask;
++
++	if (flags & LX_CLONE_SETTLS) {
++		lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p ldt=0x%p "
++		    "ctidp=0x%p", flags, cldstk, ptidp, ldtinfo, ctidp);
++	} else {
++		lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p)",
++		    flags, cldstk, ptidp);
++	}
++
++	/*
++	 * Only supported for pid 0 on Linux
++	 */
++	if (flags & LX_CLONE_PID)
++		return (-EINVAL);
++
++	/*
++	 * CLONE_THREAD requires CLONE_SIGHAND.
++	 *
++	 * CLONE_THREAD and CLONE_DETACHED must both be either set or cleared
++	 * in kernel 2.4 and prior.
++	 * In kernel 2.6 CLONE_DETACHED was dropped completely, so we no
++	 * longer have this requirement.
++	 */
++
++	if (flags & CLONE_TD) {
++		if (!(flags & LX_CLONE_SIGHAND))
++			return (-EINVAL);
++		if ((lx_get_kern_version() <= LX_KERN_2_4) &&
++		    (flags & CLONE_TD) != CLONE_TD)
++			return (-EINVAL);
++	}
++
++	rp = lx_syscall_regs();
++
++	/* test if pointer passed by user are writable */
++	if (flags & LX_CLONE_PARENT_SETTID) {
++		if (uucopy(ptidp, &pid, sizeof (int)) != 0)
++			return (-EFAULT);
++		if (uucopy(&pid, ptidp, sizeof (int)) != 0)
++			return (-EFAULT);
++	}
++	if (flags & LX_CLONE_CHILD_SETTID) {
++		if (uucopy(ctidp, &pid, sizeof (int)) != 0)
++			return (-EFAULT);
++		if (uucopy(&pid, ctidp, sizeof (int)) != 0)
++			return (-EFAULT);
++	}
++
++	/* See if this is a fork() operation or a thr_create().  */
++	if (IS_FORK(flags) || IS_VFORK(flags)) {
++		if (flags & LX_CLONE_PARENT) {
++			lx_unsupported(gettext(
++			    "clone(2) only supports CLONE_PARENT "
++			    "for threads.\n"));
++			return (-ENOTSUP);
++		}
++
++		if (flags & LX_CLONE_PTRACE)
++			lx_ptrace_fork();
++
++		if (flags & LX_CLONE_VFORK) {
++			is_vforked++;
++			rval = vfork();
++			if (rval != 0)
++				is_vforked--;
++		} else {
++			rval = fork1();
++			if (rval == 0 && lx_is_rpm)
++				(void) sleep(lx_rpm_delay);
++		}
++
++		/*
++		 * Since we've already forked, we can't do much if uucopy fails,
++		 * so we just ignore failure. Failure is unlikely since we've
++		 * tested the memory before we did the fork.
++		 */
++		if (rval > 0 && (flags & LX_CLONE_PARENT_SETTID)) {
++			(void) uucopy(&rval, ptidp, sizeof (int));
++		}
++
++		if (rval == 0 && (flags & LX_CLONE_CHILD_SETTID)) {
++			/*
++			 * lx_getpid should not fail, and if it does, there's
++			 * not much we can do about it since we've already
++			 * forked, so on failure, we just don't copy the
++			 * memory.
++			 */
++			pid = lx_getpid();
++			if (pid >= 0)
++				(void) uucopy(&pid, ctidp, sizeof (int));
++		}
++
++		/* Parent just returns */
++		if (rval != 0)
++			return ((rval < 0) ? -errno : rval);
++
++		/*
++		 * If provided, the child needs its new stack set up.
++		 */
++		if (cldstk)
++			lx_setup_clone(rp->lxr_gs, (void *)rp->lxr_eip, cldstk);
++
++		return (0);
++	}
++
++	/*
++	 * We have very restricted support.... only exactly these flags are
++	 * supported
++	 */
++	if (((flags & SHARED_AS) != SHARED_AS)) {
++		lx_unsupported(gettext(
++		    "clone(2) requires that all or none of CLONE_VM "
++		    "CLONE_FS, CLONE_FILES, and CLONE_SIGHAND be set.\n"));
++		return (-ENOTSUP);
++	}
++
++	if (cldstk == NULL) {
++		lx_unsupported(gettext(
++		    "clone(2) requires the caller to allocate the "
++		    "child's stack.\n"));
++		return (-ENOTSUP);
++	}
++
++	/*
++	 * If we want a signal-on-exit, ensure that the signal is valid.
++	 */
++	if ((sig = ltos_signo[flags & LX_CSIGNAL]) == -1) {
++		lx_unsupported(gettext(
++		    "clone(2) passed unsupported signal: %d"), sig);
++		return (-ENOTSUP);
++	}
++
++	/*
++	 * To avoid malloc() here, we steal a part of the new thread's
++	 * stack to store all the info that thread might need for
++	 * initialization.  We also make it 64-bit aligned for good
++	 * measure.
++	 */
++	cs = (struct clone_state *)
++	    ((p2 - sizeof (struct clone_state)) & -((uintptr_t)8));
++	cs->c_flags = flags;
++	cs->c_sig = sig;
++	cs->c_stk = cldstk;
++	cs->c_ptidp = ptidp;
++	cs->c_ldtinfo = ldtinfo;
++	cs->c_ctidp = ctidp;
++	cs->c_clone_res = &clone_res;
++	cs->c_gs = rp->lxr_gs;
++
++	if (lx_sched_getaffinity(0, sizeof (cs->c_affmask),
++	    (uintptr_t)&cs->c_affmask) == -1)
++		lx_err_fatal(gettext(
++		    "Unable to get affinity mask for parent thread: %s"),
++		    strerror(errno));
++
++	/*
++	 * We want the new thread to return directly to the return site for
++	 * the system call.
++	 */
++	cs->c_retaddr = (void *)rp->lxr_eip;
++	clone_res = 0;
++
++	(void) sigfillset(&sigmask);
++
++	/*
++	 * Block all signals because the thread we create won't be able to
++	 * properly handle them until it's fully set up.
++	 */
++	if (sigprocmask(SIG_BLOCK, &sigmask, &cs->c_sigmask) < 0) {
++		lx_debug("lx_clone sigprocmask() failed: %s", strerror(errno));
++		return (-errno);
++	}
++
++	rval = thr_create(NULL, NULL, clone_start, cs, THR_DETACHED, &tid);
++
++	/*
++	 * Release any pending signals
++	 */
++	(void) sigprocmask(SIG_SETMASK, &cs->c_sigmask, NULL);
++
++	/*
++	 * Wait for the child to be created and have its tid assigned.
++	 */
++	if (rval == 0) {
++		while (clone_res == 0)
++			;
++
++		rval = clone_res;
++	}
++
++	return (rval);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/debug.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/debug.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,147 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <assert.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <strings.h>
++#include <thread.h>
++#include <unistd.h>
++
++#include <sys/modctl.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++
++#include <sys/lx_brand.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++
++/* internal debugging state */
++static char	*lx_debug_path = NULL;		/* debug output file path */
++static char	lx_debug_path_buf[MAXPATHLEN];
++
++void
++lx_debug_enable(void)
++{
++	/* send all debugging output to /dev/tty */
++	lx_debug_path = "/dev/tty";
++	lx_debug("lx_debug: debugging output enabled: %s", lx_debug_path);
++}
++
++void
++lx_debug_init(void)
++{
++	if (getenv("LX_DEBUG") == NULL)
++		return;
++
++	/*
++	 * It's OK to use this value without any locking, as all callers can
++	 * use the return value to decide whether extra work should be done
++	 * before calling lx_debug().
++	 *
++	 * If debugging is disabled after a routine calls this function it
++	 * doesn't really matter as lx_debug() will see debugging is disabled
++	 * and will not output anything.
++	 */
++	lx_debug_enabled = 1;
++
++	/* check if there's a debug log file specified */
++	lx_debug_path = getenv("LX_DEBUG_FILE");
++	if (lx_debug_path == NULL) {
++		/* send all debugging output to /dev/tty */
++		lx_debug_path = "/dev/tty";
++	}
++
++	(void) strlcpy(lx_debug_path_buf, lx_debug_path,
++	    sizeof (lx_debug_path_buf));
++	lx_debug_path = lx_debug_path_buf;
++
++	lx_debug("lx_debug: debugging output ENABLED to path: \"%s\"",
++	    lx_debug_path);
++}
++
++void
++lx_debug(const char *msg, ...)
++{
++	va_list		ap;
++	char		buf[LX_MSG_MAXLEN + 1];
++	int		rv, fd, n;
++	int		errno_backup;
++
++	if (lx_debug_enabled == 0)
++		return;
++
++	errno_backup = errno;
++
++	/* prefix the message with pid/tid */
++	if ((n = snprintf(buf, sizeof (buf), "%u/%u: ",
++	    getpid(), thr_self())) == -1) {
++		errno = errno_backup;
++		return;
++	}
++
++	/* format the message */
++	va_start(ap, msg);
++	rv = vsnprintf(&buf[n], sizeof (buf) - n, msg, ap);
++	va_end(ap);
++	if (rv == -1) {
++		errno = errno_backup;
++		return;
++	}
++
++	/* add a carrige return if there isn't one already */
++	if ((buf[strlen(buf) - 1] != '\n') &&
++	    (strlcat(buf, "\n", sizeof (buf)) >= sizeof (buf))) {
++		errno = errno_backup;
++		return;
++	}
++
++	/*
++	 * Open the debugging output file.  note that we don't protect
++	 * ourselves against exec or fork1 here.  if an mt process were
++	 * to exec/fork1 while we're doing this they'd end up with an
++	 * extra open desciptor in their fd space.  a'well.  shouldn't
++	 * really matter.
++	 */
++	if ((fd = open(lx_debug_path,
++	    O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY, 0666)) == -1) {
++		return;
++	}
++	(void) fchmod(fd, 0666);
++
++	/* we retry in case of EINTR */
++	do {
++		rv = write(fd, buf, strlen(buf));
++	} while ((rv == -1) && (errno == EINTR));
++	(void) fsync(fd);
++
++	(void) close(fd);
++	errno = errno_backup;
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/dir.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/dir.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,160 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <string.h>
++#include <stddef.h>
++#include <errno.h>
++#include <unistd.h>
++#include <assert.h>
++#include <sys/types.h>
++#include <sys/systm.h>
++#include <sys/dirent.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_debug.h>
++
++#define	LX_NAMEMAX	256
++
++struct lx_dirent {
++	long		d_ino;  /* not l_ino_t */
++	long		d_off;
++	ushort_t	d_reclen;
++	char 		d_name[LX_NAMEMAX];
++};
++
++struct lx_dirent64 {
++	uint64_t	d_ino;
++	int64_t		d_off;
++	ushort_t	d_reclen;
++	uchar_t		d_type;
++	char		d_name[LX_NAMEMAX];
++};
++
++#define	LX_RECLEN(namelen)	\
++	((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7)
++
++/*
++ * Read in one dirent structure from fd into dirp.
++ * p3 (count) is ignored.
++ */
++/*ARGSUSED*/
++int
++lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int fd = (int)p1;
++	struct lx_dirent *dirp = (struct lx_dirent *)p2;
++	uint_t count = sizeof (struct lx_dirent);
++	int rc = 0;
++	struct lx_dirent _ld;
++	struct dirent *sd = (struct dirent *)&_ld;
++
++	/*
++	 * The return value from getdents is not applicable, as
++	 * it might have squeezed more than one dirent in the buffer
++	 * we provided.
++	 *
++	 * getdents() will deal with the case of dirp == NULL
++	 */
++	if ((rc = getdents(fd, sd, count)) < 0)
++		return (-errno);
++
++	/*
++	 * Set rc 1 (pass), or 0 (end of directory).
++	 */
++	rc = (sd->d_reclen == 0) ? 0 : 1;
++
++	if (uucopy(sd, dirp, count) != 0)
++		return (-errno);
++
++	return (rc);
++}
++
++/*
++ * Read in dirent64 structures from p1 (fd) into p2 (buffer).
++ * p3 (count) is the size of the memory area.
++ */
++int
++lx_getdents64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int fd = (uint_t)p1;
++	void *buf = (void *)p2;
++	void *sbuf, *lbuf;
++	int lbufsz = (uint_t)p3;
++	int sbufsz;
++	int namelen;
++	struct dirent *sd;
++	struct lx_dirent64 *ld;
++	int bytes, rc;
++
++	if (lbufsz < sizeof (struct lx_dirent64))
++		return (-EINVAL);
++
++	/*
++	 * The Linux dirent64 is bigger than the Solaris dirent64.  To
++	 * avoid inadvertently consuming more of the directory than we can
++	 * pass back to the Linux app, we hand the kernel a smaller buffer
++	 * than the app handed us.
++	 */
++	sbufsz = (lbufsz / 32) * 24;
++
++	sbuf = SAFE_ALLOCA(sbufsz);
++	lbuf = SAFE_ALLOCA(lbufsz);
++	if (sbuf == NULL || lbuf == NULL)
++		return (-ENOMEM);
++
++	if ((bytes = getdents(fd, sbuf, sbufsz)) < 0)
++		return (-errno);
++
++	/* munge the Solaris buffer to a linux buffer. */
++	sd = (struct dirent *)sbuf;
++	ld = (struct lx_dirent64 *)lbuf;
++	rc = 0;
++	while (bytes > 0) {
++		namelen = strlen(sd->d_name);
++		if (namelen >= LX_NAMEMAX)
++			namelen = LX_NAMEMAX - 1;
++		ld->d_ino = (uint64_t)sd->d_ino;
++		ld->d_off = (int64_t)sd->d_off;
++		ld->d_type = 0;
++
++		(void) strncpy(ld->d_name, sd->d_name, namelen);
++		ld->d_name[namelen] = 0;
++		ld->d_reclen = (ushort_t)LX_RECLEN(namelen);
++
++		bytes -= (int)sd->d_reclen;
++		rc += (int)ld->d_reclen;
++
++		sd = (struct dirent *)(void *)((caddr_t)sd + sd->d_reclen);
++		ld = (struct lx_dirent64 *)(void *)((caddr_t)ld + ld->d_reclen);
++	}
++
++	/* now copy the lbuf to the userland buffer */
++	assert(rc <= lbufsz);
++	if (uucopy(lbuf, buf, rc) != 0)
++		return (-EFAULT);
++
++	return (rc);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/fcntl.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/fcntl.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,387 @@
++/*
++ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <sys/types.h>
++#include <sys/filio.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <stropts.h>
++#include <libintl.h>
++#include <errno.h>
++#include <string.h>
++
++#include <sys/lx_fcntl.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++
++static int lx_fcntl_com(int fd, int cmd, ulong_t arg);
++static void ltos_flock(struct lx_flock *l, struct flock *s);
++static void stol_flock(struct flock *s, struct lx_flock *l);
++static void ltos_flock64(struct lx_flock64 *l, struct flock64 *s);
++static void stol_flock64(struct flock64 *s, struct lx_flock64 *l);
++static short ltos_type(short l_type);
++static short stol_type(short l_type);
++static int lx_fcntl_getfl(int fd);
++static int lx_fcntl_setfl(int fd, ulong_t arg);
++
++int
++lx_dup2(uintptr_t p1, uintptr_t p2)
++{
++	int oldfd = (int)p1;
++	int newfd = (int)p2;
++	int rc;
++
++	rc = fcntl(oldfd, F_DUP2FD, newfd);
++	return ((rc == -1) ? -errno : rc);
++}
++
++int
++lx_fcntl(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int		fd = (int)p1;
++	int		cmd = (int)p2;
++	ulong_t		arg = (ulong_t)p3;
++	struct lx_flock lxflk;
++	struct flock	fl;
++	int		lk = 0;
++	int		rc;
++
++	/*
++	 * The 64-bit fcntl commands must go through fcntl64().
++	 */
++	if (cmd == LX_F_GETLK64 || cmd == LX_F_SETLK64 ||
++	    cmd == LX_F_SETLKW64)
++		return (-EINVAL);
++
++	if (cmd == LX_F_SETSIG || cmd == LX_F_GETSIG || cmd == LX_F_SETLEASE ||
++	    cmd == LX_F_GETLEASE) {
++		lx_unsupported(gettext("%s(): unsupported command: %d"),
++		    "fcntl", cmd);
++		return (-ENOTSUP);
++	}
++
++	if (cmd == LX_F_GETLK || cmd == LX_F_SETLK ||
++	    cmd == LX_F_SETLKW) {
++		if (uucopy((void *)p3, (void *)&lxflk,
++		    sizeof (struct lx_flock)) != 0)
++			return (-errno);
++		lk = 1;
++		ltos_flock(&lxflk, &fl);
++		arg = (ulong_t)&fl;
++	}
++
++	rc = lx_fcntl_com(fd, cmd, arg);
++
++	if (lk)
++		stol_flock(&fl, (struct lx_flock *)p3);
++
++	return (rc);
++}
++
++int
++lx_fcntl64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int		fd = (int)p1;
++	int		cmd = (int)p2;
++	struct lx_flock lxflk;
++	struct lx_flock64 lxflk64;
++	struct flock	fl;
++	struct flock64	fl64;
++	int		rc;
++
++	if (cmd == LX_F_SETSIG || cmd == LX_F_GETSIG || cmd == LX_F_SETLEASE ||
++	    cmd == LX_F_GETLEASE) {
++		lx_unsupported(gettext("%s(): unsupported command: %d"),
++		    "fcntl64", cmd);
++		return (-ENOTSUP);
++	}
++
++	if (cmd == LX_F_GETLK || cmd == LX_F_SETLK || cmd == LX_F_SETLKW) {
++		if (uucopy((void *)p3, (void *)&lxflk,
++		    sizeof (struct lx_flock)) != 0)
++			return (-errno);
++		ltos_flock(&lxflk, &fl);
++		rc = lx_fcntl_com(fd, cmd, (ulong_t)&fl);
++		stol_flock(&fl, (struct lx_flock *)p3);
++	} else if (cmd == LX_F_GETLK64 || cmd == LX_F_SETLKW64 || \
++	    cmd == LX_F_SETLK64) {
++		if (uucopy((void *)p3, (void *)&lxflk64,
++		    sizeof (struct lx_flock64)) != 0)
++			return (-errno);
++		ltos_flock64(&lxflk64, &fl64);
++		rc = lx_fcntl_com(fd, cmd, (ulong_t)&fl64);
++		stol_flock64(&fl64, (struct lx_flock64 *)p3);
++	} else {
++		rc = lx_fcntl_com(fd, cmd, (ulong_t)p3);
++	}
++
++	return (rc);
++}
++
++static int
++lx_fcntl_com(int fd, int cmd, ulong_t arg)
++{
++	int		rc = 0;
++
++	switch (cmd) {
++	case LX_F_DUPFD:
++		rc = fcntl(fd, F_DUPFD, arg);
++		break;
++
++	case LX_F_GETFD:
++		rc = fcntl(fd, F_GETFD, 0);
++		break;
++
++	case LX_F_SETFD:
++		rc = fcntl(fd, F_SETFD, arg);
++		break;
++
++	case LX_F_GETFL:
++		rc = lx_fcntl_getfl(fd);
++		break;
++
++	case LX_F_SETFL:
++		rc = lx_fcntl_setfl(fd, arg);
++		break;
++
++	case LX_F_GETLK:
++		rc = fcntl(fd, F_GETLK, arg);
++		break;
++
++	case LX_F_SETLK:
++		rc = fcntl(fd, F_SETLK, arg);
++		break;
++
++	case LX_F_SETLKW:
++		rc = fcntl(fd, F_SETLKW, arg);
++		break;
++
++	case LX_F_GETLK64:
++		rc = fcntl(fd, F_GETLK64, arg);
++		break;
++
++	case LX_F_SETLK64:
++		rc = fcntl(fd, F_SETLK64, arg);
++		break;
++
++	case LX_F_SETLKW64:
++		rc = fcntl(fd, F_SETLKW64, arg);
++		break;
++
++	case LX_F_SETOWN:
++		rc = fcntl(fd, F_SETOWN, arg);
++		break;
++
++	case LX_F_GETOWN:
++		rc = fcntl(fd, F_GETOWN, arg);
++		break;
++
++	default:
++		return (-EINVAL);
++	}
++
++	return ((rc == -1) ? -errno : rc);
++}
++
++
++#define	LTOS_FLOCK(l, s)						\
++{									\
++	s->l_type = ltos_type(l->l_type);				\
++	s->l_whence = l->l_whence;					\
++	s->l_start = l->l_start;					\
++	s->l_len = l->l_len;						\
++	s->l_sysid = 0;			/* not defined in linux */	\
++	s->l_pid = (pid_t)l->l_pid;					\
++}
++
++#define	STOL_FLOCK(s, l)						\
++{									\
++	l->l_type = stol_type(s->l_type);				\
++	l->l_whence = s->l_whence;					\
++	l->l_start = s->l_start;					\
++	l->l_len = s->l_len;						\
++	l->l_pid = (int)s->l_pid;					\
++}
++
++static void
++ltos_flock(struct lx_flock *l, struct flock *s)
++{
++	LTOS_FLOCK(l, s)
++}
++
++static void
++stol_flock(struct flock *s, struct lx_flock *l)
++{
++	STOL_FLOCK(s, l)
++}
++
++static void
++ltos_flock64(struct lx_flock64 *l, struct flock64 *s)
++{
++	LTOS_FLOCK(l, s)
++}
++
++static void
++stol_flock64(struct flock64 *s, struct lx_flock64 *l)
++{
++	STOL_FLOCK(s, l)
++}
++
++static short
++ltos_type(short l_type)
++{
++	switch (l_type) {
++	case LX_F_RDLCK:
++		return (F_RDLCK);
++	case LX_F_WRLCK:
++		return (F_WRLCK);
++	case LX_F_UNLCK:
++		return (F_UNLCK);
++	default:
++		return (-1);
++	}
++}
++
++static short
++stol_type(short l_type)
++{
++	switch (l_type) {
++	case F_RDLCK:
++		return (LX_F_RDLCK);
++	case F_WRLCK:
++		return (LX_F_WRLCK);
++	case F_UNLCK:
++		return (LX_F_UNLCK);
++	default:
++		/* can't ever happen */
++		return (0);
++	}
++}
++
++int
++lx_fcntl_getfl(int fd)
++{
++	int retval;
++	int rc;
++
++	retval = fcntl(fd, F_GETFL, 0);
++
++	if ((retval & O_ACCMODE) == O_RDONLY)
++		rc = LX_O_RDONLY;
++	else if ((retval & O_ACCMODE) == O_WRONLY)
++		rc = LX_O_WRONLY;
++	else
++		rc = LX_O_RDWR;
++	/* O_NDELAY != O_NONBLOCK, so we need to check for both */
++	if (retval & O_NDELAY)
++		rc |= LX_O_NDELAY;
++	if (retval & O_NONBLOCK)
++		rc |= LX_O_NONBLOCK;
++	if (retval & O_APPEND)
++		rc |= LX_O_APPEND;
++	if (retval & O_SYNC)
++		rc |= LX_O_SYNC;
++	if (retval & O_LARGEFILE)
++		rc |= LX_O_LARGEFILE;
++	if (retval & FASYNC)
++		rc |= LX_O_ASYNC;
++
++	return (rc);
++}
++
++int
++lx_fcntl_setfl(int fd, ulong_t arg)
++{
++	int new_arg;
++
++	new_arg = 0;
++	/* LX_O_NDELAY == LX_O_NONBLOCK, so we only check for one */
++	if (arg & LX_O_NDELAY)
++		new_arg |= O_NONBLOCK;
++	if (arg & LX_O_APPEND)
++		new_arg |= O_APPEND;
++	if (arg & LX_O_SYNC)
++		new_arg |= O_SYNC;
++	if (arg & LX_O_LARGEFILE)
++		new_arg |= O_LARGEFILE;
++	if (arg & LX_O_ASYNC)
++		new_arg |= FASYNC;
++
++	return ((fcntl(fd, F_SETFL, new_arg) == 0) ? 0 : -errno);
++}
++
++/*
++ * flock() applies or removes an advisory lock on the file
++ * associated with the file descriptor fd.
++ *
++ * Stolen verbatim from usr/src/ucblib/libucb/port/sys/flock.c
++ *
++ * operation is: LX_LOCK_SH, LX_LOCK_EX, LX_LOCK_UN, LX_LOCK_NB
++ */
++int
++lx_flock(uintptr_t p1, uintptr_t p2)
++{
++	int			fd = (int)p1;
++	int			operation = (int)p2;
++	struct flock		fl;
++	int			cmd;
++	int			ret;
++
++	/* In non-blocking lock, use F_SETLK for cmd, F_SETLKW otherwise */
++	if (operation & LX_LOCK_NB) {
++		cmd = F_SETLK;
++		operation &= ~LX_LOCK_NB; /* turn off this bit */
++	} else
++		cmd = F_SETLKW;
++
++	switch (operation) {
++		case LX_LOCK_UN:
++			fl.l_type = F_UNLCK;
++			break;
++		case LX_LOCK_SH:
++			fl.l_type = F_RDLCK;
++			break;
++		case LX_LOCK_EX:
++			fl.l_type = F_WRLCK;
++			break;
++		default:
++			return (-EINVAL);
++	}
++
++	fl.l_whence = 0;
++	fl.l_start = 0;
++	fl.l_len = 0;
++
++	ret = fcntl(fd, cmd, &fl);
++
++	if (ret == -1 && errno == EACCES)
++		return (-EWOULDBLOCK);
++
++	return ((ret == -1) ? -errno : ret);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/file.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/file.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,741 @@
++/*
++ * 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 <sys/fstyp.h>
++#include <sys/fsid.h>
++
++#include <errno.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/vnode.h>
++#include <fcntl.h>
++#include <string.h>
++#include <utime.h>
++#include <atomic.h>
++
++#include <sys/lx_syscall.h>
++#include <sys/lx_types.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_fcntl.h>
++
++static int
++install_checkpath(uintptr_t p1)
++{
++	int saved_errno = errno;
++	char path[MAXPATHLEN];
++
++	/*
++	 * The "dev" RPM package wants to modify /dev/pts, but /dev/pts is a
++	 * lofs mounted copy of /native/dev/pts, so that won't work.
++	 *
++	 * Instead, if we're trying to modify /dev/pts from install mode, just
++	 * act as if it succeded.
++	 */
++	if (uucopystr((void *)p1, path, MAXPATHLEN) == -1)
++		return (-errno);
++
++	if (strcmp(path, "/dev/pts") == 0)
++		return (0);
++
++	errno = saved_errno;
++	return (-errno);
++}
++
++/*
++ * Convert linux LX_AT_* flags to solaris AT_* flags, while verifying allowed
++ * flags have been passed. This also allows EACCESS/REMOVEDIR to be translated
++ * correctly since on linux they have the same value.
++ */
++int
++ltos_at_flag(int lflag, int allow)
++{
++	int sflag = 0;
++
++	if ((lflag & LX_AT_EACCESS) && (allow & AT_EACCESS)) {
++		lflag &= ~LX_AT_EACCESS;
++		sflag |= AT_EACCESS;
++	}
++
++	if ((lflag & LX_AT_REMOVEDIR) && (allow & AT_REMOVEDIR)) {
++		lflag &= ~LX_AT_REMOVEDIR;
++		sflag |= AT_REMOVEDIR;
++	}
++
++	if ((lflag & LX_AT_SYMLINK_NOFOLLOW) && (allow & AT_SYMLINK_NOFOLLOW)) {
++		lflag &= ~LX_AT_SYMLINK_NOFOLLOW;
++		sflag |= AT_SYMLINK_NOFOLLOW;
++	}
++
++	/* right now solaris doesn't have a _FOLLOW flag, so use a fake one */
++	if ((lflag & LX_AT_SYMLINK_FOLLOW) && (allow & LX_AT_SYMLINK_FOLLOW)) {
++		lflag &= ~LX_AT_SYMLINK_FOLLOW;
++		sflag |= LX_AT_SYMLINK_FOLLOW;
++	}
++
++	/* if flag is not zero than some flags did not hit the above code */
++	if (lflag)
++		return (-EINVAL);
++
++	return (sflag);
++}
++
++
++/*
++ * Miscellaneous file-related system calls.
++ */
++
++/*
++ * Linux creates half-duplex unnamed pipes and Solaris creates full-duplex
++ * pipes.  Thus, to get the correct semantics, our simple pipe() system
++ * call actually needs to create a named pipe, do three opens, a close, and
++ * an unlink.  This is woefully expensive.  If performance becomes a real
++ * issue, we can implement a half-duplex pipe() in the brand module.
++ */
++#define	PIPENAMESZ	32 /* enough room for /tmp/.pipe.<pid>.<num> */
++
++int
++lx_pipe(uintptr_t p1)
++{
++	static uint32_t pipecnt = 0;
++	int cnt;
++	char pipename[PIPENAMESZ];
++	int fds[3];
++	int r = 0;
++
++	fds[0] = -1;
++	fds[1] = -1;
++	fds[2] = -1;
++
++	/*
++	 * Construct a name for the named pipe: /tmp/.pipe.<pid>.<++cnt>
++	 */
++	cnt = atomic_inc_32_nv(&pipecnt);
++
++	(void) snprintf(pipename, PIPENAMESZ, "/tmp/.pipe.%d.%d",
++	    getpid(), cnt);
++
++	if (mkfifo(pipename, 0600))
++		return (-errno);
++
++	/*
++	 * To prevent either the read-only or write-only open from
++	 * blocking, we first need to open the pipe for both reading and
++	 * writing.
++	 */
++	if (((fds[2] = open(pipename, O_RDWR)) < 0) ||
++	    ((fds[0] = open(pipename, O_RDONLY)) < 0) ||
++	    ((fds[1] = open(pipename, O_WRONLY)) < 0)) {
++		r = errno;
++	} else {
++		/*
++		 * Copy the two one-way fds back to the app's address
++		 * space.
++		 */
++		if (uucopy(fds, (void *)p1, 2 * sizeof (int)))
++			r = errno;
++	}
++
++	if (fds[2] >= 0)
++		(void) close(fds[2]);
++	(void) unlink(pipename);
++
++	if (r != 0) {
++		if (fds[0] >= 0)
++			(void) close(fds[0]);
++		if (fds[1] >= 0)
++			(void) close(fds[1]);
++	}
++
++	return (-r);
++}
++
++/*
++ * On Linux, even root cannot create a link to a directory, so we have to
++ * add an explicit check.
++ */
++int
++lx_link(uintptr_t p1, uintptr_t p2)
++{
++	char *from = (char *)p1;
++	char *to = (char *)p2;
++	struct stat64 statbuf;
++
++	if ((stat64(from, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
++		return (-EPERM);
++
++	return (link(from, to) ? -errno : 0);
++}
++
++/*
++ * On Linux, an unlink of a directory returns EISDIR, not EPERM.
++ */
++int
++lx_unlink(uintptr_t p)
++{
++	char *pathname = (char *)p;
++	struct stat64 statbuf;
++
++	if ((lstat64(pathname, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
++		return (-EISDIR);
++
++	return (unlink(pathname) ? -errno : 0);
++}
++
++int
++lx_unlinkat(uintptr_t ext1, uintptr_t p1, uintptr_t p2)
++{
++	int atfd = (int)ext1;
++	char *pathname = (char *)p1;
++	int flag = (int)p2;
++	struct stat64 statbuf;
++
++	if (atfd == LX_AT_FDCWD)
++		atfd = AT_FDCWD;
++
++	flag = ltos_at_flag(flag, AT_REMOVEDIR);
++	if (flag < 0)
++		return (-EINVAL);
++
++	if (!(flag & AT_REMOVEDIR)) {
++		/* Behave like unlink() */
++		if ((fstatat64(atfd, pathname, &statbuf, AT_SYMLINK_NOFOLLOW) ==
++		    0) && S_ISDIR(statbuf.st_mode))
++			return (-EISDIR);
++	}
++
++	return (unlinkat(atfd, pathname, flag) ? -errno : 0);
++}
++
++/*
++ * fsync() and fdatasync() - On Solaris, these calls translate into a common
++ * fsync() syscall with a different parameter, so we layer on top of the librt
++ * functions instead.
++ */
++int
++lx_fsync(uintptr_t fd)
++{
++	int fildes = (int)fd;
++	struct stat64 statbuf;
++
++	if ((fstat64(fildes, &statbuf) == 0) &&
++	    (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
++		return (-EINVAL);
++
++	return (fsync((int)fd) ? -errno : 0);
++}
++
++int
++lx_fdatasync(uintptr_t fd)
++{
++	int fildes = (int)fd;
++	struct stat64 statbuf;
++
++	if ((fstat64(fildes, &statbuf) == 0) &&
++	    (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
++		return (-EINVAL);
++
++	return (fdatasync((int)fd) ? -errno : 0);
++}
++
++/*
++ * Linux, unlike Solaris, ALWAYS resets the setuid and setgid bits on a
++ * chown/fchown  regardless of whether it was done by root or not.  Therefore,
++ * we must do extra work after each chown/fchown call to emulate this behavior.
++ */
++#define	SETUGID	(S_ISUID | S_ISGID)
++
++/*
++ * [lf]chown16() - Translate the uid/gid and pass onto the real functions.
++ */
++int
++lx_chown16(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	char *filename = (char *)p1;
++	struct stat64 statbuf;
++
++	if (chown(filename, LX_UID16_TO_UID32((lx_gid16_t)p2),
++	    LX_GID16_TO_GID32((lx_gid16_t)p3)))
++		return (-errno);
++
++	if (stat64(filename, &statbuf) == 0) {
++		statbuf.st_mode &= ~S_ISUID;
++		if (statbuf.st_mode & S_IXGRP)
++			statbuf.st_mode &= ~S_ISGID;
++		(void) chmod(filename, (statbuf.st_mode & MODEMASK));
++	}
++
++	return (0);
++}
++
++int
++lx_fchown16(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int fd = (int)p1;
++	struct stat64 statbuf;
++
++	if (fchown(fd, LX_UID16_TO_UID32((lx_gid16_t)p2),
++	    LX_GID16_TO_GID32((lx_gid16_t)p3)))
++		return (-errno);
++
++	if (fstat64(fd, &statbuf) == 0) {
++		statbuf.st_mode &= ~S_ISUID;
++		if (statbuf.st_mode & S_IXGRP)
++			statbuf.st_mode &= ~S_ISGID;
++		(void) fchmod(fd, (statbuf.st_mode & MODEMASK));
++	}
++
++	return (0);
++}
++
++int
++lx_lchown16(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	return (lchown((char *)p1, LX_UID16_TO_UID32((lx_gid16_t)p2),
++	    LX_GID16_TO_GID32((lx_gid16_t)p3)) ? -errno : 0);
++}
++
++int
++lx_chown(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	char *filename = (char *)p1;
++	struct stat64 statbuf;
++	int ret;
++
++	ret = chown(filename, (uid_t)p2, (gid_t)p3);
++
++	if (ret < 0) {
++		/*
++		 * If chown() failed and we're in install mode, return success
++		 * if the the reason we failed was because the source file
++		 * didn't actually exist or if we're trying to modify /dev/pts.
++		 */
++		if ((lx_install != 0) &&
++		    ((errno == ENOENT) || (install_checkpath(p1) == 0)))
++			return (0);
++
++		return (-errno);
++	}
++
++	if (stat64(filename, &statbuf) == 0) {
++		statbuf.st_mode &= ~S_ISUID;
++		if (statbuf.st_mode & S_IXGRP)
++			statbuf.st_mode &= ~S_ISGID;
++		(void) chmod(filename, (statbuf.st_mode & MODEMASK));
++	}
++
++	return (0);
++}
++
++int
++lx_fchown(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int fd = (int)p1;
++	struct stat64 statbuf;
++
++	if (fchown(fd, (uid_t)p2, (gid_t)p3))
++		return (-errno);
++
++	if (fstat64(fd, &statbuf) == 0) {
++		statbuf.st_mode &= ~S_ISUID;
++		if (statbuf.st_mode & S_IXGRP)
++			statbuf.st_mode &= ~S_ISGID;
++		(void) fchmod(fd, (statbuf.st_mode & MODEMASK));
++	}
++
++	return (0);
++}
++
++int
++lx_chmod(uintptr_t p1, uintptr_t p2)
++{
++	int ret;
++
++	ret = chmod((const char *)p1, (mode_t)p2);
++
++	if (ret < 0) {
++		/*
++		 * If chown() failed and we're in install mode, return success
++		 * if the the reason we failed was because the source file
++		 * didn't actually exist or if we're trying to modify /dev/pts.
++		 */
++		if ((lx_install != 0) &&
++		    ((errno == ENOENT) || (install_checkpath(p1) == 0)))
++			return (0);
++
++		return (-errno);
++	}
++
++	return (0);
++}
++
++int
++lx_utime(uintptr_t p1, uintptr_t p2)
++{
++	int ret;
++
++	ret = utime((const char *)p1, (const struct utimbuf *)p2);
++
++	if (ret < 0) {
++		/*
++		 * If chown() failed and we're in install mode, return success
++		 * if the the reason we failed was because the source file
++		 * didn't actually exist or if we're trying to modify /dev/pts.
++		 */
++		if ((lx_install != 0) &&
++		    ((errno == ENOENT) || (install_checkpath(p1) == 0)))
++			return (0);
++
++		return (-errno);
++	}
++
++	return (0);
++}
++
++/*
++ * llseek() - The Linux implementation takes an additional parameter, which is
++ * the resulting position in the file.
++ */
++int
++lx_llseek(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5)
++{
++	offset_t ret;
++	offset_t *res = (offset_t *)p4;
++
++	/* SEEK_DATA and SEEK_HOLE are only valid in Solaris */
++	if ((int)p5 > SEEK_END)
++		return (-EINVAL);
++
++	if ((ret = llseek((int)p1, LX_32TO64(p3, p2), p5)) < 0)
++		return (-errno);
++
++	*res = ret;
++	return (0);
++}
++
++/*
++ * seek() - When the resultant file offset cannot be represented in 32 bits,
++ * Linux performs the seek but Solaris doesn't, though both set EOVERFLOW.  We
++ * call llseek() and then check to see if we need to return EOVERFLOW.
++ */
++int
++lx_lseek(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	offset_t offset = (offset_t)(off_t)(p2);	/* sign extend */
++	offset_t ret;
++	off_t ret32;
++
++	/* SEEK_DATA and SEEK_HOLE are only valid in Solaris */
++	if ((int)p3 > SEEK_END)
++		return (-EINVAL);
++
++	if ((ret = llseek((int)p1, offset, p3)) < 0)
++		return (-errno);
++
++	ret32 = (off_t)ret;
++	if ((offset_t)ret32 == ret)
++		return (ret32);
++	else
++		return (-EOVERFLOW);
++}
++
++/*
++ * Neither Solaris nor Linux actually returns anything to the caller, but glibc
++ * expects to see SOME value returned, so placate it and return 0.
++ */
++int
++lx_sync(void)
++{
++	sync();
++	return (0);
++}
++
++int
++lx_rmdir(uintptr_t p1)
++{
++	int r;
++
++	r = rmdir((char *)p1);
++	if (r < 0)
++		return ((errno == EEXIST) ? -ENOTEMPTY : -errno);
++	return (0);
++}
++
++/*
++ * Exactly the same as Solaris' sysfs(2), except Linux numbers their fs indices
++ * starting at 0, and Solaris starts at 1.
++ */
++int
++lx_sysfs(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int option = (int)p1;
++	int res;
++
++	/*
++	 * Linux actually doesn't have #defines for these; their sysfs(2)
++	 * man page literally defines the "option" field as being 1, 2 or 3,
++	 * corresponding to Solaris' GETFSIND, GETFSTYP and GETNFSTYP,
++	 * respectively.
++	 */
++	switch (option) {
++		case 1:
++			if ((res = sysfs(GETFSIND, (const char *)p2)) < 0)
++				return (-errno);
++
++			return (res - 1);
++
++		case 2:
++			if ((res = sysfs(GETFSTYP, (int)p2 + 1,
++			    (char *)p3)) < 0)
++				return (-errno);
++
++			return (0);
++
++		case 3:
++			if ((res = sysfs(GETNFSTYP)) < 0)
++				return (-errno);
++
++			return (res);
++
++		default:
++			break;
++	}
++
++	return (-EINVAL);
++}
++
++int
++lx_faccessat(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
++{
++	int atfd = (int)p1;
++	char *path = (char *)p2;
++	int mode = (mode_t)p3;
++	int flag = (int)p4;
++
++	if (atfd == LX_AT_FDCWD)
++		atfd = AT_FDCWD;
++
++	flag = ltos_at_flag(flag, AT_EACCESS);
++	if (flag < 0)
++		return (-EINVAL);
++
++	return (faccessat(atfd, path, mode, flag) ? -errno : 0);
++}
++
++int
++lx_futimesat(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int atfd = (int)p1;
++	char *path = (char *)p2;
++	struct timeval *times = (struct timeval *)p3;
++
++	if (atfd == LX_AT_FDCWD)
++		atfd = AT_FDCWD;
++
++	return (futimesat(atfd, path, times) ? -errno : 0);
++}
++
++
++/*
++ * Constructs an absolute path string in buf from the path of fd and the
++ * relative path string pointed to by "p1". This is required for emulating
++ * *at() system calls.
++ * Example:
++ *    If the path of fd is "/foo/bar" and path is "etc" the string returned is
++ *    "/foo/bar/etc", if the fd is a file fd then it fails with ENOTDIR.
++ *    If path is absolute then no modifcations are made to it when copied.
++ */
++static int
++getpathat(int fd, uintptr_t p1, char *outbuf, size_t outbuf_size)
++{
++	char pathbuf[MAXPATHLEN];
++	char fdpathbuf[MAXPATHLEN];
++	char *fdpath;
++	struct stat64 statbuf;
++
++	if (uucopystr((void *)p1, pathbuf, MAXPATHLEN) == -1)
++		return (-errno);
++
++	/* If the path is absolute then we can early out */
++	if ((pathbuf[0] == '/') || (fd == LX_AT_FDCWD)) {
++		(void) strlcpy(outbuf, pathbuf, outbuf_size);
++		return (0);
++	}
++
++	fdpath = lx_fd_to_path(fd, fdpathbuf, sizeof (fdpathbuf));
++	if (fdpath == NULL)
++		return (-EBADF);
++
++	if ((fstat64(fd, &statbuf) < 0))
++		return (-EBADF);
++
++	if (!S_ISDIR(statbuf.st_mode))
++		return (-ENOTDIR);
++
++	if (snprintf(outbuf, outbuf_size, "%s/%s", fdpath, pathbuf) >
++	    (outbuf_size-1))
++		return (-ENAMETOOLONG);
++
++	return (0);
++}
++
++int
++lx_mkdirat(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int atfd = (int)p1;
++	mode_t mode = (mode_t)p3;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	ret = getpathat(atfd, p2, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	return (mkdir(pathbuf, mode) ? -errno : 0);
++}
++
++int
++lx_mknodat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int atfd = (int)ext1;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	return (lx_mknod((uintptr_t)pathbuf, p2, p3));
++}
++
++int
++lx_symlinkat(uintptr_t p1, uintptr_t ext1, uintptr_t p2)
++{
++	int atfd = (int)ext1;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	ret = getpathat(atfd, p2, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	return (symlink((char *)p1, pathbuf) ? -errno : 0);
++}
++
++int
++lx_linkat(uintptr_t ext1, uintptr_t p1, uintptr_t ext2, uintptr_t p2,
++    uintptr_t p3)
++{
++	int atfd1 = (int)ext1;
++	int atfd2 = (int)ext2;
++	char pathbuf1[MAXPATHLEN];
++	char pathbuf2[MAXPATHLEN];
++	int ret;
++
++	/*
++	 * The flag specifies whether the hardlink will point to a symlink or
++	 * not, on solaris the default behaviour of link() is to dereference a
++	 * symlink and there is no obvious way to trigger the other behaviour.
++	 * So for now we just ignore this flag and act like link().
++	 */
++	/* LINTED [set but not used in function] */
++	int flag = p3;
++
++	ret = getpathat(atfd1, p1, pathbuf1, sizeof (pathbuf1));
++	if (ret < 0)
++		return (ret);
++
++	ret = getpathat(atfd2, p2, pathbuf2, sizeof (pathbuf2));
++	if (ret < 0)
++		return (ret);
++
++	return (lx_link((uintptr_t)pathbuf1, (uintptr_t)pathbuf2));
++}
++
++int
++lx_readlinkat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int atfd = (int)ext1;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	ret = readlink(pathbuf, (char *)p2, (size_t)p3);
++	if (ret < 0)
++		return (-errno);
++
++	return (ret);
++}
++
++int
++lx_fchownat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4)
++{
++	int flag;
++	int atfd = (int)ext1;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	flag = ltos_at_flag(p4, AT_SYMLINK_NOFOLLOW);
++	if (flag < 0)
++		return (-EINVAL);
++
++	ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	if (flag & AT_SYMLINK_NOFOLLOW)
++		return (lchown(pathbuf, (uid_t)p2, (gid_t)p3) ? -errno : 0);
++	else
++		return (lx_chown((uintptr_t)pathbuf, p2, p3));
++}
++
++int
++lx_fchmodat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int atfd = (int)ext1;
++	char pathbuf[MAXPATHLEN];
++	int ret;
++
++	/*
++	 * It seems that at least some versions of glibc do not set or clear
++	 * the flags arg, so checking them will result in random behaviour.
++	 */
++	/* LINTED [set but not used in function] */
++	int flag = p3;
++
++	ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
++	if (ret < 0)
++		return (ret);
++
++	return (lx_chmod((uintptr_t)pathbuf, p2));
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/fork.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/fork.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,65 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <errno.h>
++#include <unistd.h>
++#include <sys/lx_misc.h>
++
++/*
++ * fork() and vfork()
++ *
++ * These cannot be pass thru system calls because we need libc to do its own
++ * initialization or else bad things will happen (i.e. ending up with a bad
++ * schedctl page).  On Linux, there is no such thing as forkall(), so we use
++ * fork1() here.
++ */
++int
++lx_fork(void)
++{
++	int ret = fork1();
++
++	if (ret == 0 && lx_is_rpm)
++		(void) sleep(lx_rpm_delay);
++
++	return (ret == -1 ? -errno : ret);
++}
++
++/*
++ * For vfork(), we have a serious problem because the child is not allowed to
++ * return from the current frame because it will corrupt the parent's stack.
++ * Since the semantics of vfork() are rather ill-defined (other than "it's
++ * faster than fork"), we should theoretically be safe by falling back to
++ * fork1().
++ */
++int
++lx_vfork(void)
++{
++	int ret = fork1();
++
++	return (ret == -1 ? -errno : ret);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/id.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/id.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,269 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <sys/types.h>
++#include <sys/systm.h>
++#include <sys/errno.h>
++#include <sys/zone.h>
++#include <sys/lx_types.h>
++#include <sys/lx_syscall.h>
++#include <sys/cred_impl.h>
++#include <sys/policy.h>
++#include <sys/ucred.h>
++#include <sys/syscall.h>
++#include <alloca.h>
++#include <errno.h>
++#include <ucred.h>
++#include <unistd.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/lx_misc.h>
++
++int
++lx_setuid16(uintptr_t uid)
++{
++	return ((setuid(LX_UID16_TO_UID32((lx_uid16_t)uid))) ? -errno : 0);
++}
++
++int
++lx_getuid16(void)
++{
++	return ((int)LX_UID32_TO_UID16(getuid()));
++}
++
++int
++lx_setgid16(uintptr_t gid)
++{
++	return ((setgid(LX_GID16_TO_GID32((lx_gid16_t)gid))) ? -errno : 0);
++}
++
++int
++lx_getgid16(void)
++{
++	return ((int)LX_GID32_TO_GID16(getgid()));
++}
++
++int
++lx_geteuid16(void)
++{
++	return ((int)LX_UID32_TO_UID16(geteuid()));
++}
++
++int
++lx_getegid16(void)
++{
++	return ((int)LX_GID32_TO_GID16(getegid()));
++}
++
++int
++lx_geteuid(void)
++{
++	return ((int)geteuid());
++}
++
++int
++lx_getegid(void)
++{
++	return ((int)getegid());
++}
++
++int
++lx_getresuid(uintptr_t ruid, uintptr_t euid, uintptr_t suid)
++{
++	lx_uid_t lx_ruid, lx_euid, lx_suid;
++	ucred_t	*cr;
++	size_t sz;
++
++	/*
++	 * We allocate a ucred_t ourselves rather than call ucred_get(3C)
++	 * because ucred_get() calls malloc(3C), which the brand library cannot
++	 * use.  Because we allocate the space with SAFE_ALLOCA(), there's
++	 * no need to free it when we're done.
++	 */
++	sz = ucred_size();
++	cr = (ucred_t *)SAFE_ALLOCA(sz);
++	if (cr == NULL)
++		return (-ENOMEM);
++
++	if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, P_MYID, cr) != 0)
++		return (-errno);
++
++	if (((lx_ruid = (lx_uid_t)ucred_getruid(cr)) == (lx_uid_t)-1) ||
++	    ((lx_euid = (lx_uid_t)ucred_geteuid(cr)) == (lx_uid_t)-1) ||
++	    ((lx_suid = (lx_uid_t)ucred_getsuid(cr)) == (lx_uid_t)-1)) {
++		return (-errno);
++	}
++
++	if (uucopy(&lx_ruid, (void *)ruid, sizeof (lx_uid_t)) != 0)
++		return (-errno);
++
++	if (uucopy(&lx_euid, (void *)euid, sizeof (lx_uid_t)) != 0)
++		return (-errno);
++
++	return ((uucopy(&lx_suid, (void *)suid, sizeof (lx_uid_t)) != 0)
++	    ? -errno : 0);
++}
++
++int
++lx_getresuid16(uintptr_t ruid16, uintptr_t euid16, uintptr_t suid16)
++{
++	lx_uid_t lx_ruid, lx_euid, lx_suid;
++	lx_uid16_t lx_ruid16, lx_euid16, lx_suid16;
++	int rv;
++
++	if ((rv = lx_getresuid((uintptr_t)&lx_ruid, (uintptr_t)&lx_euid,
++	    (uintptr_t)&lx_suid)) != 0)
++		return (rv);
++
++	lx_ruid16 = LX_UID32_TO_UID16(lx_ruid);
++	lx_euid16 = LX_UID32_TO_UID16(lx_euid);
++	lx_suid16 = LX_UID32_TO_UID16(lx_suid);
++
++	if (uucopy(&lx_ruid16, (void *)ruid16, sizeof (lx_uid16_t)) != 0)
++		return (-errno);
++
++	if (uucopy(&lx_euid16, (void *)euid16, sizeof (lx_uid16_t)) != 0)
++		return (-errno);
++
++	return ((uucopy(&lx_suid16, (void *)suid16, sizeof (lx_uid16_t)) != 0)
++	    ? -errno : 0);
++}
++
++int
++lx_getresgid(uintptr_t rgid, uintptr_t egid, uintptr_t sgid)
++{
++	ucred_t	*cr;
++	lx_gid_t lx_rgid, lx_egid, lx_sgid;
++	size_t sz;
++
++	/*
++	 * We allocate a ucred_t ourselves rather than call ucred_get(3C)
++	 * because ucred_get() calls malloc(3C), which the brand library cannot
++	 * use.  Because we allocate the space with SAFE_ALLOCA(), there's
++	 * no need to free it when we're done.
++	 */
++	sz = ucred_size();
++	cr = (ucred_t *)SAFE_ALLOCA(sz);
++	if (cr == NULL)
++		return (-ENOMEM);
++
++	if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, P_MYID, cr) != 0)
++		return (-errno);
++
++	if (((lx_rgid = (lx_gid_t)ucred_getrgid(cr)) == (lx_gid_t)-1) ||
++	    ((lx_egid = (lx_gid_t)ucred_getegid(cr)) == (lx_gid_t)-1) ||
++	    ((lx_sgid = (lx_gid_t)ucred_getsgid(cr)) == (lx_gid_t)-1)) {
++		return (-errno);
++	}
++
++	if (uucopy(&lx_rgid, (void *)rgid, sizeof (lx_gid_t)) != 0)
++		return (-errno);
++
++	if (uucopy(&lx_egid, (void *)egid, sizeof (lx_gid_t)) != 0)
++		return (-errno);
++
++	return ((uucopy(&lx_sgid, (void *)sgid, sizeof (lx_gid_t)) != 0)
++	    ? -errno : 0);
++}
++
++int
++lx_getresgid16(uintptr_t rgid16, uintptr_t egid16, uintptr_t sgid16)
++{
++	lx_gid_t lx_rgid, lx_egid, lx_sgid;
++	lx_gid16_t lx_rgid16, lx_egid16, lx_sgid16;
++	int rv;
++
++	if ((rv = lx_getresgid((uintptr_t)&lx_rgid, (uintptr_t)&lx_egid,
++	    (uintptr_t)&lx_sgid)) != 0)
++		return (rv);
++
++	lx_rgid16 = LX_UID32_TO_UID16(lx_rgid);
++	lx_egid16 = LX_UID32_TO_UID16(lx_egid);
++	lx_sgid16 = LX_UID32_TO_UID16(lx_sgid);
++
++	if (uucopy(&lx_rgid16, (void *)rgid16, sizeof (lx_gid16_t)) != 0)
++		return (-errno);
++
++	if (uucopy(&lx_egid16, (void *)egid16, sizeof (lx_gid16_t)) != 0)
++		return (-errno);
++
++	return ((uucopy(&lx_sgid16, (void *)sgid16, sizeof (lx_gid16_t)) != 0)
++	    ? -errno : 0);
++}
++
++int
++lx_setreuid16(uintptr_t ruid, uintptr_t euid)
++{
++	return ((setreuid(LX_UID16_TO_UID32((lx_uid16_t)ruid),
++	    LX_UID16_TO_UID32((lx_uid16_t)euid))) ? -errno : 0);
++}
++
++int
++lx_setregid16(uintptr_t rgid, uintptr_t egid)
++{
++	return ((setregid(LX_UID16_TO_UID32((lx_gid16_t)rgid),
++	    LX_UID16_TO_UID32((lx_gid16_t)egid))) ? -errno : 0);
++}
++
++/*
++ * The lx brand cannot support the setfs[ug]id16/setfs[ug]id calls as that
++ * would require significant rework of Solaris' privilege mechanisms, so
++ * instead return the current effective [ug]id.
++ *
++ * In Linux, fsids track effective IDs, so returning the effective IDs works
++ * as a substitute; returning the current value also denotes failure of the
++ * call if the caller had specified something different.  We don't need to
++ * worry about setting error codes because the Linux calls don't set any.
++ */
++/*ARGSUSED*/
++int
++lx_setfsuid16(uintptr_t fsuid16)
++{
++	return (lx_geteuid16());
++}
++
++/*ARGSUSED*/
++int
++lx_setfsgid16(uintptr_t fsgid16)
++{
++	return (lx_getegid16());
++}
++
++/*ARGSUSED*/
++int
++lx_setfsuid(uintptr_t fsuid)
++{
++	return (geteuid());
++}
++
++/*ARGSUSED*/
++int
++lx_setfsgid(uintptr_t fsgid)
++{
++	return (getegid());
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/ioctl.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/ioctl.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,2719 @@
++/*
++ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <assert.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <signal.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <limits.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <stropts.h>
++#include <strings.h>
++#include <thread.h>
++#include <errno.h>
++#include <libintl.h>
++#include <sys/bitmap.h>
++#include <sys/lx_autofs.h>
++#include <sys/modctl.h>
++#include <sys/filio.h>
++#include <sys/termios.h>
++#include <sys/termio.h>
++#include <sys/sockio.h>
++#include <net/if.h>
++#include <net/if_arp.h>
++#include <sys/ptms.h>
++#include <sys/ldlinux.h>
++#include <sys/lx_ptm.h>
++#include <sys/lx_socket.h>
++#include <sys/syscall.h>
++#include <sys/brand.h>
++#include <sys/lx_audio.h>
++#include <sys/lx_ioctl.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_debug.h>
++#include <sys/ptyvar.h>
++#include <sys/audio.h>
++#include <sys/mixer.h>
++
++/* Define _KERNEL to get the devt manipulation macros. */
++#define	_KERNEL
++#include <sys/sysmacros.h>
++#undef	_KERNEL
++
++/* Maximum number of modules on a stream that we can handle. */
++#define	MAX_STRMODS	10
++
++/* Maximum buffer size for debugging messages. */
++#define	MSGBUF		1024
++
++/* Structure used to define an ioctl translator. */
++typedef struct ioc_cmd_translator {
++	int	ict_lx_cmd;
++	char	*ict_lx_cmd_str;
++	int	ict_cmd;
++	char	*ict_cmd_str;
++	int	(*ict_func)(int fd, struct stat *stat,
++	    int cmd, char *cmd_str, intptr_t arg);
++} ioc_cmd_translator_t;
++
++/*
++ * Structures used to associate a group of ioctl translators with
++ * a specific device.
++ */
++typedef struct ioc_dev_translator {
++	char			*idt_driver;
++	major_t			idt_major;
++
++	/* Array of command translators. */
++	ioc_cmd_translator_t	*idt_cmds;
++} ioc_dev_translator_t;
++
++/*
++ * Structures used to associate a group of ioctl translators with
++ * a specific filesystem.
++ */
++typedef struct ioc_fs_translator {
++	char			*ift_filesystem;
++
++	/* Array of command translators. */
++	ioc_cmd_translator_t	*ift_cmds;
++} ioc_fs_translator_t;
++
++/* Structure used to define a unsupported ioctl error codes. */
++typedef struct ioc_errno_translator {
++	int	iet_lx_cmd;
++	char	*iet_lx_cmd_str;
++	int	iet_errno;
++} ioc_errno_translator_t;
++
++/* Structure used to convert oss format flags into Solaris options. */
++typedef struct oss_fmt_translator {
++	int	oft_oss_fmt;
++	int	oft_encoding;
++	int	oft_precision;
++} oss_fmt_translator_t;
++
++/* Translator forward declerations. */
++static oss_fmt_translator_t oft_table[];
++static ioc_cmd_translator_t ioc_translators_file[];
++static ioc_cmd_translator_t ioc_translators_fifo[];
++static ioc_cmd_translator_t ioc_translators_sock[];
++static ioc_dev_translator_t ioc_translator_ptm;
++static ioc_dev_translator_t *ioc_translators_dev[];
++static ioc_fs_translator_t *ioc_translators_fs[];
++static ioc_errno_translator_t ioc_translators_errno[];
++
++/*
++ * Interface name table.
++ */
++typedef struct ifname_map {
++	char	im_linux[IFNAMSIZ];
++	char	im_solaris[IFNAMSIZ];
++	struct ifname_map *im_next;
++} ifname_map_t;
++
++static ifname_map_t *ifname_map;
++static mutex_t ifname_mtx;
++
++/*
++ * Macros and structures to help convert integers to string
++ * values that they represent (for displaying in debug output).
++ */
++#define	I2S_ENTRY(x)	{ x, #x },
++#define	I2S_END		{ 0, NULL }
++
++typedef struct int2str {
++	int	i2s_int;
++	char	*i2s_str;
++} int2str_t;
++
++static int2str_t st_mode_strings[] = {
++	I2S_ENTRY(S_IFIFO)
++	I2S_ENTRY(S_IFCHR)
++	I2S_ENTRY(S_IFDIR)
++	I2S_ENTRY(S_IFBLK)
++	I2S_ENTRY(S_IFREG)
++	I2S_ENTRY(S_IFLNK)
++	I2S_ENTRY(S_IFSOCK)
++	I2S_ENTRY(S_IFDOOR)
++	I2S_ENTRY(S_IFPORT)
++	I2S_END
++};
++
++static int2str_t oss_fmt_str[] = {
++	I2S_ENTRY(LX_OSS_AFMT_QUERY)
++	I2S_ENTRY(LX_OSS_AFMT_MU_LAW)
++	I2S_ENTRY(LX_OSS_AFMT_A_LAW)
++	I2S_ENTRY(LX_OSS_AFMT_IMA_ADPCM)
++	I2S_ENTRY(LX_OSS_AFMT_U8)
++	I2S_ENTRY(LX_OSS_AFMT_S16_LE)
++	I2S_ENTRY(LX_OSS_AFMT_S16_BE)
++	I2S_ENTRY(LX_OSS_AFMT_S8)
++	I2S_ENTRY(LX_OSS_AFMT_U16_LE)
++	I2S_ENTRY(LX_OSS_AFMT_U16_BE)
++	I2S_ENTRY(LX_OSS_AFMT_MPEG)
++	I2S_END
++};
++
++static void
++lx_ioctl_msg(int fd, int cmd, char *lx_cmd_str, struct stat *stat, char *msg)
++{
++	int	errno_backup = errno;
++	char	*path, path_buf[MAXPATHLEN];
++
++	assert(msg != NULL);
++
++	if (lx_debug_enabled == 0)
++		return;
++
++	path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
++	if (path == NULL)
++		path = "?";
++
++	if (lx_cmd_str == NULL)
++		lx_cmd_str = "?";
++
++	/* Display the initial error message and extended ioctl information. */
++	lx_debug("\t%s", msg);
++	lx_debug("\tlx_ioctl(): cmd = 0x%x - %s, fd = %d - %s",
++	    cmd, lx_cmd_str, fd, path);
++
++	/* Display information about the target file, if it's available. */
++	if (stat != NULL) {
++		major_t	fd_major = getmajor(stat->st_rdev);
++		minor_t	fd_minor = getminor(stat->st_rdev);
++		int	fd_mode = stat->st_mode & S_IFMT;
++		char	*fd_mode_str = "unknown";
++		char	buf[LX_MSG_MAXLEN];
++		int	i;
++
++		/* Translate the file type bits into a string. */
++		for (i = 0; st_mode_strings[i].i2s_str != NULL; i++) {
++			if (fd_mode != st_mode_strings[i].i2s_int)
++				continue;
++			fd_mode_str = st_mode_strings[i].i2s_str;
++			break;
++		}
++
++		(void) snprintf(buf, sizeof (buf),
++		    "\tlx_ioctl(): mode = %s", fd_mode_str);
++
++		if ((fd_mode == S_IFCHR) || (fd_mode == S_IFBLK)) {
++			char	*fd_driver[MODMAXNAMELEN + 1];
++			int	i;
++
++			/* This is a device so display the devt. */
++			i = strlen(buf);
++			(void) snprintf(buf + i, sizeof (buf) - i,
++			    "; rdev = [%d, %d]", fd_major, fd_minor);
++
++			/* Try to display the drivers name. */
++			if (modctl(MODGETNAME,
++			    fd_driver, sizeof (fd_driver), &fd_major) == 0)
++				i = strlen(buf);
++				(void) snprintf(buf + i, sizeof (buf) - i,
++				    "; driver = %s", fd_driver);
++		}
++		lx_debug(buf);
++	}
++
++	/* Restore errno. */
++	errno = errno_backup;
++}
++
++static int
++ldlinux_check(int fd)
++{
++	struct str_mlist	mlist[MAX_STRMODS];
++	struct str_list		strlist;
++	int			i;
++
++	/* Get the number of modules on the stream. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, I_LIST, "I_LIST");
++	if ((i = ioctl(fd, I_LIST, (struct str_list *)NULL)) < 0) {
++		lx_debug("\tldlinux_check(): unable to count stream modules");
++		return (-errno);
++	}
++
++	/* Sanity check the number of modules on the stream. */
++	assert(i <= MAX_STRMODS);
++
++	/* Get the list of modules on the stream. */
++	strlist.sl_nmods = i;
++	strlist.sl_modlist = mlist;
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, I_LIST, "I_LIST");
++	if (ioctl(fd, I_LIST, &strlist) < 0) {
++		lx_debug("\tldlinux_check(): unable to list stream modules");
++		return (-errno);
++	}
++
++	for (i = 0; i < strlist.sl_nmods; i++)
++		if (strcmp(strlist.sl_modlist[i].l_name, LDLINUX_MOD) == 0)
++			return (1);
++
++	return (0);
++}
++
++static int
++ioctl_istr(int fd, int cmd, char *cmd_str, void *arg, int arg_len)
++{
++	struct strioctl istr;
++
++	istr.ic_cmd = cmd;
++	istr.ic_len = arg_len;
++	istr.ic_timout = 0;
++	istr.ic_dp = arg;
++
++	lx_debug("\tioctl_istr(%d, 0x%x - %s, ...)", fd, cmd, cmd_str);
++	if (ioctl(fd, I_STR, &istr) < 0)
++		return (-1);
++	return (0);
++}
++
++/*
++ * Add an interface name mapping if it doesn't already exist.
++ *
++ * Interfaces with IFF_LOOPBACK flag get renamed to loXXX.
++ * Interfaces with IFF_BROADCAST flag get renamed to ethXXX.
++ *
++ * Caller locks the name table.
++ */
++static int
++ifname_add(char *if_name, int if_flags)
++{
++	static int eth_index = 0;
++	static int lo_index = 0;
++	ifname_map_t **im_pp;
++
++	for (im_pp = &ifname_map; *im_pp; im_pp = &(*im_pp)->im_next)
++		if (strncmp((*im_pp)->im_solaris, if_name, IFNAMSIZ) == 0)
++			return (0);
++
++	*im_pp = calloc(1, sizeof (ifname_map_t));
++	if (*im_pp == NULL)
++		return (-1);
++
++	(void) strlcpy((*im_pp)->im_solaris, if_name, IFNAMSIZ);
++	if (if_flags & IFF_LOOPBACK) {
++		/* Loopback */
++		if (lo_index == 0)
++			(void) strlcpy((*im_pp)->im_linux, "lo", IFNAMSIZ);
++		else
++			(void) snprintf((*im_pp)->im_linux, IFNAMSIZ,
++			    "lo:%d", lo_index);
++		lo_index++;
++	} else if (if_flags & IFF_BROADCAST) {
++		/* Assume ether if it has a broadcast address */
++		(void) snprintf((*im_pp)->im_linux, IFNAMSIZ,
++		    "eth%d", eth_index);
++		eth_index++;
++	} else {
++		/* Do not translate unknown interfaces */
++		(void) strlcpy((*im_pp)->im_linux, if_name, IFNAMSIZ);
++	}
++
++	lx_debug("map interface %s -> %s", if_name, (*im_pp)->im_linux);
++
++	return (0);
++}
++
++static int
++ifname_cmp(const void *p1, const void *p2)
++{
++	struct ifreq *rp1 = (struct ifreq *)p1;
++	struct ifreq *rp2 = (struct ifreq *)p2;
++
++	return (strncmp(rp1->ifr_name, rp2->ifr_name, IFNAMSIZ));
++}
++
++/*
++ * (Re-)scan all interfaces and add them to the name table.
++ * Caller locks the name table.
++ */
++static int
++ifname_scan(void)
++{
++	struct ifconf conf;
++	int i, fd, ifcount;
++
++	conf.ifc_buf = NULL;
++
++	if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
++		goto fail;
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, SIOCGIFNUM, "SIOCGIFNUM");
++	if (ioctl(fd, SIOCGIFNUM, &ifcount) < 0) {
++		lx_debug("\tifname_scan(): unable to get number of interfaces");
++		goto fail;
++	}
++
++	conf.ifc_len = ifcount * sizeof (struct ifreq);
++	if ((conf.ifc_buf = calloc(ifcount, sizeof (struct ifreq))) == NULL)
++		goto fail;
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, SIOCGIFCONF, "SIOCGIFCONF");
++	if (ioctl(fd, SIOCGIFCONF, &conf) < 0) {
++		lx_debug("\tifname_scan(): unable to get interfaces");
++		goto fail;
++	}
++
++	/* Get the interface flags */
++	for (i = 0; i < ifcount; i++) {
++		lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++		    fd, SIOCGIFFLAGS, "SIOCGIFFLAGS");
++		if (ioctl(fd, SIOCGIFFLAGS, &conf.ifc_req[i]) < 0) {
++			conf.ifc_req[i].ifr_flags = 0;
++			lx_debug("\tifname_scan(): unable to get flags for %s",
++			    conf.ifc_req[i].ifr_name);
++		}
++	}
++
++	/*
++	 * Sort the interfaces by name to preserve the order
++	 * across reboots of this zone.  Note that the order of
++	 * interface names won't be consistent across network
++	 * configuration changes.  ie.  If network interfaces
++	 * are added or removed from a zone (either dynamically
++	 * or statically) the network interfaces names to physical
++	 * network interface mappings that linux apps see may
++	 * change.
++	 */
++	qsort(conf.ifc_req, ifcount, sizeof (struct ifreq), ifname_cmp);
++
++	/* Add to the name table */
++	for (i = 0; i < ifcount; i++)
++		if (ifname_add(conf.ifc_req[i].ifr_name,
++		    conf.ifc_req[i].ifr_flags) != 0)
++			goto fail;
++
++	(void) close(fd);
++	free(conf.ifc_buf);
++
++	return (0);
++
++fail:
++	if (fd >= 0)
++		(void) close(fd);
++	if (conf.ifc_buf != NULL)
++		free(conf.ifc_buf);
++
++	return (-1);
++}
++
++static int
++ifname_from_linux(char *name)
++{
++	int pass;
++	ifname_map_t *im_p;
++
++	(void) mutex_lock(&ifname_mtx);
++
++	for (pass = 0; pass < 2; pass++) {
++		for (im_p = ifname_map; im_p; im_p = im_p->im_next)
++			if (strncmp(im_p->im_linux, name, IFNAMSIZ) == 0)
++				break;
++		if (im_p != NULL || (pass == 0 && ifname_scan() != 0))
++			break;
++	}
++
++	(void) mutex_unlock(&ifname_mtx);
++
++	if (im_p) {
++		(void) strlcpy(name, im_p->im_solaris, IFNAMSIZ);
++		return (0);
++	}
++
++	return (-1);
++}
++
++static int
++ifname_from_solaris(char *name)
++{
++	int pass;
++	ifname_map_t *im_p;
++
++	(void) mutex_lock(&ifname_mtx);
++
++	for (pass = 0; pass < 2; pass++) {
++		for (im_p = ifname_map; im_p; im_p = im_p->im_next)
++			if (strncmp(im_p->im_solaris, name, IFNAMSIZ) == 0)
++				break;
++		if (im_p != NULL || (pass == 0 && ifname_scan() != 0))
++			break;
++	}
++
++	(void) mutex_unlock(&ifname_mtx);
++
++	if (im_p) {
++		(void) strlcpy(name, im_p->im_linux, IFNAMSIZ);
++		return (0);
++	}
++
++	return (-1);
++}
++
++/*
++ * Called to initialize the ioctl translation subsystem.
++ */
++int
++lx_ioctl_init()
++{
++	int i, ret;
++
++	/* Figure out the major numbers for our devices translators. */
++	for (i = 0; ioc_translators_dev[i] != NULL; i++) {
++		ioc_dev_translator_t *idt = ioc_translators_dev[i];
++
++		ret = modctl(MODGETMAJBIND,
++		    idt->idt_driver, strlen(idt->idt_driver) + 1,
++		    &idt->idt_major);
++
++		if (ret != 0) {
++			lx_err(gettext("%s%s) failed: %s\n"),
++			    "lx_ioctl_init(): modctl(MODGETMAJBIND, ",
++			    idt->idt_driver, strerror(errno));
++			lx_err(gettext("%s: %s translator disabled for: %s\n"),
++			    "lx_ioctl_init()", "ioctl", idt->idt_driver);
++			idt->idt_major = (major_t)-1;
++		}
++	}
++
++	/* Create the interface name table */
++	if (ifname_scan() != 0)
++		lx_err("lx_ioctl_init(): ifname_scan() failed\n");
++
++	return (0);
++}
++
++static ioc_cmd_translator_t *
++lx_ioctl_find_ict_cmd(ioc_cmd_translator_t *ict, int cmd)
++{
++	assert(ict != NULL);
++	while ((ict != NULL) && (ict->ict_func != NULL)) {
++		if (cmd == ict->ict_lx_cmd)
++			return (ict);
++		ict++;
++	}
++	return (NULL);
++}
++
++/*
++ * Main entry point for the ioctl translater.
++ */
++int
++lx_ioctl(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int			fd = (int)p1;
++	int			cmd = (int)p2;
++	intptr_t		arg = (uintptr_t)p3;
++	struct stat		stat;
++	ioc_cmd_translator_t	*ict = NULL;
++	ioc_errno_translator_t	*iet = NULL;
++	major_t			fd_major;
++	int			i, ret;
++
++	if (fstat(fd, &stat) != 0) {
++		lx_ioctl_msg(fd, cmd, NULL, NULL,
++		    "lx_ioctl(): fstat() failed");
++
++		/*
++		 * Linux ioctl(2) is only documented to return EBADF, EFAULT,
++		 * EINVAL or ENOTTY.
++		 *
++		 * EINVAL is documented to be "Request or argp is not valid",
++		 * so it's reasonable to force any errno that's not EBADF,
++		 * EFAULT or ENOTTY to be EINVAL.
++		 */
++		if ((errno != EBADF) && (errno != EFAULT) && (errno != ENOTTY))
++			errno = EINVAL;
++
++		return (-errno);	/* errno already set. */
++	}
++
++	switch (stat.st_mode & S_IFMT) {
++	default:
++		break;
++	case S_IFREG:
++		/* Use file translators. */
++		ict = ioc_translators_file;
++		break;
++
++	case S_IFSOCK:
++		/* Use socket translators. */
++		ict = ioc_translators_sock;
++		break;
++
++	case S_IFIFO:
++		/* Use fifo translators. */
++		ict = ioc_translators_fifo;
++		break;
++
++	case S_IFCHR:
++		fd_major = getmajor(stat.st_rdev);
++
++		/*
++		 * Look through all the device translators to see if there
++		 * is one for this device.
++		 */
++		for (i = 0; ioc_translators_dev[i] != NULL; i++) {
++			if (fd_major != ioc_translators_dev[i]->idt_major)
++				continue;
++
++			/* We found a translator for this device. */
++			ict = ioc_translators_dev[i]->idt_cmds;
++			break;
++		}
++		break;
++	}
++
++	/*
++	 * Search the selected translator group to see if we have a
++	 * translator for this specific command.
++	 */
++	if ((ict != NULL) &&
++	    ((ict = lx_ioctl_find_ict_cmd(ict, cmd)) != NULL)) {
++		/* We found a translator for this command, invoke it. */
++		lx_ioctl_msg(fd, cmd, ict->ict_lx_cmd_str, &stat,
++		    "lx_ioctl(): emulating ioctl");
++
++		ret = ict->ict_func(fd, &stat, ict->ict_cmd, ict->ict_cmd_str,
++		    arg);
++
++		if ((ret < 0) && (ret != -EBADF) && (ret != -EFAULT) &&
++		    (ret != -ENOTTY))
++			ret = -EINVAL;
++
++		return (ret);
++	}
++
++	/*
++	 * If we didn't find a file or device translator for this
++	 * command then try to find a filesystem translator for
++	 * this command.
++	 */
++	for (i = 0; ioc_translators_fs[i] != NULL; i++) {
++		if (strcmp(stat.st_fstype,
++		    ioc_translators_fs[i]->ift_filesystem) != 0)
++			continue;
++
++		/* We found a translator for this filesystem. */
++		ict = ioc_translators_fs[i]->ift_cmds;
++		break;
++	}
++
++	/*
++	 * Search the selected translator group to see if we have a
++	 * translator for this specific command.
++	 */
++	if ((ict != NULL) &&
++	    ((ict = lx_ioctl_find_ict_cmd(ict, cmd)) != NULL)) {
++		/* We found a translator for this command, invoke it. */
++		lx_ioctl_msg(fd, cmd, ict->ict_lx_cmd_str, &stat,
++		    "lx_ioctl(): emulating ioctl");
++		ret = ict->ict_func(fd, &stat, ict->ict_cmd, ict->ict_cmd_str,
++		    arg);
++
++		if ((ret < 0) && (ret != -EBADF) && (ret != -EFAULT) &&
++		    (ret != -ENOTTY))
++			ret = -EINVAL;
++
++		return (ret);
++	}
++
++	/*
++	 * No translator for this ioctl was found.
++	 * Check if there is an errno translator.
++	 */
++	for (iet = ioc_translators_errno; iet->iet_lx_cmd_str != NULL; iet++) {
++		if (cmd != iet->iet_lx_cmd)
++			continue;
++
++		/* We found a an errno translator for this ioctl. */
++		lx_ioctl_msg(fd, cmd, iet->iet_lx_cmd_str, &stat,
++		    "lx_ioctl(): emulating errno");
++
++		ret = -iet->iet_errno;
++
++		if ((ret < 0) && (ret != -EBADF) && (ret != -EFAULT) &&
++		    (ret != -ENOTTY))
++			ret = -EINVAL;
++
++		return (ret);
++	}
++
++	lx_ioctl_msg(fd, cmd, NULL, &stat,
++	    "lx_ioctl(): unsupported linux ioctl");
++	lx_unsupported(gettext("lx_ioctl(): unsupported linux ioctl (%d)"),
++	    cmd);
++	return (-EINVAL);
++}
++
++
++/*
++ * Ioctl translator functions.
++ */
++/*
++ * Used by translators that want to explicitly return EINVAL for an
++ * ioctl(2) instead of having the translation framework do it implicitly.
++ * This allows us to indicate which unsupported ioctl(2)s should not
++ * trigger a SIGSYS when running in LX_STRICT mode.
++ */
++/* ARGSUSED */
++static int
++ict_einval(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	return (-EINVAL);
++}
++
++static int
++/*ARGSUSED*/
++ict_pass(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	int ret;
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, cmd, cmd_str);
++	ret = ioctl(fd, cmd, arg);
++	return (ret < 0 ? -errno : ret);
++}
++
++static int
++/*ARGSUSED*/
++ict_tcsbrkp(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	int ret, dur = 0;
++
++	assert(cmd == LX_TCSBRKP);
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TCSBRK, "TCSBRK");
++	ret = ioctl(fd, TCSBRK, (intptr_t)&dur);
++	return (ret < 0 ? -errno : ret);
++}
++
++static int
++/*ARGSUSED*/
++ict_sioifoob(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	int req, *reqp = (int *)arg;
++	int len, val;
++
++	assert(cmd == SIOCATMARK);
++
++	if (uucopy(reqp, &req, sizeof (req)) != 0)
++		return (-errno);
++
++	len = sizeof (val);
++
++	/*
++	 * Linux expects a SIOCATMARK of a UDP socket to return EINVAL, while
++	 * Solaris allows it.
++	 */
++	if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &len) < 0) {
++		lx_debug("ict_siofmark: getsockopt failed, errno %d", errno);
++		return (-EINVAL);
++	}
++
++	if ((len != sizeof (val)) || (val != SOCK_STREAM))
++		return (-EINVAL);
++
++	if (ioctl(fd, cmd, &req) < 0)
++		return (-errno);
++
++	if (uucopy(&req, reqp, sizeof (req)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_sioifreq(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct ifreq req, *reqp = (struct ifreq *)arg;
++
++	assert(cmd == SIOCGIFFLAGS || cmd == SIOCSIFFLAGS ||
++	    cmd == SIOCGIFADDR || cmd == SIOCSIFADDR ||
++	    cmd == SIOCGIFDSTADDR || cmd == SIOCSIFDSTADDR ||
++	    cmd == SIOCGIFBRDADDR || cmd == SIOCSIFBRDADDR ||
++	    cmd == SIOCGIFNETMASK || cmd == SIOCSIFNETMASK ||
++	    cmd == SIOCGIFMETRIC || cmd == SIOCSIFMETRIC ||
++	    cmd == SIOCGIFMTU || cmd == SIOCSIFMTU);
++
++	/* Copy in the data */
++	if (uucopy(reqp, &req, sizeof (struct ifreq)) != 0)
++		return (-errno);
++
++	if (ifname_from_linux(req.ifr_name) < 0)
++		return (-EINVAL);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, %.14s",
++	    fd, cmd, cmd_str, req.ifr_name);
++
++	if (ioctl(fd, cmd, &req) < 0)
++		return (-errno);
++
++	if (ifname_from_solaris(req.ifr_name) < 0)
++		return (-EINVAL);
++
++	/* Copy out the data */
++	if (uucopy(&req, reqp, sizeof (struct ifreq)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_siocgifconf(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct ifconf	conf, *confp = (struct ifconf *)arg;
++	int		i, ifcount, ret;
++
++	assert(cmd == LX_SIOCGIFCONF);
++
++	/* Copy in the data. */
++	if (uucopy(confp, &conf, sizeof (conf)) != 0)
++		return (-errno);
++
++	if (conf.ifc_len == 0) {
++		/* They want to know how many interfaces there are. */
++		lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++		    fd, SIOCGIFNUM, "SIOCGIFNUM");
++		if (ioctl(fd, SIOCGIFNUM, (intptr_t)&ifcount) < 0)
++			return (-errno);
++		conf.ifc_len = ifcount * sizeof (struct ifreq);
++
++		/* Check if we're done. */
++		if (conf.ifc_buf == NULL) {
++			/* Copy out the data. */
++			if (uucopy(&conf, confp, sizeof (conf)) != 0)
++				return (-errno);
++			return (0);
++		}
++	}
++
++	/* Get interface configuration list. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, SIOCGIFCONF, "SIOCGIFCONF");
++	ret = ioctl(fd, SIOCGIFCONF, &conf);
++	if (ret < 0)
++		return (-errno);
++
++	/* Rename interfaces to linux */
++	for (i = 0; i < conf.ifc_len / sizeof (struct ifreq); i++)
++		if (ifname_from_solaris(conf.ifc_req[i].ifr_name) < 0)
++			return (-EINVAL);
++
++	/* Copy out the data */
++	if (uucopy(&conf, confp, sizeof (conf)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_siocifhwaddr(int fd, struct stat *stat, int cmd, char *cmd_str,
++    intptr_t arg)
++{
++	struct ifreq req, *reqp = (struct ifreq *)arg;
++	struct arpreq arpreq;
++
++	assert(cmd == LX_SIOCGIFHWADDR || cmd == LX_SIOCSIFHWADDR);
++
++	/* Copy in the data */
++	if (uucopy(reqp, &req, sizeof (struct ifreq)) != 0)
++		return (-errno);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, lx %.14s)",
++	    fd, cmd,
++	    (cmd == LX_SIOCGIFHWADDR) ? "SIOCGIFHWADDR" : "SIOCSIFHWADDR",
++	    req.ifr_name);
++
++	/*
++	 * We're not going to support SIOCSIFHWADDR, but we need to be
++	 * able to check the result of the uucopy first to see if the command
++	 * should have returned EFAULT.
++	 */
++	if (cmd == LX_SIOCSIFHWADDR) {
++		lx_unsupported(gettext(
++		    "lx_ioctl(): unsupported linux ioctl: %s"),
++		    "SIOCSIFHWADDR");
++		return (-EINVAL);
++	}
++
++	if (strcmp(req.ifr_name, "lo") == 0 ||
++	    strncmp(req.ifr_name, "lo:", 3) == 0) {
++		/* Abuse ifr_addr for linux ifr_hwaddr */
++		bzero(&req.ifr_addr, sizeof (struct sockaddr));
++		req.ifr_addr.sa_family = LX_ARPHRD_LOOPBACK;
++
++		/* Copy out the data */
++		if (uucopy(&req, reqp, sizeof (struct ifreq)) != 0)
++			return (-errno);
++
++		return (0);
++	}
++
++	if (ifname_from_linux(req.ifr_name) < 0)
++		return (-EINVAL);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, %.14s)",
++	    fd, SIOCGIFADDR, "SIOCGIFADDR", req.ifr_name);
++
++	if (ioctl(fd, SIOCGIFADDR, &req) < 0)
++		return (-errno);
++
++	bcopy(&req.ifr_addr, &arpreq.arp_pa, sizeof (struct sockaddr));
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, SIOCGARP, "SIOCGARP");
++
++	if (ioctl(fd, SIOCGARP, &arpreq) < 0)
++		return (-errno);
++
++	if (ifname_from_solaris(req.ifr_name) < 0)
++		return (-EINVAL);
++
++	/* Abuse ifr_addr for linux ifr_hwaddr */
++	bcopy(&arpreq.arp_ha, &req.ifr_addr, sizeof (struct sockaddr));
++	if (strncmp(req.ifr_name, "eth", 3) == 0)
++		req.ifr_addr.sa_family = LX_ARPHRD_ETHER;
++	else
++		req.ifr_addr.sa_family = LX_ARPHRD_VOID;
++
++	/* Copy out the data */
++	if (uucopy(&req, reqp, sizeof (struct ifreq)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static void
++l2s_termios(struct lx_termios *l_tios, struct termios *s_tios)
++{
++	assert((l_tios != NULL) && (s_tios != NULL));
++
++	bzero(s_tios, sizeof (*s_tios));
++
++	s_tios->c_iflag = l_tios->c_iflag;
++	s_tios->c_oflag = l_tios->c_oflag;
++	s_tios->c_cflag = l_tios->c_cflag;
++
++	s_tios->c_lflag = l_tios->c_lflag;
++	if (s_tios->c_lflag & ICANON) {
++		s_tios->c_cc[VEOF] = l_tios->c_cc[LX_VEOF];
++		s_tios->c_cc[VEOL] = l_tios->c_cc[LX_VEOL];
++	} else {
++		s_tios->c_cc[VMIN] = l_tios->c_cc[LX_VMIN];
++		s_tios->c_cc[VTIME] = l_tios->c_cc[LX_VTIME];
++	}
++
++	s_tios->c_cc[VEOL2] = l_tios->c_cc[LX_VEOL2];
++	s_tios->c_cc[VERASE] = l_tios->c_cc[LX_VERASE];
++	s_tios->c_cc[VKILL] = l_tios->c_cc[LX_VKILL];
++	s_tios->c_cc[VREPRINT] = l_tios->c_cc[LX_VREPRINT];
++	s_tios->c_cc[VLNEXT] = l_tios->c_cc[LX_VLNEXT];
++	s_tios->c_cc[VWERASE] = l_tios->c_cc[LX_VWERASE];
++	s_tios->c_cc[VINTR] = l_tios->c_cc[LX_VINTR];
++	s_tios->c_cc[VQUIT] = l_tios->c_cc[LX_VQUIT];
++	s_tios->c_cc[VSWTCH] = l_tios->c_cc[LX_VSWTC];
++	s_tios->c_cc[VSTART] = l_tios->c_cc[LX_VSTART];
++	s_tios->c_cc[VSTOP] = l_tios->c_cc[LX_VSTOP];
++	s_tios->c_cc[VSUSP] = l_tios->c_cc[LX_VSUSP];
++	s_tios->c_cc[VDISCARD] = l_tios->c_cc[LX_VDISCARD];
++}
++
++static void
++l2s_termio(struct lx_termio *l_tio, struct termio *s_tio)
++{
++	assert((l_tio != NULL) && (s_tio != NULL));
++
++	bzero(s_tio, sizeof (*s_tio));
++
++	s_tio->c_iflag = l_tio->c_iflag;
++	s_tio->c_oflag = l_tio->c_oflag;
++	s_tio->c_cflag = l_tio->c_cflag;
++
++	s_tio->c_lflag = l_tio->c_lflag;
++	if (s_tio->c_lflag & ICANON) {
++		s_tio->c_cc[VEOF] = l_tio->c_cc[LX_VEOF];
++	} else {
++		s_tio->c_cc[VMIN] = l_tio->c_cc[LX_VMIN];
++		s_tio->c_cc[VTIME] = l_tio->c_cc[LX_VTIME];
++	}
++
++	s_tio->c_cc[VINTR] = l_tio->c_cc[LX_VINTR];
++	s_tio->c_cc[VQUIT] = l_tio->c_cc[LX_VQUIT];
++	s_tio->c_cc[VERASE] = l_tio->c_cc[LX_VERASE];
++	s_tio->c_cc[VKILL] = l_tio->c_cc[LX_VKILL];
++	s_tio->c_cc[VSWTCH] = l_tio->c_cc[LX_VSWTC];
++}
++
++static void
++termios2lx_cc(struct lx_termios *l_tios, struct lx_cc *lio)
++{
++	assert((l_tios != NULL) && (lio != NULL));
++
++	bzero(lio, sizeof (*lio));
++
++	lio->veof = l_tios->c_cc[LX_VEOF];
++	lio->veol = l_tios->c_cc[LX_VEOL];
++	lio->vmin = l_tios->c_cc[LX_VMIN];
++	lio->vtime = l_tios->c_cc[LX_VTIME];
++}
++
++static void
++termio2lx_cc(struct lx_termio *l_tio, struct lx_cc *lio)
++{
++	assert((l_tio != NULL) && (lio != NULL));
++
++	bzero(lio, sizeof (*lio));
++
++	lio->veof = l_tio->c_cc[LX_VEOF];
++	lio->veol = 0;
++	lio->vmin = l_tio->c_cc[LX_VMIN];
++	lio->vtime = l_tio->c_cc[LX_VTIME];
++}
++
++static void
++s2l_termios(struct termios *s_tios, struct lx_termios *l_tios)
++{
++	assert((s_tios != NULL) && (l_tios != NULL));
++
++	bzero(l_tios, sizeof (*l_tios));
++
++	l_tios->c_iflag = s_tios->c_iflag;
++	l_tios->c_oflag = s_tios->c_oflag;
++	l_tios->c_cflag = s_tios->c_cflag;
++	l_tios->c_lflag = s_tios->c_lflag;
++
++	if (s_tios->c_lflag & ICANON) {
++		l_tios->c_cc[LX_VEOF] = s_tios->c_cc[VEOF];
++		l_tios->c_cc[LX_VEOL] = s_tios->c_cc[VEOL];
++	} else {
++		l_tios->c_cc[LX_VMIN] = s_tios->c_cc[VMIN];
++		l_tios->c_cc[LX_VTIME] = s_tios->c_cc[VTIME];
++	}
++
++	l_tios->c_cc[LX_VEOL2] = s_tios->c_cc[VEOL2];
++	l_tios->c_cc[LX_VERASE] = s_tios->c_cc[VERASE];
++	l_tios->c_cc[LX_VKILL] = s_tios->c_cc[VKILL];
++	l_tios->c_cc[LX_VREPRINT] = s_tios->c_cc[VREPRINT];
++	l_tios->c_cc[LX_VLNEXT] = s_tios->c_cc[VLNEXT];
++	l_tios->c_cc[LX_VWERASE] = s_tios->c_cc[VWERASE];
++	l_tios->c_cc[LX_VINTR] = s_tios->c_cc[VINTR];
++	l_tios->c_cc[LX_VQUIT] = s_tios->c_cc[VQUIT];
++	l_tios->c_cc[LX_VSWTC] = s_tios->c_cc[VSWTCH];
++	l_tios->c_cc[LX_VSTART] = s_tios->c_cc[VSTART];
++	l_tios->c_cc[LX_VSTOP] = s_tios->c_cc[VSTOP];
++	l_tios->c_cc[LX_VSUSP] = s_tios->c_cc[VSUSP];
++	l_tios->c_cc[LX_VDISCARD] = s_tios->c_cc[VDISCARD];
++}
++
++static void
++s2l_termio(struct termio *s_tio, struct lx_termio *l_tio)
++{
++	assert((s_tio != NULL) && (l_tio != NULL));
++
++	bzero(l_tio, sizeof (*l_tio));
++
++	l_tio->c_iflag = s_tio->c_iflag;
++	l_tio->c_oflag = s_tio->c_oflag;
++	l_tio->c_cflag = s_tio->c_cflag;
++	l_tio->c_lflag = s_tio->c_lflag;
++
++	if (s_tio->c_lflag & ICANON) {
++		l_tio->c_cc[LX_VEOF] = s_tio->c_cc[VEOF];
++	} else {
++		l_tio->c_cc[LX_VMIN] = s_tio->c_cc[VMIN];
++		l_tio->c_cc[LX_VTIME] = s_tio->c_cc[VTIME];
++	}
++
++	l_tio->c_cc[LX_VINTR] = s_tio->c_cc[VINTR];
++	l_tio->c_cc[LX_VQUIT] = s_tio->c_cc[VQUIT];
++	l_tio->c_cc[LX_VERASE] = s_tio->c_cc[VERASE];
++	l_tio->c_cc[LX_VKILL] = s_tio->c_cc[VKILL];
++	l_tio->c_cc[LX_VSWTC] = s_tio->c_cc[VSWTCH];
++}
++
++static int
++/*ARGSUSED*/
++ict_tcsets(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct lx_termios	l_tios, *l_tiosp = (struct lx_termios *)arg;
++	struct termios		s_tios;
++	struct lx_cc		lio;
++	int			ldlinux, ret;
++
++	assert(cmd == TCSETS || cmd == TCSETSW || cmd == TCSETSF);
++
++	/* Copy in the data. */
++	if (uucopy(l_tiosp, &l_tios, sizeof (l_tios)) != 0)
++		return (-errno);
++
++	/*
++	 * The TIOCSETLD/TIOCGETLD ioctls are only supported by the
++	 * ldlinux strmod.  So make sure the module exists on the
++	 * target stream before we invoke the ioctl.
++	 */
++	if ((ldlinux = ldlinux_check(fd)) < 0)
++		return (ldlinux);
++
++	if (ldlinux == 1) {
++		termios2lx_cc(&l_tios, &lio);
++		if (ioctl_istr(fd, TIOCSETLD, "TIOCSETLD",
++		    &lio, sizeof (lio)) < 0)
++			return (-errno);
++	}
++
++	l2s_termios(&l_tios, &s_tios);
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, cmd, cmd_str);
++	ret = ioctl(fd, cmd, (intptr_t)&s_tios);
++	return ((ret < 0) ? -errno : ret);
++}
++
++static int
++/*ARGSUSED*/
++ict_tcseta(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct lx_termio	l_tio, *l_tiop = (struct lx_termio *)arg;
++	struct termio		s_tio;
++	struct lx_cc		lio;
++	int			ldlinux, ret;
++
++	assert(cmd == TCSETA || cmd == TCSETAW || cmd == TCSETAF);
++
++	/* Copy in the data. */
++	if (uucopy(l_tiop, &l_tio, sizeof (l_tio)) != 0)
++		return (-errno);
++
++	/*
++	 * The TIOCSETLD/TIOCGETLD ioctls are only supported by the
++	 * ldlinux strmod.  So make sure the module exists on the
++	 * target stream before we invoke the ioctl.
++	 */
++	if ((ldlinux = ldlinux_check(fd)) < 0)
++		return (ldlinux);
++
++	if (ldlinux == 1) {
++		termio2lx_cc(&l_tio, &lio);
++		if (ioctl_istr(fd, TIOCSETLD, "TIOCSETLD",
++		    &lio, sizeof (lio)) < 0)
++			return (-errno);
++	}
++
++	l2s_termio(&l_tio, &s_tio);
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, cmd, cmd_str);
++	ret = ioctl(fd, cmd, (intptr_t)&s_tio);
++	return ((ret < 0) ? -errno : ret);
++}
++
++/*
++ * The Solaris TIOCGPGRP ioctl does not have exactly the same semantics as
++ * the Linux one. To mimic Linux semantics we have to do some extra work
++ * normally done by the Solaris version of tcgetpgrp().
++ */
++static int
++/*ARGSUSED*/
++ict_tiocgpgrp(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	pid_t	ttysid, mysid;
++	int	ret;
++
++	assert(cmd == LX_TIOCGPGRP);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TIOCGSID, "TIOCGSID");
++	if (ioctl(fd, TIOCGSID, (intptr_t)&ttysid) < 0)
++		return (-errno);
++	if ((mysid = getsid(0)) < 0)
++		return (-errno);
++	if (mysid != ttysid)
++		return (-ENOTTY);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TIOCGPGRP, "TIOCGPGRP");
++	ret = ioctl(fd, TIOCGPGRP, arg);
++	return ((ret < 0) ? -errno : ret);
++}
++
++static int
++/*ARGSUSED*/
++ict_sptlock(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	assert(cmd == LX_TIOCSPTLCK);
++
++	/*
++	 * The success/fail return values are different between Linux
++	 * and Solaris.   Linux expects 0 or -1.  Solaris can return
++	 * positive number on success.
++	 */
++	if (ioctl_istr(fd, UNLKPT, "UNLKPT", NULL, 0) < 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_gptn(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	int		ptyno, *ptynop = (int *)arg;
++	pt_own_t	pto;
++
++	assert(cmd == LX_TIOCGPTN);
++	assert(getmajor(stat->st_rdev) == ioc_translator_ptm.idt_major);
++
++	/* This operation is only valid for the lx_ptm device. */
++	ptyno = LX_PTM_DEV_TO_PTS(stat->st_rdev);
++
++	/*
++	 * We'd like to just use grantpt() directly, but we can't since
++	 * it assumes the fd node that's passed to it is a ptm node,
++	 * and in our case it's an lx_ptm node.  It also relies on
++	 * naming services to get the current process group name.
++	 * Hence we have to invoke the OWNERPT ioctl directly here.
++	 */
++	pto.pto_ruid = getuid();
++	pto.pto_rgid = getgid();
++	if (ioctl_istr(fd, OWNERPT, "OWNERPT", &pto, sizeof (pto)) != 0)
++		return (-EACCES);
++
++	/* Copy out the data. */
++	if (uucopy(&ptyno, ptynop, sizeof (ptyno)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_tiocgwinsz(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct winsize	winsize, *winsizep = (struct winsize *)arg;
++
++	assert(cmd == LX_TIOCGWINSZ);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, TIOCGWINSZ, "TIOCGWINSZ");
++	if (ioctl(fd, TIOCGWINSZ, arg) >= 0)
++		return (0);
++	if (errno != EINVAL)
++		return (-errno);
++
++	bzero(&winsize, sizeof (winsize));
++	if (uucopy(&winsize, winsizep, sizeof (winsize)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_tcgets_emulate(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	struct lx_termios	l_tios, *l_tiosp = (struct lx_termios *)arg;
++	struct termios		s_tios;
++
++	assert(cmd == LX_TCGETS);
++
++	if (syscall(SYS_brand, B_TTYMODES, &s_tios) < 0)
++		return (-errno);
++
++	/* Now munge the data to how Linux wants it. */
++	s2l_termios(&s_tios, &l_tios);
++	if (uucopy(&l_tios, l_tiosp, sizeof (l_tios)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_tcgets_native(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	struct lx_termios	l_tios, *l_tiosp = (struct lx_termios *)arg;
++	struct termios		s_tios;
++	struct lx_cc		lio;
++	int			ldlinux;
++
++	assert(cmd == LX_TCGETS);
++
++	if ((ldlinux = ldlinux_check(fd)) < 0)
++		return (ldlinux);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TCGETS, "TCGETS");
++	if (ioctl(fd, TCGETS, (intptr_t)&s_tios) < 0)
++		return (-errno);
++
++	/* Now munge the data to how Linux wants it. */
++	s2l_termios(&s_tios, &l_tios);
++
++	/*
++	 * The TIOCSETLD/TIOCGETLD ioctls are only supported by the
++	 * ldlinux strmod.  So make sure the module exists on the
++	 * target stream before we invoke the ioctl.
++	 */
++	if (ldlinux != 0) {
++		if (ioctl_istr(fd, TIOCGETLD, "TIOCGETLD",
++		    &lio, sizeof (lio)) < 0)
++			return (-errno);
++
++		l_tios.c_cc[LX_VEOF] = lio.veof;
++		l_tios.c_cc[LX_VEOL] = lio.veol;
++		l_tios.c_cc[LX_VMIN] = lio.vmin;
++		l_tios.c_cc[LX_VTIME] = lio.vtime;
++	}
++
++	/* Copy out the data. */
++	if (uucopy(&l_tios, l_tiosp, sizeof (l_tios)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_tcgeta(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	struct lx_termio	l_tio, *l_tiop = (struct lx_termio *)arg;
++	struct termio		s_tio;
++	struct lx_cc		lio;
++	int			ldlinux;
++
++	assert(cmd == LX_TCGETA);
++
++	if ((ldlinux = ldlinux_check(fd)) < 0)
++		return (ldlinux);
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TCGETA, "TCGETA");
++	if (ioctl(fd, TCGETA, (intptr_t)&s_tio) < 0)
++		return (-errno);
++
++	/* Now munge the data to how Linux wants it. */
++	s2l_termio(&s_tio, &l_tio);
++
++	/*
++	 * The TIOCSETLD/TIOCGETLD ioctls are only supported by the
++	 * ldlinux strmod.  So make sure the module exists on the
++	 * target stream before we invoke the ioctl.
++	 */
++	if (ldlinux != 0) {
++		if (ioctl_istr(fd, TIOCGETLD, "TIOCGETLD",
++		    &lio, sizeof (lio)) < 0)
++			return (-errno);
++
++		l_tio.c_cc[LX_VEOF] = lio.veof;
++		l_tio.c_cc[LX_VMIN] = lio.vmin;
++		l_tio.c_cc[LX_VTIME] = lio.vtime;
++	}
++
++	/* Copy out the data. */
++	if (uucopy(&l_tio, l_tiop, sizeof (l_tio)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_tiocsctty(int fd, struct stat *stat, int cmd, char *cmd_str, intptr_t arg)
++{
++	pid_t	mysid, ttysid;
++
++	if ((mysid = getsid(0)) < 0)
++		return (-errno);
++
++	/* Check if this fd is already our ctty. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TIOCGSID, "TIOCGSID");
++	if (ioctl(fd, TIOCGSID, (intptr_t)&ttysid) >= 0)
++		if (mysid == ttysid)
++			return (0);
++
++	/*
++	 * Need to make sure we're a session leader, otherwise the
++	 * TIOCSCTTY ioctl will fail.
++	 */
++	if (mysid != getpid())
++		(void) setpgrp();
++
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, TIOCSCTTY, "TIOCSCTTY");
++	if (ioctl(fd, TIOCSCTTY, 0) < 0)
++		return (-errno);
++	return (0);
++}
++
++/*
++ * /dev/dsp ioctl translators and support
++ */
++static int
++i_is_dsp_dev(int fd)
++{
++	int minor;
++
++	/*
++	 * This is a cloning device so we have to ask the driver
++	 * what kind of minor node this is.
++	 */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_GETMINORNUM, "LXA_IOC_GETMINORNUM");
++	if (ioctl(fd, LXA_IOC_GETMINORNUM, &minor) < 0)
++		return (-EINVAL);
++	if (minor != LXA_MINORNUM_DSP)
++		return (-EINVAL);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_reset(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	int err;
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* Nothing to really do on Solaris. */
++	return (0);
++}
++
++static void
++i_oss_fmt_str(char *buf, int buf_size, uint_t mask)
++{
++	int i, first = 1;
++
++	assert(buf != NULL);
++
++	buf[0] = '\0';
++	for (i = 0; oss_fmt_str[i].i2s_str != NULL; i++) {
++		if ((oss_fmt_str[i].i2s_int != mask) &&
++		    ((oss_fmt_str[i].i2s_int & mask) == 0))
++			continue;
++		if (first)
++			first = 0;
++		else
++			(void) strlcat(buf, " | ", buf_size);
++		(void) strlcat(buf, oss_fmt_str[i].i2s_str, buf_size);
++	}
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_getfmts(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	audio_info_t	sa_info;
++	char		buf[MSGBUF];
++	uint_t		*maskp = (uint_t *)arg;
++	uint_t		mask = 0;
++	int		i, amode, err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_GETFMTS);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* We need to know the access mode for the file. */
++	if ((amode = fcntl(fd, F_GETFL)) < 0)
++		return (-EINVAL);
++	amode &= O_ACCMODE;
++	assert((amode == O_RDONLY) || (amode == O_WRONLY) || (amode == O_RDWR));
++
++	/* Test to see what Linux oss formats the target device supports. */
++	for (i = 0; oft_table[i].oft_oss_fmt != 0; i++) {
++
++		/* Initialize the mode request. */
++		AUDIO_INITINFO(&sa_info);
++
++		/* Translate a Linux oss format into Solaris settings. */
++		if ((amode == O_RDONLY) || (amode == O_RDWR)) {
++			sa_info.record.encoding = oft_table[i].oft_encoding;
++			sa_info.record.precision = oft_table[i].oft_precision;
++		}
++		if ((amode == O_WRONLY) || (amode == O_RDWR)) {
++			sa_info.play.encoding = oft_table[i].oft_encoding;
++			sa_info.play.precision = oft_table[i].oft_precision;
++		}
++
++		/* Send the request. */
++		lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++		    fd, AUDIO_SETINFO, "AUDIO_SETINFO");
++		if (ioctl(fd, AUDIO_SETINFO, &sa_info) < 0)
++			continue;
++
++		/* This Linux oss format is supported. */
++		mask |= oft_table[i].oft_oss_fmt;
++	}
++
++	if (lx_debug_enabled != 0) {
++		i_oss_fmt_str(buf, sizeof (buf), mask);
++		lx_debug("\toss formats supported = 0x%x (%s)", mask, buf);
++	}
++	if (uucopy(&mask, maskp, sizeof (mask)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_setfmts(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	audio_info_t	sa_info;
++	char		buf[MSGBUF];
++	uint_t		*maskp = (uint_t *)arg;
++	uint_t		mask;
++	int		i, amode, err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_SETFMTS);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(maskp, &mask, sizeof (mask)) != 0)
++		return (-errno);
++
++	if (lx_debug_enabled != 0) {
++		i_oss_fmt_str(buf, sizeof (buf), mask);
++		lx_debug("\toss formats request = 0x%x (%s)", mask, buf);
++	}
++
++	if ((mask == (uint_t)-1) || (mask == 0)) {
++		lx_debug("\tXXX: possible oss formats query?");
++		return (-EINVAL);
++	}
++
++	/* Check if multiple format bits were specified. */
++	if (!BIT_ONLYONESET(mask))
++		return (-EINVAL);
++
++	/* Decode the oss format request into a native format. */
++	for (i = 0; oft_table[i].oft_oss_fmt != 0; i++) {
++		if (oft_table[i].oft_oss_fmt == mask)
++			break;
++	}
++	if (oft_table[i].oft_oss_fmt == 0)
++		return (-EINVAL);
++
++	/* We need to know the access mode for the file. */
++	if ((amode = fcntl(fd, F_GETFL)) < 0)
++		return (-EINVAL);
++	amode &= O_ACCMODE;
++	assert((amode == O_RDONLY) || (amode == O_WRONLY) || (amode == O_RDWR));
++
++	/* Initialize the mode request. */
++	AUDIO_INITINFO(&sa_info);
++
++	/* Translate the Linux oss request into a Solaris request. */
++	if ((amode == O_RDONLY) || (amode == O_RDWR)) {
++		sa_info.record.encoding = oft_table[i].oft_encoding;
++		sa_info.record.precision = oft_table[i].oft_precision;
++	}
++	if ((amode == O_WRONLY) || (amode == O_RDWR)) {
++		sa_info.play.encoding = oft_table[i].oft_encoding;
++		sa_info.play.precision = oft_table[i].oft_precision;
++	}
++
++	/* Send the request. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, AUDIO_SETINFO, "AUDIO_SETINFO");
++	return ((ioctl(fd, AUDIO_SETINFO, &sa_info) < 0) ? -errno : 0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_channels(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	audio_info_t	sa_info;
++	uint_t		*channelsp = (uint_t *)arg;
++	uint_t		channels;
++	int		amode, err;
++
++	assert((cmd == LX_OSS_SNDCTL_DSP_CHANNELS) ||
++	    (cmd == LX_OSS_SNDCTL_DSP_STEREO));
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(channelsp, &channels, sizeof (channels)) != 0)
++		return (-errno);
++
++	lx_debug("\toss %s request = 0x%x (%u)",
++	    (cmd == LX_OSS_SNDCTL_DSP_CHANNELS) ? "channel" : "stereo",
++	    channels, channels);
++
++	if (channels == (uint_t)-1) {
++		lx_debug("\tXXX: possible channel/stereo query?");
++		return (-EINVAL);
++	}
++
++	if (cmd == LX_OSS_SNDCTL_DSP_STEREO) {
++		/*
++		 * There doesn't seem to be any documentation for
++		 * SNDCTL_DSP_STEREO.  Looking at source that uses or
++		 * used this ioctl seems to indicate that the
++		 * functionality provided by this ioctl has been
++		 * subsumed by the SNDCTL_DSP_CHANNELS ioctl.  It
++		 * seems that the only arguments ever passed to
++		 * the SNDCTL_DSP_STEREO.  Ioctl are boolean values
++		 * of '0' or '1'.  Hence we'll start out strict and
++		 * only support those values.
++		 *
++		 * Some online forum discussions about this ioctl
++		 * seemed to indicate that in case of success it
++		 * returns the "stereo" setting (ie, either
++		 * '0' for mono or '1' for stereo).
++		 */
++		if ((channels != 0) && (channels != 1)) {
++			lx_debug("\tinvalid stereo request");
++			return (-EINVAL);
++		}
++		channels += 1;
++	} else {
++		/* Limit the system to one or two channels. */
++		if ((channels != 1) && (channels != 2)) {
++			lx_debug("\tinvalid channel request");
++			return (-EINVAL);
++		}
++	}
++
++	/* We need to know the access mode for the file. */
++	if ((amode = fcntl(fd, F_GETFL)) < 0)
++		return (-EINVAL);
++	amode &= O_ACCMODE;
++	assert((amode == O_RDONLY) || (amode == O_WRONLY) || (amode == O_RDWR));
++
++	/* Initialize the channel request. */
++	AUDIO_INITINFO(&sa_info);
++
++	/* Translate the Linux oss request into a Solaris request. */
++	if ((amode == O_RDONLY) || (amode == O_RDWR))
++		sa_info.record.channels = channels;
++	if ((amode == O_WRONLY) || (amode == O_RDWR))
++		sa_info.play.channels = channels;
++
++	/* Send the request. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, AUDIO_SETINFO, "AUDIO_SETINFO");
++	if (ioctl(fd, AUDIO_SETINFO, &sa_info) < 0)
++		return (-errno);
++
++	if (cmd == LX_OSS_SNDCTL_DSP_STEREO)
++		return (channels - 1);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_speed(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	audio_info_t	sa_info;
++	uint_t		*speedp = (uint_t *)arg;
++	uint_t		speed;
++	int		amode, err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_SPEED);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(speedp, &speed, sizeof (speed)) != 0)
++		return (-errno);
++
++	lx_debug("\toss speed request = 0x%x (%u)", speed, speed);
++
++	if (speed == (uint_t)-1) {
++		lx_debug("\tXXX: possible oss speed query?");
++		return (-EINVAL);
++	}
++
++	/* We need to know the access mode for the file. */
++	if ((amode = fcntl(fd, F_GETFL)) < 0)
++		return (-EINVAL);
++	amode &= O_ACCMODE;
++	assert((amode == O_RDONLY) || (amode == O_WRONLY) || (amode == O_RDWR));
++
++	/* Initialize the speed request. */
++	AUDIO_INITINFO(&sa_info);
++
++	/* Translate the Linux oss request into a Solaris request. */
++	if ((amode == O_RDONLY) || (amode == O_RDWR))
++		sa_info.record.sample_rate = speed;
++	if ((amode == O_WRONLY) || (amode == O_RDWR))
++		sa_info.play.sample_rate = speed;
++
++	/* Send the request. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, AUDIO_SETINFO, "AUDIO_SETINFO");
++	return ((ioctl(fd, AUDIO_SETINFO, &sa_info) < 0) ? -errno : 0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_getblksize(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_frag_info_t	fi;
++	uint_t		*blksizep = (uint_t *)arg;
++	uint_t		blksize;
++	int		err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_GETBLKSIZE);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* Query the current fragment count and size. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_GET_FRAG_INFO, "LXA_IOC_GET_FRAG_INFO");
++	if (ioctl(fd, LXA_IOC_GET_FRAG_INFO, &fi) < 0)
++		return (-errno);
++
++	blksize = fi.lxa_fi_size;
++
++	if (uucopy(&blksize, blksizep, sizeof (blksize)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_getspace(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lx_oss_audio_buf_info_t	*spacep = (lx_oss_audio_buf_info_t *)arg;
++	lx_oss_audio_buf_info_t	space;
++	lxa_frag_info_t		fi;
++	int			err;
++
++	assert((cmd == LX_OSS_SNDCTL_DSP_GETOSPACE) ||
++	    (cmd == LX_OSS_SNDCTL_DSP_GETISPACE));
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* Query the current fragment count and size. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_GET_FRAG_INFO, "LXA_IOC_GET_FRAG_INFO");
++	if (ioctl(fd, LXA_IOC_GET_FRAG_INFO, &fi) < 0)
++		return (-errno);
++
++	/* Return the current fragment count and size. */
++	space.fragstotal = fi.lxa_fi_cnt;
++	space.fragsize = fi.lxa_fi_size;
++
++	/*
++	 * We'll lie and tell applications that they can always write
++	 * out at least one fragment without blocking.
++	 */
++	space.fragments = 1;
++	space.bytes = space.fragsize;
++
++	if (cmd == LX_OSS_SNDCTL_DSP_GETOSPACE)
++		lx_debug("\toss get output space result = ");
++	if (cmd == LX_OSS_SNDCTL_DSP_GETISPACE)
++		lx_debug("\toss get input space result = ");
++
++	lx_debug("\t\tbytes = 0x%x (%u), fragments = 0x%x (%u)",
++	    space.bytes, space.bytes, space.fragments, space.fragments);
++	lx_debug("\t\tfragtotal = 0x%x (%u), fragsize = 0x%x (%u)",
++	    space.fragstotal, space.fragstotal,
++	    space.fragsize, space.fragsize);
++
++	if (uucopy(&space, spacep, sizeof (space)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_setfragment(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_frag_info_t	fi;
++	uint_t		*fraginfop = (uint_t *)arg;
++	uint_t		fraginfo, frag_size, frag_cnt;
++	int		err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_SETFRAGMENT);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(fraginfop, &fraginfo, sizeof (fraginfo)) != 0)
++		return (-errno);
++
++	/*
++	 * The argument to this ioctl is a 32-bit integer of the
++	 * format 0x MMMM SSSS where:
++	 * 	SSSS - requests a fragment size of 2^SSSS
++	 * 	MMMM - requests a maximum fragment count of 2^MMMM
++	 * if MMMM is 0x7fff then the application is requesting
++	 * no limits on the number of fragments.
++	 */
++
++	frag_size = fraginfo & 0xffff;
++	frag_cnt = fraginfo >> 16;
++
++	lx_debug("\toss fragment request: "
++	    "power size = 0x%x (%u), power cnt = 0x%x (%u)",
++	    frag_size, frag_size, frag_cnt, frag_cnt);
++
++	/* Limit the supported fragment size from 2^4 to 2^31. */
++	if ((frag_size < 4) || (frag_size > 31))
++		return (-EINVAL);
++
++	/* Limit the number of fragments from 2^1 to 2^32. */
++	if (((frag_cnt < 1) || (frag_cnt > 32)) && (frag_cnt != 0x7fff))
++		return (-EINVAL);
++
++	/* Expand the fragment values. */
++	frag_size = 1 << frag_size;
++	if ((frag_cnt == 32) || (frag_cnt == 0x7fff)) {
++		frag_cnt = UINT_MAX;
++	} else {
++		frag_cnt = 1 << frag_cnt;
++	}
++
++	lx_debug("\toss fragment request: "
++	    "translated size = 0x%x (%u), translated cnt = 0x%x (%u)",
++	    frag_size, frag_size, frag_cnt, frag_cnt);
++
++	fi.lxa_fi_size = frag_size;
++	fi.lxa_fi_cnt = frag_cnt;
++
++	/* Set the current fragment count and size. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_SET_FRAG_INFO, "LXA_IOC_SET_FRAG_INFO");
++	return ((ioctl(fd, LXA_IOC_SET_FRAG_INFO, &fi) < 0) ? -errno : 0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_getcaps(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	uint_t	*capsp = (uint_t *)arg;
++	uint_t	caps;
++	int	err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_GETCAPS);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/*
++	 * Report that we support mmap access
++	 * this is where things start to get fun.
++	 */
++	caps = LX_OSS_DSP_CAP_MMAP | LX_OSS_DSP_CAP_TRIGGER;
++
++	if (uucopy(&caps, capsp, sizeof (caps)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_settrigger(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	uint_t		*triggerp = (uint_t *)arg;
++	uint_t		trigger;
++	int		err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_SETTRIGGER);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(triggerp, &trigger, sizeof (trigger)) != 0)
++		return (-errno);
++
++	lx_debug("\toss set trigger request = 0x%x (%u)",
++	    trigger, trigger);
++
++	/* We only support two types of trigger requests. */
++	if ((trigger != LX_OSS_PCM_DISABLE_OUTPUT) &&
++	    (trigger != LX_OSS_PCM_ENABLE_OUTPUT))
++		return (-EINVAL);
++
++	/*
++	 * We only support triggers on devices open for write access,
++	 * but we don't need to check for that here since the driver will
++	 * verify this for us.
++	 */
++
++	/* Send the trigger command to the audio device. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_MMAP_OUTPUT, "LXA_IOC_MMAP_OUTPUT");
++	return ((ioctl(fd, LXA_IOC_MMAP_OUTPUT, &trigger) < 0) ? -errno : 0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_getoptr(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	static uint_t		bytes = 0;
++	lx_oss_count_info_t	ci;
++	lxa_frag_info_t		fi;
++	audio_info_t		ai;
++	int			ptr, err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_GETOPTR);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* Query the current fragment size. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_GET_FRAG_INFO, "LXA_IOC_GET_FRAG_INFO");
++	if (ioctl(fd, LXA_IOC_GET_FRAG_INFO, &fi) < 0)
++		return (-errno);
++
++	/* Figure out how many samples have been played. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, AUDIO_GETINFO, "AUDIO_GETINFO");
++	if (ioctl(fd, AUDIO_GETINFO, &ai) < 0)
++		return (-errno);
++	ci.bytes = ai.play.samples + ai.record.samples;
++
++	/*
++	 * Figure out how many fragments of audio have gone out since
++	 * the last call to this ioctl.
++	 */
++	ci.blocks = (ci.bytes - bytes) / fi.lxa_fi_size;
++	bytes = ci.bytes;
++
++	/* Figure out the current fragment offset for mmap audio output. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_MMAP_PTR, "LXA_IOC_MMAP_PTR");
++	if (ioctl(fd, LXA_IOC_MMAP_PTR, &ptr) < 0) {
++		/*
++		 * We really should return an error here, but some
++		 * application (*cough* *cough* flash) expect this
++		 * ioctl to work even if they haven't mmaped the
++		 * device.
++		 */
++		ci.ptr = 0;
++	} else {
++		ci.ptr = ptr;
++	}
++
++	lx_debug("\toss get output ptr result = ");
++	lx_debug("\t\t"
++	    "bytes = 0x%x (%u), blocks = 0x%x (%u), ptr = 0x%x (%u)",
++	    ci.bytes, ci.bytes, ci.blocks, ci.blocks, ci.ptr, ci.ptr);
++
++	if (uucopy(&ci, (void *)arg, sizeof (ci)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_sndctl_dsp_sync(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	int		amode, err;
++
++	assert(cmd == LX_OSS_SNDCTL_DSP_SYNC);
++
++	/* Ioctl is only supported on dsp audio devices. */
++	if ((err = i_is_dsp_dev(fd)) != 0)
++		return (err);
++
++	/* We need to know the access mode for the file. */
++	if ((amode = fcntl(fd, F_GETFL)) < 0)
++		return (-EINVAL);
++	amode &= O_ACCMODE;
++	assert((amode == O_RDONLY) || (amode == O_WRONLY) || (amode == O_RDWR));
++
++	/*
++	 * A sync is basically a noop for record only device.
++	 * We check for this here because on Linux a sync on a record
++	 * only device returns success immediately.  But the Solaris
++	 * equivalent to a drain operation is a AUDIO_DRAIN, and if
++	 * it's issued to a record only device it will fail and return
++	 * EINVAL.
++	 */
++	if (amode == O_RDONLY)
++		return (0);
++
++	/* Drain any pending output. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, AUDIO_DRAIN, "AUDIO_DRAIN");
++	return ((ioctl(fd, AUDIO_DRAIN, NULL) < 0) ? -errno : 0);
++}
++
++/*
++ * /dev/mixer ioctl translators and support
++ *
++ * There are some interesting things to take note of for supporting
++ * /dev/mixer ioctls.
++ *
++ * 1) We report support for the following mixer resources:
++ * 	VOLUME, PCM, MIC
++ *
++ * 2) We assume the following number of channels for each mixer resource:
++ *	VOLUME:	2 channels
++ *	PCM:	2 channels
++ *	MIC:	1 channel
++ *
++ * 3) OSS sets the gain on each channel independently but on Solaris
++ *    there is only one gain value and a balance value.  So we need
++ *    to do some translation back and forth.
++ *
++ * 4) OSS assumes direct access to hardware but Solaris provides
++ *    virtualized audio device access (where everyone who opens /dev/audio
++ *    get a virtualized audio channel stream, all of which are merged
++ *    together by a software mixer before reaching the hardware).  Hence
++ *    mapping OSS mixer resources to Solaris mixer resources takes some
++ *    work.  VOLUME and Mic resources are mapped to the actual underlying
++ *    audio hardware resources.  PCM resource are mapped to the virtual
++ *    audio channel output level.  This mapping becomes more complicated
++ *    if there are no open audio output channels.  In this case the
++ *    lx_audio device caches the PCM channels setting for us and applies
++ *    them to any new audio output channels that get opened.  (This
++ *    is the reason that we don't use AUDIO_SETINFO ioctls directly
++ *    but instead the lx_audio driver custom LXA_IOC_MIXER_SET_*
++ *    and LXA_IOC_MIXER_GET_* ioctls.)  For more information see
++ *    the comments in lx_audio.c.
++ */
++static int
++i_is_mixer_dev(int fd)
++{
++	int minor;
++
++	/*
++	 * This is a cloning device so we have to ask the driver
++	 * what kind of minor node this is.
++	 */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_GETMINORNUM, "LXA_IOC_GETMINORNUM");
++	if (ioctl(fd, LXA_IOC_GETMINORNUM, &minor) < 0)
++		return (-EINVAL);
++	if (minor != LXA_MINORNUM_MIXER)
++		return (-EINVAL);
++	return (0);
++}
++
++static int
++i_oss_mixer_ml_to_val(lxa_mixer_levels_t *ml, uint_t *val)
++{
++	int range, val1, val2;
++
++	/* Deal with the other easy case, both channels have the same level. */
++	if (ml->lxa_ml_balance == AUDIO_MID_BALANCE) {
++		*val = LX_OSS_MIXER_ENC2(
++		    LX_OSS_S2L_GAIN(ml->lxa_ml_gain),
++		    LX_OSS_S2L_GAIN(ml->lxa_ml_gain));
++		assert(LX_OSS_MIXER_2CH_OK(*val));
++		return (0);
++	}
++
++	/* Decode the balance/gain into two separate levels. */
++	if (ml->lxa_ml_balance > AUDIO_MID_BALANCE) {
++		val2 = ml->lxa_ml_gain;
++
++		range = AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE;
++		val1 = AUDIO_RIGHT_BALANCE - ml->lxa_ml_balance;
++		val1 = (val2 * val1) / range;
++	} else {
++		assert(ml->lxa_ml_balance < AUDIO_MID_BALANCE);
++		val1 = ml->lxa_ml_gain;
++
++		range = AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE;
++		val2 = ml->lxa_ml_balance;
++		val2 = (val1 * val2) / range;
++	}
++
++	*val = LX_OSS_MIXER_ENC2(LX_OSS_S2L_GAIN(val1),
++	    LX_OSS_S2L_GAIN(val2));
++	return (0);
++}
++
++static int
++i_oss_mixer_val_to_ml(uint_t val, lxa_mixer_levels_t *ml_old,
++    lxa_mixer_levels_t *ml)
++{
++	int range, val1, val2;
++
++	if (!LX_OSS_MIXER_2CH_OK(val))
++		return (-EINVAL);
++
++	val1 = LX_OSS_MIXER_DEC1(val);
++	val2 = LX_OSS_MIXER_DEC2(val);
++
++	/*
++	 * Deal with the easy case.
++	 * Both channels have the same non-zero level.
++	 */
++	if ((val1 != 0) && (val1 == val2)) {
++		ml->lxa_ml_gain = LX_OSS_L2S_GAIN(val1);
++		ml->lxa_ml_balance = AUDIO_MID_BALANCE;
++		return (0);
++	}
++
++	/* If both levels are zero, preserve the current balance setting. */
++	if ((val1 == 0) && (val2 == 0)) {
++		ml->lxa_ml_gain = 0;
++		ml->lxa_ml_balance = ml_old->lxa_ml_balance;
++		return (0);
++	}
++
++	/*
++	 * First set the gain to match the highest channel value volume.
++	 * Then use the balance to simulate lower volume on the second
++	 * channel.
++	 */
++	if (val1 > val2) {
++		ml->lxa_ml_gain = LX_OSS_L2S_GAIN(val1);
++
++		range = AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE;
++		ml->lxa_ml_balance = 0;
++		ml->lxa_ml_balance += ((val2 * range) / val1);
++	} else {
++		assert(val1 < val2);
++
++		ml->lxa_ml_gain = LX_OSS_L2S_GAIN(val2);
++
++		range = AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE;
++		ml->lxa_ml_balance = AUDIO_RIGHT_BALANCE;
++		ml->lxa_ml_balance -= ((val1 * range) / val2);
++	}
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_mixer_read_volume(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_mixer_levels_t	ml;
++	uint_t			*valp = (uint_t *)arg;
++	uint_t			val;
++	char			*cmd_txt;
++	int			err, cmd_new;
++
++	assert((cmd == LX_OSS_SOUND_MIXER_READ_VOLUME) ||
++	    (cmd == LX_OSS_SOUND_MIXER_READ_PCM));
++
++	/* Ioctl is only supported on mixer audio devices. */
++	if ((err = i_is_mixer_dev(fd)) != 0)
++		return (err);
++
++	if (cmd == LX_OSS_SOUND_MIXER_READ_VOLUME) {
++		cmd_new = LXA_IOC_MIXER_GET_VOL;
++		cmd_txt = "LXA_IOC_MIXER_GET_VOL";
++	}
++	if (cmd == LX_OSS_SOUND_MIXER_READ_PCM) {
++		cmd_new = LXA_IOC_MIXER_GET_PCM;
++		cmd_txt = "LXA_IOC_MIXER_GET_PCM";
++	}
++
++	/* Attempt to set the device output gain. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, cmd_new, cmd_txt);
++	if (ioctl(fd, cmd_new, &ml) < 0)
++		return (-errno);
++
++	lx_debug("\tlx_audio mixer results, "
++	    "gain = 0x%x (%u), balance = 0x%x (%u)",
++	    ml.lxa_ml_gain, ml.lxa_ml_gain,
++	    ml.lxa_ml_balance, ml.lxa_ml_balance);
++
++	assert(LXA_MIXER_LEVELS_OK(&ml));
++
++	/* Translate the mixer levels struct to an OSS mixer value. */
++	if ((err = i_oss_mixer_ml_to_val(&ml, &val)) != 0)
++		return (err);
++	assert(LX_OSS_MIXER_2CH_OK(val));
++
++	lx_debug("\toss get mixer %s result = 0x%x (%u)",
++	    (cmd == LX_OSS_SOUND_MIXER_READ_VOLUME) ? "volume" : "pcm",
++	    val, val);
++
++	if (uucopy(&val, valp, sizeof (val)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_mixer_write_volume(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_mixer_levels_t	ml, ml_old;
++	uint_t			*valp = (uint_t *)arg;
++	uint_t			val;
++	char			*cmd_txt;
++	int			err, cmd_new;
++
++	assert((cmd == LX_OSS_SOUND_MIXER_WRITE_VOLUME) ||
++	    (cmd == LX_OSS_SOUND_MIXER_WRITE_PCM));
++
++	/* Ioctl is only supported on mixer audio devices. */
++	if ((err = i_is_mixer_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(valp, &val, sizeof (val)) != 0)
++		return (-errno);
++
++	if (cmd == LX_OSS_SOUND_MIXER_WRITE_VOLUME) {
++		cmd_new = LXA_IOC_MIXER_SET_VOL;
++		cmd_txt = "LXA_IOC_MIXER_SET_VOL";
++
++		/* Attempt to get the device output gain. */
++		lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd,
++		    LXA_IOC_MIXER_GET_VOL, "LXA_IOC_MIXER_GET_VOL");
++		if (ioctl(fd, LXA_IOC_MIXER_GET_VOL, &ml_old) < 0)
++			return (-errno);
++	}
++
++	if (cmd == LX_OSS_SOUND_MIXER_WRITE_PCM) {
++		cmd_new = LXA_IOC_MIXER_SET_PCM;
++		cmd_txt = "LXA_IOC_MIXER_SET_PCM";
++
++		/* Attempt to get the device output gain. */
++		lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd,
++		    LXA_IOC_MIXER_GET_PCM, "LXA_IOC_MIXER_GET_PCM");
++		if (ioctl(fd, LXA_IOC_MIXER_GET_PCM, &ml_old) < 0)
++			return (-errno);
++	}
++
++	lx_debug("\toss set mixer %s request = 0x%x (%u)",
++	    (cmd == LX_OSS_SOUND_MIXER_WRITE_VOLUME) ? "volume" : "pcm",
++	    val, val);
++
++	/* Translate an OSS mixer value to mixer levels. */
++	if ((err = i_oss_mixer_val_to_ml(val, &ml_old, &ml)) != 0)
++		return (err);
++	assert(LXA_MIXER_LEVELS_OK(&ml));
++
++	lx_debug("\tlx_audio mixer request, "
++	    "gain = 0x%x (%u), balance = 0x%x (%u)",
++	    ml.lxa_ml_gain, ml.lxa_ml_gain,
++	    ml.lxa_ml_balance, ml.lxa_ml_balance);
++
++	/* Attempt to set the device output gain. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)", fd, cmd_new, cmd_txt);
++	if (ioctl(fd, cmd_new, &ml) < 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_mixer_read_mic(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_mixer_levels_t	ml;
++	uint_t			*valp = (uint_t *)arg;
++	uint_t			val;
++	int			err;
++
++	assert((cmd == LX_OSS_SOUND_MIXER_READ_MIC) ||
++	    (cmd == LX_OSS_SOUND_MIXER_READ_IGAIN));
++
++	/* Ioctl is only supported on mixer audio devices. */
++	if ((err = i_is_mixer_dev(fd)) != 0)
++		return (err);
++
++	/* Attempt to get the device input gain. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_MIXER_GET_MIC, "LXA_IOC_MIXER_GET_MIC");
++	if (ioctl(fd, LXA_IOC_MIXER_GET_MIC, &ml) < 0)
++		return (-errno);
++
++	/* Report the mixer as having two channels. */
++	val = LX_OSS_MIXER_ENC2(
++	    LX_OSS_S2L_GAIN(ml.lxa_ml_gain),
++	    LX_OSS_S2L_GAIN(ml.lxa_ml_gain));
++
++	if (cmd == LX_OSS_SOUND_MIXER_READ_MIC)
++		lx_debug("\toss get mixer mic result = 0x%x (%u)", val, val);
++	if (cmd == LX_OSS_SOUND_MIXER_READ_IGAIN)
++		lx_debug("\toss get mixer igain result = 0x%x (%u)", val, val);
++
++	if (uucopy(&val, valp, sizeof (val)) != 0)
++		return (-errno);
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_mixer_write_mic(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	lxa_mixer_levels_t	ml;
++	uint_t			*valp = (uint_t *)arg;
++	uint_t			val;
++	int			err;
++
++	assert((cmd == LX_OSS_SOUND_MIXER_WRITE_MIC) ||
++	    (cmd == LX_OSS_SOUND_MIXER_WRITE_IGAIN));
++
++	/* Ioctl is only supported on mixer audio devices. */
++	if ((err = i_is_mixer_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(valp, &val, sizeof (val)) != 0)
++		return (-errno);
++
++	if (cmd == LX_OSS_SOUND_MIXER_WRITE_MIC)
++		lx_debug("\toss set mixer mic request = 0x%x (%u)", val, val);
++	if (cmd == LX_OSS_SOUND_MIXER_WRITE_IGAIN)
++		lx_debug("\toss set mixer igain request = 0x%x (%u)", val, val);
++
++	/* The mic only supports one channel. */
++	val = LX_OSS_MIXER_DEC1(val);
++
++	ml.lxa_ml_balance = AUDIO_MID_BALANCE;
++	ml.lxa_ml_gain = LX_OSS_L2S_GAIN(val);
++
++	/* Attempt to set the device input gain. */
++	lx_debug("\tioctl(%d, 0x%x - %s, ...)",
++	    fd, LXA_IOC_MIXER_SET_MIC, "LXA_IOC_MIXER_SET_MIC");
++	if (ioctl(fd, LXA_IOC_MIXER_SET_MIC, &ml) < 0)
++		return (-errno);
++
++	return (0);
++}
++
++static int
++/*ARGSUSED*/
++ict_oss_mixer_read_devs(int fd, struct stat *stat,
++    int cmd, char *cmd_str, intptr_t arg)
++{
++	uint_t		*resultp = (uint_t *)arg;
++	uint_t		result = 0;
++	int		err;
++
++	if (cmd == LX_OSS_SOUND_MIXER_READ_DEVMASK) {
++		/* Bitmap of all the mixer channels we supposedly support. */
++		result = ((1 << LX_OSS_SM_PCM) |
++		    (1 << LX_OSS_SM_MIC) |
++		    (1 << LX_OSS_SM_VOLUME));
++	}
++	if (cmd == LX_OSS_SOUND_MIXER_READ_STEREODEVS) {
++		/* Bitmap of the stereo mixer channels we supposedly support. */
++		result = ((1 << LX_OSS_SM_PCM) |
++		    (1 << LX_OSS_SM_VOLUME));
++	}
++	if ((cmd == LX_OSS_SOUND_MIXER_READ_RECMASK) ||
++	    (cmd == LX_OSS_SOUND_MIXER_READ_RECSRC)) {
++		/* Bitmap of the mixer input channels we supposedly support. */
++		result = (1 << LX_OSS_SM_MIC);
++	}
++	assert(result != 0);
++
++	/* Ioctl is only supported on mixer audio devices. */
++	if ((err = i_is_mixer_dev(fd)) != 0)
++		return (err);
++
++	if (uucopy(&result, resultp, sizeof (result)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++/*
++ * Audio ioctl conversion support structures.
++ */
++static oss_fmt_translator_t oft_table[] = {
++	{ LX_OSS_AFMT_MU_LAW,		AUDIO_ENCODING_ULAW,	8 },
++	{ LX_OSS_AFMT_A_LAW,		AUDIO_ENCODING_ALAW,	8 },
++	{ LX_OSS_AFMT_S8,		AUDIO_ENCODING_LINEAR,	8 },
++	{ LX_OSS_AFMT_U8,		AUDIO_ENCODING_LINEAR8,	8 },
++	{ LX_OSS_AFMT_S16_NE,		AUDIO_ENCODING_LINEAR,	16 },
++	{ 0,				0,			0 }
++};
++
++/*
++ * Ioctl translator definitions.
++ */
++
++/*
++ * Defines to help with creating ioctl translators.
++ *
++ * IOC_CMD_TRANSLATOR_NONE - Ioctl has the same semantics and argument
++ * values on Solaris and Linux but may have different command values.
++ * (Macro assumes the symbolic Linux name assigned to the ioctl command
++ * value is the same as the Solaris symbol but pre-pended with an "LX_")
++ *
++ * IOC_CMD_TRANSLATOR_PASS - Ioctl is a Linux specific ioctl and should
++ * be passed through unmodified.
++ *
++ * IOC_CMD_TRANSLATOR_FILTER - Ioctl has the same command name on
++ * Solaris and Linux and needs a translation function that is common to
++ * more than one ioctl. (Macro assumes the symbolic Linux name assigned
++ * to the ioctl command value is the same as the Solaris symbol but
++ * pre-pended with an "LX_")
++ *
++ * IOC_CMD_TRANSLATOR_CUSTOM - Ioctl needs special handling via a
++ * translation function.
++ */
++#define	IOC_CMD_TRANSLATOR_NONE(ioc_cmd_sym)				\
++	{ (int)LX_##ioc_cmd_sym, "LX_" #ioc_cmd_sym,			\
++		ioc_cmd_sym, #ioc_cmd_sym, ict_pass },
++
++#define	IOC_CMD_TRANSLATOR_PASS(ioc_cmd_sym)				\
++	{ (int)ioc_cmd_sym, #ioc_cmd_sym,				\
++		ioc_cmd_sym, #ioc_cmd_sym, ict_pass },
++
++#define	IOC_CMD_TRANSLATOR_FILTER(ioc_cmd_sym, ioct_handler)		\
++	{ (int)LX_##ioc_cmd_sym, "LX_" #ioc_cmd_sym,			\
++		ioc_cmd_sym, #ioc_cmd_sym, ioct_handler },
++
++#define	IOC_CMD_TRANSLATOR_CUSTOM(ioc_cmd_sym, ioct_handler)		\
++	{ (int)ioc_cmd_sym, #ioc_cmd_sym,				\
++		(int)ioc_cmd_sym, #ioc_cmd_sym, ioct_handler },
++
++#define	IOC_CMD_TRANSLATOR_END						\
++	{ 0, NULL, 0, NULL, NULL }
++
++/* All files will need to support these ioctls. */
++#define	IOC_CMD_TRANSLATORS_ALL						\
++	IOC_CMD_TRANSLATOR_NONE(FIONREAD)				\
++	IOC_CMD_TRANSLATOR_NONE(FIONBIO)
++
++/* Any files supporting streams semantics will need these ioctls. */
++#define	IOC_CMD_TRANSLATORS_STREAMS					\
++	IOC_CMD_TRANSLATOR_NONE(TCXONC)					\
++	IOC_CMD_TRANSLATOR_NONE(TCFLSH)					\
++	IOC_CMD_TRANSLATOR_NONE(TIOCEXCL)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCNXCL)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCSPGRP)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCSTI)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCSWINSZ)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCMBIS)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCMBIC)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCMSET)				\
++	IOC_CMD_TRANSLATOR_NONE(TIOCSETD)				\
++	IOC_CMD_TRANSLATOR_NONE(FIOASYNC)				\
++	IOC_CMD_TRANSLATOR_NONE(FIOSETOWN)				\
++	IOC_CMD_TRANSLATOR_NONE(TCSBRK)					\
++									\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETS,		ict_tcsets)	\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETSW,		ict_tcsets)	\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETSF,		ict_tcsets)	\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETA,		ict_tcseta)	\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETAW,		ict_tcseta)	\
++	IOC_CMD_TRANSLATOR_FILTER(TCSETAF,		ict_tcseta)	\
++									\
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCSBRKP,		ict_tcsbrkp)
++
++
++/*
++ * Translators for non-device files.
++ */
++static ioc_cmd_translator_t ioc_translators_file[] = {
++	IOC_CMD_TRANSLATORS_ALL
++	IOC_CMD_TRANSLATOR_END
++};
++
++static ioc_cmd_translator_t ioc_translators_fifo[] = {
++	IOC_CMD_TRANSLATORS_ALL
++	IOC_CMD_TRANSLATORS_STREAMS
++	IOC_CMD_TRANSLATOR_END
++};
++
++static ioc_cmd_translator_t ioc_translators_sock[] = {
++	IOC_CMD_TRANSLATORS_ALL
++
++	IOC_CMD_TRANSLATOR_NONE(FIOASYNC)
++	IOC_CMD_TRANSLATOR_NONE(FIOGETOWN)
++	IOC_CMD_TRANSLATOR_NONE(FIOSETOWN)
++	IOC_CMD_TRANSLATOR_NONE(SIOCSPGRP)
++	IOC_CMD_TRANSLATOR_NONE(SIOCGPGRP)
++
++	IOC_CMD_TRANSLATOR_FILTER(SIOCATMARK,		ict_sioifoob)
++
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFFLAGS,		ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFFLAGS,		ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFADDR,		ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFADDR,		ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFDSTADDR,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFDSTADDR,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFBRDADDR,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFBRDADDR,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFNETMASK,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFNETMASK,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMETRIC,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMETRIC,	ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMTU,		ict_sioifreq)
++	IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMTU,		ict_sioifreq)
++
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFCONF,	ict_siocgifconf)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFHWADDR,	ict_siocifhwaddr)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCSIFHWADDR,	ict_siocifhwaddr)
++
++	IOC_CMD_TRANSLATOR_END
++};
++
++/*
++ * Translators for devices.
++ */
++static ioc_cmd_translator_t ioc_cmd_translators_ptm[] = {
++	IOC_CMD_TRANSLATORS_ALL
++	IOC_CMD_TRANSLATORS_STREAMS
++
++	IOC_CMD_TRANSLATOR_NONE(TIOCPKT)
++
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPGRP,		ict_tiocgpgrp)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSPTLCK,	ict_sptlock)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPTN,		ict_gptn)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGWINSZ,	ict_tiocgwinsz)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCGETS,		ict_tcgets_emulate)
++
++	IOC_CMD_TRANSLATOR_END
++};
++static ioc_dev_translator_t ioc_translator_ptm = {
++	LX_PTM_DRV,	/* idt_driver */
++	0,		/* idt_major */
++	ioc_cmd_translators_ptm
++};
++
++static ioc_cmd_translator_t ioc_cmd_translators_pts[] = {
++	IOC_CMD_TRANSLATORS_ALL
++	IOC_CMD_TRANSLATORS_STREAMS
++
++	IOC_CMD_TRANSLATOR_NONE(TIOCGETD)
++	IOC_CMD_TRANSLATOR_NONE(TIOCGSID)
++	IOC_CMD_TRANSLATOR_NONE(TIOCNOTTY)
++
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPGRP,		ict_tiocgpgrp)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCGETS,		ict_tcgets_native)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCGETA,		ict_tcgeta)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGWINSZ,	ict_tiocgwinsz)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSCTTY,		ict_tiocsctty)
++
++	IOC_CMD_TRANSLATOR_END
++};
++static ioc_dev_translator_t ioc_translator_pts = {
++	"pts",		/* idt_driver */
++	0,		/* idt_major */
++	ioc_cmd_translators_pts
++};
++
++static ioc_dev_translator_t ioc_translator_sy = {
++	"sy",		/* idt_driver */
++	0,		/* idt_major */
++
++	/*
++	 * /dev/tty (which is implemented via the "sy" driver) is basically
++	 * a layered driver that passes on requests to the ctty for the
++	 * current process.  Since ctty's are currently always implemented
++	 * via the pts driver, we should make sure to support all the
++	 * same ioctls on the sy driver as we do on the pts driver.
++	 */
++	ioc_cmd_translators_pts
++};
++
++static ioc_cmd_translator_t ioc_cmd_translators_zcons[] = {
++	IOC_CMD_TRANSLATORS_ALL
++	IOC_CMD_TRANSLATORS_STREAMS
++
++	IOC_CMD_TRANSLATOR_NONE(TIOCNOTTY)
++
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCGETS,		ict_tcgets_native)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TCGETA,		ict_tcgeta)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGWINSZ,	ict_tiocgwinsz)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSCTTY,		ict_tiocsctty)
++
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCLINUX,		ict_einval)
++
++	IOC_CMD_TRANSLATOR_END
++};
++static ioc_dev_translator_t ioc_translator_zcons = {
++	"zcons",	/* idt_driver */
++	0,		/* idt_major */
++	ioc_cmd_translators_zcons
++};
++
++static ioc_cmd_translator_t ioc_cmd_translators_lx_audio[] = {
++	IOC_CMD_TRANSLATORS_ALL
++
++	/* /dev/dsp ioctls */
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_RESET,
++	    ict_oss_sndctl_dsp_reset)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETFMTS,
++	    ict_oss_sndctl_dsp_getfmts)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_SETFMTS,
++	    ict_oss_sndctl_dsp_setfmts)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_CHANNELS,
++	    ict_oss_sndctl_dsp_channels)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_STEREO,
++	    ict_oss_sndctl_dsp_channels)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_SPEED,
++	    ict_oss_sndctl_dsp_speed)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETBLKSIZE,
++	    ict_oss_sndctl_dsp_getblksize)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_SYNC,
++	    ict_oss_sndctl_dsp_sync)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_SETFRAGMENT,
++	    ict_oss_sndctl_dsp_setfragment)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETOSPACE,
++	    ict_oss_sndctl_dsp_getspace)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETCAPS,
++	    ict_oss_sndctl_dsp_getcaps)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_SETTRIGGER,
++	    ict_oss_sndctl_dsp_settrigger)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETOPTR,
++	    ict_oss_sndctl_dsp_getoptr)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SNDCTL_DSP_GETISPACE,
++	    ict_oss_sndctl_dsp_getspace)
++
++	/* /dev/mixer level ioctls */
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_VOLUME,
++	    ict_oss_mixer_read_volume)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_PCM,
++	    ict_oss_mixer_read_volume)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_MIC,
++	    ict_oss_mixer_read_mic)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_IGAIN,
++	    ict_oss_mixer_read_mic)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_WRITE_VOLUME,
++	    ict_oss_mixer_write_volume)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_WRITE_PCM,
++	    ict_oss_mixer_write_volume)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_WRITE_MIC,
++	    ict_oss_mixer_write_mic)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_WRITE_IGAIN,
++	    ict_oss_mixer_write_mic)
++
++	/* /dev/mixer capability ioctls */
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_STEREODEVS,
++	    ict_oss_mixer_read_devs)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_DEVMASK,
++	    ict_oss_mixer_read_devs)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_RECMASK,
++	    ict_oss_mixer_read_devs)
++	IOC_CMD_TRANSLATOR_CUSTOM(LX_OSS_SOUND_MIXER_READ_RECSRC,
++	    ict_oss_mixer_read_devs)
++
++	IOC_CMD_TRANSLATOR_END
++};
++static ioc_dev_translator_t ioc_translator_lx_audio = {
++	"lx_audio",	/* idt_driver */
++	0,		/* idt_major */
++	ioc_cmd_translators_lx_audio
++};
++
++/*
++ * An array of all the device translators.
++ */
++static ioc_dev_translator_t *ioc_translators_dev[] = {
++	&ioc_translator_lx_audio,
++	&ioc_translator_ptm,
++	&ioc_translator_pts,
++	&ioc_translator_sy,
++	&ioc_translator_zcons,
++	NULL
++};
++
++/*
++ * Translators for filesystems.
++ */
++static ioc_cmd_translator_t ioc_cmd_translators_autofs[] = {
++	IOC_CMD_TRANSLATOR_PASS(LX_AUTOFS_IOC_READY)
++	IOC_CMD_TRANSLATOR_PASS(LX_AUTOFS_IOC_FAIL)
++	IOC_CMD_TRANSLATOR_PASS(LX_AUTOFS_IOC_CATATONIC)
++	IOC_CMD_TRANSLATOR_END
++};
++
++static ioc_fs_translator_t ioc_translator_autofs = {
++	LX_AUTOFS_NAME,	/* ift_filesystem */
++	ioc_cmd_translators_autofs
++};
++
++/*
++ * An array of all the filesystem translators.
++ */
++static ioc_fs_translator_t *ioc_translators_fs[] = {
++	&ioc_translator_autofs,
++	NULL
++};
++
++/*
++ * Ioctl error translator definitions.
++ */
++#define	IOC_ERRNO_TRANSLATOR(iet_cmd_sym, iet_errno)			\
++	{ (int)LX_##iet_cmd_sym, "LX_" #iet_cmd_sym, iet_errno },
++
++#define	IOC_ERRNO_TRANSLATOR_END					\
++	{ 0, NULL, 0 }
++
++static ioc_errno_translator_t ioc_translators_errno[] = {
++	IOC_ERRNO_TRANSLATOR(TCGETS, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TCSETS, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TCSBRK, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TCXONC, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TCFLSH, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TIOCGPGRP, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TIOCSPGRP, ENOTTY)
++	IOC_ERRNO_TRANSLATOR(TIOCGWINSZ, ENOTTY)
++	IOC_ERRNO_TRANSLATOR_END
++};
++
++int
++lx_vhangup(void)
++{
++	if (geteuid() != 0)
++		return (-EPERM);
++
++	vhangup();
++
++	return (0);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/iovec.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/iovec.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,241 @@
++/*
++ * 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 <errno.h>
++#include <unistd.h>
++#include <sys/uio.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <alloca.h>
++#include <string.h>
++#include <sys/lx_syscall.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_types.h>
++
++static int
++lx_is_directory(int fd)
++{
++	struct stat64 sbuf;
++
++	if (fstat64(fd, &sbuf) < 0)
++		sbuf.st_mode = 0;
++
++	return ((sbuf.st_mode & S_IFMT) == S_IFDIR);
++}
++
++int
++lx_read(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int 		fd = (int)p1;
++	void		*buf = (void *)p2;
++	size_t		nbyte = (size_t)p3;
++	ssize_t		ret;
++
++	if (lx_is_directory(fd))
++		return (-EISDIR);
++
++	if ((ret = read(fd, buf, nbyte)) < 0)
++		return (-errno);
++
++	return (ret);
++}
++
++int
++lx_pread64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5)
++{
++	int 		fd = (int)p1;
++	void		*buf = (void *)p2;
++	size_t		nbyte = (size_t)p3;
++	uintptr_t	off_lo = p4;
++	uintptr_t	off_hi = p5;
++	ssize_t		ret;
++
++	if (lx_is_directory(fd))
++		return (-EISDIR);
++
++	ret = pread64(fd, buf, nbyte, (off64_t)LX_32TO64(off_lo, off_hi));
++
++	if (ret < 0)
++		return (-errno);
++
++	return (ret);
++}
++
++/*
++ * On Linux, the pwrite(2) system call behaves identically to Solaris except
++ * in the case of the file being opened with O_APPEND. In that case Linux's
++ * pwrite(2) ignores the offset parameter and instead appends the data to the
++ * file without modifying the current seek pointer.
++ */
++int
++lx_pwrite64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5)
++{
++	int fd = (int)p1;
++	void *buf = (void *)p2;
++	size_t nbyte = (size_t)p3;
++	uintptr_t off_lo = p4;
++	uintptr_t off_hi = p5;
++	ssize_t ret;
++	int rval;
++	struct stat64 statbuf;
++
++	if ((rval = fcntl(fd, F_GETFL, 0)) < 0)
++		return (-errno);
++
++	if (!(rval & O_APPEND)) {
++		ret = pwrite64(fd, buf, nbyte,
++		    (off64_t)LX_32TO64(off_lo, off_hi));
++	} else if ((ret = fstat64(fd, &statbuf)) == 0) {
++		ret = pwrite64(fd, buf, nbyte, statbuf.st_size);
++	}
++
++	if (ret < 0)
++		return (-errno);
++
++	return (ret);
++}
++
++/*
++ * Implementation of Linux readv() and writev() system calls.
++ *
++ * The Linux system calls differ from the Solaris system calls in a few key
++ * areas:
++ *
++ * - On Solaris, the maximum number of I/O vectors that can be passed to readv()
++ *   or writev() is IOV_MAX (16).  Linux has a much larger restriction (1024).
++ *
++ * - Passing 0 as a vector count is an error on Solaris, but on Linux results
++ *   in a return value of 0. Even though the man page says the opposite.
++ *
++ * - If the Nth vector results in an error, Solaris will return an error code
++ *   for the entire operation.  Linux only returns an error if there has been
++ *   no data transferred yet.  Otherwise, it returns the number of bytes
++ *   transferred up until that point.
++ *
++ * In order to accomodate these differences, we implement these functions as a
++ * series of ordinary read() or write() calls.
++ */
++
++#define	LX_IOV_MAX 1024		/* Also called MAX_IOVEC */
++
++static int
++lx_iovec_copy_and_check(const struct iovec *iovp, struct iovec *iov, int count)
++{
++	int	i;
++	ssize_t	cnt = 0;
++
++	if (uucopy(iovp, (void *)iov, count * sizeof (struct iovec)) != 0)
++		return (-errno);
++
++	for (i = 0; i < count; i++) {
++		cnt += iov[i].iov_len;
++		if (iov[i].iov_len < 0 || cnt < 0)
++			return (-EINVAL);
++	}
++
++	return (0);
++}
++
++int
++lx_readv(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int			fd = (int)p1;
++	const struct iovec	*iovp = (const struct iovec *)p2;
++	int			count = (int)p3;
++	struct iovec		*iov;
++	ssize_t			total = 0, ret;
++	int			i;
++
++	if (count == 0)
++		return (0);
++
++	if (count < 0 || count > LX_IOV_MAX)
++		return (-EINVAL);
++
++	if (lx_is_directory(fd))
++		return (-EISDIR);
++
++	iov = SAFE_ALLOCA(count * sizeof (struct iovec));
++	if (iov == NULL)
++		return (-ENOMEM);
++	if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0)
++		return (ret);
++
++	for (i = 0; i < count; i++) {
++		ret = read(fd, iov[i].iov_base, iov[i].iov_len);
++
++		if (ret < 0) {
++			if (total > 0)
++				return (total);
++			return (-errno);
++		}
++
++		total += ret;
++	}
++
++	return (total);
++}
++
++int
++lx_writev(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	int			fd = (int)p1;
++	const struct iovec	*iovp = (const struct iovec *)p2;
++	int			count = (int)p3;
++	struct iovec		*iov;
++	ssize_t			total = 0, ret;
++	int			i;
++
++	if (count == 0)
++		return (0);
++
++	if (count < 0 || count > LX_IOV_MAX)
++		return (-EINVAL);
++
++	iov = SAFE_ALLOCA(count * sizeof (struct iovec));
++	if (iov == NULL)
++		return (-ENOMEM);
++	if ((ret = lx_iovec_copy_and_check(iovp, iov, count)) != 0)
++		return (ret);
++
++	for (i = 0; i < count; i++) {
++		ret = write(fd, iov[i].iov_base, iov[i].iov_len);
++
++		if (ret < 0) {
++			if (total > 0)
++				return (total);
++			return (-errno);
++		}
++
++		total += ret;
++	}
++
++	return (total);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,1298 @@
++/*
++ * 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 <sys/types.h>
++#include <sys/syscall.h>
++#include <sys/utsname.h>
++#include <sys/inttypes.h>
++#include <sys/stat.h>
++#include <sys/mman.h>
++#include <sys/fstyp.h>
++#include <sys/fsid.h>
++#include <sys/systm.h>
++#include <sys/auxv.h>
++#include <sys/frame.h>
++#include <sys/brand.h>
++
++#include <assert.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <strings.h>
++#include <unistd.h>
++#include <errno.h>
++#include <syslog.h>
++#include <signal.h>
++#include <fcntl.h>
++#include <synch.h>
++#include <libelf.h>
++#include <libgen.h>
++#include <pthread.h>
++#include <utime.h>
++#include <dirent.h>
++#include <ucontext.h>
++#include <libintl.h>
++#include <locale.h>
++
++#include <sys/lx_misc.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_brand.h>
++#include <sys/lx_types.h>
++#include <sys/lx_stat.h>
++#include <sys/lx_statfs.h>
++#include <sys/lx_ioctl.h>
++#include <sys/lx_signal.h>
++#include <sys/lx_syscall.h>
++#include <sys/lx_thread.h>
++#include <sys/lx_thunk_server.h>
++
++/*
++ * Map solaris errno to the linux equivalent.
++ */
++static int stol_errno[] = {
++	0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
++	10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
++	20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
++	30,  31,  32,  33,  34,  42,  43,  44,  45,  46,
++	47,  48,  49,  50,  51,  35,  47,  22,  38,  22, /* 49 */
++	52,  53,  54,  55,  56,  57,  58,  59,  22,  22,
++	61,  61,  62,  63,  64,  65,  66,  67,  68,  69,
++	70,  71,  22,  22,  72,  22,  22,  74,  36,  75,
++	76,  77,  78,  79,  80,  81,  82,  83,  84,  38,
++	40,  85,  86,  39,  87,  88,  89,  90,  91,  92, /* 99 */
++	22,  22,  22,  22,  22,  22,  22,  22,  22,  22,
++	22,  22,  22,  22,  22,  22,  22,  22,  22,  22,
++	93,  94,  95,  96,  97,  98,  99, 100, 101, 102,
++	103, 104, 105, 106, 107,  22,  22,  22,  22,  22,
++	22,  22,  22, 108, 109, 110, 111, 112, 113, 114, /* 149 */
++	115, 116
++};
++
++char lx_release[128];
++
++/*
++ * Map a linux locale ending string to the solaris equivalent.
++ */
++struct lx_locale_ending {
++	const char	*linux_end;	/* linux ending string */
++	const char	*solaris_end;	/* to transform with this string */
++	int		le_size;	/* linux ending string length */
++	int		se_size;	/* solaris ending string length */
++};
++
++#define	l2s_locale(lname, sname) \
++	{(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1}
++
++static struct lx_locale_ending lx_locales[] = {
++	l2s_locale(".utf8",	 ".UTF-8"),
++	l2s_locale(".utf8@euro", ".UTF-8"),
++	l2s_locale("@euro",	 ".ISO8859-15"),
++	l2s_locale(".iso885915", ".ISO8859-15"),
++	l2s_locale(".euckr",	 ".EUC"),
++	l2s_locale(".euctw",	 ".EUC"),
++	l2s_locale(".koi8r",	 ".KOI8-R"),
++	l2s_locale(".gb18030",	 ".GB18030"),
++	l2s_locale(".gbk",	 ".GBK"),
++	l2s_locale("@cyrillic",	 ".ISO8859-5")
++};
++
++#define	MAXLOCALENAMELEN	30
++#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
++#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
++#endif
++
++/*
++ * This flag is part of the registration with the in-kernel brand module. It's
++ * used in lx_handler() to determine if we should go back into the kernel after
++ * a system call in case the kernel needs to perform some post-syscall work
++ * like tracing for example.
++ */
++int lx_traceflag;
++
++#define	NOSYS_NULL		1
++#define	NOSYS_NO_EQUIV		2
++#define	NOSYS_KERNEL		3
++#define	NOSYS_UNDOC		4
++#define	NOSYS_OBSOLETE		5
++
++/*
++ * SYS_PASSTHRU denotes a system call we can just call on behalf of the
++ * branded process without having to translate the arguments.
++ *
++ * The restriction on this is that the call in question MUST return -1 to
++ * denote an error.
++ */
++#define	SYS_PASSTHRU		5
++
++static char *nosys_msgs[] = {
++	"Either not yet done, or we haven't come up with an excuse",
++	"No such Linux system call",
++	"No equivalent Solaris functionality",
++	"Reads/modifies Linux kernel state",
++	"Undocumented and/or rarely used system call",
++	"Unsupported, obsolete system call"
++};
++
++struct lx_sysent {
++	char    *sy_name;
++	int	(*sy_callc)();
++	char	sy_flags;
++	char	sy_narg;
++};
++
++static struct lx_sysent sysents[LX_NSYSCALLS + 1];
++
++static uintptr_t stack_bottom;
++
++int lx_install = 0;		/* install mode enabled if non-zero */
++boolean_t lx_is_rpm = B_FALSE;
++int lx_rpm_delay = 1;
++int lx_strict = 0;		/* "strict" mode enabled if non-zero */
++int lx_verbose = 0;		/* verbose mode enabled if non-zero */
++int lx_debug_enabled = 0;	/* debugging output enabled if non-zero */
++
++pid_t zoneinit_pid;		/* zone init PID */
++
++thread_key_t lx_tsd_key;
++
++int
++uucopy_unsafe(const void *src, void *dst, size_t n)
++{
++	bcopy(src, dst, n);
++	return (0);
++}
++
++int
++uucopystr_unsafe(const void *src, void *dst, size_t n)
++{
++	(void) strncpy((char *)src, dst, n);
++	return (0);
++}
++
++static void
++i_lx_msg(int fd, char *msg, va_list ap)
++{
++	int	i;
++	char	buf[LX_MSG_MAXLEN];
++
++	/* LINTED [possible expansion issues] */
++	i = vsnprintf(buf, sizeof (buf), msg, ap);
++	buf[LX_MSG_MAXLEN - 1] = '\0';
++	if (i == -1)
++		return;
++
++	/* if debugging is enabled, send this message to debug output */
++	if (lx_debug_enabled != 0)
++		lx_debug(buf);
++
++	/*
++	 * If we are trying to print to stderr, we also want to send the
++	 * message to syslog.
++	 */
++	if (fd == 2) {
++		syslog(LOG_ERR, "%s", buf);
++
++		/*
++		 * We let the user choose whether or not to see these
++		 * messages on the console.
++		 */
++		if (lx_verbose == 0)
++			return;
++	}
++
++	/* we retry in case of EINTR */
++	do {
++		i = write(fd, buf, strlen(buf));
++	} while ((i == -1) && (errno == EINTR));
++}
++
++/*PRINTFLIKE1*/
++void
++lx_err(char *msg, ...)
++{
++	va_list	ap;
++
++	assert(msg != NULL);
++
++	va_start(ap, msg);
++	i_lx_msg(STDERR_FILENO, msg, ap);
++	va_end(ap);
++}
++
++/*
++ * This is just a non-zero exit value which also isn't one that would allow
++ * us to easily detect if a branded process exited because of a recursive
++ * fatal error.
++ */
++#define	LX_ERR_FATAL	42
++
++/*
++ * Our own custom version of abort(), this routine will be used in place
++ * of the one located in libc.  The primary difference is that this version
++ * will first reset the signal handler for SIGABRT to SIG_DFL, ensuring the
++ * SIGABRT sent causes us to dump core and is not caught by a user program.
++ */
++void
++abort(void)
++{
++	static int aborting = 0;
++
++	struct sigaction sa;
++	sigset_t sigmask;
++
++	/* watch out for recursive calls to this function */
++	if (aborting != 0)
++		exit(LX_ERR_FATAL);
++
++	aborting = 1;
++
++	/*
++	 * Block all signals here to avoid taking any signals while exiting
++	 * in an effort to avoid any strange user interaction with our death.
++	 */
++	(void) sigfillset(&sigmask);
++	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
++
++	/*
++	 * Our own version of abort(3C) that we know will never call
++	 * a user-installed SIGABRT handler first.  We WANT to die.
++	 *
++	 * Do this by resetting the handler to SIG_DFL, and releasing any
++	 * held SIGABRTs.
++	 *
++	 * If no SIGABRTs are pending, send ourselves one.
++	 *
++	 * The while loop is a bit of overkill, but abort(3C) does it to
++	 * assure it never returns so we will as well.
++	 */
++	(void) sigemptyset(&sa.sa_mask);
++	sa.sa_sigaction = SIG_DFL;
++	sa.sa_flags = 0;
++
++	for (;;) {
++		(void) sigaction(SIGABRT, &sa, NULL);
++		(void) sigrelse(SIGABRT);
++		(void) thr_kill(thr_self(), SIGABRT);
++	}
++
++	/*NOTREACHED*/
++}
++
++/*PRINTFLIKE1*/
++void
++lx_msg(char *msg, ...)
++{
++	va_list	ap;
++
++	assert(msg != NULL);
++	va_start(ap, msg);
++	i_lx_msg(STDOUT_FILENO, msg, ap);
++	va_end(ap);
++}
++
++/*PRINTFLIKE1*/
++void
++lx_err_fatal(char *msg, ...)
++{
++	va_list	ap;
++
++	assert(msg != NULL);
++
++	va_start(ap, msg);
++	i_lx_msg(STDERR_FILENO, msg, ap);
++	va_end(ap);
++	abort();
++}
++
++/*
++ * See if it is safe to alloca() sz bytes.  Return 1 for yes, 0 for no.
++ */
++int
++lx_check_alloca(size_t sz)
++{
++	uintptr_t sp = (uintptr_t)&sz;
++	uintptr_t end = sp - sz;
++
++	return ((end < sp) && (end >= stack_bottom));
++}
++
++/*PRINTFLIKE1*/
++void
++lx_unsupported(char *msg, ...)
++{
++	va_list	ap;
++
++	assert(msg != NULL);
++
++	/* send the msg to the error stream */
++	va_start(ap, msg);
++	i_lx_msg(STDERR_FILENO, msg, ap);
++	va_end(ap);
++
++	/*
++	 * If the user doesn't trust the application to responsibly
++	 * handle ENOTSUP, we kill the application.
++	 */
++	if (lx_strict)
++		(void) kill(getpid(), SIGSYS);
++}
++
++extern void lx_runexe(void *argv, int32_t entry);
++int lx_init(int argc, char *argv[], char *envp[]);
++
++static int
++lx_emulate_args(lx_regs_t *rp, struct lx_sysent *s, uintptr_t *args)
++{
++	/*
++	 * If the system call takes 6 args, then libc has stashed them in
++	 * memory at the address contained in %ebx. Except for some syscalls
++	 * which store the 6th argument in %ebp.
++	 */
++	if (s->sy_narg == 6 && !(s->sy_flags & EBP_HAS_ARG6)) {
++		if (uucopy((void *)rp->lxr_ebx, args,
++		    sizeof (args[0]) * 6) != 0)
++			return (-stol_errno[errno]);
++	} else {
++		args[0] = rp->lxr_ebx;
++		args[1] = rp->lxr_ecx;
++		args[2] = rp->lxr_edx;
++		args[3] = rp->lxr_esi;
++		args[4] = rp->lxr_edi;
++		args[5] = rp->lxr_ebp;
++	}
++
++	return (0);
++}
++
++void
++lx_emulate(lx_regs_t *rp)
++{
++	struct lx_sysent *s;
++	uintptr_t args[6];
++	uintptr_t gs = rp->lxr_gs & 0xffff;	/* %gs is only 16 bits */
++	int syscall_num, ret;
++
++	syscall_num = rp->lxr_eax;
++
++	/*
++	 * lx_brand_int80_callback() ensures that the syscall_num is sane;
++	 * Use it as is.
++	 */
++	assert(syscall_num >= 0);
++	assert(syscall_num < (sizeof (sysents) / sizeof (sysents[0])));
++	s = &sysents[syscall_num];
++
++	if ((ret = lx_emulate_args(rp, s, args)) != 0)
++		goto out;
++
++	/*
++	 * If the tracing flag is enabled we call into the brand-specific
++	 * kernel module to handle the tracing activity (DTrace or ptrace).
++	 * It would be tempting to perform DTrace activity in the brand
++	 * module's syscall trap callback, rather than having to return
++	 * to the kernel here, but -- since argument encoding can vary
++	 * according to the specific system call -- that would require
++	 * replicating the knowledge of argument decoding in the kernel
++	 * module as well as here in the brand library.
++	 */
++	if (lx_traceflag != 0) {
++		/*
++		 * Part of the ptrace "interface" is that on syscall entry
++		 * %eax should be reported as -ENOSYS while the orig_eax
++		 * field of the user structure needs to contain the actual
++		 * system call number. If we end up stopping here, the
++		 * controlling process will dig the lx_regs_t structure out of
++		 * our stack.
++		 */
++		rp->lxr_orig_eax = syscall_num;
++		rp->lxr_eax = -stol_errno[ENOSYS];
++
++		(void) syscall(SYS_brand, B_SYSENTRY, syscall_num, args);
++
++		/*
++		 * The external tracer may have modified the arguments to this
++		 * system call. Refresh the argument cache to account for this.
++		 */
++		if ((ret = lx_emulate_args(rp, s, args)) != 0)
++			goto out;
++	}
++
++	if (s->sy_callc == NULL) {
++		lx_unsupported(gettext("unimplemented syscall #%d (%s): %s\n"),
++		    syscall_num, s->sy_name, nosys_msgs[s->sy_flags]);
++		ret = -stol_errno[ENOTSUP];
++		goto out;
++	}
++
++	if (lx_debug_enabled != 0) {
++		const char *fmt;
++
++		switch (s->sy_narg) {
++		case 0:
++			fmt = "calling %s()";
++			break;
++		case 1:
++			fmt = "calling %s(0x%p)";
++			break;
++		case 2:
++			fmt = "calling %s(0x%p, 0x%p)";
++			break;
++		case 3:
++			fmt = "calling %s(0x%p, 0x%p, 0x%p)";
++			break;
++		case 4:
++			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p)";
++			break;
++		case 5:
++			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p, 0x%p)";
++			break;
++		case 6:
++			fmt = "calling %s(0x%p, 0x%p, 0x%p, 0x%p, 0x%p, 0x%p)";
++			break;
++		}
++
++		lx_debug(fmt, s->sy_name, args[0], args[1], args[2], args[3],
++		    args[4], args[5]);
++	}
++
++	if (gs != LWPGS_SEL) {
++		lx_tsd_t *lx_tsd;
++
++		/*
++		 * While a %gs of 0 is technically legal (as long as the
++		 * application never dereferences memory using %gs), Solaris
++		 * has its own ideas as to how a zero %gs should be handled in
++		 * _update_sregs(), such that any 32-bit user process with a
++		 * %gs of zero running on a system with a 64-bit kernel will
++		 * have its %gs hidden base register stomped on on return from
++		 * a system call, leaving an incorrect base address in place
++		 * until the next time %gs is actually reloaded (forcing a
++		 * reload of the base address from the appropriate descriptor
++		 * table.)
++		 *
++		 * Of course the kernel will once again stomp on THAT base
++		 * address when returning from a system call, resulting in an
++		 * an application segmentation fault.
++		 *
++		 * To avoid this situation, disallow a save of a zero %gs
++		 * here in order to try and capture any Linux process that
++		 * attempts to make a syscall with a zero %gs installed.
++		 */
++		assert(gs != 0);
++
++		if ((ret = thr_getspecific(lx_tsd_key,
++		    (void **)&lx_tsd)) != 0)
++			lx_err_fatal(gettext(
++			    "%s: unable to read thread-specific data: %s"),
++			    "lx_emulate", strerror(ret));
++
++		assert(lx_tsd != 0);
++
++		lx_tsd->lxtsd_gs = gs;
++
++		lx_debug("lx_emulate(): gsp 0x%p, saved gs: 0x%x", lx_tsd, gs);
++	}
++
++	if (s->sy_flags == SYS_PASSTHRU)
++		lx_debug("\tCalling Solaris %s()", s->sy_name);
++
++	ret = s->sy_callc(args[0], args[1], args[2], args[3], args[4], args[5]);
++
++	if (ret > -65536 && ret < 65536)
++		lx_debug("\t= %d", ret);
++	else
++		lx_debug("\t= 0x%x", ret);
++
++	if ((s->sy_flags == SYS_PASSTHRU) && (ret == -1)) {
++		ret = -stol_errno[errno];
++	} else {
++		/*
++		 * If the return value is between -4096 and 0 we assume it's an
++		 * error, so we translate the Solaris error number into the
++		 * Linux equivalent.
++		 */
++		if (ret < 0 && ret > -4096) {
++			if (-ret >=
++			    sizeof (stol_errno) / sizeof (stol_errno[0])) {
++				lx_debug("Invalid return value from emulated "
++				    "syscall %d (%s): %d\n",
++				    syscall_num, s->sy_name, ret);
++				assert(0);
++			}
++
++			ret = -stol_errno[-ret];
++		}
++	}
++
++out:
++	/*
++	 * %eax holds the return code from the system call.
++	 */
++	rp->lxr_eax = ret;
++
++	/*
++	 * If the trace flag is set, bounce into the kernel to let it do
++	 * any necessary tracing (DTrace or ptrace).
++	 */
++	if (lx_traceflag != 0) {
++		rp->lxr_orig_eax = syscall_num;
++		(void) syscall(SYS_brand, B_SYSRETURN, syscall_num, ret);
++	}
++}
++
++/* Transform the Linux locale name to make it look like a Solaris locale name */
++static const char *
++lx_translate_locale(char *translated_name_mem, int mem_size)
++{
++	char *loc;
++	int i;
++	size_t len;
++
++	if ((loc = getenv("LC_ALL")) == NULL)
++		if ((loc = getenv("LANG")) == NULL)
++			return ("C");
++
++	if (strlcpy(translated_name_mem, loc, mem_size) >= mem_size)
++		return ("");
++
++	len = strlen(loc);
++
++	/* replace the end of the locale name if it's a known pattern */
++	for (i = 0; i < sizeof (lx_locales) / sizeof (struct lx_locale_ending);
++	    i++) {
++		if (len <= lx_locales[i].le_size)
++			continue;
++
++		if (strncmp(loc + len - lx_locales[i].le_size,
++		    lx_locales[i].linux_end, lx_locales[i].le_size))
++			continue; /* don't match */
++
++		if (len - lx_locales[i].le_size + lx_locales[i].se_size
++		    >= mem_size)
++			return ("C"); /* size too small for the new name */
++
++		(void) strlcpy(translated_name_mem + len -
++		    lx_locales[i].le_size, lx_locales[i].solaris_end,
++		    lx_locales[i].se_size + 1);
++
++		return ((const char *)translated_name_mem);
++	}
++
++	/* no match */
++	return ("");
++}
++
++static void
++lx_close_fh(FILE *file)
++{
++	int fd, fd_new;
++
++	if (file == NULL)
++		return;
++
++	if ((fd = fileno(file)) < 0)
++		return;
++
++	fd_new = dup(fd);
++	if (fd_new == -1)
++		return;
++
++	(void) fclose(file);
++	(void) dup2(fd_new, fd);
++	(void) close(fd_new);
++}
++
++extern int set_l10n_alternate_root(char *path);
++
++/*ARGSUSED*/
++int
++lx_init(int argc, char *argv[], char *envp[])
++{
++	char		*r;
++	auxv_t		*ap;
++	int		*p, err;
++	lx_elf_data_t	edp;
++	lx_brand_registration_t reg;
++	char 		locale_translated_name[MAXLOCALENAMELEN];
++	static lx_tsd_t lx_tsd;
++
++	/* Look up the PID that serves as init for this zone */
++	if ((err = lx_lpid_to_spid(1, &zoneinit_pid)) < 0)
++		lx_err_fatal(gettext(
++		    "Unable to find PID for zone init process: %s"),
++		    strerror(err));
++
++	/*
++	 * Ubuntu init will fail if its TERM environment variable is not set
++	 * so if we are running init, and TERM is not set, we set term and
++	 * reexec so that the new environment variable is propagated to the
++	 * linux application stack.
++	 */
++	if ((getpid() == zoneinit_pid) && (getenv("TERM") == NULL)) {
++		if (setenv("TERM", "vt100", 1) < 0 || execv(argv[0], argv) < 0)
++			lx_err_fatal(gettext("failed to set TERM"));
++	}
++
++	if ((set_l10n_alternate_root("/native") == 0) &&
++	    (setlocale(LC_ALL, lx_translate_locale(locale_translated_name,
++	    sizeof (locale_translated_name))) != NULL) &&
++	    (bindtextdomain(TEXT_DOMAIN, "/native/usr/lib/locale") != NULL)) {
++		(void) textdomain(TEXT_DOMAIN);
++	}
++
++	stack_bottom = 2 * sysconf(_SC_PAGESIZE);
++
++	/*
++	 * We need to shutdown all libc stdio.  libc stdio normally goes to
++	 * file descriptors, but since we're actually part of a linux
++	 * process we don't own these file descriptors and we can't make
++	 * any assumptions about their state.
++	 */
++	lx_close_fh(stdin);
++	lx_close_fh(stdout);
++	lx_close_fh(stderr);
++
++	lx_debug_init();
++
++	r = getenv("LX_RELEASE");
++	if (r == NULL) {
++		if (lx_get_kern_version() == LX_KERN_2_6)
++			(void) strlcpy(lx_release, LX_UNAME_RELEASE_2_6,
++			    sizeof (lx_release));
++		else
++			(void) strlcpy(lx_release, LX_UNAME_RELEASE_2_4,
++			    sizeof (lx_release));
++	} else {
++		(void) strlcpy(lx_release, r, 128);
++	}
++
++	lx_debug("lx_release: %s\n", lx_release);
++
++	/*
++	 * Should we kill an application that attempts an unimplemented
++	 * system call?
++	 */
++	if (getenv("LX_STRICT") != NULL) {
++		lx_strict = 1;
++		lx_debug("STRICT mode enabled.\n");
++	}
++
++	/*
++	 * Are we in install mode?
++	 */
++	if (getenv("LX_INSTALL") != NULL) {
++		lx_install = 1;
++		lx_debug("INSTALL mode enabled.\n");
++	}
++
++	/*
++	 * Should we attempt to send messages to the screen?
++	 */
++	if (getenv("LX_VERBOSE") != NULL) {
++		lx_verbose = 1;
++		lx_debug("VERBOSE mode enabled.\n");
++	}
++
++	lx_debug("executing linux process: %s", argv[0]);
++	lx_debug("branding myself and setting handler to 0x%p",
++	    (void *)lx_handler_table);
++
++	/*
++	 * The version of rpm that ships with CentOS/RHEL 3.x has a race
++	 * condition in it.  If it creates a child process to run a
++	 * post-install script, and that child process completes too
++	 * quickly, it will disappear before the parent notices.  This
++	 * causes the parent to hang forever waiting for the already dead
++	 * child to die.  I'm sure there's a Lazarus joke buried in here
++	 * somewhere.
++	 *
++	 * Anyway, as a workaround, we make every child of an 'rpm' process
++	 * sleep for 1 second, giving the parent a chance to enter its
++	 * wait-for-the-child-to-die loop.  Thay may be the hackiest trick
++	 * in all of our Linux emulation code - and that's saying
++	 * something.
++	 */
++	if (strcmp("rpm", basename(argv[0])) == NULL)
++		lx_is_rpm = B_TRUE;
++
++	reg.lxbr_version = LX_VERSION;
++	reg.lxbr_handler = (void *)&lx_handler_table;
++	reg.lxbr_tracehandler = (void *)&lx_handler_trace_table;
++	reg.lxbr_traceflag = &lx_traceflag;
++
++	/*
++	 * Register the address of the user-space handler with the lx
++	 * brand module.
++	 */
++	if (syscall(SYS_brand, B_REGISTER, &reg))
++		lx_err_fatal(gettext("failed to brand the process"));
++
++	/*
++	 * Download data about the lx executable from the kernel.
++	 */
++	if (syscall(SYS_brand, B_ELFDATA, (void *)&edp))
++		lx_err_fatal(gettext(
++		    "failed to get required ELF data from the kernel"));
++
++	if (lx_ioctl_init() != 0)
++		lx_err_fatal(gettext("failed to setup the %s translator"),
++		    "ioctl");
++
++	if (lx_stat_init() != 0)
++		lx_err_fatal(gettext("failed to setup the %s translator"),
++		    "stat");
++
++	if (lx_statfs_init() != 0)
++		lx_err_fatal(gettext("failed to setup the %s translator"),
++		    "statfs");
++
++	/*
++	 * Find the aux vector on the stack.
++	 */
++	p = (int *)envp;
++	while (*p != NULL)
++		p++;
++	/*
++	 * p is now pointing at the 0 word after the environ pointers. After
++	 * that is the aux vectors.
++	 */
++	p++;
++	for (ap = (auxv_t *)p; ap->a_type != 0; ap++) {
++		switch (ap->a_type) {
++			case AT_BASE:
++				ap->a_un.a_val = edp.ed_base;
++				break;
++			case AT_ENTRY:
++				ap->a_un.a_val = edp.ed_entry;
++				break;
++			case AT_PHDR:
++				ap->a_un.a_val = edp.ed_phdr;
++				break;
++			case AT_PHENT:
++				ap->a_un.a_val = edp.ed_phent;
++				break;
++			case AT_PHNUM:
++				ap->a_un.a_val = edp.ed_phnum;
++				break;
++			default:
++				break;
++		}
++	}
++
++	/* Do any thunk server initalization. */
++	lxt_server_init(argc, argv);
++
++	/* Setup signal handler information. */
++	if (lx_siginit())
++		lx_err_fatal(gettext(
++		    "failed to initialize lx signals for the branded process"));
++
++	/* Setup thread-specific data area for managing linux threads. */
++	if ((err = thr_keycreate(&lx_tsd_key, NULL)) != 0)
++		lx_err_fatal(
++		    gettext("%s failed: %s"), "thr_keycreate(lx_tsd_key)",
++		    strerror(err));
++
++	lx_debug("thr_keycreate created lx_tsd_key (%d)", lx_tsd_key);
++
++	/* Initialize the thread specific data for this thread. */
++	bzero(&lx_tsd, sizeof (lx_tsd));
++	lx_tsd.lxtsd_gs = LWPGS_SEL;
++
++	if ((err = thr_setspecific(lx_tsd_key, &lx_tsd)) != 0)
++		lx_err_fatal(gettext(
++		    "Unable to initialize thread-specific data: %s"),
++		    strerror(err));
++
++	/*
++	 * Save the current context of this thread.
++	 * We'll restore this context when this thread attempts to exit.
++	 */
++	if (getcontext(&lx_tsd.lxtsd_exit_context) != 0)
++		lx_err_fatal(gettext(
++		    "Unable to initialize thread-specific exit context: %s"),
++		    strerror(errno));
++
++	if (lx_tsd.lxtsd_exit == 0) {
++		lx_runexe(argv, edp.ed_ldentry);
++		/* lx_runexe() never returns. */
++		assert(0);
++	}
++
++	/*
++	 * We are here because the Linux application called the exit() or
++	 * exit_group() system call.  In turn the brand library did a
++	 * setcontext() to jump to the thread context state we saved above.
++	 */
++	if (lx_tsd.lxtsd_exit == 1)
++		thr_exit((void *)lx_tsd.lxtsd_exit_status);
++	else
++		exit(lx_tsd.lxtsd_exit_status);
++
++	assert(0);
++
++	/*NOTREACHED*/
++	return (0);
++}
++
++/*
++ * Walk back through the stack until we find the lx_emulate() frame.
++ */
++lx_regs_t *
++lx_syscall_regs(void)
++{
++	/* LINTED - alignment */
++	struct frame *fr = (struct frame *)_getfp();
++
++	while (fr->fr_savpc != (uintptr_t)&lx_emulate_done) {
++		fr = (struct frame *)fr->fr_savfp;
++		assert(fr->fr_savpc != NULL);
++	}
++
++	return ((lx_regs_t *)((uintptr_t *)fr)[2]);
++}
++
++int
++lx_lpid_to_spair(pid_t lpid, pid_t *spid, lwpid_t *slwp)
++{
++	pid_t pid;
++	lwpid_t tid;
++
++	if (lpid == 0) {
++		pid = getpid();
++		tid = thr_self();
++	} else {
++		if (syscall(SYS_brand, B_LPID_TO_SPAIR, lpid, &pid, &tid) < 0)
++			return (-errno);
++
++		/*
++		 * If the returned pid is -1, that indicates we tried to
++		 * look up the PID for init, but that process no longer
++		 * exists.
++		 */
++		if (pid == -1)
++			return (-ESRCH);
++	}
++
++	if (uucopy(&pid, spid, sizeof (pid_t)) != 0)
++		return (-errno);
++
++	if (uucopy(&tid, slwp, sizeof (lwpid_t)) != 0)
++		return (-errno);
++
++	return (0);
++}
++
++int
++lx_lpid_to_spid(pid_t lpid, pid_t *spid)
++{
++	lwpid_t slwp;
++
++	return (lx_lpid_to_spair(lpid, spid, &slwp));
++}
++
++char *
++lx_fd_to_path(int fd, char *buf, int buf_size)
++{
++	char	path_proc[MAXPATHLEN];
++	pid_t	pid;
++	int	n;
++
++	assert((buf != NULL) && (buf_size >= 0));
++
++	if (fd < 0)
++		return (NULL);
++
++	if ((pid = getpid()) == -1)
++		return (NULL);
++
++	(void) snprintf(path_proc, MAXPATHLEN,
++	    "/native/proc/%d/path/%d", pid, fd);
++
++	if ((n = readlink(path_proc, buf, buf_size - 1)) == -1)
++		return (NULL);
++	buf[n] = '\0';
++
++	return (buf);
++}
++
++/*
++ * Create a translation routine that jumps to a particular emulation
++ * module syscall.
++ */
++#define	IN_KERNEL_SYSCALL(name, num)		\
++int						\
++lx_##name(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,	\
++	uintptr_t p5, uintptr_t p6)		\
++{						\
++	int r;					\
++	lx_debug("\tsyscall %d re-vectoring to lx kernel module "	\
++	    "for " #name "()", num);		\
++	r = syscall(SYS_brand, B_EMULATE_SYSCALL + num, p1, p2,		\
++	    p3, p4, p5, p6);			\
++	return ((r == -1) ? -errno : r);		\
++}
++
++IN_KERNEL_SYSCALL(kill, 37)
++IN_KERNEL_SYSCALL(brk, 45)
++IN_KERNEL_SYSCALL(ustat, 62)
++IN_KERNEL_SYSCALL(getppid, 64)
++IN_KERNEL_SYSCALL(sysinfo, 116)
++IN_KERNEL_SYSCALL(modify_ldt, 123)
++IN_KERNEL_SYSCALL(adjtimex, 124)
++IN_KERNEL_SYSCALL(setresuid16, 164)
++IN_KERNEL_SYSCALL(setresgid16, 170)
++IN_KERNEL_SYSCALL(setresuid, 208)
++IN_KERNEL_SYSCALL(setresgid, 210)
++IN_KERNEL_SYSCALL(gettid, 224)
++IN_KERNEL_SYSCALL(tkill, 238)
++IN_KERNEL_SYSCALL(futex, 240)
++IN_KERNEL_SYSCALL(set_thread_area, 243)
++IN_KERNEL_SYSCALL(get_thread_area, 244)
++IN_KERNEL_SYSCALL(set_tid_address, 258)
++
++static struct lx_sysent sysents[] = {
++	{"nosys",	NULL,		NOSYS_NULL,	0},	/*  0 */
++	{"exit",	lx_exit,	0,		1},	/*  1 */
++	{"fork",	lx_fork,	0,		0},	/*  2 */
++	{"read",	lx_read,	0,		3},	/*  3 */
++	{"write",	write,		SYS_PASSTHRU,	3},	/*  4 */
++	{"open",	lx_open,	0,		3},	/*  5 */
++	{"close",	close,		SYS_PASSTHRU,	1},	/*  6 */
++	{"waitpid",	lx_waitpid,	0,		3},	/*  7 */
++	{"creat",	creat,		SYS_PASSTHRU,	2},	/*  8 */
++	{"link",	lx_link,	0,		2},	/*  9 */
++	{"unlink",	lx_unlink,	0,		1},	/* 10 */
++	{"execve",	lx_execve,	0,		3},	/* 11 */
++	{"chdir",	chdir,		SYS_PASSTHRU,	1},	/* 12 */
++	{"time",	lx_time,	0,		1},	/* 13 */
++	{"mknod",	lx_mknod,	0,		3},	/* 14 */
++	{"chmod",	lx_chmod,	0,		2},	/* 15 */
++	{"lchown16",	lx_lchown16,	0,		3},	/* 16 */
++	{"break",	NULL,		NOSYS_OBSOLETE,	0},	/* 17 */
++	{"stat",	NULL,		NOSYS_OBSOLETE,	0},	/* 18 */
++	{"lseek",	lx_lseek,	0,		3},	/* 19 */
++	{"getpid",	lx_getpid,	0,		0},	/* 20 */
++	{"mount",	lx_mount,	0,		5},	/* 21 */
++	{"umount",	lx_umount,	0,		1},	/* 22 */
++	{"setuid16",	lx_setuid16,	0,		1},	/* 23 */
++	{"getuid16",	lx_getuid16,	0,		0},	/* 24 */
++	{"stime",	stime,		SYS_PASSTHRU,	1},	/* 25 */
++	{"ptrace",	lx_ptrace,	0,		4},	/* 26 */
++	{"alarm",	(int (*)())alarm, SYS_PASSTHRU,	1},	/* 27 */
++	{"fstat",	NULL,		NOSYS_OBSOLETE,	0},	/* 28 */
++	{"pause",	pause,		SYS_PASSTHRU,	0},	/* 29 */
++	{"utime",	lx_utime,	0,		2},	/* 30 */
++	{"stty",	NULL,		NOSYS_OBSOLETE,	0},	/* 31 */
++	{"gtty",	NULL,		NOSYS_OBSOLETE,	0},	/* 32 */
++	{"access",	access,		SYS_PASSTHRU,	2},	/* 33 */
++	{"nice",	nice,		SYS_PASSTHRU,	1},	/* 34 */
++	{"ftime",	NULL,		NOSYS_OBSOLETE,	0},	/* 35 */
++	{"sync",	lx_sync, 	0, 		0},	/* 36 */
++	{"kill",	lx_kill,	0,		2},	/* 37 */
++	{"rename",	lx_rename,	0,		2},	/* 38 */
++	{"mkdir",	mkdir,		SYS_PASSTHRU,	2},	/* 39 */
++	{"rmdir",	lx_rmdir,	0,		1},	/* 40 */
++	{"dup",		dup,		SYS_PASSTHRU,	1},	/* 41 */
++	{"pipe",	lx_pipe,	0,		1},	/* 42 */
++	{"times",	lx_times,	0,		1},	/* 43 */
++	{"prof",	NULL,		NOSYS_OBSOLETE,	0},	/* 44 */
++	{"brk",		lx_brk,		0,		1},	/* 45 */
++	{"setgid16",	lx_setgid16,	0,		1},	/* 46 */
++	{"getgid16",	lx_getgid16,	0,		0},	/* 47 */
++	{"signal",	lx_signal,	0,		2},	/* 48 */
++	{"geteuid16",	lx_geteuid16,	0,		0},	/* 49 */
++	{"getegid16",	lx_getegid16,	0,		0},	/* 50 */
++	{"acct",	NULL,		NOSYS_NO_EQUIV,	0},	/* 51 */
++	{"umount2",	lx_umount2,	0,		2},	/* 52 */
++	{"lock",	NULL,		NOSYS_OBSOLETE,	0},	/* 53 */
++	{"ioctl",	lx_ioctl,	0,		3},	/* 54 */
++	{"fcntl",	lx_fcntl,	0,		3},	/* 55 */
++	{"mpx",		NULL,		NOSYS_OBSOLETE,	0},	/* 56 */
++	{"setpgid",	lx_setpgid,	0,		2},	/* 57 */
++	{"ulimit",	NULL,		NOSYS_OBSOLETE,	0},	/* 58 */
++	{"olduname",	NULL,		NOSYS_OBSOLETE,	0},	/* 59 */
++	{"umask",	(int (*)())umask, SYS_PASSTHRU,	1},	/* 60 */
++	{"chroot",	chroot,		SYS_PASSTHRU,	1},	/* 61 */
++	{"ustat",	lx_ustat,	0,		2},	/* 62 */
++	{"dup2",	lx_dup2,	0,		2},	/* 63 */
++	{"getppid",	lx_getppid,	0,		0},	/* 64 */
++	{"getpgrp",	lx_getpgrp,	0,		0},	/* 65 */
++	{"setsid",	lx_setsid,	0,		0},	/* 66 */
++	{"sigaction",	lx_sigaction,	0,		3},	/* 67 */
++	{"sgetmask",	NULL,		NOSYS_OBSOLETE,	0},	/* 68 */
++	{"ssetmask",	NULL,		NOSYS_OBSOLETE,	0},	/* 69 */
++	{"setreuid16",	lx_setreuid16,	0,		2},	/* 70 */
++	{"setregid16",	lx_setregid16,	0,		2},	/* 71 */
++	{"sigsuspend",	lx_sigsuspend,	0,		1},	/* 72 */
++	{"sigpending",	lx_sigpending,	0,		1},	/* 73 */
++	{"sethostname",	lx_sethostname,	0,		2},	/* 74 */
++	{"setrlimit",	lx_setrlimit,	0,		2},	/* 75 */
++	{"getrlimit",	lx_oldgetrlimit, 0,		2},	/* 76 */
++	{"getrusage",	lx_getrusage,	0,		2},	/* 77 */
++	{"gettimeofday", lx_gettimeofday, 0,		2},	/* 78 */
++	{"settimeofday", lx_settimeofday, 0,		2},	/* 79 */
++	{"getgroups16",	lx_getgroups16,	0,		2},	/* 80 */
++	{"setgroups16",	lx_setgroups16,	0,		2},	/* 81 */
++	{"select",	NULL,		NOSYS_OBSOLETE,	0},	/* 82 */
++	{"symlink",	symlink,	SYS_PASSTHRU,	2},	/* 83 */
++	{"oldlstat",	NULL,		NOSYS_OBSOLETE,	0},	/* 84 */
++	{"readlink",	readlink,	SYS_PASSTHRU,	3},	/* 85 */
++	{"uselib",	NULL,		NOSYS_KERNEL,	0},	/* 86 */
++	{"swapon",	NULL,		NOSYS_KERNEL,	0},	/* 87 */
++	{"reboot",	lx_reboot,	0,		4},	/* 88 */
++	{"readdir",	lx_readdir,	0,		3},	/* 89 */
++	{"mmap",	lx_mmap,	0,		6},	/* 90 */
++	{"munmap",	munmap,		SYS_PASSTHRU,	2},	/* 91 */
++	{"truncate",	lx_truncate,	0,		2},	/* 92 */
++	{"ftruncate",	lx_ftruncate,	0,		2},	/* 93 */
++	{"fchmod",	fchmod,		SYS_PASSTHRU,	2},	/* 94 */
++	{"fchown16",	lx_fchown16,	0,		3},	/* 95 */
++	{"getpriority",	lx_getpriority,	0,		2},	/* 96 */
++	{"setpriority",	lx_setpriority,	0,		3},	/* 97 */
++	{"profil",	NULL,		NOSYS_NO_EQUIV,	0},	/* 98 */
++	{"statfs",	lx_statfs,	0,		2},	/* 99 */
++	{"fstatfs",	lx_fstatfs,	0,		2},	/* 100 */
++	{"ioperm",	NULL,		NOSYS_NO_EQUIV,	0},	/* 101 */
++	{"socketcall",	lx_socketcall,	0,		2},	/* 102 */
++	{"syslog",	NULL,		NOSYS_KERNEL,	0},	/* 103 */
++	{"setitimer",	lx_setitimer,	0,		3},	/* 104 */
++	{"getitimer",	getitimer,	SYS_PASSTHRU,	2},	/* 105 */
++	{"stat",	lx_stat,	0,		2},	/* 106 */
++	{"lstat",	lx_lstat,	0,		2},	/* 107 */
++	{"fstat",	lx_fstat,	0,		2},	/* 108 */
++	{"uname",	NULL,		NOSYS_OBSOLETE,	0},	/* 109 */
++	{"oldiopl",	NULL,		NOSYS_NO_EQUIV,	0},	/* 110 */
++	{"vhangup",	lx_vhangup,	0,		0},	/* 111 */
++	{"idle",	NULL,		NOSYS_NO_EQUIV,	0},	/* 112 */
++	{"vm86old",	NULL,		NOSYS_OBSOLETE,	0},	/* 113 */
++	{"wait4",	lx_wait4,	0,		4},	/* 114 */
++	{"swapoff",	NULL,		NOSYS_KERNEL,	0},	/* 115 */
++	{"sysinfo",	lx_sysinfo,	0,		1},	/* 116 */
++	{"ipc",		lx_ipc,		0,		5},	/* 117 */
++	{"fsync",	lx_fsync,	0,		1},	/* 118 */
++	{"sigreturn",	lx_sigreturn,	0,		1},	/* 119 */
++	{"clone",	lx_clone,	0,		5},	/* 120 */
++	{"setdomainname", lx_setdomainname, 0,		2},	/* 121 */
++	{"uname",	lx_uname,	0,		1},	/* 122 */
++	{"modify_ldt",	lx_modify_ldt,	0,		3},	/* 123 */
++	{"adjtimex",	lx_adjtimex,	0,		1},	/* 124 */
++	{"mprotect",	lx_mprotect,	0,		3},	/* 125 */
++	{"sigprocmask",	lx_sigprocmask,	0,		3},	/* 126 */
++	{"create_module", NULL,		NOSYS_KERNEL,	0},	/* 127 */
++	{"init_module",	NULL,		NOSYS_KERNEL,	0},	/* 128 */
++	{"delete_module", NULL,		NOSYS_KERNEL,	0},	/* 129 */
++	{"get_kernel_syms", NULL,	NOSYS_KERNEL,	0},	/* 130 */
++	{"quotactl",	NULL,		NOSYS_KERNEL,	0},	/* 131 */
++	{"getpgid",	lx_getpgid,	0,		1},	/* 132 */
++	{"fchdir",	fchdir,		SYS_PASSTHRU,	1},	/* 133 */
++	{"bdflush",	NULL,		NOSYS_KERNEL,	0},	/* 134 */
++	{"sysfs",	lx_sysfs, 	0,		3},	/* 135 */
++	{"personality",	lx_personality,	0,		1},	/* 136 */
++	{"afs_syscall",	NULL,		NOSYS_KERNEL,	0},	/* 137 */
++	{"setfsuid16",	lx_setfsuid16,	0,		1},	/* 138 */
++	{"setfsgid16",	lx_setfsgid16,	0,		1},	/* 139 */
++	{"llseek",	lx_llseek,	0,		5},	/* 140 */
++	{"getdents",	getdents,	SYS_PASSTHRU,	3},	/* 141 */
++	{"select",	lx_select,	0,		5},	/* 142 */
++	{"flock",	lx_flock,	0,		2},	/* 143 */
++	{"msync",	lx_msync,	0,		3},	/* 144 */
++	{"readv",	lx_readv,	0,		3},	/* 145 */
++	{"writev",	lx_writev,	0,		3},	/* 146 */
++	{"getsid",	lx_getsid,	0,		1},	/* 147 */
++	{"fdatasync",	lx_fdatasync,	0,		1},	/* 148 */
++	{"sysctl",	lx_sysctl,	0,		1},	/* 149 */
++	{"mlock",	lx_mlock,	0,		2},	/* 150 */
++	{"munlock",	lx_munlock,	0,		2},	/* 151 */
++	{"mlockall",	lx_mlockall,	0,		1},	/* 152 */
++	{"munlockall",	lx_munlockall,	0,		0},	/* 153 */
++	{"sched_setparam", lx_sched_setparam,	0,	2},	/* 154 */
++	{"sched_getparam", lx_sched_getparam,	0,	2},	/* 155 */
++	{"sched_setscheduler", lx_sched_setscheduler, 0, 3},	/* 156 */
++	{"sched_getscheduler", lx_sched_getscheduler, 0, 1},	/* 157 */
++	{"sched_yield",	(int (*)())yield, SYS_PASSTHRU,	0},	/* 158 */
++	{"sched_get_priority_max", lx_sched_get_priority_max, 0, 1}, /* 159 */
++	{"sched_get_priority_min", lx_sched_get_priority_min, 0, 1}, /* 160 */
++	{"sched_rr_get_interval", lx_sched_rr_get_interval, 0,	2},  /* 161 */
++	{"nanosleep",	nanosleep,	SYS_PASSTHRU,	2},	/* 162 */
++	{"mremap",	NULL,		NOSYS_NO_EQUIV,	0},	/* 163 */
++	{"setresuid16",	lx_setresuid16,	0,		3},	/* 164 */
++	{"getresuid16",	lx_getresuid16,	0,		3},	/* 165 */
++	{"vm86",	NULL,		NOSYS_NO_EQUIV,	0},	/* 166 */
++	{"query_module", lx_query_module, NOSYS_KERNEL,	5},	/* 167 */
++	{"poll",	lx_poll,	0,		3},	/* 168 */
++	{"nfsservctl",	NULL,		NOSYS_KERNEL,	0},	/* 169 */
++	{"setresgid16",	lx_setresgid16,	0,		3},	/* 170 */
++	{"getresgid16",	lx_getresgid16,	0,		3},	/* 171 */
++	{"prctl",	NULL,		NOSYS_UNDOC,	0},	/* 172 */
++	{"rt_sigreturn", lx_rt_sigreturn, 0,		0},	/* 173 */
++	{"rt_sigaction", lx_rt_sigaction, 0,		4},	/* 174 */
++	{"rt_sigprocmask", lx_rt_sigprocmask, 0,	4},	/* 175 */
++	{"rt_sigpending", lx_rt_sigpending, 0,		2},	/* 176 */
++	{"rt_sigtimedwait", lx_rt_sigtimedwait,	0,	4},	/* 177 */
++	{"sigqueueinfo", NULL,		NOSYS_UNDOC,	0},	/* 178 */
++	{"rt_sigsuspend", lx_rt_sigsuspend, 0,		2},	/* 179 */
++	{"pread64",	lx_pread64,	0,		5},	/* 180 */
++	{"pwrite64",	lx_pwrite64,	0,		5},	/* 181 */
++	{"chown16",	lx_chown16,	0,		3},	/* 182 */
++	{"getcwd",	lx_getcwd,	0,		2},	/* 183 */
++	{"capget",	NULL,		NOSYS_NO_EQUIV,	0},	/* 184 */
++	{"capset",	NULL,		NOSYS_NO_EQUIV,	0},	/* 185 */
++	{"sigaltstack",	lx_sigaltstack,	0,		2},	/* 186 */
++	{"sendfile",	lx_sendfile,	0,		4},	/* 187 */
++	{"getpmsg",	NULL,		NOSYS_OBSOLETE,	0},	/* 188 */
++	{"putpmsg",	NULL,		NOSYS_OBSOLETE,	0},	/* 189 */
++	{"vfork",	lx_vfork,	0,		0},	/* 190 */
++	{"getrlimit",	lx_getrlimit,	0,		2},	/* 191 */
++	{"mmap2",	lx_mmap2,	EBP_HAS_ARG6,	6},	/* 192 */
++	{"truncate64",	lx_truncate64,	0,		3},	/* 193 */
++	{"ftruncate64",	lx_ftruncate64,	0,		3},	/* 194 */
++	{"stat64",	lx_stat64,	0,		2},	/* 195 */
++	{"lstat64",	lx_lstat64,	0,		2},	/* 196 */
++	{"fstat64",	lx_fstat64,	0,		2},	/* 197 */
++	{"lchown",	lchown,		SYS_PASSTHRU,	3},	/* 198 */
++	{"getuid",	(int (*)())getuid, SYS_PASSTHRU, 0},	/* 199 */
++	{"getgid",	(int (*)())getgid, SYS_PASSTHRU, 0},	/* 200 */
++	{"geteuid",	lx_geteuid,	0,		0},	/* 201 */
++	{"getegid",	lx_getegid,	0,		0},	/* 202 */
++	{"setreuid",	setreuid,	SYS_PASSTHRU,	0},	/* 203 */
++	{"setregid",	setregid,	SYS_PASSTHRU,	0},	/* 204 */
++	{"getgroups",	getgroups,	SYS_PASSTHRU,	2},	/* 205 */
++	{"setgroups",	lx_setgroups,	0,		2},	/* 206 */
++	{"fchown",	lx_fchown,	0,		3},	/* 207 */
++	{"setresuid",	lx_setresuid,	0,		3},	/* 208 */
++	{"getresuid",	lx_getresuid,	0,		3},	/* 209 */
++	{"setresgid",	lx_setresgid,	0,		3},	/* 210 */
++	{"getresgid",	lx_getresgid,	0,		3},	/* 211 */
++	{"chown",	lx_chown,	0,		3},	/* 212 */
++	{"setuid",	setuid,		SYS_PASSTHRU,	1},	/* 213 */
++	{"setgid",	setgid,		SYS_PASSTHRU,	1},	/* 214 */
++	{"setfsuid",	lx_setfsuid,	0,		1},	/* 215 */
++	{"setfsgid",	lx_setfsgid,	0,		1},	/* 216 */
++	{"pivot_root",	NULL,		NOSYS_KERNEL,	0},	/* 217 */
++	{"mincore",	mincore,	SYS_PASSTHRU,	3},	/* 218 */
++	{"madvise",	lx_madvise,	0,		3},	/* 219 */
++	{"getdents64",	lx_getdents64,	0,		3},	/* 220 */
++	{"fcntl64",	lx_fcntl64,	0,		3},	/* 221 */
++	{"tux",		NULL,		NOSYS_NO_EQUIV,	0},	/* 222 */
++	{"security",	NULL,		NOSYS_NO_EQUIV,	0},	/* 223 */
++	{"gettid",	lx_gettid,	0,		0},	/* 224 */
++	{"readahead",	NULL,		NOSYS_NO_EQUIV,	0},	/* 225 */
++	{"setxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 226 */
++	{"lsetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 227 */
++	{"fsetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 228 */
++	{"getxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 229 */
++	{"lgetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 230 */
++	{"fgetxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 231 */
++	{"listxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 232 */
++	{"llistxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 233 */
++	{"flistxattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 234 */
++	{"removexattr",	NULL,		NOSYS_NO_EQUIV,	0},	/* 235 */
++	{"lremovexattr", NULL,		NOSYS_NO_EQUIV,	0},	/* 236 */
++	{"fremovexattr", NULL,		NOSYS_NO_EQUIV,	0},	/* 237 */
++	{"tkill",	lx_tkill,	0,		2},	/* 238 */
++	{"sendfile64",	lx_sendfile64,	0,		4},	/* 239 */
++	{"futex",	lx_futex,	EBP_HAS_ARG6,	6},	/* 240 */
++	{"sched_setaffinity",	lx_sched_setaffinity,	0, 3},	/* 241 */
++	{"sched_getaffinity",	lx_sched_getaffinity,	0, 3},	/* 242 */
++	{"set_thread_area", lx_set_thread_area,	0,	1},	/* 243 */
++	{"get_thread_area", lx_get_thread_area,	0,	1},	/* 244 */
++	{"io_setup",	NULL,		NOSYS_NO_EQUIV,	0},	/* 245 */
++	{"io_destroy",	NULL,		NOSYS_NO_EQUIV,	0},	/* 246 */
++	{"io_getevents", NULL,		NOSYS_NO_EQUIV,	0},	/* 247 */
++	{"io_submit",	NULL,		NOSYS_NO_EQUIV,	0},	/* 248 */
++	{"io_cancel",	NULL,		NOSYS_NO_EQUIV,	0},	/* 249 */
++	{"fadvise64",	NULL,		NOSYS_UNDOC,	0},	/* 250 */
++	{"nosys",	NULL,		0,		0},	/* 251 */
++	{"group_exit",	lx_group_exit,	0,		1},	/* 252 */
++	{"lookup_dcookie", NULL,	NOSYS_NO_EQUIV,	0},	/* 253 */
++	{"epoll_create", NULL,		NOSYS_NO_EQUIV,	0},	/* 254 */
++	{"epoll_ctl",	NULL,		NOSYS_NO_EQUIV,	0},	/* 255 */
++	{"epoll_wait",	NULL,		NOSYS_NO_EQUIV,	0},	/* 256 */
++	{"remap_file_pages", NULL,	NOSYS_NO_EQUIV,	0},	/* 257 */
++	{"set_tid_address", lx_set_tid_address,	0,	1},	/* 258 */
++	{"timer_create", NULL,		NOSYS_UNDOC,	0},	/* 259 */
++	{"timer_settime", NULL,		NOSYS_UNDOC,	0},	/* 260 */
++	{"timer_gettime", NULL,		NOSYS_UNDOC,	0},	/* 261 */
++	{"timer_getoverrun", NULL,	NOSYS_UNDOC,	0},	/* 262 */
++	{"timer_delete", NULL,		NOSYS_UNDOC,	0},	/* 263 */
++	{"clock_settime", lx_clock_settime,	0,	2},	/* 264 */
++	{"clock_gettime", lx_clock_gettime,	0,	2},	/* 265 */
++	{"clock_getres", lx_clock_getres,	0,	2},	/* 266 */
++	{"clock_nanosleep", lx_clock_nanosleep,	0,	4},	/* 267 */
++	{"statfs64",	lx_statfs64,	0,		2},	/* 268 */
++	{"fstatfs64",	lx_fstatfs64,	0,		2},	/* 269 */
++	{"tgkill",	lx_tgkill,	0,		3},	/* 270 */
++
++	/* The following system calls only exist in kernel 2.6 and greater */
++	{"utimes",	utimes,		SYS_PASSTHRU,	2},	/* 271 */
++	{"fadvise64_64", NULL,		NOSYS_NULL,	0},	/* 272 */
++	{"vserver",	NULL,		NOSYS_NULL,	0},	/* 273 */
++	{"mbind",	NULL,		NOSYS_NULL,	0},	/* 274 */
++	{"get_mempolicy", NULL,		NOSYS_NULL,	0},	/* 275 */
++	{"set_mempolicy", NULL,		NOSYS_NULL,	0},	/* 276 */
++	{"mq_open",	NULL,		NOSYS_NULL,	0},	/* 277 */
++	{"mq_unlink",	NULL,		NOSYS_NULL,	0},	/* 278 */
++	{"mq_timedsend", NULL,		NOSYS_NULL,	0},	/* 279 */
++	{"mq_timedreceive", NULL,	NOSYS_NULL,	0},	/* 280 */
++	{"mq_notify",	NULL,		NOSYS_NULL,	0},	/* 281 */
++	{"mq_getsetattr", NULL,		NOSYS_NULL,	0},	/* 282 */
++	{"kexec_load",	NULL,		NOSYS_NULL,	0},	/* 283 */
++	{"waitid",	lx_waitid,	0,		4},	/* 284 */
++	{"sys_setaltroot", NULL,	NOSYS_NULL,	0},	/* 285 */
++	{"add_key",	NULL,		NOSYS_NULL,	0},	/* 286 */
++	{"request_key",	NULL,		NOSYS_NULL,	0},	/* 287 */
++	{"keyctl",	NULL,		NOSYS_NULL,	0},	/* 288 */
++	{"ioprio_set",	NULL,		NOSYS_NULL,	0},	/* 289 */
++	{"ioprio_get",	NULL,		NOSYS_NULL,	0},	/* 290 */
++	{"inotify_init", NULL,		NOSYS_NULL,	0},	/* 291 */
++	{"inotify_add_watch", NULL,	NOSYS_NULL,	0},	/* 292 */
++	{"inotify_rm_watch", NULL,	NOSYS_NULL,	0},	/* 293 */
++	{"migrate_pages", NULL,		NOSYS_NULL,	0},	/* 294 */
++	{"openat",	lx_openat,	0,		4},	/* 295 */
++	{"mkdirat",	lx_mkdirat,	0,		3},	/* 296 */
++	{"mknodat",	lx_mknodat,	0,		4},	/* 297 */
++	{"fchownat",	lx_fchownat,	0,		5},	/* 298 */
++	{"futimesat",	lx_futimesat,	0,		3},	/* 299 */
++	{"fstatat64",	lx_fstatat64,	0,		4},	/* 300 */
++	{"unlinkat",	lx_unlinkat,	0,		3},	/* 301 */
++	{"renameat",	lx_renameat,	0,		4},	/* 302 */
++	{"linkat",	lx_linkat,	0,		5},	/* 303 */
++	{"symlinkat",	lx_symlinkat,	0,		3},	/* 304 */
++	{"readlinkat",	lx_readlinkat,	0,		4},	/* 305 */
++	{"fchmodat",	lx_fchmodat,	0,		4},	/* 306 */
++	{"faccessat",	lx_faccessat,	0,		4},	/* 307 */
++	{"pselect6",	NULL,		NOSYS_NULL,	0},	/* 308 */
++	{"ppoll",	NULL,		NOSYS_NULL,	0},	/* 309 */
++	{"unshare",	NULL,		NOSYS_NULL,	0},	/* 310 */
++	{"set_robust_list", NULL,	NOSYS_NULL,	0},	/* 311 */
++	{"get_robust_list", NULL,	NOSYS_NULL,	0},	/* 312 */
++	{"splice",	NULL,		NOSYS_NULL,	0},	/* 313 */
++	{"sync_file_range", NULL,	NOSYS_NULL,	0},	/* 314 */
++	{"tee",		NULL,		NOSYS_NULL,	0},	/* 315 */
++	{"vmsplice",	NULL,		NOSYS_NULL,	0},	/* 316 */
++	{"move_pages",	NULL,		NOSYS_NULL,	0},	/* 317 */
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/lx_thunk_server.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/lx_thunk_server.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,1026 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++/*
++ * The BrandZ Linux thunking server.
++ *
++ * The interfaces defined in this file form the server side of a bridge
++ * to allow native solaris process to access Linux services.  Currently
++ * the Linux services that is made accessible by these interfaces here
++ * are:
++ *	- Linux host <-> address naming services
++ *	- Linux service <-> port naming services
++ *	- Linux syslog
++ *
++ * Access to all these services is provided through a doors server.
++ * Currently the only client of these interfaces and the process that
++ * initially starts up the doors server is lx_thunk.so.
++ *
++ * lx_thunk.so is a native solaris library that is loaded into native
++ * solaris process that need to run inside a Linux zone and have access
++ * to Linux services.  When lx_thunk.so receives a request that requires
++ * accessing Linux services it creates a "thunk server" process by
++ * forking and executing the following shell script (which runs as
++ * a native /bin/sh Linux process):
++ * 	/native/usr/lib/brand/lx/lx_thunk
++ *
++ * The first and only thing this shell script attempts to do is re-exec
++ * itself.  The brand library will detect when this script attempts to
++ * re-exec itself and take control of the process.  The exec() system
++ * call made by the Linux shell will never return.
++ *
++ * At this point the process becomes a "thunk server" process.
++ * The first thing it does is a bunch of initialization:
++ *
++ * - Sanity check that a file descriptor based communication mechanism
++ *   needed talk to the parent process is correctly initialized.
++ *
++ * - Verify that two predetermined file descriptors are FIFOs.
++ *   These FIFOs will be used to establish communications with
++ *   the client program that spawned us and which will be sending
++ *   us requests.
++ *
++ * - Use existing debugging libraries (libproc.so, librtld_db.so,
++ *   and the BrandZ lx plug-in to librtld_db.so) and /native/proc to
++ *   walk the Linux link maps in our own address space to determine
++ *   the address of the Linux dlsym() function.
++ *
++ * - Use the native Linux dlsym() function to look up other symbols
++ *   (for both functions and variables) that we will need access
++ *   to service thunking requests.
++ *
++ * - Create a doors server and notify the parent process that we
++ *   are ready to service requests.
++ *
++ * - Enter a service loop and wait for requests.
++ *
++ * At this point the lx_thunk process is ready to service door
++ * based requests.  When door service request is received the
++ * following happens inside the lx_thunk process:
++ *
++ * - The doors server function is is invoked on a new solaris thread
++ *   that the kernel injects into the lx_thunk process.  We sanity
++ *   check the incoming request, place it on a service queue, and
++ *   wait for notification that the request has been completed.
++ *
++ * - A Linux thread takes this request off the service queue
++ *   and dispatches it to a service function that will:
++ *	- Decode the request.
++ *	- Handle the request by invoking native Linux interfaces.
++ *	- Encode the results for the request.
++ *
++ * - The Linux thread then notifies the requesting doors server
++ *   thread that the  request has been completed and goes to sleep
++ *   until it receives another request.
++ *
++ * - the solaris door server thread returns the results of the
++ *   operation to the caller.
++ *
++ * Notes:
++ *
++ * - The service request hand off operation from the solaris doors thread to
++ *   the "Linux thread" is required because only "Linux threads" can call
++ *   into Linux code.  In this context a "Linux thread" is a thread that
++ *   is either the initial thread of a Linux process or a thread that was
++ *   created by calling the Linux version of thread_create().  The reason
++ *   for this restriction is that any thread that invokes Linux code needs
++ *   to have been initialized in the Linux threading libraries and have
++ *   things like Linux thread local storage properly setup.
++ *
++ *   But under solaris all door server threads are created and destroyed
++ *   dynamically.  This means that when a doors server function is invoked,
++ *   it is invoked via a thread that hasn't been initialized in the Linux
++ *   environment and there for can't call directly into Linux code.
++ *
++ * - Currently when a thunk server process is starting up, it communicated
++ *   with it's parent via two FIFOs.  These FIFOs are setup by the
++ *   lx_thunk.so library.  After creating the FIFOs and starting the lx_thunk
++ *   server, lx_thunk.so writes the name of the file that the door should
++ *   be attached to to the first pipe.  The lx_thunk server reads in this
++ *   value, initialized the server, fattach()s it to the file request by
++ *   lx_thunk.so and does a write to the second FIFO to let lx_thunk.so
++ *   know that the server is ready to take requests.
++ *
++ *   This negotiation could be simplified to use only use one FIFO.
++ *   lx_thunk.so would attempt to read from the FIFO and the lx_thunk
++ *   server process could send the new door server file descriptor
++ *   to this process via an I_SENDFD ioctl (see streamio.7I).
++ *
++ * - The lx_thunk server process will exit when the client process
++ *   that it's handling requests for exists.  (ie, when there are no
++ *   more open file handles to the doors server.)
++ */
++
++#include <assert.h>
++#include <door.h>
++#include <errno.h>
++#include <libproc.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <strings.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_thread.h>
++#include <sys/lx_thunk_server.h>
++#include <sys/varargs.h>
++#include <thread.h>
++#include <unistd.h>
++
++/*
++ * Generic interfaces used for looking up and calling Linux functions.
++ */
++typedef struct __lx_handle_dlsym	*lx_handle_dlsym_t;
++typedef struct __lx_handle_sym		*lx_handle_sym_t;
++
++uintptr_t lx_call0(lx_handle_sym_t);
++uintptr_t lx_call1(lx_handle_sym_t, uintptr_t);
++uintptr_t lx_call2(lx_handle_sym_t, uintptr_t, uintptr_t);
++uintptr_t lx_call3(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t);
++uintptr_t lx_call4(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t,
++    uintptr_t);
++uintptr_t lx_call5(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t,
++    uintptr_t, uintptr_t);
++uintptr_t lx_call6(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t,
++    uintptr_t, uintptr_t, uintptr_t);
++uintptr_t lx_call7(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t,
++    uintptr_t, uintptr_t, uintptr_t, uintptr_t);
++uintptr_t lx_call8(lx_handle_sym_t, uintptr_t, uintptr_t, uintptr_t,
++    uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
++
++/*
++ * Flag indicating if this process is destined to become a thunking
++ * server process.
++ */
++static int lxt_server_processes = 0;
++
++/*
++ * Linux function call defines and handles.
++ */
++static lx_handle_dlsym_t	lxh_init = NULL;
++
++#define	LXTH_GETHOSTBYNAME_R	0
++#define	LXTH_GETHOSTBYADDR_R	1
++#define	LXTH_GETSERVBYNAME_R	2
++#define	LXTH_GETSERVBYPORT_R	3
++#define	LXTH_OPENLOG		4
++#define	LXTH_SYSLOG		5
++#define	LXTH_CLOSELOG		6
++#define	LXTH_PROGNAME		7
++
++static struct lxt_handles {
++	int		lxth_index;
++	char		*lxth_name;
++	lx_handle_sym_t	lxth_handle;
++} lxt_handles[] = {
++	{ LXTH_GETHOSTBYNAME_R,	"gethostbyname_r",	NULL },
++	{ LXTH_GETHOSTBYADDR_R,	"gethostbyaddr_r",	NULL },
++	{ LXTH_GETSERVBYNAME_R,	"getservbyname_r",	NULL },
++	{ LXTH_GETSERVBYPORT_R,	"getservbyport_r",	NULL },
++	{ LXTH_OPENLOG,		"openlog",		NULL },
++	{ LXTH_SYSLOG,		"syslog",		NULL },
++	{ LXTH_CLOSELOG,	"closelog",		NULL },
++	{ LXTH_PROGNAME,	"__progname",		NULL },
++	{ -1,			NULL, 			NULL },
++};
++
++/*
++ * Door server operations dispatch functions and table.
++ *
++ * When the doors server get's a request for a particlar operation
++ * this dispatch table controls what function will be invoked to
++ * service the request.  The function is invoked via Linux thread
++ * so that it can call into native Linux code if necessary.
++ */
++static void lxt_server_gethost(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++static void lxt_server_getserv(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++static void lxt_server_openlog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++static void lxt_server_syslog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++static void lxt_server_closelog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++
++typedef void (*lxt_op_func_t)(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size);
++
++static struct lxt_operations {
++	int		lxto_index;
++	lxt_op_func_t	lxto_fp;
++} lxt_operations[] = {
++	{ LXT_SERVER_OP_PING,		NULL },
++	{ LXT_SERVER_OP_NAME2HOST,	lxt_server_gethost },
++	{ LXT_SERVER_OP_ADDR2HOST,	lxt_server_gethost },
++	{ LXT_SERVER_OP_NAME2SERV,	lxt_server_getserv },
++	{ LXT_SERVER_OP_PORT2SERV,	lxt_server_getserv },
++	{ LXT_SERVER_OP_OPENLOG,	lxt_server_openlog },
++	{ LXT_SERVER_OP_SYSLOG,		lxt_server_syslog },
++	{ LXT_SERVER_OP_CLOSELOG,	lxt_server_closelog },
++};
++
++/*
++ * Structures for passing off requests from doors threads (which are
++ * solaris threads) to a Linux thread that that can handle them.
++ */
++typedef struct lxt_req {
++	lxt_server_arg_t	*lxtr_request;
++	size_t			lxtr_request_size;
++	char			*lxtr_result;
++	size_t			lxtr_result_size;
++	int			lxtr_complete;
++	cond_t			lxtr_complete_cv;
++} lxt_req_t;
++
++static mutex_t		lxt_req_lock = DEFAULTMUTEX;
++static cond_t		lxt_req_cv = DEFAULTCV;
++static lxt_req_t	*lxt_req_ptr = NULL;
++
++static mutex_t		lxt_pid_lock = DEFAULTMUTEX;
++static pid_t		lxt_pid = NULL;
++
++/*
++ * Interfaces used to call from lx_brand.so into Linux code.
++ */
++typedef struct lookup_cb_arg {
++	struct ps_prochandle	*lca_ph;
++	caddr_t			lca_ptr;
++} lookup_cb_arg_t;
++
++static int
++/*ARGSUSED*/
++lookup_cb(void *data, const prmap_t *pmp, const char *object)
++{
++	lookup_cb_arg_t		*lcap = (lookup_cb_arg_t *)data;
++	prsyminfo_t		si;
++	GElf_Sym		sym;
++
++	if (Pxlookup_by_name(lcap->lca_ph,
++	    LM_ID_BASE, object, "dlsym", &sym, &si) != 0)
++		return (0);
++
++	if (sym.st_shndx == SHN_UNDEF)
++		return (0);
++
++	/*
++	 * XXX: we should be more paranoid and verify that the symbol
++	 * we just looked up is libdl.so.2`dlsym
++	 */
++	lcap->lca_ptr = (caddr_t)(uintptr_t)sym.st_value;
++	return (1);
++}
++
++lx_handle_dlsym_t
++lx_call_init(void)
++{
++	struct ps_prochandle	*ph;
++	lookup_cb_arg_t		lca;
++	extern int 		__libc_threaded;
++	int			err;
++
++	lx_debug("lx_call_init(): looking up Linux dlsym");
++
++	/*
++	 * The handle is really the address of the Linux "dlsym" function.
++	 * Once we have this address we can call into the Linux "dlsym"
++	 * function to lookup other functions.  It's the initial lookup
++	 * of "dlsym" that's difficult.  To do this we'll leverage the
++	 * brand support that we added to librtld_db.  We're going
++	 * to fire up a seperate native solaris process that will
++	 * attach to us via libproc/librtld_db and lookup the symbol
++	 * for us.
++	 */
++
++	/* Make sure we're single threaded. */
++	if (__libc_threaded) {
++		lx_debug("lx_call_init() fail: "
++		    "process must be single threaded");
++		return (NULL);
++	}
++
++	/* Tell libproc.so where the real procfs is mounted. */
++	Pset_procfs_path("/native/proc");
++
++	/* Tell librtld_db.so where the real /native is */
++	(void) rd_ctl(RD_CTL_SET_HELPPATH, "/native");
++
++	/* Grab ourselves but don't stop ourselves. */
++	if ((ph = Pgrab(getpid(),
++	    PGRAB_FORCE | PGRAB_RDONLY | PGRAB_NOSTOP, &err)) == NULL) {
++		lx_debug("lx_call_init() fail: Pgrab failed: %s",
++		    Pgrab_error(err));
++		return (NULL);
++	}
++
++	lca.lca_ph = ph;
++	if (Pobject_iter(ph, lookup_cb, &lca) == -1) {
++		lx_debug("lx_call_init() fail: couldn't find Linux dlsym");
++		return (NULL);
++	}
++
++	lx_debug("lx_call_init(): Linux dlsym = 0x%p", lca.lca_ptr);
++	return ((lx_handle_dlsym_t)lca.lca_ptr);
++}
++
++#define	LX_RTLD_DEFAULT		((void *)0)
++#define	LX_RTLD_NEXT		((void *) -1l)
++
++lx_handle_sym_t
++lx_call_dlsym(lx_handle_dlsym_t lxh_dlsym, const char *str)
++{
++	lx_handle_sym_t result;
++	lx_debug("lx_call_dlsym: calling Linux dlsym for: %s", str);
++	result = (lx_handle_sym_t)lx_call2((lx_handle_sym_t)lxh_dlsym,
++	    (uintptr_t)LX_RTLD_DEFAULT, (uintptr_t)str);
++	lx_debug("lx_call_dlsym: Linux sym: \"%s\" = 0x%p", str, result);
++	return (result);
++}
++
++static uintptr_t
++/*ARGSUSED*/
++lx_call(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2,
++    uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6, uintptr_t p7,
++    uintptr_t p8)
++{
++	typedef uintptr_t	(*fp8_t)(uintptr_t, uintptr_t, uintptr_t,
++	    uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
++	lx_regs_t		*rp;
++	uintptr_t		ret;
++	fp8_t			lx_funcp = (fp8_t)lx_ch;
++	long			cur_gs;
++
++	rp = lx_syscall_regs();
++
++	lx_debug("lx_call: calling to Linux code at 0x%p", lx_ch);
++	lx_debug("lx_call: loading Linux gs, rp = 0x%p, gs = 0x%p",
++	    rp, rp->lxr_gs);
++
++	lx_swap_gs(rp->lxr_gs, &cur_gs);
++	ret = lx_funcp(p1, p2, p3, p4, p5, p6, p7, p8);
++	lx_swap_gs(cur_gs, &rp->lxr_gs);
++
++	lx_debug("lx_call: returned from Linux code at 0x%p (%p)", lx_ch, ret);
++	lx_debug("lx_call: restored solaris gs 0x%p", cur_gs);
++	return (ret);
++}
++
++uintptr_t
++lx_call0(lx_handle_sym_t lx_ch)
++{
++	return (lx_call(lx_ch, 0, 0, 0, 0, 0, 0, 0, 0));
++}
++
++uintptr_t
++lx_call1(lx_handle_sym_t lx_ch, uintptr_t p1)
++{
++	return (lx_call(lx_ch, p1, 0, 0, 0, 0, 0, 0, 0));
++}
++
++uintptr_t
++lx_call2(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2)
++{
++	return (lx_call(lx_ch, p1, p2, 0, 0, 0, 0, 0, 0));
++}
++
++uintptr_t
++lx_call3(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	return (lx_call(lx_ch, p1, p2, p3, 0, 0, 0, 0, 0));
++}
++
++uintptr_t
++lx_call4(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4)
++{
++	return (lx_call(lx_ch, p1, p2, p3, p4, 0, 0, 0, 0));
++}
++
++uintptr_t
++lx_call5(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4, uintptr_t p5)
++{
++	return (lx_call(lx_ch, p1, p2, p3, p4, p5, 0, 0, 0));
++}
++
++uintptr_t
++lx_call6(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4, uintptr_t p5, uintptr_t p6)
++{
++	return (lx_call(lx_ch, p1, p2, p3, p4, p5, p6, 0, 0));
++}
++
++uintptr_t
++lx_call7(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4, uintptr_t p5, uintptr_t p6, uintptr_t p7)
++{
++	return (lx_call(lx_ch, p1, p2, p3, p4, p5, p6, p7, 0));
++}
++
++uintptr_t
++lx_call8(lx_handle_sym_t lx_ch, uintptr_t p1, uintptr_t p2, uintptr_t p3,
++    uintptr_t p4, uintptr_t p5, uintptr_t p6, uintptr_t p7, uintptr_t p8)
++{
++	return (lx_call(lx_ch, p1, p2, p3, p4, p5, p6, p7, p8));
++}
++
++/*
++ * Linux Thunking Interfaces - Server Side
++ */
++static int
++lxt_gethost_arg_check(lxt_gethost_arg_t *x, int x_size)
++{
++	if (x_size != sizeof (*x) + x->lxt_gh_buf_len - 1)
++		return (-1);
++
++	if ((x->lxt_gh_token_len < 0) || (x->lxt_gh_buf_len < 0))
++		return (-1);
++
++	/* Token and buf should use up all the storage. */
++	if ((x->lxt_gh_token_len + x->lxt_gh_buf_len) != x->lxt_gh_storage_len)
++		return (-1);
++
++	return (0);
++}
++
++static void
++lxt_server_gethost(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size)
++{
++	lxt_gethost_arg_t	*data;
++	struct hostent		*result, *rv;
++	int			token_len, buf_len, type, data_size, i;
++	char			*token, *buf;
++	int			h_errnop;
++
++	assert((request->lxt_sa_op == LXT_SERVER_OP_NAME2HOST) ||
++	    (request->lxt_sa_op == LXT_SERVER_OP_ADDR2HOST));
++
++	/*LINTED*/
++	data = (lxt_gethost_arg_t *)&request->lxt_sa_data[0];
++	data_size = request_size - sizeof (*request) - 1;
++
++	if (!lxt_gethost_arg_check(data, data_size)) {
++		lx_debug("lxt_server_gethost: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++
++	/* Unpack the arguments. */
++	type = data->lxt_gh_type;
++	token = &data->lxt_gh_storage[0];
++	token_len = data->lxt_gh_token_len;
++	result = &data->lxt_gh_result;
++	buf = &data->lxt_gh_storage[data->lxt_gh_token_len];
++	buf_len = data->lxt_gh_buf_len - data->lxt_gh_token_len;
++
++	if (request->lxt_sa_op == LXT_SERVER_OP_NAME2HOST) {
++		(void) lx_call6(lxt_handles[LXTH_GETHOSTBYNAME_R].lxth_handle,
++		    (uintptr_t)token, (uintptr_t)result,
++		    (uintptr_t)buf, buf_len, (uintptr_t)&rv,
++		    (uintptr_t)&h_errnop);
++	} else {
++		(void) lx_call8(lxt_handles[LXTH_GETHOSTBYADDR_R].lxth_handle,
++		    (uintptr_t)token, token_len, type, (uintptr_t)result,
++		    (uintptr_t)buf, buf_len, (uintptr_t)&rv,
++		    (uintptr_t)&h_errnop);
++	}
++
++	if (rv == NULL) {
++		/* the lookup failed */
++		request->lxt_sa_success = 0;
++		request->lxt_sa_errno = errno;
++		data->lxt_gh_h_errno = h_errnop;
++		*door_result = (char *)request;
++		*door_result_size = request_size;
++		return;
++	}
++	request->lxt_sa_success = 1;
++	request->lxt_sa_errno = 0;
++	data->lxt_gh_h_errno = 0;
++
++	/*
++	 * The result structure that we would normally return contains a
++	 * bunch of pointers, but those pointers are useless to our caller
++	 * since they are in a different address space.  So before returning
++	 * we'll convert all the result pointers into offsets.  The caller
++	 * can then map the offsets back into pointers.
++	 */
++	for (i = 0; result->h_aliases[i] != NULL; i++) {
++		result->h_aliases[i] =
++		    LXT_PTR_TO_OFFSET(result->h_aliases[i], buf);
++	}
++	for (i = 0; result->h_addr_list[i] != NULL; i++) {
++		result->h_addr_list[i] =
++		    LXT_PTR_TO_OFFSET(result->h_addr_list[i], buf);
++	}
++	result->h_name = LXT_PTR_TO_OFFSET(result->h_name, buf);
++	result->h_aliases = LXT_PTR_TO_OFFSET(result->h_aliases, buf);
++	result->h_addr_list = LXT_PTR_TO_OFFSET(result->h_addr_list, buf);
++
++	*door_result = (char *)request;
++	*door_result_size = request_size;
++}
++
++static int
++lxt_getserv_arg_check(lxt_getserv_arg_t *x, int x_size)
++{
++	if (x_size != sizeof (*x) + x->lxt_gs_buf_len - 1)
++		return (-1);
++
++	if ((x->lxt_gs_token_len < 0) || (x->lxt_gs_buf_len < 0))
++		return (-1);
++
++	/* Token and buf should use up all the storage. */
++	if ((x->lxt_gs_token_len + x->lxt_gs_buf_len) != x->lxt_gs_storage_len)
++		return (-1);
++
++	return (0);
++}
++
++static void
++lxt_server_getserv(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size)
++{
++	lxt_getserv_arg_t	*data;
++	struct servent		*result, *rv;
++	int			token_len, buf_len, data_size, i, port;
++	char			*token, *buf, *proto = NULL;
++
++	assert((request->lxt_sa_op == LXT_SERVER_OP_NAME2SERV) ||
++	    (request->lxt_sa_op == LXT_SERVER_OP_PORT2SERV));
++
++	/*LINTED*/
++	data = (lxt_getserv_arg_t *)&request->lxt_sa_data[0];
++	data_size = request_size - sizeof (*request) - 1;
++
++	if (!lxt_getserv_arg_check(data, data_size)) {
++		lx_debug("lxt_server_getserv: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++
++	/* Unpack the arguments. */
++	token = &data->lxt_gs_storage[0];
++	token_len = data->lxt_gs_token_len;
++	result = &data->lxt_gs_result;
++	buf = &data->lxt_gs_storage[data->lxt_gs_token_len];
++	buf_len = data->lxt_gs_buf_len - data->lxt_gs_token_len;
++	if (strlen(data->lxt_gs_proto) > 0)
++		proto = data->lxt_gs_proto;
++
++	/* Do more sanity checks */
++	if ((request->lxt_sa_op == LXT_SERVER_OP_PORT2SERV) &&
++	    (token_len != sizeof (int))) {
++		lx_debug("lxt_server_getserv: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++
++	if (request->lxt_sa_op == LXT_SERVER_OP_NAME2SERV) {
++		(void) lx_call6(lxt_handles[LXTH_GETSERVBYNAME_R].lxth_handle,
++		    (uintptr_t)token, (uintptr_t)proto, (uintptr_t)result,
++		    (uintptr_t)buf, buf_len, (uintptr_t)&rv);
++	} else {
++		bcopy(token, &port, sizeof (int));
++		(void) lx_call6(lxt_handles[LXTH_GETSERVBYPORT_R].lxth_handle,
++		    port, (uintptr_t)proto, (uintptr_t)result,
++		    (uintptr_t)buf, buf_len, (uintptr_t)&rv);
++	}
++
++	if (rv == NULL) {
++		/* the lookup failed */
++		request->lxt_sa_success = 0;
++		request->lxt_sa_errno = errno;
++		*door_result = (char *)request;
++		*door_result_size = request_size;
++		return;
++	}
++	request->lxt_sa_success = 1;
++	request->lxt_sa_errno = 0;
++
++	/*
++	 * The result structure that we would normally return contains a
++	 * bunch of pointers, but those pointers are useless to our caller
++	 * since they are in a different address space.  So before returning
++	 * we'll convert all the result pointers into offsets.  The caller
++	 * can then map the offsets back into pointers.
++	 */
++	for (i = 0; result->s_aliases[i] != NULL; i++) {
++		result->s_aliases[i] =
++		    LXT_PTR_TO_OFFSET(result->s_aliases[i], buf);
++	}
++	result->s_proto = LXT_PTR_TO_OFFSET(result->s_proto, buf);
++	result->s_aliases = LXT_PTR_TO_OFFSET(result->s_aliases, buf);
++	result->s_name = LXT_PTR_TO_OFFSET(result->s_name, buf);
++
++	*door_result = (char *)request;
++	*door_result_size = request_size;
++}
++
++static void
++/*ARGSUSED*/
++lxt_server_openlog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size)
++{
++	lxt_openlog_arg_t	*data;
++	int			data_size;
++	static char		ident[128];
++
++	assert(request->lxt_sa_op == LXT_SERVER_OP_OPENLOG);
++
++	/*LINTED*/
++	data = (lxt_openlog_arg_t *)&request->lxt_sa_data[0];
++	data_size = request_size - sizeof (*request);
++
++	if (data_size != sizeof (*data)) {
++		lx_debug("lxt_server_openlog: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++
++	/*
++	 * Linux expects that the ident pointer passed to openlog()
++	 * points to a static string that won't go away.  Linux
++	 * saves the pointer and references with syslog() is called.
++	 * Hence we'll make a local copy of the ident string here.
++	 */
++	(void) mutex_lock(&lxt_pid_lock);
++	(void) strlcpy(ident, data->lxt_ol_ident, sizeof (ident));
++	(void) mutex_unlock(&lxt_pid_lock);
++
++	/* Call Linx openlog(). */
++	(void) lx_call3(lxt_handles[LXTH_OPENLOG].lxth_handle,
++	    (uintptr_t)ident, data->lxt_ol_logopt, data->lxt_ol_facility);
++
++	request->lxt_sa_success = 1;
++	request->lxt_sa_errno = 0;
++	*door_result = (char *)request;
++	*door_result_size = request_size;
++}
++
++static void
++/*ARGSUSED*/
++lxt_server_syslog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size)
++{
++	lxt_syslog_arg_t	*data;
++	int			data_size;
++	char			*progname_ptr_new;
++	char			*progname_ptr_old;
++
++	assert(request->lxt_sa_op == LXT_SERVER_OP_SYSLOG);
++
++	/*LINTED*/
++	data = (lxt_syslog_arg_t *)&request->lxt_sa_data[0];
++	data_size = request_size - sizeof (*request);
++
++	if (data_size != sizeof (*data)) {
++		lx_debug("lxt_server_openlog: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++	progname_ptr_new = data->lxt_sl_progname;
++
++	(void) mutex_lock(&lxt_pid_lock);
++
++	/*
++	 * Ensure the message has the correct pid.
++	 * We do this by telling our getpid() system call to return a
++	 * different value.
++	 */
++	lxt_pid = data->lxt_sl_pid;
++
++	/*
++	 * Ensure the message has the correct program name.
++	 * Normally instead of a program name an "ident" string is
++	 * used, this is the string passed to openlog().  But if
++	 * openlog() wasn't called before syslog() then Linux
++	 * syslog() will attempt to use the program name as
++	 * the ident string, and the program name is determined
++	 * by looking at the __progname variable.  So we'll just
++	 * update the Linux __progname variable while we do the
++	 * call.
++	 */
++	(void) uucopy(lxt_handles[LXTH_PROGNAME].lxth_handle,
++	    &progname_ptr_old, sizeof (char *));
++	(void) uucopy(&progname_ptr_new,
++	    lxt_handles[LXTH_PROGNAME].lxth_handle, sizeof (char *));
++
++	/* Call Linux syslog(). */
++	(void) lx_call2(lxt_handles[LXTH_SYSLOG].lxth_handle,
++	    data->lxt_sl_priority, (uintptr_t)data->lxt_sl_message);
++
++	/* Restore pid and program name. */
++	(void) uucopy(&progname_ptr_old,
++	    lxt_handles[LXTH_PROGNAME].lxth_handle, sizeof (char *));
++	lxt_pid = NULL;
++
++	(void) mutex_unlock(&lxt_pid_lock);
++
++	request->lxt_sa_success = 1;
++	request->lxt_sa_errno = 0;
++	*door_result = (char *)request;
++	*door_result_size = request_size;
++}
++
++static void
++/*ARGSUSED*/
++lxt_server_closelog(lxt_server_arg_t *request, size_t request_size,
++    char **door_result, size_t *door_result_size)
++{
++	int			data_size;
++
++	assert(request->lxt_sa_op == LXT_SERVER_OP_CLOSELOG);
++
++	data_size = request_size - sizeof (*request);
++	if (data_size != 0) {
++		lx_debug("lxt_server_closelog: invalid request");
++		*door_result = NULL;
++		*door_result_size = 0;
++		return;
++	}
++
++	/* Call Linux closelog(). */
++	(void) lx_call0(lxt_handles[LXTH_CLOSELOG].lxth_handle);
++
++	request->lxt_sa_success = 1;
++	request->lxt_sa_errno = 0;
++	*door_result = (char *)request;
++	*door_result_size = request_size;
++}
++
++static void
++/*ARGSUSED*/
++lxt_server(void *cookie, char *argp, size_t request_size,
++    door_desc_t *dp, uint_t n_desc)
++{
++	/*LINTED*/
++	lxt_server_arg_t	*request = (lxt_server_arg_t *)argp;
++	lxt_req_t		lxt_req;
++	char			*door_path = cookie;
++
++	/* Check if there's no callers left */
++	if (argp == DOOR_UNREF_DATA) {
++		(void) fdetach(door_path);
++		(void) unlink(door_path);
++		lx_debug("lxt_thunk_server: no clients, exiting");
++		exit(0);
++	}
++
++	/* Sanity check the incomming request. */
++	if (request_size < sizeof (*request)) {
++		/* the lookup failed */
++		lx_debug("lxt_thunk_server: invalid request size");
++		(void) door_return(NULL, 0, NULL, 0);
++		return;
++	}
++
++	if ((request->lxt_sa_op < LXT_SERVER_OP_MIN) ||
++	    (request->lxt_sa_op > LXT_SERVER_OP_MAX)) {
++		lx_debug("lxt_thunk_server: invalid request op");
++		(void) door_return(NULL, 0, NULL, 0);
++		return;
++	}
++
++	/* Handle ping requests immediatly, return here. */
++	if (request->lxt_sa_op == LXT_SERVER_OP_PING) {
++		lx_debug("lxt_thunk_server: handling ping request");
++		request->lxt_sa_success = 1;
++		(void) door_return((char *)request, request_size, NULL, 0);
++		return;
++	}
++
++	lx_debug("lxt_thunk_server: hand off request to Linux thread, "
++	    "request = 0x%p", request);
++
++	/* Pack the request up so we can pass it to a Linux thread. */
++	lxt_req.lxtr_request = request;
++	lxt_req.lxtr_request_size = request_size;
++	lxt_req.lxtr_result = NULL;
++	lxt_req.lxtr_result_size = 0;
++	lxt_req.lxtr_complete = 0;
++	(void) cond_init(&lxt_req.lxtr_complete_cv, USYNC_THREAD, NULL);
++
++	/* Pass the request onto a Linux thread. */
++	(void) mutex_lock(&lxt_req_lock);
++	while (lxt_req_ptr != NULL)
++		(void) cond_wait(&lxt_req_cv, &lxt_req_lock);
++	lxt_req_ptr = &lxt_req;
++	(void) cond_broadcast(&lxt_req_cv);
++
++	/* Wait for the request to be completed. */
++	while (lxt_req.lxtr_complete == 0)
++		(void) cond_wait(&lxt_req.lxtr_complete_cv, &lxt_req_lock);
++	assert(lxt_req_ptr != &lxt_req);
++	(void) mutex_unlock(&lxt_req_lock);
++
++	lx_debug("lxt_thunk_server: hand off request completed, "
++	    "request = 0x%p", request);
++
++	/*
++	 * If door_return() is successfull it never returns, so if we made
++	 * it here there was some kind of error, but there's nothing we can
++	 * really do about it.
++	 */
++	(void) door_return(
++	    lxt_req.lxtr_result, lxt_req.lxtr_result_size, NULL, 0);
++}
++
++static void
++lxt_server_loop(void)
++{
++	lxt_req_t		*lxt_req;
++	lxt_server_arg_t	*request;
++	size_t			request_size;
++	char			*door_result;
++	size_t			door_result_size;
++
++	for (;;) {
++		/* Wait for a request from a doors server thread. */
++		(void) mutex_lock(&lxt_req_lock);
++		while (lxt_req_ptr == NULL)
++			(void) cond_wait(&lxt_req_cv, &lxt_req_lock);
++
++		/* We got a request, get a local pointer to it. */
++		lxt_req = lxt_req_ptr;
++		lxt_req_ptr = NULL;
++		(void) cond_broadcast(&lxt_req_cv);
++		(void) mutex_unlock(&lxt_req_lock);
++
++		/* Get a pointer to the request. */
++		request = lxt_req->lxtr_request;
++		request_size = lxt_req->lxtr_request_size;
++
++		lx_debug("lxt_server_loop: Linux thread request recieved, "
++		    "request = %p", request);
++
++		/* Dispatch the request. */
++		assert((request->lxt_sa_op > LXT_SERVER_OP_PING) ||
++		    (request->lxt_sa_op < LXT_SERVER_OP_MAX));
++		lxt_operations[request->lxt_sa_op].lxto_fp(
++		    request, request_size, &door_result, &door_result_size);
++
++		lx_debug("lxt_server_loop: Linux thread request completed, "
++		    "request = %p", request);
++
++		(void) mutex_lock(&lxt_req_lock);
++
++		/* Set the result pointers for the calling door thread. */
++		lxt_req->lxtr_result = door_result;
++		lxt_req->lxtr_result_size = door_result_size;
++
++		/* Let the door thread know we're done. */
++		lxt_req->lxtr_complete = 1;
++		(void) cond_signal(&lxt_req->lxtr_complete_cv);
++
++		(void) mutex_unlock(&lxt_req_lock);
++	}
++	/*NOTREACHED*/
++}
++
++static void
++lxt_server_enter(int fifo1_wr, int fifo2_rd)
++{
++	struct stat	stat;
++	char		door_path[MAXPATHLEN];
++	int		i, dfd, junk = 0;
++
++	/*
++	 * Do some sanity checks.  Make sure we've got the fifos
++	 * we need passed to us on the correct file descriptors.
++	 */
++	if ((fstat(fifo1_wr, &stat) != 0) ||
++	    ((stat.st_mode & S_IFMT) != S_IFIFO) ||
++	    (fstat(fifo2_rd, &stat) != 0) ||
++	    ((stat.st_mode & S_IFMT) != S_IFIFO)) {
++		lx_err("lx_thunk server aborting, can't contact parent");
++		exit(-1);
++	}
++
++	/*
++	 * Get the initial Linux call handle so we can invoke other
++	 * Linux calls.
++	 */
++	lxh_init = lx_call_init();
++	if (lxh_init == NULL) {
++		lx_err("lx_thunk server aborting, failed Linux call init");
++		exit(-1);
++	}
++
++	/* Now lookup other Linux symbols we'll need access to. */
++	for (i = 0; lxt_handles[i].lxth_name != NULL; i++) {
++		assert(lxt_handles[i].lxth_index == i);
++		if ((lxt_handles[i].lxth_handle = lx_call_dlsym(lxh_init,
++		    lxt_handles[i].lxth_name)) == NULL) {
++			lx_err("lx_thunk server aborting, "
++			    "failed Linux symbol lookup: %s",
++			    lxt_handles[i].lxth_name);
++			exit(-1);
++		}
++	}
++
++	/* get the path to the door server */
++	if (read(fifo2_rd, door_path, sizeof (door_path)) < 0) {
++		lx_err("lxt_server_enter: failed to get door path");
++		exit(-1);
++	}
++	(void) close(fifo2_rd);
++
++	/* Create the door server. */
++	if ((dfd = door_create(lxt_server, door_path,
++	    DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
++		lx_err("lxt_server_enter: door_create() failed");
++		exit(-1);
++	}
++
++	/* Attach the door to a file system path. */
++	(void) fdetach(door_path);
++	if (fattach(dfd, door_path) < 0) {
++		lx_err("lxt_server_enter: fattach() failed");
++		exit(-1);
++	}
++
++	/* The door server is ready, signal this via a fifo write */
++	(void) write(fifo1_wr, &junk, 1);
++	(void) close(fifo1_wr);
++
++	lx_debug("lxt_server_enter: doors server initialized");
++	lxt_server_loop();
++	/*NOTREACHED*/
++}
++
++void
++lxt_server_exec_check(void)
++{
++	if (lxt_server_processes == 0)
++		return;
++
++	/*
++	 * We're a thunk server process, so we take over control of
++	 * the current Linux process here.
++	 */
++	lx_debug("lx_thunk server initalization starting");
++	lxt_server_enter(LXT_SERVER_FIFO_WR_FD, LXT_SERVER_FIFO_RD_FD);
++	/*NOTREACHED*/
++}
++
++void
++lxt_server_init(int argc, char *argv[])
++{
++	/*
++	 * The thunk server process is a shell script named LXT_SERVER_BINARY.
++	 * It is executed without any parameters.  Since it's a shell script
++	 * the arguments passed to the shell's main entry point are:
++	 *	1) the name of the shell
++	 *	2) the name of the script to execute
++	 *
++	 * So to check if we're the thunk server process we first check
++	 * for the expected number of arduments and then we'll look at
++	 * the second parameter to see if it's LXT_SERVER_BINARY.
++	 */
++	if ((argc != 2) ||
++	    (strcmp(argv[1], LXT_SERVER_BINARY) != 0))
++		return;
++
++	lxt_server_processes = 1;
++	lx_debug("lx_thunk server detected, delaying initalization");
++}
++
++int
++lxt_server_pid(int *pid)
++{
++	if (lxt_server_processes == 0)
++		return (0);
++	*pid = lxt_pid;
++	return (1);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/mapfile
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,47 @@
++#
++# 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.
++#
++
++#
++# MAPFILE HEADER START
++#
++# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
++# Object versioning must comply with the rules detailed in
++#
++#	usr/src/lib/README.mapfiles
++#
++# You should not be making modifications here until you've read the most current
++# copy of that file. If you need help, contact a gatekeeper for guidance.
++#
++# MAPFILE HEADER END
++#
++
++#
++# Scope everything local -- our .init section is our only public interface.
++#
++{
++	local:
++		*;
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/mapfile-vers
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,47 @@
++#
++# 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.
++#
++
++#
++# MAPFILE HEADER START
++#
++# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
++# Object versioning must comply with the rules detailed in
++#
++#	usr/src/lib/README.mapfiles
++#
++# You should not be making modifications here until you've read the most current
++# copy of that file. If you need help, contact a gatekeeper for guidance.
++#
++# MAPFILE HEADER END
++#
++
++#
++# Scope everything local -- our .init section is our only public interface.
++#
++{
++	local:
++		*;
++};
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/mem.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/mem.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,210 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <errno.h>
++#include <unistd.h>
++#include <sys/mman.h>
++#include <sys/param.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++
++/*
++ * There are two forms of mmap, mmap() and mmap2().  The only difference is that
++ * the final argument to mmap2() specifies the number of pages, not bytes.
++ * Linux has a number of additional flags, but they are all deprecated.  We also
++ * ignore the MAP_GROWSDOWN flag, which has no equivalent on Solaris.
++ *
++ * The Linux mmap() returns ENOMEM in some cases where Solaris returns
++ * EOVERFLOW, so we translate the errno as necessary.
++ */
++
++int pagesize;	/* needed for mmap2() */
++
++#define	LX_MAP_ANONYMOUS	0x00020
++#define	LX_MAP_NORESERVE	0x04000
++
++static int
++ltos_mmap_flags(int flags)
++{
++	int new_flags;
++
++	new_flags = flags & (MAP_TYPE | MAP_FIXED);
++	if (flags & LX_MAP_ANONYMOUS)
++		new_flags |= MAP_ANONYMOUS;
++	if (flags & LX_MAP_NORESERVE)
++		new_flags |= MAP_NORESERVE;
++
++	return (new_flags);
++}
++
++static int
++mmap_common(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5, off64_t p6)
++{
++	void *addr = (void *)p1;
++	size_t len = p2;
++	int prot = p3;
++	int flags = p4;
++	int fd = p5;
++	off64_t off = p6;
++	void *ret;
++
++	if (lx_debug_enabled != 0) {
++		char *path, path_buf[MAXPATHLEN];
++
++		path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
++		if (path == NULL)
++			path = "?";
++
++		lx_debug("\tmmap_common(): fd = %d - %s", fd, path);
++	}
++
++	/*
++	 * Under Linux, the file descriptor is ignored when mapping zfod
++	 * anonymous memory,  On Solaris, we want the fd set to -1 for the
++	 * same functionality.
++	 */
++	if (flags & LX_MAP_ANONYMOUS)
++		fd = -1;
++
++	/*
++	 * This is totally insane. The NOTES section in the linux mmap(2) man
++	 * page claims that on some architectures, read protection may
++	 * automatically include exec protection. It has been observed on a
++	 * native linux system that the /proc/<pid>/maps file does indeed
++	 * show that segments mmap'd from userland (such as libraries mapped in
++	 * by the dynamic linker) all have exec the permission set, even for
++	 * data segments.
++	 */
++	if (prot & PROT_READ)
++		prot |= PROT_EXEC;
++
++	ret = mmap64(addr, len, prot, ltos_mmap_flags(flags), fd, off);
++
++	if (ret == MAP_FAILED)
++		return (errno == EOVERFLOW ? -ENOMEM : -errno);
++	else
++		return ((int)ret);
++}
++
++int
++lx_mmap(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5, uintptr_t p6)
++{
++	return (mmap_common(p1, p2, p3, p4, p5, (off64_t)p6));
++}
++
++int
++lx_mmap2(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5, uintptr_t p6)
++{
++	if (pagesize == 0)
++		pagesize = sysconf(_SC_PAGESIZE);
++
++	return (mmap_common(p1, p2, p3, p4, p5, (off64_t)p6 * pagesize));
++}
++
++
++/*
++ * The locking family of system calls, as well as msync(), are identical.  On
++ * Solaris, they are layered on top of the memcntl syscall, so they cannot be
++ * pass-thru.
++ */
++int
++lx_mlock(uintptr_t addr, uintptr_t len)
++{
++	uintptr_t addr1 = addr & PAGEMASK;
++	uintptr_t len1 = len + (addr & PAGEOFFSET);
++
++	return (mlock((void *)addr1, (size_t)len1) ? -errno : 0);
++}
++
++int
++lx_mlockall(uintptr_t flags)
++{
++	return (mlockall(flags) ? -errno : 0);
++}
++
++int
++lx_munlock(uintptr_t addr, uintptr_t len)
++{
++	uintptr_t addr1 = addr & PAGEMASK;
++	uintptr_t len1 = len + (addr & PAGEOFFSET);
++
++	return (munlock((void *)addr1, (size_t)len1) ? -errno : 0);
++}
++
++int
++lx_munlockall(void)
++{
++	return (munlockall() ? -errno : 0);
++}
++
++int
++lx_msync(uintptr_t addr, uintptr_t len, uintptr_t flags)
++{
++	return (msync((void *)addr, (size_t)len, flags) ? -errno : 0);
++}
++
++/*
++ * Solaris recognizes more flags than Linux, so we don't want to inadvertently
++ * use what would be an invalid flag on Linux.  Linux also allows the length to
++ * be zero, while Solaris does not.
++ */
++int
++lx_madvise(uintptr_t start, uintptr_t len, uintptr_t advice)
++{
++	if (len == 0)
++		return (0);
++
++	switch (advice) {
++	case MADV_NORMAL:
++	case MADV_RANDOM:
++	case MADV_SEQUENTIAL:
++	case MADV_WILLNEED:
++	case MADV_DONTNEED:
++		return (madvise((void *)start, len, advice) ? -errno : 0);
++
++	default:
++		return (-EINVAL);
++	}
++}
++
++/*
++ * mprotect() is identical except that we ignore the Linux flags PROT_GROWSDOWN
++ * and PROT_GROWSUP, which have no equivalent on Solaris.
++ */
++#define	LX_PROT_GROWSDOWN	0x01000000
++#define	LX_PROT_GROWSUP		0x02000000
++
++int
++lx_mprotect(uintptr_t start, uintptr_t len, uintptr_t prot)
++{
++	prot &= ~(LX_PROT_GROWSUP | LX_PROT_GROWSDOWN);
++
++	return (mprotect((void *)start, len, prot) ? -errno : 0);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/misc.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,546 @@
++/*
++ * 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 <assert.h>
++#include <alloca.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <strings.h>
++#include <macros.h>
++#include <sys/brand.h>
++#include <sys/reboot.h>
++#include <sys/stat.h>
++#include <sys/syscall.h>
++#include <sys/sysmacros.h>
++#include <sys/systeminfo.h>
++#include <sys/types.h>
++#include <sys/lx_types.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_stat.h>
++#include <sys/lx_syscall.h>
++#include <sys/lx_thunk_server.h>
++#include <sys/lx_fcntl.h>
++#include <unistd.h>
++#include <libintl.h>
++#include <zone.h>
++
++extern int sethostname(char *, int);
++
++/* ARGUSED */
++int
++lx_rename(uintptr_t p1, uintptr_t p2)
++{
++	int ret;
++
++	ret = rename((const char *)p1, (const char *)p2);
++
++	if (ret < 0) {
++		/*
++		 * If rename(2) failed and we're in install mode, return
++		 * success if the the reason we failed was either because the
++		 * source file didn't actually exist or if it was because we
++		 * tried to rename it to be the name of a device currently in
++		 * use (resulting in an EBUSY.)
++		 *
++		 * To help install along further, if the failure was due
++		 * to an EBUSY, delete the original file so we don't leave
++		 * extra files lying around.
++		 */
++		if (lx_install != 0) {
++			if (errno == ENOENT)
++				return (0);
++
++			if (errno == EBUSY) {
++				(void) unlink((const char *)p1);
++				return (0);
++			}
++		}
++
++		return (-errno);
++	}
++
++	return (0);
++}
++
++int
++lx_renameat(uintptr_t ext1, uintptr_t p1, uintptr_t ext2, uintptr_t p2)
++{
++	int ret;
++	int atfd1 = (int)ext1;
++	int atfd2 = (int)ext2;
++
++	if (atfd1 == LX_AT_FDCWD)
++		atfd1 = AT_FDCWD;
++
++	if (atfd2 == LX_AT_FDCWD)
++		atfd2 = AT_FDCWD;
++
++	ret = renameat(atfd1, (const char *)p1, atfd2, (const char *)p2);
++
++	if (ret < 0) {
++		/* see lx_rename() for why we check lx_install */
++		if (lx_install != 0) {
++			if (errno == ENOENT)
++				return (0);
++
++			if (errno == EBUSY) {
++				(void) unlinkat(ext1, (const char *)p1, 0);
++				return (0);
++			}
++		}
++
++		return (-errno);
++	}
++
++	return (0);
++}
++
++/*ARGSUSED*/
++int
++lx_reboot(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
++{
++	int magic = (int)p1;
++	int magic2 = (int)p2;
++	uint_t flag = (int)p3;
++	int rc;
++
++	if (magic != LINUX_REBOOT_MAGIC1)
++		return (-EINVAL);
++	if (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A &&
++	    magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C &&
++	    magic2 != LINUX_REBOOT_MAGIC2D)
++		return (-EINVAL);
++
++	if (geteuid() != 0)
++		return (-EPERM);
++
++	switch (flag) {
++	case LINUX_REBOOT_CMD_CAD_ON:
++	case LINUX_REBOOT_CMD_CAD_OFF:
++		/* ignored */
++		rc = 0;
++		break;
++	case LINUX_REBOOT_CMD_POWER_OFF:
++	case LINUX_REBOOT_CMD_HALT:
++		rc = reboot(RB_HALT, NULL);
++		break;
++	case LINUX_REBOOT_CMD_RESTART:
++	case LINUX_REBOOT_CMD_RESTART2:
++		/* RESTART2 may need more work */
++		lx_msg(gettext("Restarting system.\n"));
++		rc = reboot(RB_AUTOBOOT, NULL);
++		break;
++	default:
++		return (-EINVAL);
++	}
++
++	return ((rc == -1) ? -errno : rc);
++}
++
++/*
++ * getcwd() - Linux syscall semantics are slightly different; we need to return
++ * the length of the pathname copied (+ 1 for the terminating NULL byte.)
++ */
++int
++lx_getcwd(uintptr_t p1, uintptr_t p2)
++{
++	char *buf;
++	size_t buflen = (size_t)p2;
++	size_t copylen, local_len;
++	size_t len = 0;
++
++	if ((getcwd((char *)p1, (size_t)p2)) == NULL)
++		return (-errno);
++
++	/*
++	 * We need the length of the pathname getcwd() copied but we never want
++	 * to dereference a Linux pointer for any reason.
++	 *
++	 * Thus, to get the string length we will uucopy() up to copylen bytes
++	 * at a time into a local buffer and will walk each chunk looking for
++	 * the string-terminating NULL byte.
++	 *
++	 * We can use strlen() to find the length of the string in the
++	 * local buffer by delimiting the buffer with a NULL byte in the
++	 * last element that will never be overwritten.
++	 */
++	copylen = min(buflen, MAXPATHLEN + 1);
++	buf = SAFE_ALLOCA(copylen + 1);
++	if (buf == NULL)
++		return (-ENOMEM);
++	buf[copylen] = '\0';
++
++	for (;;) {
++		if (uucopy((char *)p1 + len, buf, copylen) != 0)
++			return (-errno);
++
++		local_len = strlen(buf);
++		len += local_len;
++
++		/*
++		 * If the strlen() is less than copylen, we found the
++		 * real end of the string -- not the NULL byte used to
++		 * delimit the end of our buffer.
++		 */
++		if (local_len != copylen)
++			break;
++
++		/* prepare to check the next chunk of the string */
++		buflen -= copylen;
++		copylen = min(buflen, copylen);
++	}
++
++	return (len + 1);
++}
++
++int
++lx_get_kern_version(void)
++{
++	/*
++	 * Since this function is called quite often, and zone_getattr is slow,
++	 * we cache the kernel version in kvers_cache. -1 signifies that no
++	 * value has yet been cached.
++	 */
++	static int kvers_cache = -1;
++	/* dummy variable for use in zone_getattr */
++	int kvers;
++
++	if (kvers_cache != -1)
++		return (kvers_cache);
++	if (zone_getattr(getzoneid(), LX_KERN_VERSION_NUM, &kvers, sizeof (int))
++	    != sizeof (int))
++		return (kvers_cache = LX_KERN_2_4);
++	else
++		return (kvers_cache = kvers);
++}
++
++int
++lx_uname(uintptr_t p1)
++{
++	struct lx_utsname *un = (struct lx_utsname *)p1;
++	char buf[LX_SYS_UTS_LN + 1];
++
++	if (gethostname(un->nodename, sizeof (un->nodename)) == -1)
++		return (-errno);
++
++	(void) strlcpy(un->sysname, LX_UNAME_SYSNAME, LX_SYS_UTS_LN);
++	(void) strlcpy(un->release, lx_release, LX_SYS_UTS_LN);
++	(void) strlcpy(un->version, LX_UNAME_VERSION, LX_SYS_UTS_LN);
++	(void) strlcpy(un->machine, LX_UNAME_MACHINE, LX_SYS_UTS_LN);
++	if ((sysinfo(SI_SRPC_DOMAIN, buf, LX_SYS_UTS_LN) < 0))
++		un->domainname[0] = '\0';
++	else
++		(void) strlcpy(un->domainname, buf, LX_SYS_UTS_LN);
++
++	return (0);
++}
++
++/*
++ * {get,set}groups16() - Handle the conversion between 16-bit Linux gids and
++ * 32-bit Solaris gids.
++ */
++int
++lx_getgroups16(uintptr_t p1, uintptr_t p2)
++{
++	int count = (int)p1;
++	lx_gid16_t *grouplist = (lx_gid16_t *)p2;
++	gid_t *grouplist32;
++	int ret;
++	int i;
++
++	grouplist32 = SAFE_ALLOCA(count * sizeof (gid_t));
++	if (grouplist32 == NULL)
++		return (-ENOMEM);
++	if ((ret = getgroups(count, grouplist32)) < 0)
++		return (-errno);
++
++	for (i = 0; i < ret; i++)
++		grouplist[i] = LX_GID32_TO_GID16(grouplist32[i]);
++
++	return (ret);
++}
++
++int
++lx_setgroups16(uintptr_t p1, uintptr_t p2)
++{
++	int count = (int)p1;
++	lx_gid16_t *grouplist = (lx_gid16_t *)p2;
++	gid_t *grouplist32;
++	int i;
++
++	grouplist32 = SAFE_ALLOCA(count * sizeof (gid_t));
++	if (grouplist32 == NULL)
++		return (-ENOMEM);
++	for (i = 0; i < count; i++)
++		grouplist32[i] = LX_GID16_TO_GID32(grouplist[i]);
++
++	return (setgroups(count, grouplist32) ? -errno : 0);
++}
++
++/*
++ * personality() - Solaris doesn't support Linux personalities, but we have to
++ * emulate enough to show that we support the basic personality.
++ */
++#define	LX_PER_LINUX	0x0
++
++int
++lx_personality(uintptr_t p1)
++{
++	int per = (int)p1;
++
++	switch (per) {
++	case -1:
++		/* Request current personality */
++		return (LX_PER_LINUX);
++	case LX_PER_LINUX:
++		return (0);
++	default:
++		return (-EINVAL);
++	}
++}
++
++/*
++ * mknod() - Since we don't have the SYS_CONFIG privilege within a zone, the
++ * only mode we have to support is S_IFIFO.  We also have to distinguish between
++ * an invalid type and insufficient privileges.
++ */
++#define	LX_S_IFMT	0170000
++#define	LX_S_IFDIR	0040000
++#define	LX_S_IFCHR	0020000
++#define	LX_S_IFBLK	0060000
++#define	LX_S_IFREG	0100000
++#define	LX_S_IFIFO	0010000
++#define	LX_S_IFLNK	0120000
++#define	LX_S_IFSOCK	0140000
++
++/*ARGSUSED*/
++int
++lx_mknod(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	char *path = (char *)p1;
++	lx_dev_t lx_dev = (lx_dev_t)p3;
++	struct sockaddr_un sockaddr;
++	struct stat statbuf;
++	mode_t mode, type;
++	dev_t dev;
++	int fd;
++
++	type = ((mode_t)p2 & LX_S_IFMT);
++	mode = ((mode_t)p2 & 07777);
++
++	switch (type) {
++	case 0:
++	case LX_S_IFREG:
++		/* create a regular file */
++		if (stat(path, &statbuf) == 0)
++			return (-EEXIST);
++
++		if (errno != ENOENT)
++			return (-errno);
++
++		if ((fd = creat(path, mode)) < 0)
++			return (-errno);
++
++		(void) close(fd);
++		return (0);
++
++	case LX_S_IFSOCK:
++		/*
++		 * Create a UNIX domain socket.
++		 *
++		 * Most programmers aren't even aware you can do this.
++		 *
++		 * Note you can also do this via Solaris' mknod(2), but
++		 * Linux allows anyone who can create a UNIX domain
++		 * socket via bind(2) to create one via mknod(2);
++		 * Solaris requires the caller to be privileged.
++		 */
++		if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
++			return (-errno);
++
++		if (stat(path, &statbuf) == 0)
++			return (-EEXIST);
++
++		if (errno != ENOENT)
++			return (-errno);
++
++		if (uucopy(path, &sockaddr.sun_path,
++		    sizeof (sockaddr.sun_path)) < 0)
++			return (-errno);
++
++		/* assure NULL termination of sockaddr.sun_path */
++		sockaddr.sun_path[sizeof (sockaddr.sun_path) - 1] = '\0';
++		sockaddr.sun_family = AF_UNIX;
++
++		if (bind(fd, (struct sockaddr *)&sockaddr,
++		    strlen(sockaddr.sun_path) +
++		    sizeof (sockaddr.sun_family)) < 0)
++			return (-errno);
++
++		(void) close(fd);
++		return (0);
++
++	case LX_S_IFIFO:
++		dev = 0;
++		break;
++
++	case LX_S_IFCHR:
++	case LX_S_IFBLK:
++		/*
++		 * The "dev" RPM package wants to create all possible Linux
++		 * device nodes, so just report its mknod()s as having
++		 * succeeded if we're in install mode.
++		 */
++		if (lx_install != 0) {
++			lx_debug("lx_mknod: install mode spoofed creation of "
++			    "Linux device [%lld, %lld]\n",
++			    LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
++
++			return (0);
++		}
++
++		dev = makedevice(LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
++		break;
++
++	default:
++		return (-EINVAL);
++	}
++
++	return (mknod(path, mode | type, dev) ? -errno : 0);
++}
++
++int
++lx_sethostname(uintptr_t p1, uintptr_t p2)
++{
++	char *name = (char *)p1;
++	int len = (size_t)p2;
++
++	return (sethostname(name, len) ? -errno : 0);
++}
++
++int
++lx_setdomainname(uintptr_t p1, uintptr_t p2)
++{
++	char *name = (char *)p1;
++	int len = (size_t)p2;
++	long rval;
++
++	if (len < 0 || len >= LX_SYS_UTS_LN)
++		return (-EINVAL);
++
++	rval = sysinfo(SI_SET_SRPC_DOMAIN, name, len);
++
++	return ((rval < 0) ? -errno : 0);
++}
++
++int
++lx_getpid(void)
++{
++	int pid;
++
++	/* First call the thunk server hook. */
++	if (lxt_server_pid(&pid) != 0)
++		return (pid);
++
++	pid = syscall(SYS_brand, B_EMULATE_SYSCALL + 20);
++	return ((pid == -1) ? -errno : pid);
++}
++
++int
++lx_execve(uintptr_t p1, uintptr_t p2, uintptr_t p3)
++{
++	char *filename = (char *)p1;
++	char **argv = (char **)p2;
++	char **envp = (char **)p3;
++	char *nullist[] = { NULL };
++	char path[64];
++
++	/* First call the thunk server hook. */
++	lxt_server_exec_check();
++
++	/* Get a copy of the executable we're trying to run */
++	path[0] = '\0';
++	(void) uucopystr(filename, path, sizeof (path));
++
++	/* Check if we're trying to run a native binary */
++	if (strncmp(path, "/native/usr/lib/brand/lx/lx_native",
++	    sizeof (path)) == 0) {
++		/* Skip the first element in the argv array */
++		argv++;
++
++		/*
++		 * The name of the new program to execute was the first
++		 * parameter passed to lx_native.
++		 */
++		if (uucopy(argv, &filename, sizeof (char *)) != 0)
++			return (-errno);
++
++		(void) syscall(SYS_brand, B_EXEC_NATIVE, filename, argv, envp,
++		    NULL, NULL, NULL);
++		return (-errno);
++	}
++
++	if (argv == NULL)
++		argv = nullist;
++
++	/* This is a normal exec call. */
++	(void) execve(filename, argv, envp);
++
++	return (-errno);
++}
++
++int
++lx_setgroups(uintptr_t p1, uintptr_t p2)
++{
++	int ng = (int)p1;
++	gid_t *glist;
++	int i, r;
++
++	lx_debug("\tlx_setgroups(%d, 0x%p", ng, p2);
++
++	if (ng > 0) {
++		if ((glist = (gid_t *)SAFE_ALLOCA(ng * sizeof (gid_t))) == NULL)
++			return (-ENOMEM);
++
++		if (uucopy((void *)p2, glist, ng * sizeof (gid_t)) != 0)
++			return (-errno);
++
++		/*
++		 * Linux doesn't check the validity of the group IDs, but
++		 * Solaris does. Change any invalid group IDs to a known, valid
++		 * value (yuck).
++		 */
++		for (i = 0; i < ng; i++) {
++			if (glist[i] > MAXUID)
++				glist[i] = MAXUID;
++		}
++	}
++
++	r = syscall(SYS_brand, B_EMULATE_SYSCALL + LX_SYS_setgroups32,
++	    ng, glist);
++
++	return ((r == -1) ? -errno : r);
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/module.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/module.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,90 @@
++/*
++ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++/*
++ * We don't support Linux modules, but we have to emulate enough of the system
++ * calls to show that we don't have any modules installed.
++ */
++
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/lx_misc.h>
++
++/*
++ * For query_module(), we provide an empty list of modules, and return ENOENT
++ * on any request for a specific module.
++ */
++#define	LX_QM_MODULES	1
++#define	LX_QM_DEPS	2
++#define	LX_QM_REFS	3
++#define	LX_QM_SYMBOLS	4
++#define	LX_QM_INFO	5
++
++/*ARGSUSED*/
++int
++lx_query_module(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
++    uintptr_t p5)
++{
++	/*
++	 * parameter p1 is the 'name' argument.
++	 */
++	int which = (int)p2;
++	char *buf = (char *)p3;
++	size_t bufsize = (size_t)p4;
++	size_t *ret = (size_t *)p5;
++
++	switch (which) {
++	case 0:
++		/*
++		 * Special case: always return 0
++		 */
++		return (0);
++
++	case LX_QM_MODULES:
++		/*
++		 * Generate an empty list of modules.
++		 */
++		if (bufsize && buf)
++			buf[0] = '\0';
++		if (ret)
++			*ret = 0;
++		return (0);
++
++	case LX_QM_DEPS:
++	case LX_QM_REFS:
++	case LX_QM_SYMBOLS:
++	case LX_QM_INFO:
++		/*
++		 * Any requests for specific module information return ENOENT.
++		 */
++		return (-ENOENT);
++
++	default:
++		return (-EINVAL);
++	}
++}
+diff -r 4a1868d4ae91 usr/src/lib/brand/lx/lx_brand/common/mount.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/usr/src/lib/brand/lx/lx_brand/common/mount.c	Thu Jul 21 20:14:17 2011 -0400
+@@ -0,0 +1,719 @@
++/*
++ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
++ * Use is subject to license terms.
++ */
++
++#pragma ident	"%Z%%M%	%I%	%E% SMI"
++
++#include <alloca.h>
++#include <assert.h>
++#include <ctype.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <signal.h>
++#include <string.h>
++#include <strings.h>
++#include <nfs/mount.h>
++#include <sys/types.h>
++#include <sys/mount.h>
++#include <sys/param.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++
++#include <sys/lx_autofs.h>
++#include <sys/lx_debug.h>
++#include <sys/lx_misc.h>
++#include <sys/lx_mount.h>
++
++/*
++ * support definitions
++ */
++union fh_buffer {
++	struct nfs_fid	fh2;
++	struct nfs_fh3	fh3;
++	char		fh_data[NFS3_FHSIZE + 2];
++};
++
++typedef enum mount_opt_type {
++	MOUNT_OPT_INVALID	= 0,
++	MOUNT_OPT_NORMAL	= 1,	/* option value: none */
++	MOUNT_OPT_UINT		= 2	/* option value: unsigned int */
++} mount_opt_type_t;
++
++typedef struct mount_opt {
++	char			*mo_name;
++	mount_opt_type_t	mo_type;
++} mount_opt_t;
++
++
++/*
++ * Globals
++ */
++mount_opt_t lofs_options[] = {
++	{ NULL, MOUNT_OPT_INVALID }
++};
++
++mount_opt_t lx_proc_options[] = {
++	{ NULL, MOUNT_OPT_INVALID }
++};
++
++mount_opt_t lx_autofs_options[] = {
++	{ LX_MNTOPT_FD,		MOUNT_OPT_UINT },
++	{ LX_MNTOPT_PGRP,	MOUNT_OPT_UINT },
++	{ LX_MNTOPT_MINPROTO,	MOUNT_OPT_UINT },
++	{ LX_MNTOPT_MAXPROTO,	MOUNT_OPT_UINT },
++};
++
++
++/*
++ * i_lx_opt_verify() - Check the mount options.
++ *
++ * You might wonder why we're being so strict about the mount options
++ * we allow.  The reason is that normally all mount option verification
++ * is done by the Solaris userland mount command.  Once mount options
++ * are passed to the kernel, invalid options are simply ignored.  So
++ * if we actually want to catch requests for functionality that we
++ * don't support, or if we want to make sure that we don't randomly
++ * enable options that we haven't check to make sure they have the
++ * same syntax on Linux and Solaris, we need to reject any options
++ * we don't know to be ok here.
++ */
++static int
++i_lx_opt_verify(char *opts, mount_opt_t *mop)
++{
++	int	opts_len = strlen(opts);
++	char	*opts_tmp, *opt;
++	int	opt_len, i;
++
++	assert((opts != NULL) && (mop != NULL));
++
++	/* If no options were specified, there's no problem. */
++	if (opts_len == 0)
++		return (1);
++
++	/* If no options are allowed, fail. */
++	if (mop[0].mo_name == NULL)
++		return (0);
++
++	/* Don't accept leading or trailing ','. */
++	if ((opts[0] == ',') || (opts[opts_len] == ','))
++		return (0);
++
++	/* Don't accept sequential ','. */
++	for (i = 1; i < opts_len; i++)
++		if ((opts[i - 1] ==  ',') && (opts[i] ==  ','))
++			return (0);
++
++	/*
++	 * We're going to use strtok() which modifies the target
++	 * string so make a temporary copy.
++	 */
++	opts_tmp = SAFE_ALLOCA(opts_len);
++	if (opts_tmp == NULL)
++		return (-1);
++	bcopy(opts, opts_tmp, opts_len + 1);
++
++	/* Verify each prop one at a time. */
++	opt = strtok(opts_tmp, ",");
++	opt_len = strlen(opt);
++	for (;;) {
++
++		/* Check for matching option/value pair. */
++		for (i = 0; mop[i].mo_name != NULL; i++) {
++			char	*ovalue;
++			int	ovalue_len, mo_len;
++
++			/* If the options is too short don't bother comparing */
++			mo_len = strlen(mop[i].mo_name);