For bug #181021, preserve timestamps from during merge. For replacement operations...
authorZac Medico <zmedico@gentoo.org>
Fri, 8 Jun 2007 19:54:15 +0000 (19:54 -0000)
committerZac Medico <zmedico@gentoo.org>
Fri, 8 Jun 2007 19:54:15 +0000 (19:54 -0000)
svn path=/main/branches/2.1.2/; revision=6760

pym/portage.py

index d52a6da57a0f408a3daff76ccdc0cc9baf824419..0719aa23d245396d216f4fabbd71bdb0bd2f3ac2 100644 (file)
@@ -3891,6 +3891,8 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None):
                        else:
                                os.symlink(target,dest)
                        lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
+                       # utime() only works on the target of a symlink, so it's not
+                       # possible to perserve mtime on symlinks.
                        return os.lstat(dest)[stat.ST_MTIME]
                except SystemExit, e:
                        raise
@@ -6827,9 +6829,9 @@ class dblink:
                        mysettings.get("CONFIG_PROTECT","").split(),
                        mysettings.get("CONFIG_PROTECT_MASK","").split())
                self.updateprotect = protect_obj.updateprotect
-               self._config_protect = protect_obj
+               self.isprotected = protect_obj.isprotected
                self._installed_instance = None
-               self.contentscache=[]
+               self.contentscache = None
                self._contents_inodes = None
 
        def lockdb(self):
@@ -6894,7 +6896,7 @@ class dblink:
                """
                if not os.path.exists(self.dbdir+"/CONTENTS"):
                        return None
-               if self.contentscache != []:
+               if self.contentscache is not None:
                        return self.contentscache
                pkgfiles={}
                myc=open(self.dbdir+"/CONTENTS","r")
@@ -6953,7 +6955,7 @@ class dblink:
                return pkgfiles
 
        def unmerge(self, pkgfiles=None, trimworld=1, cleanup=1,
-               ldpath_mtimes=None):
+               ldpath_mtimes=None, new_contents=None):
                """
                Calls prerm
                Unmerges a given package (CPV)
@@ -6969,6 +6971,8 @@ class dblink:
                @type cleanup: Boolean
                @param ldpath_mtimes: mtimes to pass to env_update (see env_update)
                @type ldpath_mtimes: Dictionary
+               @param new_contents: contents from a new instance that will replace this one
+               @type new_contents: Dictionary
                @rtype: Integer
                @returns:
                1. os.EX_OK if everything went well.
@@ -7032,7 +7036,7 @@ class dblink:
                                        writemsg("!!! FAILED prerm: %s\n" % retval, noiselevel=-1)
                                        return retval
 
-                       self._unmerge_pkgfiles(pkgfiles)
+                       self._unmerge_pkgfiles(pkgfiles, new_contents=new_contents)
 
                        if myebuildpath:
                                retval = doebuild(myebuildpath, "postrm", self.myroot,
@@ -7071,7 +7075,7 @@ class dblink:
                        contents=contents)
                return os.EX_OK
 
-       def _unmerge_pkgfiles(self, pkgfiles):
+       def _unmerge_pkgfiles(self, pkgfiles, new_contents=None):
                """
                
                Unmerges the contents of a package from the liveFS
@@ -7079,6 +7083,8 @@ class dblink:
                
                @param pkgfiles: typically self.getcontents()
                @type pkgfiles: Dictionary { filename: [ 'type', '?', 'md5sum' ] }
+               @param new_contents: contents from a new instance that will replace this one
+               @type new_contents: Dictionary
                @rtype: None
                """
                global dircache
@@ -7088,6 +7094,28 @@ class dblink:
                        writemsg_stdout("No package files given... Grabbing a set.\n")
                        pkgfiles=self.getcontents()
 
+               if not new_contents:
+                       counter = self.vartree.dbapi.cpv_counter(self.mycpv)
+                       slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
+                       slot_matches = self.vartree.dbapi.match(
+                               "%s:%s" % (dep_getkey(self.mycpv), slot))
+                       new_cpv = None
+                       if slot_matches:
+                               max_counter = -1
+                               for cur_cpv in slot_matches:
+                                       cur_counter = self.vartree.dbapi.cpv_counter(cur_cpv)
+                                       if cur_counter == counter and \
+                                               cur_cpv == self.mycpv:
+                                               continue
+                                       if cur_counter > max_counter:
+                                               max_counter = cur_counter
+                                               new_cpv = cur_cpv
+                       if new_cpv:
+                               # The current instance has been replaced by a newer instance.
+                               new_cat, new_pkg = catsplit(new_cpv)
+                               new_contents = dblink(new_cat, new_pkg, self.vartree.root,
+                                       self.settings, vartree=self.vartree).getcontents()
+
                if pkgfiles:
                        mykeys=pkgfiles.keys()
                        mykeys.sort()
@@ -7098,8 +7126,12 @@ class dblink:
                        modprotect="/lib/modules/"
                        for objkey in mykeys:
                                obj = normalize_path(objkey)
-                               if obj[:2]=="//":
-                                       obj=obj[1:]
+                               if new_contents and obj in new_contents:
+                                       # A new instance of this package claims the file, so don't
+                                       # unmerge it.
+                                       writemsg_stdout("--- !owned %s %s\n" % \
+                                               (pkgfiles[objkey][0], obj))
+                                       continue
                                statobj = None
                                try:
                                        statobj = os.stat(obj)
@@ -7233,31 +7265,6 @@ class dblink:
 
                return False
 
