Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #24174; allow copyDir and copyDirWithPermissions skipping special files #24190

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading