Improve all of the Windows-specific error reporting.

Also make the Windows function declarations more readable and consistent.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
Daira Hopwood 2015-10-27 14:47:11 +00:00
parent 92301cb731
commit b88d450666
3 changed files with 93 additions and 45 deletions

View File

@ -7,9 +7,9 @@ from collections import namedtuple
from errno import ENOENT from errno import ENOENT
if sys.platform == "win32": if sys.platform == "win32":
from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong, create_unicode_buffer from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_ulonglong, \
from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR, LPWSTR, LPVOID, WinError, get_last_error create_unicode_buffer, get_last_error
from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR, LPWSTR, LPVOID
from twisted.python import log from twisted.python import log
@ -345,8 +345,7 @@ have_GetDiskFreeSpaceExW = False
if sys.platform == "win32": if sys.platform == "win32":
# <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx>
GetEnvironmentVariableW = WINFUNCTYPE( GetEnvironmentVariableW = WINFUNCTYPE(
DWORD, DWORD, LPCWSTR, LPWSTR, DWORD,
LPCWSTR, LPWSTR, DWORD,
use_last_error=True use_last_error=True
)(("GetEnvironmentVariableW", windll.kernel32)) )(("GetEnvironmentVariableW", windll.kernel32))
@ -356,8 +355,7 @@ if sys.platform == "win32":
# <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx>
GetDiskFreeSpaceExW = WINFUNCTYPE( GetDiskFreeSpaceExW = WINFUNCTYPE(
BOOL, BOOL, LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
use_last_error=True use_last_error=True
)(("GetDiskFreeSpaceExW", windll.kernel32)) )(("GetDiskFreeSpaceExW", windll.kernel32))
@ -407,8 +405,8 @@ def windows_getenv(name):
err = get_last_error() err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND: if err == ERROR_ENVVAR_NOT_FOUND:
return None return None
raise OSError("Windows error %d attempting to read size of environment variable %r" raise OSError("WinError: %s\n attempting to read size of environment variable %r"
% (err, name)) % (WinError(err), name))
if n == 1: if n == 1:
# Avoid an ambiguity between a zero-length string and an error in the return value of the # Avoid an ambiguity between a zero-length string and an error in the return value of the
# call to GetEnvironmentVariableW below. # call to GetEnvironmentVariableW below.
@ -420,8 +418,8 @@ def windows_getenv(name):
err = get_last_error() err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND: if err == ERROR_ENVVAR_NOT_FOUND:
return None return None
raise OSError("Windows error %d attempting to read environment variable %r" raise OSError("WinError: %s\n attempting to read environment variable %r"
% (err, name)) % (WinError(err), name))
if retval >= n: if retval >= n:
raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r" raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r"
% (retval, n, name)) % (retval, n, name))
@ -463,8 +461,8 @@ def get_disk_stats(whichdir, reserved_space=0):
byref(n_total), byref(n_total),
byref(n_free_for_root)) byref(n_free_for_root))
if retval == 0: if retval == 0:
raise OSError("Windows error %d attempting to get disk statistics for %r" raise OSError("WinError: %s\n attempting to get disk statistics for %r"
% (get_last_error(), whichdir)) % (WinError(get_last_error()), whichdir))
free_for_nonroot = n_free_for_nonroot.value free_for_nonroot = n_free_for_nonroot.value
total = n_total.value total = n_total.value
free_for_root = n_free_for_root.value free_for_root = n_free_for_root.value
@ -523,8 +521,10 @@ def get_available_space(whichdir, reserved_space):
if sys.platform == "win32": if sys.platform == "win32":
# <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx>
CreateFileW = WINFUNCTYPE(HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE) \ CreateFileW = WINFUNCTYPE(
(("CreateFileW", windll.kernel32)) HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE,
use_last_error=True
)(("CreateFileW", windll.kernel32))
GENERIC_WRITE = 0x40000000 GENERIC_WRITE = 0x40000000
FILE_SHARE_READ = 0x00000001 FILE_SHARE_READ = 0x00000001
@ -533,10 +533,16 @@ if sys.platform == "win32":
INVALID_HANDLE_VALUE = 0xFFFFFFFF INVALID_HANDLE_VALUE = 0xFFFFFFFF
# <http://msdn.microsoft.com/en-us/library/aa364439%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa364439%28v=vs.85%29.aspx>
FlushFileBuffers = WINFUNCTYPE(BOOL, HANDLE)(("FlushFileBuffers", windll.kernel32)) FlushFileBuffers = WINFUNCTYPE(
BOOL, HANDLE,
use_last_error=True
)(("FlushFileBuffers", windll.kernel32))
# <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx>
CloseHandle = WINFUNCTYPE(BOOL, HANDLE)(("CloseHandle", windll.kernel32)) CloseHandle = WINFUNCTYPE(
BOOL, HANDLE,
use_last_error=True
)(("CloseHandle", windll.kernel32))
# <http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/> # <http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/>
def flush_volume(path): def flush_volume(path):
@ -551,10 +557,10 @@ if sys.platform == "win32":
None None
) )
if hVolume == INVALID_HANDLE_VALUE: if hVolume == INVALID_HANDLE_VALUE:
raise WinError() raise WinError(get_last_error())
if FlushFileBuffers(hVolume) == 0: if FlushFileBuffers(hVolume) == 0:
raise WinError() raise WinError(get_last_error())
CloseHandle(hVolume) CloseHandle(hVolume)
else: else:
@ -577,8 +583,7 @@ def reraise(wrapper):
if sys.platform == "win32": if sys.platform == "win32":
# <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512%28v=vs.85%29.aspx> # <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512%28v=vs.85%29.aspx>
ReplaceFileW = WINFUNCTYPE( ReplaceFileW = WINFUNCTYPE(
BOOL, BOOL, LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID,
LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID,
use_last_error=True use_last_error=True
)(("ReplaceFileW", windll.kernel32)) )(("ReplaceFileW", windll.kernel32))

