From 3464e5ace3ef520344a330601e0aac69bcdef222 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 11 Dec 2012 01:00:25 -0800 Subject: prepstrip: preserve xattrs, bug #446420 --- bin/ebuild-helpers/prepstrip | 36 ++++++++- bin/xattr-helper.py | 173 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 3 deletions(-) create mode 100755 bin/xattr-helper.py (limited to 'bin') diff --git a/bin/ebuild-helpers/prepstrip b/bin/ebuild-helpers/prepstrip index 6a09ff457..fb20777e8 100755 --- a/bin/ebuild-helpers/prepstrip +++ b/bin/ebuild-helpers/prepstrip @@ -15,7 +15,7 @@ exp_tf() { eval ${var}_${flag}=$(tf has ${flag} ${!var}) done } -exp_tf FEATURES compressdebug installsources nostrip splitdebug +exp_tf FEATURES compressdebug installsources nostrip splitdebug xattr exp_tf RESTRICT binchecks installsources strip if ! ___eapi_has_prefix_variables; then @@ -30,6 +30,28 @@ if ${RESTRICT_strip} || ${FEATURES_nostrip} ; then ${FEATURES_installsources} || exit 0 fi +PRESERVE_XATTR=false +if [[ ${KERNEL} == linux ]] && ${FEATURES_xattr} ; then + PRESERVE_XATTR=true + if type -P getfattr >/dev/null && type -P setfattr >/dev/null ; then + dump_xattrs() { + getfattr -d --absolute-names "$1" + } + restore_xattrs() { + setfattr --restore=- + } + else + dump_xattrs() { + "${PORTAGE_PYTHON:-/usr/bin/python}" \ + "${PORTAGE_BIN_PATH}/xattr-helper.py" --dump < <(echo -n "$1") + } + restore_xattrs() { + "${PORTAGE_PYTHON:-/usr/bin/python}" \ + "${PORTAGE_BIN_PATH}/xattr-helper.py" --restore + } + fi +fi + # look up the tools we might be using for t in STRIP:strip OBJCOPY:objcopy READELF:readelf ; do v=${t%:*} # STRIP @@ -152,7 +174,7 @@ save_elf_debug() { # Usage: process_elf process_elf() { local x=$1 inode_link=$2 strip_flags=${*:3} - local already_stripped lockfile + local already_stripped lockfile xt_data __vecho " ${x:${#ED}}" @@ -171,7 +193,12 @@ process_elf() { [ -f "${inode_link}_stripped" ] && already_stripped=true || already_stripped=false - ${already_stripped} || save_elf_sources "${x}" + if ! ${already_stripped} ; then + if ${PRESERVE_XATTR} ; then + xt_data=$(dump_xattrs "${x}") + fi + save_elf_sources "${x}" + fi if ${strip_this} ; then @@ -197,6 +224,9 @@ process_elf() { ln "${inode_link}_stripped" "${x}" || die "ln failed unexpectedly" else ln "${x}" "${inode_link}_stripped" || die "ln failed unexpectedly" + if [[ ${xt_data} ]] ; then + restore_xattrs <<< "${xt_data}" + fi fi [[ -n ${lockfile} ]] && rm -f "${lockfile}" diff --git a/bin/xattr-helper.py b/bin/xattr-helper.py new file mode 100755 index 000000000..d40217c9a --- /dev/null +++ b/bin/xattr-helper.py @@ -0,0 +1,173 @@ +#!/usr/bin/python +# Copyright 2012 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import array +import optparse +import os +import re +import sys + +if hasattr(os, "getxattr"): + + class xattr(object): + get = os.getxattr + set = os.setxattr + list = os.listxattr + +else: + import xattr + +_unquote_re = re.compile(br'\\[0-7]{3}') +_fs_encoding = sys.getfilesystemencoding() + +if sys.hexversion < 0x3000000: + + def octal_quote_byte(b): + return b'\\%03o' % ord(b) + + def unicode_encode(s): + if isinstance(s, unicode): + s = s.encode(_fs_encoding) + return s +else: + + def octal_quote_byte(b): + return ('\\%03o' % ord(b)).encode('ascii') + + def unicode_encode(s): + if isinstance(s, str): + s = s.encode(_fs_encoding) + return s + +def quote(s, quote_chars): + + quote_re = re.compile(b'[' + quote_chars + b']') + result = [] + pos = 0 + s_len = len(s) + + while pos < s_len: + m = quote_re.search(s, pos=pos) + if m is None: + result.append(s[pos:]) + pos = s_len + else: + start = m.start() + result.append(s[pos:start]) + result.append(octal_quote_byte(s[start:start+1])) + pos = start + 1 + + return b"".join(result) + +def unquote(s): + + result = [] + pos = 0 + s_len = len(s) + + while pos < s_len: + m = _unquote_re.search(s, pos=pos) + if m is None: + result.append(s[pos:]) + pos = s_len + else: + start = m.start() + result.append(s[pos:start]) + pos = start + 4 + a = array.array('B') + a.append(int(s[start + 1:pos], 8)) + try: + # Python >= 3.2 + result.append(a.tobytes()) + except AttributeError: + result.append(a.tostring()) + + return b"".join(result) + +def dump_xattrs(file_in, file_out): + + for pathname in file_in.read().split(b'\0'): + if not pathname: + continue + + attrs = xattr.list(pathname) + if not attrs: + continue + file_out.write(b'# file: ' + quote(pathname, b'\n\r') + b'\n') + for attr in attrs: + attr = unicode_encode(attr) + file_out.write(quote(attr, b'=\n\r') + b'="' + + quote(xattr.get(pathname, attr), b'\\\0\n\r"') + b'"\n') + +def restore_xattrs(file_in): + + pathname = None + for i, line in enumerate(file_in): + if line.startswith(b'# file: '): + pathname = unquote(line.rstrip(b'\n')[8:]) + else: + parts = line.split(b'=', 1) + if len(parts) == 2: + if pathname is None: + raise AssertionError('line %d: missing pathname' % i + 1) + attr = unquote(parts[0]) + # strip trailing newline and quotes + value = unquote(parts[1].rstrip(b'\n')[1:-1]) + xattr.set(pathname, attr, value) + elif line.strip(): + raise AssertionError("line %d: malformed entry" % i + 1) + +def main(argv): + + description = "Dump and restore extended attributes," \ + " using format like that used by getfattr --dump." + usage = "usage: %s [--dump | --restore]\n" % \ + os.path.basename(argv[0]) + + parser = optparse.OptionParser(description=description, usage=usage) + + actions = optparse.OptionGroup(parser, 'Actions') + actions.add_option("--dump", + action="store_true", + help="Dump the values of all extended " + "attributes associated with null-separated" + " paths read from stdin.") + actions.add_option("--restore", + action="store_true", + help="Restore extended attributes using" + " a dump read from stdin.") + parser.add_option_group(actions) + + options, args = parser.parse_args(argv[1:]) + + if len(args) != 0: + parser.error("expected zero arguments, " + "got %s" % len(args)) + + if sys.hexversion >= 0x3000000: + file_in = sys.stdin.buffer.raw + else: + file_in = sys.stdin + + if options.dump: + + if sys.hexversion >= 0x3000000: + file_out = sys.stdout.buffer + else: + file_out = sys.stdout + + dump_xattrs(file_in, file_out) + + elif options.restore: + + restore_xattrs(file_in) + + else: + parser.error("available actions: --dump, --restore") + + return os.EX_OK + +if __name__ == "__main__": + rval = main(sys.argv[:]) + sys.exit(rval) -- cgit v1.2.3-1-g7c22