+#define _XOPEN_SOURCE 600
+
#include "config.h"
#include <cerrno>
#include <algorithm>
#include <vector>
+#include <map>
+
+#include <strings.h> // for strcasecmp
-#define _XOPEN_SOURCE 600
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace nix {
-
static string archiveVersion1 = "nix-archive-1";
+static string caseHackSuffix = "~nix~case~hack~";
PathFilter defaultPathFilter;
-static void dump(const string & path, Sink & sink, PathFilter & filter);
-
-
-static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
-{
- Strings names = readDirectory(path);
- vector<string> names2(names.begin(), names.end());
- sort(names2.begin(), names2.end());
-
- for (vector<string>::iterator i = names2.begin();
- i != names2.end(); ++i)
- {
- Path entry = path + "/" + *i;
- if (filter(entry)) {
- writeString("entry", sink);
- writeString("(", sink);
- writeString("name", sink);
- writeString(*i, sink);
- writeString("node", sink);
- dump(entry, sink, filter);
- writeString(")", sink);
- }
- }
-}
-
-
-static void dumpContents(const Path & path, size_t size,
+static void dumpContents(const Path & path, size_t size,
Sink & sink)
{
writeString("contents", sink);
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
-
+
unsigned char buf[65536];
size_t left = size;
writeString("", sink);
}
dumpContents(path, (size_t) st.st_size, sink);
- }
+ }
else if (S_ISDIR(st.st_mode)) {
writeString("type", sink);
writeString("directory", sink);
- dumpEntries(path, sink, filter);
+
+ /* If we're on a case-insensitive system like Mac OS X, undo
+ the case hack applied by restorePath(). */
+ std::map<string, string> unhacked;
+ for (auto & i : readDirectory(path))
+ unhacked[i.name] = i.name;
+
+ for (auto & i : unhacked)
+ if (filter(path + "/" + i.first)) {
+ writeString("entry", sink);
+ writeString("(", sink);
+ writeString("name", sink);
+ writeString(i.first, sink);
+ writeString("node", sink);
+ dump(path + "/" + i.second, sink, filter);
+ writeString(")", sink);
+ }
}
else if (S_ISLNK(st.st_mode)) {
}
+#if 0
static void skipGeneric(Source & source)
{
if (readString(source) == "(") {
skipGeneric(source);
}
}
-
-
-static void parse(ParseSink & sink, Source & source, const Path & path);
-
-
-
-static void parseEntry(ParseSink & sink, Source & source, const Path & path)
-{
- string s, name;
-
- 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);
- } else if (s == "node") {
- if (s == "") throw badArchive("entry name missing");
- parse(sink, source, path + "/" + name);
- } else {
- throw badArchive("unknown field " + s);
- skipGeneric(source);
- }
- }
-}
+#endif
static void parseContents(ParseSink & sink, Source & source, const Path & path)
{
unsigned long long size = readLongLong(source);
-
+
sink.preallocateContents(size);
unsigned long long left = size;
}
+struct CaseInsensitiveCompare
+{
+ bool operator() (const string & a, const string & b) const
+ {
+ return strcasecmp(a.c_str(), b.c_str()) < 0;
+ }
+};
+
+
static void parse(ParseSink & sink, Source & source, const Path & path)
{
string s;
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
+ std::map<Path, int, CaseInsensitiveCompare> names;
+
while (1) {
checkInterrupt();
else if (t == "symlink") {
type = tpSymlink;
}
-
+
else throw badArchive("unknown file type " + t);
-
+
}
else if (s == "contents" && type == tpRegular) {
}
else if (s == "entry" && type == tpDirectory) {
- parseEntry(sink, source, path);
+ 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;
+ } else if (s == "node") {
+ if (s.empty()) throw badArchive("entry name missing");
+ parse(sink, source, path + "/" + name);
+ } else
+ throw badArchive("unknown field " + s);
+ }
}
else if (s == "target" && type == tpSymlink) {
sink.createSymlink(path, target);
}
- else {
+ else
throw badArchive("unknown field " + s);
- skipGeneric(source);
- }
}
}
void parseDump(ParseSink & sink, Source & source)
{
- string version;
+ string version;
try {
version = readString(source);
} catch (SerialisationError & e) {
decoded. Ignore and throw the exception below. */
}
if (version != archiveVersion1)
- throw badArchive("input doesn't look like a Nix archive");
+ throw badArchive("input doesn't look like a normalized archive");
parse(sink, source, "");
}
}
};
-
+
void restorePath(const Path & path, Source & source)
{
RestoreSink sink;
parseDump(sink, source);
}
-
+
}