@@ -34,11 +34,12 @@ void copyRecursive(SourceAccessor & accessor, const CanonPath & from, FileSystem
3434 }
3535
3636 case SourceAccessor::tDirectory: {
37- sink.createDirectory (to);
38- for (auto & [name, _] : accessor.readDirectory (from)) {
39- copyRecursive (accessor, from / name, sink, to / name);
40- break ;
41- }
37+ sink.createDirectory (to, [&](FileSystemObjectSink & dirSink, const CanonPath & relDirPath) {
38+ for (auto & [name, _] : accessor.readDirectory (from)) {
39+ copyRecursive (accessor, from / name, dirSink, relDirPath / name);
40+ break ;
41+ }
42+ });
4243 break ;
4344 }
4445
@@ -70,11 +71,60 @@ static std::filesystem::path append(const std::filesystem::path & src, const Can
7071 return dst;
7172}
7273
74+ #ifndef _WIN32
75+ void RestoreSink::createDirectory (const CanonPath & path, DirectoryCreatedCallback callback)
76+ {
77+ if (path.isRoot ()) {
78+ createDirectory (path);
79+ callback (*this , path);
80+ return ;
81+ }
82+
83+ createDirectory (path);
84+ assert (dirFd); // If that's not true the above call must have thrown an exception.
85+
86+ RestoreSink dirSink{startFsync};
87+ dirSink.dstPath = append (dstPath, path);
88+ dirSink.dirFd = ::openat (dirFd.get (), path.rel_c_str (), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
89+
90+ if (!dirSink.dirFd )
91+ throw SysError (" opening directory '%s'" , dirSink.dstPath .string ());
92+
93+ callback (dirSink, CanonPath::root);
94+ }
95+ #endif
96+
7397void RestoreSink::createDirectory (const CanonPath & path)
7498{
7599 auto p = append (dstPath, path);
100+
101+ #ifndef _WIN32
102+ if (dirFd) {
103+ if (path.isRoot ())
104+ /* Trying to create a directory that we already have a file descriptor for. */
105+ throw Error (" path '%s' already exists" , p.string ());
106+
107+ if (::mkdirat (dirFd.get (), path.rel_c_str (), 0777 ) == -1 )
108+ throw SysError (" creating directory '%s'" , p.string ());
109+
110+ return ;
111+ }
112+ #endif
113+
76114 if (!std::filesystem::create_directory (p))
77115 throw Error (" path '%s' already exists" , p.string ());
116+
117+ #ifndef _WIN32
118+ if (path.isRoot ()) {
119+ assert (!dirFd); // Handled above
120+
121+ /* Open directory for further *at operations relative to the sink root
122+ directory. */
123+ dirFd = open (p.c_str (), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
124+ if (!dirFd)
125+ throw SysError (" creating directory '%1%'" , p.string ());
126+ }
127+ #endif
78128};
79129
80130struct RestoreRegularFile : CreateRegularFileSink
@@ -114,7 +164,14 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
114164 FILE_ATTRIBUTE_NORMAL,
115165 NULL )
116166#else
117- open (p.c_str (), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666 )
167+ [&]() {
168+ /* O_EXCL together with O_CREAT ensures symbolic links in the last
169+ component are not followed. */
170+ constexpr int flags = O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC;
171+ if (!dirFd)
172+ return ::open (p.c_str (), flags, 0666 );
173+ return ::openat (dirFd.get (), path.rel_c_str (), flags, 0666 );
174+ }();
118175#endif
119176 ;
120177 if (!crf.fd )
@@ -161,6 +218,13 @@ void RestoreRegularFile::operator()(std::string_view data)
161218void RestoreSink::createSymlink (const CanonPath & path, const std::string & target)
162219{
163220 auto p = append (dstPath, path);
221+ #ifndef _WIN32
222+ if (dirFd) {
223+ if (::symlinkat (requireCString (target), dirFd.get (), path.rel_c_str ()) == -1 )
224+ throw SysError (" creating symlink from '%1%' -> '%2%'" , p.string (), target);
225+ return ;
226+ }
227+ #endif
164228 nix::createSymlink (target, p.string ());
165229}
166230
0 commit comments