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 |