diff options
-rw-r--r-- | morphlib/bins.py | 91 | ||||
-rw-r--r-- | morphlib/bins_tests.py | 4 | ||||
-rw-r--r-- | morphlib/builder2.py | 2 | ||||
-rw-r--r-- | morphlib/stagingarea.py | 84 |
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. |