diff options
author | Richard Maw <richard.maw@codethink.co.uk> | 2013-08-06 14:41:49 +0100 |
---|---|---|
committer | Richard Maw <richard.maw@codethink.co.uk> | 2013-08-06 15:35:08 +0000 |
commit | 4fa9c69157ee93e01d9a2cb3edd12ffc4e2708e1 (patch) | |
tree | 23f4ba98b1c45957e64a0691fb7459c4f60e4895 | |
parent | f38205025791a93fa73945fa2c6f2ac666b212a1 (diff) | |
download | morph-4fa9c69157ee93e01d9a2cb3edd12ffc4e2708e1.tar.gz |
stagingarea: Construct hardlink cache atomically
Paul had file system problems which led to a partially constructed
chunk hardlink cache, which caused later builds to fail, since they
got a partially extracted chunk.
This patch fixes the case where the failed extract caused unreproducible
builds, but it's possible to corrupt the hardlink cache in other ways.
Read-only btrfs subvolumes would fix this, but either tie us further to
btrfs, or complicates the codebase with fallback logic.
-rw-r--r-- | morphlib/stagingarea.py | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/morphlib/stagingarea.py b/morphlib/stagingarea.py index 8e92e039..9aa8021b 100644 --- a/morphlib/stagingarea.py +++ b/morphlib/stagingarea.py @@ -20,6 +20,7 @@ import shutil import stat import cliapp from urlparse import urlparse +import tempfile import morphlib @@ -158,14 +159,21 @@ class StagingArea(object): logging.debug('Installing artifact %s' % getattr(handle, 'name', 'unknown name')) + chunk_cache_dir = os.path.join(self._app.settings['tempdir'], 'chunks') unpacked_artifact = os.path.join( - self._app.settings['tempdir'], - 'chunks', - os.path.basename(handle.name) + '.d') + chunk_cache_dir, os.path.basename(handle.name) + '.d') if not os.path.exists(unpacked_artifact): - self._mkdir(unpacked_artifact) - morphlib.bins.unpack_binary_from_file( - handle, unpacked_artifact + '/') + savedir = tempfile.mkdtemp(dir=chunk_cache_dir) + try: + morphlib.bins.unpack_binary_from_file( + handle, savedir + '/') + except BaseException, e: # pragma: no cover + shutil.rmtree(savedir) + raise + # TODO: This rename is not concurrency safe if two builds are + # extracting the same chunk, one build will fail because + # the other renamed its tempdir here first. + os.rename(savedir, unpacked_artifact) if not os.path.exists(self.dirname): self._mkdir(self.dirname) |