6909222 reboot of system upgraded from 128 to build 129 generated error from an s10 zone due to boot-archive
authorjv227347 <Jordan.Vaughan@Sun.com>
Tue, 18 May 2010 12:28:09 -0700
changeset 12433 a15cace5f27d
parent 12432 222dd376dd6c
child 12434 f1c333f3f0b5
6909222 reboot of system upgraded from 128 to build 129 generated error from an s10 zone due to boot-archive 6927878 removing patches that affect solaris10 emulation hoses solaris10-branded zones 6947847 Solaris 10 Containers need s10_replacefile to support S10 patch tools
usr/src/lib/brand/solaris10/Makefile
usr/src/lib/brand/solaris10/s10_replacefile/Makefile
usr/src/lib/brand/solaris10/s10_replacefile/s10_replacefile.c
usr/src/lib/brand/solaris10/s10_support/s10_support.c
usr/src/lib/brand/solaris10/zone/p2v.ksh
usr/src/pkg/manifests/system-zones-brand-s10.mf
--- a/usr/src/lib/brand/solaris10/Makefile	Tue May 18 12:28:45 2010 -0600
+++ b/usr/src/lib/brand/solaris10/Makefile	Tue May 18 12:28:09 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 default: all
@@ -30,7 +29,8 @@
 # Build everything in parallel; use .WAIT for dependencies
 .PARALLEL:
 
-SUBDIRS =	librtld_db s10_npreload s10_brand s10_support zone cmd
+SUBDIRS =	librtld_db s10_npreload s10_brand s10_support s10_replacefile \
+		zone cmd
 MSGSUBDIRS =	s10_support zone
 
 all :=		TARGET= all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/brand/solaris10/s10_replacefile/Makefile	Tue May 18 12:28:09 2010 -0700
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+PROG =		s10_replacefile
+PROGS =		$(PROG)
+OBJS =		s10_replacefile
+
+all:		$(PROG)
+
+include ../Makefile.s10
+include $(SRC)/cmd/Makefile.cmd
+
+# override the install directory
+ROOTBIN =	$(ROOTBRANDDIR)
+CLOBBERFILES =	$(OBJS) $(ROOTPROGS)
+
+UTSBASE =	$(SRC)/uts
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_REENTRANT
+
+.KEEP_STATE:
+
+install:	all $(ROOTPROGS)
+
+clean:
+		$(RM) $(PROG) $(OBJS)
+
+lint:		lint_PROG
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/brand/solaris10/s10_replacefile/s10_replacefile.c	Tue May 18 12:28:09 2010 -0700
@@ -0,0 +1,141 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <unistd.h>
+
+/*
+ * This program aids the Solaris 10 patch tools (specifically
+ * /usr/lib/patch/patch_common_lib) in DAP patching.
+ *
+ * Whenever the patch tools replace a critical system component (e.g.,
+ * /lib/libc.so.1), they move the old component to a temporary location,
+ * move the new component to where the old component was, and establish
+ * an overlay mount of the old component on top of the new component.
+ * The patch tools do this with a shell script; consequently, the three
+ * operations occur in three processes.
+ *
+ * This doesn't work inside Solaris 10 Containers (S10Cs).  Suppose the
+ * patch tools need to replace /lib/libc.so.1.  The tools will move the old
+ * libc.so.1 to a temporary location.  But when they try to move the new
+ * libc.so.1, they fork a mv(1) process, which loads the solaris10 brand's
+ * emulation library.  The emulation library will try to load the zone's
+ * libc.so.1, but the library no longer exists; consequently, the emulation
+ * library aborts and the zone's users won't be able to start any processes.
+ *
+ * This program solves the problem by combining the move and mount operations
+ * into a single process.  The emulation library will already have loaded
+ * libc.so.1 for the process by the time the process starts to replace
+ * libc.so.1.
+ *
+ * This program takes six parameters that correspond to six variables within
+ * /usr/lib/patch/patch_common_lib:InstallSafemodeObject():
+ *
+ *	argv[1] - dstActual (the path to the file that will be replaced)
+ *	argv[2] - tmp_file (the temporary location to which the file will be
+ *		moved)
+ *	argv[3] - tmpDst (the path to the replacement file)
+ *	argv[4] - tmpFile (the path to a temporary copy of the running system's
+ *		version of the file being replaced; the source [special] of
+ *		the overlay mount)
+ *	argv[5] - cksumTmpDst (checksum of the file represented by tmpDst)
+ *	argv[6] - cksumTmpFile (checksum of the file represented by tmpFile)
+ *
+ * NOTE: This program will only establish an overlay mount if argv[4] or argv[5]
+ * is emtpy or if argv[4] and argv[5] differ.
+ *
+ * This program returns zero when it succeeds.  Non-negative values indicate
+ * failure.
+ */
+int
+main(int argc, char **argv)
+{
+	struct stat statbuf;
+	char mntoptions[MAX_MNTOPT_STR];
+
+	/*
+	 * Check the number of arguments that were passed to s10_replacefile.
+	 */
+	if (argc != 7) {
+		(void) fprintf(stderr, "Usage: %s dstActual tmp_file tmpDst "
+		    "tmpFile cksumTmpDst cksumTmpFile\n", argv[0]);
+		return (1);
+	}
+
+	/*
+	 * Move the destination file (dstActual) out of the way and move the
+	 * new file (tmpDst) into its place.
+	 *
+	 * NOTE: s10_replacefile won't print error messages here because
+	 * the Solaris 10 patch tools will.
+	 */
+	if (rename(argv[1], argv[2]) != 0)
+		return (2);
+	if (rename(argv[3], argv[1]) != 0)
+		return (3);
+
+	/*
+	 * If there was a lofs mount on dstActual (which we just moved), then
+	 * s10_replacefile should reestablish the lofs mount.  A lofs mount
+	 * existed if tmpFile exists.
+	 */
+	if (stat(argv[4], &statbuf) == 0 && (statbuf.st_mode & S_IFREG)) {
+		/*
+		 * Create a lofs overlay mount only if the checksums of the
+		 * old file at dstActual and the new file at dstActual differ.
+		 */
+		if (argv[5][0] == '\0' || argv[6][0] == '\0' ||
+		    strcmp(argv[5], argv[6]) != 0) {
+			mntoptions[0] = '\0';
+			if (mount(argv[4], argv[1], MS_OVERLAY | MS_OPTIONSTR,
+			    MNTTYPE_LOFS, NULL, 0, mntoptions,
+			    sizeof (mntoptions)) != 0) {
+				/*
+				 * Although the patch tools will print error
+				 * messages, the tools won't know that
+				 * s10_replacefile failed to establish an
+				 * overlay mount.  Printing an error message
+				 * here clarifies the problem for the user.
+				 */
+				(void) fprintf(stderr, "ERROR: Failed to "
+				    "overlay mount %s onto %s\n", argv[4],
+				    argv[1]);
+				return (4);
+			}
+		} else {
+			/*
+			 * dstActual does not need an overlay mount.  Delete
+			 * tmpFile.
+			 */
+			(void) unlink(argv[4]);
+		}
+	}
+	return (0);
+}
--- a/usr/src/lib/brand/solaris10/s10_support/s10_support.c	Tue May 18 12:28:45 2010 -0600
+++ b/usr/src/lib/brand/solaris10/s10_support/s10_support.c	Tue May 18 12:28:09 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -64,6 +63,13 @@
 
 static char *bname = NULL;
 
+/*
+ * DELETE_LIST_PATH represents the path to a solaris10-branded zone's "delete
+ * list", which is generated by patchrm when it needs to remove files after
+ * the zone reboots.  See set_zone_emul_bitmap() below for additional details.
+ */
+#define	DELETE_LIST_PATH	"/var/sadm/patch/.delete_list"
+
 #define	PKGINFO_RD_LEN	128
 #define	PATCHLIST	"PATCHLIST="
 
@@ -338,6 +344,26 @@
 }
 
 /*
+ * Convert the specified file basename into an unsigned integer.  If the
+ * basename contains characters that cannot be converted into digits or the
+ * basename isn't NULL or newline-terminated, then this function returns
+ * the unsigned equivalent of -1.
+ */
+static unsigned int
+basename_to_uint(const char *basenamep)
+{
+	char *filename_endptr;
+	unsigned int bit_index;
+
+	errno = 0;
+	bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10);
+	if (errno != 0 || (*filename_endptr != '\n' &&
+	    *filename_endptr != '\0') || filename_endptr == basenamep)
+		return ((unsigned int)-1);
+	return (bit_index);
+}
+
+/*
  * Determine which features/behaviors should be emulated and construct a bitmap
  * representing the results.  Associate the bitmap with the zone so that
  * the brand's emulation library will be able to retrieve the bitmap and
@@ -348,26 +374,27 @@
 static void
 set_zone_emul_bitmap(char *zonename)
 {
-	char			req_emulation_dir_path[MAXPATHLEN];
+	char			zoneroot[MAXPATHLEN];
+	char			path[MAXPATHLEN];
 	DIR			*req_emulation_dirp;
 	struct dirent		*emul_feature_filep;
-	char			*filename_endptr;
 	s10_emul_bitmap_t	bitmap;
 	unsigned int		bit_index;
 	zoneid_t		zoneid;
+	FILE			*delete_listp;
 
 	/*
 	 * If the Solaris 10 directory containing emulation feature files
 	 * doesn't exist in the zone, then assume that it only needs the
 	 * most basic emulation and, therefore, doesn't need a bitmap.
 	 */
-	if (zone_get_rootpath(zonename, req_emulation_dir_path,
-	    sizeof (req_emulation_dir_path)) != Z_OK)
+	if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK)
 		s10_err(gettext("error getting zone's path"));