View File

@ -9,13 +9,18 @@ def initialize():
done = True done = True
import codecs, re 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 ctypes.wintypes import BOOL, HANDLE, DWORD, UINT, LPWSTR, LPCWSTR, LPVOID
from allmydata.util import log from allmydata.util import log
from allmydata.util.encodingutil import canonical_encoding from allmydata.util.encodingutil import canonical_encoding
# <https://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx> # <https://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx>
SetErrorMode = WINFUNCTYPE(UINT, UINT)(("SetErrorMode", windll.kernel32)) SetErrorMode = WINFUNCTYPE(
UINT, UINT,
use_last_error=True
)(("SetErrorMode", windll.kernel32))
SEM_FAILCRITICALERRORS = 0x0001 SEM_FAILCRITICALERRORS = 0x0001
SEM_NOOPENFILEERRORBOX = 0x8000 SEM_NOOPENFILEERRORBOX = 0x8000
@ -50,13 +55,27 @@ def initialize():
# <https://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx> # <https://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx>
# BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode); # 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_OUTPUT_HANDLE = DWORD(-11)
STD_ERROR_HANDLE = DWORD(-12) 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_CHAR = 0x0002
FILE_TYPE_REMOTE = 0x8000 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 INVALID_HANDLE_VALUE = DWORD(-1).value
def not_a_console(handle): def not_a_console(handle):
@ -88,11 +107,14 @@ def initialize():
real_stderr = False real_stderr = False
if real_stdout or real_stderr: if real_stdout or real_stderr:
# <https://msdn.microsoft.com/en-us/library/windows/desktop/ms687401%28v=vs.85%29.aspx>
# BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars, # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
# LPDWORD lpCharsWritten, LPVOID lpReserved); # LPDWORD lpCharsWritten, LPVOID lpReserved);
WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID) \ WriteConsoleW = WINFUNCTYPE(
(("WriteConsoleW", windll.kernel32)) BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID,
use_last_error=True
)(("WriteConsoleW", windll.kernel32))
class UnicodeOutput: class UnicodeOutput:
def __init__(self, hConsole, stream, fileno, name): 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 # There is a shorter-than-documented limitation on the length of the string
# passed to WriteConsoleW (see #1232). # passed to WriteConsoleW (see #1232).
retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None) retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None)
if retval == 0 or n.value == 0: if retval == 0:
raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value)) 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 remaining -= n.value
if remaining == 0: break if remaining == 0: break
text = text[n.value:] text = text[n.value:]
@ -169,12 +193,23 @@ def initialize():
_complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,)) _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))
# This works around <http://bugs.python.org/issue2128>. # This works around <http://bugs.python.org/issue2128>.
GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int)) \ # <https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156%28v=vs.85%29.aspx>
(("CommandLineToArgvW", windll.shell32)) GetCommandLineW = WINFUNCTYPE(
LPWSTR,
use_last_error=True
)(("GetCommandLineW", windll.kernel32))
# <https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx>
CommandLineToArgvW = WINFUNCTYPE(
POINTER(LPWSTR), LPCWSTR, POINTER(c_int),
use_last_error=True
)(("CommandLineToArgvW", windll.shell32))
argc = c_int(0) argc = c_int(0)
argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))
if argv_unicode is None:
raise WinError(get_last_error())
# Because of <http://bugs.python.org/issue8775> (and similar limitations in # Because of <http://bugs.python.org/issue8775> (and similar limitations in
# twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments. # twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments.

