From 9afcc2a0547ed77733c714050336d98fbda0821b Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 8 Jun 2007 19:54:15 +0000 Subject: For bug #181021, preserve timestamps from during merge. For replacement operations, the unmerge phase checks the contents of the newer instance in order to prevent identical files in the newer instance from being unmerged. (trunk r6739, r6740, and r6745) svn path=/main/branches/2.1.2/; revision=6760 --- pym/portage.py | 145 ++++++++++++++++++++------------------------------------- 1 file changed, 51 insertions(+), 94 deletions(-) (limited to 'pym') diff --git a/pym/portage.py b/pym/portage.py index d52a6da57..0719aa23d 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -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 != "!\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=">>>" -- cgit v1.2.3-1-g7c22