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

Add git add-to-store support for Nix #3

Merged
merged 35 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
130267d
Handle git ingestion over daemon
matthewbauer May 28, 2020
df74744
Parse git blobs correctly
matthewbauer May 28, 2020
f292e50
Add --git argument to add-to-store
matthewbauer May 28, 2020
1592d09
Add test for nix add-to-store
matthewbauer May 28, 2020
cf81991
Depend on install for installcheck
matthewbauer May 28, 2020
9e9c2ce
Give error when git ingestion doesn’t use sha1
matthewbauer May 29, 2020
6f7fab9
Use SHA1 for Git objects
matthewbauer May 29, 2020
ee989c6
Pass storeDir to restoreGit
matthewbauer May 29, 2020
9cd3ac7
Pad 0s for dumpGitInternal
matthewbauer May 29, 2020
b5ed6a9
Add createExecutableFile primitive to ParseSink
matthewbauer May 29, 2020
6ceba63
Read related git objects from nix store in parseGit
matthewbauer May 29, 2020
e44956d
Allow sha1 in --hash results
matthewbauer May 29, 2020
646862e
Add test for adding git tree hash
matthewbauer May 29, 2020
592e926
Expand git tests
matthewbauer May 29, 2020
a4a038f
Cleanup git.cc code
matthewbauer May 29, 2020
4ecb49d
Use path-based addToStore method for add-to-store
matthewbauer May 29, 2020
d68b774
Properly handle realStoreDir vs. storeDir in git.cc
matthewbauer May 29, 2020
18d4a3d
Allow sending git objects over daemon
matthewbauer May 29, 2020
d8f34fd
Recursively add to store from git ingestion
matthewbauer May 29, 2020
27ffbc7
Add hash to local store correctly from dump
matthewbauer May 30, 2020
05cbfde
Move git tests to own file
matthewbauer May 30, 2020
97ce2cc
Revert "Depend on install for installcheck"
matthewbauer May 30, 2020
229ce9c
Make computeStorePathForPath work in Git ingestion
matthewbauer Jun 1, 2020
031fa72
Use SHA256 for narHash
matthewbauer Jun 1, 2020
d49c873
Use correct narHash in add-to-store
matthewbauer Jun 1, 2020
cd3ef3f
Throw error when hashAlgo != SHA1 on git ingestion
matthewbauer Jun 1, 2020
4dae98e
Don’t include realStoreDir refs in git output
matthewbauer Jun 1, 2020
1b14945
Use FdSink to simplify copying
matthewbauer Jun 1, 2020
54ee74e
Fixup bad hash in tests/git.sh
matthewbauer Jun 1, 2020
b1e1ace
Update tests/git.sh hashes
matthewbauer Jun 1, 2020
7fe9a48
Don’t preallocate file size
matthewbauer Jun 1, 2020
eb90cc6
Copy instead of symlinking directories
matthewbauer Jun 1, 2020
b120259
Use custom copyDirectory instead of libc++fs
matthewbauer Jun 1, 2020
7432a9d
Fixup bad rebase
matthewbauer Jun 2, 2020
24544d3
Verify added path matches calculated store path in add-to-store
matthewbauer Jun 2, 2020
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
21 changes: 17 additions & 4 deletions src/libstore/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "archive.hh"
#include "derivations.hh"
#include "args.hh"
#include "git.hh"

namespace nix::daemon {

Expand Down Expand Up @@ -358,7 +359,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
std::string s, baseName;
FileIngestionMethod method;
{
bool fixed, recursive;
bool fixed;
unsigned char recursive;
from >> baseName >> fixed /* obsolete */ >> recursive >> s;
method = FileIngestionMethod { recursive };
/* Compatibility hack. */
Expand All @@ -372,20 +374,31 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
TeeSource savedNAR(from);
RetrieveRegularNARSink savedRegular;

if (method == FileIngestionMethod::Recursive) {
switch (method) {
case FileIngestionMethod::Recursive: {
/* Get the entire NAR dump from the client and save it to
a string so that we can pass it to
addToStoreFromDump(). */
ParseSink sink; /* null sink; just parse the NAR */
parseDump(sink, savedNAR);
} else
break;
}
case FileIngestionMethod::Git: {
ParseSink sink;
parseGit(sink, savedNAR, store->storeDir, store->storeDir);
break;
}
case FileIngestionMethod::Flat: {
parseDump(savedRegular, from);
break;
}
}

logger->startWork();
if (!savedRegular.regular) throw Error("regular file expected");

auto path = store->addToStoreFromDump(
method == FileIngestionMethod::Recursive ? *savedNAR.data : savedRegular.s,
method == FileIngestionMethod::Flat ? savedRegular.s : *savedNAR.data,
baseName,
method,
hashAlgo);
Expand Down
35 changes: 31 additions & 4 deletions src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,10 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
return n;
});

