1 # This is a copy of the new, unreleased, version in 0launch. Once the new
2 # version is out, we can drop this copy.
7 from tempfile
import mkdtemp
, mkstemp
10 from logging
import debug
, info
, warn
11 from zeroinstall
import SafeException
13 _recent_gnu_tar
= None
15 global _recent_gnu_tar
16 if _recent_gnu_tar
is None:
17 _recent_gnu_tar
= False
18 version
= os
.popen('tar --version 2>&1').next()
19 if '(GNU tar)' in version
:
21 version
= version
.split(')', 1)[1].strip()
23 version
= map(int, version
.split('.'))
24 _recent_gnu_tar
= version
> [1, 13, 92]
26 warn("Failed to extract GNU tar version number")
27 debug("Recent GNU tar = %s", _recent_gnu_tar
)
28 return _recent_gnu_tar
30 def unpack_archive(url
, data
, destdir
, extract
= None):
31 """Unpack stream 'data' into directory 'destdir'. If extract is given, extract just
32 that sub-directory from the archive. Works out the format from the name."""
34 if url
.endswith('.tar.bz2'):
35 extract_tar(data
, destdir
, extract
, '--bzip2')
36 elif url
.endswith('.rpm'):
37 extract_rpm(data
, destdir
, extract
)
38 elif url
.endswith('.tar.gz') or url
.endswith('.tgz'):
39 extract_tar(data
, destdir
, extract
, '-z')
41 raise SafeException('Unknown extension on "%s"; I only know .tgz, .tar.bz2 and .rpm' % url
)
43 def extract_rpm(stream
, destdir
, extract
= None):
45 raise SafeException('Sorry, but the "extract" attribute is not yet supported for RPMs')
46 fd
, cpiopath
= mkstemp('-rpm-tmp')
52 os
.dup2(stream
.fileno(), 0)
54 os
.execlp('rpm2cpio', 'rpm2cpio', '-')
59 id, status
= os
.waitpid(child
, 0)
62 raise SafeException("rpm2cpio failed; can't unpack RPM archive; exit code %d" % status
)
65 args
= ['cpio', '-mid', '--quiet']
66 _extract(file(cpiopath
), destdir
, args
)
67 # Set the mtime of every directory under 'tmp' to 0, since cpio doesn't
68 # preserve directory mtimes.
69 os
.path
.walk(destdir
, lambda arg
, dirname
, names
: os
.utime(dirname
, (0, 0)), None)
75 def extract_tar(stream
, destdir
, extract
, decompress
):
77 # Limit the characters we accept, to avoid sending dodgy
79 if not re
.match('^[a-zA-Z0-9][- _a-zA-Z0-9.]*$', extract
):
80 raise SafeException('Illegal character in extract attribute')
83 args
= ['tar', decompress
, '-x', '--no-same-owner', '--no-same-permissions']
85 args
= ['tar', decompress
, '-xf', '-']
90 _extract(stream
, destdir
, args
)
92 def _extract(stream
, destdir
, command
):
93 """Run execvp('command') inside destdir in a child process, with a
94 rewound stream as stdin."""
101 os
.dup2(stream
.fileno(), 0)
102 os
.execvp(command
[0], command
)
104 traceback
.print_exc()
107 id, status
= os
.waitpid(child
, 0)
110 raise SafeException('Failed to extract archive; exit code %d' % status
)