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/xattr-helper.py | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 bin/xattr-helper.py (limited to 'bin/xattr-helper.py') 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