diff -ruN portage.orig/pym/portage/__init__.py portage/pym/portage/__init__.py --- portage.orig/pym/portage/__init__.py 2009-12-04 11:18:06 +0100 +++ portage/pym/portage/__init__.py 2009-12-04 11:55:54 +0100 @@ -98,6 +98,7 @@ 'portage.versions:best,catpkgsplit,catsplit,endversion_keys,' + \ 'suffix_value@endversion,pkgcmp,pkgsplit,vercmp,ververify', 'portage.xpak', + 'portage.busy', ) import portage.const @@ -7503,13 +7504,19 @@ bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: - if stat.S_ISLNK(dstat[stat.ST_MODE]): + # done for all but dir now, to assure that busy files are moved + # out of the way before moving in the new files. + #if stat.S_ISLNK(dstat[stat.ST_MODE]): + if not stat.S_ISDIR(dstat[stat.ST_MODE]): try: os.unlink(dest) destexists=0 except SystemExit as e: raise except Exception as e: + # Failed to remove the existing file. Probably "Text file busy" + # FIXXME: how can i check whether its really a busy text file? + _getBusyFiles(mysettings).addBusyFile(dest) pass if stat.S_ISLNK(sstat[stat.ST_MODE]): @@ -9084,6 +9091,11 @@ close_portdbapi_caches() commit_mtimedb() + try: + _getBusyFiles().cleanBusyFiles() + except: + pass + atexit_register(portageexit) def _global_updates(trees, prev_mtimes): @@ -9470,6 +9482,21 @@ "flushmtimedb"): globals()[k] = _LegacyGlobalProxy(k) +busyfiles=None +def _getBusyFiles(mysettings=None): + global busyfiles + + # i know, if settings differ, you won't get another path to busydb, but + # as long as you get the same path _always_, this does not matter. + if busyfiles is None: + if mysettings is None: + global settings + mysettings = settings + + busyfiles = busy.BusyFiles(mysettings) + + return busyfiles + # Clear the cache dircache={} diff -ruN portage.orig/pym/portage/busy.py portage/pym/portage/busy.py --- portage.orig/pym/portage/busy.py 1970-01-01 01:00:00 +0100 +++ portage/pym/portage/busy.py 2009-12-03 14:33:39 +0100 @@ -0,0 +1,91 @@ +import portage +from portage.const import BUSY_PATH +from portage import os + +try: + import cPickle as pickle +except ImportError: + import pickle + +class BusyFiles: + settings = None + mypickle = None + + mybusy = [] + + def __init__(self, settings): + self.settings = settings + + if self.settings is None: + self.settings = globals()["settings"] + + self.mypickle = os.path.join(self.settings["ROOT"], BUSY_PATH) + + try: + my_f = open(self.mypickle, 'rb') + self.mybusy = pickle.load(my_f) + my_f.close() + except Exception: + # simply start with an empty list. the worst case here is, that + # we have temporary files floating around in var/tmp/busy + pass + + self.cleanBusyFiles() + + def getDestFilename(self, srcfile): + """calculate a destination filename based on the srcfile filename.""" + my_fn = ".$busy-" + srcfile + "." + my_num = 0 + + while os.access(my_fn + str(my_num), os.F_OK): + my_num += 1 + + return my_fn + str(my_num) + + def addBusyFile(self, srcfile): + """moves a source file to the busy file path and remembers it in the busydb.""" + + # before doing something expensive, try to (again) remove the file. + try: + os.unlink(srcfile) + return + except: + pass + + # ok, the file really insists on beeing busy, try to move it to the + # busy file db. + dst_dir = os.path.dirname(srcfile) + src_nam = os.path.basename(srcfile) + + destfile = os.path.join(dst_dir, self.getDestFilename(src_nam)) + + try: + os.rename(srcfile, destfile) + self.mybusy.append(destfile) + except: + # uh oh... cannot add busy file, something went wrong - let movefile + # blow up like in the good-old-days... + return + + self.flush() + + def flush(self): + """flushes the busydb file (pickles the current file list).""" + my_f = open(self.mypickle, 'wb') + pickle.dump(self.mybusy, my_f) + my_f.close() + pass + + def cleanBusyFiles(self): + for file in self.mybusy: + if not os.access(file, os.F_OK): + self.mybusy.remove(file) + continue + + try: + os.unlink(file) + self.mybusy.remove(file) + except: + pass + + self.flush() diff -ruN portage.orig/pym/portage/const.py portage/pym/portage/const.py --- portage.orig/pym/portage/const.py 2009-12-04 11:18:06 +0100 +++ portage/pym/portage/const.py 2009-12-03 14:33:39 +0100 @@ -57,6 +57,7 @@ # variables used with target_root (these need to be absolute, but not # have a leading '/' since they are used directly with os.path.join) VDB_PATH = EPREFIX_LSTRIP + "/" + "var/db/pkg" +BUSY_PATH = EPREFIX_LSTRIP + "/" + "var/db/busydb" CACHE_PATH = EPREFIX_LSTRIP + "/" + "var/cache/edb" PRIVATE_PATH = EPREFIX_LSTRIP + "/" + "var/lib/portage" WORLD_FILE = PRIVATE_PATH + "/world" diff -ruN portage.orig/pym/portage/dbapi/vartree.py portage/pym/portage/dbapi/vartree.py --- portage.orig/pym/portage/dbapi/vartree.py 2009-12-03 15:03:15 +0100 +++ portage/pym/portage/dbapi/vartree.py 2009-12-04 11:52:58 +0100 @@ -3417,7 +3417,12 @@ # Remove permissions to ensure that any hardlinks to # suid/sgid files are rendered harmless. os.chmod(file_name, 0) - os.unlink(file_name) + try: + os.unlink(file_name) + except: + # rather than failing, we'll use the BusyFiles registry to + # get the file out of our way, and have it deleted later. + portage._getBusyFiles(self.settings).addBusyFile(file_name) finally: if bsd_chflags and pflags != 0: # Restore the parent flags we saved before unlinking