diff --git a/dvc/exceptions.py b/dvc/exceptions.py index 17b4a87eda..b793dad2a3 100644 --- a/dvc/exceptions.py +++ b/dvc/exceptions.py @@ -218,6 +218,11 @@ def __init__(self, path, hint=None): ) +class FileOwnershipError(DvcException): + def __init__(self, path): + super().__init__(f"file '{path}' not owned by user! ") + + class DvcIgnoreInCollectedDirError(DvcException): def __init__(self, ignore_dirname): super().__init__( diff --git a/dvc/utils/fs.py b/dvc/utils/fs.py index d80bce6ede..a5cdf1ca88 100644 --- a/dvc/utils/fs.py +++ b/dvc/utils/fs.py @@ -8,7 +8,7 @@ import nanotime from shortuuid import uuid -from dvc.exceptions import DvcException +from dvc.exceptions import DvcException, FileOwnershipError from dvc.system import System from dvc.utils import dict_md5 @@ -95,15 +95,21 @@ def move(src, dst, mode=None): dst = os.path.abspath(dst) tmp = f"{dst}.{uuid()}" + try: + if mode is not None: + os.chmod(src, mode) + except OSError as e: + if e.errno not in [errno.EACCES, errno.EPERM]: + raise + else: + raise FileOwnershipError(src) + if os.path.islink(src): shutil.copy(src, tmp) - os.unlink(src) + _unlink(src, _chmod) else: shutil.move(src, tmp) - if mode is not None: - os.chmod(tmp, mode) - shutil.move(tmp, dst)