View File

@ -23,15 +23,18 @@ from allmydata.util.encodingutil import quote_output
from allmydata.util import log, fileutil from allmydata.util import log, fileutil
from allmydata.util.pollmixin import PollMixin from allmydata.util.pollmixin import PollMixin
from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, create_string_buffer, addressof from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, create_string_buffer, \
addressof, get_last_error
from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR, LPVOID from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR, LPVOID
# <http://msdn.microsoft.com/en-us/library/gg258116%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/gg258116%28v=vs.85%29.aspx>
FILE_LIST_DIRECTORY = 1 FILE_LIST_DIRECTORY = 1
# <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx>
CreateFileW = WINFUNCTYPE(HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE) \ CreateFileW = WINFUNCTYPE(
(("CreateFileW", windll.kernel32)) HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE,
use_last_error=True
)(("CreateFileW", windll.kernel32))
FILE_SHARE_READ = 0x00000001 FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002 FILE_SHARE_WRITE = 0x00000002
@ -42,11 +45,16 @@ OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
# <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx>
CloseHandle = WINFUNCTYPE(BOOL, HANDLE)(("CloseHandle", windll.kernel32)) CloseHandle = WINFUNCTYPE(
BOOL, HANDLE,
use_last_error=True
)(("CloseHandle", windll.kernel32))
# <http://msdn.microsoft.com/en-us/library/aa365465%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa365465%28v=vs.85%29.aspx>
ReadDirectoryChangesW = WINFUNCTYPE(BOOL, HANDLE, LPVOID, DWORD, BOOL, DWORD, POINTER(DWORD), LPVOID, LPVOID) \ ReadDirectoryChangesW = WINFUNCTYPE(
(("ReadDirectoryChangesW", windll.kernel32)) BOOL, HANDLE, LPVOID, DWORD, BOOL, DWORD, POINTER(DWORD), LPVOID, LPVOID,
use_last_error=True
)(("ReadDirectoryChangesW", windll.kernel32))
FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001 FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001
FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002 FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002
@ -121,7 +129,7 @@ class FileNotifyInformation(object):
None # NULL -> no completion routine None # NULL -> no completion routine
) )
if r == 0: if r == 0:
raise WinError() raise WinError(get_last_error())
self.data = self.buffer.raw[:bytes_returned.value] self.data = self.buffer.raw[:bytes_returned.value]
def __iter__(self): def __iter__(self):
@ -157,8 +165,8 @@ def _open_directory(path_u):
None # no template file None # no template file
) )
if hDirectory == INVALID_HANDLE_VALUE: if hDirectory == INVALID_HANDLE_VALUE:
e = WinError() e = WinError(get_last_error())
raise OSError("Opening directory %s gave Windows error %r: %s" % (quote_output(path_u), e.args[0], e.args[1])) raise OSError("Opening directory %s gave WinError: %s" % (quote_output(path_u), e))
return hDirectory return hDirectory