From 2aec000c6ac2b236bdd9dba00d3a001d7fdcea7b Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Wed, 26 Sep 2007 04:46:33 +0000 Subject: Bugs #168772 and #193695 - During unmerge, only ignore specific exceptions raised from unlink() and rmdir() calls. Bug #193695 - Add support for FreeBSD chflags during unmerge. This code is adapted from the code that already exists in movefile() for the merge phase. In movefile() FreeBSD chflags handling, use chflags instead of lchflags when temporarily adjusting the flags on the parent directory since we want to follow any symlinks to the real parent directory. (trunk r7803:7807) svn path=/main/branches/2.1.2/; revision=7832 --- pym/portage.py | 83 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/pym/portage.py b/pym/portage.py index 47d204b63..39ecb0dab 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -4148,9 +4148,11 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None): if bsd_chflags: if destexists and dstat.st_flags != 0: bsd_chflags.lchflags(dest, 0) + # Use normal stat/chflags for the parent since we want to + # follow any symlinks to the real parent directory. pflags = os.stat(os.path.dirname(dest)).st_flags if pflags != 0: - bsd_chflags.lchflags(os.path.dirname(dest), 0) + bsd_chflags.chflags(os.path.dirname(dest), 0) if destexists: if stat.S_ISLNK(dstat[stat.ST_MODE]): @@ -4257,7 +4259,7 @@ def movefile(src,dest,newmtime=None,sstat=None,mysettings=None): if bsd_chflags: # Restore the flags we saved before moving if pflags: - bsd_chflags.lchflags(os.path.dirname(dest), pflags) + bsd_chflags.chflags(os.path.dirname(dest), pflags) return newmtime @@ -7446,7 +7448,30 @@ class dblink: #process symlinks second-to-last, directories last. mydirs = [] + ignored_unlink_errnos = (errno.ENOENT, errno.EISDIR) modprotect = os.path.join(self.vartree.root, "lib/modules/") + + def unlink(file_name, lstatobj): + if bsd_chflags: + if lstatobj.st_flags != 0: + bsd_chflags.lchflags(file_name, 0) + parent_name = os.path.dirname(file_name) + # Use normal stat/chflags for the parent since we want to + # follow any symlinks to the real parent directory. + pflags = os.stat(parent_name).st_flags + if pflags != 0: + bsd_chflags.chflags(parent_name, 0) + try: + if not stat.S_ISLNK(lstatobj.st_mode): + # Remove permissions to ensure that any hardlinks to + # suid/sgid files are rendered harmless. + os.chmod(file_name, 0) + os.unlink(file_name) + finally: + if bsd_chflags and pflags != 0: + # Restore the parent flags we saved before unlinking + bsd_chflags.chflags(parent_name, pflags) + def show_unmerge(zing, desc, file_type, file_name): writemsg_stdout("%s %s %s %s\n" % \ (zing, desc.ljust(8), file_type, file_name)) @@ -7496,13 +7521,11 @@ class dblink: not (islink and statobj and stat.S_ISDIR(statobj.st_mode)) and \ not self.isprotected(obj): try: - # Remove permissions to ensure that any hardlinks to - # suid/sgid files are rendered harmless. - if statobj and not islink: - os.chmod(obj, 0) - os.unlink(obj) + unlink(obj, lstatobj) except EnvironmentError, e: - pass + if e.errno not in ignored_unlink_errnos: + raise + del e show_unmerge("<<<", "", file_type, obj) continue @@ -7528,9 +7551,12 @@ class dblink: # contents as a directory even if it happens to correspond # to a symlink when it's merged to the live filesystem. try: - os.unlink(obj) + unlink(obj, lstatobj) show_unmerge("<<<", "", file_type, obj) - except (OSError,IOError),e: + except (OSError, IOError),e: + if e.errno not in ignored_unlink_errnos: + raise + del e show_unmerge("!!!", "", file_type, obj) elif pkgfiles[objkey][0] == "obj": if statobj is None or not stat.S_ISREG(statobj.st_mode): @@ -7550,13 +7576,11 @@ class dblink: show_unmerge("---", "!md5", file_type, obj) continue try: - # Remove permissions to ensure that any hardlinks to - # suid/sgid files are rendered harmless. - if not islink: - os.chmod(obj, 0) - os.unlink(obj) + unlink(obj, lstatobj) except (OSError,IOError),e: - pass + if e.errno not in ignored_unlink_errnos: + raise + del e show_unmerge("<<<", "", file_type, obj) elif pkgfiles[objkey][0] == "fif": if not stat.S_ISFIFO(lstatobj[stat.ST_MODE]): @@ -7571,10 +7595,31 @@ class dblink: for obj in mydirs: try: - os.rmdir(obj) + if bsd_chflags: + lstatobj = os.lstat(obj) + if lstatobj.st_flags != 0: + bsd_chflags.lchflags(obj, 0) + parent_name = os.path.dirname(obj) + # Use normal stat/chflags for the parent since we want to + # follow any symlinks to the real parent directory. + pflags = os.stat(parent_name).st_flags + if pflags != 0: + bsd_chflags.chflags(parent_name, 0) + try: + os.rmdir(obj) + finally: + if bsd_chflags and pflags != 0: + # Restore the parent flags we saved before unlinking + bsd_chflags.chflags(parent_name, pflags) show_unmerge("<<<", "", "dir", obj) - except (OSError, IOError): - show_unmerge("---", "!empty", "dir", obj) + except EnvironmentError, e: + if e.errno not in (errno.ENOENT, + errno.EEXIST, errno.ENOTEMPTY, + errno.ENOTDIR): + raise + if e.errno != errno.ENOENT: + show_unmerge("---", "!empty", "dir", obj) + del e #remove self from vartree database so that our own virtual gets zapped if we're the last node self.vartree.zap(self.mycpv) -- cgit v1.2.3-1-g7c22