summaryrefslogtreecommitdiff
path: root/morphlib/bins.py
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2012-05-14 17:34:50 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2012-05-14 17:34:50 +0000
commit41c647ec20d3e58348535e5d2f76f80e977b47ce (patch)
treec249faf7f1c5c8047213b67619448b605a7d1690 /morphlib/bins.py
parentcd8170c8f7a6c8c82d43f8b992c07041fa22a31b (diff)
downloadmorph-41c647ec20d3e58348535e5d2f76f80e977b47ce.tar.gz
builder: use the same tar extract logic for all artifacts
Before only the staging area had symbolic links handled, now strata and systems will have the same logic.
Diffstat (limited to 'morphlib/bins.py')
-rw-r--r--morphlib/bins.py91
1 files changed, 81 insertions, 10 deletions
diff --git a/morphlib/bins.py b/morphlib/bins.py
index 8fd01182..3947e28d 100644
--- a/morphlib/bins.py
+++ b/morphlib/bins.py
@@ -24,6 +24,7 @@ Binaries are chunks, strata, and system images.
import logging
import os
import re
+import stat
import tarfile
@@ -109,17 +110,87 @@ def unpack_binary_from_file(f, dirname): # pragma: no cover
'''
tf = tarfile.open(fileobj=f)
- tf.extractall(path=dirname)
- tf.close()
-
-
-def unpack_binary(filename, dirname, ex):
- '''Unpack a binary into a directory.
+
+ # This is evil, but necessary. For some reason Python's system
+ # call wrappers (os.mknod and such) do not (always?) set the
+ # filename attribute of the OSError exception they raise. We
+ # fix that by monkey patching the tf instance with wrappers
+ # for the relevant methods to add things. The wrapper further
+ # ignores EEXIST errors, since we do not (currently!) care about
+ # overwriting files.
- The directory must exist already.
+ def follow_symlink(path): # pragma: no cover
+ try:
+ return os.stat(path)
+ except OSError:
+ return None
- '''
+ def prepare_extract(tarinfo, targetpath): # pragma: no cover
+ '''Prepare to extract a tar file member onto targetpath?
+
+ If the target already exist, and we can live with it or
+ remove it, we do so. Otherwise, raise an error.
+
+ It's OK to extract if:
+
+ * the target does not exist
+ * the member is a directory a directory and the
+ target is a directory or a symlink to a directory
+ (just extract, no need to remove)
+ * the member is not a directory, and the target is not a directory
+ or a symlink to a directory (remove target, then extract)
+
+ '''
+
+ try:
+ existing = os.lstat(targetpath)
+ except OSError:
+ return True # target does not exist
+
+ if tarinfo.isdir():
+ if stat.S_ISDIR(existing.st_mode):
+ return True
+ elif stat.S_ISLNK(existing.st_mode):
+ st = follow_symlink(targetpath)
+ return st and stat.S_ISDIR(st.st_mode)
+ else:
+ if stat.S_ISDIR(existing.st_mode):
+ return False
+ elif stat.S_ISLNK(existing.st_mode):
+ st = follow_symlink(targetpath)
+ if st and not stat.S_ISDIR(st.st_mode):
+ os.remove(targetpath)
+ return True
+ else:
+ os.remove(targetpath)
+ return True
+ return False
+
+ def monkey_patcher(real):
+ def make_something(tarinfo, targetpath): # pragma: no cover
+ prepare_extract(tarinfo, targetpath)
+ try:
+ return real(tarinfo, targetpath)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ if e.filename is None:
+ e.filename = targetpath
+ raise e
+ else:
+ raise
+ return make_something
+
+ tf.makedir = monkey_patcher(tf.makedir)
+ tf.makefile = monkey_patcher(tf.makefile)
+ tf.makeunknown = monkey_patcher(tf.makeunknown)
+ tf.makefifo = monkey_patcher(tf.makefifo)
+ tf.makedev = monkey_patcher(tf.makedev)
+ tf.makelink = monkey_patcher(tf.makelink)
- logging.debug('Unpacking %s into %s' % (filename, dirname))
- ex.runv(['tar', '-C', dirname, '-xvhf', filename])
+ tf.extractall(path=dirname)
+ tf.close
+def unpack_binary(filename, dirname):
+ f = open(filename, "rb")
+ unpack_binary_from_file(f, dirname)
+ f.close()