summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/misc-functions.sh11
-rw-r--r--pym/portage.py33
-rw-r--r--pym/portage_util.py32
3 files changed, 59 insertions, 17 deletions
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 3a1826e66..0d7244087 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -182,17 +182,6 @@ install_qa_check() {
unset INSTALLTOD
fi
- local find_log="${T}/find-portage-log"
- find "${D}"/ -user portage -print0 > "${find_log}"
- if [[ -s ${find_log} ]] ; then
- xargs -0 chown -h ${PORTAGE_INST_UID:-0} < "${find_log}"
- fi
- find "${D}"/ -group portage -print0 > "${find_log}"
- if [[ -s ${find_log} ]] ; then
- xargs -0 chgrp -h ${PORTAGE_INST_GID:-0} < "${find_log}"
- fi
- rm -f "${find_log}"
-
# Portage regenerates this on the installed system.
if [ -f "${D}/usr/share/info/dir.gz" ]; then
rm -f "${D}/usr/share/info/dir.gz"
diff --git a/pym/portage.py b/pym/portage.py
index 443a871f5..5422e653a 100644
--- a/pym/portage.py
+++ b/pym/portage.py
@@ -1344,6 +1344,16 @@ class config:
self["PORTAGE_PYM_PATH"] = PORTAGE_PYM_PATH
self.backup_changes("PORTAGE_PYM_PATH")
+ for var in ("PORTAGE_INST_UID", "PORTAGE_INST_GID"):
+ try:
+ self[var] = str(int(self.get(var, "0")))
+ except ValueError:
+ writemsg(("!!! %s='%s' is not a valid integer. " + \
+ "Falling back to '0'.\n") % (var, self[var]),
+ noiselevel=-1)
+ self[var] = "0"
+ self.backup_changes(var)
+
self.regenerate()
self.features = portage_util.unique_array(self["FEATURES"].split())
@@ -2614,6 +2624,29 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None):
if phase_retval == os.EX_OK:
if mydo == "install":
+ # User and group bits that match the "portage" user or group are
+ # automatically mapped to PORTAGE_INST_UID and PORTAGE_INST_GID if
+ # necessary. The chown system call may clear S_ISUID and S_ISGID
+ # bits, so those bits are restored if necessary.
+ from itertools import chain
+ inst_uid = int(mysettings["PORTAGE_INST_UID"])
+ inst_gid = int(mysettings["PORTAGE_INST_GID"])
+ for parent, dirs, files in os.walk(mysettings["D"]):
+ for fname in chain(dirs, files):
+ fpath = os.path.join(parent, fname)
+ mystat = os.lstat(fpath)
+ if mystat.st_uid != portage_uid and \
+ mystat.st_gid != portage_gid:
+ continue
+ myuid = -1
+ mygid = -1
+ if mystat.st_uid == portage_uid:
+ myuid = inst_uid
+ if mystat.st_gid == portage_gid:
+ mygid = inst_gid
+ apply_secpass_permissions(fpath, uid=myuid, gid=mygid,
+ mode=mystat.st_mode, stat_cached=mystat,
+ follow_links=False)
mycommand = " ".join([MISC_SH_BINARY, "install_qa_check"])
qa_retval = spawn(mycommand, mysettings, debug=debug, logfile=logfile, **kwargs)
if qa_retval:
diff --git a/pym/portage_util.py b/pym/portage_util.py
index a0f8bc987..4c7e3176f 100644
--- a/pym/portage_util.py
+++ b/pym/portage_util.py
@@ -489,7 +489,7 @@ def unique_array(s):
return u
def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
- stat_cached=None):
+ stat_cached=None, follow_links=True):
"""Apply user, group, and mode bits to a file if the existing bits do not
already match. The default behavior is to force an exact match of mode
bits. When mask=0 is specified, mode bits on the target file are allowed
@@ -502,7 +502,10 @@ def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
if stat_cached is None:
try:
- stat_cached = os.stat(filename)
+ if follow_links:
+ stat_cached = os.stat(filename)
+ else:
+ stat_cached = os.lstat(filename)
except OSError, oe:
func_call = "stat('%s')" % filename
if oe.errno == errno.EPERM:
@@ -517,7 +520,10 @@ def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
if (uid != -1 and uid != stat_cached.st_uid) or \
(gid != -1 and gid != stat_cached.st_gid):
try:
- os.chown(filename, uid, gid)
+ if follow_links:
+ os.chown(filename, uid, gid)
+ else:
+ os.lchown(filename, uid, gid)
modified = True
except OSError, oe:
func_call = "chown('%s', %i, %i)" % (filename, uid, gid)
@@ -548,6 +554,16 @@ def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
if mode != st_mode:
new_mode = mode
+ # The chown system call may clear S_ISUID and S_ISGID
+ # bits, so those bits are restored if necessary.
+ if modified and new_mode == -1 and \
+ (st_mode & stat.S_ISUID or st_mode & stat.S_ISGID):
+ new_mode = mode & 07777
+
+ if not follow_links and stat.S_ISLNK(stat_cached.st_mode):
+ # Mode doesn't matter for symlinks.
+ new_mode = -1
+
if new_mode != -1:
try:
os.chmod(filename, new_mode)
@@ -614,7 +630,7 @@ def apply_recursive_permissions(top, uid=-1, gid=-1,
return all_applied
def apply_secpass_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
- stat_cached=None):
+ stat_cached=None, follow_links=True):
"""A wrapper around apply_permissions that uses secpass and simple
logic to apply as much of the permissions as possible without
generating an obviously avoidable permission exception. Despite
@@ -625,7 +641,10 @@ def apply_secpass_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
if stat_cached is None:
try:
- stat_cached = os.stat(filename)
+ if follow_links:
+ stat_cached = os.stat(filename)
+ else:
+ stat_cached = os.lstat(filename)
except OSError, oe:
func_call = "stat('%s')" % filename
if oe.errno == errno.EPERM:
@@ -653,7 +672,8 @@ def apply_secpass_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
all_applied = False
gid = -1
- apply_permissions(filename, uid=uid, gid=gid, mode=mode, mask=mask, stat_cached=stat_cached)
+ apply_permissions(filename, uid=uid, gid=gid, mode=mode, mask=mask,
+ stat_cached=stat_cached, follow_links=follow_links)
return all_applied
class atomic_ofstream(file):