usr/src/cmd/auto-install/auto_parse.c
changeset 862 e9f31f2f2f2d
parent 846 70dd9e819f25
child 1075 df96871c232d
--- a/usr/src/cmd/auto-install/auto_parse.c	Tue Aug 17 18:22:44 2010 -0400
+++ b/usr/src/cmd/auto-install/auto_parse.c	Fri Aug 20 11:31:18 2010 -0600
@@ -79,9 +79,10 @@
 
 /*
  * Dump errors found during syntactic validation of AI manifest -
- * capture stdout and stderr of xmllint(1M) called with following parameters:
+ * repeat the xmllint(1) call made on the Python side to capture the
+ * stdout and stderr.  xmllint will be called with following parameters:
  *
- * /usr/bin/xmllint --noout --relaxng <schema> <manifest> 2>&1
+ * /usr/bin/xmllint --noout --dtdvalid <schema> --dtdattr <manifest> 2>&1
  *
  * Returns
  * 	-1  - failed to dump syntactic errors
@@ -93,13 +94,12 @@
 	char	*cmd;
 	size_t	cmd_ln;
 	int	ret;
+	char *dtd_xmllint =
+	    "/usr/bin/xmllint --noout --dtdvalid %s --dtdattr %s 2>&1";
 
-	/* calculate size of command string - account for string terminator */
-	cmd_ln = sizeof ("/usr/bin/xmllint --noout --relaxng ") +
-	    strlen(manifest) + sizeof (" ") + strlen(schema) +
-	    sizeof (" 2>&1") + 1;
-
-	cmd = malloc(cmd_ln);
+	/* calculate size of command string */
+	cmd_ln = snprintf(NULL, 0, dtd_xmllint, schema, manifest);
+	cmd = (char *)malloc(cmd_ln + 1);
 
 	if (cmd == NULL) {
 		auto_debug_print(AUTO_DBGLVL_ERR, "malloc() failed\n");
@@ -107,8 +107,7 @@
 		return (-1);
 	}
 
-	(void) snprintf(cmd, cmd_ln,
-	    "/usr/bin/xmllint --noout --relaxng %s %s 2>&1", schema, manifest);
+	(void) snprintf(cmd, cmd_ln + 1, dtd_xmllint, schema, manifest);
 
 	ret = ai_exec_cmd(cmd);
 
@@ -126,6 +125,115 @@
 }
 
 /*
+ * Translate size units from manifest into auto_size_units_t values.
+ *
+ * Defaults to AI_SIZE_UNITS_MEGABYTES if no value given or value
+ * not recognized.
+ *
+ * Returns
+ *  auto_size_units_t
+ */
+static auto_size_units_t
+get_size_units(char *p_str)
+{
+	if ((p_str == NULL) || (! strlen(p_str))) {
+		return (AI_SIZE_UNITS_MEGABYTES);
+	}
+
+	switch (p_str[0]) {
+		case 's':
+		case 'S':
+			return (AI_SIZE_UNITS_SECTORS);
+		case 'g':
+		case 'G':
+			return (AI_SIZE_UNITS_GIGABYTES);
+		case 't':
+		case 'T':
+			return (AI_SIZE_UNITS_TERABYTES);
+		case 'm':
+		case 'M':
+		default:
+			return (AI_SIZE_UNITS_MEGABYTES);
+	}
+}
+
+/*
+ * Convert size from one unit of measurement to another.
+ *
+ * Supported units are the auto_size_units_t enumeration:
+ *  AI_SIZE_UNITS_SECTORS
+ *  AI_SIZE_UNITS_MEGABYTES
+ *  AI_SIZE_UNITS_GIGABYTES
+ *  AI_SIZE_UNITS_TERABYTES
+ * If either from_units or to_units params are not recognized, disk_size is
+ * returned unaltered.
+ *
+ * Returns
+ *  uint64_t
+ */
+static uint64_t
+convert_disk_size(uint64_t disk_size, auto_size_units_t from_units,
+    auto_size_units_t to_units)
+{
+	uint64_t retval = disk_size;
+
+	switch (to_units) {
+		case AI_SIZE_UNITS_SECTORS:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size * MB_TO_SECTORS;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size * GB_TO_MB *
+					    MB_TO_SECTORS;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB *
+					    GB_TO_MB * MB_TO_SECTORS;
+			}
+		case AI_SIZE_UNITS_MEGABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / MB_TO_SECTORS;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size * GB_TO_MB;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB *
+					    GB_TO_MB;
+			}
+		case AI_SIZE_UNITS_GIGABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / GB_TO_MB /
+					    MB_TO_SECTORS;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size / GB_TO_MB;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size * TB_TO_GB;
+			}
+		case AI_SIZE_UNITS_TERABYTES:
+			switch (from_units) {
+				case AI_SIZE_UNITS_SECTORS:
+					retval = disk_size / MB_TO_SECTORS /
+					    GB_TO_MB / TB_TO_GB;
+				case AI_SIZE_UNITS_MEGABYTES:
+					retval = disk_size / GB_TO_MB /
+					    TB_TO_GB;
+				case AI_SIZE_UNITS_GIGABYTES:
+					retval = disk_size / TB_TO_GB;
+				case AI_SIZE_UNITS_TERABYTES:
+					retval = disk_size;
+			}
+	}
+
+	return (retval);
+}
+
+/*
  * Create the manifest data image in memory.  (Does not validate it.)
  *
  * Import the manifest into an in-memory tree
@@ -226,18 +334,6 @@
 	ai_free_manifest_value_list(value_list);
 }
 
-static char **
-ai_get_manifest_partition_action(int *len)
-{
-	char **value;
-
-	value = ai_get_manifest_values(AIM_PARTITION_ACTION, len);
-
-	if (*len > 0)
-		return (value);
-	return (NULL);
-}
-
 /*
  * ai_get_manifest_element_value() - return value given xml element
  */
@@ -288,6 +384,10 @@
 {
 	char *p;
 
+	p = ai_get_manifest_element_value(AIM_TARGET_DISK_KEYWORD);
+	if (p != NULL)
+		(void) strncpy(adi->diskkeyword, p, sizeof (adi->diskkeyword));
+
 	p = ai_get_manifest_element_value(AIM_TARGET_DEVICE_NAME);
 	if (p != NULL)
 		(void) strncpy(adi->diskname, p, sizeof (adi->diskname));
@@ -314,27 +414,57 @@
 		    sizeof (adi->diskdevicepath));
 
 	p = ai_get_manifest_element_value(AIM_TARGET_DEVICE_SIZE);
-	if (p != NULL)
-		adi->disksize = (uint64_t)strtoull(p, NULL, 0);
+	if (p != NULL) {
+		char *endptr;
+		uint64_t disk_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		disk_size = (uint64_t)strtoull(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and then
+			 * convert the size from given units into number
+			 * of disk sectors.
+			 */
+			size_units = get_size_units(endptr);
+			adi->disksize = convert_disk_size(disk_size, size_units,
+			    AI_SIZE_UNITS_SECTORS);
+
+			auto_debug_print(AUTO_DBGLVL_INFO,
+			    "Requested target size [%s] converted "
+			    "to [%lld] sectors\n",
+			    p, adi->disksize);
+		} else {
+			auto_log_print(
+			    "Invalid target device size specified: [%s]",
+			    p);
+			return (AUTO_INSTALL_FAILURE);
+		}
+	}
 
 	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_USE_SOLARIS_PARTITION);
 	if (p != NULL) {
 #ifdef	__sparc
 		auto_log_print("Warning: ignoring manifest element "
-		    "target_device_use_solaris_partition on SPARC\n");
+		    "partition action='use_existing' on SPARC\n");
 #else
-		(void) strncpy(adi->diskusepart, p, sizeof (adi->diskusepart));
+		/*
+		 * In this Schema, a partition with attribute
+		 * action="use_existing" corresponds to
+		 * target_device_use_solaris_partition="true"
+		 * in the previous schema.
+		 */
+		(void) strncpy(adi->diskusepart, "true",
+		    sizeof (adi->diskusepart));
 #endif
 	}
 
 	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_OVERWRITE_ROOT_ZFS_POOL);
-	if (p != NULL)
-		(void) strncpy(adi->diskoverwrite_rpool, p,
-		    sizeof (adi->diskoverwrite_rpool));
-
-	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_INSTALL_SLICE_NUMBER);
 	if (p != NULL) {
 		int install_slice_number;
@@ -367,24 +497,6 @@
 		adi->diskiscsi.port = strtoll(p, NULL, 0);
 
 	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_NAME);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.chapname, p,
-		    sizeof (adi->diskiscsi.chapname));
-
-	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_CHAP_SECRET);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.chapsecret, p,
-		    sizeof (adi->diskiscsi.chapsecret));
-
-	p = ai_get_manifest_element_value(
-	    AIM_TARGET_DEVICE_ISCSI_TARGET_INITIATOR);
-	if (p != NULL)
-		(void) strncpy(adi->diskiscsi.initiator, p,
-		    sizeof (adi->diskiscsi.initiator));
-
-	p = ai_get_manifest_element_value(
 	    AIM_TARGET_DEVICE_ISCSI_PARAMETER_SOURCE);
 	if (p == NULL)
 		adi->diskiscsi.parm_src = AI_ISCSI_PARM_SRC_MANIFEST;
@@ -404,6 +516,43 @@
 		}
 	}
 
+	/* Debug - print disk info out to log */
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "Disk info from Manifest:\n");
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskkeyword\t\t\t: [%s]\n", adi->diskkeyword);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskname\t\t\t: [%s]\n", adi->diskname);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdisktype\t\t\t: [%s]\n", adi->disktype);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskvendor\t\t\t: [%s]\n", adi->diskvendor);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskvolname\t\t\t: [%s]\n", adi->diskvolname);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskdevid\t\t\t: [%s]\n", adi->diskdevid);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskdevicepath\t\t: [%s]\n", adi->diskdevicepath);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdisksize\t\t\t: [%d]\n", adi->disksize);
+#ifndef	__sparc
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskusepart\t\t\t: [%s]\n", adi->diskusepart);
+#endif
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.name\t\t: [%s]\n", adi->diskiscsi.name);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.ip\t\t: [%s]\n", adi->diskiscsi.ip);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.port\t\t: [%d]\n", adi->diskiscsi.port);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.lun\t\t: [%s]\n", adi->diskiscsi.lun);
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tdiskiscsi.parm_src\t: [%d] (= %s)\n", adi->diskiscsi.parm_src,
+	    adi->diskiscsi.parm_src ? "DHCP" : "MANIFEST");
+	auto_debug_print(AUTO_DBGLVL_INFO,
+	    "\tinstall_slice_num.\t: [%d]\n", adi->install_slice_number);
+
 	return (AUTO_INSTALL_SUCCESS);
 }
 
