Add preserve attribute to file action
authorDanek Duvall <danek.duvall@sun.com>
Mon, 01 Oct 2007 16:59:51 -0700
changeset 125 8b66970ab314
parent 124 99d3dd6ad07c
child 126 d03a0b814e61
Add preserve attribute to file action
src/modules/actions/file.py
src/tests/preserve.ksh
src/util/distro-import/Makefile
src/util/distro-import/solaris.py
--- a/src/modules/actions/file.py	Sun Sep 30 18:34:43 2007 -0700
+++ b/src/modules/actions/file.py	Mon Oct 01 16:59:51 2007 -0700
@@ -34,6 +34,7 @@
 import grp
 import pwd
 import errno
+import sha
 
 import generic
 
@@ -63,10 +64,32 @@
                         omode = int(orig.attrs["mode"], 8)
                         oowner = pwd.getpwnam(orig.attrs["owner"]).pw_uid
                         ogroup = grp.getgrnam(orig.attrs["group"]).gr_gid
+                        ohash = orig.hash
 
-                # If we're not upgrading, or the file contents have changed,
-                # retrieve the file and write it to a temporary location.
-                # For ELF files, only write the new file if the elfhash changed.
+                # If the action has been marked with a preserve attribute, and
+                # the file exists and has a contents hash different from what
+                # the system expected it to be, then we preserve the original
+                # file in some way, depending on the value of preserve.
+                #
+                # XXX What happens when we transition from preserve to
+                # non-preserve or vice versa? Do we want to treat a preserve
+                # attribute as turning the action into a critical action?
+                if "preserve" in self.attrs and os.path.isfile(final_path):
+                        cfile = file(final_path)
+                        chash = sha.sha(cfile.read()).hexdigest()
+
+                        # XXX We should save the originally installed file.  It
+                        # can be used as an ancestor for a three-way merge, for
+                        # example.  Where should it be stored?
+                        if chash != ohash:
+                                pres_type = self.attrs["preserve"]
+                                if pres_type == "renameold":
+                                        old_path = final_path + ".old"
+                                elif pres_type == "renamenew":
+                                        final_path = final_path + ".new"
+                                else:
+                                        return
+
                 # XXX This needs to be modularized.
                 # XXX This needs to be controlled by policy.
                 if self.needsdata(orig): 
@@ -93,9 +116,17 @@
                         if e.errno != errno.EPERM:
                                 raise
 
+                # XXX There's a window where final_path doesn't exist, but we
+                # probably don't care.
+                if "old_path" in locals():
+                        os.rename(final_path, old_path)
+
                 # This is safe even if temp == final_path.
                 os.rename(temp, final_path)
 
+        # If we're not upgrading, or the file contents have changed,
+        # retrieve the file and write it to a temporary location.
+        # For ELF files, only write the new file if the elfhash changed.
         def needsdata(self, orig):
                 bothelf = orig and "elfhash" in orig.attrs and "elfhash" in self.attrs
                 if not orig or \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tests/preserve.ksh	Mon Oct 01 16:59:51 2007 -0700
@@ -0,0 +1,55 @@
+#!/bin/ksh -px
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+eval `pkgsend open test/preserve/[email protected]`
+if [ $? != 0 ]; then
+	echo \*\* script aborted:  couldn\'t open test/preserve/A
+	exit 1
+fi
+
+echo "This is the old version" > /tmp/test1
+
+echo $PKG_TRANS_ID
+pkgsend add dir  0755 root sys /bin
+pkgsend add file 0644 root sys /bin/test1 /tmp/test1 preserve=renamenew
+pkgsend add file 0644 root sys /bin/test2 /tmp/test1 preserve=renameold
+pkgsend add file 0644 root sys /bin/test3 /tmp/test1 preserve=true
+pkgsend close
+
+eval `pkgsend open test/preserve/[email protected]`
+if [ $? != 0 ]; then
+	echo \*\* script aborted:  couldn\'t open test/preserve/A
+	exit 1
+fi
+
+echo "This is the new version" > /tmp/test1
+
+echo $PKG_TRANS_ID
+pkgsend add dir  0755 root sys /bin
+pkgsend add file 0644 root sys /bin/test1 /tmp/test1 preserve=renamenew
+pkgsend add file 0644 root sys /bin/test2 /tmp/test1 preserve=renameold
+pkgsend add file 0644 root sys /bin/test3 /tmp/test1 preserve=true
+pkgsend close
+
+rm /tmp/test1
--- a/src/util/distro-import/Makefile	Sun Sep 30 18:34:43 2007 -0700
+++ b/src/util/distro-import/Makefile	Mon Oct 01 16:59:51 2007 -0700
@@ -30,7 +30,7 @@
 
 TMPPKGS=SUNWcsdreplace
 
-default:	$(TMPPKGS)
+default:	$(TMPPKGS) cluster.import
 
 cluster.import:	$(WOS_PATH)/.clustertoc
 	./clustertoc2import.py $(WOS_PATH)/.clustertoc | egrep -v 'SUNWjds-registration|SUNWdttsr|SUNWdttsu' > $@
@@ -53,5 +53,5 @@
 	pkgmk -r `pwd` -f [email protected] -d `pwd` -o
 
 
-import:	cluster.import
+import:	cluster.import $(TMPPKGS)
 	./solaris.py -w $(WOS_PATH) all.i386
--- a/src/util/distro-import/solaris.py	Sun Sep 30 18:34:43 2007 -0700
+++ b/src/util/distro-import/solaris.py	Mon Oct 01 16:59:51 2007 -0700
@@ -253,6 +253,25 @@
         for k, g in groupby((f for f in pkg.files if f.type in "fev"), fn):
                 groups.append(list(g))
 
+        def otherattrs(action):
+                s = " ".join(
+                    "%s=%s" % (a, action.attrs[a])
+                    for a in action.attrs
+                    if a not in ("owner", "group", "mode", "path")
+                )
+                if s:
+                        return " " + s
+                else:
+                        return ""
+
+        # Maps class names to preserve attribute values.
+        preserve_dict = {
+            "renameold": "renameold",
+            "renamenew": "renamenew",
+            "preserve": "true",
+            "svmpreserve": "true"
+        }
+
         undeps = set()
         for g in groups:
                 pkgname = usedlist[g[0].pathname][0]
@@ -265,13 +284,16 @@
                                 f.attrs["owner"] = pathdict[path].owner
                                 f.attrs["group"] = pathdict[path].group
                                 f.attrs["mode"] = pathdict[path].mode
+                                if pathdict[path].klass in preserve_dict.keys():
+                                        f.attrs["preserve"] = \
+                                            preserve_dict[pathdict[path].klass]
                                 if hasattr(pathdict[path], "changed_attrs"):
                                         f.attrs.update(
                                             pathdict[path].changed_attrs)
-                                print "    %s add file %s %s %s %s" % \
+                                print "    %s add file %s %s %s %s%s" % \
                                     (pkg.name, f.attrs["mode"],
                                         f.attrs["owner"], f.attrs["group"],
-                                        path)
+                                        path, otherattrs(f))
                                 # Write the file to a temporary location.
                                 d = f.data().read()
                                 fd, tmp = mkstemp(prefix="pkg.")