Print this page
OS-200 need a better mechanism for storing persistent zone_did
OS-511 make zonecfg device resource extensible, like the net resource
OS-224 add more zonecfg net properties
OS-216 store all net config info on zone
OS-4361 libzonecfg should be aware of branded zone native root
Reviewed by: Robert Mustacchi <rm@joyent.com>
OS-3437 zonecfg failure when pool is full leaves VMs appearing to be missing
OS-399 zone phys. mem. cap should be a rctl and have associated kstat
        
*** 21,30 ****
--- 21,31 ----
  
  /*
   * Copyright 2014 Gary Mills
   * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+  * Copyright 2015 Joyent Inc.
   */
  
  #include <libsysevent.h>
  #include <pthread.h>
  #include <stdlib.h>
*** 57,66 ****
--- 58,69 ----
  #include <auth_attr.h>
  #include <auth_list.h>
  #include <secdb.h>
  #include <user_attr.h>
  #include <prof_attr.h>
+ #include <sys/debug.h>
+ #include <os_dtd.h>
  
  #include <arpa/inet.h>
  #include <netdb.h>
  
  #include <libxml/xmlmemory.h>
*** 77,93 ****
--- 80,99 ----
  #define _PATH_TMPFILE   "/zonecfg.XXXXXX"
  #define ZONE_CB_RETRY_COUNT             10
  #define ZONE_EVENT_PING_SUBCLASS        "ping"
  #define ZONE_EVENT_PING_PUBLISHER       "solaris"
  
+ #define DEBUGID_FILE    "/etc/zones/did.txt"
+ 
  /* Hard-code the DTD element/attribute/entity names just once, here. */
  #define DTD_ELEM_ATTR           (const xmlChar *) "attr"
  #define DTD_ELEM_COMMENT        (const xmlChar *) "comment"
  #define DTD_ELEM_DEVICE         (const xmlChar *) "device"
  #define DTD_ELEM_FS             (const xmlChar *) "filesystem"
  #define DTD_ELEM_FSOPTION       (const xmlChar *) "fsoption"
  #define DTD_ELEM_NET            (const xmlChar *) "network"
+ #define DTD_ELEM_NETATTR        (const xmlChar *) "net-attr"
  #define DTD_ELEM_RCTL           (const xmlChar *) "rctl"
  #define DTD_ELEM_RCTLVALUE      (const xmlChar *) "rctl-value"
  #define DTD_ELEM_ZONE           (const xmlChar *) "zone"
  #define DTD_ELEM_DATASET        (const xmlChar *) "dataset"
  #define DTD_ELEM_TMPPOOL        (const xmlChar *) "tmp_pool"
*** 103,125 ****
--- 109,134 ----
  #define DTD_ATTR_ALLOWED_ADDRESS        (const xmlChar *) "allowed-address"
  #define DTD_ATTR_AUTOBOOT       (const xmlChar *) "autoboot"
  #define DTD_ATTR_IPTYPE         (const xmlChar *) "ip-type"
  #define DTD_ATTR_DEFROUTER      (const xmlChar *) "defrouter"
  #define DTD_ATTR_DIR            (const xmlChar *) "directory"
+ #define DTD_ATTR_GNIC           (const xmlChar *) "global-nic"
  #define DTD_ATTR_LIMIT          (const xmlChar *) "limit"
  #define DTD_ATTR_LIMITPRIV      (const xmlChar *) "limitpriv"
  #define DTD_ATTR_BOOTARGS       (const xmlChar *) "bootargs"
  #define DTD_ATTR_SCHED          (const xmlChar *) "scheduling-class"
+ #define DTD_ATTR_MAC            (const xmlChar *) "mac-addr"
  #define DTD_ATTR_MATCH          (const xmlChar *) "match"
  #define DTD_ATTR_NAME           (const xmlChar *) "name"
  #define DTD_ATTR_PHYSICAL       (const xmlChar *) "physical"
  #define DTD_ATTR_POOL           (const xmlChar *) "pool"
  #define DTD_ATTR_PRIV           (const xmlChar *) "priv"
  #define DTD_ATTR_RAW            (const xmlChar *) "raw"
  #define DTD_ATTR_SPECIAL        (const xmlChar *) "special"
  #define DTD_ATTR_TYPE           (const xmlChar *) "type"
  #define DTD_ATTR_VALUE          (const xmlChar *) "value"
+ #define DTD_ATTR_VLANID         (const xmlChar *) "vlan-id"
  #define DTD_ATTR_ZONEPATH       (const xmlChar *) "zonepath"
  #define DTD_ATTR_NCPU_MIN       (const xmlChar *) "ncpu_min"
  #define DTD_ATTR_NCPU_MAX       (const xmlChar *) "ncpu_max"
  #define DTD_ATTR_IMPORTANCE     (const xmlChar *) "importance"
  #define DTD_ATTR_PHYSCAP        (const xmlChar *) "physcap"
*** 128,137 ****
--- 137,147 ----
  #define DTD_ATTR_UID            (const xmlChar *) "uid"
  #define DTD_ATTR_GID            (const xmlChar *) "gid"
  #define DTD_ATTR_MODE           (const xmlChar *) "mode"
  #define DTD_ATTR_ACL            (const xmlChar *) "acl"
  #define DTD_ATTR_BRAND          (const xmlChar *) "brand"
+ #define DTD_ATTR_DID            (const xmlChar *) "debugid"
  #define DTD_ATTR_HOSTID         (const xmlChar *) "hostid"
  #define DTD_ATTR_USER           (const xmlChar *) "user"
  #define DTD_ATTR_AUTHS          (const xmlChar *) "auths"
  #define DTD_ATTR_FS_ALLOWED     (const xmlChar *) "fs-allowed"
  
