vplat.c revision 628680125482a37a45c692030029fd62a600f914
/*
* 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
* 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.
*/
/*
* This module contains functions used to bring up and tear down the
* Virtual Platform: [un]mounting file-systems, [un]plumbing network
* interfaces, [un]configuring devices, establishing resource controls,
* and creating/destroying the zone in the kernel. These actions, on
* the way up, ready the zone; on the way down, they halt the zone.
* See the much longer block comment at the beginning of zoneadmd.c
* for a bigger picture of how the whole program functions.
*
* This module also has primary responsibility for the layout of "scratch
* zones." These are mounted, but inactive, zones that are used during
* operating system upgrade and potentially other administrative action. The
* scratch zone environment is similar to the miniroot environment. The zone's
* actual root is mounted read-write on /a, and the standard paths (/usr,
* /sbin, /lib) all lead to read-only copies of the running system's binaries.
* This allows the administrative tools to manipulate the zone using "-R /a"
* without relying on any binaries in the zone itself.
*
* If the scratch zone is on an alternate root (Live Upgrade [LU] boot
* environment), then we must resolve the lofs mounts used there to uncover
* writable (unshared) resources. Shared resources, though, are always
* read-only. In addition, if the "same" zone with a different root path is
* currently running, then "/b" inside the zone points to the running zone's
* root. This allows LU to synchronize configuration files during the upgrade
* process.
*
* To construct this environment, this module creates a tmpfs mount on
* described above is constructed on the fly. The zone is then created using
*
* Note that scratch zones are inactive. The zone's bits are not running and
* likely cannot be run correctly until upgrade is done. Init is not running
* there, nor is SMF. Because of this, the "mounted" state of a scratch zone
*/
#include <sys/systeminfo.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <libdlvlan.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <rctl.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <wait.h>
#include <limits.h>
#include <libgen.h>
#include <libzfs.h>
#include <libdevinfo.h>
#include <zone.h>
#include <assert.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <pool.h>
#include <sys/priocntl.h>
#include <libbrand.h>
#include <libzonecfg.h>
#include <synch.h>
#include "zoneadmd.h"
#include <libtsnet.h>
#define V4_ADDR_LEN 32
#define V6_ADDR_LEN 128
#define IPD_DEFAULT_OPTS \
#define MAXTNZLEN 2048
/* for routing socket */
static int rts_seqno = 0;
/* mangled zone name when mounting in an alternate root environment */
static char kernzone[ZONENAME_MAX];
/* array of cached mount entries for resolve_lofs */
/* for Trusted Extensions */
static int tsol_mounts(zlog_t *, char *, char *);
static void tsol_unmounts(zlog_t *, char *);
/* from libsocket, not in any header file */
/* from zoneadmd */
extern char query_hook[];
/*
* An optimization for build_mnttable: reallocate (and potentially copy the
* data) only once every N times through the loop.
*/
#define MNTTAB_HUNK 32
/*
* Private autofs system call
*/
extern int _autofssys(int, void *);
static int
{
/*
* Ask autofs to unmount all trigger nodes in the given zone.
*/
}
static void
{
uint_t i;
return;
for (i = 0; i < nelem; i++) {
}
}
/*
* Build the mount table for the zone rooted at "zroot", storing the resulting
* array of struct mnttabs in "mnt_arrayp" and the number of elements in the
* array in "nelemp".
*/
static int
{
nmnt = 0;
continue;
if (nmnt % MNTTAB_HUNK == 0) {
return (-1);
}
}
/*
* Zero out any fields we're not using.
*/
return (-1);
}
}
*mnt_arrayp = mnts;
return (0);
}
/*
* This is an optimization. The resolve_lofs function is used quite frequently
* to manipulate file paths, and on a machine with a large number of zones,
* there will be a huge number of mounted file systems. Thus, we trigger a
* reread of the list of mount points
*/
static void
lofs_discard_mnttab(void)
{
}
static int
{
return (-1);
&nmnts) == -1) {
return (-1);
}
return (0);
}
/*
* This function loops over potential loopback mounts and symlinks in a given
* path and resolves them all down to an absolute path.
*/
void
{
const char *altroot;
char tmppath[MAXPATHLEN];
return;
/* This happens once per zoneadmd operation. */
return;
altroot = zonecfg_get_root();
for (;;) {
/* Search in reverse order to find longest match */
mnp--) {
continue;
break;
}
if (mnp < resolve_lofs_mnts)
break;
/* If it's not a lofs then we're done */
break;
if (outside_altroot) {
char *cp;
/*
* If we run into a read-only mount outside of the
* alternate root environment, then the user doesn't
* want this path to be made read-write.
*/
NULL &&
break;
}
} else if (arlen > 0 &&
}
/* use temporary buffer because new path might be longer */
break;
}
}
/*
* For a regular mount, check if a replacement lofs mount is needed because the
* referenced device is already mounted somewhere.
*/
static int
{
/* This happens once per zoneadmd operation. */
return (-1);
/*
* If this special node isn't already in use, then it's ours alone;
* no need to worry about conflicting mounts.
*/
mnp++) {
break;
}
if (mnp >= resolve_lofs_mnt_max)
return (0);
/*
* Convert this duplicate mount into a lofs mount.
*/
sizeof (fsptr->zone_fs_special));
sizeof (fsptr->zone_fs_type));
/*
* Discard all but one of the original options and set that to be the
* same set of options used for inherit package directory resources.
*/
fsptr->zone_fs_dir);
return (-1);
}
} else {
}
}
return (0);
}
int
{
char path[MAXPATHLEN];
sizeof (path)) {
subdir);
return (-1);
}
/*
* We don't check the file mode since presumably the zone
* administrator may have had good reason to change the mode,
* and we don't need to second guess him.
*/
/*
* Allow readonly mounts of /etc/ files; this
* is needed most by Trusted Extensions.
*/
strlen("/etc/")) != 0) {
"%s is not in /etc", path);
return (-1);
}
} else {
"%s is not a directory", path);
return (-1);
}
}
return (0);
}
"a read-only file system in this local zone.\nMake "
else
return (-1);
}
return (0);
}
static void
free_remote_fstypes(char **types)
{
uint_t i;
return;
}
static char **
{
char buf[MAXPATHLEN];
char fstype[MAXPATHLEN];
uint_t i;
return (NULL);
}
/*
* Count the number of lines
*/
lines++;
if (lines == 0) /* didn't read anything; empty file */
goto out;
/*
* Allocate enough space for a NULL-terminated array.
*/
goto out;
}
i = 0;
/* LINTED - fstype is big enough to hold buf */
goto out;
}
goto out;
}
i++;
}
out:
return (types);
}
static boolean_t
{
uint_t i;
if (remote_fstypes == NULL)
return (B_FALSE);
for (i = 0; remote_fstypes[i] != NULL; i++) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* This converts a zone root path (normally of the form .../root) to a Live
* Upgrade scratch zone root (of the form .../lu).
*/
static void
{
if (!isresolved && zonecfg_in_alt_root())
}
/*
* The general strategy for unmounting filesystems is as follows:
*
* - Remote filesystems may be dead, and attempting to contact them as
* part of a regular unmount may hang forever; we want to always try to
* forcibly unmount such filesystems and only fall back to regular
* unmounts if the filesystem doesn't support forced unmounts.
*
* - We don't want to unnecessarily corrupt metadata on local
* filesystems (ie UFS), so we want to start off with graceful unmounts,
* and only escalate to doing forced unmounts if we get stuck.
*
* We start off walking backwards through the mount table. This doesn't
* give us strict ordering but ensures that we try to unmount submounts
* first. We thus limit the number of failed umount2(2) calls.
*
* The mechanism for determining if we're stuck is to count the number
* of failed unmounts each iteration through the mount table. This
* gives us an upper bound on the number of filesystems which remain
* mounted (autofs trigger nodes are dealt with separately). If at the
* end of one unmount+autofs_cleanup cycle we still have the same number
* of mounts that we started out with, we're stuck and try a forced
* unmount. If that fails (filesystem doesn't support forced unmounts)
* then we bail and are unable to teardown the zone. If it succeeds,
* we're no longer stuck so we continue with our policy of trying
* graceful mounts first.
*
* Zone must be down (ie, no processes or threads active).
*/
static int
{
int error = 0;
char **remote_fstypes = NULL;
return (-1);
}
if (unmount_cmd)
/*
* For Trusted Extensions unmount each higher level zone's mount
*/
if (!unmount_cmd)
return (-1);
}
/*
* Use our hacky mntfs ioctl so we see everything, even mounts with
* MS_NOMNTTAB.
*/
error++;
goto out;
}
/*
* Build the list of remote fstypes so we know which ones we
* should forcibly unmount.
*/
for (; /* ever */; ) {
char *path;
uint_t i;
nmnt = 0;
/*
* MNTTAB gives us a way to walk through mounted
* filesystems; we need to be able to walk them in
* reverse order, so we build a list of all mounted
* filesystems.
*/
&nmnt) != 0) {
error++;
goto out;
}
for (i = 0; i < nmnt; i++) {
/*
* Try forced unmount first for remote filesystems.
*
* Not all remote filesystems support forced unmounts,
* so if this fails (ENOTSUP) we'll continue on
* and try a regular unmount.
*/
}
/*
* Try forced unmount if we're stuck.
*/
if (stuck) {
} else {
/*
* The first failure indicates a
* mount we won't be able to get
* rid of automatically, so we
* bail.
*/
error++;
"unable to unmount '%s'", path);
goto out;
}
}
/*
* Try regular unmounts for everything else.
*/
newcount++;
}
if (newcount == 0)
break;
/*
* Last round didn't unmount anything; we're stuck and
* should start trying forced unmounts.
*/
}
/*
* Autofs doesn't let you unmount its trigger nodes from
* userland so we have to tell the kernel to cleanup for us.
*/
if (autofs_cleanup(zoneid) != 0) {
error++;
goto out;
}
}
out:
return (error ? -1 : 0);
}
static int
{
}
/*
* Fork and exec (and wait for) the mentioned binary with the provided
* arguments. Returns (-1) if something went wrong with fork(2) or exec(2),
* returns the exit status otherwise.
*
* If we were unable to exec the provided pathname (for whatever
* reason), we return the special token ZEXIT_EXEC. The current value
* of ZEXIT_EXEC doesn't conflict with legitimate exit codes of the
* consumers of this function; any future consumers must make sure this
* remains the case.
*/
static int
{
int child_status = 0;
/*
* Do not let another thread localize a message while we are forking.
*/
(void) mutex_lock(&msglock);
(void) mutex_unlock(&msglock);
if (child_pid == -1) {
return (-1);
} else if (child_pid == 0) {
closefrom(0);
/*
* Since we are in the child, there is no point calling zerror()
* since there is nobody waiting to consume it. So exit with a
* special code that the parent will recognize and call zerror()
* accordingly.
*/
} else {
}
if (WIFSIGNALED(child_status)) {
return (-1);
}
return (-1);
}
return (WEXITSTATUS(child_status));
}
static int
{
return (-1);
}
static int
{
char cmdbuf[MAXPATHLEN];
char *argv[4];
int status;
/*
*/
>= sizeof (cmdbuf)) {
return (-1);
}
/*
* If it doesn't exist, that's OK: we verified this previously
* in zoneadm.
*/
return (0);
argv[0] = "fsck";
return (status);
return (-1);
}
static int
{
char cmdbuf[MAXPATHLEN];
char *argv[6];
int status;
/*
*/
>= sizeof (cmdbuf)) {
return (-1);
}
argv[0] = "mount";
if (opts[0] == '\0') {
} else {
}
return (status);
if (opts[0] == '\0')
"failed with exit code %d",
else
"failed with exit code %d",
return (-1);
}
/*
* Check if a given mount point path exists.
* If it does, make sure it doesn't contain any symlinks.
* Note that if "leaf" is false we're checking an intermediate
* component of the mount point path, so it must be a directory.
* If "leaf" is true, then we're checking the entire mount point
* path, so the mount point itself can be anything aside from a
* symbolic link.
*
* If the path is invalid then a negative value is returned. If the
* path exists and is a valid mount point path then 0 is returned.
* If the path doesn't exist return a positive value.
*/
static int
{
char respath[MAXPATHLEN];
int res;
return (1);
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
/*
* We don't like ".."s, "."s, or "//"s throwing us off
*/
return (-1);
}
return (0);
}
/*
* Validate a mount point path. A valid mount point path is an
* absolute path that either doesn't exist, or, if it does exists it
* must be an absolute canonical path that doesn't have any symbolic
* links in it. The target of a mount point path can be any filesystem
* object. (Different filesystems can support different mount points,
* for example "lofs" and "mntfs" both support files and directories
* while "ufs" just supports directories.)
*
* If the path is invalid then a negative value is returned. If the
* path exists and is a valid mount point path then 0 is returned.
* If the path doesn't exist return a positive value.
*/
int
{
int rv;
/*
* Sanity check the target mount point path.
* It must be a non-null string that starts with a '/'.
*/
if (dir[0] != '/') {
if (spec[0] == '\0') {
/*
* This must be an invalid ipd entry (see comments
* in mount_filesystems_ipdent()).
*/
"invalid inherit-pkg-dir entry: \"%s\"", dir);
} else {
/* Something went wrong. */
"type: \"%s\", special: \"%s\", dir: \"%s\"",
}
return (-1);
}
/*
* Join rootpath and dir. Make sure abspath ends with '/', this
* is added to all paths (even non-directory paths) to allow us
* to detect the end of paths below. If the path already ends
* in a '/', then that's ok too (although we'll fail the
* cannonical path check in valid_mount_point()).
*/
return (-1);
}
/*
* Starting with rootpath, verify the mount path one component
* at a time. Continue until we've evaluated all of abspath.
*/
do {
*slashp = '\0';
if (slashp_next != NULL) {
/* This is an intermediary mount path component. */
} else {
/* This is the last component of the mount path. */
}
if (rv < 0)
return (rv);
*slashp = '/';
return (rv);
}
static int
{
}
static int
{
}
int
{
return (-1);
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* list of devices (via zonecfg) to the /dev filesystem. The filesystem will
*/
static int
{
char brand[MAXNAMELEN];
struct zone_devtab ztab;
int err;
int retval = -1;
const char *curr_iptype;
goto cleanup;
}
/*
* Get a handle to the brand info for this zone.
* If we are mounting the zone, then we must always use the native
* brand device mounts.
*/
} else {
}
goto cleanup;
}
goto cleanup;
}
switch (iptype) {
case ZS_SHARED:
curr_iptype = "shared";
break;
case ZS_EXCLUSIVE:
curr_iptype = "exclusive";
break;
}
goto cleanup;
}
mount_one_dev_symlink_cb, prof) != 0) {
goto cleanup;
}
/* Add user-specified devices and directories */
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
"user-specified device");
goto cleanup;
}
}
(void) zonecfg_enddevent(handle);
/* Send profile to kernel */
if (di_prof_commit(prof)) {
goto cleanup;
}
retval = 0;
if (prof)
return (retval);
}
static int
{
char path[MAXPATHLEN];
char specpath[MAXPATHLEN];
char optstr[MAX_MNTOPT_STR];
int rv;
return (-1);
} else if (rv > 0) {
/* The mount point path doesn't exist, create it now. */
DEFAULT_DIR_GROUP) != 0) {
return (-1);
}
/*
* Now this might seem weird, but we need to invoke
* valid_mount_path() again. Why? Because it checks
* to make sure that the mount point path is canonical,
* which it can only do if the path exists, so now that
* we've created the path we have to verify it again.
*/
fsptr->zone_fs_type)) < 0) {
"%s%s is not a valid mount point",
return (-1);
}
}
fsptr->zone_fs_dir);
/*
* A zero-length special is how we distinguish IPDs from
* general-purpose FSs. Make sure it mounts from a place that
* can be seen via the alternate zone's root.
*/
sizeof (specpath)) {
return (-1);
}
if (zonecfg_in_alt_root())
specpath);
return (-1);
}
return (0);
}
/*
* In general the strategy here is to do just as much verification as
* necessary to avoid crashing or otherwise doing something bad; if the
* administrator initiated the operation via zoneadm(1m), he'll get
* auto-verification which will let him know what's wrong. If he
* modifies the zone configuration of a running zone and doesn't attempt
* to verify that it's OK we won't crash but won't bother trying to be
* too helpful either. zoneadm verify is only a couple keystrokes away.
*/
return (-1);
}
/*
* If we're looking at an alternate root environment, then construct
* read-only loopback mounts as necessary. Note that any special
* paths for lofs zone mounts in an alternate root must have
* already been pre-pended with any alternate root path by the
* time we get here.
*/
if (zonecfg_in_alt_root()) {
/*
* If we're going to mount a block device we need
* to check if that device is already mounted
* somewhere else, and if so, do a lofs mount
* of the device instead of a direct mount
*/
return (-1);
/*
* For lofs mounts, the special node is inside the
* alternate root. We need lofs resolution for
* this case in order to get at the underlying
* read-write path.
*/
sizeof (fsptr->zone_fs_special));
}
}
/*
* Run 'fsck -m' if there's a device to fsck.
*/
return (-1);
return (-1);
}
/*
* Build up mount option string.
*/
optstr[0] = '\0';
sizeof (optstr));
sizeof (optstr));
}
}
return (rv);
/*
* The mount succeeded. If this was not a mount of /dev then
* we're done.
*/
return (0);
/*
* We just mounted an instance of a /dev filesystem, so now we
* need to configure it.
*/
}
static void
{
uint_t i;
return;
for (i = 0; i < nelem; i++)
}
/*
* This function initiates the creation of a small Solaris Environment for
* scratch zone. The Environment creation process is split up into two
* functions(build_mounted_pre_var() and build_mounted_post_var()). It
* is done this way because:
* We need to have both /etc and /var in the root of the scratchzone.
* We loopback mount zone's own /etc and /var into the root of the
* scratch zone. Unlike /etc, /var can be a seperate filesystem. So we
* need to delay the mount of /var till the zone's root gets populated.
* So mounting of localdirs[](/etc and /var) have been moved to the
* build_mounted_post_var() which gets called only after the zone
* specific filesystems are mounted.
*
* Note that the scratch zone we set up for updating the zone (Z_MNT_UPDATE)
* does not loopback mount the zone's own /etc and /var into the root of the
* scratch zone.
*/
static boolean_t
{
const char **cpp;
static const char *mkdirs[] = {
};
char *altstr;
/*
* These are mostly special mount points; not handled here. (See
* zone_mount_early.)
*/
return (B_FALSE);
}
}
/*
* This is here to support lucopy. If there's an instance of this same
* zone on the current running system, then we mount its root up as
* read-only inside the scratch zone.
*/
return (B_FALSE);
}
zonecfg_set_root("");
return (B_FALSE);
}
tmp) != 0) {
fromdir);
return (B_FALSE);
}
}
return (B_FALSE);
}
}
return (B_FALSE);
return (B_TRUE);
}
static boolean_t
const char *luroot)
{
const char **cpp;
const char **loopdirs;
const char **tmpdirs;
static const char *localdirs[] = {
};
static const char *scr_loopdirs[] = {
"/usr", NULL
};
static const char *upd_loopdirs[] = {
"/etc", "/kernel", "/lib", "/opt", "/platform", "/sbin",
};
static const char *scr_tmpdirs[] = {
};
static const char *upd_tmpdirs[] = {
};
if (mount_cmd == Z_MNT_SCRATCH) {
/*
* These are mounted read-write from the zone undergoing
* upgrade. We must be careful not to 'leak' things from the
* main system into the zone, and this accomplishes that goal.
*/
*cpp);
return (B_FALSE);
}
!= 0) {
return (B_FALSE);
}
}
}
if (mount_cmd == Z_MNT_UPDATE)
else
/*
* These are things mounted read-only from the running system because
* they contain binaries that must match system.
*/
return (B_FALSE);
}
return (B_FALSE);
}
/*
* Ignore any non-directories encountered. These are
* things that have been converted into symlinks
* fixup.
*/
continue;
}
tmp) != 0) {
*cpp);
return (B_FALSE);
}
}
if (mount_cmd == Z_MNT_UPDATE)
else
/*
* These are things with tmpfs mounted inside.
*/
return (B_FALSE);
}
/*
* We could set the mode for /tmp when we do the mkdir but
* since that can be modified by the umask we will just set
* the correct mode for /tmp now.
*/
return (B_FALSE);
}
return (B_FALSE);
}
}
return (B_TRUE);
}
typedef struct plat_gmount_cb_data {
struct zone_fstab **pgcd_fs_tab;
int *pgcd_num_fs;
/*
* plat_gmount_cb() is a callback function invoked by libbrand to iterate
* through all global brand platform mounts.
*/
int
{
num_fs++;
return (-1);
}
/* update the callback struct passed in */
sizeof (fsp->zone_fs_special));
return (-1);
}
return (0);
}
static int
{
int num_fs;
return (-1);
}
num_fs++;
(void) zonecfg_endipdent(handle);
return (-1);
}
/* update the pointers passed in */
/*
* IPDs logically only have a mount point; all other properties
* are implied.
*/
}
(void) zonecfg_endipdent(handle);
return (0);
}
static int
{
int num_fs;
return (-1);
}
/*
* ZFS filesystems will not be accessible under an alternate
* root, since the pool will not be known. Ignore them in this
* case.
*/
continue;
num_fs++;
(void) zonecfg_endfsent(handle);
return (-1);
}
/* update the pointers passed in */
sizeof (fsp->zone_fs_raw));
sizeof (fsp->zone_fs_type));
/*
* For all lofs mounts, make sure that the 'special'
* entry points inside the alternate root. The
* source path for a lofs mount in a given zone needs
* to be relative to the root of the boot environment
* that contains the zone. Note that we don't do this
* for non-lofs mounts since they will have a device
* as a backing store and device paths must always be
* specified relative to the current boot environment.
*/
sizeof (fsp->zone_fs_special));
}
sizeof (fsp->zone_fs_special));
}
(void) zonecfg_endfsent(handle);
return (0);
}
static int
{
char rootpath[MAXPATHLEN];
char zonepath[MAXPATHLEN];
char brand[MAXNAMELEN];
char luroot[MAXPATHLEN];
int i, num_fs = 0;
"zone must be in '%s' or '%s' state to mount file-systems",
goto bad;
}
goto bad;
}
goto bad;
}
goto bad;
}
goto bad;
}
/*
* If we are mounting the zone, then we must always use the native
* brand global mounts.
*/
} else {
}
/* Get a handle to the brand info for this zone */
return (-1);
}
/*
* Get the list of global filesystems to mount from the brand
* configuration.
*/
plat_gmount_cb, &cb) != 0) {
return (-1);
}
/*
* Iterate through the rest of the filesystems, first the IPDs, then
* the general FSs. Sort them all, then mount them in sorted order.
* This is to make sure the higher level directories (e.g., /usr)
*/
goto bad;
mount_cmd) != 0)
goto bad;
/*
* Normally when we mount a zone all the zone filesystems
* get mounted relative to rootpath, which is usually
* <zonepath>/root. But when mounting a zone for administration
* purposes via the zone "mount" state, build_mounted_pre_var()
* updates rootpath to be <zonepath>/lu/a so we'll mount all
* the zones filesystems there instead.
*
* build_mounted_pre_var() and build_mounted_post_var() will
* also do some extra work to create directories and lofs mount
* a bunch of global zone file system paths into <zonepath>/lu.
*
* This allows us to be able to enter the zone (now rooted at
* global zone and have them upgrade the to-be-modified zone's
* files mounted on /a. (Which mirrors the existing standard
* upgrade environment.)
*
* There is of course one catch. When doing the upgrade
* for the zone and we don't want to have any /dev filesystem
* as a normal zone filesystem by default we'll try to mount
*
* All this work is done in three phases:
* 1) Create and populate lu directory (build_mounted_pre_var()).
* 2) Mount the required filesystems as per the zone configuration.
* 3) Set up the rest of the scratch zone environment
* (build_mounted_post_var()).
*/
goto bad;
for (i = 0; i < num_fs; i++) {
/*
* but /dev is special and always goes at the top
* so strip the trailing '/a' from the rootpath.
*/
!= 0)
goto bad;
continue;
}
goto bad;
}
goto bad;
/*
*/
if (mount_cmd == Z_MNT_BOOT &&
goto bad;
/*
* Everything looks fine.
*/
return (0);
bad:
return (-1);
}
/* caller makes sure neither parameter is NULL */
static int
{
int prefixlen;
return (1);
while (prefixlen > 0) {
if (prefixlen >= 8) {
*maskstr++ = 0xFF;
prefixlen -= 8;
continue;
}
prefixlen--;
}
return (0);
}
/*
* Tear down all interfaces belonging to the given zone. This should
* be called with the zone in a state other than "running", so that
* interfaces can't be assigned to the zone after this returns.
*
* If anything goes wrong, log an error message and return an error.
*/
static int
{
ret_code = -1;
goto bad;
}
"could not determine number of network interfaces");
ret_code = -1;
goto bad;
}
ret_code = -1;
goto bad;
}
"interfaces");
ret_code = -1;
goto bad;
}
(void) close(s);
0) {
ret_code = -1;
continue;
}
/*
* Interface may have been removed by admin or
* another zone halting.
*/
continue;
"%s: could not determine the zone to which this "
ret_code = -1;
continue;
}
"%s: could not remove network interface",
ret_code = -1;
continue;
}
}
}
bad:
if (s > 0)
(void) close(s);
if (buf)
return (ret_code);
}
static union sockunion {
struct sockaddr_in sin;
struct sockaddr_dl sdl;
struct sockaddr_in6 sin6;
static struct {
char space[512];
} rtmsg;
static int
{
case AF_INET:
return (sizeof (struct sockaddr_in));
case AF_LINK:
return (sizeof (struct sockaddr_dl));
case AF_INET6:
return (sizeof (struct sockaddr_in6));
default:
return (sizeof (struct sockaddr));
}
}
#define ROUNDUP_LONG(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
/*
* Look up which zone is using a given IP address. The address in question
* is expected to have been stuffed into the structure to which lifr points
* via a previous SIOCGLIFADDR ioctl().
*
* This is done using black router socket magic.
*
* Return the name of the zone on success or NULL on failure.
*
* This is a lot of code for a simple task; a new ioctl request to take care
* of this might be a useful RFE.
*/
static char *
{
static char answer[ZONENAME_MAX];
int s, rlen, l, i;
char save_if_name[LIFNAMSIZ];
answer[0] = '\0';
return (NULL);
}
struct sockaddr_in *sin4;
} else {
struct sockaddr_in6 *sin6;
}
cp += l;
cp += l;
return (NULL);
"write to routing socket got only %d for len\n", rlen);
return (NULL);
}
do {
if (l < 0) {
return (NULL);
}
"routing message version %d not understood",
return (NULL);
}
"expected %d bytes, returned %d bytes",
return (NULL);
}
return (NULL);
}
return (NULL);
}
for (i = 1; i != 0; i <<= 1) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (i != RTA_IFP) {
continue;
}
break;
}
"determined");
return (NULL);
}
/*
* We need to set the I/F name to what we got above, then do the
* appropriate ioctl to get its zone name. But lifr->lifr_name is
* used by the calling function to do a REMOVEIF, so if we leave the
* "good" zone's I/F name in place, *that* I/F will be removed instead
* of the bad one. So we save the old (bad) I/F name before over-
* writing it and doing the ioctl, then restore it after the ioctl.
*/
if (i < 0) {
"%s: could not determine the zone network interface "
return (NULL);
}
lifr->lifr_zoneid);
return (answer);
return (NULL);
}
/*
* Configures a single interface: a new virtual interface is added, based on
* the physical interface nwiftabptr->zone_nwif_physical, with the address
* specified in nwiftabptr->zone_nwif_address, for zone zone_id. Note that
* the "address" can be an IPv6 address (with a /prefixlength required), an
* IPv4 address (with a /prefixlength optional), or a name; for the latter,
* an IPv4 name-to-address resolution will be attempted.
*
* If anything goes wrong, we log an detailed error message, attempt to tear
* down whatever we set up and return an error.
*/
static int
struct zone_nwiftab *nwiftabptr)
{
struct sockaddr_in netmask4;
struct sockaddr_in6 netmask6;
struct sockaddr_storage laddr;
int s;
char addrstr4[INET_ADDRSTRLEN];
int res;
return (-1);
}
return (-1);
}
/*
* This is a similar kind of "hack" like in addif() to get around
* the problem of SIOCLIFADDIF. The problem is that this ioctl
* does not include the netmask when adding a logical interface.
* To get around this problem, we first add the logical interface
* with a 0 address. After that, we set the netmask if provided.
* Finally we set the interface address.
*/
/*
* Here, we know that the interface can't be brought up.
* A similar warning message was already printed out to
* the console by zoneadm(1M) so instead we log the
* message to syslog and continue.
*/
(void) close(s);
return (Z_OK);
}
/* Preserve literal IPv4 address for later potential printing. */
goto bad;
}
/*
* Loopback interface will use the default netmask assigned, if no
* netmask is found.
*/
}
/*
* The IPv4 netmask can be determined either
* directly if a prefix length was supplied with
* the address or via the netmasks database. Not
* being able to determine it is a common failure,
* but it often is not fatal to operation of the
* interface. In that case, a warning will be
* printed after the rest of the interface's
* parameters have been configured.
*/
*slashp = '/';
"%s: invalid prefix length in %s",
goto bad;
}
} else if (getnetmaskbyaddr(in4,
}
if (got_netmask) {
sizeof (netmask4));
}
} else {
*slashp = '/';
"%s: invalid prefix length in %s",
goto bad;
}
sizeof (netmask6));
}
if (got_netmask &&
goto bad;
}
/* Set the interface address */
"%s: could not set IP address to %s",
goto bad;
}
goto bad;
}
int save_errno = errno;
char *zone_using;
/*
* If we failed with something other than EADDRNOTAVAIL,
* then skip to the end. Otherwise, look up our address,
* then call a function to determine which zone is already
* using that address.
*/
if (errno != EADDRNOTAVAIL) {
"%s: could not bring network interface up",
goto bad;
}
goto bad;
}
errno = save_errno;
if (zone_using == NULL)
"%s: could not bring network interface up",
else
"interface up: address in use by zone '%s'",
goto bad;
}
if (!got_netmask && !is_loopback) {
/*
* A common, but often non-fatal problem, is that the system
* cannot find the netmask for an interface address. This is
* /etc/nsswitch.conf says to use NIS or NIS+ and it's not
* in that. This doesn't show up at boot because the netmask
* available. We warn the user here that something like this
* has happened and we're just running with a default and
* possible incorrect netmask.
*/
char buffer[INET6_ADDRSTRLEN];
void *addr;
const char *nomatch = "no matching subnet found in netmasks(4)";
addr = &((struct sockaddr_in *)
else
addr = &((struct sockaddr_in6 *)
/*
* Find out what netmask the interface is going to be using.
* If we just brought up an IPMP data address on an underlying
* interface above, the address will have already migrated, so
* the SIOCGLIFNETMASK won't be able to find it (but we need
* to bring the address up to get the actual netmask). Just
* omit printing the actual netmask in this corner-case.
*/
nomatch);
} else {
"WARNING: %s: %s: %s; using default of %s.",
}
}
/*
* If a default router was specified for this interface
* set the route now. Ignore if already set.
*/
int status;
char *argv[7];
argv[0] = "route";
"interface %s to %s\n",
}
(void) close(s);
return (Z_OK);
bad:
(void) close(s);
return (-1);
}
/*
* Sets up network interfaces based on information from the zone configuration.
* IPv4 and IPv6 loopback interfaces are set up "for free", modeling the global
* system.
*
* If anything goes wrong, we log a general error message, attempt to tear down
* whatever we set up, and return an error.
*/
static int
{
return (-1);
}
return (-1);
}
return (-1);
}
for (;;) {
break;
Z_OK) {
(void) zonecfg_endnwifent(handle);
return (-1);
}
}
(void) zonecfg_endnwifent(handle);
}
if (is_system_labeled()) {
/*
* Labeled zones share the loopback interface
* so it is not plumbed for shared stack instances.
*/
return (0);
}
sizeof (loopback_iftab.zone_nwif_physical));
sizeof (loopback_iftab.zone_nwif_address));
return (-1);
/* Always plumb up the IPv6 loopback interface. */
sizeof (loopback_iftab.zone_nwif_address));
return (-1);
return (0);
}
static void
{
char errmsg[DLADM_STRSIZE];
}
static int
{
/* First check if it's in use by global zone. */
"'%s' which is used in the global zone", dlname);
return (-1);
}
/* Set zoneid of this link. */
if (err != DLADM_STATUS_OK) {
"WARNING: unable to add network interface");
return (-1);
}
return (0);
}
/*
* Add the kernel access control information for the interface names.
* If anything goes wrong, we log a general error message, attempt to tear down
* whatever we set up, and return an error.
*/
static int
{
struct zone_nwiftab nwiftab;
char rootpath[MAXPATHLEN];
char path[MAXPATHLEN];
return (-1);
}
return (-1);
}
return (0);
}
for (;;) {
break;
(void) zonecfg_endnwifent(handle);
"unable to determine dev root");
return (-1);
}
"/dev");
(void) zonecfg_endnwifent(handle);
"failed to initialize profile");
return (-1);
}
}
/*
* Create the /dev entry for backward compatibility.
* Only create the /dev entry if it's not in use.
* Note that the zone still boots when the assigned
* interface is inaccessible, used by others, etc.
* Also, when vanity naming is used, some interface do
* do not have corresponding /dev node names (for example,
* vanity named aggregations). The /dev entry is not
* accessible.
*/
nwiftab.zone_nwif_physical) == 0) {
} else {
(void) zonecfg_endnwifent(handle);
return (-1);
}
}
(void) zonecfg_endnwifent(handle);
if (di_prof_commit(prof) != 0) {
return (-1);
}
}
return (0);
}
static int
{
int dlnum = 0;
/*
* The kernel shutdown callback for the dls module should have removed
* all datalinks from this zone. If any remain, then there's a
* problem.
*/
return (-1);
}
if (dlnum != 0) {
"datalinks remain in zone after shutdown");
return (-1);
}
return (0);
}
static int
{
int fd;
int error;
return (-1);
}
return (0);
return (-1);
}
static int
{
struct sockaddr_storage l, r;
int error;
/*
* Abort IPv4 connections.
*/
local = (struct sockaddr_in *)&l;
remote = (struct sockaddr_in *)&r;
return (error);
/*
* Abort IPv6 connections.
*/
local6 = (struct sockaddr_in6 *)&l;
remote6 = (struct sockaddr_in6 *)&r;
return (error);
return (0);
}
static int
{
int error = -1;
return (-1);
}
return (-1);
}
const char *curr_iptype;
return (-1);
}
switch (iptype) {
case ZS_SHARED:
curr_iptype = "shared";
break;
case ZS_EXCLUSIVE:
curr_iptype = "exclusive";
break;
}
return (0);
}
"failed to determine the zone's default privilege set");
return (-1);
}
case Z_OK:
error = 0;
break;
case Z_PRIV_PROHIBITED:
"within the zone's privilege set", privname);
break;
case Z_PRIV_REQUIRED:
"from the zone's privilege set", privname);
break;
case Z_PRIV_UNKNOWN:
"in the zone's privilege set", privname);
break;
default:
"privilege set");
break;
}
return (error);
}
static int
{
char *nvl_packed = NULL;
int rctlcount = 0;
int error = -1;
struct zone_rctltab rctltab;
*bufsizep = 0;
return (-1);
}
return (-1);
}
goto out;
}
goto out;
}
goto out;
}
struct zone_rctlvaltab *rctlval;
/* zoneadm should have already warned about unknown rctls. */
if (!zonecfg_is_rctl(name)) {
continue;
}
count = 0;
count++;
}
if (count == 0) { /* ignore */
continue; /* Nothing to free */
}
goto out;
i = 0;
"nvlist_alloc");
goto out;
}
!= Z_OK) {
"(priv=%s,limit=%s,action=%s)",
goto out;
}
"(priv=%s,limit=%s,action=%s) is not a "
"valid value for rctl '%s'",
name);
goto out;
}
rctlblk_get_privilege(rctlblk)) != 0) {
"nvlist_add_uint64");
goto out;
}
rctlblk_get_value(rctlblk)) != 0) {
"nvlist_add_uint64");
goto out;
}
!= 0) {
"nvlist_add_uint64");
goto out;
}
}
!= 0) {
"nvlist_add_nvlist_array");
goto out;
}
for (i = 0; i < count; i++)
nvlist_free(nvlv[i]);
rctlcount++;
}
(void) zonecfg_endrctlent(handle);
if (rctlcount == 0) {
error = 0;
goto out;
}
!= 0) {
goto out;
}
error = 0;
*bufp = nvl_packed;
out:
return (error);
}
static int
{
if (query_hook[0] == '\0')
return (0);
> sizeof (cmdbuf))
return (-1);
return (-1);
return (0);
}
static int
{
struct zone_dstab dstab;
int error = -1;
char *implicit_datasets = NULL;
int implicit_len = 0;
*bufsizep = 0;
return (-1);
}
return (-1);
}
goto out;
}
goto out;
}
total = 0;
(void) zonecfg_enddsent(handle);
if (implicit_datasets != NULL)
if (implicit_len > 0)
if (total == 0) {
error = 0;
goto out;
}
goto out;
}
goto out;
}
offset = 0;
}
(void) zonecfg_enddsent(handle);
if (implicit_len > 0)
error = 0;
out:
if (implicit_datasets != NULL)
return (error);
}
static int
{
struct zone_dstab dstab;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
return (-1);
}
/*
* Automatically set the 'zoned' property. We check the value
* first because we'll get EPERM if it is already set.
*/
"on") != 0) {
"property for ZFS dataset '%s'\n",
return (-1);
}
}
(void) zonecfg_enddsent(handle);
return (0);
}
/*
* Share exported directories specified in dfstab for zone
*/
static int
{
int i;
char readonly[] = "ro";
struct zone_fstab lower_fstab;
char *argv[4];
if (!is_system_labeled())
return (0);
return (-1);
}
sizeof (lower_fstab.zone_fs_type));
/*
* Get the list of zones from the kernel
*/
return (-1);
}
if (nzents == 0) {
return (-1);
}
return (-1);
}
return (-1);
}
if (nzents != nzents_saved) {
/* list changed, try again */
goto again;
}
ip = getprivimplinfo();
return (-1);
}
for (i = 0; i < nzents; i++) {
char zid_name[ZONENAME_MAX];
char zid_rpath[MAXPATHLEN];
if (zids[i] == GLOBAL_ZONEID)
continue;
continue;
/*
* Do special setup for the zone we are booting
*/
struct zone_fstab autofs_fstab;
char map_path[MAXPATHLEN];
int fd;
/*
* Create auto_home_<zone> map for this zone
* in the global zone. The non-global zone entry
* will be created by automount when the zone
* is booted.
*/
/*
* If the map file doesn't exist create a template
*/
int len;
char map_rec[MAXPATHLEN];
}
/*
* Mount auto_home_<zone> in the global zone if absent.
* If it's already of type autofs, then
* don't mount it again.
*/
char optstr[] = "indirect,ignore,nobrowse";
/*
* Mount will fail if automounter has already
* processed the auto_home_<zonename> map
*/
}
continue;
}
(zid_state != ZONE_STATE_READY &&
/* Skip over zones without mounted filesystems */
continue;
sizeof (m_label_t)) < 0)
/* Skip over zones with unspecified label */
continue;
sizeof (zid_rpath)) == -1)
/* Skip over zones with bad path */
continue;
/* Skip over zones with bad privs */
continue;
/*
* Reading down is valid according to our label model
* but some customers want to disable it because it
* allows execute down and other possible attacks.
* Therefore, we restrict this feature to zones that
* have the NET_MAC_AWARE privilege which is required
* for NFS read-down semantics.
*/
/*
* Our zone dominates this one.
*/
/*
* If the target is already an LOFS mount
* then don't do it again.
*/
MAXPATHLEN, "%s/export",
zid_rpath) > MAXPATHLEN)
continue;
/*
* Make sure the lower-level home exists
*/
if (make_one_dir(zlogp,
DEFAULT_DIR_GROUP) != 0)
continue;
"/home", MAXPATHLEN);
/*
* Mount can fail because the lower-level
* zone may have already done a mount up.
*/
}
/*
* This zone dominates our zone.
*/
zone_name) > MAXPATHLEN)
continue;
/*
* If the target is already an LOFS mount
* then don't do it again.
*/
/*
* Mount can fail because the higher-level
* zone may have already done a mount down.
*/
}
}
}
/*
* Now share any exported directories from this zone.
* Each zone can have its own dfstab.
*/
argv[0] = "zoneshare";
/* Don't check for errors since they don't affect the zone */
return (0);
}
/*
* Unmount lofs mounts from higher level zones
* Unshare nfs exported directories
*/
static void
{
int i;
char *argv[4];
char path[MAXPATHLEN];
if (!is_system_labeled())
return;
/*
* Get the list of zones from the kernel
*/
return;
}
return;
}
if (nzents == 0)
return;
return;
}
return;
}
if (nzents != nzents_saved) {
/* list changed, try again */
goto again;
}
for (i = 0; i < nzents; i++) {
char zid_name[ZONENAME_MAX];
char zid_rpath[MAXPATHLEN];
if (zids[i] == GLOBAL_ZONEID)
continue;
continue;
/*
* Skip the zone we are halting
*/
continue;
sizeof (zid_state)) < 0) ||
(zid_state < ZONE_IS_READY))
/* Skip over zones without mounted filesystems */
continue;
sizeof (m_label_t)) < 0)
/* Skip over zones with unspecified label */
continue;
sizeof (zid_rpath)) == -1)
/* Skip over zones with bad path */
continue;
/*
* This zone dominates our zone.
*/
zone_name) > MAXPATHLEN)
continue;
/* Skip over mount failures */
}
}
/*
* Unmount global zone autofs trigger for this zone
*/
/* Skip over mount failures */
/*
* Next unshare any exported directories from this zone.
*/
argv[0] = "zoneunshare";
/* Don't check for errors since they don't affect the zone */
/*
* Finally, deallocate any devices in the zone.
*/
argv[0] = "deallocate";
/* Don't check for errors since they don't affect the zone */
}
/*
* Fetch the Trusted Extensions label and multi-level ports (MLPs) for
* this zone.
*/
static tsol_zcent_t *
{
return (NULL);
}
/*
* Check for malformed database
*/
break;
continue;
break;
}
"See tnzonecfg(4)");
} else {
/*
* Save this zone's privileges for later read-down processing
*/
return (NULL);
} else {
}
}
return (zcent);
}
/*
* Add the Trusted Extensions multi-level ports for this zone.
*/
static void
{
if (!is_system_labeled())
return;
tsme.tsme_flags = 0;
}
}
}
}
}
static void
{
if (!is_system_labeled())
return;
}
int
return (0);
}
/*
* Look for zones running on the main system that are using this root (or any
* subdirectory of it). Return B_TRUE and print an error if a conflicting zone
* is found or if we can't tell.
*/
static boolean_t
{
char zroot[MAXPATHLEN];
char zonename[ZONENAME_MAX];
for (;;) {
nzids += 10;
return (B_TRUE);
}
break;
}
while (nzids > 0) {
/*
* Ignore errors; they just mean that the zone has disappeared
* while we were busy.
*/
sizeof (zroot)) == -1)
continue;
sizeof (zonename)) == -1)
"zone root %s already in use by zone %s",
break;
}
}
return (retv);
}
/*
* Search for loopback mounts that use this same source node (same device and
* inode). Return B_TRUE if there is one or if we can't tell.
*/
static boolean_t
{
return (B_TRUE);
}
return (B_TRUE);
continue;
/* We're looking at a loopback mount. Stat it. */
"zone root %s is reachable through %s",
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Set memory cap and pool info for the zone's resource management
* configuration.
*/
static int
{
int res;
struct zone_mcaptab mcap;
char sched[MAXNAMELEN];
char pool_err[128];
return (Z_BAD_HANDLE);
}
return (res);
}
/*
* If a memory cap is configured, set the cap in the kernel using
* zone_setattr() and make sure the rcapd SMF service is enabled.
*/
char smf_err[128];
return (Z_INVAL);
}
"failed: %s", smf_err);
return (Z_INVAL);
}
}
/* Get the scheduling class set in the zone configuration. */
"default scheduling class");
== Z_OK) {
/*
* If the zone has the zone.cpu-shares rctl set then we want to
* use the Fair Share Scheduler (FSS) for processes in the
* zone. Check what scheduling class the zone would be running
* in by default so we can print a warning and modify the class
* if we wouldn't be using FSS.
*/
char class_name[PC_CLNMSZ];
sizeof (class_name)) != Z_OK) {
"the zone's scheduling class");
"rctl is set but\nFSS is not the default "
"scheduling class for\nthis zone. FSS will be "
"used for processes\nin the zone but to get the "
"full benefit of FSS,\nit should be the default "
"scheduling class.\nSee dispadmin(1M) for more "
"details.");
"zone scheduling class to FSS");
}
}
/*
* The next few blocks of code attempt to set up temporary pools as
* well as persistent pools. In all cases we call the functions
* unconditionally. Within each funtion the code will check if the
* zone is actually configured for a temporary pool or persistent pool
* and just return if there is nothing to do.
*
* If we are rebooting we want to attempt to reuse any temporary pool
* that was previously set up. zonecfg_bind_tmp_pool() will do the
* right thing in all cases (reuse or create) based on the current
* zonecfg.
*/
pool_err);
else
return (Z_POOL_BIND);
}
/*
* Check if we need to warn about poold not being enabled.
*/
if (zonecfg_warn_poold(handle)) {
"been specified\nbut the dynamic pool service is not "
"enabled.\nThe system will not dynamically adjust the\n"
"processor allocation within the specified range\n"
"See poold(1M).");
}
/* The following is a warning, not an error. */
if (res == Z_POOL_BIND)
"pool '%s'; using default pool.", pool_err);
else
}
return (Z_OK);
}
/*
* Sets the hostid of the new zone based on its configured value. The zone's
* zone_t structure must already exist in kernel memory. 'zlogp' refers to the
* log used to report errors and warnings and must be non-NULL. 'zone_namep'
* is the name of the new zone and must be non-NULL. 'zoneid' is the numeric
* ID of the new zone.
*
* This function returns zero on success and a nonzero error code on failure.
*/
static int
{
int res;
char hostidp[HW_HOSTID_LEN];
unsigned int hostid;
return (Z_BAD_HANDLE);
}
return (res);
}
Z_OK) {
"zone hostid is not valid: %s", hostidp);
return (Z_HOSTID_FUBAR);
}
sizeof (hostid)) != 0) {
"zone hostid is not valid: %s", hostidp);
return (Z_SYSTEM);
}
} else if (res != Z_BAD_PROPERTY) {
/*
* Z_BAD_PROPERTY is an acceptable error value (from
* zonecfg_get_hostid()) because it indicates that the zone
* doesn't have a hostid.
*/
"configuration is too large.");
else
"configuration");
return (res);
}
return (Z_OK);
}
{
char rootpath[MAXPATHLEN];
int xerr;
char *kzone;
int match = 0;
int doi = 0;
int flags;
return (-1);
}
if (zonecfg_in_alt_root())
return (-1);
}
switch (iptype) {
case ZS_SHARED:
flags = 0;
break;
case ZS_EXCLUSIVE:
break;
}
return (-1);
}
goto error;
if (mount_cmd == Z_MNT_BOOT &&
goto error;
}
goto error;
}
} else {
goto error;
}
}
/*
* We must do this scan twice. First, we look for zones running on the
* main system that are using this root (or any subdirectory of it).
* Next, we reduce to the shortest path and search for loopback mounts
* that use this same source node (same device and inode).
*/
goto error;
goto error;
/*
* Forge up a special root for this zone. When a zone is
* mounted, we can't let the zone have its own root because the
* tools that will be used in this "scratch zone" need access
* to both the zone's resources and the running machine's
* executables.
*
* Note that the mkdir here also catches read-only filesystems.
*/
goto error;
}
goto error;
}
if (zonecfg_in_alt_root()) {
/*
* If we are mounting up a zone in an alternate root partition,
* then we have some additional work to do before starting the
* zone. First, resolve the root path down so that we're not
* fooled by duplicates. Then forge up an internal name for
* the zone.
*/
goto error;
}
if (zonecfg_lock_scratch(fp) != 0) {
goto error;
}
NULL, 0) == 0) {
goto error;
}
/* This is the preferred name */
0) == 0) {
/* This is just an arbitrary name; note "." usage */
}
}
xerr = 0;
flags)) == -1) {
if (xerr == ZE_AREMOUNTS) {
"An unknown file-system is mounted on "
"a subdirectory of %s", rootpath);
} else {
"These file-systems are mounted on "
"subdirectories of %s:", rootpath);
(void) zonecfg_find_mounts(rootpath,
}
} else if (xerr == ZE_CHROOTED) {
"cannot create a zone from a chrooted "
"environment", "zone_create");
} else if (xerr == ZE_LABELINUSE) {
char zonename[ZONENAME_MAX];
"used by the zone '%s'.", zonename);
} else {
}
goto error;
}
if (zonecfg_in_alt_root() &&
zonecfg_get_root()) == -1) {
goto error;
}
/*
* The following actions are not performed when merely mounting a zone
* for administrative use.
*/
if (mount_cmd == Z_MNT_BOOT) {
struct brand_attr attr;
char modname[MAXPATHLEN];
goto error;
"unable to determine brand name");
goto error;
}
if (!is_system_labeled() &&
"cannot boot labeled zone on unlabeled system");
goto error;
}
/*
* If this brand requires any kernel support, now is the time to
* get it loaded and initialized.
*/
"unable to determine brand kernel module");
goto error;
}
sizeof (attr.ba_brandname));
sizeof (attr.ba_modname));
sizeof (attr) != 0)) {
"could not set zone brand attribute.");
goto error;
}
}
goto error;
}
zoneid = -1;
if (zoneid != -1) {
(void) zone_shutdown(zoneid);
(void) zone_destroy(zoneid);
}
return (rval);
}
/*
* libzonecfg (and thus zoneadm) to report the UUID and potentially other zone
* details from inside the zone.
*/
static void
{
int tmpl_fd;
int fd;
/* Locate the zone entry in the global zone's index file */
return;
break;
}
return;
return;
}
(void) ct_tmpl_clear(tmpl_fd);
return;
}
/* parent waits for child to finish */
if (child != 0) {
ct = -1;
(void) ct_tmpl_clear(tmpl_fd);
(void) contract_abandon_id(ct);
return;
}
/* child enters zone and sets up index file */
(void) ct_tmpl_clear(tmpl_fd);
uuidstr[0] = '\0';
else
uuidstr);
}
}
_exit(0);
}
int
{
char zonepath[MAXPATHLEN];
return (-1);
}
/*
* Before we try to mount filesystems we need to create the
* attribute backing store for /dev
*/
return (-1);
}
/* Make /dev directory owned by root, grouped sys */
0, 3) != 0) {
return (-1);
}
return (-1);
}
if (mount_cmd == Z_MNT_BOOT) {
return (-1);
}
switch (iptype) {
case ZS_SHARED:
/* Always do this to make lo0 get configured */
if (configure_shared_network_interfaces(zlogp) != 0) {
return (-1);
}
break;
case ZS_EXCLUSIVE:
0) {
return (-1);
}
break;
}
}
return (0);
}
static int
{
char zroot[MAXPATHLEN];
return (-1);
}
/*
* At this point, the processes are gone, the filesystems (save the
* root) are unmounted, and the zone is on death row. But there may
* still be creds floating about in the system that reference the
* zone_t, and which pin down zone_rootvp causing this call to fail
* with EBUSY. Thus, we try for a little while before just giving up.
* (How I wish this were not true, and umount2 just did the right
* thing, or tmpfs supported MS_FORCE This is a gross hack.)
*/
goto unmounted;
int tries = 10;
while (--tries >= 0) {
(void) sleep(1);
goto unmounted;
break;
}
}
return (-1);
}
/*
* Only zones in an alternate root environment have scratch zone
* entries.
*/
if (zonecfg_in_alt_root()) {
int retv;
return (-1);
}
retv = -1;
if (zonecfg_lock_scratch(fp) != 0)
else
retv = 0;
return (retv);
} else {
return (0);
}
}
int
{
char *kzone;
int res;
char pool_err[128];
char zpath[MAXPATHLEN];
char cmdbuf[MAXPATHLEN];
char errmsg[DLADM_STRSIZE];
if (zonecfg_in_alt_root()) {
goto error;
}
goto error;
}
}
if (!bringup_failure_recovery)
if (unmount_cmd)
(void) lu_root_teardown(zlogp);
goto error;
}
if (zone_shutdown(zoneid) != 0) {
goto error;
}
/* Get the zonepath of this zone */
goto error;
}
/* Get a handle to the brand info for this zone */
return (-1);
}
/*
* If there is a brand 'halt' callback, execute it now to give the
* brand a chance to cleanup any custom configuration.
*/
"halt callback.");
goto error;
}
goto error;
}
if (!unmount_cmd) {
sizeof (flags)) < 0) {
"ip-type");
goto error;
}
} else {
if (flags & ZF_NET_EXCL)
else
}
switch (iptype) {
case ZS_SHARED:
zoneid) != 0) {
"network interfaces in zone");
goto error;
}
break;
case ZS_EXCLUSIVE:
zoneid) != 0) {
"network interfaces in zone");
goto error;
}
if (status != DLADM_STATUS_OK) {
"dlmgmtd of zone halt: %s",
}
break;
}
}
goto error;
}
"unable to unmount file systems in zone");
goto error;
}
/*
* If we are rebooting then we normally don't want to destroy an
* existing temporary pool at this point so that we can just reuse it
* when the zone boots back up. However, it is also possible we were
* running with a temporary pool and the zone configuration has been
* modified to no longer use a temporary pool. In that case we need
* to destroy the temporary pool now. This case looks like the case
* where we never had a temporary pool configured but
* zonecfg_destroy_tmp_pool will do the right thing either way.
*/
if (!unmount_cmd) {
if (rebooting) {
struct zone_psettab pset_tab;
}
if (destroy_tmp_pool) {
}
}
}
if (zone_destroy(zoneid) != 0) {
goto error;
}
/*
* Special teardown for alternate boot environments: remove the tmpfs
* root for the zone and then remove it from the map file.
*/
goto error;
return (0);
return (-1);
}