PORTAGE_XATTR_EXCLUDE="security.*", bug #461868
authorZac Medico <zmedico@gentoo.org>
Wed, 27 Mar 2013 08:21:46 +0000 (01:21 -0700)
committerZac Medico <zmedico@gentoo.org>
Wed, 27 Mar 2013 08:21:46 +0000 (01:21 -0700)
cnf/make.globals
man/make.conf.5
pym/portage/package/ebuild/_config/special_env_vars.py
pym/portage/util/movefile.py

index 80a68f7d65dadcde4d9f51bb1a3ea2fe2a3f4520..f0597c1236db15797b69090ad9ccbd0d027c7174 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 1999-2011 Gentoo Foundation
+# Copyright 1999-2013 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 # System-wide defaults for the Portage system
 
@@ -123,6 +123,9 @@ PORTAGE_ELOG_MAILFROM="portage@localhost"
 # Signing command used by repoman
 PORTAGE_GPG_SIGNING_COMMAND="gpg --sign --digest-algo SHA256 --clearsign --yes --default-key \"\${PORTAGE_GPG_KEY}\" --homedir \"\${PORTAGE_GPG_DIR}\" \"\${FILE}\""
 
+# Security labels are special, see bug #461868.
+PORTAGE_XATTR_EXCLUDE="security.*"
+
 #            *****************************
 #            **  DO NOT EDIT THIS FILE  **
 # ***************************************************
index 42a5b254dc557ee0e10d23c5ad767f9c6ce0d7ea..5e35546aefa7b158533cd532ee50c1c7c0ba3134 100644 (file)
@@ -1,4 +1,4 @@
-.TH "MAKE.CONF" "5" "Jan 2013" "Portage VERSION" "Portage"
+.TH "MAKE.CONF" "5" "Mar 2013" "Portage VERSION" "Portage"
 .SH "NAME"
 make.conf \- custom settings for Portage
 .SH "SYNOPSIS"
@@ -573,7 +573,8 @@ Enable GPG verification when using \fIemerge\-webrsync\fR.
 .TP
 .B xattr
 Preserve extended attributes (filesystem-stored metadata) when installing
-files (see \fBattr\fR(1)).
+files (see \fBattr\fR(1)). The \fBPORTAGE_XATTR_EXCLUDE\fR variable may be
+used to exclude specific attributes from being preserved.
 .RE
 .TP
 .B FETCHCOMMAND
@@ -859,6 +860,12 @@ Defaults to portage.
 \fBPORTAGE_WORKDIR_MODE\fR = \fI"0700"\fR
 This variable controls permissions for \fIWORKDIR\fR (see \fBebuild\fR(5)).
 .TP