restorePath(realPath, wrapperSource);
if (hasPrefix(info.ca, "fixed:git:"))
restoreGit(realPath, wrapperSource, realStoreDir, storeDir);
else
restorePath(realPath, wrapperSource);

auto hashResult = hashSink->finish();

Expand Down Expand Up @@ -1046,6 +1049,9 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
StorePath LocalStore::addToStoreFromDump(const string & dump, const string & name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
{
if (method == FileIngestionMethod::Git && hashAlgo != htSHA1)
throw Error("git ingestion must use sha1 hash");

Hash h = hashString(hashAlgo, dump);

auto dstPath = makeFixedOutputPath(method, h, name);
Expand Down Expand Up @@ -1079,7 +1085,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam
}
case FileIngestionMethod::Git: {
StringSource source(dump);
restoreGit(realPath, source);
restoreGit(realPath, source, realStoreDir, storeDir);
break;
}
}
Expand Down Expand Up @@ -1118,14 +1124,35 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath,
{
Path srcPath(absPath(_srcPath));

if (method == FileIngestionMethod::Git && hashAlgo != htSHA1)
throw Error("git ingestion must use sha1 hash");

/* Read the whole path into memory. This is not a very scalable
method for very large paths, but `copyPath' is mainly used for
small files. */
StringSink sink;
if (method == FileIngestionMethod::Recursive)
switch (method) {
case FileIngestionMethod::Recursive: {
dumpPath(srcPath, sink, filter);
else
break;
}
case FileIngestionMethod::Git: {
// recursively add to store if path is a directory
struct stat st;
if (lstat(srcPath.c_str(), &st))
throw SysError(format("getting attributes of path '%1%'") % srcPath);
if (S_ISDIR(st.st_mode))
for (auto & i : readDirectory(srcPath))
addToStore(i.name, srcPath + "/" + i.name, method, hashAlgo, filter, repair);

dumpGit(hashAlgo, srcPath, sink, filter);
break;
}
case FileIngestionMethod::Flat: {
sink.s = make_ref<std::string>(readFile(srcPath));
break;
}
}

return addToStoreFromDump(*sink.s, name, method, hashAlgo, repair);
}
Expand Down
25 changes: 20 additions & 5 deletions src/libstore/remote-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "derivations.hh"
#include "pool.hh"
#include "finally.hh"
#include "git.hh"

#include <sys/types.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -488,17 +489,28 @@ StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath,
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");

if (method == FileIngestionMethod::Git) throw Error("cannot remotely add to store using the git file ingestion method");

auto conn(getConnection());
if (method == FileIngestionMethod::Git && hashAlgo != htSHA1)
throw Error("git ingestion must use sha1 hash");

Path srcPath(absPath(_srcPath));

// recursively add to store if path is a directory
if (method == FileIngestionMethod::Git) {
struct stat st;
if (lstat(srcPath.c_str(), &st))
throw SysError(format("getting attributes of path '%1%'") % srcPath);
if (S_ISDIR(st.st_mode))
for (auto & i : readDirectory(srcPath))
addToStore(i.name, srcPath + "/" + i.name, method, hashAlgo, filter, repair);
}

auto conn(getConnection());

conn->to
<< wopAddToStore
<< name
<< ((hashAlgo == htSHA256 && method == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */
<< (method == FileIngestionMethod::Recursive ? 1 : 0)
<< (uint8_t) method
<< printHashType(hashAlgo);

try {
Expand All @@ -507,7 +519,10 @@ StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath,
connections->incCapacity();
{
Finally cleanup([&]() { connections->decCapacity(); });
dumpPath(srcPath, conn->to, filter);
if (method == FileIngestionMethod::Git)
dumpGit(hashAlgo, srcPath, conn->to, filter);
else
dumpPath(srcPath, conn->to, filter);
}
conn->to.warn = false;
conn.processStderr();
Expand Down
23 changes: 20 additions & 3 deletions src/libstore/store-api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ StorePath Store::makeFixedOutputPath(
const StorePathSet & references,
bool hasSelfReference) const
{
if (method == FileIngestionMethod::Git && hash.type != htSHA1)
throw Error("Git file ingestion must use sha1 hash");

if (hash.type == htSHA256 && method == FileIngestionMethod::Recursive) {
return makeStorePath(makeType(*this, "source", references, hasSelfReference), hash, name);
} else {
Expand Down Expand Up @@ -206,9 +209,21 @@ StorePath Store::makeTextPath(std::string_view name, const Hash & hash,
std::pair<StorePath, Hash> Store::computeStorePathForPath(std::string_view name,
const Path & srcPath, FileIngestionMethod method, HashType hashAlgo, PathFilter & filter) const
{
Hash h = method == FileIngestionMethod::Recursive
? hashPath(hashAlgo, srcPath, filter).first
: hashFile(hashAlgo, srcPath);
Hash h;
switch (method) {
case FileIngestionMethod::Recursive: {
h = hashPath(hashAlgo, srcPath, filter).first;
break;
}
case FileIngestionMethod::Git: {
h = hashGit(hashAlgo, srcPath, filter).first;
break;
}
case FileIngestionMethod::Flat: {
h = hashFile(hashAlgo, srcPath);
break;
}
}
return std::make_pair(makeFixedOutputPath(method, h, name), h);
}

Expand Down Expand Up @@ -838,6 +853,8 @@ Strings ValidPathInfo::shortRefs() const

std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash)
{
if (method == FileIngestionMethod::Git && hash.type != htSHA1)
throw Error("git file ingestion must use sha1 hashes");
return "fixed:" + ingestionMethodPrefix(method) + hash.to_string();
}

Expand Down
36 changes: 36 additions & 0 deletions src/libutil/fs-sink.hh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ struct ParseSink
virtual void createDirectory(const Path & path) { };

virtual void createRegularFile(const Path & path) { };
virtual void createExecutableFile(const Path & path) { };
virtual void isExecutable() { };
virtual void preallocateContents(unsigned long long size) { };
virtual void receiveContents(unsigned char * data, unsigned int len) { };

virtual void createSymlink(const Path & path, const string & target) { };

virtual void copyFile(const Path & source) { };
virtual void copyDirectory(const Path & source, const Path & destination) { };
};

struct RestoreSink : ParseSink
Expand All @@ -39,6 +43,13 @@ struct RestoreSink : ParseSink
if (!fd) throw SysError(format("creating file '%1%'") % p);
}

void createExecutableFile(const Path & path)
{
Path p = dstPath + path;
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0777);
if (!fd) throw SysError(format("creating file '%1%'") % p);
}

void isExecutable()
{
struct stat st;
Expand Down Expand Up @@ -73,6 +84,31 @@ struct RestoreSink : ParseSink
Path p = dstPath + path;
nix::createSymlink(target, p);
}

void copyFile(const Path & source)
{
FdSink sink(fd.get());
readFile(source, sink);
}

void copyDirectory(const Path & source, const Path & destination)
{
Path p = dstPath + destination;
createDirectory(destination);
for (auto & i : readDirectory(source)) {
struct stat st;
Path entry = source + "/" + i.name;
if (lstat(entry.c_str(), &st))
throw SysError(format("getting attributes of path '%1%'") % entry);
if (S_ISREG(st.st_mode)) {
createRegularFile(destination + "/" + i.name);
copyFile(entry);
} else if (S_ISDIR(st.st_mode))
copyDirectory(entry, destination + "/" + i.name);
else
throw Error(format("Unknown file: %s") % entry);
}
}
};


Expand Down
Loading