stdlib/os: handle symlinks in copy/move functions (#16709)
* stdlib/os: handle symlinks in copy/move functions - Added optional `options` argument to `copyFile`, `copyFileToDir`, and `copyFileWithPermissions`. By default, symlinks are followed (copy files symlinks point to). - `copyDir` and `copyDirWithPermissions` copy symlinks as symlinks (instead of skipping them as it was before). - `moveFile` and `moveDir` move symlinks as symlinks (instead of skipping them sometimes as it was before). - Added optional `followSymlinks` argument to `setFilePermissions`. See also: https://github.com/nim-lang/RFCs/issues/319 Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Address comments in #16709 Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Address comments in #16709 (second iteration) Skip symlinks on Windows. Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
This commit is contained in:
parent
bb85bc7ebc
commit
e9b360c5df
5 changed files with 321 additions and 106 deletions
|
|
@ -26,6 +26,7 @@ Raises
|
|||
# test os path creation, iteration, and deletion
|
||||
|
||||
import os, strutils, pathnorm
|
||||
from stdtest/specialpaths import buildDir
|
||||
|
||||
block fileOperations:
|
||||
let files = @["these.txt", "are.x", "testing.r", "files.q"]
|
||||
|
|
@ -154,6 +155,112 @@ block fileOperations:
|
|||
doAssert fileExists("../dest/a/file.txt")
|
||||
removeDir("../dest")
|
||||
|
||||
# Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
|
||||
# `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
|
||||
block:
|
||||
const symlinksAreHandled = not defined(windows)
|
||||
const dname = buildDir/"D20210116T140629"
|
||||
const subDir = dname/"sub"
|
||||
const subDir2 = dname/"sub2"
|
||||
const brokenSymlinkName = "D20210101T191320_BROKEN_SYMLINK"
|
||||
const brokenSymlink = dname/brokenSymlinkName
|
||||
const brokenSymlinkSrc = "D20210101T191320_nonexistant"
|
||||
const brokenSymlinkCopy = brokenSymlink & "_COPY"
|
||||
const brokenSymlinkInSubDir = subDir/brokenSymlinkName
|
||||
const brokenSymlinkInSubDir2 = subDir2/brokenSymlinkName
|
||||
|
||||
createDir(subDir)
|
||||
createSymlink(brokenSymlinkSrc, brokenSymlink)
|
||||
|
||||
# Test copyFile
|
||||
when symlinksAreHandled:
|
||||
doAssertRaises(OSError):
|
||||
copyFile(brokenSymlink, brokenSymlinkCopy)
|
||||
doAssertRaises(OSError):
|
||||
copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkFollow})
|
||||
copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkIgnore})
|
||||
doAssert not fileExists(brokenSymlinkCopy)
|
||||
copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkAsIs})
|
||||
when symlinksAreHandled:
|
||||
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
|
||||
removeFile(brokenSymlinkCopy)
|
||||
else:
|
||||
doAssert not fileExists(brokenSymlinkCopy)
|
||||
doAssertRaises(AssertionDefect):
|
||||
copyFile(brokenSymlink, brokenSymlinkCopy,
|
||||
{cfSymlinkAsIs, cfSymlinkFollow})
|
||||
|
||||
# Test copyFileWithPermissions
|
||||
when symlinksAreHandled:
|
||||
doAssertRaises(OSError):
|
||||
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy)
|
||||
doAssertRaises(OSError):
|
||||
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
|
||||
options = {cfSymlinkFollow})
|
||||
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
|
||||
options = {cfSymlinkIgnore})
|
||||
doAssert not fileExists(brokenSymlinkCopy)
|
||||
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
|
||||
options = {cfSymlinkAsIs})
|
||||
when symlinksAreHandled:
|
||||
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
|
||||
removeFile(brokenSymlinkCopy)
|
||||
else:
|
||||
doAssert not fileExists(brokenSymlinkCopy)
|
||||
doAssertRaises(AssertionDefect):
|
||||
copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
|
||||
options = {cfSymlinkAsIs, cfSymlinkFollow})
|
||||
|
||||
# Test copyFileToDir
|
||||
when symlinksAreHandled:
|
||||
doAssertRaises(OSError):
|
||||
copyFileToDir(brokenSymlink, subDir)
|
||||
doAssertRaises(OSError):
|
||||
copyFileToDir(brokenSymlink, subDir, {cfSymlinkFollow})
|
||||
copyFileToDir(brokenSymlink, subDir, {cfSymlinkIgnore})
|
||||
doAssert not fileExists(brokenSymlinkInSubDir)
|
||||
copyFileToDir(brokenSymlink, subDir, {cfSymlinkAsIs})
|
||||
when symlinksAreHandled:
|
||||
doAssert expandSymlink(brokenSymlinkInSubDir) == brokenSymlinkSrc
|
||||
removeFile(brokenSymlinkInSubDir)
|
||||
else:
|
||||
doAssert not fileExists(brokenSymlinkInSubDir)
|
||||
|
||||
createSymlink(brokenSymlinkSrc, brokenSymlinkInSubDir)
|
||||
|
||||
# Test copyDir
|
||||
copyDir(subDir, subDir2)
|
||||
when symlinksAreHandled:
|
||||
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
|
||||
else:
|
||||
doAssert not fileExists(brokenSymlinkInSubDir2)
|
||||
removeDir(subDir2)
|
||||
|
||||
# Test copyDirWithPermissions
|
||||
copyDirWithPermissions(subDir, subDir2)
|
||||
when symlinksAreHandled:
|
||||
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
|
||||
else:
|
||||
doAssert not fileExists(brokenSymlinkInSubDir2)
|
||||
removeDir(subDir2)
|
||||
|
||||
# Test moveFile
|
||||
moveFile(brokenSymlink, brokenSymlinkCopy)
|
||||
when not defined(windows):
|
||||
doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
|
||||
else:
|
||||
doAssert symlinkExists(brokenSymlinkCopy)
|
||||
removeFile(brokenSymlinkCopy)
|
||||
|
||||
# Test moveDir
|
||||
moveDir(subDir, subDir2)
|
||||
when not defined(windows):
|
||||
doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
|
||||
else:
|
||||
doAssert symlinkExists(brokenSymlinkInSubDir2)
|
||||
|
||||
removeDir(dname)
|
||||
|
||||
import times
|
||||
block modificationTime:
|
||||
# Test get/set modification times
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue