From 40b25153b8a33b44e7b0d6f92b3db6c5a29f9594 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Thu, 20 Nov 2025 02:03:19 +0300 Subject: [PATCH] libutil: Implement second overload of createDirectory for RestoreSink Now the intermediate symlink following issue should be completely plugged. --- src/libutil/fs-sink.cc | 23 +++++++++++++++++++++++ src/libutil/include/nix/util/fs-sink.hh | 4 ++++ tests/functional/nars.sh | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 9058d6e00..45c0262e6 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -71,6 +71,29 @@ static std::filesystem::path append(const std::filesystem::path & src, const Can return dst; } +#ifndef _WIN32 +void RestoreSink::createDirectory(const CanonPath & path, DirectoryCreatedCallback callback) +{ + if (path.isRoot()) { + createDirectory(path); + callback(*this, path); + return; + } + + createDirectory(path); + assert(dirFd); // If that's not true the above call must have thrown an exception. + + RestoreSink dirSink{startFsync}; + dirSink.dstPath = append(dstPath, path); + dirSink.dirFd = ::openat(dirFd.get(), path.rel_c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + + if (!dirSink.dirFd) + throw SysError("opening directory '%s'", dirSink.dstPath.string()); + + callback(dirSink, CanonPath::root); +} +#endif + void RestoreSink::createDirectory(const CanonPath & path) { auto p = append(dstPath, path); diff --git a/src/libutil/include/nix/util/fs-sink.hh b/src/libutil/include/nix/util/fs-sink.hh index b2b009b83..60e8441dd 100644 --- a/src/libutil/include/nix/util/fs-sink.hh +++ b/src/libutil/include/nix/util/fs-sink.hh @@ -120,6 +120,10 @@ struct RestoreSink : FileSystemObjectSink void createDirectory(const CanonPath & path) override; +#ifndef _WIN32 + void createDirectory(const CanonPath & path, DirectoryCreatedCallback callback) override; +#endif + void createRegularFile(const CanonPath & path, std::function) override; void createSymlink(const CanonPath & path, const std::string & target) override; diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index a52c257bc..2925177c5 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -114,7 +114,7 @@ if (( unicodeTestCode == 1 )); then # If the command failed (MacOS or ZFS + normalization), checks that it failed # with the expected "already exists" error, and that this is the same # behavior as `touch` - echo "$unicodeTestOut" | grepQuiet "path '.*/out/â' already exists" + echo "$unicodeTestOut" | grepQuiet "creating directory '.*/out/â': File exists" (( touchFilesCount == 1 )) elif (( unicodeTestCode == 0 )); then