Skip to content

Commit 09755e6

Browse files
committed
libutil: Add callback-based FileSystemObjectSink::createDirectory
1 parent fa380e0 commit 09755e6

File tree

3 files changed

+64
-46
lines changed

3 files changed

+64
-46
lines changed

src/libutil/archive.cc

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -200,54 +200,54 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath
200200
}
201201

202202
else if (type == "directory") {
203-
sink.createDirectory(path);
203+
sink.createDirectory(path, [&](FileSystemObjectSink & dirSink, const CanonPath & relDirPath) {
204+
std::map<Path, int, CaseInsensitiveCompare> names;
204205

205-
std::map<Path, int, CaseInsensitiveCompare> names;
206+
std::string prevName;
206207

207-
std::string prevName;
208+
while (1) {
209+
auto tag = getString();
208210

209-
while (1) {
210-
auto tag = getString();
211+
if (tag == ")")
212+
break;
211213

212-
if (tag == ")")
213-
break;
214-
215-
if (tag != "entry")
216-
throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
217-
218-
expectTag("(");
219-
220-
expectTag("name");
221-
222-
auto name = getString();
223-
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos
224-
|| name.find((char) 0) != std::string::npos)
225-
throw badArchive("NAR contains invalid file name '%1%'", name);
226-
if (name <= prevName)
227-
throw badArchive("NAR directory is not sorted");
228-
prevName = name;
229-
if (archiveSettings.useCaseHack) {
230-
auto i = names.find(name);
231-
if (i != names.end()) {
232-
debug("case collision between '%1%' and '%2%'", i->first, name);
233-
name += caseHackSuffix;
234-
name += std::to_string(++i->second);
235-
auto j = names.find(name);
236-
if (j != names.end())
237-
throw badArchive(
238-
"NAR contains file name '%s' that collides with case-hacked file name '%s'",
239-
prevName,
240-
j->first);
241-
} else
242-
names[name] = 0;
243-
}
214+
if (tag != "entry")
215+
throw badArchive("expected tag 'entry' or ')', got '%s'", tag);
244216

245-
expectTag("node");
217+
expectTag("(");
246218

247-
parse(sink, source, path / name);
219+
expectTag("name");
248220

249-
expectTag(")");
250-
}
221+
auto name = getString();
222+
if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos
223+
|| name.find((char) 0) != std::string::npos)
224+
throw badArchive("NAR contains invalid file name '%1%'", name);
225+
if (name <= prevName)
226+
throw badArchive("NAR directory is not sorted");
227+
prevName = name;
228+
if (archiveSettings.useCaseHack) {
229+
auto i = names.find(name);
230+
if (i != names.end()) {
231+
debug("case collision between '%1%' and '%2%'", i->first, name);
232+
name += caseHackSuffix;
233+
name += std::to_string(++i->second);
234+
auto j = names.find(name);
235+
if (j != names.end())
236+
throw badArchive(
237+
"NAR contains file name '%s' that collides with case-hacked file name '%s'",
238+
prevName,
239+
j->first);
240+
} else
241+
names[name] = 0;
242+
}
243+
244+
expectTag("node");
245+
246+
parse(dirSink, source, relDirPath / name);
247+
248+
expectTag(")");
249+
}
250+
});
251251
}
252252

253253
else if (type == "symlink") {

src/libutil/fs-sink.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/libutil/include/nix/util/fs-sink.hh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ struct FileSystemObjectSink
3636

3737
virtual void createDirectory(const CanonPath & path) = 0;
3838

39+
using DirectoryCreatedCallback = std::function<void(FileSystemObjectSink & dirSink, const CanonPath & dirRelPath)>;
40+
41+
/**
42+
* Create a directory and invoke a callback with a pair of sink + CanonPath
43+
* of the created subdirectory relative to dirSink.
44+
*
45+
* @note This allows for UNIX RestoreSink implementations to implement
46+
* *at-style accessors that always keep an open file descriptor for the
47+
* freshly created directory. Use this when it's important to disallow any
48+
* intermediate path components from being symlinks.
49+
*/
50+
virtual void createDirectory(const CanonPath & path, DirectoryCreatedCallback callback)
51+
{
52+
createDirectory(path);
53+
callback(*this, path);
54+
}
55+
3956
/**
4057
* This function in general is no re-entrant. Only one file can be
4158
* written at a time.

0 commit comments

Comments
 (0)