| OLD | NEW |
| 1 # Copyright 2013 The LUCI Authors. All rights reserved. | 1 # Copyright 2013 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 """Provides functions: get_native_path_case(), isabs() and safe_join(). | 5 """Provides functions: get_native_path_case(), isabs() and safe_join(). |
| 6 | 6 |
| 7 This module assumes that filesystem is not changing while current process | 7 This module assumes that filesystem is not changing while current process |
| 8 is running and thus it caches results of functions that depend on FS state. | 8 is running and thus it caches results of functions that depend on FS state. |
| 9 """ | 9 """ |
| 10 | 10 |
| 11 import ctypes | 11 import ctypes |
| 12 import getpass | 12 import getpass |
| 13 import logging | 13 import logging |
| 14 import os | 14 import os |
| 15 import posixpath | 15 import posixpath |
| 16 import re | 16 import re |
| 17 import shlex | 17 import shlex |
| 18 import stat | 18 import stat |
| 19 import sys | 19 import sys |
| 20 import tempfile | 20 import tempfile |
| 21 import time | 21 import time |
| 22 import unicodedata | 22 import unicodedata |
| 23 | 23 |
| 24 from utils import fs | 24 from utils import fs |
| 25 from utils import subprocess42 |
| 25 from utils import tools | 26 from utils import tools |
| 26 | 27 |
| 27 | 28 |
| 28 # Types of action accepted by link_file(). | 29 # Types of action accepted by link_file(). |
| 29 HARDLINK, HARDLINK_WITH_FALLBACK, SYMLINK, SYMLINK_WITH_FALLBACK, COPY = range( | 30 HARDLINK, HARDLINK_WITH_FALLBACK, SYMLINK, SYMLINK_WITH_FALLBACK, COPY = range( |
| 30 1, 6) | 31 1, 6) |
| 31 | 32 |
| 32 | 33 |
| 33 ## OS-specific imports | 34 ## OS-specific imports |
| 34 | 35 |
| (...skipping 834 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 869 fs.stat(outfile).st_mode | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) | 870 fs.stat(outfile).st_mode | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) |
| 870 | 871 |
| 871 | 872 |
| 872 def set_read_only(path, read_only): | 873 def set_read_only(path, read_only): |
| 873 """Sets or resets the write bit on a file or directory. | 874 """Sets or resets the write bit on a file or directory. |
| 874 | 875 |
| 875 Zaps out access to 'group' and 'others'. | 876 Zaps out access to 'group' and 'others'. |
| 876 """ | 877 """ |
| 877 mode = fs.lstat(path).st_mode | 878 mode = fs.lstat(path).st_mode |
| 878 # TODO(maruel): Stop removing GO bits. | 879 # TODO(maruel): Stop removing GO bits. |
| 879 mode = (mode & 0500) if read_only else (mode | 0200) | 880 if read_only: |
| 881 mode &= stat.S_IRUSR|stat.S_IXUSR # 0500 |
| 882 else: |
| 883 mode |= stat.S_IRUSR|stat.S_IWUSR # 0600 |
| 884 if sys.platform != 'win32' and stat.S_ISDIR(mode): |
| 885 mode |= stat.S_IXUSR # 0100 |
| 880 if hasattr(os, 'lchmod'): | 886 if hasattr(os, 'lchmod'): |
| 881 fs.lchmod(path, mode) # pylint: disable=E1101 | 887 fs.lchmod(path, mode) # pylint: disable=E1101 |
| 882 else: | 888 else: |
| 883 if stat.S_ISLNK(mode): | 889 if stat.S_ISLNK(mode): |
| 884 # Skip symlink without lchmod() support. | 890 # Skip symlink without lchmod() support. |
| 885 logging.debug( | 891 logging.debug( |
| 886 'Can\'t change %sw bit on symlink %s', | 892 'Can\'t change %sw bit on symlink %s', |
| 887 '-' if read_only else '+', path) | 893 '-' if read_only else '+', path) |
| 888 return | 894 return |
| 889 | 895 |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1090 | 1096 |
| 1091 On Windows, the files are modified. On other platforms, modify the directory. | 1097 On Windows, the files are modified. On other platforms, modify the directory. |
| 1092 It only does the minimum so the files can be deleted safely. | 1098 It only does the minimum so the files can be deleted safely. |
| 1093 | 1099 |
| 1094 Warning on Windows: since file permission is modified, the file node is | 1100 Warning on Windows: since file permission is modified, the file node is |
| 1095 modified. This means that for hard-linked files, every directory entry for the | 1101 modified. This means that for hard-linked files, every directory entry for the |
| 1096 file node has its file permission modified. | 1102 file node has its file permission modified. |
| 1097 """ | 1103 """ |
| 1098 logging.debug('make_tree_deleteable(%s)', root) | 1104 logging.debug('make_tree_deleteable(%s)', root) |
| 1099 err = None | 1105 err = None |
| 1106 sudo_failed = False |
| 1107 |
| 1108 def try_sudo(p): |
| 1109 if sys.platform == 'linux2' and not sudo_failed: |
| 1110 # Try passwordless sudo, just in case. In practice, it is preferable |
| 1111 # to use linux capabilities. |
| 1112 with open(os.devnull, 'rb') as f: |
| 1113 if not subprocess42.call( |
| 1114 ['sudo', '-n', 'chmod', 'a+rwX', p], stdin=f): |
| 1115 return False |
| 1116 logging.debug('sudo chmod %s failed', p) |
| 1117 return True |
| 1118 |
| 1100 if sys.platform != 'win32': | 1119 if sys.platform != 'win32': |
| 1101 e = set_read_only_swallow(root, False) | 1120 e = set_read_only_swallow(root, False) |
| 1121 if e: |
| 1122 sudo_failed = try_sudo(root) |
| 1102 if not err: | 1123 if not err: |
| 1103 err = e | 1124 err = e |
| 1104 for dirpath, dirnames, filenames in fs.walk(root, topdown=True): | 1125 for dirpath, dirnames, filenames in fs.walk(root, topdown=True): |
| 1105 if sys.platform == 'win32': | 1126 if sys.platform == 'win32': |
| 1106 for filename in filenames: | 1127 for filename in filenames: |
| 1107 e = set_read_only_swallow(os.path.join(dirpath, filename), False) | 1128 e = set_read_only_swallow(os.path.join(dirpath, filename), False) |
| 1108 if not err: | 1129 if not err: |
| 1109 err = e | 1130 err = e |
| 1110 else: | 1131 else: |
| 1111 for dirname in dirnames: | 1132 for dirname in dirnames: |
| 1112 e = set_read_only_swallow(os.path.join(dirpath, dirname), False) | 1133 p = os.path.join(dirpath, dirname) |
| 1134 e = set_read_only_swallow(p, False) |
| 1135 if e: |
| 1136 sudo_failed = try_sudo(p) |
| 1113 if not err: | 1137 if not err: |
| 1114 err = e | 1138 err = e |
| 1115 if err: | 1139 if err: |
| 1116 # pylint: disable=raising-bad-type | 1140 # pylint: disable=raising-bad-type |
| 1117 raise err | 1141 raise err |
| 1118 | 1142 |
| 1119 | 1143 |
| 1120 def rmtree(root): | 1144 def rmtree(root): |
| 1121 """Wrapper around shutil.rmtree() to retry automatically on Windows. | 1145 """Wrapper around shutil.rmtree() to retry automatically on Windows. |
| 1122 | 1146 |
| 1123 On Windows, forcibly kills processes that are found to interfere with the | 1147 On Windows, forcibly kills processes that are found to interfere with the |
| 1124 deletion. | 1148 deletion. |
| 1125 | 1149 |
| 1126 Returns: | 1150 Returns: |
| 1127 True on normal execution, False if berserk techniques (like killing | 1151 True on normal execution, False if berserk techniques (like killing |
| 1128 processes) had to be used. | 1152 processes) had to be used. |
| 1129 """ | 1153 """ |
| 1130 logging.info('rmtree(%s)', root) | 1154 logging.info('rmtree(%s)', root) |
| 1131 assert sys.getdefaultencoding() == 'utf-8', sys.getdefaultencoding() | 1155 assert isinstance(root, unicode) or sys.getdefaultencoding() == 'utf-8', ( |
| 1132 # Do not assert here yet because this would break too much code. | 1156 repr(root), sys.getdefaultencoding()) |
| 1133 root = unicode(root) | 1157 root = unicode(root) |
| 1134 try: | 1158 try: |
| 1135 make_tree_deleteable(root) | 1159 make_tree_deleteable(root) |
| 1136 except OSError as e: | 1160 except OSError as e: |
| 1137 logging.warning('Swallowing make_tree_deleteable() error: %s', e) | 1161 logging.warning('Swallowing make_tree_deleteable() error: %s', e) |
| 1138 | 1162 |
| 1139 # First try the soft way: tries 3 times to delete and sleep a bit in between. | 1163 # First try the soft way: tries 3 times to delete and sleep a bit in between. |
| 1140 # Retries help if test subprocesses outlive main process and try to actively | 1164 # Retries help if test subprocesses outlive main process and try to actively |
| 1141 # use or write to the directory while it is being deleted. | 1165 # use or write to the directory while it is being deleted. |
| 1142 max_tries = 3 | 1166 max_tries = 3 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1195 sys.stderr.write( | 1219 sys.stderr.write( |
| 1196 'Failed to delete %s. The following files remain:\n' % root) | 1220 'Failed to delete %s. The following files remain:\n' % root) |
| 1197 # The same path may be listed multiple times. | 1221 # The same path may be listed multiple times. |
| 1198 for path in sorted(set(path for _, path, _ in errors)): | 1222 for path in sorted(set(path for _, path, _ in errors)): |
| 1199 sys.stderr.write('- %s\n' % path) | 1223 sys.stderr.write('- %s\n' % path) |
| 1200 raise errors[0][2][0], errors[0][2][1], errors[0][2][2] | 1224 raise errors[0][2][0], errors[0][2][1], errors[0][2][2] |
| 1201 return False | 1225 return False |
| 1202 | 1226 |
| 1203 | 1227 |
| 1204 ## Private code. | 1228 ## Private code. |
| OLD | NEW |