Merged from trunk 5113:5119
authorFabian Groffen <grobian@gentoo.org>
Wed, 22 Nov 2006 18:38:15 +0000 (18:38 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 22 Nov 2006 18:38:15 +0000 (18:38 -0000)
For bug #142993, make sure that chown and chgrp calls preserve S_ISUID
and S_ISGID mode bits.

Fix apply_permissions logic for cases where mode is unspecified.

Fix bit mask support when preserving S_ISUID and S_ISGID in
apply_permissions.
Really fix bit mask support when preserving S_ISUID and S_ISGID in
apply_permissions.

Fix a KeyError in emerge --info <pkg> when there is no matching ebuild
in the tree.  Thanks to Bo ?\195?\152rsted Andresen
<bo.andresen@zlin.dk> for reporting.

Use portage_data.lchown for compatibility.

svn path=/main/branches/prefix/; revision=5120

bin/emerge
pym/portage.py
pym/portage_util.py

index 6a4f70dc34853645af860602e8c18dd90bb8a6e2..949587aeb389dc2836f99980a03cc9995db46858 100755 (executable)
@@ -3827,7 +3827,13 @@ def action_info(settings, trees, myopts, myfiles):
                        valuesmap["IUSE"] = set(filter_iuse_defaults(valuesmap["IUSE"]))
                        valuesmap["USE"] = valuesmap["USE"].intersection(valuesmap["IUSE"])
                        pkgsettings.reset()
-                       pkgsettings.setcpv(pkg, mydb=portdb)
+                       # If a matching ebuild is no longer available in the tree, maybe it
+                       # would make sense to compare against the flags for the best
+                       # available version with the same slot?
+                       mydb = None
+                       if portdb.cpv_exists(pkg):
+                               mydb = portdb
+                       pkgsettings.setcpv(pkg, mydb=mydb)
                        if valuesmap["IUSE"].intersection(pkgsettings["USE"].split()) != \
                                valuesmap["USE"]:
                                diff_values["USE"] = valuesmap["USE"]
index 2667aff374727f7516bdb2fe8aaea0acc8724028..6c489e229601d3307e5ed52ceb47311eb4d63747 100644 (file)
@@ -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())
 
@@ -2618,6 +2628,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:
index 086f3b741030c89e01ac1ddcb4c476272401fcde..7a312deb49b1fec293d175a92340a5ffdb4cee04 100644 (file)
@@ -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,11 @@ 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:
+                               import portage_data
+                               portage_data.lchown(filename, uid, gid)
                        modified = True
                except OSError, oe:
                        func_call = "chown('%s', %i, %i)" % (filename, uid, gid)
@@ -548,6 +555,26 @@ 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):
+               if mode == -1:
+                       new_mode = st_mode
+               else:
+                       mode = mode & 07777
+                       if mask >= 0:
+                               new_mode = mode | st_mode
+                               new_mode = (mask ^ new_mode) & new_mode
+                       else:
+                               new_mode = mode
+                       if not (new_mode & stat.S_ISUID or new_mode & stat.S_ISGID):
+                               new_mode = -1
+
+       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 +641,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 +652,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 +683,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):