-	if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR,
-	    sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path))
-		s10_err(gettext("error formatting version path"));
-	if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL)
+	if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR,
+	    zoneroot) >= sizeof (path))
+		s10_err(gettext("zone's emulation versioning directory's path "
+		    "%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot);
+	if ((req_emulation_dirp = opendir(path)) == NULL)
 		return;
 	bzero(bitmap, sizeof (bitmap));
 
@@ -384,11 +411,8 @@
 		 * Convert the file's name to an unsigned integer.  Ignore
 		 * files whose names aren't unsigned integers.
 		 */
-		errno = 0;
-		bit_index = (unsigned int)strtoul(emul_feature_filep->d_name,
-		    &filename_endptr, 10);
-		if (errno != 0 || *filename_endptr != '\0' ||
-		    filename_endptr == emul_feature_filep->d_name)
+		bit_index = basename_to_uint(emul_feature_filep->d_name);
+		if (bit_index == (unsigned int)-1)
 			continue;
 
 		/*
@@ -412,11 +436,84 @@
 			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
 		}
 	}
+	(void) closedir(req_emulation_dirp);
+
+	/*
+	 * The zone's administrator might have removed a patch that delivered
+	 * an emulation feature file the last time the zone ran.  If so, then
+	 * the zone's patch utilities won't delete the file until the zone's
+	 * svc:/system/patch-finish:delete SMF service runs.  This is
+	 * problematic because the zone will be using system libraries whose
+	 * ioctl structures and syscall invocations will differ from those
+	 * expected by the emulation library.  For example, if an administrator
+	 * removes a patch that affects the formats of MNTFS ioctls, then the
+	 * administrator's zone will use a version of libc.so.1 that issues
+	 * MNTFS ioctls that use older structure versions than the zone's
+	 * emulation library will expect.
+	 *
+	 * Fortunately, the patchrm utility creates a hidden file,
+	 * /var/sadm/patch/.delete_list, which lists all files that
+	 * svc:/system/patch-finish:delete will delete.  We'll determine whether
+	 * this file exists in the zone and disable the emulation bits
+	 * associated with the emulation feature files that will be deleted.
+	 *
+	 * NOTE: The patch tools lofs mount backup copies of critical system
+	 * libraries, such as /lib/libc.so.1, over their replacements whenever
+	 * administrators add or remove DAP patches.  Consequently, there isn't
+	 * a window of vulnerability between patch addition or removal and
+	 * zone reboot.  The aforementioned problem only occurs after a zone
+	 * reboots.
+	 */
+	if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >=
+	    sizeof (path))
+		s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH
+		    " is too long"), zoneroot);
+	if ((delete_listp = fopen(path, "r")) != NULL) {
+		while (fgets(path, sizeof (path), delete_listp) != NULL) {
+			char *const basenamep = path +
+			    sizeof (S10_REQ_EMULATION_DIR);
+
+			/*
+			 * Make sure that the file is in the directory
+			 * containing emulation feature files.  If it is,
+			 * then basenamep should refer to the basename of
+			 * the file.
+			 */
+			if (strncmp(path, S10_REQ_EMULATION_DIR,
+			    sizeof (S10_REQ_EMULATION_DIR) - 1) != 0)
+				continue;
+			if (*(basenamep - 1) != '/')
+				continue;
+
+			/*
+			 * Convert the file's basename into a bit index in
+			 * the emulation bitmap.  If the file's basename isn't
+			 * integral, then skip the file.  Otherwise, clear the
+			 * corresponding bit in the bitmap.
+			 */
+			bit_index = basename_to_uint(basenamep);
+			if (bit_index == (unsigned int)-1)
+				continue;
+			if (bit_index < S10_NUM_EMUL_FEATURES)
+				bitmap[(bit_index >> 3)] &=
+				    ~(1 << (bit_index & 0x7));
+		}
+		if (ferror(delete_listp) != 0 || feof(delete_listp) == 0)
+			s10_err(gettext("The program encountered an error while"
+			    " reading from %s" DELETE_LIST_PATH "."), zoneroot);
+		(void) fclose(delete_listp);
+	} else if (errno != ENOENT) {
+		/*
+		 * The delete list exists but couldn't be opened.  Warn the
+		 * administrator.
+		 */
+		s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"),
+		    zoneroot, strerror(errno));
+	}
 
 	/*
 	 * We're done scanning files.  Set the zone's emulation bitmap.
 	 */
-	(void) closedir(req_emulation_dirp);
 	if ((zoneid = getzoneidbyname(zonename)) < 0)
 		s10_err(gettext("unable to get zoneid"));
 	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
--- a/usr/src/lib/brand/solaris10/zone/p2v.ksh	Tue May 18 12:28:45 2010 -0600
+++ b/usr/src/lib/brand/solaris10/zone/p2v.ksh	Tue May 18 12:28:09 2010 -0700
@@ -19,8 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 # NOTE: this script runs in the global zone and touches the non-global
@@ -50,6 +49,12 @@
 		/usr/sbin/zoneadm -z $ZONENAME halt
 	fi
 
+	#
+	# Delete temporary files created during the hollow package removal
+	# process.
+	#
+	rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list
+
 	exit $EXIT_CODE
 }
 
@@ -539,6 +544,9 @@
 #
 safe_dir /etc
 safe_dir /var
+safe_dir /var/sadm
+safe_dir /var/sadm/install
+safe_dir /var/sadm/pkg
 safe_opt_dir /etc/dfs
 safe_opt_dir /etc/lu
 safe_opt_dir /etc/zones
@@ -587,6 +595,88 @@
 	fatal "$e_exitfail"
 fi
 