+\fBPORTAGE_XATTR_EXCLUDE\fR = \fI[space delimited list of fnmatch patterns]\fR
+This variable may be used to exclude specific attributes from being preserved
+when \fBxattr\fR is in \fBFEATURES\fR.
+.br
+Defaults to "security.*" (security labels are special, see bug #461868).
+.TP
 \fBPORTDIR\fR = \fI[path]\fR
 Defines the location of the Portage tree. This is the repository for all
 profile information as well as all ebuilds. If you change this, you must update
index 8e85a4686e51ba4cc72de55c94778d4684307163..0b4dc39a2efa1cf5af2f01ff8aed33b2e672856e 100644 (file)
@@ -170,7 +170,8 @@ environ_filter += [
        "PORTAGE_RO_DISTDIRS",
        "PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
        "PORTAGE_RSYNC_RETRIES", "PORTAGE_SYNC_STALE",
-       "PORTAGE_USE", "PORT_LOGDIR", "PORT_LOGDIR_CLEAN",
+       "PORTAGE_USE", "PORTAGE_XATTR_EXCLUDE",
+       "PORT_LOGDIR", "PORT_LOGDIR_CLEAN",
        "QUICKPKG_DEFAULT_OPTS", "REPOMAN_DEFAULT_OPTS",
        "RESUMECOMMAND", "RESUMECOMMAND_FTP",
        "RESUMECOMMAND_HTTP", "RESUMECOMMAND_HTTPS",
index bf95f1a40908ab1795f830a5b2d118372192c5ca..8a7a2b6a65409869254564ce3ae3a9a10f1a3ecd 100644 (file)
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
 __all__ = ['movefile']
 
 import errno
+import fnmatch
 import os as _os
 import shutil as _shutil
 import stat
@@ -27,10 +28,59 @@ def _apply_stat(src_stat, dest):
        _os.chown(dest, src_stat.st_uid, src_stat.st_gid)
        _os.chmod(dest, stat.S_IMODE(src_stat.st_mode))
 
+_xattr_excluder_cache = {}
+
+def _get_xattr_excluder(pattern):
+
+       try:
+               value = _xattr_excluder_cache[pattern]
+       except KeyError:
+               value = _xattr_excluder(pattern)
+               _xattr_excluder_cache[pattern] = value
+
+       return value
+
+class _xattr_excluder(object):
+
+       __slots__ = ('_pattern_split',)
+
+       def __init__(self, pattern):
+
+               if pattern is None:
+                       self._pattern_split = None
+               else:
+                       pattern = pattern.split()
+                       if not pattern:
+                               self._pattern_split = None
+                       else:
+                               pattern.sort()
+                               self._pattern_split = tuple(pattern)
+
+       def __call__(self, attr):
+
+               if self._pattern_split is None:
+                       return False
+
+               match = fnmatch.fnmatch
+               for x in self._pattern_split:
+                       if match(attr, x):
+                               return True
+
+               return False
+
 if hasattr(_os, "getxattr"):
        # Python >=3.3 and GNU/Linux
-       def _copyxattr(src, dest):
-               for attr in _os.listxattr(src):
+       def _copyxattr(src, dest, exclude=None):
+
+               attrs = _os.listxattr(src)
+               if attrs:
+                       if exclude is not None and isinstance(attrs[0], bytes):
+                               exclude = exclude.encode(_encodings['fs'])
+                       exclude = _get_xattr_excluder(exclude)
+
+               for attr in attrs:
+                       if exclude(attr):
+                               continue
                        try:
                                _os.setxattr(dest, attr, _os.getxattr(src, attr))
                                raise_exception = False
@@ -44,8 +94,17 @@ else:
        except ImportError:
                xattr = None
        if xattr is not None:
-               def _copyxattr(src, dest):
-                       for attr in xattr.list(src):
+               def _copyxattr(src, dest, exclude=None):
+
+                       attrs = xattr.list(src)
+                       if attrs:
+                               if exclude is not None and isinstance(attrs[0], bytes):
+                                       exclude = exclude.encode(_encodings['fs'])
+                               exclude = _get_xattr_excluder(exclude)
+
+                       for attr in attrs:
+                               if exclude(attr):
+                                       continue
                                try:
                                        xattr.set(dest, attr, xattr.get(src, attr))
                                        raise_exception = False
@@ -63,7 +122,8 @@ else:
                        _has_getfattr_and_setfattr = False
                _devnull.close()
                if _has_getfattr_and_setfattr:
-                       def _copyxattr(src, dest):
+                       def _copyxattr(src, dest, exclude=None):
+                               # TODO: implement exclude
                                getfattr_process = subprocess.Popen(["getfattr", "-d", "--absolute-names", src], stdout=subprocess.PIPE)
                                getfattr_process.wait()
                                extended_attributes = getfattr_process.stdout.readlines()
@@ -75,7 +135,7 @@ else:
                                        if setfattr_process.returncode != 0:
                                                raise OperationNotSupported("Filesystem containing file '%s' does not support extended attributes" % dest)
                else:
-                       def _copyxattr(src, dest):
+                       def _copyxattr(src, dest, exclude=None):
                                pass
 
 def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
@@ -246,7 +306,8 @@ def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
                                _copyfile(src_bytes, dest_tmp_bytes)
                                if xattr_enabled:
                                        try:
-                                               _copyxattr(src_bytes, dest_tmp_bytes)
+                                               _copyxattr(src_bytes, dest_tmp_bytes,
+                                                       exclude=mysettings.get("PORTAGE_XATTR_EXCLUDE", "security.*"))
                                        except SystemExit:
                                                raise
                                        except: