* nix-store --import': import an archive created by nix-store

--export' into the Nix store, and optionally check the cryptographic
  signatures against /nix/etc/nix/signing-key.pub.  (TODO: verify
  against a set of public keys.)
This commit is contained in:
Eelco Dolstra 2007-02-21 15:45:32 +00:00
parent 46e0919ced
commit 43c4d18c6a
8 changed files with 142 additions and 10 deletions

View file

@ -6,6 +6,7 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "aterm.hh" #include "aterm.hh"
#include "derivations-ast.hh" #include "derivations-ast.hh"
#include "worker-protocol.hh"
#include "config.h" #include "config.h"
#include <iostream> #include <iostream>
@ -743,8 +744,6 @@ void LocalStore::exportPath(const Path & path, bool sign,
writeInt(1, hashAndWriteSink); writeInt(1, hashAndWriteSink);
//printMsg(lvlError, format("HASH = %1%") % printHash(hash));
Path tmpDir = createTempDir(); Path tmpDir = createTempDir();
AutoDelete delTmp(tmpDir); AutoDelete delTmp(tmpDir);
Path hashFile = tmpDir + "/hash"; Path hashFile = tmpDir + "/hash";
@ -759,8 +758,6 @@ void LocalStore::exportPath(const Path & path, bool sign,
args.push_back(hashFile); args.push_back(hashFile);
string signature = runProgram("openssl", true, args); string signature = runProgram("openssl", true, args);
//printMsg(lvlError, format("SIGNATURE = %1%") % signature);
writeString(signature, hashAndWriteSink); writeString(signature, hashAndWriteSink);
} else } else
@ -768,6 +765,115 @@ void LocalStore::exportPath(const Path & path, bool sign,
} }
struct HashAndReadSource : Source
{
Source & readSource;
HashSink hashSink;
bool hashing;
HashAndReadSource(Source & readSource) : readSource(readSource), hashSink(htSHA256)
{
hashing = true;
}
virtual void operator ()
(unsigned char * data, unsigned int len)
{
readSource(data, len);
if (hashing) hashSink(data, len);
}
};
Path LocalStore::importPath(bool requireSignature, Source & source)
{
HashAndReadSource hashAndReadSource(source);
/* We don't yet know what store path this archive contains (the
store path follows the archive data proper), and besides, we
don't know yet whether the signature is valid. */
Path tmpDir = createTempDir(nixStore);
AutoDelete delTmp(tmpDir);
Path unpacked = tmpDir + "/unpacked";
restorePath(unpacked, hashAndReadSource);
unsigned int magic = readInt(hashAndReadSource);
if (magic != EXPORT_MAGIC)
throw Error("Nix archive cannot be imported; wrong format");
Path dstPath = readStorePath(hashAndReadSource);
PathSet references = readStorePaths(hashAndReadSource);
Path deriver = readStorePath(hashAndReadSource);
Hash hash = hashAndReadSource.hashSink.finish();
hashAndReadSource.hashing = false;
bool haveSignature = readInt(hashAndReadSource) == 1;
if (requireSignature && !haveSignature)
throw Error("imported archive lacks a signature");
if (haveSignature) {
string signature = readString(hashAndReadSource);
Path sigFile = tmpDir + "/sig";
writeStringToFile(sigFile, signature);
Strings args;
args.push_back("rsautl");
args.push_back("-verify");
args.push_back("-inkey");
args.push_back(nixConfDir + "/signing-key.pub");
args.push_back("-pubin");
args.push_back("-in");
args.push_back(sigFile);
string hash2 = runProgram("openssl", true, args);
/* Note: runProgram() throws an exception if the signature is
invalid. */
if (printHash(hash) != hash2)
throw Error(
"signed hash doesn't match actual contents of imported "
"archive; archive could be corrupt, or someone is trying "
"to import a Trojan horse");
}
/* Do the actual import. */
/* !!! way too much code duplication with addTextToStore() etc. */
addTempRoot(dstPath);
if (!isValidPath(dstPath)) {
PathLocks outputLock(singleton<PathSet, Path>(dstPath));
if (!isValidPath(dstPath)) {
if (pathExists(dstPath)) deletePathWrapped(dstPath);
if (rename(unpacked.c_str(), dstPath.c_str()) == -1)
throw SysError(format("cannot move `%1%' to `%2%'")
% unpacked % dstPath);
canonicalisePathMetaData(dstPath);
Transaction txn(nixDB);
/* !!! if we were clever, we could prevent the hashPath()
here. */
registerValidPath(txn, dstPath,
hashPath(htSHA256, dstPath), references, "");
txn.commit();
}
outputLock.setDeletion(true);
}
return dstPath;
}
void deleteFromStore(const Path & _path, unsigned long long & bytesFreed) void deleteFromStore(const Path & _path, unsigned long long & bytesFreed)
{ {
bytesFreed = 0; bytesFreed = 0;

View file

@ -59,6 +59,8 @@ public:
void exportPath(const Path & path, bool sign, void exportPath(const Path & path, bool sign,
Sink & sink); Sink & sink);
Path importPath(bool requireSignature, Source & source);
void buildDerivations(const PathSet & drvPaths); void buildDerivations(const PathSet & drvPaths);
void ensurePath(const Path & path); void ensurePath(const Path & path);

View file

@ -250,6 +250,12 @@ void RemoteStore::exportPath(const Path & path, bool sign,
} }
Path RemoteStore::importPath(bool requireSignature, Source & source)
{
throw Error("not implemented");
}
void RemoteStore::buildDerivations(const PathSet & drvPaths) void RemoteStore::buildDerivations(const PathSet & drvPaths)
{ {
writeInt(wopBuildDerivations, to); writeInt(wopBuildDerivations, to);

View file

@ -47,6 +47,8 @@ public:
void exportPath(const Path & path, bool sign, void exportPath(const Path & path, bool sign,
Sink & sink); Sink & sink);
Path importPath(bool requireSignature, Source & source);
void buildDerivations(const PathSet & drvPaths); void buildDerivations(const PathSet & drvPaths);
void ensurePath(const Path & path); void ensurePath(const Path & path);

View file

@ -99,6 +99,10 @@ public:
virtual void exportPath(const Path & path, bool sign, virtual void exportPath(const Path & path, bool sign,
Sink & sink) = 0; Sink & sink) = 0;
/* Import a NAR dump created by exportPath() into the Nix
store. */
virtual Path importPath(bool requireSignature, Source & source) = 0;
/* Ensure that the output paths of the derivation are valid. If /* Ensure that the output paths of the derivation are valid. If
they are already valid, this is a no-op. Otherwise, validity they are already valid, this is a no-op. Otherwise, validity
can be reached in two ways. First, if the output paths have can be reached in two ways. First, if the output paths have

View file

@ -317,19 +317,19 @@ void makePathReadOnly(const Path & path)
} }
static Path tempName() static Path tempName(const Path & tmpRoot)
{ {
static int counter = 0; static int counter = 0;
Path tmpRoot = canonPath(getEnv("TMPDIR", "/tmp"), true); Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
return (format("%1%/nix-%2%-%3%") % tmpRoot % getpid() % counter++).str(); return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str();
} }
Path createTempDir() Path createTempDir(const Path & tmpRoot)
{ {
while (1) { while (1) {
checkInterrupt(); checkInterrupt();
Path tmpDir = tempName(); Path tmpDir = tempName(tmpRoot);
if (mkdir(tmpDir.c_str(), 0777) == 0) { if (mkdir(tmpDir.c_str(), 0777) == 0) {
/* Explicitly set the group of the directory. This is to /* Explicitly set the group of the directory. This is to
work around around problems caused by BSD's group work around around problems caused by BSD's group

View file

@ -70,7 +70,7 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
void makePathReadOnly(const Path & path); void makePathReadOnly(const Path & path);
/* Create a temporary directory. */ /* Create a temporary directory. */
Path createTempDir(); Path createTempDir(const Path & tmpRoot = "");
/* Create a directory and all its parents, if necessary. */ /* Create a directory and all its parents, if necessary. */
void createDirs(const Path & path); void createDirs(const Path & path);

View file

@ -651,6 +651,16 @@ static void opExport(Strings opFlags, Strings opArgs)
} }
static void opImport(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (!opArgs.empty()) throw UsageError("no arguments expected");
FdSource source(STDIN_FILENO);
cout << format("%1%\n") % store->importPath(false, source);
}
/* Initialise the Nix databases. */ /* Initialise the Nix databases. */
static void opInit(Strings opFlags, Strings opArgs) static void opInit(Strings opFlags, Strings opArgs)
{ {
@ -722,6 +732,8 @@ void run(Strings args)
op = opRestore; op = opRestore;
else if (arg == "--export") else if (arg == "--export")
op = opExport; op = opExport;
else if (arg == "--import")
op = opImport;
else if (arg == "--init") else if (arg == "--init")
op = opInit; op = opInit;
else if (arg == "--verify") else if (arg == "--verify")