From 818a3886b3081da3f236f307ef13842f3e6a9eba Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sat, 29 Sep 2007 20:25:12 +0000 Subject: [PATCH] Merged from trunk 7792:7838 | 7793 | When --with-bdeps=y is enabled for built packages, pull in | | zmedico | build time deps as requested, but marked them as | | | "satisfied" since they are not strictly required. This | | | allows more freedom in the merge order calculation for | | | solving circular dependencies. Don't convert to PDEPEND | | | since that could make --with-bdeps=y less effective if it | | | is used to adjust merge order to prevent built_with_use() | | | calls from failing. | | 7794 | Flush stdout before calling pkg_info() to ensure that | | zmedico | output always shows in the correct order. | | 7795 | Flush stderr and stdout if their file descriptors are in | | zmedico | fd_pipes at the beginning of spawn(). | | 7796 | In spawn(), initialize default fd_pipes before doing the | | zmedico | stdout/stderr flush. | | 7797 | When --deep is not enabled, many dependencies are dicarded | | zmedico | and left out of the digraph. This patch prevents | | | dependencies from being discarded in some cases where the | | | are needed in order to optimize merge order. It also | | | modifies the DepPriority.rebuild attribute so that it only | | | applies to build time dependencies. This leads to better | | | merge order in some cases when --deep is not enabled. For | | | example, `emerge xf86-input-keyboard xorg-server` will now | | | properly merge xorg-server before xf86-input-keyboard | | | (problem from bug #192254, comment #5). | | 7798 | tweak get_config() so that it only executes one external | | SpankMan | binary (sed) instead of chaining multiple ones | | 7799 | fix possible information leak vulnerability when doing a | | SpankMan | merge of configuration files and be better about handling | | | of whitespace in $TMP in a few places | | 7800 | s/note/not/ reported by p-y. | | zmedico | | | 7801 | Bug #190179 - Use `prelink --verify filename` to write the | | zmedico | temp file via stdout since --undo fails when run as a | | | normal non-superuser because it tries to chown the output | | | file. Also, use mkstemp() to eliminate the need for locking | | | the temp file. Thanks to Israel G. Lugo | | | for the initial patch. | | 7802 | Initialize ${TMP} before registering the die trap so that | | zmedico | we're sure which directory die will clean up. | | 7803 | Bug #74615 - Quote all file paths inside dispatch-conf | | zmedico | shell commands. | | 7804 | Bugs #168772 and #193695 - During unmerge, only ignore | | zmedico | specific exceptions raised from unlink() and rmdir() calls. | | 7805 | Bug #193695 - Add support for FreeBSD chflags during | | zmedico | unmerge. This code is adapted from the code that already | | | exists in movefile() for the merge phase. | | 7806 | In movefile() FreeBSD chflags handling, use chflags instead | | zmedico | of lchflags when temporarily adjusting the flags on the | | | parent directory since we want to follow any symlinks to | | | the real parent directory. | | 7807 | Bug #193695 - Add FreeBSD chflags support for rmdir() calls | | zmedico | during unmerge. | | 7808 | Bug #192341 - Eliminate the dependency on py-freebsd by | | zmedico | implementing it's chflags() and lchflags() functions as | | | wrappers around the chflags command (which should always be | | | available in any case). The functions are only called when | | | merging/unmerging files that actually have flags set so the | | | performance difference should be negligible. | | 7834 | Bug #192341 - When the chflags command does not exit | | zmedico | successfully, try to generate an informative error. First, | | | use stat or lstat to try and generate an ENOENT error. It | | | the path exists, verify that the chflags binary exists and | | | raise CommandNotFound if necessary. Finally, simply | | | generate an EPERM OSError with the output of the command | | | since we're not sure exactly why it failed or what the real | | | errno was. | | 7835 | Set non-blocking mode on the pty master file descriptor | | zmedico | while the slave file descriptor is still held open since | | | otherwise the fcntl call can fail on FreeBSD (the child | | | process might have already exited and closed the slave file | | | descriptor so we have to keep it open in order to avoid | | | FreeBSD potentially generating an EAGAIN exception). This | | | appoach is cleaner than triggering the exception and being | | | forced to handle it somehow. | | 7838 | Bug #192706 - Do not print a summary at the end of | | zmedico | --depclean if there is nothing to clean and --quiet is | | | enabled. | svn path=/main/branches/prefix/; revision=7882 --- bin/dispatch-conf | 6 +-- bin/etc-update | 57 ++++++++++++++++--------- cnf/dispatch-conf.conf | 4 +- man/repoman.1 | 2 +- pym/emerge/__init__.py | 63 +++++++++++++++++++--------- pym/portage/__init__.py | 81 +++++++++++++++++++++++++----------- pym/portage/checksum.py | 14 ++++--- pym/portage/dbapi/vartree.py | 75 ++++++++++++++++++++++++++------- pym/portage/dispatch_conf.py | 8 ++-- 9 files changed, 215 insertions(+), 95 deletions(-) diff --git a/bin/dispatch-conf b/bin/dispatch-conf index 972cf117c..439af5d45 100755 --- a/bin/dispatch-conf +++ b/bin/dispatch-conf @@ -29,9 +29,9 @@ from portage import dispatch_conf, const from portage.process import find_binary FIND_EXTANT_CONFIGS = "find '%s' %s -iname '._cfg????_%s' ! -iname '.*~' ! -iname '.*.bak'" -DIFF_CONTENTS = 'diff -Nu %s %s' -DIFF_CVS_INTERP = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "# .Header:.*"' -DIFF_WSCOMMENTS = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "^[-+]#" | grep -v "^[-+][:space:]*$"' +DIFF_CONTENTS = "diff -Nu '%s' '%s'" +DIFF_CVS_INTERP = "diff -Nu '%s' '%s' | grep '^[+-][^+-]' | grep -v '# .Header:.*'" +DIFF_WSCOMMENTS = "diff -Nu '%s' '%s' | grep '^[+-][^+-]' | grep -v '^[-+]#' | grep -v '^[-+][:space:]*$'" # We need a secure scratch dir and python does silly verbose errors on the use of tempnam oldmask = os.umask(0077) diff --git a/bin/etc-update b/bin/etc-update index e99cd0873..a59227c36 100755 --- a/bin/etc-update +++ b/bin/etc-update @@ -17,15 +17,21 @@ if type -P gsed >/dev/null ; then fi function get_config() { - item=$1 - - # First strip off comment lines, then grab the configuration - # item. If there's more than one of the same configuration item, - # then allow the last setting to take precedence. - local result - result=$(cut -d'#' -f1-1 ${PORTAGE_CONFIGROOT}etc/etc-update.conf | \ - sed -ne "s/^ *$item *= *\([\"']\{0,1\}\)\(.*\)\1/\2/p" |sed -e '$p;d') - eval echo $result + # the sed here does: + # - strip off comments + # - match lines that set item in question + # - delete the "item =" part + # - store the actual value into the hold space + # - on the last line, restore the hold space and print it + # If there's more than one of the same configuration item, then + # the store to the hold space clobbers previous value so the last + # setting takes precedence. + local item=$1 + eval echo $(sed -n \ + -e 's:[[:space:]]*#.*$::' \ + -e "/^[[:space:]]*$item[[:space:]]*=/{s:[^=]*=[[:space:]]*\([\"']\{0,1\}\)\(.*\)\1:\2:;h}" \ + -e '${g;p}' \ + "${PORTAGE_CONFIGROOT}"etc/etc-update.conf) } function scan() { @@ -361,18 +367,26 @@ Please select from the menu above (-1 to ignore this update): " } function do_merge() { + # make sure we keep the merged file in the secure tempdir + # so we dont leak any information contained in said file + # (think of case where the file has 0600 perms; during the + # merging process, the temp file gets umask perms!) local file="${1}" local ofile="${2}" - local mfile="${2}.merged" + local mfile="${TMP}/${2}.merged" local -i my_input=0 echo "${file} ${ofile} ${mfile}" - if [ -e ${mfile} ] ; then + if [[ -e ${mfile} ]] ; then echo "A previous version of the merged file exists, cleaning..." - rm ${rm_opts} ${mfile} + rm ${rm_opts} "${mfile}" fi + # since mfile will be like $TMP/path/to/original-file.merged, we + # need to make sure the full /path/to/ exists ahead of time + mkdir -p "${mfile%/*}" + until (( ${my_input} == -1 )); do echo "Merging ${file} and ${ofile}" $(echo "${merge_command}" | @@ -396,8 +410,8 @@ Please select from the menu above (-1 to exit, losing this merge): " chown "$(stat -f %Su:%Sg "${ofile}")" "${mfile}" chmod $(stat -f %Mp%Lp "${ofile}") "${mfile}" fi - mv ${mv_opts} ${mfile} ${ofile} - rm ${rm_opts} ${file} + mv ${mv_opts} "${mfile}" "${ofile}" + rm ${rm_opts} "${file}" return 255 ;; 2) ( echo "Showing differences between ${ofile} and ${mfile}" @@ -411,7 +425,7 @@ Please select from the menu above (-1 to exit, losing this merge): " 4) ${EDITOR:-nano -w} "${mfile}" continue ;; - 5) rm ${rm_opts} ${mfile} + 5) rm ${rm_opts} "${mfile}" return 0 ;; *) continue @@ -419,7 +433,7 @@ Please select from the menu above (-1 to exit, losing this merge): " esac done done - rm ${rm_opts} ${mfile} + rm ${rm_opts} "${mfile}" return 255 } @@ -433,7 +447,7 @@ function die() { [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" fi - rm -rf ${TMP} + rm -rf "${TMP}" exit ${2} } @@ -442,6 +456,7 @@ function die() { # scriptname=$(basename $0) +TMP="${PORTAGE_TMPDIR}/etc-update-$$" trap die term type portageq > /dev/null || exit $? @@ -459,9 +474,11 @@ PORTAGE_CONFIGROOT=${PORTAGE_CONFIGROOT}${EPREFIX}/ #echo $CONFIG_PROTECT_MASK #export PORTAGE_TMPDIR=$(/usr/lib/portage/bin/portageq envvar PORTAGE_TMPDIR) -TMP="${PORTAGE_TMPDIR}/$$" -rm -rf ${TMP} 2> /dev/null -mkdir ${TMP} || die "failed mkdir command!" 1 +rm -rf "${TMP}" 2> /dev/null +mkdir "${TMP}" || die "failed to create temp dir" 1 +# make sure we have a secure directory to work in +chmod 0700 "${TMP}" || die "failed to set perms on temp dir" 1 +chown ${UID:-0}:${GID:-0} "${TMP}" || die "failed to set ownership on temp dir" 1 # I need the CONFIG_PROTECT value #CONFIG_PROTECT=$(@PORTAGE_BASE@/bin/portageq envvar CONFIG_PROTECT) diff --git a/cnf/dispatch-conf.conf b/cnf/dispatch-conf.conf index 5a1a39a72..6a94a52ec 100644 --- a/cnf/dispatch-conf.conf +++ b/cnf/dispatch-conf.conf @@ -12,13 +12,13 @@ use-rcs=no # Diff for display # %s old file # %s new file -diff="diff -Nu %s %s | less --no-init --QUIT-AT-EOF" +diff="diff -Nu '%s' '%s' | less --no-init --QUIT-AT-EOF" # Diff for interactive merges. # %s output file # %s old file # %s new file -merge="sdiff --suppress-common-lines --output=%s %s %s" +merge="sdiff --suppress-common-lines --output='%s' '%s' '%s'" # Automerge files comprising only CVS interpolations (e.g. Header or Id) # (yes or no) diff --git a/man/repoman.1 b/man/repoman.1 index 98efbed09..7b97a0abc 100644 --- a/man/repoman.1 +++ b/man/repoman.1 @@ -226,7 +226,7 @@ Error generating cache entry for ebuild; typically caused by ebuild syntax error File is not UTF8 compliant .TP .B file.executable -Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do note need the executable bit +Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do not need the executable bit .TP .B file.name File/dir name must be composed of only the following chars: a-zA-Z0-9._-+: diff --git a/pym/emerge/__init__.py b/pym/emerge/__init__.py index 9b282f0a2..eff84886f 100644 --- a/pym/emerge/__init__.py +++ b/pym/emerge/__init__.py @@ -716,9 +716,9 @@ class DepPriority(object): return -1 if self.runtime_post: return -2 - if self.rebuild: - return -3 if self.buildtime: + if self.rebuild: + return -3 return -4 if self.runtime: return -5 @@ -1356,11 +1356,20 @@ class depgraph(object): if "--buildpkgonly" in self.myopts: edepend["RDEPEND"] = "" edepend["PDEPEND"] = "" - if not (arg and "--onlydeps" in self.myopts and \ - mytype == "ebuild") and \ - self.myopts.get("--with-bdeps", "n") == "n" and \ - (mytype == "binary" or mybigkey[3] == "nomerge"): - edepend["DEPEND"] = "" + bdeps_satisfied = False + if mytype in ("installed", "binary"): + if self.myopts.get("--with-bdeps", "n") == "y": + # Pull in build time deps as requested, but marked them as + # "satisfied" since they are not strictly required. This allows + # more freedom in the merge order calculation for solving + # circular dependencies. Don't convert to PDEPEND since that + # could make --with-bdeps=y less effective if it is used to + # adjust merge order to prevent built_with_use() calls from + # failing. + bdeps_satisfied = True + else: + # built packages do not have build time dependencies. + edepend["DEPEND"] = "" """ We have retrieve the dependency information, now we need to recursively process them. DEPEND gets processed for root = "/", {R,P}DEPEND in myroot. """ @@ -1369,7 +1378,8 @@ class depgraph(object): try: if not self.select_dep("/", edepend["DEPEND"], myparent=mp, - myuse=myuse, priority=DepPriority(buildtime=True), + myuse=myuse, priority=DepPriority(buildtime=True, + satisfied=bdeps_satisfied), parent_arg=arg): return 0 """RDEPEND is soft by definition. However, in order to ensure @@ -1726,17 +1736,6 @@ class depgraph(object): ("blocks", p_root, x[1:]), set()).add(myparent) continue else: - #We are not processing a blocker but a normal dependency - if myparent: - """In some cases, dep_check will return deps that shouldn't - be proccessed any further, so they are identified and - discarded here.""" - if "empty" not in self.myparams and \ - "deep" not in self.myparams and \ - not ("--update" in self.myopts and parent_arg) and \ - vardb.match(x): - continue - # List of acceptable packages, ordered by type preference. matched_packages = [] myeb_matches = portdb.xmatch("match-visible", x) @@ -1956,6 +1955,29 @@ class depgraph(object): # ordered by type preference ("ebuild" type is the last resort) selected_pkg = matched_packages[0] + # In some cases, dep_check will return deps that shouldn't + # be proccessed any further, so they are identified and + # discarded here. Try to discard as few as possible since + # discarded dependencies reduce the amount of information + # available for optimization of merge order. + if myparent and not arg and vardb.match(x) and \ + not existing_node and \ + "empty" not in self.myparams and \ + "deep" not in self.myparams and \ + not ("--update" in self.myopts and parent_arg): + (mytype, myroot, mykey), metadata = selected_pkg + myarg = None + if myroot == self.target_root: + try: + myarg = self._set_atoms.findAtomForPackage( + mykey, metadata) + except portage.exception.InvalidDependString: + # This is already handled inside + # self.create() when necessary. + pass + if not myarg: + continue + if myparent: #we are a dependency, so we want to be unconditionally added mypriority = priority.copy() @@ -5769,6 +5791,9 @@ def action_depclean(settings, trees, ldpath_mtimes, if action == "prune": return + if not cleanlist and "--quiet" in myopts: + return + print "Packages installed: "+str(len(myvarlist)) print "Packages in world: "+str(len(worldlist)) print "Packages in system: "+str(len(syslist)) diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index de86361c6..852f7f5f2 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -42,10 +42,32 @@ except ImportError, e: bsd_chflags = None if os.uname()[0] in ["FreeBSD"]: - try: - import freebsd as bsd_chflags - except ImportError: + def bsd_chflags(): pass + def _chflags(path, flags, opts=""): + cmd = "chflags %s %o '%s'" % (opts, flags, path) + status, output = commands.getstatusoutput(cmd) + if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK: + return + # Try to generate an ENOENT error if appropriate. + if "h" in opts: + os.lstat(path) + else: + os.stat(path) + # Make sure the binary exists. + if not portage.process.find_binary("chflags"): + raise portage.exception.CommandNotFound("chflags") + # Now we're not sure exactly why it failed or what + # the real errno was, so just report EPERM. + e = OSError(errno.EPERM, output) + e.errno = errno.EPERM + e.filename = path + e.message = output + raise e + def _lchflags(path, flags): + return _chflags(path, flags, opts="-h") + bsd_chflags.chflags = _chflags + bsd_chflags.lchflags = _lchflags try: from portage.cache.cache_errors import CacheError @@ -2394,6 +2416,18 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero env=mysettings.environ() keywords["opt_name"]="[%s]" % mysettings["PF"] + fd_pipes = keywords.get("fd_pipes") + if fd_pipes is None: + fd_pipes = {0:0, 1:1, 2:2} + # In some cases the above print statements don't flush stdout, so + # it needs to be flushed before allowing a child process to use it + # so that output always shows in the correct order. + for fd in fd_pipes.itervalues(): + if fd == sys.stdout.fileno(): + sys.stdout.flush() + if fd == sys.stderr.fileno(): + sys.stderr.flush() + # The default policy for the sesandbox domain only allows entry (via exec) # from shells and from binaries that belong to portage (the number of entry # points is minimized). The "tee" binary is not among the allowed entry @@ -2407,10 +2441,7 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero got_pty = False if logfile: del keywords["logfile"] - fd_pipes = keywords.get("fd_pipes") - if fd_pipes is None: - fd_pipes = {0:0, 1:1, 2:2} - elif 1 not in fd_pipes or 2 not in fd_pipes: + if 1 not in fd_pipes or 2 not in fd_pipes: raise ValueError(fd_pipes) from pty import openpty try: @@ -2420,6 +2451,16 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero writemsg("openpty failed: '%s'\n" % str(e), noiselevel=1) del e master_fd, slave_fd = os.pipe() + + # We must set non-blocking mode before we close the slave_fd + # since otherwise the fcntl call can fail on FreeBSD (the child + # process might have already exited and closed slave_fd so we + # have to keep it open in order to avoid FreeBSD potentially + # generating an EAGAIN exception). + import fcntl + fcntl.fcntl(master_fd, fcntl.F_SETFL, + fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK) + fd_pipes.setdefault(0, sys.stdin.fileno()) fd_pipes_orig = fd_pipes.copy() if got_pty and os.isatty(fd_pipes_orig[1]): @@ -2466,7 +2507,7 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero try: mypids.extend(spawn_func(mystring, env=env, **keywords)) finally: - if slave_fd: + if logfile: os.close(slave_fd) if sesandbox: selinux.setexec(None) @@ -2481,26 +2522,14 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero iwtd = [master_file] owtd = [] ewtd = [] - import array, fcntl, select - fd_flags = {} - for f in iwtd: - fd_flags[f] = fcntl.fcntl(f.fileno(), fcntl.F_GETFL) + import array, select buffsize = 65536 eof = False - # Use non-blocking mode to prevent read - # calls from blocking indefinitely. - try: - fcntl.fcntl(master_file.fileno(), fcntl.F_SETFL, - fd_flags[master_file] | os.O_NONBLOCK) - except EnvironmentError, e: - if e.errno != errno.EAGAIN: - raise - del e - # The EAGAIN error signals eof on FreeBSD. - eof = True while not eof: events = select.select(iwtd, owtd, ewtd) for f in events[0]: + # Use non-blocking mode to prevent read + # calls from blocking indefinitely. buf = array.array('B') try: buf.fromfile(f, buffsize) @@ -4191,9 +4220,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]): @@ -4300,7 +4331,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 diff --git a/pym/portage/checksum.py b/pym/portage/checksum.py index fa00247ab..c663b68d9 100644 --- a/pym/portage/checksum.py +++ b/pym/portage/checksum.py @@ -7,6 +7,7 @@ from portage.const import PRIVATE_PATH,PRELINK_BINARY,HASHING_BLOCKSIZE import os import errno import stat +import tempfile import portage.exception import portage.process import portage.locks @@ -198,16 +199,19 @@ def perform_checksum(filename, hashname="MD5", calc_prelink=0): """ global prelink_capable myfilename = filename[:] - prelink_tmpfile = os.path.join("/", PRIVATE_PATH, "prelink-checksum.tmp." + str(os.getpid())) + prelink_tmpfile = None mylock = None try: if calc_prelink and prelink_capable: - mylock = portage.locks.lockfile(prelink_tmpfile, wantnewlockfile=1) # Create non-prelinked temporary file to checksum. # Files rejected by prelink are summed in place. try: - retval = portage.process.spawn([PRELINK_BINARY, "--undo", "-o", - prelink_tmpfile, filename], fd_pipes={}) + tmpfile_fd, prelink_tmpfile = tempfile.mkstemp() + try: + retval = portage.process.spawn([PRELINK_BINARY, + "--verify", filename], fd_pipes={1:tmpfile_fd}) + finally: + os.close(tmpfile_fd) if retval == os.EX_OK: myfilename = prelink_tmpfile except portage.exception.CommandNotFound: @@ -222,7 +226,7 @@ def perform_checksum(filename, hashname="MD5", calc_prelink=0): if e.errno == errno.ENOENT: raise portage.exception.FileNotFound(myfilename) raise - if calc_prelink and prelink_capable: + if prelink_tmpfile: try: os.unlink(prelink_tmpfile) except OSError, e: diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 855b2b5b8..064db4d2f 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1184,7 +1184,30 @@ class dblink(object): #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)) @@ -1234,13 +1257,11 @@ class dblink(object): 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 @@ -1266,9 +1287,12 @@ class dblink(object): # 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: + 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): @@ -1288,13 +1312,11 @@ class dblink(object): 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]): @@ -1309,10 +1331,31 @@ class dblink(object): 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 EnvironmentError: - 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) diff --git a/pym/portage/dispatch_conf.py b/pym/portage/dispatch_conf.py index b88d2cf73..df256dc77 100644 --- a/pym/portage/dispatch_conf.py +++ b/pym/portage/dispatch_conf.py @@ -16,9 +16,9 @@ RCS_BRANCH = '1.1.1' RCS_LOCK = 'rcs -ko -M -l' RCS_PUT = 'ci -t-"Archived config file." -m"dispatch-conf update."' RCS_GET = 'co' -RCS_MERGE = 'rcsmerge -p -r' + RCS_BRANCH + ' %s >%s' +RCS_MERGE = "rcsmerge -p -r" + RCS_BRANCH + " '%s' > '%s'" -DIFF3_MERGE = 'diff3 -mE %s %s %s >%s' +DIFF3_MERGE = "diff3 -mE '%s' '%s' '%s' > '%s'" def read_config(mandatory_opts): try: @@ -33,7 +33,7 @@ def read_config(mandatory_opts): for key in mandatory_opts: if not opts.has_key(key): if key == "merge": - opts["merge"] = "sdiff --suppress-common-lines --output=%s %s %s" + opts["merge"] = "sdiff --suppress-common-lines --output='%s' '%s' '%s'" else: print >> sys.stderr, 'dispatch-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal' % (key,) @@ -106,7 +106,7 @@ def file_archive(archive, curconf, newconf, mrgconf): # Archive the current config file if it isn't already saved if os.path.exists(archive) \ - and len(commands.getoutput('diff -aq %s %s' % (curconf,archive))) != 0: + and len(commands.getoutput("diff -aq '%s' '%s'" % (curconf,archive))) != 0: suf = 1 while suf < 9 and os.path.exists(archive + '.' + str(suf)): suf += 1 -- 2.26.2