New implementation for os.sameFile on Windows
Hard-links on Windows are now treated just as they are on POSIX. The new implementation is faster than the previous, but still it's quite slower than fstat (use with caution).
This commit is contained in:
parent
446b042188
commit
0e609d2101
3 changed files with 66 additions and 25 deletions
|
|
@ -567,26 +567,45 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||||
result = path[0] == '/'
|
result = path[0] == '/'
|
||||||
|
|
||||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
|
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns True if both pathname arguments refer to the same file or
|
## Returns True if both pathname arguments refer to the same physical
|
||||||
## directory (as indicated by device number and i-node number).
|
## file or directory. Raises an exception if any of the files does not
|
||||||
## Raises an exception if an stat() call on either pathname fails.
|
## exist or information about it can not be obtained.
|
||||||
|
##
|
||||||
|
## This proc will return true if given two alternative hard-linked or
|
||||||
|
## sym-linked paths to the same file or directory.
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
var
|
var success = true
|
||||||
a, b: TWin32FindData
|
|
||||||
var resA = findfirstFileA(path1, a)
|
template OpenHandle(path: expr): expr =
|
||||||
var resB = findfirstFileA(path2, b)
|
CreateFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
||||||
if resA != -1 and resB != -1:
|
FILE_SHARE_WRITE, nil, OPEN_EXISTING,
|
||||||
result = $a.cFileName == $b.cFileName
|
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
|
||||||
else:
|
|
||||||
# work around some ``findfirstFileA`` bugs
|
var f1 = OpenHandle(path1)
|
||||||
result = cmpPaths(path1, path2) == 0
|
var f2 = OpenHandle(path2)
|
||||||
if resA != -1: findclose(resA)
|
|
||||||
if resB != -1: findclose(resB)
|
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
||||||
|
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
|
||||||
|
|
||||||
|
if GetFileInformationByHandle(f1, addr(fi1)) != 0 and
|
||||||
|
GetFileInformationByHandle(f2, addr(fi2)) != 0:
|
||||||
|
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
|
||||||
|
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
|
||||||
|
fi1.nFileIndexLow == fi2.nFileIndexLow
|
||||||
|
else: success = false
|
||||||
|
else: success = false
|
||||||
|
|
||||||
|
discard CloseHandle(f1)
|
||||||
|
discard CloseHandle(f2)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
OSError()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
var
|
var
|
||||||
a, b: TStat
|
a, b: TStat
|
||||||
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
|
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
|
||||||
result = cmpPaths(path1, path2) == 0 # be consistent with Windows
|
OSError()
|
||||||
else:
|
else:
|
||||||
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ when not defined(ECMAScript):
|
||||||
posix_gettimeofday(a)
|
posix_gettimeofday(a)
|
||||||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
|
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
|
||||||
elif defined(windows):
|
elif defined(windows):
|
||||||
var f: winlean.Filetime
|
var f: winlean.TFiletime
|
||||||
GetSystemTimeAsFileTime(f)
|
GetSystemTimeAsFileTime(f)
|
||||||
var i64 = rdFileTime(f) - epochDiff
|
var i64 = rdFileTime(f) - epochDiff
|
||||||
var secs = i64 div rateDiff
|
var secs = i64 div rateDiff
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,22 @@ type
|
||||||
dwProcessId*: int32
|
dwProcessId*: int32
|
||||||
dwThreadId*: int32
|
dwThreadId*: int32
|
||||||
|
|
||||||
|
TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
|
||||||
|
dwLowDateTime*: DWORD
|
||||||
|
dwHighDateTime*: DWORD
|
||||||
|
|
||||||
|
TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
|
||||||
|
dwFileAttributes*: DWORD
|
||||||
|
ftCreationTime*: TFILETIME
|
||||||
|
ftLastAccessTime*: TFILETIME
|
||||||
|
ftLastWriteTime*: TFILETIME
|
||||||
|
dwVolumeSerialNumber*: DWORD
|
||||||
|
nFileSizeHigh*: DWORD
|
||||||
|
nFileSizeLow*: DWORD
|
||||||
|
nNumberOfLinks*: DWORD
|
||||||
|
nFileIndexHigh*: DWORD
|
||||||
|
nFileIndexLow*: DWORD
|
||||||
|
|
||||||
const
|
const
|
||||||
STARTF_USESHOWWINDOW* = 1'i32
|
STARTF_USESHOWWINDOW* = 1'i32
|
||||||
STARTF_USESTDHANDLES* = 256'i32
|
STARTF_USESTDHANDLES* = 256'i32
|
||||||
|
|
@ -149,14 +165,11 @@ const
|
||||||
|
|
||||||
MAX_PATH* = 260
|
MAX_PATH* = 260
|
||||||
type
|
type
|
||||||
FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
|
|
||||||
dwLowDateTime*: int32
|
|
||||||
dwHighDateTime*: int32
|
|
||||||
TWIN32_FIND_DATA* {.pure.} = object
|
TWIN32_FIND_DATA* {.pure.} = object
|
||||||
dwFileAttributes*: int32
|
dwFileAttributes*: int32
|
||||||
ftCreationTime*: FILETIME
|
ftCreationTime*: TFILETIME
|
||||||
ftLastAccessTime*: FILETIME
|
ftLastAccessTime*: TFILETIME
|
||||||
ftLastWriteTime*: FILETIME
|
ftLastWriteTime*: TFILETIME
|
||||||
nFileSizeHigh*: int32
|
nFileSizeHigh*: int32
|
||||||
nFileSizeLow*: int32
|
nFileSizeLow*: int32
|
||||||
dwReserved0: int32
|
dwReserved0: int32
|
||||||
|
|
@ -192,13 +205,13 @@ proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
|
||||||
|
|
||||||
proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
|
proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
|
||||||
|
|
||||||
proc rdFileTime*(f: FILETIME): int64 =
|
proc rdFileTime*(f: TFILETIME): int64 =
|
||||||
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
|
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
|
||||||
|
|
||||||
proc rdFileSize*(f: TWin32FindData): int64 =
|
proc rdFileSize*(f: TWin32FindData): int64 =
|
||||||
result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
|
result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
|
||||||
|
|
||||||
proc GetSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FileTime) {.
|
proc GetSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {.
|
||||||
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
|
importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
|
||||||
|
|
||||||
proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
|
proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
|
||||||
|
|
@ -209,12 +222,16 @@ proc ShellExecute*(HWND: THandle, lpOperation, lpFile,
|
||||||
nShowCmd: int32): THandle{.
|
nShowCmd: int32): THandle{.
|
||||||
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
|
stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
|
||||||
|
|
||||||
|
proc GetFileInformationByHandle*(hFile: THandle,
|
||||||
|
lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
|
||||||
|
|
||||||
const
|
const
|
||||||
WSADESCRIPTION_LEN* = 256
|
WSADESCRIPTION_LEN* = 256
|
||||||
WSASYS_STATUS_LEN* = 128
|
WSASYS_STATUS_LEN* = 128
|
||||||
FD_SETSIZE* = 64
|
FD_SETSIZE* = 64
|
||||||
MSG_PEEK* = 2
|
MSG_PEEK* = 2
|
||||||
|
|
||||||
INADDR_ANY* = 0
|
INADDR_ANY* = 0
|
||||||
INADDR_LOOPBACK* = 0x7F000001
|
INADDR_LOOPBACK* = 0x7F000001
|
||||||
INADDR_BROADCAST* = -1
|
INADDR_BROADCAST* = -1
|
||||||
|
|
@ -410,6 +427,9 @@ const
|
||||||
GENERIC_READ* = 0x80000000'i32
|
GENERIC_READ* = 0x80000000'i32
|
||||||
GENERIC_ALL* = 0x10000000'i32
|
GENERIC_ALL* = 0x10000000'i32
|
||||||
FILE_SHARE_READ* = 1'i32
|
FILE_SHARE_READ* = 1'i32
|
||||||
|
FILE_SHARE_DELETE* = 4'i32
|
||||||
|
FILE_SHARE_WRITE* = 2'i32
|
||||||
|
|
||||||
CREATE_ALWAYS* = 2'i32
|
CREATE_ALWAYS* = 2'i32
|
||||||
OPEN_EXISTING* = 3'i32
|
OPEN_EXISTING* = 3'i32
|
||||||
FILE_BEGIN* = 0'i32
|
FILE_BEGIN* = 0'i32
|
||||||
|
|
@ -421,6 +441,8 @@ const
|
||||||
FILE_MAP_WRITE* = 2'i32
|
FILE_MAP_WRITE* = 2'i32
|
||||||
INVALID_FILE_SIZE* = -1'i32
|
INVALID_FILE_SIZE* = -1'i32
|
||||||
|
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
|
||||||
|
|
||||||
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
|
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
|
||||||
lpSecurityAttributes: pointer,
|
lpSecurityAttributes: pointer,
|
||||||
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
|
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue