Fix posix version of moveFile between different filesystems (#5580)
This commit is contained in:
parent
568c954062
commit
434a7c8426
3 changed files with 48 additions and 21 deletions
|
|
@ -617,20 +617,6 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
flushFile(d)
|
flushFile(d)
|
||||||
close(d)
|
close(d)
|
||||||
|
|
||||||
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadIOEffect, WriteIOEffect].} =
|
|
||||||
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
|
|
||||||
when defined(Windows):
|
|
||||||
when useWinUnicode:
|
|
||||||
let s = newWideCString(source)
|
|
||||||
let d = newWideCString(dest)
|
|
||||||
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if c_rename(source, dest) != 0'i32:
|
|
||||||
raiseOSError(osLastError(), $strerror(errno))
|
|
||||||
|
|
||||||
when not declared(ENOENT) and not defined(Windows):
|
when not declared(ENOENT) and not defined(Windows):
|
||||||
when NoFakeVars:
|
when NoFakeVars:
|
||||||
const ENOENT = cint(2) # 2 on most systems including Solaris
|
const ENOENT = cint(2) # 2 on most systems including Solaris
|
||||||
|
|
@ -647,25 +633,63 @@ when defined(Windows):
|
||||||
template setFileAttributes(file, attrs: untyped): untyped =
|
template setFileAttributes(file, attrs: untyped): untyped =
|
||||||
setFileAttributesA(file, attrs)
|
setFileAttributesA(file, attrs)
|
||||||
|
|
||||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
## Removes the `file`. If this fails, `OSError` is raised. This does not fail
|
## Removes the `file`. If this fails, returns `false`. This does not fail
|
||||||
## if the file never existed in the first place.
|
## if the file never existed in the first place.
|
||||||
## On Windows, ignores the read-only attribute.
|
## On Windows, ignores the read-only attribute.
|
||||||
|
result = true
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
let f = newWideCString(file)
|
let f = newWideCString(file)
|
||||||
else:
|
else:
|
||||||
let f = file
|
let f = file
|
||||||
if deleteFile(f) == 0:
|
if deleteFile(f) == 0:
|
||||||
if getLastError() == ERROR_ACCESS_DENIED:
|
result = false
|
||||||
if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
|
let err = getLastError()
|
||||||
raiseOSError(osLastError())
|
if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
|
||||||
if deleteFile(f) == 0:
|
result = true
|
||||||
raiseOSError(osLastError())
|
elif err == ERROR_ACCESS_DENIED and
|
||||||
|
setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
|
||||||
|
deleteFile(f) != 0:
|
||||||
|
result = true
|
||||||
else:
|
else:
|
||||||
if c_remove(file) != 0'i32 and errno != ENOENT:
|
if c_remove(file) != 0'i32 and errno != ENOENT:
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
|
## Removes the `file`. If this fails, `OSError` is raised. This does not fail
|
||||||
|
## if the file never existed in the first place.
|
||||||
|
## On Windows, ignores the read-only attribute.
|
||||||
|
if not tryRemoveFile(file):
|
||||||
|
when defined(Windows):
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
raiseOSError(osLastError(), $strerror(errno))
|
raiseOSError(osLastError(), $strerror(errno))
|
||||||
|
|
||||||
|
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
|
tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
|
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
|
||||||
|
when defined(Windows):
|
||||||
|
when useWinUnicode:
|
||||||
|
let s = newWideCString(source)
|
||||||
|
let d = newWideCString(dest)
|
||||||
|
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if c_rename(source, dest) != 0'i32:
|
||||||
|
let err = osLastError()
|
||||||
|
if err == EXDEV.OSErrorCode:
|
||||||
|
# Fallback to copy & del
|
||||||
|
copyFile(source, dest)
|
||||||
|
try:
|
||||||
|
removeFile(source)
|
||||||
|
except:
|
||||||
|
discard tryRemoveFile(dest)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
raiseOSError(err, $strerror(errno))
|
||||||
|
|
||||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
tags: [ExecIOEffect].} =
|
tags: [ExecIOEffect].} =
|
||||||
## Executes a `shell command`:idx:.
|
## Executes a `shell command`:idx:.
|
||||||
|
|
|
||||||
|
|
@ -667,6 +667,8 @@ const
|
||||||
|
|
||||||
# Error Constants
|
# Error Constants
|
||||||
const
|
const
|
||||||
|
ERROR_FILE_NOT_FOUND* = 2
|
||||||
|
ERROR_PATH_NOT_FOUND* = 3
|
||||||
ERROR_ACCESS_DENIED* = 5
|
ERROR_ACCESS_DENIED* = 5
|
||||||
ERROR_HANDLE_EOF* = 38
|
ERROR_HANDLE_EOF* = 38
|
||||||
ERROR_BAD_ARGUMENTS* = 165
|
ERROR_BAD_ARGUMENTS* = 165
|
||||||
|
|
|
||||||
|
|
@ -35,5 +35,6 @@ except:
|
||||||
echo "Second readLine raised an exception"
|
echo "Second readLine raised an exception"
|
||||||
|
|
||||||
echo line
|
echo line
|
||||||
|
f.close()
|
||||||
|
|
||||||
removeFile(fn)
|
removeFile(fn)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue