Skip to content

Commit a9a6847

Browse files
author
Roman Inflianskas
committed
Address comments in #16709 (second iteration)
Skip symlinks on Windows.
1 parent d3bd6dc commit a9a6847

File tree

4 files changed

+47
-72
lines changed

4 files changed

+47
-72
lines changed

changelog.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,14 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.
114114

115115

116116
- Added optional `options` argument to `copyFile`, `copyFileToDir`, and
117-
`copyFileWithPermissions`. By default, symlinks are followed (copy files
118-
symlinks point to).
119-
- `copyDir` and `copyDirWithPermissions` copy symlinks as symlinks (instead of
120-
skipping them as it was before).
121-
- `moveFile` and `moveDir` move symlinks as symlinks (instead of skipping them
122-
sometimes as it was before).
117+
`copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are
118+
followed (copy files symlinks point to); on Windows, `options` argument is
119+
ignored and symlinks are skipped.
120+
- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as
121+
symlinks (instead of skipping them as it was before); on Windows symlinks are
122+
skipped.
123+
- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks
124+
(instead of skipping them sometimes as it was before).
123125
- Added optional `followSymlinks` argument to `setFilePermissions`.
124126

125127
## Language changes

lib/pure/os.nim

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,8 +1725,9 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl,
17251725
noWeirdTarget.} =
17261726
## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
17271727
##
1728-
## `options` specify the way file is copied; by default, if `source` is a
1729-
## symlink, copies the file symlink points to.
1728+
## On non-Windows OSes, `options` specify the way file is copied; by default,
1729+
## if `source` is a symlink, copies the file symlink points to. `options` is
1730+
## ignored on Windows: symlinks are skipped.
17301731
##
17311732
## If this fails, `OSError` is raised.
17321733
##
@@ -1759,30 +1760,17 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl,
17591760
doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " &
17601761
"one cfSymlink* in options"
17611762
let isSymlink = source.symlinkExists
1762-
if isSymlink and cfSymlinkIgnore in options:
1763+
if isSymlink and (cfSymlinkIgnore in options or defined(windows)):
17631764
return
17641765
when defined(Windows):
1765-
proc handleOSError =
1766-
const ERROR_PRIVILEGE_NOT_HELD = 1314
1767-
let errCode = osLastError()
1768-
let context = $(source, dest, options)
1769-
if isSymlink and errCode.int32 == ERROR_PRIVILEGE_NOT_HELD:
1770-
stderr.write("Failed copy the symlink: error " & $errCode & ": " &
1771-
osErrorMsg(errCode) & "Additional info: " & context &
1772-
"\n")
1773-
else:
1774-
raiseOSError(errCode, context)
1775-
1776-
let dwCopyFlags = if cfSymlinkAsIs in options: COPY_FILE_COPY_SYMLINK else: 0'i32
1777-
var pbCancel = 0'i32
17781766
when useWinUnicode:
17791767
let s = newWideCString(source)
17801768
let d = newWideCString(dest)
1781-
if copyFileExW(s, d, nil, nil, addr pbCancel, dwCopyFlags) == 0'i32:
1782-
handleOSError()
1769+
if copyFileW(s, d, 0'i32) == 0'i32:
1770+
raiseOSError(osLastError(), $(source, dest))
17831771
else:
1784-
if copyFileExA(source, dest, nil, nil, addr pbCancel, dwCopyFlags) == 0'i32:
1785-
handleOSError()
1772+
if copyFileA(source, dest, 0'i32) == 0'i32:
1773+
raiseOSError(osLastError(), $(source, dest))
17861774
elif hasCCopyfile:
17871775
let state = copyfile_state_alloc()
17881776
# xxx `COPYFILE_STAT` could be used for one-shot `copyFileWithPermissions`.
@@ -1824,8 +1812,9 @@ proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow})
18241812
{.noWeirdTarget, since: (1,3,7).} =
18251813
## Copies a file `source` into directory `dir`, which must exist.
18261814
##
1827-
## `options` specify the way file is copied; by default, if `source` is a
1828-
## symlink, copies the file symlink points to.
1815+
## On non-Windows OSes, `options` specify the way file is copied; by default,
1816+
## if `source` is a symlink, copies the file symlink points to. `options` is
1817+
## ignored on Windows: symlinks are skipped.
18291818
##
18301819
## See also:
18311820
## * `CopyFlag enum <#CopyFlag>`_
@@ -2460,7 +2449,8 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
24602449
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
24612450
## Copies a directory from `source` to `dest`.
24622451
##
2463-
## Symlinks are copied as symlinks.
2452+
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
2453+
## are skipped.
24642454
##
24652455
## If this fails, `OSError` is raised.
24662456
##
@@ -2491,8 +2481,8 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
24912481
proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
24922482
## Moves a directory from `source` to `dest`.
24932483
##
2494-
## Symlinks are not followed: if `source` contains symlinks, they itself are
2495-
## moved, not theirs target.
2484+
## Symlinks are not followed: if `source` contains symlinks, they themself are
2485+
## moved, not their target.
24962486
##
24972487
## If this fails, `OSError` is raised.
24982488
##
@@ -2536,8 +2526,9 @@ proc copyFileWithPermissions*(source, dest: string,
25362526
options = {cfSymlinkFollow}) {.noWeirdTarget.} =
25372527
## Copies a file from `source` to `dest` preserving file permissions.
25382528
##
2539-
## `options` specify the way file is copied; by default, if `source` is a
2540-
## symlink, copies the file symlink points to.
2529+
## On non-Windows OSes, `options` specify the way file is copied; by default,
2530+
## if `source` is a symlink, copies the file symlink points to. `options` is
2531+
## ignored on Windows: symlinks are skipped.
25412532
##
25422533
## This is a wrapper proc around `copyFile <#copyFile,string,string>`_,
25432534
## `getFilePermissions <#getFilePermissions,string>`_ and
@@ -2576,7 +2567,8 @@ proc copyDirWithPermissions*(source, dest: string,
25762567
benign, noWeirdTarget.} =
25772568
## Copies a directory from `source` to `dest` preserving file permissions.
25782569
##
2579-
## Symlinks are copied as symlinks.
2570+
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
2571+
## are skipped.
25802572
##
25812573
## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir
25822574
## <#copyDir,string,string>`_ and `copyFileWithPermissions