@@ -422,10 +571,28 @@
 	adsi->swap_size = -1;
 	p = ai_get_manifest_element_value(AIM_SWAP_SIZE);
 	if (p != NULL) {
-		if (sscanf(p, "%ld", &adsi->swap_size) > 0) {
+		char *endptr;
+		int32_t swap_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		swap_size = (int32_t)strtol(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and
+			 * then convert the size from given units into MB.
+			 */
+			size_units = get_size_units(endptr);
+			adsi->swap_size = (int32_t)convert_disk_size(swap_size,
+			    size_units,
+			    AI_SIZE_UNITS_MEGABYTES);
+
 			auto_debug_print(AUTO_DBGLVL_INFO,
-			    "Swap Size Requested=%lu\n",
-			    adsi->swap_size);
+			    "Requested swap size [%s] converted to [%d] MB\n",
+			    p, adsi->swap_size);
 		} else {
 			adsi->swap_size = 0;
 			auto_log_print("Invalid swap size "
@@ -454,10 +621,28 @@
 	addi->dump_size = -1;
 	p = ai_get_manifest_element_value(AIM_DUMP_SIZE);
 	if (p != NULL) {
-		if (sscanf(p, "%ld", &addi->dump_size) > 0) {
+		char *endptr;
+		int32_t dump_size;
+		auto_size_units_t size_units;
+
+		errno = 0;
+
+		/* Get the numerical portion of the size value */
+		dump_size = (int32_t)strtol(p, &endptr, 0);
+
+		if (errno == 0 && endptr != p) {
+			/*
+			 * Get the units portion of the size val and
+			 * then convert the size from given units into MB.
+			 */
+			size_units = get_size_units(endptr);
+			addi->dump_size = (int32_t)convert_disk_size(dump_size,
+			    size_units,
+			    AI_SIZE_UNITS_MEGABYTES);
+
 			auto_debug_print(AUTO_DBGLVL_INFO,
-			    "Dump Size Requested=%lu\n",
-			    addi->dump_size);
+			    "Requested dump size [%s] converted to [%d] MB\n",
+			    p, addi->dump_size);
 		} else {
 			addi->dump_size = 0;
 			auto_log_print("Invalid dump device size "
@@ -472,6 +657,167 @@
 }
 
 /*
+ * Create a partition info struct and populate it with details
+ * from the manifest matching the specified tags (enhanced
+ * nodepaths).
+ *
+ * pstatus - return status pointer, must point to valid storage
+ *		If no problems in validating partition info,
+ *		set to zero, otherwise set to non-zero value
+ *
+ * This function allocates memory for an auto_partition_info
+ * struct. The caller MUST free this memory.
+ */
+static auto_partition_info *
+get_partition_by_tags(char *number_tag, char *action_tag,
+    char *start_tag, char *size_tag, char *type_tag, int *pstatus)
+{
+	auto_partition_info *api;
+	char *p;
+	char *endptr;
+
+	api = calloc(sizeof (auto_partition_info), 1);
+	if (api == NULL)
+		return (NULL);
+
+	/* Get the name (number) for this partition */
+	p = ai_get_manifest_element_value(number_tag);
+	if (p != NULL) {
+		errno = 0;
+		api->partition_number = (int) strtoul(p, &endptr, 10);
+		if (errno != 0 || endptr == p) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Partition name in manifest (%s) is "
+			    "not a valid value.\n",
+			    p);
+			*pstatus = 1;
+			free(api);
+			errno = 0;
+			return (NULL);
+		}
+	}
+
+	/* Get the action for this partition */
+	p = ai_get_manifest_element_value(action_tag);
+	if (p != NULL)
+		(void) strlcpy(api->partition_action, p,
+		    sizeof (api->partition_action));
+
+	/*
+	 * Get the start_sector for this partition
+	 *
+	 * set default for starting sector (unspecified)
+	 * stored as unsigned in C, * but signed in XML
+	 * so that -1 can be used in default value manifest
+	 * to tell AI to find best location when starting sector not specified
+	 * see om_create_partition()
+	 */
+	api->partition_start_sector = (uint64_t)-1LL;
+	p = ai_get_manifest_element_value(start_tag);
+	if (p != NULL) {
+		api->partition_start_sector =
+		    (uint64_t)strtoll(p, NULL, 0);
+	}
+
+	/*
+	 * Get the size (value + units) for this partition.
+	 * This is only used for "create" action.
+	 */
+	if (strcmp(api->partition_action, "create") == 0) {
+		p = ai_get_manifest_element_value(size_tag);
+		if (p != NULL) {
+			errno = 0;
+
+			/* Get the numerical portion of the size value */
+			api->partition_size = strtoull(p, &endptr, 0);
+
+			if (errno == 0 && endptr != p) {
+				/* Get the units portion of the size value */
+				api->partition_size_units =
+				    get_size_units(endptr);
+			} else {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "Partition size in manifest (%s) is "
+				    "not a valid value.\n",
+				    p);
+				*pstatus = 1;
+				free(api);
+				errno = 0;
+				return (NULL);
+			}
+		} else {
+			/*
+			 * Default to 0mb.  This is not strictly necessary,
+			 * as both these values correspond to 0, which was
+			 * the value they were already set to when the struct
+			 * was calloc()ed.
+			 */
+			api->partition_size = (uint64_t) 0;
+			api->partition_size_units = AI_SIZE_UNITS_MEGABYTES;
+		}
+	}
+
+	/* Get the filesystem type for this partition */
+	p = ai_get_manifest_element_value(type_tag);
+	if (p != NULL) {
+		/* allow some common partition type names */
+		if (strcasecmp(p, "SOLARIS") == 0) {
+			api->partition_type = SUNIXOS2;
+			auto_log_print(
+			    "New Solaris2 partition requested\n");
+		} else if (strcasecmp(p, "DOS16") == 0) {
+			api->partition_type = DOSOS16;
+			auto_log_print(
+			    "New 16-bit DOS partition requested\n");
+		} else if (strcasecmp(p, "FAT32") == 0) {
+			api->partition_type = FDISK_WINDOWS;
+			auto_log_print(
+			    "New FAT32 partition requested\n");
+		} else if (strcasecmp(p, "DOSEXT") == 0) {
+			api->partition_type = EXTDOS;
+			auto_log_print(
+			    "New DOS extended partition requested\n");
+		} else if (strcasecmp(p, "DOSEXTLBA") == 0) {
+			api->partition_type = FDISK_EXTLBA;
+			auto_log_print(
+			    "New DOS extended LBA partition requested"
+			    "\n");
+		} else {
+            /*
+             * Use partition type number, eg "191" to
+             * represent a Solaris partition.
+             */
+			char *endptr;
+
+			errno = 0;
+			api->partition_type =
+			    strtoull(p, &endptr, 0);
+			if (errno != 0 || endptr == p) {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "Partition type in manifest (%s) is "
+				    "not a valid number or partition type.\n",
+				    p);
+				*pstatus = 1;
+				free(api);
+				errno = 0;
+				return (NULL);
+			}
+		}
+	}
+
+	/*
+	 * Determine if this is a logical partition
+	 * This is inferred from the partition number.  Numbers of
+	 * 5 or greater imply the partition must be logical.
+	 */
+	if (api->partition_number >= 5) {
+		api->partition_is_logical = B_TRUE;
+	}
+
+	return (api);
+}
+
+/*
  * Retrieve the information about the partitions
  * that need to be configured
  *
@@ -485,189 +831,245 @@
 auto_partition_info *
 ai_get_manifest_partition_info(int *pstatus)
 {
+	auto_partition_info *ret_api;
 	auto_partition_info *api;
-	int i, len;
-	char **p;
+	int i, j;
+	int actions_len = 0;
+	int numbered_len = 0;
+	int unnumbered_len = 0;
+	char **partition_actions;
+	char **numbered_partitions;
+	char **unnumbered_partitions;
+	char *p;
+	char number_tag[MAXPATHLEN];
+	char action_tag[MAXPATHLEN];
+	char start_tag[MAXPATHLEN];
+	char size_tag[MAXPATHLEN];
+	char type_tag[MAXPATHLEN];
 
 	*pstatus = 0;	/* assume no parsing errors */
 
-	p = ai_get_manifest_partition_action(&len);
-	if (p == NULL)
+	/*
+	 * The name (number) is not mandatory for partitions, but if a partition
+	 * does not have a name then it must have the action 'use_existing'.
+	 * There can only be one 'use_existing' partition specified and it may
+	 * or may not be named.
+	 * We will first see if there is an un-named 'use_existing' partition
+	 * in the manifest and if so, fetch its details.  Then we will fetch
+	 * details for all the named partitions, using name+action as a unique
+	 * key.
+	 */
+
+	unnumbered_partitions = ai_get_manifest_values(
+	    AIM_USE_EXISTING_PARTITIONS, &unnumbered_len);
+	numbered_partitions = ai_get_manifest_values(
+	    AIM_NUMBERED_PARTITIONS, &numbered_len);
+
+	if (unnumbered_partitions == NULL) {
+		/* ai_get_manifest_values sets len to -1 if none found */
+		unnumbered_len = 0;
+	} else {
+		if (unnumbered_len > 1) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Only one 'use_existing' partition is permitted, "
+			    "%d were specified.\n", unnumbered_len);
+			*pstatus = 1;
+			return (NULL);
+		}
+
+		p = ai_get_manifest_element_value(
+		    AIM_UNNUMBERED_PARTITION_NUMBER);
+		if (p != NULL) {
+			/*
+			 * There is one 'use_existing' partition but a
+			 * name (number) was specified with it, so it
+			 * will be handled along with numbered
+			 * partitions - no need for specical handling here.
+			 */
+			unnumbered_len = 0;
+		}
+	}
+
+	if (numbered_partitions == NULL) {
+		/* ai_get_manifest_values sets len to -1 if none found */
+		numbered_len = 0;
+	}
+
+	if ((unnumbered_len + numbered_len) == 0)
 		return (NULL);
 
 	/* len+1 -- '1' for the NULL entry */
-	api = calloc(sizeof (auto_partition_info), len + 1);
+	ret_api = calloc(sizeof (auto_partition_info),
+	    numbered_len + unnumbered_len + 1);
+	if (ret_api == NULL)
+		return (NULL);
 
-	for (i = 0; i < len; i++) {
-		if (strlcpy((api + i)->partition_action, p[i],
-		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
-			auto_debug_print(AUTO_DBGLVL_ERR,
-			    "Partition action in manifest is too long (%s)\n",
-			    p[i]);
-			*pstatus = 1;
-			free(api);
+	if (unnumbered_len) {
+		/*
+		 * We have exactly one partition whose action is 'use_existing'
+		 * which does not have a name (number) specified.  We need
+		 * to fetch its details seperately from the numbered partitions.
+		 */
+		(void) snprintf(number_tag, sizeof (number_tag),
+		    AIM_UNNUMBERED_PARTITION_NUMBER);
+		(void) snprintf(action_tag, sizeof (action_tag),
+		    AIM_UNNUMBERED_PARTITION_ACTION);
+		(void) snprintf(start_tag, sizeof (start_tag),
+		    AIM_UNNUMBERED_PARTITION_START_SECTOR);
+		(void) snprintf(size_tag, sizeof (size_tag),
+		    AIM_UNNUMBERED_PARTITION_SIZE);
+		(void) snprintf(type_tag, sizeof (type_tag),
+		    AIM_UNNUMBERED_PARTITION_TYPE);
+
+		api = get_partition_by_tags(number_tag, action_tag,
+		    start_tag, size_tag, type_tag, pstatus);
+
+		if (api == NULL) {
+			free(unnumbered_partitions);
+			free(ret_api);
 			return (NULL);
 		}
-	}
-	free(p);
 
-	p = get_manifest_element_array(AIM_PARTITION_NUMBER);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(api + i)->partition_number = atoi(p[i]);
-		}
-		free(p);
+		(void) memcpy(ret_api, api, sizeof (auto_partition_info));
+		free(api);
+
+		free(unnumbered_partitions);
 	}
 
-	/*
-	 * set default for starting sector (unspecified)
-	 * stored as unsigned in C, * but signed in XML
-	 * so that -1 can be used in default value manifest
-	 * to tell AI to find best location when starting sector not specified
-	 * see om_create_partition()
-	 */
-	for (i = 0; i < len; i++) /* if not specified, AI finds best location */
-		(api + i)->partition_start_sector = (uint64_t)-1LL;
-	p = get_manifest_element_array(AIM_PARTITION_START_SECTOR);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(api + i)->partition_start_sector =
-			    (uint64_t)strtoll(p[i], NULL, 0);
+	if (numbered_len) {
+		partition_actions = ai_get_manifest_values(
+		    AIM_PARTITION_ACTIONS, &actions_len);
+
+		if (partition_actions == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Error fetching partition actions.\n");
+			*pstatus = 1;
+			free(numbered_partitions);
+			free(ret_api);
+			return (NULL);
 		}
-		free(p);
-	}
 
-	p = get_manifest_element_array(AIM_PARTITION_SIZE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* if action is create, size is mandatory */
-			if (strcmp((api + i)->partition_action, "create") != 0)
-				continue;
-			if (p[i] == NULL)	{ /* if size not provided */
-				/* size required for create action */
+		if (unnumbered_len) {
+			/*
+			 * Remove the unnamed 'use_existing' partition from
+			 * partition_actions.
+			 */
+			for (i = 0; i < actions_len; i++) {
+				if (strcmp(partition_actions[i],
+				    "use_existing") == 0) {
+					/*
+					 * Shuffle the remaining items up
+					 * one position.
+					 */
+					for (j = i; j < (actions_len - 1);
+					    j++) {
+						partition_actions[j] =
+						    partition_actions[j+1];
+					}
+					partition_actions[actions_len] = NULL;
+					actions_len--;
+				}
+			}
+		}
+
+		if (numbered_len != actions_len) {
+			if (numbered_len < actions_len) {
+				/*
+				 * If this mismatch occurs, there must have
+				 * been an unnamed partion whose action is
+				 * not 'use_existing'.
+				 */
 				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition size for create action "
-				    "is missing from manifest.\n");
+				    "Invalid unnamed partition specified in "
+				    "manifest. Only one unnamed partition "
+				    "allowed, whose action must be "
+				    "'use_existing'.\n");
 				*pstatus = 1;
-				free(api);
+				free(numbered_partitions);
+				free(partition_actions);
+				free(ret_api);
 				return (NULL);
-			}
-			if (strcasecmp(p[i], "max_size") == 0) {
-				(api + i)->partition_size = OM_MAX_SIZE;
-				/* zero will indicate maximum size */
-				auto_log_print("Maximum size requested for "
-				    "new partition.  (%d)\n", i);
 			} else {
-				char *endptr;
-
-				errno = 0;
-				(api + i)->partition_size =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
 				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition size in manifest (%s) is "
-				    "not a valid number or \"max_size\".\n",
-				    p[i]);
+				    "Error matching partition actions to "
+				    "names.\n");
 				*pstatus = 1;
-				free(api);
-				errno = 0;
+				free(numbered_partitions);
+				free(partition_actions);
+				free(ret_api);
 				return (NULL);
 			}
 		}