*** 174,183 ****
--- 184,195 ----
          {ALIAS_MAXSHMIDS, "zone.max-shm-ids", "privileged", "deny", 0},
          {ALIAS_MAXMSGIDS, "zone.max-msg-ids", "privileged", "deny", 0},
          {ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
          {ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
          {ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
+         {ALIAS_MAXPHYSMEM, "zone.max-physical-memory", "privileged", "deny",
+             1048576},
          {ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
          {ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0},
          {ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100},
          {NULL, NULL, NULL, NULL, 0}
  };
*** 267,289 ****
   * Callers of the _file_path() functions are expected to have the second
   * parameter be a (char foo[MAXPATHLEN]).
   */
  
  static boolean_t
! config_file_path(const char *zonename, char *answer)
  {
!         return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
!             ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
  }
  
  static boolean_t
! snap_file_path(const char *zonename, char *answer)
  {
!         return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
!             zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
  }
  
  /*ARGSUSED*/
  static void
  zonecfg_error_func(void *ctx, const char *msg, ...)
  {
          /*
--- 279,319 ----
   * Callers of the _file_path() functions are expected to have the second
   * parameter be a (char foo[MAXPATHLEN]).
   */
  
  static boolean_t
! file_path_common(const char *zonename, const char *subdir, const char *stem,
!     char *answer, size_t answer_size)
  {
!         const char *native_root = zone_get_nroot();
! 
!         if (native_root == NULL || zonecfg_in_alt_root()) {
!                 /*
!                  * Do not prepend the native system root (e.g. "/native") if an
!                  * alternative configuration root has been selected.
!                  */
!                 native_root = "";
!         }
! 
!         return (snprintf(answer, answer_size, "%s%s%s/%s.%s", native_root,
!             zonecfg_root, subdir, zonename, stem) < answer_size);
  }
  
  static boolean_t
! config_file_path(const char *zonename, char *answer, size_t answer_size)
  {
!         return (file_path_common(zonename, ZONE_CONFIG_ROOT, "xml", answer,
!             answer_size));
  }
  
+ static boolean_t
+ snap_file_path(const char *zonename, char *answer, size_t answer_size)
+ {
+         return (file_path_common(zonename, ZONE_SNAPSHOT_ROOT, "snapshot.xml",
+             answer, answer_size));
+ }
+ 
  /*ARGSUSED*/
  static void
  zonecfg_error_func(void *ctx, const char *msg, ...)
  {
          /*
*** 348,358 ****
          char path[MAXPATHLEN];
          struct zoneent ze;
          int err, state_err;
          zone_state_t state;
  
!         if (!config_file_path(zonename, path))
                  return (Z_MISC_FS);
  
          state_err = zone_get_state((char *)zonename, &state);
          err = access(path, W_OK);
  
--- 378,388 ----
          char path[MAXPATHLEN];
          struct zoneent ze;
          int err, state_err;
          zone_state_t state;
  
!         if (!config_file_path(zonename, path, sizeof (path)))
                  return (Z_MISC_FS);
  
          state_err = zone_get_state((char *)zonename, &state);
          err = access(path, W_OK);
  
*** 405,415 ****
  int
  zonecfg_destroy_snapshot(const char *zonename)
  {
          char path[MAXPATHLEN];
  
!         if (!snap_file_path(zonename, path))
                  return (Z_MISC_FS);
          return (zonecfg_destroy_impl(path));
  }
  
  static int
--- 435,445 ----
  int
  zonecfg_destroy_snapshot(const char *zonename)
  {
          char path[MAXPATHLEN];
  
!         if (!snap_file_path(zonename, path, sizeof (path)))
                  return (Z_MISC_FS);
          return (zonecfg_destroy_impl(path));
  }
  
  static int
*** 572,584 ****
  
  static int
  zonecfg_get_handle_impl(const char *zonename, const char *filename,
      zone_dochandle_t handle)
  {
-         xmlValidCtxtPtr cvp;
          struct stat statbuf;
!         int valid;
  
          if (zonename == NULL)
                  return (Z_NO_ZONE);
  
          if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
--- 602,613 ----
  
  static int
  zonecfg_get_handle_impl(const char *zonename, const char *filename,
      zone_dochandle_t handle)
  {
          struct stat statbuf;
!         boolean_t valid;
  
          if (zonename == NULL)
                  return (Z_NO_ZONE);
  
          if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
*** 585,602 ****
                  /* distinguish file not found vs. found but not parsed */
                  if (stat(filename, &statbuf) == 0)
                          return (Z_INVALID_DOCUMENT);
                  return (Z_NO_ZONE);
          }
!         if ((cvp = xmlNewValidCtxt()) == NULL)
                  return (Z_NOMEM);
!         cvp->error = zonecfg_error_func;
!         cvp->warning = zonecfg_error_func;
!         valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
!         xmlFreeValidCtxt(cvp);
!         if (valid == 0)
                  return (Z_INVALID_DOCUMENT);
  
          /* delete any comments such as inherited Sun copyright / ident str */
          stripcomments(handle);
          return (Z_OK);
  }
--- 614,630 ----
                  /* distinguish file not found vs. found but not parsed */
                  if (stat(filename, &statbuf) == 0)
                          return (Z_INVALID_DOCUMENT);
                  return (Z_NO_ZONE);
          }
! 
!         if (os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid) != 0) {
                  return (Z_NOMEM);
!         }
!         if (!valid) {
                  return (Z_INVALID_DOCUMENT);
+         }
  
          /* delete any comments such as inherited Sun copyright / ident str */
          stripcomments(handle);
          return (Z_OK);
  }
*** 604,614 ****
  int
  zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
  
!         if (!config_file_path(zonename, path))
                  return (Z_MISC_FS);
          handle->zone_dh_newzone = B_FALSE;
  
          return (zonecfg_get_handle_impl(zonename, path, handle));
  }
--- 632,642 ----
  int
  zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
  
!         if (!config_file_path(zonename, path, sizeof (path)))
                  return (Z_MISC_FS);
          handle->zone_dh_newzone = B_FALSE;
  
          return (zonecfg_get_handle_impl(zonename, path, handle));
  }
*** 648,658 ****
  int
  zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
  
!         if (!snap_file_path(zonename, path))
                  return (Z_MISC_FS);
          handle->zone_dh_newzone = B_FALSE;
          return (zonecfg_get_handle_impl(zonename, path, handle));
  }
  
--- 676,686 ----
  int
  zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
  
!         if (!snap_file_path(zonename, path, sizeof (path)))
                  return (Z_MISC_FS);
          handle->zone_dh_newzone = B_FALSE;
          return (zonecfg_get_handle_impl(zonename, path, handle));
  }
  
*** 661,671 ****
      zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
          int err;
  
!         if (!config_file_path(template, path))
                  return (Z_MISC_FS);
  
          if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
                  return (err);
          handle->zone_dh_newzone = B_TRUE;
--- 689,699 ----
      zone_dochandle_t handle)
  {
          char path[MAXPATHLEN];
          int err;
  
!         if (!config_file_path(template, path, sizeof (path)))
                  return (Z_MISC_FS);
  
          if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
                  return (err);
          handle->zone_dh_newzone = B_TRUE;
*** 695,719 ****
   */
  int
  zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
      zone_dochandle_t rem_handle)
  {
!         xmlValidCtxtPtr cvp;
!         int valid;
  
          /* load the manifest into the handle for the remote system */
          if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
                  return (Z_INVALID_DOCUMENT);
          }
!         if ((cvp = xmlNewValidCtxt()) == NULL)
                  return (Z_NOMEM);
!         cvp->error = zonecfg_error_func;
!         cvp->warning = zonecfg_error_func;
!         valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc);
!         xmlFreeValidCtxt(cvp);
!         if (valid == 0)
                  return (Z_INVALID_DOCUMENT);
  
          /* delete any comments such as inherited Sun copyright / ident str */
          stripcomments(rem_handle);
  
          rem_handle->zone_dh_newzone = B_TRUE;
--- 723,745 ----
   */
  int
  zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
      zone_dochandle_t rem_handle)
  {
!         boolean_t valid;
  
          /* load the manifest into the handle for the remote system */
          if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
                  return (Z_INVALID_DOCUMENT);
          }
! 
!         if (os_dtd_validate(rem_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
                  return (Z_NOMEM);
!         }
!         if (!valid) {
                  return (Z_INVALID_DOCUMENT);
+         }
  
          /* delete any comments such as inherited Sun copyright / ident str */
          stripcomments(rem_handle);
  
          rem_handle->zone_dh_newzone = B_TRUE;
*** 730,747 ****
  
          /*
           * We need to re-run xmlValidateDocument on local_handle to properly
           * update the in-core representation of the configuration.
           */
!         if ((cvp = xmlNewValidCtxt()) == NULL)
                  return (Z_NOMEM);
!         cvp->error = zonecfg_error_func;
!         cvp->warning = zonecfg_error_func;
!         valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc);
!         xmlFreeValidCtxt(cvp);
!         if (valid == 0)
                  return (Z_INVALID_DOCUMENT);
  
          strip_sw_inv(local_handle);
  
          local_handle->zone_dh_newzone = B_TRUE;
          local_handle->zone_dh_sw_inv = B_FALSE;
--- 756,771 ----
  
          /*
           * We need to re-run xmlValidateDocument on local_handle to properly
           * update the in-core representation of the configuration.
           */
!         if (os_dtd_validate(local_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
                  return (Z_NOMEM);
!         }
!         if (!valid) {
                  return (Z_INVALID_DOCUMENT);
+         }
  
          strip_sw_inv(local_handle);
  
          local_handle->zone_dh_newzone = B_TRUE;
          local_handle->zone_dh_sw_inv = B_FALSE;
*** 1191,1203 ****
  static int
  zonecfg_save_impl(zone_dochandle_t handle, char *filename)
  {
          char tmpfile[MAXPATHLEN];
          char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
!         int tmpfd, err, valid;
!         xmlValidCtxt cvp = { NULL };
          boolean_t backup;
  
          (void) strlcpy(tmpfile, filename, sizeof (tmpfile));
          (void) dirname(tmpfile);
          (void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
  
--- 1215,1227 ----
  static int
  zonecfg_save_impl(zone_dochandle_t handle, char *filename)
  {
          char tmpfile[MAXPATHLEN];
          char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
!         int tmpfd, err;
          boolean_t backup;
+         boolean_t valid;
  
          (void) strlcpy(tmpfile, filename, sizeof (tmpfile));
          (void) dirname(tmpfile);
          (void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
  
*** 1206,1225 ****
                  (void) unlink(tmpfile);
                  return (Z_TEMP_FILE);
          }
          (void) close(tmpfd);
  
-         cvp.error = zonecfg_error_func;
-         cvp.warning = zonecfg_error_func;
- 
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
!         assert(valid != 0);
  
          if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
                  goto err;
  
          (void) chmod(tmpfile, 0644);
--- 1230,1246 ----
                  (void) unlink(tmpfile);
                  return (Z_TEMP_FILE);
          }
          (void) close(tmpfd);
  
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
!         VERIFY(valid == B_TRUE);
  
          if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
                  goto err;
  
          (void) chmod(tmpfile, 0644);
*** 1270,1280 ****
          if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) {
                  if (backup) {
                          /*
                           * Try to restore from our backup.
                           */
-                         (void) unlink(filename);
                          (void) rename(bakfile, filename);
                  } else {
                          /*
                           * Either the zone is new, in which case we can delete
                           * new.xml, or we're doing a rename, so ditto.
--- 1291,1300 ----
*** 1313,1323 ****
                  return (Z_INVAL);
  
          if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
                  return (err);
  
!         if (!config_file_path(zname, path))
                  return (Z_MISC_FS);
  
          addcomment(handle, "\n    DO NOT EDIT THIS "
              "FILE.  Use zonecfg(1M) instead.\n");
  
--- 1333,1343 ----
                  return (Z_INVAL);
  
          if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
                  return (err);
  
!         if (!config_file_path(zname, path, sizeof (path)))
                  return (Z_MISC_FS);
  
          addcomment(handle, "\n    DO NOT EDIT THIS "
              "FILE.  Use zonecfg(1M) instead.\n");
  
*** 1334,1371 ****
                  return (err);
  
          handle->zone_dh_newzone = B_FALSE;
  
          if (is_renaming(handle)) {
!                 if (config_file_path(handle->zone_dh_delete_name, delpath))
                          (void) unlink(delpath);
                  handle->zone_dh_delete_name[0] = '\0';
          }
  
          return (Z_OK);
  }
  
  int
  zonecfg_verify_save(zone_dochandle_t handle, char *filename)
  {
!         int valid;
  
-         xmlValidCtxt cvp = { NULL };
- 
          if (zonecfg_check_handle(handle) != Z_OK)
                  return (Z_BAD_HANDLE);
  
-         cvp.error = zonecfg_error_func;
-         cvp.warning = zonecfg_error_func;
- 
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
!         assert(valid != 0);
  
          if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
                  return (Z_SAVING_FILE);
  
          return (Z_OK);
--- 1354,1388 ----
                  return (err);
  
          handle->zone_dh_newzone = B_FALSE;
  
          if (is_renaming(handle)) {
!                 if (config_file_path(handle->zone_dh_delete_name, delpath,
!                     sizeof (delpath))) {
                          (void) unlink(delpath);
+                 }
                  handle->zone_dh_delete_name[0] = '\0';
          }
  
          return (Z_OK);
  }
  
  int
  zonecfg_verify_save(zone_dochandle_t handle, char *filename)
  {
!         boolean_t valid;
  
          if (zonecfg_check_handle(handle) != Z_OK)
                  return (Z_BAD_HANDLE);
  
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
!         VERIFY(valid == B_TRUE);
  
          if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
                  return (Z_SAVING_FILE);
  
          return (Z_OK);
*** 1375,1387 ****
  zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
  {
          char zname[ZONENAME_MAX];
          char path[MAXPATHLEN];
          char migpath[MAXPATHLEN];
-         xmlValidCtxt cvp = { NULL };
          int err = Z_SAVING_FILE;
!         int valid;
  
          if (zonecfg_check_handle(handle) != Z_OK)
                  return (Z_BAD_HANDLE);
  
          if (flags & ZONE_DRY_RUN) {
--- 1392,1403 ----
  zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
  {
          char zname[ZONENAME_MAX];
          char path[MAXPATHLEN];
          char migpath[MAXPATHLEN];
          int err = Z_SAVING_FILE;
!         boolean_t valid;
  
          if (zonecfg_check_handle(handle) != Z_OK)
                  return (Z_BAD_HANDLE);
  
          if (flags & ZONE_DRY_RUN) {
*** 1404,1423 ****
                  return (err);
  
          addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
              "Use zonecfg(1M) and zoneadm(1M) attach.\n");
  
-         cvp.error = zonecfg_error_func;
-         cvp.warning = zonecfg_error_func;
- 
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
!         assert(valid != 0);
  
          if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
                  return (Z_SAVING_FILE);
  
          if (!(flags & ZONE_DRY_RUN))
--- 1420,1436 ----
                  return (err);
  
          addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
              "Use zonecfg(1M) and zoneadm(1M) attach.\n");
  
          /*
           * We do a final validation of the document.  Since the library has
           * malfunctioned if it fails to validate, we follow-up with an
           * assert() that the doc is valid.
           */
!         VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
!         VERIFY(valid == B_TRUE);
  
          if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
                  return (Z_SAVING_FILE);
  
          if (!(flags & ZONE_DRY_RUN))
*** 1486,1496 ****
  int
  zonecfg_access(const char *zonename, int amode)
  {
          char path[MAXPATHLEN];
  
!         if (!config_file_path(zonename, path))
                  return (Z_INVAL);
          if (access(path, amode) == 0)
                  return (Z_OK);
          if (errno == ENOENT) {
                  if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
--- 1499,1509 ----
  int
  zonecfg_access(const char *zonename, int amode)
  {
          char path[MAXPATHLEN];
  
!         if (!config_file_path(zonename, path, sizeof (path)))
                  return (Z_INVAL);
          if (access(path, amode) == 0)
                  return (Z_OK);
          if (errno == ENOENT) {
                  if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
*** 1551,1561 ****
          if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
                  error = Z_MISC_FS;
                  goto out;
          }
  
!         if (!snap_file_path(zonename, path)) {
                  error = Z_MISC_FS;
                  goto out;
          }
  
          addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
--- 1564,1574 ----
          if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
                  error = Z_MISC_FS;
                  goto out;
          }
  
!         if (!snap_file_path(zonename, path, sizeof (path))) {
                  error = Z_MISC_FS;
                  goto out;
          }
  
          addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
*** 2082,2098 ****
   * address, and default router information are stored in 'tabptr'.
   */
  int
  zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr cur;
          xmlNodePtr firstmatch;
          int err;
          char address[INET6_ADDRSTRLEN];
          char physical[LIFNAMSIZ];
          size_t addrspec;                /* nonzero if tabptr has IP addr */
          size_t physspec;                /* nonzero if tabptr has interface */
          size_t defrouterspec;           /* nonzero if tabptr has def. router */
          size_t allowed_addrspec;
          zone_iptype_t iptype;
  
          if (tabptr == NULL)
--- 2095,2115 ----
   * address, and default router information are stored in 'tabptr'.
   */
  int
  zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr cur, val;
          xmlNodePtr firstmatch;
          int err;
          char address[INET6_ADDRSTRLEN];
          char physical[LIFNAMSIZ];
+         char mac[MAXMACADDRLEN];
+         char gnic[LIFNAMSIZ];
          size_t addrspec;                /* nonzero if tabptr has IP addr */
          size_t physspec;                /* nonzero if tabptr has interface */
+         size_t macspec;                 /* nonzero if tabptr has mac addr */
+         size_t gnicspec;                /* nonzero if tabptr has gnic */
          size_t defrouterspec;           /* nonzero if tabptr has def. router */
          size_t allowed_addrspec;
          zone_iptype_t iptype;
  
          if (tabptr == NULL)
*** 2100,2120 ****
  
          /*
           * Determine the fields that will be searched.  There must be at least
           * one.
           *
!          * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
           * arrays, so no NULL checks are necessary.
           */
          addrspec = strlen(tabptr->zone_nwif_address);
          physspec = strlen(tabptr->zone_nwif_physical);
          defrouterspec = strlen(tabptr->zone_nwif_defrouter);
          allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
          if (addrspec != 0 && allowed_addrspec != 0)
                  return (Z_INVAL); /* can't specify both */
          if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
!             allowed_addrspec == 0)
                  return (Z_INSUFFICIENT_SPEC);
  
          if ((err = operation_prep(handle)) != Z_OK)
                  return (err);
  
--- 2117,2140 ----
  
          /*
           * Determine the fields that will be searched.  There must be at least
           * one.
           *
!          * zone_nwif_address, zone_nwif_physical, zone_nwif_defrouter,
!          * zone_nwif_mac, zone_nwif_vlan_id and zone_nwif_gnic  are
           * arrays, so no NULL checks are necessary.
           */
          addrspec = strlen(tabptr->zone_nwif_address);
          physspec = strlen(tabptr->zone_nwif_physical);
+         macspec = strlen(tabptr->zone_nwif_mac);
+         gnicspec = strlen(tabptr->zone_nwif_gnic);
          defrouterspec = strlen(tabptr->zone_nwif_defrouter);
          allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
          if (addrspec != 0 && allowed_addrspec != 0)
                  return (Z_INVAL); /* can't specify both */
          if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
!             allowed_addrspec == 0 && macspec == 0 && gnicspec == 0)
                  return (Z_INSUFFICIENT_SPEC);
  
          if ((err = operation_prep(handle)) != Z_OK)
                  return (err);
  
*** 2137,2146 ****
--- 2157,2175 ----
                   */
                  if (physspec != 0 && (fetchprop(cur, DTD_ATTR_PHYSICAL,
                      physical, sizeof (physical)) != Z_OK ||
                      strcmp(tabptr->zone_nwif_physical, physical) != 0))
                          continue;
+                 if (iptype == ZS_EXCLUSIVE && macspec != 0 &&
+                     (fetchprop(cur, DTD_ATTR_MAC, mac, sizeof (mac)) != Z_OK ||
+                     strcmp(tabptr->zone_nwif_mac, mac) != 0))
+                         continue;
+                 if (iptype == ZS_EXCLUSIVE && gnicspec != 0 &&
+                     (fetchprop(cur, DTD_ATTR_GNIC, gnic,
+                     sizeof (gnic)) != Z_OK ||
+                     strcmp(tabptr->zone_nwif_gnic, gnic) != 0))
+                         continue;
                  if (iptype == ZS_SHARED && addrspec != 0 &&
                      (fetchprop(cur, DTD_ATTR_ADDRESS, address,
                      sizeof (address)) != Z_OK ||
                      !zonecfg_same_net_address(tabptr->zone_nwif_address,
                      address)))
*** 2179,2188 ****
--- 2208,2232 ----
              (err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
              sizeof (tabptr->zone_nwif_address))) != Z_OK)
                  return (err);
  
          if (iptype == ZS_EXCLUSIVE &&
+             (err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+             sizeof (tabptr->zone_nwif_mac))) != Z_OK)
+                 return (err);
+ 
+         if (iptype == ZS_EXCLUSIVE &&
+             (err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+             sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK)
+                 return (err);
+ 
+         if (iptype == ZS_EXCLUSIVE &&
+             (err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+             sizeof (tabptr->zone_nwif_gnic))) != Z_OK)
+                 return (err);
+ 
+         if (iptype == ZS_EXCLUSIVE &&
              (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
              tabptr->zone_nwif_allowed_address,
              sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK)
                  return (err);
  
*** 2189,2205 ****
          if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
              tabptr->zone_nwif_defrouter,
              sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
                  return (err);
  
          return (Z_OK);
  }
  
  static int
  zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr newnode, cur = handle->zone_dh_cur;
          int err;
  
          newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
          if (strlen(tabptr->zone_nwif_address) > 0 &&
              (err = newprop(newnode, DTD_ATTR_ADDRESS,
--- 2233,2276 ----
          if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
              tabptr->zone_nwif_defrouter,
              sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
                  return (err);
  
+         tabptr->zone_nwif_attrp = NULL;
+         for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+                 struct zone_res_attrtab *valptr;
+ 
+                 valptr = (struct zone_res_attrtab *)malloc(
+                     sizeof (struct zone_res_attrtab));
+                 if (valptr == NULL)
+                         return (Z_NOMEM);
+ 
+                 valptr->zone_res_attr_name[0] =
+                     valptr->zone_res_attr_value[0] = '\0';
+                 if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+                     != Z_OK) {
+                         free(valptr);
+                         break;
+                 }
+ 
+                 if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+                     sizeof (valptr->zone_res_attr_name)) != Z_OK))
+                         break;
+                 if ((fetchprop(val, DTD_ATTR_VALUE,
+                     valptr->zone_res_attr_value,
+                     sizeof (valptr->zone_res_attr_value)) != Z_OK))
+                         break;
+         }
+ 
          return (Z_OK);
  }
  
  static int
  zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