lib/windows/winlean.nim

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,6 @@ else:
350350
proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32",
351351
importc: "FindClose".}
352352

353-
type LPPROGRESS_ROUTINE* = proc(TotalFileSize, TotalBytesTransferred,
354-
StreamSize, StreamBytesTransferred: int64, dwStreamNumber,
355-
dwCallbackReason: DWORD, hSourceFile, hDestinationFile: Handle,
356-
lpData: pointer): void {.stdcall.}
357-
358-
const COPY_FILE_COPY_SYMLINK* = 0x00000800'i32
359-
360353
when useWinUnicode:
361354
proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
362355
lpBuffer: WideCString,
@@ -373,10 +366,6 @@ when useWinUnicode:
373366
proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
374367
bFailIfExists: WINBOOL): WINBOOL {.
375368
importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.}
376-
proc copyFileExW*(lpExistingFileName, lpNewFileName: WideCString,
377-
lpProgressRoutine: LPPROGRESS_ROUTINE, lpData: pointer,
378-
pbCancel: ptr WINBOOL, dwCopyFlags: DWORD): WINBOOL {.
379-
importc: "CopyFileExW", stdcall, dynlib: "kernel32", sideEffect.}
380369

381370
proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
382371
importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.}
@@ -407,10 +396,6 @@ else:
407396
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
408397
bFailIfExists: cint): cint {.
409398
importc: "CopyFileA", stdcall, dynlib: "kernel32", sideEffect.}
410-
proc copyFileExA*(lpExistingFileName, lpNewFileName: cstring,
411-
lpProgressRoutine: LPPROGRESS_ROUTINE, lpData: pointer,
412-
pbCancel: ptr WINBOOL, dwCopyFlags: DWORD): WINBOOL {.
413-
importc: "CopyFileExA", stdcall, dynlib: "kernel32", sideEffect.}
414399

415400
proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
416401
importc: "MoveFileA", stdcall, dynlib: "kernel32", sideEffect.}