-		free(p);
-	}
 
-	p = get_manifest_element_array(AIM_PARTITION_TYPE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* allow some common partition type names */
-			if (strcasecmp(p[i], "SOLARIS") == 0) {
-				(api + i)->partition_type = SUNIXOS2;
-				auto_log_print(
-				    "New Solaris2 partition requested\n");
-			} else if (strcasecmp(p[i], "DOS16") == 0) {
-				(api + i)->partition_type = DOSOS16;
-				auto_log_print(
-				    "New 16-bit DOS partition requested\n");
-			} else if (strcasecmp(p[i], "FAT32") == 0) {
-				(api + i)->partition_type = FDISK_WINDOWS;
-				auto_log_print(
-				    "New FAT32 partition requested\n");
-			} else if (strcasecmp(p[i], "DOSEXT") == 0) {
-				(api + i)->partition_type = EXTDOS;
-				auto_log_print(
-				    "New DOS extended partition requested\n");
-			} else if (strcasecmp(p[i], "DOSEXTLBA") == 0) {
-				(api + i)->partition_type = FDISK_EXTLBA;
-				auto_log_print(
-				    "New DOS extended LBA partition requested"
-				    "\n");
-			} else {	/* use partition type number */
-				char *endptr;
+		/*
+		 * One or more numbered partitions have been specified.
+		 * Fetch the necessary details for each.
+		 */
+		for (i = 0; i < numbered_len; i++) {
+			(void) snprintf(number_tag, sizeof (number_tag),
+			    AIM_NUMBERED_PARTITION_NUMBER,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(action_tag, sizeof (action_tag),
+			    AIM_NUMBERED_PARTITION_ACTION,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(start_tag, sizeof (start_tag),
+			    AIM_NUMBERED_PARTITION_START_SECTOR,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(size_tag, sizeof (size_tag),
+			    AIM_NUMBERED_PARTITION_SIZE,
+			    numbered_partitions[i], partition_actions[i]);
+			(void) snprintf(type_tag, sizeof (type_tag),
+			    AIM_NUMBERED_PARTITION_TYPE,
+			    numbered_partitions[i], partition_actions[i]);
 
-				errno = 0;
-				(api + i)->partition_type =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
-				auto_debug_print(AUTO_DBGLVL_ERR,
-				    "Partition type in manifest (%s) is "
-				    "not a valid number or partition type.\n",
-				    p[i]);
-				*pstatus = 1;
-				free(api);
-				errno = 0;
+			api = get_partition_by_tags(number_tag, action_tag,
+			    start_tag, size_tag, type_tag, pstatus);
+
+			if (api == NULL) {
+				free(numbered_partitions);
+				free(ret_api);
 				return (NULL);
 			}
+
+			(void) memcpy((ret_api + unnumbered_len + i),
+			    api, sizeof (auto_partition_info));
+			free(api);
 		}
-		free(p);
+
+		free(numbered_partitions);
 	}
 
-	p = get_manifest_element_array(AIM_PARTITION_SIZE_UNITS);
-	/* partition size units can be sectors, GB, TB, or MB (default) */
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			if (p[i] == NULL) { /* default to MB */
-				(api + i)->partition_size_units =
-				    AI_SIZE_UNITS_MEGABYTES;
-				continue;
-			}
-			switch (p[i][0]) {
-				case 's':
-				case 'S':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_SECTORS;
-					break;
-				case 'g':
-				case 'G':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_GIGABYTES;
-					break;
-				case 't':
-				case 'T':
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_TERABYTES;
-					break;
-				case 'm':
-				case 'M':
-				default:
-					(api + i)->partition_size_units =
-					    AI_SIZE_UNITS_MEGABYTES;
-					break;
-			}
-		}
-		free(p);
+	/* Debug - print partition info out to log */
+	api = ret_api;
+	for (; api->partition_action[0] != '\0'; api++) {
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "Partition details from Manifest:\n");
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_action\t\t: [%s]\n",
+		    api->partition_action);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_number\t\t: [%d]\n",
+		    api->partition_number);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_start_sector\t: [%lld]\n",
+		    api->partition_start_sector);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_size\t\t\t: [%lld]\n",
+		    api->partition_size);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_type\t\t\t: [%d]\n",
+		    api->partition_type);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_size_units\t: [%d] (= %s)\n",
+		    (int)api->partition_size_units,
+		    CONVERT_UNITS_TO_TEXT(api->partition_size_units));
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tpartition_is_logical\t: [%d] (= %s)\n",
+		    (int)api->partition_is_logical,
+		    api->partition_is_logical ? "true" : "false");
 	}