+#
+# Remove all files and directories installed by hollow packages.  Such files
+# and directories shouldn't exist inside zones.
+#
+hollow_pkgs=$(mktemp -t .hollow.pkgs.XXXXXX)
+hollow_file_list=$(mktemp $ZONEROOT/.hollow.pkgs.files.XXXXXX)
+hollow_dir_list=$(mktemp $ZONEROOT/.hollow.pkgs.dirs.XXXXXX)
+[ -f "$hollow_pkgs" -a -f "$hollow_file_list" -a -f "$hollow_dir_list" ] || {
+	error "$e_tmpfile"
+	rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list
+	fatal "$e_exitfail"
+}
+for pkg_name in $ZONEROOT/var/sadm/pkg/*; do
+	grep 'SUNW_PKG_HOLLOW=true' $pkg_name/pkginfo >/dev/null 2>&1 && \
+	    basename $pkg_name >>$hollow_pkgs
+done
+/usr/bin/nawk -v hollowpkgs=$hollow_pkgs -v filelist=$hollow_file_list \
+    -v dirlist=$hollow_dir_list '
+	BEGIN {
+		while (getline p <hollowpkgs > 0)
+			pkgs[p] = 1;
+		close(hollowpkgs);
+	}
+	{
+		# fld is the field where the pkg names begin.
+		# nm is the file/dir entry name.
+		if ($2 == "f") {
+			fld=10;
+			nm=$1;
+		} else if ($2 == "d") {
+			fld=7;
+			nm=$1;
+		} else if ($2 == "s" || $2 == "l") {
+			fld=4;
+			split($1, a, "=");
+			nm=a[1];
+		} else {
+			next;
+		}
+
+		# Determine whether the file or directory is delivered by any
+		# non-hollow packages.  Files and directories can be
+		# delivered by multiple pkgs.  The file or directory should only
+		# be removed if it is only delivered by hollow packages.
+		for (i = fld; i <= NF; i++) {
+			if (pkgs[get_pkg_name($i)] != 1) {
+				# We encountered a non-hollow package.  Skip
+				# this entry.
+				next;
+			}
+		}
+
+		# The file or directory is only delivered by hollow packages.
+		# Mark it for removal.
+		if (fld != 7)
+			print nm >>filelist
+		else
+			print nm >>dirlist
+	}
+
+	# Get the clean pkg name from the fld entry.
+	function get_pkg_name(fld) {
+		# Remove any pkg control prefix (e.g. *, !)
+		first = substr(fld, 1, 1)
+		if (match(first, /[A-Za-z]/)) {
+			pname = fld 
+		} else {
+			pname = substr(fld, 2)
+		}
+
+		# Then remove any class action script name
+		pos = index(pname, ":")
+		if (pos != 0)
+			pname = substr(pname, 1, pos - 1)
+                return (pname)
+        }
+' $ZONEROOT/var/sadm/install/contents
+/usr/sbin/zlogin -S $ZONENAME "cat /$(basename $hollow_file_list) | xargs rm -f"
+/usr/sbin/zlogin -S $ZONENAME "sort -r /$(basename $hollow_dir_list) | \
+    xargs rmdir >/dev/null 2>&1"
+rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list
+
 # cleanup SMF services
 fix_smf
 
--- a/usr/src/pkg/manifests/system-zones-brand-s10.mf	Tue May 18 12:28:45 2010 -0600
+++ b/usr/src/pkg/manifests/system-zones-brand-s10.mf	Tue May 18 12:28:09 2010 -0700
@@ -20,8 +20,7 @@
 #
 
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 set name=pkg.fmri value=pkg:/system/zones/brand/s10@$(PKGVERS)
@@ -86,6 +85,7 @@
 file path=usr/lib/brand/solaris10/s10_boot mode=0755
 file path=usr/lib/brand/solaris10/s10_isaexec_wrapper mode=0755
 file path=usr/lib/brand/solaris10/s10_native mode=0755
+file path=usr/lib/brand/solaris10/s10_replacefile mode=0755
 file path=usr/lib/brand/solaris10/s10_support mode=0755
 file path=usr/lib/brand/solaris10/smf_disable.lst mode=0444
 file path=usr/lib/brand/solaris10/solaris10_librtld_db.so.1