Gentoo Archives: gentoo-dev

From: Mike Frysinger <vapier@g.o>
To: gentoo-dev@l.g.o
Subject: [gentoo-dev] unpacker.eclass
Date: Wed, 01 Feb 2012 20:06:09
Message-Id: 201202011505.41142.vapier@gentoo.org
1 any feedback before merging this initial version ?
2 https://bugs.gentoo.org/399019
3 -mike
4
5 # Copyright 1999-2012 Gentoo Foundation
6 # Distributed under the terms of the GNU General Public License v2
7 # $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.377 2012/01/03 08:45:36 jlec Exp $
8
9 # @ECLASS: unpacker.eclass
10 # @MAINTAINER:
11 # base-system@g.o
12 # @BLURB: helpers for extraneous file formats and consistent behavior across EAPI's
13 # @DESCRIPTION:
14 # Some extraneous file formats are not part of PMS, or are only in certain
15 # EAPI's. Rather than worrying about that, support the crazy cruft here
16 # and for all EAPI versions.
17
18 # Possible todos:
19 # - merge rpm unpacking
20 # - support partial unpacks?
21
22 if [[ ${___ECLASS_ONCE_UNPACKER} != "recur -_+^+_- spank" ]] ; then
23 ___ECLASS_ONCE_UNPACKER="recur -_+^+_- spank"
24
25 # @ECLASS-VARIABLE: UNPACKER_BZ2
26 # @DEFAULT_UNSET
27 # @DESCRIPTION:
28 # Utility to use to decompress bzip2 files. Will dynamically pick between
29 # `pbzip2` and `bzip2`. Make sure your choice accepts the "-c" option.
30 # Note: this is meant for users to set, not ebuilds.
31
32 # for internal use only (unpack_pdv and unpack_makeself)
33 find_unpackable_file() {
34 local src=$1
35 if [[ -z ${src} ]] ; then
36 src=${DISTDIR}/${A}
37 else
38 if [[ ${src} == ./* ]] ; then
39 : # already what we want
40 elif [[ -e ${DISTDIR}/${src} ]] ; then
41 src=${DISTDIR}/${src}
42 elif [[ -e ${PWD}/${src} ]] ; then
43 src=${PWD}/${src}
44 elif [[ -e ${src} ]] ; then
45 src=${src}
46 fi
47 fi
48 [[ ! -e ${src} ]] && return 1
49 echo "${src}"
50 }
51
52 unpack_banner() {
53 echo ">>> Unpacking ${1##*/} to ${PWD}"
54 }
55
56 # @FUNCTION: unpack_pdv
57 # @USAGE: <file to unpack> <size of off_t>
58 # @DESCRIPTION:
59 # Unpack those pesky pdv generated files ...
60 # They're self-unpacking programs with the binary package stuffed in
61 # the middle of the archive. Valve seems to use it a lot ... too bad
62 # it seems to like to segfault a lot :(. So lets take it apart ourselves.
63 #
64 # You have to specify the off_t size ... I have no idea how to extract that
65 # information out of the binary executable myself. Basically you pass in
66 # the size of the off_t type (in bytes) on the machine that built the pdv
67 # archive.
68 #
69 # One way to determine this is by running the following commands:
70 #
71 # @CODE
72 # strings <pdv archive> | grep lseek
73 # strace -elseek <pdv archive>
74 # @CODE
75 #
76 # Basically look for the first lseek command (we do the strings/grep because
77 # sometimes the function call is _llseek or something) and steal the 2nd
78 # parameter. Here is an example:
79 #
80 # @CODE
81 # vapier@vapier 0 pdv_unpack # strings hldsupdatetool.bin | grep lseek
82 # lseek
83 # vapier@vapier 0 pdv_unpack # strace -elseek ./hldsupdatetool.bin
84 # lseek(3, -4, SEEK_END) = 2981250
85 # @CODE
86 #
87 # Thus we would pass in the value of '4' as the second parameter.
88 unpack_pdv() {
89 local src=$(find_unpackable_file "$1")
90 local sizeoff_t=$2
91
92 [[ -z ${src} ]] && die "Could not locate source for '$1'"
93 [[ -z ${sizeoff_t} ]] && die "No idea what off_t size was used for this pdv :("
94
95 unpack_banner "${src}"
96
97 local metaskip=$(tail -c ${sizeoff_t} "${src}" | hexdump -e \"%i\")
98 local tailskip=$(tail -c $((${sizeoff_t}*2)) "${src}" | head -c ${sizeoff_t} | hexdump -e \"%i\")
99
100 # grab metadata for debug reasons
101 local metafile=$(emktemp)
102 tail -c +$((${metaskip}+1)) "${src}" > "${metafile}"
103
104 # rip out the final file name from the metadata
105 local datafile=$(tail -c +$((${metaskip}+1)) "${src}" | strings | head -n 1)
106 datafile=$(basename "${datafile}")
107
108 # now lets uncompress/untar the file if need be
109 local tmpfile=$(emktemp)
110 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null | head -c 512 > ${tmpfile}
111
112 local iscompressed=$(file -b "${tmpfile}")
113 if [[ ${iscompressed:0:8} == "compress" ]] ; then
114 iscompressed=1
115 mv ${tmpfile}{,.Z}
116 gunzip ${tmpfile}
117 else
118 iscompressed=0
119 fi
120 local istar=$(file -b "${tmpfile}")
121 if [[ ${istar:0:9} == "POSIX tar" ]] ; then
122 istar=1
123 else
124 istar=0
125 fi
126
127 #for some reason gzip dies with this ... dd cant provide buffer fast enough ?
128 #dd if=${src} ibs=${metaskip} count=1 \
129 # | dd ibs=${tailskip} skip=1 \
130 # | gzip -dc \
131 # > ${datafile}
132 if [ ${iscompressed} -eq 1 ] ; then
133 if [ ${istar} -eq 1 ] ; then
134 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
135 | head -c $((${metaskip}-${tailskip})) \
136 | tar -xzf -
137 else
138 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
139 | head -c $((${metaskip}-${tailskip})) \
140 | gzip -dc \
141 > ${datafile}
142 fi
143 else
144 if [ ${istar} -eq 1 ] ; then
145 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
146 | head -c $((${metaskip}-${tailskip})) \
147 | tar --no-same-owner -xf -
148 else
149 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
150 | head -c $((${metaskip}-${tailskip})) \
151 > ${datafile}
152 fi
153 fi
154 true
155 #[ -s "${datafile}" ] || die "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
156 #assert "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
157 }
158
159 # @FUNCTION: unpack_makeself
160 # @USAGE: [file to unpack] [offset] [tail|dd]
161 # @DESCRIPTION:
162 # Unpack those pesky makeself generated files ...
163 # They're shell scripts with the binary package tagged onto
164 # the end of the archive. Loki utilized the format as does
165 # many other game companies.
166 #
167 # If the file is not specified, then ${A} is used. If the
168 # offset is not specified then we will attempt to extract
169 # the proper offset from the script itself.
170 unpack_makeself() {
171 local src_input=${1:-${A}}
172 local src=$(find_unpackable_file "${src_input}")
173 local skip=$2
174 local exe=$3
175
176 [[ -z ${src} ]] && die "Could not locate source for '${src_input}'"
177
178 unpack_banner "${src}"
179
180 if [[ -z ${skip} ]] ; then
181 local ver=$(grep -m1 -a '#.*Makeself' "${src}" | awk '{print $NF}')
182 local skip=0
183 exe=tail
184 case ${ver} in
185 1.5.*|1.6.0-nv) # tested 1.5.{3,4,5} ... guessing 1.5.x series is same
186 skip=$(grep -a ^skip= "${src}" | cut -d= -f2)
187 ;;
188 2.0|2.0.1)
189 skip=$(grep -a ^$'\t'tail "${src}" | awk '{print $2}' | cut -b2-)
190 ;;
191 2.1.1)
192 skip=$(grep -a ^offset= "${src}" | awk '{print $2}' | cut -b2-)
193 (( skip++ ))
194 ;;
195 2.1.2)
196 skip=$(grep -a ^offset= "${src}" | awk '{print $3}' | head -n 1)
197 (( skip++ ))
198 ;;
199 2.1.3)
200 skip=`grep -a ^offset= "${src}" | awk '{print $3}'`
201 (( skip++ ))
202 ;;
203 2.1.4|2.1.5)
204 skip=$(grep -a offset=.*head.*wc "${src}" | awk '{print $3}' | head -n 1)
205 skip=$(head -n ${skip} "${src}" | wc -c)
206 exe="dd"
207 ;;
208 *)
209 eerror "I'm sorry, but I was unable to support the Makeself file."
210 eerror "The version I detected was '${ver}'."
211 eerror "Please file a bug about the file ${src##*/} at"
212 eerror "http://bugs.gentoo.org/ so that support can be added."
213 die "makeself version '${ver}' not supported"
214 ;;
215 esac
216 debug-print "Detected Makeself version ${ver} ... using ${skip} as offset"
217 fi
218 case ${exe} in
219 tail) exe="tail -n +${skip} '${src}'";;
220 dd) exe="dd ibs=${skip} skip=1 if='${src}'";;
221 *) die "makeself cant handle exe '${exe}'"
222 esac
223
224 # lets grab the first few bytes of the file to figure out what kind of archive it is
225 local filetype tmpfile=$(emktemp)
226 eval ${exe} 2>/dev/null | head -c 512 > "${tmpfile}"
227 filetype=$(file -b "${tmpfile}") || die
228 case ${filetype} in
229 *tar\ archive*)
230 eval ${exe} | tar --no-same-owner -xf -
231 ;;
232 bzip2*)
233 eval ${exe} | bzip2 -dc | tar --no-same-owner -xf -
234 ;;
235 gzip*)
236 eval ${exe} | tar --no-same-owner -xzf -
237 ;;
238 compress*)
239 eval ${exe} | gunzip | tar --no-same-owner -xf -
240 ;;
241 *)
242 eerror "Unknown filetype \"${filetype}\" ?"
243 false
244 ;;
245 esac
246 assert "failure unpacking (${filetype}) makeself ${src##*/} ('${ver}' +${skip})"
247 }
248
249 # @FUNCTION: unpack_deb
250 # @USAGE: <one deb to unpack>
251 # @DESCRIPTION:
252 # Unpack a Debian .deb archive in style.
253 unpack_deb() {
254 [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
255
256 local deb=$(find_unpackable_file "$1")
257
258 unpack_banner "${deb}"
259
260 ar x "${deb}"
261 unpack ./data.tar*
262 }
263
264 # @FUNCTION: _unpacker
265 # @USAGE: <one archive to unpack>
266 # @INTERNAL
267 # @DESCRIPTION:
268 # Unpack the specified archive. We only operate on one archive here
269 # to keep down on the looping logic (that is handled by `unpacker`).
270 _unpacker() {
271 [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
272
273 local a=$1
274 local m=$(echo "${a}" | tr '[:upper:]' '[:lower:]')
275 a=$(find_unpackable_file "${a}")
276
277 # first figure out the decompression method
278 case ${m} in
279 *.bz2|*.tbz|*.tbz2)
280 : ${PORTAGE_BZIP2_COMMAND:=$(type -P pbzip2 || bzip2)}
281 : ${PORTAGE_BUNZIP2_COMMAND:=${PORTAGE_BZIP2_COMMAND} -d}
282 : ${UNPACKER_BZ2:=${PORTAGE_BUNZIP2_COMMAND}}
283 comp="${PORTAGE_BUNZIP2_COMMAND} -c"
284 ;;
285 *.z|*.gz|*.tgz)
286 comp="gzip -dc" ;;
287 *.lzma|*.xz|*.txz)
288 comp="xz -dc" ;;
289 *) comp="" ;;
290 esac
291
292 # then figure out if there are any archiving aspects
293 case ${m} in
294 *.tgz|*.tbz|*.tbz2|*.txz|*.tar.*|*.tar)
295 arch="tar --no-same-owner -xof" ;;
296 *.deb)
297 arch="unpack_deb" ;;
298 *.run)
299 arch="unpack_makeself" ;;
300 *) arch="" ;;
301 esac
302
303 # finally do the unpack
304 [[ -z ${arch}${comp} ]] && return 1
305
306 [[ ${arch} != unpack_* ]] && unpack_banner "${a}"
307
308 if [[ -z ${arch} ]] ; then
309 ${comp} "${a}" > "${a%.*}"
310 elif [[ -z ${comp} ]] ; then
311 ${arch} "${a}"
312 else
313 ${comp} "${a}" | ${arch} -
314 fi
315
316 assert "unpacking ${a} failed (comp=${comp} arch=${arch})"
317 }
318
319 # @FUNCTION: unpacker
320 # @USAGE: [archives to unpack]
321 # @DESCRIPTION:
322 # This works in the same way that `unpack` does. If you don't specify
323 # any files, it will default to ${A}.
324 unpacker() {
325 local a
326 [[ $# -eq 0 ]] && set -- ${A}
327 for a ; do _unpacker "${a}" ; done
328 }
329
330 # @FUNCTION: unpacker_src_unpack
331 # @DESCRIPTION:
332 # Run `unpacker` to unpack all our stuff.
333 unpacker_src_unpack() {
334 unpacker
335 }
336
337 # @FUNCTION: unpacker
338 # @USAGE: [archives that we will unpack]
339 # @RETURN: Dependencies needed to unpack all the archives
340 # @DESCRIPTION:
341 # Walk all the specified files (defaults to $SRC_URI) and figure out the
342 # dependencies that are needed to unpack things.
343 #
344 # Note: USE flags are not yet handled.
345 unpacker_src_uri_depends() {
346 local uri deps d
347
348 [[ $# -eq 0 ]] && set -- ${SRC_URI}
349
350 for uri in "$@" ; do
351 case ${uri} in
352 *.rar|*.RAR)
353 d="app-arch/unrar" ;;
354 *.7z)
355 d="app-arch/p7zip" ;;
356 *.xz)
357 d="app-arch/xz-utils" ;;
358 esac
359 deps+=" ${d}"
360 done
361
362 echo "${deps}"
363 }
364
365 EXPORT_FUNCTIONS src_unpack
366
367 fi

Attachments

File name MIME type
signature.asc application/pgp-signature

Replies

Subject Author
Re: [gentoo-dev] unpacker.eclass "Michał Górny" <mgorny@g.o>
Re: [gentoo-dev] unpacker.eclass Alexandre Rostovtsev <tetromino@g.o>
Re: [gentoo-dev] unpacker.eclass Mike Frysinger <vapier@g.o>
[gentoo-dev] unpacker.eclass and base.eclass integration Mike Frysinger <vapier@g.o>