-	/*
-	 * mark any partitions marked as logical
-	 */
-	p = get_manifest_element_array(AIM_PARTITION_IS_LOGICAL);
-	if (p != NULL)
-		for (i = 0; i < len; i++)
-			if (strcasecmp(p[i], "true") == 0)
-				(api + i)->partition_is_logical = B_TRUE;
-	return (api);
+
+	return (ret_api);
 }
 
 /*
@@ -684,133 +1086,154 @@
 ai_get_manifest_slice_info(int *pstatus)
 {
 	auto_slice_info *asi;
-	int i, len = 0;
-	char **p;
+	auto_slice_info *tmp_asi;
+	int i, names_len = 0, actions_len = 0;
+	char *p;
+	char **slice_names;
+	char **slice_actions;
+	char tag[MAXPATHLEN];
+	char *endptr;
 
 	*pstatus = 0;	/* assume no parsing errors */
-	p = ai_get_manifest_values(AIM_SLICE_ACTION, &len);
-	if (p == NULL || len <= 0)
+
+	/*
+	 * The name (number) and action attributes are mandatory for slices, so
+	 * we will use these two values as the unique key for slice elements.
+	 * First we fetch all the slice numbers and actions, then we query the
+	 * manifest using these values to fetch the additional details for
+	 * each slice.
+	 */
+
+	slice_names = ai_get_manifest_values(AIM_SLICE_NUMBER, &names_len);
+
+	if (slice_names == NULL || names_len <= 0)
 		return (NULL);
 
+	slice_actions = ai_get_manifest_values(AIM_SLICE_ACTION, &actions_len);
+
+	if (actions_len != names_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Error matching slice names to actions.\n");
+		*pstatus = 1;
+		free(slice_names);
+		free(slice_actions);
+		return (NULL);
+	}
+
 	/* len+1 -- '1' for end of array marker */
-	asi = calloc(sizeof (auto_slice_info), len + 1);
+	asi = calloc(sizeof (auto_slice_info), names_len + 1);
 
-	for (i = 0; i < len; i++) {
-		if (strlcpy((asi + i)->slice_action, p[i],
-		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
-			auto_debug_print(AUTO_DBGLVL_ERR,
-			    "Slice action in manifest is too long (%s)\n",
-			    p[i]);
-			*pstatus = 1;
-			free(asi);
-			return (NULL);
-		}
-	}
-	free(p);
-
-	p = get_manifest_element_array(AIM_SLICE_NUMBER);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			(asi + i)->slice_number = atoi(p[i]);
-		}
-		free(p);
+	if (asi == NULL) {
+		free(slice_names);
+		free(slice_actions);
+		return (NULL);
 	}
 
-	p = get_manifest_element_array(AIM_SLICE_SIZE);
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			/* if action is create, size is mandatory */
-			if (p[i] == NULL)	/* if size not provided */
-				/* size required for create action */
-				if (strcmp((asi + i)->slice_action, "create")
-				    != 0)
-					continue;
-				else {
-					auto_debug_print(AUTO_DBGLVL_ERR,
-					    "Slice size for create action "
-					    "is missing from manifest.\n");
-					*pstatus = 1;
-					free(asi);
-					return (NULL);
-				}
-			if (strcasecmp(p[i], "max_size") == 0) {
-				(asi + i)->slice_size = OM_MAX_SIZE;
-				/* zero will indicate maximum size */
-				auto_log_print("Maximum size requested for "
-				    "new slice.  (%d)\n", i);
+	for (i = 0; i < names_len; i++) {
+		/* Get the number for this slice */
+		(asi + i)->slice_number = atoi(slice_names[i]);
+
+		/* Get the action for this slice */
+		if (strlcpy((asi + i)->slice_action, slice_actions[i],
+		    AUTO_MAX_ACTION_LEN) >= AUTO_MAX_ACTION_LEN) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Slice action in manifest is too long (%s)\n", p);
+			*pstatus = 1;
+			free(asi);
+			free(slice_names);
+			free(slice_actions);
+			return (NULL);
+		}
+
+		/* Get the size (value + units) for this slice */
+		(void) snprintf(tag, sizeof (tag), AIM_SLICE_SIZE,
+		    slice_names[i], slice_actions[i]);
+		p = ai_get_manifest_element_value(tag);
+		if (p == NULL) {
+			/*
+			 * Default to 0mb.  This is not strictly necessary,
+			 * as both these values correspond to 0, which was
+			 * the value they were already set to when the struct
+			 * was calloc()ed.
+			 */
+			(asi + i)->slice_size = (uint64_t) 0;
+			(asi + i)->slice_size_units = AI_SIZE_UNITS_MEGABYTES;
+		} else {
+			errno = 0;
+
+			/* Get the numerical portion of the size value */
+			(asi + i)->slice_size = strtoull(p, &endptr, 0);
+
+			if (errno == 0 && endptr != p) {
+				/* Get the units portion of the size value */
+				(asi + i)->slice_size_units =
+				    get_size_units(endptr);
 			} else {
-				char *endptr;
-
-				errno = 0;
-				(asi + i)->slice_size =
-				    strtoull(p[i], &endptr, 0);
-				if (errno == 0 && endptr != p[i])
-					continue;
 				auto_debug_print(AUTO_DBGLVL_ERR,
 				    "Slice size in manifest (%s) is "
-				    "not a valid number or \"max_size\".\n",
-				    p[i]);
+				    "not a valid number.\n",
+				    p);
 				*pstatus = 1;
 				free(asi);
+				free(slice_names);
+				free(slice_actions);
 				errno = 0;
 				return (NULL);
 			}
 		}
-		free(p);
+
+		/*
+		 * Determine behavior for create action on existing slices.
+		 */
+		(void) snprintf(tag, sizeof (tag),
+		    AIM_SLICE_ON_EXISTING, slice_names[i], slice_actions[i]);
+		p = ai_get_manifest_element_value(tag);
+		if (p != NULL) {
+			/*
+			 * Since the slice information array is initialized
+			 * to zero, and the default enum value is also zero,
+			 * the "error" case will also be the default in the
+			 * slice information array.
+			 *
+			 * In the new schema, the slice attribute 'force'
+			 * controls this.  If force="false" (the default)
+			 * then we leave on_existing=0, which equates to
+			 * OM_ON_EXISTING_ERROR. If force="true", then we
+			 * set it to OM_ON_EXISTING_OVERWRITE.
+			 */
+			if (strcasecmp(p, "true") == 0)
+				(asi + i)->on_existing =
+				    OM_ON_EXISTING_OVERWRITE;
+		}
 	}
 
-	p = get_manifest_element_array(AIM_SLICE_SIZE_UNITS);
-	/* slice size units can be sectors, GB, TB, or MB (default) */
-	if (p != NULL) {
-		for (i = 0; i < len; i++) {
-			if (p[i] == NULL) { /* default to MB */
-				(asi + i)->slice_size_units =
-				    AI_SIZE_UNITS_MEGABYTES;
-				continue;
-			}
-			switch (p[i][0]) {
-				case 's':
-				case 'S':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_SECTORS;
-					break;
-				case 'g':
-				case 'G':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_GIGABYTES;
-					break;
-				case 't':
-				case 'T':
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_TERABYTES;
-					break;
-				case 'm':
-				case 'M':
-				default:
-					(asi + i)->slice_size_units =
-					    AI_SIZE_UNITS_MEGABYTES;
-					break;
-			}
-		}
-		free(p);
+	free(slice_names);
+	free(slice_actions);
+
+	/* Debug - print slice info out to log */
+	tmp_asi = asi;
+	for (; tmp_asi->slice_action[0] != '\0'; tmp_asi++) {
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "Slice details from Manifest:\n");
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_action\t\t: [%s]\n",
+		    tmp_asi->slice_action);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_number\t\t: [%d]\n",
+		    tmp_asi->slice_number);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_size\t\t\t: [%lld]\n",
+		    tmp_asi->slice_size);
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\tslice_size_units\t: [%d] (= %s)\n",
+		    (int)tmp_asi->slice_size_units,
+		    CONVERT_UNITS_TO_TEXT(tmp_asi->slice_size_units));
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "\ton_existing\t: [%d] (= %s)\n",
+		    (int)tmp_asi->on_existing,
+		    tmp_asi->on_existing ? "OVERWRITE" : "ERROR");
 	}
 
-	/*
-	 * Determine behavior for create action on existing slices.
-	 */
-	p = get_manifest_element_array(AIM_SLICE_ON_EXISTING);
-	if (p != NULL) {
-		/*
-		 * Since the slice information array is initialized to zero,
-		 * and the default enum value is also zero, the "error" case
-		 * will also be the default in the slice information array.
-		 */
-		for (i = 0; i < len; i++)
-			if (p[i] != NULL && strcasecmp(p[i], "overwrite") == 0)
-				(asi + i)->on_existing =
-				    OM_ON_EXISTING_OVERWRITE;
-		free(p);
-	}
 	return (asi);
 }
 
@@ -823,15 +1246,7 @@
 	char	**value;
 	char	*url;
 
