From e88e07a2781506a5eb661008ed6f5afde5ff5d4d Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Mon, 28 Dec 2015 20:27:35 +0000 Subject: [PATCH] Improved error handling and cosmetics for ctypes calls on Windows. Signed-off-by: Daira Hopwood --- src/allmydata/util/fileutil.py | 31 +++++++++-------- src/allmydata/windows/fixups.py | 59 ++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/allmydata/util/fileutil.py b/src/allmydata/util/fileutil.py index a2c3841f..152cae0b 100644 --- a/src/allmydata/util/fileutil.py +++ b/src/allmydata/util/fileutil.py @@ -4,6 +4,11 @@ Futz with files like a pro. import sys, exceptions, os, stat, tempfile, time, binascii +if sys.platform == "win32": + from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_ulonglong, \ + create_unicode_buffer, get_last_error + from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR + from twisted.python import log from pycryptopp.cipher.aes import AES @@ -335,16 +340,11 @@ def to_windows_long_path(path): have_GetDiskFreeSpaceExW = False if sys.platform == "win32": - from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong, create_unicode_buffer, \ - get_last_error - from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR - # GetEnvironmentVariableW = WINFUNCTYPE( - DWORD, - LPCWSTR, LPWSTR, DWORD, + DWORD, LPCWSTR, LPWSTR, DWORD, use_last_error=True - )(("GetEnvironmentVariableW", windll.kernel32)) + )(("GetEnvironmentVariableW", windll.kernel32)) try: # @@ -352,10 +352,9 @@ if sys.platform == "win32": # GetDiskFreeSpaceExW = WINFUNCTYPE( - BOOL, - LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER, + BOOL, LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER, use_last_error=True - )(("GetDiskFreeSpaceExW", windll.kernel32)) + )(("GetDiskFreeSpaceExW", windll.kernel32)) have_GetDiskFreeSpaceExW = True except Exception: @@ -403,8 +402,8 @@ def windows_getenv(name): err = get_last_error() if err == ERROR_ENVVAR_NOT_FOUND: return None - raise OSError("Windows error %d attempting to read size of environment variable %r" - % (err, name)) + raise OSError("WinError: %s\n attempting to read size of environment variable %r" + % (WinError(err), name)) if n == 1: # Avoid an ambiguity between a zero-length string and an error in the return value of the # call to GetEnvironmentVariableW below. @@ -416,8 +415,8 @@ def windows_getenv(name): err = get_last_error() if err == ERROR_ENVVAR_NOT_FOUND: return None - raise OSError("Windows error %d attempting to read environment variable %r" - % (err, name)) + raise OSError("WinError: %s\n attempting to read environment variable %r" + % (WinError(err), name)) if retval >= n: raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r" % (retval, n, name)) @@ -459,8 +458,8 @@ def get_disk_stats(whichdir, reserved_space=0): byref(n_total), byref(n_free_for_root)) if retval == 0: - raise OSError("Windows error %d attempting to get disk statistics for %r" - % (get_last_error(), whichdir)) + raise OSError("WinError: %s\n attempting to get disk statistics for %r" + % (WinError(get_last_error()), whichdir)) free_for_nonroot = n_free_for_nonroot.value total = n_total.value free_for_root = n_free_for_root.value diff --git a/src/allmydata/windows/fixups.py b/src/allmydata/windows/fixups.py index eaf5d5eb..490e0850 100644 --- a/src/allmydata/windows/fixups.py +++ b/src/allmydata/windows/fixups.py @@ -9,13 +9,18 @@ def initialize(): done = True import codecs, re - from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int + from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_int, get_last_error from ctypes.wintypes import BOOL, HANDLE, DWORD, UINT, LPWSTR, LPCWSTR, LPVOID + from allmydata.util import log from allmydata.util.encodingutil import canonical_encoding # - SetErrorMode = WINFUNCTYPE(UINT, UINT)(("SetErrorMode", windll.kernel32)) + SetErrorMode = WINFUNCTYPE( + UINT, UINT, + use_last_error=True + )(("SetErrorMode", windll.kernel32)) + SEM_FAILCRITICALERRORS = 0x0001 SEM_NOOPENFILEERRORBOX = 0x8000 @@ -50,13 +55,27 @@ def initialize(): # # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode); - GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32)) + GetStdHandle = WINFUNCTYPE( + HANDLE, DWORD, + use_last_error=True + )(("GetStdHandle", windll.kernel32)) + STD_OUTPUT_HANDLE = DWORD(-11) STD_ERROR_HANDLE = DWORD(-12) - GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32)) + + GetFileType = WINFUNCTYPE( + DWORD, DWORD, + use_last_error=True + )(("GetFileType", windll.kernel32)) + FILE_TYPE_CHAR = 0x0002 FILE_TYPE_REMOTE = 0x8000 - GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32)) + + GetConsoleMode = WINFUNCTYPE( + BOOL, HANDLE, POINTER(DWORD), + use_last_error=True + )(("GetConsoleMode", windll.kernel32)) + INVALID_HANDLE_VALUE = DWORD(-1).value def not_a_console(handle): @@ -88,11 +107,14 @@ def initialize(): real_stderr = False if real_stdout or real_stderr: + # # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars, # LPDWORD lpCharsWritten, LPVOID lpReserved); - WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID) \ - (("WriteConsoleW", windll.kernel32)) + WriteConsoleW = WINFUNCTYPE( + BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID, + use_last_error=True + )(("WriteConsoleW", windll.kernel32)) class UnicodeOutput: def __init__(self, hConsole, stream, fileno, name): @@ -139,8 +161,10 @@ def initialize(): # There is a shorter-than-documented limitation on the length of the string # passed to WriteConsoleW (see #1232). retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None) - if retval == 0 or n.value == 0: - raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value)) + if retval == 0: + raise IOError("WriteConsoleW failed with WinError: %s" % (WinError(get_last_error()),)) + if n.value == 0: + raise IOError("WriteConsoleW returned %r, n.value = 0" % (retval,)) remaining -= n.value if remaining == 0: break text = text[n.value:] @@ -169,12 +193,23 @@ def initialize(): _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,)) # This works around . - GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) - CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int)) \ - (("CommandLineToArgvW", windll.shell32)) + + # + GetCommandLineW = WINFUNCTYPE( + LPWSTR, + use_last_error=True + )(("GetCommandLineW", windll.kernel32)) + + # + CommandLineToArgvW = WINFUNCTYPE( + POINTER(LPWSTR), LPCWSTR, POINTER(c_int), + use_last_error=True + )(("CommandLineToArgvW", windll.shell32)) argc = c_int(0) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) + if argv_unicode is None: + raise WinError(get_last_error()) # Because of (and similar limitations in # twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments. -- 2.45.2