about summary refs log tree commit diff
path: root/third_party/nix/src/libutil/archive.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/libutil/archive.cc')
-rw-r--r--third_party/nix/src/libutil/archive.cc556
1 files changed, 267 insertions, 289 deletions
diff --git a/third_party/nix/src/libutil/archive.cc b/third_party/nix/src/libutil/archive.cc
index 3aa1202709..9ae5c76db7 100644
--- a/third_party/nix/src/libutil/archive.cc
+++ b/third_party/nix/src/libutil/archive.cc
@@ -1,32 +1,31 @@
-#include <cerrno>
-#include <algorithm>
-#include <vector>
-#include <map>
-
-#include <strings.h> // for strcasecmp
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include "archive.hh"
 #include <dirent.h>
 #include <fcntl.h>
-
-#include "archive.hh"
-#include "util.hh"
+#include <strings.h>  // for strcasecmp
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <algorithm>
+#include <cerrno>
+#include <map>
+#include <vector>
 #include "config.hh"
+#include "util.hh"
 
 namespace nix {
 
-struct ArchiveSettings : Config
-{
-    Setting<bool> useCaseHack{this,
-        #if __APPLE__
-            true,
-        #else
-            false,
-        #endif
+struct ArchiveSettings : Config {
+  Setting<bool> useCaseHack {
+    this,
+#if __APPLE__
+        true,
+#else
+        false,
+#endif
         "use-case-hack",
-        "Whether to enable a Darwin-specific hack for dealing with file name collisions."};
+        "Whether to enable a Darwin-specific hack for dealing with file name "
+        "collisions."
+  };
 };
 
 static ArchiveSettings archiveSettings;
@@ -37,105 +36,105 @@ const std::string narVersionMagic1 = "nix-archive-1";
 
 static string caseHackSuffix = "~nix~case~hack~";
 
-PathFilter defaultPathFilter = [](const Path &) { return true; };
-
+PathFilter defaultPathFilter = [](const Path&) { return true; };
 
-static void dumpContents(const Path & path, size_t size,
-    Sink & sink)
-{
-    sink << "contents" << size;
+static void dumpContents(const Path& path, size_t size, Sink& sink) {
+  sink << "contents" << size;
 
-    AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
-    if (!fd) throw SysError(format("opening file '%1%'") % path);
+  AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (!fd) throw SysError(format("opening file '%1%'") % path);
 
-    std::vector<unsigned char> buf(65536);
-    size_t left = size;
+  std::vector<unsigned char> buf(65536);
+  size_t left = size;
 
-    while (left > 0) {
-        auto n = std::min(left, buf.size());
-        readFull(fd.get(), buf.data(), n);
-        left -= n;
-        sink(buf.data(), n);
-    }
+  while (left > 0) {
+    auto n = std::min(left, buf.size());
+    readFull(fd.get(), buf.data(), n);
+    left -= n;
+    sink(buf.data(), n);
+  }
 
-    writePadding(size, sink);
+  writePadding(size, sink);
 }
 
-
-static void dump(const Path & path, Sink & sink, PathFilter & filter)
-{
-    checkInterrupt();
-
-    struct stat st;
-    if (lstat(path.c_str(), &st))
-        throw SysError(format("getting attributes of path '%1%'") % path);
-
-    sink << "(";
-
-    if (S_ISREG(st.st_mode)) {
-        sink << "type" << "regular";
-        if (st.st_mode & S_IXUSR)
-            sink << "executable" << "";
-        dumpContents(path, (size_t) st.st_size, sink);
-    }
-
-    else if (S_ISDIR(st.st_mode)) {
-        sink << "type" << "directory";
-
-        /* If we're on a case-insensitive system like macOS, undo
-           the case hack applied by restorePath(). */
-        std::map<string, string> unhacked;
-        for (auto & i : readDirectory(path))
-            if (archiveSettings.useCaseHack) {
-                string name(i.name);
-                size_t pos = i.name.find(caseHackSuffix);
-                if (pos != string::npos) {
-                    debug(format("removing case hack suffix from '%1%'") % (path + "/" + i.name));
-                    name.erase(pos);
-                }
-                if (unhacked.find(name) != unhacked.end())
-                    throw Error(format("file name collision in between '%1%' and '%2%'")
-                        % (path + "/" + unhacked[name]) % (path + "/" + i.name));
-                unhacked[name] = i.name;
-            } else
-                unhacked[i.name] = i.name;
-
-        for (auto & i : unhacked)
-            if (filter(path + "/" + i.first)) {
-                sink << "entry" << "(" << "name" << i.first << "node";
-                dump(path + "/" + i.second, sink, filter);
-                sink << ")";
-            }
-    }
-
-    else if (S_ISLNK(st.st_mode))
-        sink << "type" << "symlink" << "target" << readLink(path);
-
-    else throw Error(format("file '%1%' has an unsupported type") % path);
-
-    sink << ")";
+static void dump(const Path& path, Sink& sink, PathFilter& filter) {
+  checkInterrupt();
+
+  struct stat st;
+  if (lstat(path.c_str(), &st))
+    throw SysError(format("getting attributes of path '%1%'") % path);
+
+  sink << "(";
+
+  if (S_ISREG(st.st_mode)) {
+    sink << "type"
+         << "regular";
+    if (st.st_mode & S_IXUSR)
+      sink << "executable"
+           << "";
+    dumpContents(path, (size_t)st.st_size, sink);
+  }
+
+  else if (S_ISDIR(st.st_mode)) {
+    sink << "type"
+         << "directory";
+
+    /* If we're on a case-insensitive system like macOS, undo
+       the case hack applied by restorePath(). */
+    std::map<string, string> unhacked;
+    for (auto& i : readDirectory(path))
+      if (archiveSettings.useCaseHack) {
+        string name(i.name);
+        size_t pos = i.name.find(caseHackSuffix);
+        if (pos != string::npos) {
+          debug(format("removing case hack suffix from '%1%'") %
+                (path + "/" + i.name));
+          name.erase(pos);
+        }
+        if (unhacked.find(name) != unhacked.end())
+          throw Error(format("file name collision in between '%1%' and '%2%'") %
+                      (path + "/" + unhacked[name]) % (path + "/" + i.name));
+        unhacked[name] = i.name;
+      } else
+        unhacked[i.name] = i.name;
+
+    for (auto& i : unhacked)
+      if (filter(path + "/" + i.first)) {
+        sink << "entry"
+             << "("
+             << "name" << i.first << "node";
+        dump(path + "/" + i.second, sink, filter);
+        sink << ")";
+      }
+  }
+
+  else if (S_ISLNK(st.st_mode))
+    sink << "type"
+         << "symlink"
+         << "target" << readLink(path);
+
+  else
+    throw Error(format("file '%1%' has an unsupported type") % path);
+
+  sink << ")";
 }
 
-
-void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
-{
-    sink << narVersionMagic1;
-    dump(path, sink, filter);
+void dumpPath(const Path& path, Sink& sink, PathFilter& filter) {
+  sink << narVersionMagic1;
+  dump(path, sink, filter);
 }
 
-
-void dumpString(const std::string & s, Sink & sink)
-{
-    sink << narVersionMagic1 << "(" << "type" << "regular" << "contents" << s << ")";
+void dumpString(const std::string& s, Sink& sink) {
+  sink << narVersionMagic1 << "("
+       << "type"
+       << "regular"
+       << "contents" << s << ")";
 }
 
-
-static SerialisationError badArchive(string s)
-{
-    return SerialisationError("bad archive: " + s);
+static SerialisationError badArchive(string s) {
+  return SerialisationError("bad archive: " + s);
 }
 
-
 #if 0
 static void skipGeneric(Source & source)
 {
@@ -146,233 +145,212 @@ static void skipGeneric(Source & source)
 }
 #endif
 
+static void parseContents(ParseSink& sink, Source& source, const Path& path) {
+  unsigned long long size = readLongLong(source);
 
-static void parseContents(ParseSink & sink, Source & source, const Path & path)
-{
-    unsigned long long size = readLongLong(source);
+  sink.preallocateContents(size);
 
-    sink.preallocateContents(size);
+  unsigned long long left = size;
+  std::vector<unsigned char> buf(65536);
 
-    unsigned long long left = size;
-    std::vector<unsigned char> buf(65536);
+  while (left) {
+    checkInterrupt();
+    auto n = buf.size();
+    if ((unsigned long long)n > left) n = left;
+    source(buf.data(), n);
+    sink.receiveContents(buf.data(), n);
+    left -= n;
+  }
+
+  readPadding(size, source);
+}
 
-    while (left) {
-        checkInterrupt();
-        auto n = buf.size();
-        if ((unsigned long long)n > left) n = left;
-        source(buf.data(), n);
-        sink.receiveContents(buf.data(), n);
-        left -= n;
-    }
+struct CaseInsensitiveCompare {
+  bool operator()(const string& a, const string& b) const {
+    return strcasecmp(a.c_str(), b.c_str()) < 0;
+  }
+};
 
-    readPadding(size, source);
-}
+static void parse(ParseSink& sink, Source& source, const Path& path) {
+  string s;
 
+  s = readString(source);
+  if (s != "(") throw badArchive("expected open tag");
 
-struct CaseInsensitiveCompare
-{
-    bool operator() (const string & a, const string & b) const
-    {
-        return strcasecmp(a.c_str(), b.c_str()) < 0;
-    }
-};
+  enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
 
+  std::map<Path, int, CaseInsensitiveCompare> names;
 
-static void parse(ParseSink & sink, Source & source, const Path & path)
-{
-    string s;
+  while (1) {
+    checkInterrupt();
 
     s = readString(source);
-    if (s != "(") throw badArchive("expected open tag");
 
-    enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
+    if (s == ")") {
+      break;
+    }
 
-    std::map<Path, int, CaseInsensitiveCompare> names;
+    else if (s == "type") {
+      if (type != tpUnknown) throw badArchive("multiple type fields");
+      string t = readString(source);
 
-    while (1) {
-        checkInterrupt();
+      if (t == "regular") {
+        type = tpRegular;
+        sink.createRegularFile(path);
+      }
 
-        s = readString(source);
+      else if (t == "directory") {
+        sink.createDirectory(path);
+        type = tpDirectory;
+      }
 
-        if (s == ")") {
-            break;
-        }
-
-        else if (s == "type") {
-            if (type != tpUnknown)
-                throw badArchive("multiple type fields");
-            string t = readString(source);
+      else if (t == "symlink") {
+        type = tpSymlink;
+      }
 
-            if (t == "regular") {
-                type = tpRegular;
-                sink.createRegularFile(path);
-            }
+      else
+        throw badArchive("unknown file type " + t);
 
-            else if (t == "directory") {
-                sink.createDirectory(path);
-                type = tpDirectory;
-            }
+    }
 
-            else if (t == "symlink") {
-                type = tpSymlink;
-            }
+    else if (s == "contents" && type == tpRegular) {
+      parseContents(sink, source, path);
+    }
 
-            else throw badArchive("unknown file type " + t);
+    else if (s == "executable" && type == tpRegular) {
+      auto s = readString(source);
+      if (s != "") throw badArchive("executable marker has non-empty value");
+      sink.isExecutable();
+    }
 
-        }
+    else if (s == "entry" && type == tpDirectory) {
+      string name, prevName;
 
-        else if (s == "contents" && type == tpRegular) {
-            parseContents(sink, source, path);
-        }
+      s = readString(source);
+      if (s != "(") throw badArchive("expected open tag");
 
-        else if (s == "executable" && type == tpRegular) {
-            auto s = readString(source);
-            if (s != "") throw badArchive("executable marker has non-empty value");
-            sink.isExecutable();
-        }
+      while (1) {
+        checkInterrupt();
 
-        else if (s == "entry" && type == tpDirectory) {
-            string name, prevName;
-
-            s = readString(source);
-            if (s != "(") throw badArchive("expected open tag");
-
-            while (1) {
-                checkInterrupt();
-
-                s = readString(source);
-
-                if (s == ")") {
-                    break;
-                } else if (s == "name") {
-                    name = readString(source);
-                    if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
-                        throw Error(format("NAR contains invalid file name '%1%'") % name);
-                    if (name <= prevName)
-                        throw Error("NAR directory is not sorted");
-                    prevName = name;
-                    if (archiveSettings.useCaseHack) {
-                        auto i = names.find(name);
-                        if (i != names.end()) {
-                            debug(format("case collision between '%1%' and '%2%'") % i->first % name);
-                            name += caseHackSuffix;
-                            name += std::to_string(++i->second);
-                        } else
-                            names[name] = 0;
-                    }
-                } else if (s == "node") {
-                    if (s.empty()) throw badArchive("entry name missing");
-                    parse(sink, source, path + "/" + name);
-                } else
-                    throw badArchive("unknown field " + s);
-            }
-        }
+        s = readString(source);
 
-        else if (s == "target" && type == tpSymlink) {
-            string target = readString(source);
-            sink.createSymlink(path, target);
-        }
+        if (s == ")") {
+          break;
+        } else if (s == "name") {
+          name = readString(source);
+          if (name.empty() || name == "." || name == ".." ||
+              name.find('/') != string::npos ||
+              name.find((char)0) != string::npos)
+            throw Error(format("NAR contains invalid file name '%1%'") % name);
+          if (name <= prevName) throw Error("NAR directory is not sorted");
+          prevName = name;
+          if (archiveSettings.useCaseHack) {
+            auto i = names.find(name);
+            if (i != names.end()) {
+              debug(format("case collision between '%1%' and '%2%'") %
+                    i->first % name);
+              name += caseHackSuffix;
+              name += std::to_string(++i->second);
+            } else
+              names[name] = 0;
+          }
+        } else if (s == "node") {
+          if (s.empty()) throw badArchive("entry name missing");
+          parse(sink, source, path + "/" + name);
+        } else
+          throw badArchive("unknown field " + s);
+      }
+    }
 
-        else
-            throw badArchive("unknown field " + s);
+    else if (s == "target" && type == tpSymlink) {
+      string target = readString(source);
+      sink.createSymlink(path, target);
     }
-}
 
+    else
+      throw badArchive("unknown field " + s);
+  }
+}
 
-void parseDump(ParseSink & sink, Source & source)
-{
-    string version;
-    try {
-        version = readString(source, narVersionMagic1.size());
-    } catch (SerialisationError & e) {
-        /* This generally means the integer at the start couldn't be
-           decoded.  Ignore and throw the exception below. */
-    }
-    if (version != narVersionMagic1)
-        throw badArchive("input doesn't look like a Nix archive");
-    parse(sink, source, "");
+void parseDump(ParseSink& sink, Source& source) {
+  string version;
+  try {
+    version = readString(source, narVersionMagic1.size());
+  } catch (SerialisationError& e) {
+    /* This generally means the integer at the start couldn't be
+       decoded.  Ignore and throw the exception below. */
+  }
+  if (version != narVersionMagic1)
+    throw badArchive("input doesn't look like a Nix archive");
+  parse(sink, source, "");
 }
 
+struct RestoreSink : ParseSink {
+  Path dstPath;
+  AutoCloseFD fd;
 
-struct RestoreSink : ParseSink
-{
-    Path dstPath;
-    AutoCloseFD fd;
-
-    void createDirectory(const Path & path)
-    {
-        Path p = dstPath + path;
-        if (mkdir(p.c_str(), 0777) == -1)
-            throw SysError(format("creating directory '%1%'") % p);
-    };
-
-    void createRegularFile(const Path & path)
-    {
-        Path p = dstPath + path;
-        fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
-        if (!fd) throw SysError(format("creating file '%1%'") % p);
-    }
+  void createDirectory(const Path& path) {
+    Path p = dstPath + path;
+    if (mkdir(p.c_str(), 0777) == -1)
+      throw SysError(format("creating directory '%1%'") % p);
+  };
 
-    void isExecutable()
-    {
-        struct stat st;
-        if (fstat(fd.get(), &st) == -1)
-            throw SysError("fstat");
-        if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
-            throw SysError("fchmod");
-    }
+  void createRegularFile(const Path& path) {
+    Path p = dstPath + path;
+    fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
+    if (!fd) throw SysError(format("creating file '%1%'") % p);
+  }
+
+  void isExecutable() {
+    struct stat st;
+    if (fstat(fd.get(), &st) == -1) throw SysError("fstat");
+    if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
+      throw SysError("fchmod");
+  }
 
-    void preallocateContents(unsigned long long len)
-    {
+  void preallocateContents(unsigned long long len) {
 #if HAVE_POSIX_FALLOCATE
-        if (len) {
-            errno = posix_fallocate(fd.get(), 0, len);
-            /* Note that EINVAL may indicate that the underlying
-               filesystem doesn't support preallocation (e.g. on
-               OpenSolaris).  Since preallocation is just an
-               optimisation, ignore it. */
-            if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS)
-                throw SysError(format("preallocating file of %1% bytes") % len);
-        }
-#endif
+    if (len) {
+      errno = posix_fallocate(fd.get(), 0, len);
+      /* Note that EINVAL may indicate that the underlying
+         filesystem doesn't support preallocation (e.g. on
+         OpenSolaris).  Since preallocation is just an
+         optimisation, ignore it. */
+      if (errno && errno != EINVAL && errno != EOPNOTSUPP && errno != ENOSYS)
+        throw SysError(format("preallocating file of %1% bytes") % len);
     }
+#endif
+  }
 
-    void receiveContents(unsigned char * data, unsigned int len)
-    {
-        writeFull(fd.get(), data, len);
-    }
+  void receiveContents(unsigned char* data, unsigned int len) {
+    writeFull(fd.get(), data, len);
+  }
 
-    void createSymlink(const Path & path, const string & target)
-    {
-        Path p = dstPath + path;
-        nix::createSymlink(target, p);
-    }
+  void createSymlink(const Path& path, const string& target) {
+    Path p = dstPath + path;
+    nix::createSymlink(target, p);
+  }
 };
 
-
-void restorePath(const Path & path, Source & source)
-{
-    RestoreSink sink;
-    sink.dstPath = path;
-    parseDump(sink, source);
+void restorePath(const Path& path, Source& source) {
+  RestoreSink sink;
+  sink.dstPath = path;
+  parseDump(sink, source);
 }
 
+void copyNAR(Source& source, Sink& sink) {
+  // FIXME: if 'source' is the output of dumpPath() followed by EOF,
+  // we should just forward all data directly without parsing.
 
-void copyNAR(Source & source, Sink & sink)
-{
-    // FIXME: if 'source' is the output of dumpPath() followed by EOF,
-    // we should just forward all data directly without parsing.
-
-    ParseSink parseSink; /* null sink; just parse the NAR */
+  ParseSink parseSink; /* null sink; just parse the NAR */
 
-    LambdaSource wrapper([&](unsigned char * data, size_t len) {
-        auto n = source.read(data, len);
-        sink(data, n);
-        return n;
-    });
+  LambdaSource wrapper([&](unsigned char* data, size_t len) {
+    auto n = source.read(data, len);
+    sink(data, n);
+    return n;
+  });
 
-    parseDump(parseSink, wrapper);
+  parseDump(parseSink, wrapper);
 }
 
-
-}
+}  // namespace nix