-       def isprotected(self, filename):
-               """In cases where an installed package in the same slot owns a
-               protected file that will be merged, bump the mtime on the installed
-               file in order to ensure that it isn't unmerged."""
-               if not self._config_protect.isprotected(filename):
-                       return False
-               if self._installed_instance is None:
-                       return True
-               mydata = self._installed_instance.getcontents().get(filename, None)
-               if mydata is None:
-                       return True
-
-               # Bump the mtime in order to ensure that the old config file doesn't
-               # get unmerged.  The user will have an opportunity to merge the new
-               # config with the old one.
-               try:
-                       os.utime(filename, None)
-               except OSError, e:
-                       if e.errno != errno.ENOENT:
-                               raise
-                       del e
-                       # The file has disappeared, so it's not protected.
-                       return False
-               return True
-
        def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
                mydbapi=None, prev_mtimes=None):
                """
@@ -7442,9 +7449,6 @@ class dblink:
                                        raise
                                del e
 
-               # get old contents info for later unmerging
-               oldcontents = self.getcontents()
-
                self.dbdir = self.dbtmpdir
                self.delete()
                if not os.path.exists(self.dbtmpdir):
@@ -7490,12 +7494,11 @@ class dblink:
                else:
                        cfgfiledict["IGNORE"]=0
 
-               # Timestamp for files being merged.  Use time() - 1 in order to prevent
-               # a collision with timestamps that are bumped by the utime() call
-               # inside isprotected().  This ensures that the new and old config have
-               # different timestamps (for the benefit of programs like rsync that
-               # that need distiguishable timestamps to detect file changes).
-               mymtime = long(time.time() - 1)
+               # Don't bump mtimes on merge since some application require
+               # preservation of timestamps.  This means that the unmerge phase must
+               # check to see if file belongs to an installed instance in the same
+               # slot.
+               mymtime = None
 
                # set umask to 0 for merging; back up umask, save old one in prevmask (since this is a global change)
                prevmask   = os.umask(0)
@@ -7534,12 +7537,14 @@ class dblink:
                #if we opened it, close it
                outfile.flush()
                outfile.close()
+               self.contentscache = None
+               new_contents = self.getcontents()
 
                if os.path.exists(self.dbpkgdir):
                        writemsg_stdout(">>> Safely unmerging already-installed instance...\n")
-                       self.dbdir = self.dbpkgdir
-                       self.unmerge(oldcontents, trimworld=0, ldpath_mtimes=prev_mtimes)
-                       self.dbdir = self.dbtmpdir
+                       dblink(self.cat, self.pkg, destroot, self.settings,
+                               vartree=self.vartree).unmerge(trimworld=0,
+                                       ldpath_mtimes=prev_mtimes, new_contents=new_contents)
                        writemsg_stdout(">>> Original instance of package unmerged safely.\n")
 
                # We hold both directory locks.
@@ -7805,9 +7810,7 @@ class dblink:
                                                        destmd5=portage_checksum.perform_md5(mydest,calc_prelink=1)
                                                        if mymd5==destmd5:
                                                                #file already in place; simply update mtimes of destination
-                                                               os.utime(mydest,(thismtime,thismtime))
-                                                               zing="---"
-                                                               moveme=0
+                                                               moveme = 1
                                                        else:
                                                                if mymd5 == cfgfiledict.get(myrealdest, [None])[0]:
                                                                        """ An identical update has previously been
@@ -7836,52 +7839,6 @@ class dblink:
                                        if mymtime is None:
                                                sys.exit(1)
                                        zing=">>>"
-                               else:
-                                       mymtime = long(time.time())
-                                       # We need to touch the destination so that on --update the
-                                       # old package won't yank the file with it. (non-cfgprot related)
-                                       os.utime(mydest, (mymtime, mymtime))
-                                       zing="---"
-                               if self.settings["USERLAND"] == "Darwin" and myrealdest[-2:] == ".a":
-
-                                       # XXX kludge, can be killed when portage stops relying on
-                                       # md5+mtime, and uses refcounts
-                                       # alright, we've fooled w/ mtime on the file; this pisses off static archives
-                                       # basically internal mtime != file's mtime, so the linker (falsely) thinks
-                                       # the archive is stale, and needs to have it's toc rebuilt.
-
-                                       myf = open(mydest, "r+")
-
-                                       # ar mtime field is digits padded with spaces, 12 bytes.
-                                       lms=str(thismtime+5).ljust(12)
-                                       myf.seek(0)
-                                       magic=myf.read(8)
-                                       if magic != "!<arch>\n":
-                                               # not an archive (dolib.a from portage.py makes it here fex)
-                                               myf.close()
-                                       else:
-                                               st = os.stat(mydest)
-                                               while myf.tell() < st.st_size - 12:
-                                                       # skip object name
-                                                       myf.seek(16,1)
-
-                                                       # update mtime
-                                                       myf.write(lms)
-
-                                                       # skip uid/gid/mperm
-                                                       myf.seek(20,1)
-
-                                                       # read the archive member's size
-                                                       x=long(myf.read(10))
-
-                                                       # skip the trailing newlines, and add the potential
-                                                       # extra padding byte if it's not an even size
-                                                       myf.seek(x + 2 + (x % 2),1)
-
-                                               # and now we're at the end. yay.
-                                               myf.close()
-                                               mymd5 = portage_checksum.perform_md5(mydest, calc_prelink=1)
-                                       os.utime(mydest,(thismtime,thismtime))
 
                                if mymtime!=None:
                                        zing=">>>"