tests/stdlib/tos.nim

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,7 @@ block fileOperations:
158158
# Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
159159
# `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
160160
block:
161-
when not defined(windows):
162-
const checkExpandSymlink = true
163-
else:
164-
const checkExpandSymlink = false
165-
161+
const checkSymlink = not defined(windows)
166162
const dname = buildDir/"D20210116T140629"
167163
const subDir = dname/"sub"
168164
const subDir2 = dname/"sub2"
@@ -184,14 +180,14 @@ block fileOperations:
184180
copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkIgnore})
185181
doAssert not fileExists(brokenSymlinkCopy)
186182
copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkAsIs})
187-
when checkExpandSymlink:
183+
when checkSymlink:
188184
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
185+
removeFile(brokenSymlinkCopy)
189186
else:
190-
doAssert symlinkExists(brokenSymlinkCopy)
191-
removeFile(brokenSymlinkCopy)
187+
doAssert not fileExists(brokenSymlinkCopy)
192188
doAssertRaises(AssertionDefect):
193189
copyFile(brokenSymlink, brokenSymlinkCopy,
194-
{cfSymlinkAsIs, cfSymlinkFollow})
190+
{cfSymlinkAsIs, cfSymlinkFollow})
195191

196192
# Test copyFileWithPermissions
197193
doAssertRaises(OSError):
@@ -204,11 +200,11 @@ block fileOperations:
204200
doAssert not fileExists(brokenSymlinkCopy)
205201
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
206202
options = {cfSymlinkAsIs})
207-
when checkExpandSymlink:
203+
when checkSymlink:
208204
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
205+
removeFile(brokenSymlinkCopy)
209206
else:
210-
doAssert symlinkExists(brokenSymlinkCopy)
211-
removeFile(brokenSymlinkCopy)
207+
doAssert not fileExists(brokenSymlinkCopy)
212208
doAssertRaises(AssertionDefect):
213209
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
214210
options = {cfSymlinkAsIs, cfSymlinkFollow})
@@ -221,44 +217,44 @@ block fileOperations:
221217
copyFileToDir(brokenSymlink, subDir, {cfSymlinkIgnore})
222218
doAssert not fileExists(brokenSymlinkInSubDir)
223219
copyFileToDir(brokenSymlink, subDir, {cfSymlinkAsIs})
224-
when checkExpandSymlink:
220+
when checkSymlink:
225221
doAssert expandSymlink(brokenSymlinkInSubDir) == brokenSymlinkSrc
222+
removeFile(brokenSymlinkInSubDir)
226223
else:
227-
doAssert symlinkExists(brokenSymlinkInSubDir)
228-
removeFile(brokenSymlinkInSubDir)
224+
doAssert not fileExists(brokenSymlinkInSubDir)
229225

230226
createSymlink(brokenSymlinkSrc, brokenSymlinkInSubDir)
231227

232228
# Test copyDir
233229
copyDir(subDir, subDir2)
234-
when checkExpandSymlink:
230+
when checkSymlink:
235231
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
236232
else:
237-
doAssert symlinkExists(brokenSymlinkInSubDir2)
233+
doAssert not fileExists(brokenSymlinkInSubDir2)
238234
removeDir(subDir2)
239235

240236
# Test copyDirWithPermissions
241237
copyDirWithPermissions(subDir, subDir2)
242-
when checkExpandSymlink:
238+
when checkSymlink:
243239
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
244240
else:
245-
doAssert symlinkExists(brokenSymlinkInSubDir2)
241+
doAssert not fileExists(brokenSymlinkInSubDir2)
246242
removeDir(subDir2)
247243

248244
# Test moveFile
249245
moveFile(brokenSymlink, brokenSymlinkCopy)
250-
when checkExpandSymlink:
246+
when checkSymlink:
251247
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
252248
else:
253-
doAssert symlinkExists(brokenSymlinkCopy)
249+
doAssert not fileExists(brokenSymlinkCopy)
254250
removeFile(brokenSymlinkCopy)
255251

256252
# Test moveDir
257253
moveDir(subDir, subDir2)
258-
when checkExpandSymlink:
254+
when checkSymlink:
259255
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
260256
else:
261-
doAssert symlinkExists(brokenSymlinkInSubDir2)
257+
doAssert not fileExists(brokenSymlinkInSubDir2)
262258

263259
removeDir(dname)
264260

0 commit comments

Comments
 (0)