Refactor creation of hardlinks on windows

After creating the hardlink, open and close the file, to ensure that
the directory entry for the file contains the correct file size, see
http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
This commit is contained in:
Kovid Goyal 2013-07-04 11:11:22 +05:30
parent 852bd49453
commit ef4efd5768

View File

@ -70,9 +70,11 @@ def shorten_components_to(length, components, more_to_take=0):
else: else:
if x is components[-1]: if x is components[-1]:
b, e = os.path.splitext(x) b, e = os.path.splitext(x)
if e == '.': e = '' if e == '.':
e = ''
r = shorten_component(b, delta)+e r = shorten_component(b, delta)+e
if r.startswith('.'): r = x[0]+r if r.startswith('.'):
r = x[0]+r
else: else:
r = shorten_component(x, delta) r = shorten_component(x, delta)
r = r.strip() r = r.strip()
@ -115,7 +117,7 @@ def is_case_sensitive(path):
os.remove(f1) os.remove(f1)
return is_case_sensitive return is_case_sensitive
def case_preserving_open_file(path, mode='wb', mkdir_mode=0777): def case_preserving_open_file(path, mode='wb', mkdir_mode=0o777):
''' '''
Open the file pointed to by path with the specified mode. If any Open the file pointed to by path with the specified mode. If any
directories in path do not exist, they are created. Returns the directories in path do not exist, they are created. Returns the
@ -211,7 +213,8 @@ def samefile_windows(src, dst):
handles = [] handles = []
def get_fileid(x): def get_fileid(x):
if isbytestring(x): x = x.decode(filesystem_encoding) if isbytestring(x):
x = x.decode(filesystem_encoding)
try: try:
h = win32file.CreateFile(x, 0, 0, None, win32file.OPEN_EXISTING, h = win32file.CreateFile(x, 0, 0, None, win32file.OPEN_EXISTING,
win32file.FILE_FLAG_BACKUP_SEMANTICS, 0) win32file.FILE_FLAG_BACKUP_SEMANTICS, 0)
@ -254,6 +257,24 @@ def samefile(src, dst):
os.path.normcase(os.path.abspath(dst))) os.path.normcase(os.path.abspath(dst)))
return samestring return samestring
def windows_hardlink(src, dest):
import win32file, pywintypes
msg = u'Creating hardlink from %s to %s failed: %%s' % (src, dest)
try:
win32file.CreateHardLink(dest, src)
except pywintypes.error as e:
raise Exception(msg % e)
# We open and close dest, to ensure its directory entry is updated
# see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/26/10251026.aspx
h = win32file.CreateFile(
dest, 0, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE | win32file.FILE_SHARE_DELETE,
None, win32file.OPEN_EXISTING, 0, None)
sz = win32file.GetFileSize(h)
win32file.CloseHandle(h)
if sz != os.path.getsize(src):
raise Exception(msg % ('hardlink size: %d not the same as source size' % sz))
class WindowsAtomicFolderMove(object): class WindowsAtomicFolderMove(object):
''' '''
@ -270,14 +291,16 @@ class WindowsAtomicFolderMove(object):
import win32file, winerror import win32file, winerror
from pywintypes import error from pywintypes import error
if isbytestring(path): path = path.decode(filesystem_encoding) if isbytestring(path):
path = path.decode(filesystem_encoding)
if not os.path.exists(path): if not os.path.exists(path):
return return
for x in os.listdir(path): for x in os.listdir(path):
f = os.path.normcase(os.path.abspath(os.path.join(path, x))) f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
if not os.path.isfile(f): continue if not os.path.isfile(f):
continue
try: try:
# Ensure the file is not read-only # Ensure the file is not read-only
win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL) win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL)
@ -315,9 +338,7 @@ class WindowsAtomicFolderMove(object):
else: else:
raise ValueError(u'The file %r does not exist'%path) raise ValueError(u'The file %r does not exist'%path)
try: try:
win32file.CreateHardLink(dest, path) windows_hardlink(path, dest)
if os.path.getsize(dest) != os.path.getsize(path):
raise Exception('This apparently can happen on network shares. Sigh.')
return return
except: except:
pass pass
@ -355,10 +376,8 @@ class WindowsAtomicFolderMove(object):
def hardlink_file(src, dest): def hardlink_file(src, dest):
if iswindows: if iswindows:
import win32file windows_hardlink(src, dest)
win32file.CreateHardLink(dest, src)
if os.path.getsize(dest) != os.path.getsize(src):
raise Exception('This apparently can happen on network shares. Sigh.')
return return
os.link(src, dest) os.link(src, dest)