1 |
# Copyright 1999-2014 Gentoo Foundation |
2 |
# Distributed under the terms of the GNU General Public License v2 |
3 |
# $Header: /var/cvsroot/gentoo-x86/eclass/unpacker.eclass,v 1.17 2014/05/01 19:27:14 ottxor Exp $ |
4 |
|
5 |
# @ECLASS: unpacker.eclass |
6 |
# @MAINTAINER: |
7 |
# base-system@gentoo.org |
8 |
# @BLURB: helpers for extraneous file formats and consistent behavior across EAPIs |
9 |
# @DESCRIPTION: |
10 |
# Some extraneous file formats are not part of PMS, or are only in certain |
11 |
# EAPIs. Rather than worrying about that, support the crazy cruft here |
12 |
# and for all EAPI versions. |
13 |
|
14 |
# Possible todos: |
15 |
# - merge rpm unpacking |
16 |
# - support partial unpacks? |
17 |
|
18 |
if [[ -z ${_UNPACKER_ECLASS} ]]; then |
19 |
_UNPACKER_ECLASS=1 |
20 |
|
21 |
# @ECLASS-VARIABLE: UNPACKER_BZ2 |
22 |
# @DEFAULT_UNSET |
23 |
# @DESCRIPTION: |
24 |
# Utility to use to decompress bzip2 files. Will dynamically pick between |
25 |
# `pbzip2` and `bzip2`. Make sure your choice accepts the "-dc" options. |
26 |
# Note: this is meant for users to set, not ebuilds. |
27 |
|
28 |
# @ECLASS-VARIABLE: UNPACKER_LZIP |
29 |
# @DEFAULT_UNSET |
30 |
# @DESCRIPTION: |
31 |
# Utility to use to decompress lzip files. Will dynamically pick between |
32 |
# `plzip`, `pdlzip` and `lzip`. Make sure your choice accepts the "-dc" options. |
33 |
# Note: this is meant for users to set, not ebuilds. |
34 |
|
35 |
# for internal use only (unpack_pdv and unpack_makeself) |
36 |
find_unpackable_file() { |
37 |
local src=$1 |
38 |
if [[ -z ${src} ]] ; then |
39 |
src=${DISTDIR}/${A} |
40 |
else |
41 |
if [[ ${src} == ./* ]] ; then |
42 |
: # already what we want |
43 |
elif [[ -e ${DISTDIR}/${src} ]] ; then |
44 |
src=${DISTDIR}/${src} |
45 |
elif [[ -e ${PWD}/${src} ]] ; then |
46 |
src=${PWD}/${src} |
47 |
elif [[ -e ${src} ]] ; then |
48 |
src=${src} |
49 |
fi |
50 |
fi |
51 |
[[ ! -e ${src} ]] && return 1 |
52 |
echo "${src}" |
53 |
} |
54 |
|
55 |
unpack_banner() { |
56 |
echo ">>> Unpacking ${1##*/} to ${PWD}" |
57 |
} |
58 |
|
59 |
# @FUNCTION: unpack_pdv |
60 |
# @USAGE: <file to unpack> <size of off_t> |
61 |
# @DESCRIPTION: |
62 |
# Unpack those pesky pdv generated files ... |
63 |
# They're self-unpacking programs with the binary package stuffed in |
64 |
# the middle of the archive. Valve seems to use it a lot ... too bad |
65 |
# it seems to like to segfault a lot :(. So lets take it apart ourselves. |
66 |
# |
67 |
# You have to specify the off_t size ... I have no idea how to extract that |
68 |
# information out of the binary executable myself. Basically you pass in |
69 |
# the size of the off_t type (in bytes) on the machine that built the pdv |
70 |
# archive. |
71 |
# |
72 |
# One way to determine this is by running the following commands: |
73 |
# |
74 |
# @CODE |
75 |
# strings <pdv archive> | grep lseek |
76 |
# strace -elseek <pdv archive> |
77 |
# @CODE |
78 |
# |
79 |
# Basically look for the first lseek command (we do the strings/grep because |
80 |
# sometimes the function call is _llseek or something) and steal the 2nd |
81 |
# parameter. Here is an example: |
82 |
# |
83 |
# @CODE |
84 |
# $ strings hldsupdatetool.bin | grep lseek |
85 |
# lseek |
86 |
# $ strace -elseek ./hldsupdatetool.bin |
87 |
# lseek(3, -4, SEEK_END) = 2981250 |
88 |
# @CODE |
89 |
# |
90 |
# Thus we would pass in the value of '4' as the second parameter. |
91 |
unpack_pdv() { |
92 |
local src=$(find_unpackable_file "$1") |
93 |
local sizeoff_t=$2 |
94 |
|
95 |
[[ -z ${src} ]] && die "Could not locate source for '$1'" |
96 |
[[ -z ${sizeoff_t} ]] && die "No idea what off_t size was used for this pdv :(" |
97 |
|
98 |
unpack_banner "${src}" |
99 |
|
100 |
local metaskip=$(tail -c ${sizeoff_t} "${src}" | hexdump -e \"%i\") |
101 |
local tailskip=$(tail -c $((${sizeoff_t}*2)) "${src}" | head -c ${sizeoff_t} | hexdump -e \"%i\") |
102 |
|
103 |
# grab metadata for debug reasons |
104 |
local metafile="${T}/${FUNCNAME}.meta" |
105 |
tail -c +$((${metaskip}+1)) "${src}" > "${metafile}" |
106 |
|
107 |
# rip out the final file name from the metadata |
108 |
local datafile=$(tail -c +$((${metaskip}+1)) "${src}" | strings | head -n 1) |
109 |
datafile=$(basename "${datafile}") |
110 |
|
111 |
# now lets uncompress/untar the file if need be |
112 |
local tmpfile="${T}/${FUNCNAME}" |
113 |
tail -c +$((${tailskip}+1)) ${src} 2>/dev/null | head -c 512 > "${tmpfile}" |
114 |
|
115 |
local iscompressed=$(file -b "${tmpfile}") |
116 |
if [[ ${iscompressed:0:8} == "compress" ]] ; then |
117 |
iscompressed=1 |
118 |
mv "${tmpfile}"{,.Z} |
119 |
gunzip "${tmpfile}" |
120 |
else |
121 |
iscompressed=0 |
122 |
fi |
123 |
local istar=$(file -b "${tmpfile}") |
124 |
if [[ ${istar:0:9} == "POSIX tar" ]] ; then |
125 |
istar=1 |
126 |
else |
127 |
istar=0 |
128 |
fi |
129 |
|
130 |
#for some reason gzip dies with this ... dd cant provide buffer fast enough ? |
131 |
#dd if=${src} ibs=${metaskip} count=1 \ |
132 |
# | dd ibs=${tailskip} skip=1 \ |
133 |
# | gzip -dc \ |
134 |
# > ${datafile} |
135 |
if [ ${iscompressed} -eq 1 ] ; then |
136 |
if [ ${istar} -eq 1 ] ; then |
137 |
tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \ |
138 |
| head -c $((${metaskip}-${tailskip})) \ |
139 |
| tar -xzf - |
140 |
else |
141 |
tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \ |
142 |
| head -c $((${metaskip}-${tailskip})) \ |
143 |
| gzip -dc \ |
144 |
> ${datafile} |
145 |
fi |
146 |
else |
147 |
if [ ${istar} -eq 1 ] ; then |
148 |
tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \ |
149 |
| head -c $((${metaskip}-${tailskip})) \ |
150 |
| tar --no-same-owner -xf - |
151 |
else |
152 |
tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \ |
153 |
| head -c $((${metaskip}-${tailskip})) \ |
154 |
> ${datafile} |
155 |
fi |
156 |
fi |
157 |
true |
158 |
#[ -s "${datafile}" ] || die "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')" |
159 |
#assert "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')" |
160 |
} |
161 |
|
162 |
# @FUNCTION: unpack_makeself |
163 |
# @USAGE: [file to unpack] [offset] [tail|dd] |
164 |
# @DESCRIPTION: |
165 |
# Unpack those pesky makeself generated files ... |
166 |
# They're shell scripts with the binary package tagged onto |
167 |
# the end of the archive. Loki utilized the format as does |
168 |
# many other game companies. |
169 |
# |
170 |
# If the file is not specified, then ${A} is used. If the |
171 |
# offset is not specified then we will attempt to extract |
172 |
# the proper offset from the script itself. |
173 |
unpack_makeself() { |
174 |
local src_input=${1:-${A}} |
175 |
local src=$(find_unpackable_file "${src_input}") |
176 |
local skip=$2 |
177 |
local exe=$3 |
178 |
|
179 |
[[ -z ${src} ]] && die "Could not locate source for '${src_input}'" |
180 |
|
181 |
unpack_banner "${src}" |
182 |
|
183 |
if [[ -z ${skip} ]] ; then |
184 |
local ver=$(grep -m1 -a '#.*Makeself' "${src}" | awk '{print $NF}') |
185 |
local skip=0 |
186 |
exe=tail |
187 |
case ${ver} in |
188 |
1.5.*|1.6.0-nv*) # tested 1.5.{3,4,5} ... guessing 1.5.x series is same |
189 |
skip=$(grep -a ^skip= "${src}" | cut -d= -f2) |
190 |
;; |
191 |
2.0|2.0.1) |
192 |
skip=$(grep -a ^$'\t'tail "${src}" | awk '{print $2}' | cut -b2-) |
193 |
;; |
194 |
2.1.1) |
195 |
skip=$(grep -a ^offset= "${src}" | awk '{print $2}' | cut -b2-) |
196 |
(( skip++ )) |
197 |
;; |
198 |
2.1.2) |
199 |
skip=$(grep -a ^offset= "${src}" | awk '{print $3}' | head -n 1) |
200 |
(( skip++ )) |
201 |
;; |
202 |
2.1.3) |
203 |
skip=`grep -a ^offset= "${src}" | awk '{print $3}'` |
204 |
(( skip++ )) |
205 |
;; |
206 |
2.1.4|2.1.5|2.1.6|2.2.0) |
207 |
skip=$(grep -a offset=.*head.*wc "${src}" | awk '{print $3}' | head -n 1) |
208 |
skip=$(head -n ${skip} "${src}" | wc -c) |
209 |
exe="dd" |
210 |
;; |
211 |
*) |
212 |
eerror "I'm sorry, but I was unable to support the Makeself file." |
213 |
eerror "The version I detected was '${ver}'." |
214 |
eerror "Please file a bug about the file ${src##*/} at" |
215 |
eerror "http://bugs.gentoo.org/ so that support can be added." |
216 |
die "makeself version '${ver}' not supported" |
217 |
;; |
218 |
esac |
219 |
debug-print "Detected Makeself version ${ver} ... using ${skip} as offset" |
220 |
fi |
221 |
case ${exe} in |
222 |
tail) exe="tail -n +${skip} '${src}'";; |
223 |
dd) exe="dd ibs=${skip} skip=1 if='${src}'";; |
224 |
*) die "makeself cant handle exe '${exe}'" |
225 |
esac |
226 |
|
227 |
# lets grab the first few bytes of the file to figure out what kind of archive it is |
228 |
local filetype tmpfile="${T}/${FUNCNAME}" |
229 |
eval ${exe} 2>/dev/null | head -c 512 > "${tmpfile}" |
230 |
filetype=$(file -b "${tmpfile}") || die |
231 |
case ${filetype} in |
232 |
*tar\ archive*) |
233 |
eval ${exe} | tar --no-same-owner -xf - |
234 |
;; |
235 |
bzip2*) |
236 |
eval ${exe} | bzip2 -dc | tar --no-same-owner -xf - |
237 |
;; |
238 |
gzip*) |
239 |
eval ${exe} | tar --no-same-owner -xzf - |
240 |
;; |
241 |
compress*) |
242 |
eval ${exe} | gunzip | tar --no-same-owner -xf - |
243 |
;; |
244 |
XZ*) |
245 |
eval ${exe} | unxz | tar --no-same-owner -xf - |
246 |
;; |
247 |
*) |
248 |
eerror "Unknown filetype \"${filetype}\" ?" |
249 |
false |
250 |
;; |
251 |
esac |
252 |
assert "failure unpacking (${filetype}) makeself ${src##*/} ('${ver}' +${skip})" |
253 |
} |
254 |
|
255 |
# @FUNCTION: unpack_deb |
256 |
# @USAGE: <one deb to unpack> |
257 |
# @DESCRIPTION: |
258 |
# Unpack a Debian .deb archive in style. |
259 |
unpack_deb() { |
260 |
[[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>" |
261 |
|
262 |
local deb=$(find_unpackable_file "$1") |
263 |
|
264 |
unpack_banner "${deb}" |
265 |
|
266 |
# on AIX ar doesn't work out as their ar used a different format |
267 |
# from what GNU ar (and thus what .deb files) produce |
268 |
if [[ -n ${EPREFIX} ]] ; then |
269 |
{ |
270 |
read # global header |
271 |
[[ ${REPLY} = "!<arch>" ]] || die "${deb} does not seem to be a deb archive" |
272 |
local f timestamp uid gid mode size magic |
273 |
while read f timestamp uid gid mode size magic ; do |
274 |
[[ -n ${f} && -n ${size} ]] || continue # ignore empty lines |
275 |
if [[ ${f} = "data.tar"* ]] ; then |
276 |
head -c "${size}" > "${f}" |
277 |
else |
278 |
head -c "${size}" > /dev/null # trash it |
279 |
fi |
280 |
done |
281 |
} < "${deb}" |
282 |
else |
283 |
ar x "${deb}" |
284 |
fi |
285 |
|
286 |
unpacker ./data.tar* |
287 |
|
288 |
# Clean things up #458658. No one seems to actually care about |
289 |
# these, so wait until someone requests to do something else ... |
290 |
rm -f debian-binary {control,data}.tar* |
291 |
} |
292 |
|
293 |
# @FUNCTION: unpack_cpio |
294 |
# @USAGE: <one cpio to unpack> |
295 |
# @DESCRIPTION: |
296 |
# Unpack a cpio archive, file "-" means stdin. |
297 |
unpack_cpio() { |
298 |
[[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>" |
299 |
|
300 |
# needed as cpio always reads from stdin |
301 |
local cpio_cmd=( cpio --make-directories --extract --preserve-modification-time ) |
302 |
if [[ $1 == "-" ]] ; then |
303 |
unpack_banner "stdin" |
304 |
"${cpio_cmd[@]}" |
305 |
else |
306 |
local cpio=$(find_unpackable_file "$1") |
307 |
unpack_banner "${cpio}" |
308 |
"${cpio_cmd[@]}" <"${cpio}" |
309 |
fi |
310 |
} |
311 |
|
312 |
# @FUNCTION: unpack_zip |
313 |
# @USAGE: <zip file> |
314 |
# @DESCRIPTION: |
315 |
# Unpack zip archives. |
316 |
# This function ignores all non-fatal errors (i.e. warnings). |
317 |
# That is useful for zip archives with extra crap attached |
318 |
# (e.g. self-extracting archives). |
319 |
unpack_zip() { |
320 |
[[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>" |
321 |
|
322 |
local zip=$(find_unpackable_file "$1") |
323 |
unpack_banner "${zip}" |
324 |
unzip -qo "${zip}" |
325 |
|
326 |
[[ $? -le 1 ]] || die "unpacking ${zip} failed (arch=unpack_zip)" |
327 |
} |
328 |
|
329 |
# @FUNCTION: _unpacker |
330 |
# @USAGE: <one archive to unpack> |
331 |
# @INTERNAL |
332 |
# @DESCRIPTION: |
333 |
# Unpack the specified archive. We only operate on one archive here |
334 |
# to keep down on the looping logic (that is handled by `unpacker`). |
335 |
_unpacker() { |
336 |
[[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>" |
337 |
|
338 |
local a=$1 |
339 |
local m=$(echo "${a}" | tr '[:upper:]' '[:lower:]') |
340 |
a=$(find_unpackable_file "${a}") |
341 |
|
342 |
# first figure out the decompression method |
343 |
case ${m} in |
344 |
*.bz2|*.tbz|*.tbz2) |
345 |
local bzcmd=${PORTAGE_BZIP2_COMMAND:-$(type -P pbzip2 || type -P bzip2)} |
346 |
local bzuncmd=${PORTAGE_BUNZIP2_COMMAND:-${bzcmd} -d} |
347 |
: ${UNPACKER_BZ2:=${bzuncmd}} |
348 |
comp="${UNPACKER_BZ2} -c" |
349 |
;; |
350 |
*.z|*.gz|*.tgz) |
351 |
comp="gzip -dc" ;; |
352 |
*.lzma|*.xz|*.txz) |
353 |
comp="xz -dc" ;; |
354 |
*.lz) |
355 |
: ${UNPACKER_LZIP:=$(type -P plzip || type -P pdlzip || type -P lzip)} |
356 |
comp="${UNPACKER_LZIP} -dc" ;; |
357 |
*) comp="" ;; |
358 |
esac |
359 |
|
360 |
# then figure out if there are any archiving aspects |
361 |
arch="" |
362 |
case ${m} in |
363 |
*.tgz|*.tbz|*.tbz2|*.txz|*.tar.*|*.tar) |
364 |
arch="tar --no-same-owner -xof" ;; |
365 |
*.cpio.*|*.cpio) |
366 |
arch="unpack_cpio" ;; |
367 |
*.deb) |
368 |
arch="unpack_deb" ;; |
369 |
*.run) |
370 |
arch="unpack_makeself" ;; |
371 |
*.sh) |
372 |
# Not all shell scripts are makeself |
373 |
if head -n 30 "${a}" | grep -qs '#.*Makeself' ; then |
374 |
arch="unpack_makeself" |
375 |
fi |
376 |
;; |
377 |
*.bin) |
378 |
# Makeself archives can be annoyingly named |
379 |
if head -c 100 "${a}" | grep -qs '#.*Makeself' ; then |
380 |
arch="unpack_makeself" |
381 |
fi |
382 |
;; |
383 |
*.zip) |
384 |
arch="unpack_zip" ;; |
385 |
esac |
386 |
|
387 |
# finally do the unpack |
388 |
if [[ -z ${arch}${comp} ]] ; then |
389 |
unpack "$1" |
390 |
return $? |
391 |
fi |
392 |
|
393 |
[[ ${arch} != unpack_* ]] && unpack_banner "${a}" |
394 |
|
395 |
if [[ -z ${arch} ]] ; then |
396 |
# Need to decompress the file into $PWD #408801 |
397 |
local _a=${a%.*} |
398 |
${comp} "${a}" > "${_a##*/}" |
399 |
elif [[ -z ${comp} ]] ; then |
400 |
${arch} "${a}" |
401 |
else |
402 |
${comp} "${a}" | ${arch} - |
403 |
fi |
404 |
|
405 |
assert "unpacking ${a} failed (comp=${comp} arch=${arch})" |
406 |
} |
407 |
|
408 |
# @FUNCTION: unpacker |
409 |
# @USAGE: [archives to unpack] |
410 |
# @DESCRIPTION: |
411 |
# This works in the same way that `unpack` does. If you don't specify |
412 |
# any files, it will default to ${A}. |
413 |
unpacker() { |
414 |
local a |
415 |
[[ $# -eq 0 ]] && set -- ${A} |
416 |
for a ; do _unpacker "${a}" ; done |
417 |
} |
418 |
|
419 |
# @FUNCTION: unpacker_src_unpack |
420 |
# @DESCRIPTION: |
421 |
# Run `unpacker` to unpack all our stuff. |
422 |
unpacker_src_unpack() { |
423 |
unpacker |
424 |
} |
425 |
|
426 |
# @FUNCTION: unpacker_src_uri_depends |
427 |
# @USAGE: [archives that we will unpack] |
428 |
# @RETURN: Dependencies needed to unpack all the archives |
429 |
# @DESCRIPTION: |
430 |
# Walk all the specified files (defaults to $SRC_URI) and figure out the |
431 |
# dependencies that are needed to unpack things. |
432 |
# |
433 |
# Note: USE flags are not yet handled. |
434 |
unpacker_src_uri_depends() { |
435 |
local uri deps d |
436 |
|
437 |
[[ $# -eq 0 ]] && set -- ${SRC_URI} |
438 |
|
439 |
for uri in "$@" ; do |
440 |
case ${uri} in |
441 |
*.cpio.*|*.cpio) |
442 |
d="app-arch/cpio" ;; |
443 |
*.rar|*.RAR) |
444 |
d="app-arch/unrar" ;; |
445 |
*.7z) |
446 |
d="app-arch/p7zip" ;; |
447 |
*.xz) |
448 |
d="app-arch/xz-utils" ;; |
449 |
*.zip) |
450 |
d="app-arch/unzip" ;; |
451 |
*.lz) |
452 |
d="|| ( app-arch/plzip app-arch/pdlzip app-arch/lzip )" ;; |
453 |
esac |
454 |
deps+=" ${d}" |
455 |
done |
456 |
|
457 |
echo "${deps}" |
458 |
} |
459 |
|
460 |
EXPORT_FUNCTIONS src_unpack |
461 |
|
462 |
fi |