diff options
author | Lars Wirzenius <lars.wirzenius@codethink.co.uk> | 2012-04-30 11:30:19 +0100 |
---|---|---|
committer | Lars Wirzenius <lars.wirzenius@codethink.co.uk> | 2012-04-30 11:30:19 +0100 |
commit | 8de9212fff953e967209dfc7f285ecb794152a9a (patch) | |
tree | 59432ec0debe4d835bed918b668c08a79d4e8278 /morphlib | |
parent | 29f35250b009101a3ac86353da9512879ecface3 (diff) | |
download | morph-8de9212fff953e967209dfc7f285ecb794152a9a.tar.gz |
Improve artifact extraction logic when overwriting existing stuff on filesystem
We had a problem where something in the bootstrap would fail because
two different chunk artifacts had a path (/tools/lib64) exist
as a directory exist as a symlink (to /tools/lib) versus a directory,
and morph confusing things.
The new logic should handle this better.
Diffstat (limited to 'morphlib')
-rw-r--r-- | morphlib/stagingarea.py | 52 |
1 files changed, 49 insertions, 3 deletions
diff --git a/morphlib/stagingarea.py b/morphlib/stagingarea.py index f730fd51..4b74a4d0 100644 --- a/morphlib/stagingarea.py +++ b/morphlib/stagingarea.py @@ -18,6 +18,7 @@ import errno import logging import os import shutil +import stat import tarfile import morphlib @@ -101,11 +102,56 @@ class StagingArea(object): # 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 existing.isdir(): + return True + elif existing.islnk(): + st = follow_symlink(targetpath) + return st and stat.S_ISDIR(st.st_mode) + else: + if existing.isdir(): + return False + elif existing.islnk(): + 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 - if os.path.exists(targetpath): - try: os.remove(targetpath) - except: pass + prepare_extract(tarinfo, targetpath) try: return real(tarinfo, targetpath) except OSError, e: |