-	value = ai_get_manifest_values(AIM_IPS_DEFAULT_PUBLISHER_URL, len);
-
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (*len <= 0) {
-		value = ai_get_manifest_values(
-		    AIM_IPS_DEFAULT_AUTH_URL, len);
-	}
+	value = ai_get_manifest_values(AIM_IPS_PUBLISHER_URL, len);
 
 	if (*len > 0) {
 		url = value[0];
@@ -842,22 +1257,20 @@
 }
 
 /*
- * Retrieve the URL for the additional publisher
+ * Retrieve the URL(s) for the additional publisher(s)
+ *
+ * Default and additional (or primary and secondary) publishers
+ * now use the same nodepaths, so this function repeats the same
+ * search as ai_get_manifest_default_url() but the results are
+ * handled differently.
  */
 char **
 ai_get_manifest_addl_url(int *len)
 {
 	char	**value;
 
-	value = ai_get_manifest_values(AIM_IPS_ADDL_PUBLISHER_URL, len);
+	value = ai_get_manifest_values(AIM_IPS_PUBLISHER_URL, len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (*len <= 0) {
-		value = ai_get_manifest_values(
-		    AIM_IPS_ADDL_AUTH_URL, len);
-	}
 	if (*len > 0) {
 		return (value);
 	}
@@ -866,41 +1279,19 @@
 
 /*
  * Retrieve an publisher name from the manifest using url value
- * This is the common function for default publisher and
- * additional publisher. If the value of the flag is_default_publisher
- * is true, then the default publisher tag is used.
  */
 char *
-ai_get_manifest_repo_publisher(boolean_t is_default_publisher, char *url)
+ai_get_manifest_repo_publisher(char *url)
 {
 	char	**value;
 	char	*publisher;
 	int	len;
 	char	tag[MAXPATHLEN];
 
-	if (is_default_publisher) {
-		(void) snprintf(tag, sizeof (tag),
-		    AIM_ADD_DEFAULT_URL_PUBLISHER_NAME, url);
-	} else {
-		(void) snprintf(tag, sizeof (tag),
-		    AIM_ADD_ADDL_URL_PUBLISHER_NAME, url);
-	}
+	(void) snprintf(tag, sizeof (tag),
+	    AIM_ADD_URL_PUBLISHER_NAME, url);
 	value = ai_get_manifest_values(tag, &len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (len <= 0) {
-		if (is_default_publisher) {
-			snprintf(tag, sizeof (tag),
-			    AIM_ADD_DEFAULT_URL_AUTH_NAME, url);
-		} else {
-			snprintf(tag, sizeof (tag),
-			    AIM_ADD_ADDL_URL_AUTH_NAME, url);
-		}
-		value = ai_get_manifest_values(tag, &len);
-	}
-
 	if (len > 0) {
 		publisher = value[0];
 		free(value);
@@ -911,12 +1302,9 @@
 
 /*
  * Retrieve the URL for an IPS repo mirrors
- * This is the common function for default publisher and
- * additional publisher. If the value of the flag is_default_publisher
- * is true, then the default publisher tag is used.
  */
 auto_mirror_repo_t *
-ai_get_manifest_repo_mirrors(boolean_t is_default_publisher, char *url)
+ai_get_manifest_repo_mirrors(char *url)
 {
 	int			i, len = 0;
 	char			**value;
@@ -924,30 +1312,10 @@
 	auto_mirror_repo_t	*ptr, *tmp_ptr;
 	auto_mirror_repo_t	*mirror = NULL;
 
-	if (is_default_publisher) {
-		(void) snprintf(buf, sizeof (buf),
-		    AIM_ADD_DEFAULT_URL_PUBLISHER_MIRROR, url);
-	} else {
-		(void) snprintf(buf, sizeof (buf),
-		    AIM_ADD_ADDL_URL_PUBLISHER_MIRROR, url);
-	}
-
+	(void) snprintf(buf, sizeof (buf),
+	    AIM_ADD_URL_PUBLISHER_MIRROR, url);
 	value = ai_get_manifest_values(buf, &len);
 
-	/*
-	 * If publisher is not supplied, check for authority
-	 */
-	if (len <= 0) {
-		if (is_default_publisher) {
-			(void) snprintf(buf, sizeof (buf),
-			    AIM_ADD_DEFAULT_URL_AUTH_MIRROR, url);
-		} else {
-			(void) snprintf(buf, sizeof (buf),
-			    AIM_ADD_ADDL_URL_AUTH_MIRROR, url);
-		}
-		value = ai_get_manifest_values(buf, &len);
-	}
-
 	if (len <= 0) {
 		return (NULL);
 	}
@@ -996,7 +1364,6 @@
 	char			*current_url, *default_url;
 	int			num_url;
 	auto_repo_info_t 	*repo, *default_repo;
-	boolean_t		is_default_publisher = B_TRUE;
 
 	default_repo = NULL;
 
@@ -1005,7 +1372,11 @@
 	 */
 	current_url = ai_get_manifest_default_url(&num_url);
 	if (current_url == NULL) {
-		return (NULL);
+		/*
+		 * If the publisher wasn't specified in the manifest,
+		 * provide a default value.
+		 */
+		current_url = AIM_FALLBACK_PUBLISHER_URL;
 	}
 
 	repo = calloc(sizeof (auto_repo_info_t), 1);
@@ -1017,9 +1388,18 @@
 	 * Save the value before calling another ai_get_manifest_*()
 	 */
 	default_url = strdup(current_url);
-	p = ai_get_manifest_repo_publisher(is_default_publisher, default_url);
+	p = ai_get_manifest_repo_publisher(default_url);
 	if (p == NULL) {
-		goto get_out;
+		/*
+		 * If the primary publisher URL is AIM_FALLBACK_PUBLISHER_URL
+		 * and no name was specified, then provide a default value.
+		 * For all other URLs, if a name is not specified for the
+		 * publisher, then it is an error.
+		 */
+		if (strcasecmp(current_url, AIM_FALLBACK_PUBLISHER_URL) == 0)
+			p = AIM_FALLBACK_PUBLISHER_NAME;
+		else
+			goto get_out;
 	}
 	repo->publisher = strdup(p);
 	repo->url = strdup(default_url);
@@ -1031,7 +1411,7 @@
 	 * get the mirrors for this publishers
 	 */
 	repo->mirror_repo =
-	    ai_get_manifest_repo_mirrors(is_default_publisher, default_url);
+	    ai_get_manifest_repo_mirrors(default_url);
 	repo->next_repo = NULL;
 	default_repo = repo;
 
@@ -1063,7 +1443,6 @@
 	char			**urls;
 	int			i,  num_url;
 	auto_repo_info_t 	*repo, *tmp_repo, *addl_repo;
-	boolean_t		is_default_publisher = B_FALSE;
 
 	addl_repo = NULL;
 	tmp_repo = NULL;
@@ -1077,10 +1456,15 @@
 		return (NULL);
 
 	/*
+	 * We start iterating through the urls at index 1
+	 * instead of index 0, because the first url returned
+	 * is the primary publisher.  All subsequent urls are
+	 * secondary, or additional, publishers, which is what
+	 * we want here.
 	 * Allocate space and save the urls because the next
 	 * call to ai_get_manifest_*() will overwrite them
 	 */
-	for (i = 0; i < num_url; i++) {
+	for (i = 1; i < num_url; i++) {
 		/*
 		 * Ignore the empty string
 		 */
@@ -1112,8 +1496,7 @@
 	 * mirrors (if any).
 	 */
 	for (repo = addl_repo; repo != NULL; repo = repo -> next_repo) {
-		p = ai_get_manifest_repo_publisher(
-		    is_default_publisher, repo->url);
+		p = ai_get_manifest_repo_publisher(repo->url);
 		if (p == NULL) {
 			goto get_out;
 		}
@@ -1125,8 +1508,7 @@
 		/*
 		 * get the mirrors for this publisher
 		 */
-		repo->mirror_repo = ai_get_manifest_repo_mirrors(
-		    is_default_publisher, repo->url);
+		repo->mirror_repo = ai_get_manifest_repo_mirrors(repo->url);
 	}
 
 	free(urls);