summaryrefslogtreecommitdiff
path: root/morphlib/stagingarea.py
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2013-08-06 14:41:49 +0100
committerRichard Maw <richard.maw@codethink.co.uk>2013-08-06 15:35:08 +0000
commit4fa9c69157ee93e01d9a2cb3edd12ffc4e2708e1 (patch)
tree23f4ba98b1c45957e64a0691fb7459c4f60e4895 /morphlib/stagingarea.py
parentf38205025791a93fa73945fa2c6f2ac666b212a1 (diff)
downloadmorph-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.
Diffstat (limited to 'morphlib/stagingarea.py')
-rw-r--r--morphlib/stagingarea.py20
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)