Skip to content

Commit

Permalink
allow copyDir and copyDirWithPermissions skipping special files
Browse files Browse the repository at this point in the history
  • Loading branch information
ringabout committed Sep 27, 2024
1 parent 2cdc0e9 commit 4f44597
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.

[//]: # "Additions:"

Expand Down
21 changes: 15 additions & 6 deletions lib/std/private/osdirs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,17 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
else:
discard existsOrCreateDir(p)

proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1",
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
## Copies a directory from `source` to `dest`.
##
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
## are skipped.
##
## If `skipSpecial` is true, then (besides all directories) only *regular*
## files (**without** special "file" objects like FIFOs, device files,
## etc) will be copied on Unix.
##
## If this fails, `OSError` is raised.
##
## On the Windows platform this proc will copy the attributes from
Expand All @@ -472,23 +476,28 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
## * `createDir proc`_
## * `moveDir proc`_
createDir(dest)
for kind, path in walkDir(source):
for kind, path in walkDir(source, skipSpecial = skipSpecial):
var noSource = splitPath(path).tail
if kind == pcDir:
copyDir(path, dest / noSource)
copyDir(path, dest / noSource, skipSpecial = skipSpecial)
else:
copyFile(path, dest / noSource, {cfSymlinkAsIs})


proc copyDirWithPermissions*(source, dest: string,
ignorePermissionErrors = true)
ignorePermissionErrors = true,
skipSpecial = false)
{.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
benign, noWeirdTarget.} =
## Copies a directory from `source` to `dest` preserving file permissions.
##
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
## are skipped.
##
## If `skipSpecial` is true, then (besides all directories) only *regular*
## files (**without** special "file" objects like FIFOs, device files,
## etc) will be copied on Unix.
##
## If this fails, `OSError` is raised. This is a wrapper proc around
## `copyDir`_ and `copyFileWithPermissions`_ procs
## on non-Windows platforms.
Expand Down Expand Up @@ -518,10 +527,10 @@ proc copyDirWithPermissions*(source, dest: string,
except:
if not ignorePermissionErrors:
raise
for kind, path in walkDir(source):
for kind, path in walkDir(source, skipSpecial = skipSpecial):
var noSource = splitPath(path).tail
if kind == pcDir:
copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial)
else:
copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs})

Expand Down
2 changes: 1 addition & 1 deletion lib/std/private/osfiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1
else:
# generic version of copyFile which works for any platform:
var d, s: File
if not open(s, source):raiseOSError(osLastError(), source)
if not open(s, source): raiseOSError(osLastError(), source)
if not open(d, dest, fmWrite):
close(s)
raiseOSError(osLastError(), dest)
Expand Down
15 changes: 13 additions & 2 deletions tests/stdlib/tos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ Raises
"""
# test os path creation, iteration, and deletion

import os, strutils, pathnorm
from stdtest/specialpaths import buildDir
import std/[syncio, assertions]
import std/[syncio, assertions, osproc, os, strutils, pathnorm]

block fileOperations:
let files = @["these.txt", "are.x", "testing.r", "files.q"]
Expand Down Expand Up @@ -161,6 +160,18 @@ block fileOperations:
# createDir should not fail if `dir` is empty
createDir("")


when defined(linux): # bug #24174
createDir("a/b")
open("a/file.txt", fmWrite).close

if not fileExists("a/fifoFile"):
doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0

copyDir("a/", "../dest/a/", skipSpecial = true)
copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true)
removeDir("a")

# Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
# `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
block:
Expand Down

0 comments on commit 4f44597

Please sign in to comment.