* A function to restore from a Nix archive.

* addValue() can now import any dumpable FS object.
This commit is contained in:
Eelco Dolstra 2003-06-23 13:27:59 +00:00
parent 85effedca3
commit 5f5cab0ac7
6 changed files with 293 additions and 14 deletions

View file

@ -10,7 +10,10 @@
#include "util.hh" #include "util.hh"
static void pad(unsigned int len, DumpSink & sink) static string archiveVersion1 = "nix-archive-1";
static void writePadding(unsigned int len, DumpSink & sink)
{ {
if (len % 8) { if (len % 8) {
unsigned char zero[8]; unsigned char zero[8];
@ -37,10 +40,13 @@ static void writeString(const string & s, DumpSink & sink)
unsigned int len = s.length(); unsigned int len = s.length();
writeInt(len, sink); writeInt(len, sink);
sink((const unsigned char *) s.c_str(), len); sink((const unsigned char *) s.c_str(), len);
pad(len, sink); writePadding(len, sink);
} }
static void dump(const string & path, DumpSink & sink);
static void dumpEntries(const string & path, DumpSink & sink) static void dumpEntries(const string & path, DumpSink & sink)
{ {
DIR * dir = opendir(path.c_str()); DIR * dir = opendir(path.c_str());
@ -65,8 +71,8 @@ static void dumpEntries(const string & path, DumpSink & sink)
writeString("(", sink); writeString("(", sink);
writeString("name", sink); writeString("name", sink);
writeString(*it, sink); writeString(*it, sink);
writeString("file", sink); writeString("node", sink);
dumpPath(path + "/" + *it, sink); dump(path + "/" + *it, sink);
writeString(")", sink); writeString(")", sink);
} }
@ -96,13 +102,13 @@ static void dumpContents(const string & path, unsigned int size,
if (total != size) if (total != size)
throw SysError("file changed while reading it: " + path); throw SysError("file changed while reading it: " + path);
pad(size, sink); writePadding(size, sink);
close(fd); /* !!! close on exception */ close(fd); /* !!! close on exception */
} }
void dumpPath(const string & path, DumpSink & sink) static void dump(const string & path, DumpSink & sink)
{ {
struct stat st; struct stat st;
if (lstat(path.c_str(), &st)) if (lstat(path.c_str(), &st))
@ -138,6 +144,182 @@ void dumpPath(const string & path, DumpSink & sink)
} }
void restorePath(const string & path, ReadSource & source) void dumpPath(const string & path, DumpSink & sink)
{ {
writeString(archiveVersion1, sink);
dump(path, sink);
} }
static Error badArchive(string s)
{
return Error("bad archive: " + s);
}
static void readPadding(unsigned int len, RestoreSource & source)
{
if (len % 8) {
unsigned char zero[8];
unsigned int n = 8 - (len % 8);
source(zero, n);
for (unsigned int i = 0; i < n; i++)
if (zero[i]) throw badArchive("non-zero padding");
}
}
static unsigned int readInt(RestoreSource & source)
{
unsigned char buf[8];
source(buf, sizeof(buf));
if (buf[4] || buf[5] || buf[6] || buf[7])
throw Error("implementation cannot deal with > 32-bit integers");
return
buf[0] |
(buf[1] << 8) |
(buf[2] << 16) |
(buf[3] << 24);
}
static string readString(RestoreSource & source)
{
unsigned int len = readInt(source);
char buf[len];
source((const unsigned char *) buf, len);
readPadding(len, source);
return string(buf, len);
}
static void skipGeneric(RestoreSource & source)
{
if (readString(source) == "(") {
while (readString(source) != ")")
skipGeneric(source);
}
}
static void restore(const string & path, RestoreSource & source);
static void restoreEntry(const string & path, RestoreSource & source)
{
string s, name;
s = readString(source);
if (s != "(") throw badArchive("expected open tag");
while (1) {
s = readString(source);
if (s == ")") {
break;
} else if (s == "name") {
name = readString(source);
} else if (s == "node") {
if (s == "") throw badArchive("entry name missing");
restore(path + "/" + name, source);
} else {
throw badArchive("unknown field " + s);
skipGeneric(source);
}
}
}
static void restoreContents(int fd, const string & path, RestoreSource & source)
{
unsigned int size = readInt(source);
unsigned int left = size;
unsigned char buf[65536];
while (left) {
unsigned int n = sizeof(buf);
if (n > left) n = left;
source(buf, n);
if (write(fd, buf, n) != (ssize_t) n)
throw SysError("writing file " + path);
left -= n;
}
readPadding(size, source);
}
static void restore(const string & path, RestoreSource & source)
{
string s;
s = readString(source);
if (s != "(") throw badArchive("expected open tag");
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
int fd = -1; /* !!! close on exception */
while (1) {
s = readString(source);
if (s == ")") {
break;
}
else if (s == "type") {
if (type != tpUnknown)
throw badArchive("multiple type fields");
string t = readString(source);
if (t == "regular") {
type = tpRegular;
fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd == -1)
throw SysError("creating file " + path);
}
else if (t == "directory") {
type = tpDirectory;
if (mkdir(path.c_str(), 0777) == -1)
throw SysError("creating directory " + path);
}
else if (t == "symlink") {
type = tpSymlink;
}
else throw badArchive("unknown file type " + t);
}
else if (s == "contents" && type == tpRegular) {
restoreContents(fd, path, source);
}
else if (s == "entry" && type == tpDirectory) {
restoreEntry(path, source);
}
else if (s == "target" && type == tpSymlink) {
string target = readString(source);
if (symlink(target.c_str(), path.c_str()) == -1)
throw SysError("creating symlink " + path);
}
else {
throw badArchive("unknown field " + s);
skipGeneric(source);
}
}
if (fd != -1) close(fd);
}
void restorePath(const string & path, RestoreSource & source)
{
if (readString(source) != archiveVersion1)
throw badArchive("expected Nix archive");
restore(path, source);
}

View file

@ -48,7 +48,7 @@ struct DumpSink
void dumpPath(const string & path, DumpSink & sink); void dumpPath(const string & path, DumpSink & sink);
struct ReadSource struct RestoreSource
{ {
/* The callee should store exactly *len bytes in the buffer /* The callee should store exactly *len bytes in the buffer
pointed to by data. It should block if that much data is not pointed to by data. It should block if that much data is not
@ -56,3 +56,5 @@ struct ReadSource
available. */ available. */
virtual void operator () (const unsigned char * data, unsigned int len) = 0; virtual void operator () (const unsigned char * data, unsigned int len) = 0;
}; };
void restorePath(const string & path, RestoreSource & source);

View file

@ -123,8 +123,8 @@ static Hash computeDerived(Hash sourceHash, string targetName,
} catch (exception & e) { } catch (exception & e) {
cerr << "build error: " << e.what() << endl; cerr << "build error: " << e.what() << endl;
_exit(1);
} }
_exit(1);
} }

View file

@ -133,7 +133,8 @@ struct StdoutSink : DumpSink
(const unsigned char * data, unsigned int len) (const unsigned char * data, unsigned int len)
{ {
/* Don't use cout, it's slow as hell! */ /* Don't use cout, it's slow as hell! */
write(STDOUT_FILENO, (char *) data, len); if (write(STDOUT_FILENO, (char *) data, len) != len)
throw SysError("writing to stdout");
} }
}; };

View file

@ -23,7 +23,21 @@ struct MySink : DumpSink
virtual void operator () (const unsigned char * data, unsigned int len) virtual void operator () (const unsigned char * data, unsigned int len)
{ {
/* Don't use cout, it's slow as hell! */ /* Don't use cout, it's slow as hell! */
write(STDOUT_FILENO, (char *) data, len); if (write(STDOUT_FILENO, (char *) data, len) != (ssize_t) len)
throw SysError("writing to stdout");
}
};
struct MySource : RestoreSource
{
virtual void operator () (const unsigned char * data, unsigned int len)
{
ssize_t res = read(STDIN_FILENO, (char *) data, len);
if (res == -1)
throw SysError("reading from stdin");
if (res != (ssize_t) len)
throw Error("not enough data available on stdin");
} }
}; };
@ -53,6 +67,14 @@ void runTests()
cout << (string) hashPath("scratch") << endl; cout << (string) hashPath("scratch") << endl;
#endif #endif
/* Restoring. */
#if 1
MySource source;
restorePath("outdir", source);
cout << (string) hashPath("outdir") << endl;
return;
#endif
/* Set up the test environment. */ /* Set up the test environment. */
mkdir("scratch", 0777); mkdir("scratch", 0777);

View file

@ -1,13 +1,85 @@
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include "values.hh" #include "values.hh"
#include "globals.hh" #include "globals.hh"
#include "db.hh" #include "db.hh"
#include "archive.hh"
struct CopySink : DumpSink
{
int fd;
virtual void operator () (const unsigned char * data, unsigned int len)
{
if (write(fd, (char *) data, len) != (ssize_t) len)
throw SysError("writing to child");
}
};
struct CopySource : RestoreSource
{
int fd;
virtual void operator () (const unsigned char * data, unsigned int len)
{
ssize_t res = read(fd, (char *) data, len);
if (res == -1)
throw SysError("reading from parent");
if (res != (ssize_t) len)
throw Error("not enough data available on parent");
}
};
static void copyFile(string src, string dst) static void copyFile(string src, string dst)
{ {
int res = system(("cat " + src + " > " + dst).c_str()); /* !!! escape */ /* Unfortunately C++ doesn't support coprocedures, so we have no
if (WEXITSTATUS(res) != 0) nice way to chain CopySink and CopySource together. Instead we
throw Error("cannot copy " + src + " to " + dst); fork off a child to run the sink. (Fork-less platforms should
use a thread). */
/* Create a pipe. */
int fds[2];
if (pipe(fds) == -1) throw SysError("creating pipe");
/* Fork. */
pid_t pid;
switch (pid = fork()) {
case -1:
throw SysError("unable to fork");
case 0: /* child */
try {
close(fds[1]);
CopySource source;
source.fd = fds[0];
restorePath(dst, source);
_exit(0);
} catch (exception & e) {
cerr << "error: " << e.what() << endl;
}
_exit(1);
}
close(fds[0]);
/* Parent. */
CopySink sink;
sink.fd = fds[1];
dumpPath(src, sink);
/* Wait for the child to finish. */
int status;
if (waitpid(pid, &status, 0) != pid)
throw SysError("waiting for child");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
throw Error("cannot copy file: child died");
} }