Merge remote-tracking branch 'jirutka/lxc-grsec'
[gentoo.git] / eclass / unpacker.eclass
1 # Copyright 1999-2014 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
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 "https://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