!         struct zone_res_attrtab *valptr;
          int err;
  
          newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
          if (strlen(tabptr->zone_nwif_address) > 0 &&
              (err = newprop(newnode, DTD_ATTR_ADDRESS,
*** 2211,2227 ****
                  return (err);
          if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
              tabptr->zone_nwif_physical)) != Z_OK)
                  return (err);
          /*
!          * Do not add this property when it is not set, for backwards
!          * compatibility and because it is optional.
           */
          if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
              ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
              tabptr->zone_nwif_defrouter)) != Z_OK))
                  return (err);
          return (Z_OK);
  }
  
  int
  zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
--- 2282,2325 ----
                  return (err);
          if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
              tabptr->zone_nwif_physical)) != Z_OK)
                  return (err);
          /*
!          * Do not add these properties when they are not set, for backwards
!          * compatibility and because they are optional.
           */
          if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
              ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
              tabptr->zone_nwif_defrouter)) != Z_OK))
                  return (err);
+         if (strlen(tabptr->zone_nwif_mac) > 0 &&
+             (err = newprop(newnode, DTD_ATTR_MAC,
+             tabptr->zone_nwif_mac)) != Z_OK)
+                 return (err);
+         if (strlen(tabptr->zone_nwif_vlan_id) > 0 &&
+             (err = newprop(newnode, DTD_ATTR_VLANID,
+             tabptr->zone_nwif_vlan_id)) != Z_OK)
+                 return (err);
+         if (strlen(tabptr->zone_nwif_gnic) > 0 &&
+             (err = newprop(newnode, DTD_ATTR_GNIC,
+             tabptr->zone_nwif_gnic)) != Z_OK)
+                 return (err);
+ 
+         for (valptr = tabptr->zone_nwif_attrp; valptr != NULL;
+             valptr = valptr->zone_res_attr_next) {
+                 valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+                     NULL);
+                 err = newprop(valnode, DTD_ATTR_NAME,
+                     valptr->zone_res_attr_name);
+                 if (err != Z_OK)
+                         return (err);
+                 err = newprop(valnode, DTD_ATTR_VALUE,
+                     valptr->zone_res_attr_value);
+                 if (err != Z_OK)
+                         return (err);
+         }
+ 
          return (Z_OK);
  }
  
  int
  zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
*** 2242,2252 ****
  
  static int
  zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
          xmlNodePtr cur = handle->zone_dh_cur;
!         boolean_t addr_match, phys_match, allowed_addr_match;
  
          for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
                  if (xmlStrcmp(cur->name, DTD_ELEM_NET))
                          continue;
  
--- 2340,2351 ----
  
  static int
  zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
          xmlNodePtr cur = handle->zone_dh_cur;
!         boolean_t addr_match, phys_match, allowed_addr_match, mac_match,
!             gnic_match;
  
          for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
                  if (xmlStrcmp(cur->name, DTD_ELEM_NET))
                          continue;
  
*** 2254,2265 ****
                      tabptr->zone_nwif_address);
                  allowed_addr_match = match_prop(cur, DTD_ATTR_ALLOWED_ADDRESS,
                      tabptr->zone_nwif_allowed_address);
                  phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
                      tabptr->zone_nwif_physical);
  
!                 if (addr_match && allowed_addr_match && phys_match) {
                          xmlUnlinkNode(cur);
                          xmlFreeNode(cur);
                          return (Z_OK);
                  }
          }
--- 2353,2369 ----
                      tabptr->zone_nwif_address);
                  allowed_addr_match = match_prop(cur, DTD_ATTR_ALLOWED_ADDRESS,
                      tabptr->zone_nwif_allowed_address);
                  phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
                      tabptr->zone_nwif_physical);
+                 mac_match = match_prop(cur, DTD_ATTR_MAC,
+                     tabptr->zone_nwif_mac);
+                 gnic_match = match_prop(cur, DTD_ATTR_GNIC,
+                     tabptr->zone_nwif_gnic);
  
!                 if ((addr_match || allowed_addr_match || mac_match ||
!                     gnic_match) && phys_match) {
                          xmlUnlinkNode(cur);
                          xmlFreeNode(cur);
                          return (Z_OK);
                  }
          }
*** 2304,2313 ****
--- 2408,2469 ----
                  return (err);
  
          return (Z_OK);
  }
  
+ void
+ zonecfg_free_res_attr_list(struct zone_res_attrtab *valtab)
+ {
+         if (valtab == NULL)
+                 return;
+         zonecfg_free_res_attr_list(valtab->zone_res_attr_next);
+         free(valtab);
+ }
+ 
+ int
+ zonecfg_add_res_attr(struct zone_res_attrtab **headptr,
+     struct zone_res_attrtab *valtabptr)
+ {
+         struct zone_res_attrtab *last, *old, *new;
+ 
+         last = *headptr;
+         for (old = last; old != NULL; old = old->zone_res_attr_next)
+                 last = old;     /* walk to the end of the list */
+         new = valtabptr;        /* alloc'd by caller */
+         new->zone_res_attr_next = NULL;
+         if (last == NULL)
+                 *headptr = new;
+         else
+                 last->zone_res_attr_next = new;
+         return (Z_OK);
+ }
+ 
+ int
+ zonecfg_remove_res_attr(struct zone_res_attrtab **headptr,
+     struct zone_res_attrtab *valtabptr)
+ {
+         struct zone_res_attrtab *last, *this, *next;
+ 
+         last = *headptr;
+         for (this = last; this != NULL; this = this->zone_res_attr_next) {
+                 if (strcmp(this->zone_res_attr_name,
+                     valtabptr->zone_res_attr_name) == 0 &&
+                     strcmp(this->zone_res_attr_value,
+                     valtabptr->zone_res_attr_value) == 0) {
+                         next = this->zone_res_attr_next;
+                         if (this == *headptr)
+                                 *headptr = next;
+                         else
+                                 last->zone_res_attr_next = next;
+                         free(this);
+                         return (Z_OK);
+                 } else
+                         last = this;
+         }
+         return (Z_NO_PROPERTY_ID);
+ }
+ 
  /*
   * Must be a comma-separated list of alpha-numeric file system names.
   */
  static int
  zonecfg_valid_fs_allowed(const char *fsallowedp)
*** 2453,2463 ****
  }
  
  int
  zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr cur, firstmatch;
          int err;
          char match[MAXPATHLEN];
  
          if (tabptr == NULL)
                  return (Z_INVAL);
--- 2609,2619 ----
  }
  
  int
  zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr cur, val, firstmatch;
          int err;
          char match[MAXPATHLEN];
  
          if (tabptr == NULL)
                  return (Z_INVAL);
*** 2498,2522 ****
  
          if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
              sizeof (tabptr->zone_dev_match))) != Z_OK)
                  return (err);
  
          return (Z_OK);
  }
  
  static int
  zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr newnode, cur = handle->zone_dh_cur;
          int err;
  
          newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
  
          if ((err = newprop(newnode, DTD_ATTR_MATCH,
              tabptr->zone_dev_match)) != Z_OK)
                  return (err);
  
          return (Z_OK);
  }
  
  int
  zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
--- 2654,2720 ----
  
          if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
              sizeof (tabptr->zone_dev_match))) != Z_OK)
                  return (err);
  
+         tabptr->zone_dev_attrp = NULL;
+         for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+                 struct zone_res_attrtab *valptr;
+ 
+                 valptr = (struct zone_res_attrtab *)malloc(
+                     sizeof (struct zone_res_attrtab));
+                 if (valptr == NULL)
+                         return (Z_NOMEM);
+ 
+                 valptr->zone_res_attr_name[0] =
+                     valptr->zone_res_attr_value[0] = '\0';
+                 if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+                     != Z_OK) {
+                         free(valptr);
+                         break;
+                 }
+ 
+                 if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+                     sizeof (valptr->zone_res_attr_name)) != Z_OK))
+                         break;
+                 if ((fetchprop(val, DTD_ATTR_VALUE,
+                     valptr->zone_res_attr_value,
+                     sizeof (valptr->zone_res_attr_value)) != Z_OK))
+                         break;
+         }
+ 
          return (Z_OK);
  }
  
  static int
  zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
!         struct zone_res_attrtab *valptr;
          int err;
  
          newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
  
          if ((err = newprop(newnode, DTD_ATTR_MATCH,
              tabptr->zone_dev_match)) != Z_OK)
                  return (err);
  
+         for (valptr = tabptr->zone_dev_attrp; valptr != NULL;
+             valptr = valptr->zone_res_attr_next) {
+                 valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+                     NULL);
+                 err = newprop(valnode, DTD_ATTR_NAME,
+                     valptr->zone_res_attr_name);
+                 if (err != Z_OK)
+                         return (err);
+                 err = newprop(valnode, DTD_ATTR_VALUE,
+                     valptr->zone_res_attr_value);
+                 if (err != Z_OK)
+                         return (err);
+         }
+ 
+ 
          return (Z_OK);
  }
  
  int
  zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
*** 4710,4720 ****
  }
  
  int
  zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr cur;
          int err;
  
          if (handle == NULL)
                  return (Z_INVAL);
  
--- 4908,4919 ----
  }
  
  int
  zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
  {
!         xmlNodePtr cur, val;
!         struct zone_res_attrtab *valptr;
          int err;
  
          if (handle == NULL)
                  return (Z_INVAL);
  
*** 4746,4762 ****
--- 4945,5002 ----
              sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
                  handle->zone_dh_cur = handle->zone_dh_top;
                  return (err);
          }
  
+         if ((err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+             sizeof (tabptr->zone_nwif_mac))) != Z_OK) {
+                 handle->zone_dh_cur = handle->zone_dh_top;
+                 return (err);
+         }
+ 
+         if ((err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+             sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) {
+                 handle->zone_dh_cur = handle->zone_dh_top;
+                 return (err);
+         }
+ 
+         if ((err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+             sizeof (tabptr->zone_nwif_gnic))) != Z_OK) {
+                 handle->zone_dh_cur = handle->zone_dh_top;
+                 return (err);
+         }
+ 
          if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
              tabptr->zone_nwif_defrouter,
              sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
                  handle->zone_dh_cur = handle->zone_dh_top;
                  return (err);
          }
  
+         tabptr->zone_nwif_attrp = NULL;
+         for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+                 valptr = (struct zone_res_attrtab *)malloc(
+                     sizeof (struct zone_res_attrtab));
+                 if (valptr == NULL)
+                         return (Z_NOMEM);
+ 
+                 valptr->zone_res_attr_name[0] =
+                     valptr->zone_res_attr_value[0] = '\0';
+                 if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+                     != Z_OK) {
+                         free(valptr);
+                         break;
+                 }
+ 
+                 if (fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+                     sizeof (valptr->zone_res_attr_name)) != Z_OK)
+                         break;
+                 if (fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+                     sizeof (valptr->zone_res_attr_value)) != Z_OK)
+                         break;
+         }
+ 
          handle->zone_dh_cur = cur->next;
          return (Z_OK);
  }
  
  int
*** 4772,4782 ****
  }
  
  int
  zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr cur;
          int err;
  
          if (handle == NULL)
                  return (Z_INVAL);
  
--- 5012,5022 ----
  }
  
  int
  zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
  {
!         xmlNodePtr cur, val;
          int err;
  
          if (handle == NULL)
                  return (Z_INVAL);
  
*** 4795,4804 ****
--- 5035,5069 ----
              sizeof (tabptr->zone_dev_match))) != Z_OK) {
                  handle->zone_dh_cur = handle->zone_dh_top;
                  return (err);
          }
  
+         tabptr->zone_dev_attrp = NULL;
+         for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+                 struct zone_res_attrtab *valptr;
+ 
+                 valptr = (struct zone_res_attrtab *)malloc(
+                     sizeof (struct zone_res_attrtab));
+                 if (valptr == NULL)
+                         return (Z_NOMEM);
+ 
+                 valptr->zone_res_attr_name[0] =
+                     valptr->zone_res_attr_value[0] = '\0';
+                 if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+                     != Z_OK) {
+                         free(valptr);
+                         break;
+                 }
+ 
+                 if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+                     sizeof (valptr->zone_res_attr_name)) != Z_OK))
+                         break;
+                 if ((fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+                     sizeof (valptr->zone_res_attr_value)) != Z_OK))
+                         break;
+         }
+ 
          handle->zone_dh_cur = cur->next;
          return (Z_OK);
  }
  
  int
*** 5523,5532 ****
--- 5788,5955 ----
          zonecfg_fini_handle(handle);
          return (err);
  }
  
  /*
+  * Atomically get a new zone_did value.  The currently allocated value
+  * is stored in /etc/zones/did.txt.  Lock the file, read the current value,
+  * increment, save the new value and unlock the file.  Return the new value
+  * or -1 if there was an error.  The ID namespace is large enough that we
+  * don't worry about recycling an ID when a zone is deleted.
+  */
+ static zoneid_t
+ new_zone_did()
+ {
+         int fd;
+         int len;
+         int val;
+         struct flock lck;
+         char buf[80];
+ 
+         if ((fd = open(DEBUGID_FILE, O_RDWR | O_CREAT,
+             S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+                 perror("new_zone_did open failed");
+                 return (-1);
+         }
+ 
+         /* Initialize the lock. */
+         lck.l_whence = SEEK_SET;
+         lck.l_start = 0;
+         lck.l_len = 0;
+ 
+         /* Wait until we acquire an exclusive lock on the file. */
+         lck.l_type = F_WRLCK;
+         if (fcntl(fd, F_SETLKW, &lck) == -1) {
+                 perror("new_zone_did lock failed");
+                 (void) close(fd);
+                 return (-1);
+         }
+ 
+         /* Get currently allocated value */
+         len = read(fd, buf, sizeof (buf));
+         if (len == -1) {
+                 perror("new_zone_did read failed");
+                 val = -1;
+         } else {
+                 if (lseek(fd, 0L, SEEK_SET) == -1) {
+                         perror("new_zone_did seek failed");
+                         val = -1;
+                 } else {
+                         if (len == 0) {
+                                 /* Just created the file, initialize at 1 */
+                                 val = 1;
+                         } else {
+                                 val = atoi(buf);
+                                 val++;
+                         }
+ 
+                         (void) snprintf(buf, sizeof (buf), "%d\n", val);
+                         len = strlen(buf);
+ 
+                         /* Save newly allocated value */
+                         if (write(fd, buf, len) == -1) {
+                                 perror("new_zone_did write failed");
+                                 val = -1;
+                         }
+                 }
+         }
+ 
+         /* Release the file lock. */
+         lck.l_type = F_UNLCK;
+         if (fcntl(fd, F_SETLK, &lck) == -1) {
+                 perror("new_zone_did unlock failed");
+                 val = -1;
+         }
+ 
+         if (close(fd) != 0)
+                 perror("new_zone_did close failed");
+ 
+         return (val);
+ }
+ 
+ /*
+  * Called by zoneadmd to get the zone's debug ID.
+  * If the zone doesn't already have an ID, a new one is generated and
+  * persistently saved onto the zone.  Normally either zoneadm or zonecfg
+  * will assign a new ID for the zone, so zoneadmd should never have to
+  * generate one, but we also handle that here just to be paranoid.
+  */
+ zoneid_t
+ zone_get_did(char *zone_name)
+ {
+         int res;
+         zoneid_t new_did;
+         zone_dochandle_t handle;
+         char did_str[80];
+ 
+         if ((handle = zonecfg_init_handle()) == NULL)
+                 return (getpid());
+ 
+         if (zonecfg_get_handle((char *)zone_name, handle) != Z_OK)
+                 return (getpid());
+ 
+         res = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+ 
+         /* If the zone already has an assigned debug ID, return it. */
+         if (res == Z_OK && did_str[0] != '\0') {
+                 zonecfg_fini_handle(handle);
+                 return (atoi(did_str));
+         }
+ 
+         /*
+          * The zone doesn't have an assigned debug ID yet, generate one and
+          * save it as part of the zone definition.
+          */
+         if ((new_did = new_zone_did()) == -1) {
+                 /*
+                  * We should really never hit this block of code.
+                  * Generating a new ID failed for some reason.  Use the current
+                  * pid as a temporary ID so that the zone can continue to boot
+                  * but we don't persistently save this temporary ID on the zone.
+                  */
+                 zonecfg_fini_handle(handle);
+                 return (getpid());
+         }
+ 
+         /* Now persistently save this new ID onto the zone. */
+         (void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+         (void) setrootattr(handle, DTD_ATTR_DID, did_str);
+         (void) zonecfg_save(handle);
+ 
+         zonecfg_fini_handle(handle);
+         return (new_did);
+ }
+ 
+ zoneid_t
+ zonecfg_get_did(zone_dochandle_t handle)
+ {
+         char did_str[80];
+         int err;
+         zoneid_t did;
+ 
+         err = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+         if (err == Z_OK && did_str[0] != '\0')
+                 did = atoi(did_str);
+         else
+                 did = -1;
+ 
+         return (did);
+ }
+ 
+ void
+ zonecfg_set_did(zone_dochandle_t handle)
+ {
+         zoneid_t new_did;
+         char did_str[80];
+ 
+         if ((new_did = new_zone_did()) == -1)
+                 return;
+         (void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+         (void) setrootattr(handle, DTD_ATTR_DID, did_str);
+ }
+ 
+ /*
   * Return the appropriate root for the active /dev.
   * For normal zone, the path is $ZONEPATH/root;
   * for scratch zone, the dev path is $ZONEPATH/lu.
   */
  int
*** 6837,6973 ****
          (void) zonecfg_endent(handle);
  
          return (err);
  }
  
! static int
! add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
! {
!         xmlNodePtr newnode, cur = handle->zone_dh_cur;
!         int err;
! 
!         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
!         if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
!             != Z_OK)
!                 return (err);
! 
!         return (Z_OK);
! }
! 
  int
! zonecfg_delete_mcap(zone_dochandle_t handle)
  {
!         int err;
!         xmlNodePtr cur = handle->zone_dh_cur;
! 
!         if ((err = operation_prep(handle)) != Z_OK)
!                 return (err);
! 
!         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
!                 if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
!                         continue;
! 
!                 xmlUnlinkNode(cur);
!                 xmlFreeNode(cur);
!                 return (Z_OK);
!         }
!         return (Z_NO_RESOURCE_ID);
! }
! 
! int
! zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
! {
!         int err;
! 
!         if (tabptr == NULL)
!                 return (Z_INVAL);
! 
!         err = zonecfg_delete_mcap(handle);
!         /* it is ok if there is no mcap entry */
!         if (err != Z_OK && err != Z_NO_RESOURCE_ID)
!                 return (err);
! 
!         if ((err = add_mcap(handle, tabptr)) != Z_OK)
!                 return (err);
! 
!         return (Z_OK);
! }
! 
! int
! zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
! {
          xmlNodePtr cur;
!         int err;
  
!         if (tabptr == NULL)
!                 return (Z_INVAL);
  
!         if ((err = operation_prep(handle)) != Z_OK)
!                 return (err);
! 
          cur = handle->zone_dh_cur;
          for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
                  if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
                          continue;
-                 if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
-                     tabptr->zone_physmem_cap,
-                     sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
-                         handle->zone_dh_cur = handle->zone_dh_top;
-                         return (err);
-                 }
  
!                 return (Z_OK);
          }
  
!         return (Z_NO_ENTRY);
! }
! 
! static int
! getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
! {
!         xmlNodePtr cur;
!         int err;
! 
!         if (handle == NULL)
!                 return (Z_INVAL);
! 
!         if ((cur = handle->zone_dh_cur) == NULL)
!                 return (Z_NO_ENTRY);
! 
!         for (; cur != NULL; cur = cur->next)
!                 if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
                          break;
-         if (cur == NULL) {
-                 handle->zone_dh_cur = handle->zone_dh_top;
-                 return (Z_NO_ENTRY);
          }
  
!         if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
!             sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
!                 handle->zone_dh_cur = handle->zone_dh_top;
!                 return (err);
          }
  
!         handle->zone_dh_cur = cur->next;
!         return (Z_OK);
  }
  
- int
- zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
- {
-         int err;
- 
-         if ((err = zonecfg_setent(handle)) != Z_OK)
-                 return (err);
- 
-         err = getmcapent_core(handle, tabptr);
- 
-         (void) zonecfg_endent(handle);
- 
-         return (err);
- }
- 
  /*
   * Get the full tree of pkg metadata in a set of nested AVL trees.
   * pkgs_avl is an AVL tree of pkgs.
   *
   * The zone xml data contains DTD_ELEM_PACKAGE elements.
--- 7260,7314 ----
          (void) zonecfg_endent(handle);
  
          return (err);
  }
  
! /*
!  * Cleanup obsolete constructs in the configuration.
!  * Return true of the config has been updated and must be commited.
!  */
  int
! zonecfg_fix_obsolete(zone_dochandle_t handle)
  {
!         int res = 0;
!         int add_physmem_rctl = 0;
          xmlNodePtr cur;
!         char    zone_physmem_cap[MAXNAMELEN];
  
!         if (operation_prep(handle) != Z_OK)
!                 return (res);
  
!         /*
!          * If an obsolete mcap entry exists, convert it to the rctl.
!          */
          cur = handle->zone_dh_cur;
          for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
                  if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
                          continue;
  
!                 if (fetchprop(cur, DTD_ATTR_PHYSCAP,
!                     zone_physmem_cap, sizeof (zone_physmem_cap)) == Z_OK) {
!                         res = 1;
!                         add_physmem_rctl = 1;
                  }
  
!                 xmlUnlinkNode(cur);
!                 xmlFreeNode(cur);
                  break;
          }
  
!         if (add_physmem_rctl) {
!                 uint64_t cap;
!                 char *endp;
! 
!                 cap = strtoull(zone_physmem_cap, &endp, 10);
!                 (void) zonecfg_set_aliased_rctl(handle, ALIAS_MAXPHYSMEM, cap);
          }
  
!         return (res);
  }
  
  /*
   * Get the full tree of pkg metadata in a set of nested AVL trees.
   * pkgs_avl is an AVL tree of pkgs.
   *
   * The zone xml data contains DTD_ELEM_PACKAGE elements.
*** 7629,7639 ****
                  zerror(zonename, gettext("could not stat file %s: %s"),
                      USERATTR_FILENAME, strerror(errno));
                  (void) fclose(uaf);
                  return (Z_MISC_FS);
          }
!         if (!config_file_path(zonename, config_file)) {
                  (void) fclose(uaf);
                  return (Z_MISC_FS);
          }
  
          if ((err = stat(config_file, &config_st)) != 0) {
--- 7970,7980 ----
                  zerror(zonename, gettext("could not stat file %s: %s"),
                      USERATTR_FILENAME, strerror(errno));
                  (void) fclose(uaf);
                  return (Z_MISC_FS);
          }
!         if (!config_file_path(zonename, config_file, sizeof (config_file))) {
                  (void) fclose(uaf);
                  return (Z_MISC_FS);
          }
  
          if ((err = stat(config_file, &config_st)) != 0) {