diff options
Diffstat (limited to 'src/values.cc')
-rw-r--r-- | src/values.cc | 78 |
1 files changed, 75 insertions, 3 deletions
diff --git a/src/values.cc b/src/values.cc index 7cef5d3def2b..22f84c83e61b 100644 --- a/src/values.cc +++ b/src/values.cc @@ -1,13 +1,85 @@ +#include <iostream> + +#include <sys/types.h> +#include <sys/wait.h> + #include "values.hh" #include "globals.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) { - int res = system(("cat " + src + " > " + dst).c_str()); /* !!! escape */ - if (WEXITSTATUS(res) != 0) - throw Error("cannot copy " + src + " to " + dst); + /* Unfortunately C++ doesn't support coprocedures, so we have no + nice way to chain CopySink and CopySource together. Instead we + 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"); } |