Skip to content

Commit 8af12d9

Browse files
committed
feat(Lib/os): unlink,remove supports dir_fd
1 parent 2b26c30 commit 8af12d9

File tree

4 files changed

+89
-22
lines changed

4 files changed

+89
-22
lines changed
Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
11

22
import ./unlinkImpl
3+
import ./pyCfg
4+
import ./chkarg
5+
import ../private/iph_utils
36

7+
when not InJS:
8+
importConfig [
9+
os
10+
]
11+
else:
12+
template decl(f, val) =
13+
const `HAVE f` = val
14+
decl unlinkat, false
415

5-
proc unlink*[T](p: PathLike[T]) =
6-
sys.audit("os.remove", p, -1)
7-
unlinkImpl p
16+
proc unlink*[T](p: PathLike[T], dir_fd=DEFAULT_DIR_FD) =
17+
sys.audit("os.remove", p, if dir_fd==DEFAULT_DIR_FD: -1 else: dir_fd)
18+
var res: cint
19+
with_Py_SUPPRESS_IPH:
20+
when HAVE_UNLINKAT:
21+
var unlinkat_unavailable = false
22+
if dir_fd != DEFAULT_DIR_FD:
23+
when HAVE_UNLINKAT_RUNTIME:
24+
res = unlinkat(dir_fd.cint, cstring $p, 0)
25+
else:
26+
unlinkat_unavailable = true
27+
else:
28+
unlinkImpl(p)
29+
else:
30+
unlinkImpl(p)
31+
when HAVE_UNLINKAT:
32+
if unlinkat_unavailable:
33+
argument_unavailable_error("dir_fd")
34+
if res != 0:
35+
raiseExcWithPath(p)
836
9-
proc remove*[T](p: PathLike[T]) =
37+
proc remove*[T](p: PathLike[T], dir_fd=DEFAULT_DIR_FD) =
1038
## This function is semantically identical to `unlink`_
11-
unlink(p)
39+
unlink(p, dir_fd)

