summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/bins.py91
-rw-r--r--morphlib/bins_tests.py4
-rw-r--r--morphlib/builder2.py2
-rw-r--r--morphlib/stagingarea.py84
4 files changed, 86 insertions, 95 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()
diff --git a/morphlib/bins_tests.py b/morphlib/bins_tests.py
index 82ceb246..dd2fb886 100644
--- a/morphlib/bins_tests.py
+++ b/morphlib/bins_tests.py
@@ -113,7 +113,7 @@ class ChunkTests(BinsTest):
def unpack_chunk(self):
os.mkdir(self.unpacked)
- morphlib.bins.unpack_binary(self.chunk_file, self.unpacked, self.ex)
+ morphlib.bins.unpack_binary(self.chunk_file, self.unpacked)
def test_empties_everything(self):
self.create_chunk(['.'])
@@ -158,7 +158,7 @@ class StratumTests(BinsTest):
morphlib.bins.create_stratum(self.instdir, self.stratum_f, self.ex)
self.stratum_f.flush()
os.mkdir(self.unpacked)
- morphlib.bins.unpack_binary(self.stratum_file, self.unpacked, self.ex)
+ morphlib.bins.unpack_binary(self.stratum_file, self.unpacked)
self.assertEqual(self.recursive_lstat(self.instdir),
self.recursive_lstat(self.unpacked))
diff --git a/morphlib/builder2.py b/morphlib/builder2.py
index 2a7ca2d2..62e484f4 100644
--- a/morphlib/builder2.py
+++ b/morphlib/builder2.py
@@ -81,7 +81,7 @@ def check_overlap(artifact, constituents, lac): #pragma: no cover
logging.warning('Overlaps in artifact %s detected' % artifact.name)
for overlapping, files in sorted(overlaps.iteritems()):
logging.warning(' Artifacts %s overlap with files:' %
- ', '.join(a.name for a in sorted(overlapping))
+ ', '.join(sorted(a.name for a in overlapping))
)
for filename in sorted(files):
logging.warning(' %s' % filename)
diff --git a/morphlib/stagingarea.py b/morphlib/stagingarea.py
index 8c84ac36..4f6d76bf 100644
--- a/morphlib/stagingarea.py
+++ b/morphlib/stagingarea.py
@@ -18,8 +18,6 @@ import errno
import logging
import os
import shutil
-import stat
-import tarfile
import morphlib
@@ -91,86 +89,8 @@ class StagingArea(object):
'''
logging.debug('Installing artifact %s' %
- getattr(handle, 'name', 'unknown name'))
- tf = tarfile.open(fileobj=handle)
-
- # 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.
-
- 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)
-
- tf.extractall(path=self.dirname)
+ getattr(handle, 'name', 'unknown name'))
+ morphlib.bins.unpack_binary_from_file(handle, self.dirname)
def remove(self):
'''Remove the entire staging area.