mirror of
https://github.com/NixOS/nix.git
synced 2024-11-21 14:22:29 +00:00
parent
d1dd7abbf0
commit
35bdb9cee7
@ -115,10 +115,10 @@ git_oid hashToOID(const Hash & hash)
|
||||
return oid;
|
||||
}
|
||||
|
||||
Object lookupObject(git_repository * repo, const git_oid & oid)
|
||||
Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t type = GIT_OBJECT_ANY)
|
||||
{
|
||||
Object obj;
|
||||
if (git_object_lookup(Setter(obj), repo, &oid, GIT_OBJECT_ANY)) {
|
||||
if (git_object_lookup(Setter(obj), repo, &oid, type)) {
|
||||
auto err = git_error_last();
|
||||
throw Error("getting Git object '%s': %s", oid, err->message);
|
||||
}
|
||||
@ -909,6 +909,50 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
|
||||
addToTree(*pathComponents.rbegin(), oid, GIT_FILEMODE_LINK);
|
||||
}
|
||||
|
||||
void createHardlink(const Path & path, const CanonPath & target) override
|
||||
{
|
||||
auto pathComponents = tokenizeString<std::vector<std::string>>(path, "/");
|
||||
if (!prepareDirs(pathComponents, false)) return;
|
||||
|
||||
auto relTarget = CanonPath(path).parent()->makeRelative(target);
|
||||
|
||||
auto dir = pendingDirs.rbegin();
|
||||
|
||||
// For each ../ component at the start, go up one directory.
|
||||
std::string_view relTargetLeft(relTarget);
|
||||
while (hasPrefix(relTargetLeft, "../")) {
|
||||
if (dir == pendingDirs.rend())
|
||||
throw Error("invalid hard link target '%s'", target);
|
||||
++dir;
|
||||
relTargetLeft = relTargetLeft.substr(3);
|
||||
}
|
||||
|
||||
// Look up the remainder of the target, starting at the
|
||||
// top-most `git_treebuilder`.
|
||||
std::variant<git_treebuilder *, git_oid> curDir{dir->builder.get()};
|
||||
Object tree; // needed to keep `entry` alive
|
||||
const git_tree_entry * entry = nullptr;
|
||||
|
||||
for (auto & c : CanonPath(relTargetLeft)) {
|
||||
if (auto builder = std::get_if<git_treebuilder *>(&curDir)) {
|
||||
if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str())))
|
||||
throw Error("cannot find hard link target '%s'", target);
|
||||
curDir = *git_tree_entry_id(entry);
|
||||
} else if (auto oid = std::get_if<git_oid>(&curDir)) {
|
||||
tree = lookupObject(*repo, *oid, GIT_OBJECT_TREE);
|
||||
if (!(entry = git_tree_entry_byname((const git_tree *) &*tree, std::string(c).c_str())))
|
||||
throw Error("cannot find hard link target '%s'", target);
|
||||
curDir = *git_tree_entry_id(entry);
|
||||
}
|
||||
}
|
||||
|
||||
assert(entry);
|
||||
|
||||
addToTree(*pathComponents.rbegin(),
|
||||
*git_tree_entry_id(entry),
|
||||
git_tree_entry_filemode(entry));
|
||||
}
|
||||
|
||||
Hash sync() override {
|
||||
updateBuilders({});
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace nix {
|
||||
|
||||
namespace fetchers { struct PublicKey; }
|
||||
|
||||
struct GitFileSystemObjectSink : FileSystemObjectSink
|
||||
struct GitFileSystemObjectSink : ExtendedFileSystemObjectSink
|
||||
{
|
||||
/**
|
||||
* Flush builder and return a final Git hash.
|
||||
|
@ -41,6 +41,19 @@ struct FileSystemObjectSink
|
||||
virtual void createSymlink(const Path & path, const std::string & target) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* An extension of `FileSystemObjectSink` that supports file types
|
||||
* that are not supported by Nix's FSO model.
|
||||
*/
|
||||
struct ExtendedFileSystemObjectSink : FileSystemObjectSink
|
||||
{
|
||||
/**
|
||||
* Create a hard link. The target must be the path of a previously
|
||||
* encountered file relative to the root of the FSO.
|
||||
*/
|
||||
virtual void createHardlink(const Path & path, const CanonPath & target) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively copy file system objects from the source into the sink.
|
||||
*/
|
||||
|
@ -163,7 +163,7 @@ void unpackTarfile(const Path & tarFile, const Path & destDir)
|
||||
extract_archive(archive, destDir);
|
||||
}
|
||||
|
||||
time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink)
|
||||
time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink)
|
||||
{
|
||||
time_t lastModified = 0;
|
||||
|
||||
@ -183,7 +183,12 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin
|
||||
|
||||
lastModified = std::max(lastModified, archive_entry_mtime(entry));
|
||||
|
||||
switch (archive_entry_filetype(entry)) {
|
||||
if (auto target = archive_entry_hardlink(entry)) {
|
||||
parseSink.createHardlink(path, CanonPath(target));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (auto type = archive_entry_filetype(entry)) {
|
||||
|
||||
case AE_IFDIR:
|
||||
parseSink.createDirectory(path);
|
||||
@ -220,7 +225,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin
|
||||
}
|
||||
|
||||
default:
|
||||
throw Error("file '%s' in tarball has unsupported file type", path);
|
||||
throw Error("file '%s' in tarball has unsupported file type %d", path, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,6 @@ void unpackTarfile(Source & source, const Path & destDir);
|
||||
|
||||
void unpackTarfile(const Path & tarFile, const Path & destDir);
|
||||
|
||||
time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink);
|
||||
time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink);
|
||||
|
||||
}
|
||||
|
@ -59,3 +59,12 @@ test_tarball() {
|
||||
test_tarball '' cat
|
||||
test_tarball .xz xz
|
||||
test_tarball .gz gzip
|
||||
|
||||
# Test hard links.
|
||||
path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .storePath)"
|
||||
[[ $(cat "$path/a/b/foo") = bar ]]
|
||||
[[ $(cat "$path/a/b/xyzzy") = bar ]]
|
||||
[[ $(cat "$path/a/yyy") = bar ]]
|
||||
[[ $(cat "$path/a/zzz") = bar ]]
|
||||
[[ $(cat "$path/c/aap") = bar ]]
|
||||
[[ $(cat "$path/fnord") = bar ]]
|
||||
|
BIN
tests/functional/tree.tar.gz
Normal file
BIN
tests/functional/tree.tar.gz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user