# -*- coding: utf-8 -*- # Copyright (C) 2012 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause from __future__ import absolute_import, division, print_function, unicode_literals from errno import EACCES, ENOENT, EPERM, EROFS from itertools import chain from logging import getLogger from os import X_OK, access, chmod, lstat, walk from os.path import isdir, isfile, join from stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISREG, S_IWRITE, S_IXGRP, S_IXOTH, S_IXUSR from . import MAX_TRIES, exp_backoff_fn from .link import islink, lchmod from ...common.compat import on_win log = getLogger(__name__) def make_writable(path): try: mode = lstat(path).st_mode if S_ISDIR(mode): chmod(path, S_IMODE(mode) | S_IWRITE | S_IEXEC) elif islink(path): lchmod(path, S_IMODE(mode) | S_IWRITE) elif S_ISREG(mode): chmod(path, S_IMODE(mode) | S_IWRITE) else: log.debug("path cannot be made writable: %s", path) return True except Exception as e: eno = getattr(e, 'errno', None) if eno in (ENOENT,): log.debug("tried to make writable, but didn't exist: %s", path) raise elif eno in (EACCES, EPERM, EROFS): log.debug("tried make writable but failed: %s\n%r", path, e) return False else: log.warn("Error making path writable: %s\n%r", path, e) raise def make_read_only(path): mode = lstat(path).st_mode if S_ISDIR(mode): chmod(path, S_IMODE(mode) & ~S_IWRITE) elif islink(path): lchmod(path, S_IMODE(mode) & ~S_IWRITE) elif S_ISREG(mode): chmod(path, S_IMODE(mode) & ~S_IWRITE) else: log.debug("path cannot be made read only: %s", path) return True def recursive_make_writable(path, max_tries=MAX_TRIES): # The need for this function was pointed out at # https://github.com/conda/conda/issues/3266#issuecomment-239241915 # Especially on windows, file removal will often fail because it is marked read-only if isdir(path): for root, dirs, files in walk(path): for path in chain.from_iterable((files, dirs)): try: exp_backoff_fn(make_writable, join(root, path), max_tries=max_tries) except (IOError, OSError) as e: if e.errno == ENOENT: log.debug("no such file or directory: %s", path) else: raise else: exp_backoff_fn(make_writable, path, max_tries=max_tries) def make_executable(path): if isfile(path): mode = lstat(path).st_mode log.trace('chmod +x %s', path) chmod(path, S_IMODE(mode) | S_IXUSR | S_IXGRP | S_IXOTH) else: log.error("Cannot make path '%s' executable", path) def is_executable(path): if isfile(path): # for now, leave out `and not islink(path)` return path.endswith(('.exe', '.bat')) if on_win else access(path, X_OK) return False