mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-30 01:38:55 +00:00
Windows fileutil improvements
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
parent
9402b40da5
commit
9bfcb72400
@ -3,6 +3,8 @@ Futz with files like a pro.
|
||||
"""
|
||||
|
||||
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, \
|
||||
@ -514,3 +516,142 @@ def get_available_space(whichdir, reserved_space):
|
||||
except EnvironmentError:
|
||||
log.msg("OS call to get disk statistics failed")
|
||||
return 0
|
||||
|
||||
|
||||
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))
|
||||
|
||||
GENERIC_WRITE = 0x40000000
|
||||
FILE_SHARE_READ = 0x00000001
|
||||
FILE_SHARE_WRITE = 0x00000002
|
||||
OPEN_EXISTING = 3
|
||||
INVALID_HANDLE_VALUE = 0xFFFFFFFF
|
||||
|
||||
# <http://msdn.microsoft.com/en-us/library/aa364439%28v=vs.85%29.aspx>
|
||||
FlushFileBuffers = WINFUNCTYPE(BOOL, HANDLE)(("FlushFileBuffers", windll.kernel32))
|
||||
|
||||
# <http://msdn.microsoft.com/en-us/library/ms724211%28v=vs.85%29.aspx>
|
||||
CloseHandle = WINFUNCTYPE(BOOL, HANDLE)(("CloseHandle", windll.kernel32))
|
||||
|
||||
# <http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/>
|
||||
def flush_volume(path):
|
||||
drive = os.path.splitdrive(os.path.realpath(path))[0]
|
||||
|
||||
hVolume = CreateFileW(u"\\\\.\\" + drive,
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
None
|
||||
)
|
||||
if hVolume == INVALID_HANDLE_VALUE:
|
||||
raise WinError()
|
||||
|
||||
if FlushFileBuffers(hVolume) == 0:
|
||||
raise WinError()
|
||||
|
||||
CloseHandle(hVolume)
|
||||
else:
|
||||
def flush_volume(path):
|
||||
# use sync()?
|
||||
pass
|
||||
|
||||
|
||||
class ConflictError(Exception):
|
||||
pass
|
||||
|
||||
class UnableToUnlinkReplacementError(Exception):
|
||||
pass
|
||||
|
||||
def reraise(wrapper):
|
||||
_, exc, tb = sys.exc_info()
|
||||
wrapper_exc = wrapper("%s: %s" % (exc.__class__.__name__, exc))
|
||||
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,
|
||||
use_last_error=True
|
||||
)(("ReplaceFileW", windll.kernel32))
|
||||
|
||||
REPLACEFILE_IGNORE_MERGE_ERRORS = 0x00000002
|
||||
|
||||
def rename_no_overwrite(source_path, dest_path):
|
||||
os.rename(source_path, dest_path)
|
||||
|
||||
def replace_file(replaced_path, replacement_path, backup_path):
|
||||
precondition_abspath(replaced_path)
|
||||
precondition_abspath(replacement_path)
|
||||
precondition_abspath(backup_path)
|
||||
|
||||
r = ReplaceFileW(replaced_path, replacement_path, backup_path,
|
||||
REPLACEFILE_IGNORE_MERGE_ERRORS, None, None)
|
||||
if r == 0:
|
||||
# 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)))
|
||||
else:
|
||||
def rename_no_overwrite(source_path, dest_path):
|
||||
# link will fail with EEXIST if there is already something at dest_path.
|
||||
os.link(source_path, dest_path)
|
||||
try:
|
||||
os.unlink(source_path)
|
||||
except EnvironmentError:
|
||||
reraise(UnableToUnlinkReplacementError)
|
||||
|
||||
def replace_file(replaced_path, replacement_path, backup_path):
|
||||
precondition_abspath(replaced_path)
|
||||
precondition_abspath(replacement_path)
|
||||
precondition_abspath(backup_path)
|
||||
|
||||
if not os.path.exists(replacement_path):
|
||||
raise ConflictError("Replacement file not found: %r" % (replacement_path,))
|
||||
|
||||
try:
|
||||
os.rename(replaced_path, backup_path)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
try:
|
||||
rename_no_overwrite(replacement_path, replaced_path)
|
||||
except EnvironmentError:
|
||||
reraise(ConflictError)
|
||||
|
||||
PathInfo = namedtuple('PathInfo', 'isdir isfile islink exists size mtime ctime')
|
||||
|
||||
def get_pathinfo(path_u, now=None):
|
||||
try:
|
||||
statinfo = os.lstat(path_u)
|
||||
mode = statinfo.st_mode
|
||||
return PathInfo(isdir =stat.S_ISDIR(mode),
|
||||
isfile=stat.S_ISREG(mode),
|
||||
islink=stat.S_ISLNK(mode),
|
||||
exists=True,
|
||||
size =statinfo.st_size,
|
||||
mtime =statinfo.st_mtime,
|
||||
ctime =statinfo.st_ctime,
|
||||
)
|
||||
except OSError as e:
|
||||
if e.errno == ENOENT:
|
||||
if now is None:
|
||||
now = time.time()
|
||||
return PathInfo(isdir =False,
|
||||
isfile=False,
|
||||
islink=False,
|
||||
exists=False,
|
||||
size =None,
|
||||
mtime =now,
|
||||
ctime =now,
|
||||
)
|
||||
raise
|
||||
|
Loading…
Reference in New Issue
Block a user