Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] BinpkgExtractorAsync: xz and gzip decompression (142579)
Date: Fri, 16 Jan 2015 01:27:59
Message-Id: 1421371643-28495-1-git-send-email-zmedico@gentoo.org
1 This adds support for using a binary package's compression header to
2 determine the compression type, providing forward-compatibility for
3 xz and gzip decompression. The file name extension is disregared, so
4 that it will be possible use a compression-independent file naming
5 scheme in the future (see bug #150031 for discussion about proposed
6 file naming schemes).
7
8 Currently, only decompression is supported. It's useful to provide
9 forward-compatibility now, so that binhost clients will be prepared
10 to handle future binhost servers that use xz or gzip compression.
11
12 X-Gentoo-Bug: 142579
13 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=142579
14 ---
15 pym/_emerge/BinpkgExtractorAsync.py | 28 ++++++++++++++--
16 pym/portage/util/compression_probe.py | 62 +++++++++++++++++++++++++++++++++++
17 2 files changed, 88 insertions(+), 2 deletions(-)
18 create mode 100644 pym/portage/util/compression_probe.py
19
20 diff --git a/pym/_emerge/BinpkgExtractorAsync.py b/pym/_emerge/BinpkgExtractorAsync.py
21 index be74c2f..8d446f9 100644
22 --- a/pym/_emerge/BinpkgExtractorAsync.py
23 +++ b/pym/_emerge/BinpkgExtractorAsync.py
24 @@ -1,8 +1,12 @@
25 # Copyright 1999-2013 Gentoo Foundation
26 # Distributed under the terms of the GNU General Public License v2
27
28 +import logging
29 +
30 from _emerge.SpawnProcess import SpawnProcess
31 import portage
32 +from portage.localization import _
33 +from portage.util.compression_probe import compression_probe
34 import signal
35 import subprocess
36
37 @@ -20,19 +24,39 @@ class BinpkgExtractorAsync(SpawnProcess):
38 if b"--xattrs" in output:
39 tar_options = "--xattrs"
40
41 + comp = compression_probe(self.pkg_path)
42 + if comp is None:
43 + self.scheduler.output("!!! %s\n" %
44 + _("File compression header unrecognized: %s") %
45 + self.pkg_path, log_path=self.logfile,
46 + background=self.background, level=logging.ERROR)
47 + self.returncode = 1
48 + self._async_wait()
49 + return
50 +
51 # Add -q to bzip2 opts, in order to avoid "trailing garbage after
52 # EOF ignored" warning messages due to xpak trailer.
53 + if comp == "bzip2":
54 + decomp_cmd = "${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d}"
55 + elif comp == "xz":
56 + decomp_cmd = "xz -d"
57 + elif comp == "gzip":
58 + decomp_cmd = "gzip -d"
59 + else:
60 + raise AssertionError("Unexpected compression: %s" % comp)
61 +
62 # SIGPIPE handling (128 + SIGPIPE) should be compatible with
63 # assert_sigpipe_ok() that's used by the ebuild unpack() helper.
64 self.args = [self._shell_binary, "-c",
65 - ("${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d} -cq -- %s | tar -xp %s -C %s -f - ; " + \
66 + ("%s -cq -- %s | tar -xp %s -C %s -f - ; " + \
67 "p=(${PIPESTATUS[@]}) ; " + \
68 "if [[ ${p[0]} != 0 && ${p[0]} != %d ]] ; then " % (128 + signal.SIGPIPE) + \
69 "echo bzip2 failed with status ${p[0]} ; exit ${p[0]} ; fi ; " + \
70 "if [ ${p[1]} != 0 ] ; then " + \
71 "echo tar failed with status ${p[1]} ; exit ${p[1]} ; fi ; " + \
72 "exit 0 ;") % \
73 - (portage._shell_quote(self.pkg_path),
74 + (decomp_cmd,
75 + portage._shell_quote(self.pkg_path),
76 tar_options,
77 portage._shell_quote(self.image_dir))]
78
79 diff --git a/pym/portage/util/compression_probe.py b/pym/portage/util/compression_probe.py
80 new file mode 100644
81 index 0000000..7bdd28f
82 --- /dev/null
83 +++ b/pym/portage/util/compression_probe.py
84 @@ -0,0 +1,62 @@
85 +# Copyright 2015 Gentoo Foundation
86 +# Distributed under the terms of the GNU General Public License v2
87 +
88 +import errno
89 +import re
90 +import sys
91 +
92 +if sys.hexversion >= 0x3000000:
93 + basestring = str
94 +
95 +from portage import _encodings, _unicode_encode
96 +from portage.exception import FileNotFound, PermissionDenied
97 +
98 +_compression_re = re.compile(b'^(' +
99 + b'(?P<gzip>\x1f\x8b)|' +
100 + b'(?P<bzip2>\x42\x5a\x68\x39)|' +
101 + b'(?P<xz>\xfd\x37\x7a\x58\x5a\x00))')
102 +
103 +def compression_probe(f):
104 + """
105 + Identify the compression type of a file. Returns one of the
106 + following identifier strings:
107 +
108 + bzip2
109 + gzip
110 + xz
111 +
112 + @param f: a file path, or file-like object
113 + @type f: file-like object
114 + @return: a string identifying the compression type, or None if the
115 + compression type is unrecognized
116 + @rtype str or None
117 + """
118 +
119 + open_file = isinstance(f, basestring)
120 + if open_file:
121 + try:
122 + f = open(_unicode_encode(f,
123 + encoding=_encodings['fs'], errors='strict'), mode='rb')
124 + except IOError as e:
125 + if e.errno == PermissionDenied.errno:
126 + raise PermissionDenied(f)
127 + elif e.errno in (errno.ENOENT, errno.ESTALE):
128 + raise FileNotFound(f)
129 + else:
130 + raise
131 +
132 + try:
133 + return _compression_probe_file(f)
134 + finally:
135 + if open_file:
136 + f.close()
137 +
138 +def _compression_probe_file(f):
139 +
140 + m = _compression_re.match(f.read(6))
141 + if m is not None:
142 + for k, v in m.groupdict().items():
143 + if v is not None:
144 + return k
145 +
146 + return None
147 --
148 2.0.5

Replies