]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/util/fileutil.py
Improve all of the Windows-specific error reporting.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / fileutil.py
index f923e1a03f466231e34a4d64bbb69c23eb7c9be5..857d3ebc12dce4fc96a7129dc74b34d03f416880 100644 (file)
@@ -6,6 +6,11 @@ import sys, exceptions, os, stat, tempfile, time, binascii
 from collections import namedtuple
 from errno import ENOENT
 
+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, HANDLE, DWORD, LPCWSTR, LPWSTR, LPVOID
+
 from twisted.python import log
 
 from pycryptopp.cipher.aes import AES
@@ -338,16 +343,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
-
     # <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx>
     GetEnvironmentVariableW = WINFUNCTYPE(
-        DWORD,
-          LPCWSTR, LPWSTR, DWORD,
+        DWORD,  LPCWSTR, LPWSTR, DWORD,
         use_last_error=True
-      )(("GetEnvironmentVariableW", windll.kernel32))
+    )(("GetEnvironmentVariableW", windll.kernel32))
 
     try:
         # <http://msdn.microsoft.com/en-us/library/aa383742%28v=VS.85%29.aspx>
@@ -355,10 +355,9 @@ if sys.platform == "win32":
 
         # <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx>
         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:
@@ -406,8 +405,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.
@@ -419,8 +418,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))
@@ -462,8 +461,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
@@ -521,11 +520,11 @@ def get_available_space(whichdir, reserved_space):
 
 
 if sys.platform == "win32":
-    from ctypes.wintypes import BOOL, HANDLE, DWORD, LPCWSTR, LPVOID, WinError, get_last_error
-
     # <http://msdn.microsoft.com/en-us/library/aa363858%28v=vs.85%29.aspx>
-    CreateFileW = WINFUNCTYPE(HANDLE, LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE) \
-                      (("CreateFileW", windll.kernel32))
+    CreateFileW = WINFUNCTYPE(
+        HANDLE,  LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE,
+        use_last_error=True
+    )(("CreateFileW", windll.kernel32))
 
     GENERIC_WRITE        = 0x40000000
     FILE_SHARE_READ      = 0x00000001
@@ -534,10 +533,16 @@ if sys.platform == "win32":
     INVALID_HANDLE_VALUE = 0xFFFFFFFF
 
     # <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>
-    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/>
     def flush_volume(path):
@@ -552,10 +557,10 @@ if sys.platform == "win32":
                               None
                              )
         if hVolume == INVALID_HANDLE_VALUE:
-            raise WinError()
+            raise WinError(get_last_error())
 
         if FlushFileBuffers(hVolume) == 0:
-            raise WinError()
+            raise WinError(get_last_error())
 
         CloseHandle(hVolume)
 else:
@@ -576,18 +581,17 @@ def reraise(wrapper):
     raise wrapper_exc.__class__, wrapper_exc, tb
 
 if sys.platform == "win32":
-    from ctypes import WINFUNCTYPE, windll, WinError, get_last_error
-    from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPVOID
-
     # <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512%28v=vs.85%29.aspx>
     ReplaceFileW = WINFUNCTYPE(
-        BOOL,
-          LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID,
+        BOOL,  LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID,
         use_last_error=True
-      )(("ReplaceFileW", windll.kernel32))
+    )(("ReplaceFileW", windll.kernel32))
 
     REPLACEFILE_IGNORE_MERGE_ERRORS = 0x00000002
 
+    # <https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382%28v=vs.85%29.aspx>
+    ERROR_FILE_NOT_FOUND = 2
+
     def rename_no_overwrite(source_path, dest_path):
         os.rename(source_path, dest_path)
 
@@ -602,7 +606,13 @@ if sys.platform == "win32":
             # The UnableToUnlinkReplacementError case does not happen on Windows;
             # all errors should be treated as signalling a conflict.
             err = get_last_error()
-            raise ConflictError("WinError: %s" % (WinError(err)))
+            if err != ERROR_FILE_NOT_FOUND:
+                raise ConflictError("WinError: %s" % (WinError(err),))
+
+            try:
+                rename_no_overwrite(replacement_path, replaced_path)
+            except EnvironmentError:
+                reraise(ConflictError)
 else:
     def rename_no_overwrite(source_path, dest_path):
         # link will fail with EEXIST if there is already something at dest_path.