| Index: client/utils/file_path.py
|
| diff --git a/client/utils/file_path.py b/client/utils/file_path.py
|
| index 68e03b39c645ac2e28fbc2abf073782770493a2e..60dbb6b31353f9aaa216ba60b8807ca1c888e803 100644
|
| --- a/client/utils/file_path.py
|
| +++ b/client/utils/file_path.py
|
| @@ -246,6 +246,7 @@ if sys.platform == 'win32':
|
| if not (os.stat(path).st_mode & stat.S_IWUSR):
|
| os.chmod(path, 0777)
|
|
|
| +
|
| def isabs(path):
|
| """Accepts X: as an absolute path, unlike python's os.path.isabs()."""
|
| return os.path.isabs(path) or len(path) == 2 and path[1] == ':'
|
| @@ -307,7 +308,95 @@ if sys.platform == 'win32':
|
| return out[0].upper() + out[1:] + suffix
|
|
|
|
|
| - def enum_processes_win():
|
| + def get_process_token():
|
| + """Get the current process token."""
|
| + TOKEN_ALL_ACCESS = 0xF01FF
|
| + token = ctypes.wintypes.HANDLE()
|
| + if not OpenProcessToken(
|
| + GetCurrentProcess(), TOKEN_ALL_ACCESS, ctypes.byref(token)):
|
| + # pylint: disable=undefined-variable
|
| + raise WindowsError('Couldn\'t get process token')
|
| + return token
|
| +
|
| +
|
| + def get_luid(name):
|
| + """Returns the LUID for a privilege."""
|
| + luid = LUID()
|
| + if not LookupPrivilegeValue(None, unicode(name), ctypes.byref(luid)):
|
| + # pylint: disable=undefined-variable
|
| + raise WindowsError('Couldn\'t lookup privilege value')
|
| + return luid
|
| +
|
| +
|
| + def enable_privilege(name):
|
| + """Enables the privilege for the current process token.
|
| +
|
| + Returns:
|
| + - True if the assignment is successful.
|
| + """
|
| + SE_PRIVILEGE_ENABLED = 2
|
| + ERROR_NOT_ALL_ASSIGNED = 1300
|
| +
|
| + size = ctypes.sizeof(TOKEN_PRIVILEGES) + ctypes.sizeof(LUID_AND_ATTRIBUTES)
|
| + buf = ctypes.create_string_buffer(size)
|
| + tp = ctypes.cast(buf, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
|
| + tp.count = 1
|
| + tp.get_array()[0].LUID = get_luid(name)
|
| + tp.get_array()[0].Attributes = SE_PRIVILEGE_ENABLED
|
| + token = get_process_token()
|
| + try:
|
| + if not AdjustTokenPrivileges(token, False, tp, 0, None, None):
|
| + # pylint: disable=undefined-variable
|
| + raise WindowsError('Error in AdjustTokenPrivileges')
|
| + finally:
|
| + ctypes.windll.kernel32.CloseHandle(token)
|
| + return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED
|
| +
|
| +
|
| + def enable_symlink():
|
| + """Enable SeCreateSymbolicLinkPrivilege for the current token.
|
| +
|
| + Returns:
|
| + - True if symlink support is enabled.
|
| +
|
| + Thanks Microsoft. This is appreciated.
|
| + """
|
| + return enable_privilege(u'SeCreateSymbolicLinkPrivilege')
|
| +
|
| +
|
| + def kill_children_processes(root):
|
| + """Try to kill all children processes indistriminately and prints updates to
|
| + stderr.
|
| +
|
| + Returns:
|
| + True if at least one child process was found.
|
| + """
|
| + processes = _get_children_processes_win(root)
|
| + if not processes:
|
| + return False
|
| + sys.stderr.write('Enumerating processes:\n')
|
| + for _, proc in sorted(processes.iteritems()):
|
| + sys.stderr.write(
|
| + '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
|
| + proc.ProcessId,
|
| + proc.HandleCount,
|
| + proc.ExecutablePath,
|
| + proc.CommandLine))
|
| + sys.stderr.write('Terminating %d processes:\n' % len(processes))
|
| + for pid in sorted(processes):
|
| + try:
|
| + # Killing is asynchronous.
|
| + os.kill(pid, 9)
|
| + sys.stderr.write('- %d killed\n' % pid)
|
| + except OSError:
|
| + sys.stderr.write('- failed to kill %s\n' % pid)
|
| + return True
|
| +
|
| +
|
| + ## Windows private code.
|
| +
|
| +
|
| + def _enum_processes_win():
|
| """Returns all processes on the system that are accessible to this process.
|
|
|
| Returns:
|
| @@ -320,7 +409,7 @@ if sys.platform == 'win32':
|
| return [proc for proc in wbem.ExecQuery('SELECT * FROM Win32_Process')]
|
|
|
|
|
| - def filter_processes_dir_win(processes, root_dir):
|
| + def _filter_processes_dir_win(processes, root_dir):
|
| """Returns all processes which has their main executable located inside
|
| root_dir.
|
| """
|
| @@ -352,7 +441,7 @@ if sys.platform == 'win32':
|
| ]
|
|
|
|
|
| - def filter_processes_tree_win(processes):
|
| + def _filter_processes_tree_win(processes):
|
| """Returns all the processes under the current process."""
|
| # Convert to dict.
|
| processes = dict((p.ProcessId, p) for p in processes)
|
| @@ -371,60 +460,25 @@ if sys.platform == 'win32':
|
| return out.values()
|
|
|
|
|
| - def get_process_token():
|
| - """Get the current process token."""
|
| - TOKEN_ALL_ACCESS = 0xF01FF
|
| - token = ctypes.wintypes.HANDLE()
|
| - if not OpenProcessToken(
|
| - GetCurrentProcess(), TOKEN_ALL_ACCESS, ctypes.byref(token)):
|
| - # pylint: disable=undefined-variable
|
| - raise WindowsError('Couldn\'t get process token')
|
| - return token
|
| + def _get_children_processes_win(root):
|
| + """Returns a list of processes.
|
|
|
| + Enumerates both:
|
| + - all child processes from this process.
|
| + - processes where the main executable in inside 'root'. The reason is that
|
| + the ancestry may be broken so stray grand-children processes could be
|
| + undetected by the first technique.
|
|
|
| - def get_luid(name):
|
| - """Returns the LUID for a privilege."""
|
| - luid = LUID()
|
| - if not LookupPrivilegeValue(None, unicode(name), ctypes.byref(luid)):
|
| - # pylint: disable=undefined-variable
|
| - raise WindowsError('Couldn\'t lookup privilege value')
|
| - return luid
|
| -
|
| -
|
| - def enable_privilege(name):
|
| - """Enables the privilege for the current process token.
|
| -
|
| - Returns:
|
| - - True if the assignment is successful.
|
| + This technique is not fool-proof but gets mostly there.
|
| """
|
| - SE_PRIVILEGE_ENABLED = 2
|
| - ERROR_NOT_ALL_ASSIGNED = 1300
|
| -
|
| - size = ctypes.sizeof(TOKEN_PRIVILEGES) + ctypes.sizeof(LUID_AND_ATTRIBUTES)
|
| - buf = ctypes.create_string_buffer(size)
|
| - tp = ctypes.cast(buf, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
|
| - tp.count = 1
|
| - tp.get_array()[0].LUID = get_luid(name)
|
| - tp.get_array()[0].Attributes = SE_PRIVILEGE_ENABLED
|
| - token = get_process_token()
|
| - try:
|
| - if not AdjustTokenPrivileges(token, False, tp, 0, None, None):
|
| - # pylint: disable=undefined-variable
|
| - raise WindowsError('Error in AdjustTokenPrivileges')
|
| - finally:
|
| - ctypes.windll.kernel32.CloseHandle(token)
|
| - return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED
|
| -
|
| -
|
| - def enable_symlink():
|
| - """Enable SeCreateSymbolicLinkPrivilege for the current token.
|
| -
|
| - Returns:
|
| - - True if symlink support is enabled.
|
| -
|
| - Thanks Microsoft. This is appreciated.
|
| - """
|
| - return enable_privilege(u'SeCreateSymbolicLinkPrivilege')
|
| + processes = _enum_processes_win()
|
| + tree_processes = _filter_processes_tree_win(processes)
|
| + dir_processes = _filter_processes_dir_win(processes, root)
|
| + # Convert to dict to remove duplicates.
|
| + processes = dict((p.ProcessId, p) for p in tree_processes)
|
| + processes.update((p.ProcessId, p) for p in dir_processes)
|
| + processes.pop(os.getpid())
|
| + return processes
|
|
|
|
|
| elif sys.platform == 'darwin':
|
| @@ -434,42 +488,6 @@ elif sys.platform == 'darwin':
|
| isabs = os.path.isabs
|
|
|
|
|
| - def _native_case(p):
|
| - """Gets the native path case. Warning: this function resolves symlinks."""
|
| - try:
|
| - rel_ref, _ = Carbon.File.FSPathMakeRef(p.encode('utf-8'))
|
| - # The OSX underlying code uses NFD but python strings are in NFC. This
|
| - # will cause issues with os.listdir() for example. Since the dtrace log
|
| - # *is* in NFC, normalize it here.
|
| - out = unicodedata.normalize(
|
| - 'NFC', rel_ref.FSRefMakePath().decode('utf-8'))
|
| - if p.endswith(os.path.sep) and not out.endswith(os.path.sep):
|
| - return out + os.path.sep
|
| - return out
|
| - except MacOS.Error, e:
|
| - if e.args[0] in (-43, -120):
|
| - # The path does not exist. Try to recurse and reconstruct the path.
|
| - # -43 means file not found.
|
| - # -120 means directory not found.
|
| - base = os.path.dirname(p)
|
| - rest = os.path.basename(p)
|
| - return os.path.join(_native_case(base), rest)
|
| - raise OSError(
|
| - e.args[0], 'Failed to get native path for %s' % p, p, e.args[1])
|
| -
|
| -
|
| - def _split_at_symlink_native(base_path, rest):
|
| - """Returns the native path for a symlink."""
|
| - base, symlink, rest = split_at_symlink(base_path, rest)
|
| - if symlink:
|
| - if not base_path:
|
| - base_path = base
|
| - else:
|
| - base_path = safe_join(base_path, base)
|
| - symlink = find_item_native_case(base_path, symlink)
|
| - return base, symlink, rest
|
| -
|
| -
|
| def find_item_native_case(root_path, item):
|
| """Gets the native path case of a single item based at root_path.
|
|
|
| @@ -537,6 +555,45 @@ elif sys.platform == 'darwin':
|
| return True
|
|
|
|
|
| + ## OSX private code.
|
| +
|
| +
|
| + def _native_case(p):
|
| + """Gets the native path case. Warning: this function resolves symlinks."""
|
| + try:
|
| + rel_ref, _ = Carbon.File.FSPathMakeRef(p.encode('utf-8'))
|
| + # The OSX underlying code uses NFD but python strings are in NFC. This
|
| + # will cause issues with os.listdir() for example. Since the dtrace log
|
| + # *is* in NFC, normalize it here.
|
| + out = unicodedata.normalize(
|
| + 'NFC', rel_ref.FSRefMakePath().decode('utf-8'))
|
| + if p.endswith(os.path.sep) and not out.endswith(os.path.sep):
|
| + return out + os.path.sep
|
| + return out
|
| + except MacOS.Error, e:
|
| + if e.args[0] in (-43, -120):
|
| + # The path does not exist. Try to recurse and reconstruct the path.
|
| + # -43 means file not found.
|
| + # -120 means directory not found.
|
| + base = os.path.dirname(p)
|
| + rest = os.path.basename(p)
|
| + return os.path.join(_native_case(base), rest)
|
| + raise OSError(
|
| + e.args[0], 'Failed to get native path for %s' % p, p, e.args[1])
|
| +
|
| +
|
| + def _split_at_symlink_native(base_path, rest):
|
| + """Returns the native path for a symlink."""
|
| + base, symlink, rest = split_at_symlink(base_path, rest)
|
| + if symlink:
|
| + if not base_path:
|
| + base_path = base
|
| + else:
|
| + base_path = safe_join(base_path, base)
|
| + symlink = find_item_native_case(base_path, symlink)
|
| + return base, symlink, rest
|
| +
|
| +
|
| else: # OSes other than Windows and OSX.
|
|
|
|
|
| @@ -652,6 +709,12 @@ if sys.platform != 'win32': # All non-Windows OSes.
|
| return relfile, None, None
|
|
|
|
|
| + def kill_children_processes(root):
|
| + """Not yet implemented on posix."""
|
| + # pylint: disable=unused-argument
|
| + return False
|
| +
|
| +
|
| def relpath(path, root):
|
| """os.path.relpath() that keeps trailing os.path.sep."""
|
| out = os.path.relpath(path, root)
|
| @@ -693,17 +756,6 @@ def posix_relpath(path, root):
|
| return out
|
|
|
|
|
| -def cleanup_path(x):
|
| - """Cleans up a relative path. Converts any os.path.sep to '/' on Windows."""
|
| - if x:
|
| - x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
|
| - if x == '.':
|
| - x = ''
|
| - if x:
|
| - x += '/'
|
| - return x
|
| -
|
| -
|
| def is_url(path):
|
| """Returns True if it looks like an HTTP url instead of a file path."""
|
| return bool(re.match(r'^https?://.+$', path))
|
| @@ -1124,7 +1176,7 @@ def rmtree(root):
|
|
|
| # The soft way was not good enough. Try the hard way.
|
| for i in xrange(max_tries):
|
| - if not _kill_children_processes_win(root):
|
| + if not kill_children_processes(root):
|
| break
|
| if i != max_tries - 1:
|
| time.sleep((i+1)*2)
|
| @@ -1150,53 +1202,3 @@ def rmtree(root):
|
|
|
|
|
| ## Private code.
|
| -
|
| -
|
| -def _kill_children_processes_win(root):
|
| - """Try to kill all children processes indistriminately and prints updates to
|
| - stderr.
|
| -
|
| - Returns:
|
| - True if at least one child process was found.
|
| - """
|
| - processes = _get_children_processes_win(root)
|
| - if not processes:
|
| - return False
|
| - sys.stderr.write('Enumerating processes:\n')
|
| - for _, proc in sorted(processes.iteritems()):
|
| - sys.stderr.write(
|
| - '- pid %d; Handles: %d; Exe: %s; Cmd: %s\n' % (
|
| - proc.ProcessId,
|
| - proc.HandleCount,
|
| - proc.ExecutablePath,
|
| - proc.CommandLine))
|
| - sys.stderr.write('Terminating %d processes:\n' % len(processes))
|
| - for pid in sorted(processes):
|
| - try:
|
| - # Killing is asynchronous.
|
| - os.kill(pid, 9)
|
| - sys.stderr.write('- %d killed\n' % pid)
|
| - except OSError:
|
| - sys.stderr.write('- failed to kill %s\n' % pid)
|
| - return True
|
| -
|
| -
|
| -def _get_children_processes_win(root):
|
| - """Returns a list of processes.
|
| -
|
| - Enumerates both:
|
| - - all child processes from this process.
|
| - - processes where the main executable in inside 'root'. The reason is that
|
| - the ancestry may be broken so stray grand-children processes could be
|
| - undetected by the first technique.
|
| -
|
| - This technique is not fool-proof but gets mostly there.
|
| - """
|
| - processes = enum_processes_win()
|
| - tree_processes = filter_processes_tree_win(processes)
|
| - dir_processes = filter_processes_dir_win(processes, root)
|
| - # Convert to dict to remove duplicates.
|
| - processes = dict((p.ProcessId, p) for p in tree_processes)
|
| - processes.update((p.ProcessId, p) for p in dir_processes)
|
| - processes.pop(os.getpid())
|
| - return processes
|
|
|