src/pylib/Lib/os_impl/posix_like/unlinkImpl.nim

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,44 @@ when InJs:
1717
]#
1818
else:
1919
when defined(windows):
20-
template deleteFile(file: untyped): untyped = deleteFileW(file)
21-
template setFileAttributes(file, attrs: untyped): untyped =
22-
setFileAttributesW(file, attrs)
20+
import ../util/mywinlean
21+
type
22+
GET_FILEEX_INFO_LEVELS = enum
23+
GetFileExInfoStandard = cint 0
24+
GetFileExMaxInfoLevel = cint 1
25+
WIN32_FILE_ATTRIBUTE_DATA{.importc, header: "<fileapi.h>",
26+
incompleteStruct.} = object
27+
dwFileAttributes: mywinlean.DWORD
28+
29+
proc GetFileAttributesExW(lpFileName: LPCWSTR, fInfoLevelId: GET_FILEEX_INFO_LEVELS,
30+
lpFileInformation: pointer): BOOL{.importc, header: "<fileapi.h>".}
31+
proc Py_DeleteFileW*(lpFileName: LPCWSTR): BOOL =
32+
var info: WIN32_FILE_ATTRIBUTE_DATA
33+
var find_data: WIN32_FIND_DATAW
34+
var is_directory = false
35+
var is_link = false
36+
37+
if GetFileAttributesExW(lpFileName, GetFileExInfoStandard, addr info):
38+
is_directory = bool info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY
39+
40+
# Get WIN32_FIND_DATA structure for the path to determine if it is a symlink
41+
if is_directory and (info.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0:
42+
let find_data_handle = FindFirstFileW(lpFileName, find_data)
43+
44+
if find_data_handle != INVALID_HANDLE_VALUE:
45+
# IO_REPARSE_TAG_SYMLINK if it is a symlink and
46+
# IO_REPARSE_TAG_MOUNT_POINT if it is a junction point.
47+
is_link = (find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) or
48+
(find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT)
49+
FindClose(find_data_handle)
50+
51+
if is_directory and is_link:
52+
return BOOL removeDirectoryW(lpFileName)
53+
54+
return BOOL deleteFileW(lpFileName)
2355

2456
# from nim-2.1.2/lib/std/private/osfiles.nim `proc tryRemoveFile`
25-
proc unlinkAux[T](p: PathLike[T],
26-
winIgnoreRO: static[bool] = false # Python does not
57+
proc unlinkAux[T](p: PathLike[T]
2758
): bool {.noWeirdTarget.} =
2859
## Removes the file at `p`.
2960
##
@@ -36,14 +67,8 @@ else:
3667
let file = $p
3768
when defined(windows):
3869
let f = newWideCString(file)
39-
if deleteFile(f) != 0: ## returns TRUE
70+
if Py_DeleteFileW(f) != 0: ## returns TRUE
4071
return true
41-
when winIgnoreRO:
42-
let err = getLastError()
43-
if err == ERROR_ACCESS_DENIED and
44-
setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
45-
deleteFile(f) != 0:
46-
return true # success
4772
else:
4873
if unlink(cstring file) == 0'i32: return true # success
4974
proc unlinkImpl*(p: PathLike) =

src/pylib/Lib/os_impl/util/mywinlean.nim

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import std/winlean except DWORD, ULONG
33
export winlean except DWORD, ULONG,
44
OPEN_EXISTING, FILE_SHARE_READ, FILE_SHARE_WRITE,
55
FILE_FLAG_OPEN_REPARSE_POINT, FILE_ATTRIBUTE_DIRECTORY,
6-
BY_HANDLE_FILE_INFORMATION
6+
BY_HANDLE_FILE_INFORMATION, FILE_ATTRIBUTE_REPARSE_POINT
77
import ./get_osfhandle
88
export get_osfhandle
99
type
@@ -60,7 +60,7 @@ typedef union _LARGE_INTEGER {
6060

6161
WCHAR* = uint16
6262

63-
WIN32_FIND_DATAW*{.pure, importc, header: "<minwinbase.h>".} = object
63+
WIN32_FIND_DATAW*{.importc, header: "<minwinbase.h>".} = object
6464
dwFileAttributes*: DWORD
6565
ftCreationTime*: FILETIME
6666
ftLastAccessTime*: FILETIME
@@ -75,7 +75,7 @@ typedef union _LARGE_INTEGER {
7575
dwCreatorType*: DWORD ## Obsolete. Do not use.
7676
wFinderFlags*: WORD ## Obsolete. Do not use.
7777

78-
BY_HANDLE_FILE_INFORMATION*{.pure, importc, header: "<fileapi.h>".} = object
78+
BY_HANDLE_FILE_INFORMATION*{.importc, header: "<fileapi.h>".} = object
7979
dwFileAttributes*: DWORD
8080
ftCreationTime*: FILETIME
8181
ftLastAccessTime*: FILETIME
@@ -87,9 +87,8 @@ typedef union _LARGE_INTEGER {
8787
nFileIndexHigh*: DWORD
8888
nFileIndexLow*: DWORD
8989

90-
FILE_ATTRIBUTE_TAG_INFO*{.pure, importc, header: "<winbase.h>".} = object
90+
FILE_ATTRIBUTE_TAG_INFO*{.importc, header: "<winbase.h>".} = object
9191
FileAttributes*, ReparseTag*: DWORD
92-
9392
const
9493
ERROR_INVALID_HANDLE* = 6
9594
ERROR_NOT_ENOUGH_MEMORY* = 8
@@ -204,3 +203,6 @@ proc CreateFileW*(
204203
205204
proc IsReparseTagNameSurrogate*(tag: DWORD): BOOL {.
206205
importc, header: "<winnt.h>".}
206+
207+
proc FindClose*(hFindFile: Handle): BOOL{.discardable,
208+
importc, header: "<fileapi.h>".}

src/pylib/pyconfig/os.nim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11

22
import ./util
3+
import ./have_x_runtime
34
const
45
DEFAULT_DIR_FD* = from_c_int(AT_FDCWD, "<fcntl.h>", -100)
56
AT_SYMLINK_NOFOLLOW* = from_c_int(AT_SYMLINK_NOFOLLOW, "<fcntl.h>", 0x100)
67

78
HAVE_OPENAT* = true
89
HAVE_FTRUNCATE* = true
10+
11+
AC_CHECK_FUNC(unlinkat)
12+
13+
check_func_runtime unlinkat, 10.10, 8.0
14+
15+
16+
when HAVE_UNLINKAT_RUNTIME and not declared(unlinkat):
17+
proc unlinkat*(dir_fd: cint, path: cstring, flag: cint): cint{.importc,
18+
header: "<unistd.h>".}
19+
let AT_REMOVEDIR*{.importc, header: "<fcntl.h>".}: cint
20+

0 commit comments

Comments
 (0)