1 #define _XOPEN_SOURCE 600
10 #include <strings.h> // for strcasecmp
12 #include <sys/types.h>
24 static string archiveVersion1
= "nix-archive-1";
26 static string caseHackSuffix
= "~nix~case~hack~";
28 PathFilter defaultPathFilter
;
31 static void dumpContents(const Path
& path
, size_t size
,
34 writeString("contents", sink
);
35 writeLongLong(size
, sink
);
37 AutoCloseFD fd
= open(path
.c_str(), O_RDONLY
);
38 if (fd
== -1) throw SysError(format("opening file `%1%'") % path
);
40 unsigned char buf
[65536];
44 size_t n
= left
> sizeof(buf
) ? sizeof(buf
) : left
;
50 writePadding(size
, sink
);
54 static void dump(const Path
& path
, Sink
& sink
, PathFilter
& filter
)
57 if (lstat(path
.c_str(), &st
))
58 throw SysError(format("getting attributes of path `%1%'") % path
);
60 writeString("(", sink
);
62 if (S_ISREG(st
.st_mode
)) {
63 writeString("type", sink
);
64 writeString("regular", sink
);
65 if (st
.st_mode
& S_IXUSR
) {
66 writeString("executable", sink
);
67 writeString("", sink
);
69 dumpContents(path
, (size_t) st
.st_size
, sink
);
72 else if (S_ISDIR(st
.st_mode
)) {
73 writeString("type", sink
);
74 writeString("directory", sink
);
76 /* If we're on a case-insensitive system like Mac OS X, undo
77 the case hack applied by restorePath(). */
78 std::map
<string
, string
> unhacked
;
79 for (auto & i
: readDirectory(path
))
80 unhacked
[i
.name
] = i
.name
;
82 for (auto & i
: unhacked
)
83 if (filter(path
+ "/" + i
.first
)) {
84 writeString("entry", sink
);
85 writeString("(", sink
);
86 writeString("name", sink
);
87 writeString(i
.first
, sink
);
88 writeString("node", sink
);
89 dump(path
+ "/" + i
.second
, sink
, filter
);
90 writeString(")", sink
);
94 else if (S_ISLNK(st
.st_mode
)) {
95 writeString("type", sink
);
96 writeString("symlink", sink
);
97 writeString("target", sink
);
98 writeString(readLink(path
), sink
);
101 else throw Error(format("file `%1%' has an unsupported type") % path
);
103 writeString(")", sink
);
107 void dumpPath(const Path
& path
, Sink
& sink
, PathFilter
& filter
)
109 writeString(archiveVersion1
, sink
);
110 dump(path
, sink
, filter
);
114 static SerialisationError
badArchive(string s
)
116 return SerialisationError("bad archive: " + s
);
121 static void skipGeneric(Source
& source
)
123 if (readString(source
) == "(") {
124 while (readString(source
) != ")")
131 static void parseContents(ParseSink
& sink
, Source
& source
, const Path
& path
)
133 unsigned long long size
= readLongLong(source
);
135 sink
.preallocateContents(size
);
137 unsigned long long left
= size
;
138 unsigned char buf
[65536];
142 unsigned int n
= sizeof(buf
);
143 if ((unsigned long long) n
> left
) n
= left
;
145 sink
.receiveContents(buf
, n
);
149 readPadding(size
, source
);
153 struct CaseInsensitiveCompare
155 bool operator() (const string
& a
, const string
& b
) const
157 return strcasecmp(a
.c_str(), b
.c_str()) < 0;
162 static void parse(ParseSink
& sink
, Source
& source
, const Path
& path
)
166 s
= readString(source
);
167 if (s
!= "(") throw badArchive("expected open tag");
169 enum { tpUnknown
, tpRegular
, tpDirectory
, tpSymlink
} type
= tpUnknown
;
171 std::map
<Path
, int, CaseInsensitiveCompare
> names
;
176 s
= readString(source
);
182 else if (s
== "type") {
183 if (type
!= tpUnknown
)
184 throw badArchive("multiple type fields");
185 string t
= readString(source
);
187 if (t
== "regular") {
189 sink
.createRegularFile(path
);
192 else if (t
== "directory") {
193 sink
.createDirectory(path
);
197 else if (t
== "symlink") {
201 else throw badArchive("unknown file type " + t
);
205 else if (s
== "contents" && type
== tpRegular
) {
206 parseContents(sink
, source
, path
);
209 else if (s
== "executable" && type
== tpRegular
) {
214 else if (s
== "entry" && type
== tpDirectory
) {
215 string name
, prevName
;
217 s
= readString(source
);
218 if (s
!= "(") throw badArchive("expected open tag");
223 s
= readString(source
);
227 } else if (s
== "name") {
228 name
= readString(source
);
229 if (name
.empty() || name
== "." || name
== ".." || name
.find('/') != string::npos
|| name
.find((char) 0) != string::npos
)
230 throw Error(format("NAR contains invalid file name `%1%'") % name
);
231 if (name
<= prevName
)
232 throw Error("NAR directory is not sorted");
234 } else if (s
== "node") {
235 if (s
.empty()) throw badArchive("entry name missing");
236 parse(sink
, source
, path
+ "/" + name
);
238 throw badArchive("unknown field " + s
);
242 else if (s
== "target" && type
== tpSymlink
) {
243 string target
= readString(source
);
244 sink
.createSymlink(path
, target
);
248 throw badArchive("unknown field " + s
);
253 void parseDump(ParseSink
& sink
, Source
& source
)
257 version
= readString(source
);
258 } catch (SerialisationError
& e
) {
259 /* This generally means the integer at the start couldn't be
260 decoded. Ignore and throw the exception below. */
262 if (version
!= archiveVersion1
)
263 throw badArchive("input doesn't look like a normalized archive");
264 parse(sink
, source
, "");
268 struct RestoreSink
: ParseSink
273 void createDirectory(const Path
& path
)
275 Path p
= dstPath
+ path
;
276 if (mkdir(p
.c_str(), 0777) == -1)
277 throw SysError(format("creating directory `%1%'") % p
);
280 void createRegularFile(const Path
& path
)
282 Path p
= dstPath
+ path
;
284 fd
= open(p
.c_str(), O_CREAT
| O_EXCL
| O_WRONLY
, 0666);
285 if (fd
== -1) throw SysError(format("creating file `%1%'") % p
);
291 if (fstat(fd
, &st
) == -1)
292 throw SysError("fstat");
293 if (fchmod(fd
, st
.st_mode
| (S_IXUSR
| S_IXGRP
| S_IXOTH
)) == -1)
294 throw SysError("fchmod");
297 void preallocateContents(unsigned long long len
)
299 #if HAVE_POSIX_FALLOCATE
301 errno
= posix_fallocate(fd
, 0, len
);
302 /* Note that EINVAL may indicate that the underlying
303 filesystem doesn't support preallocation (e.g. on
304 OpenSolaris). Since preallocation is just an
305 optimisation, ignore it. */
306 if (errno
&& errno
!= EINVAL
)
307 throw SysError(format("preallocating file of %1% bytes") % len
);
312 void receiveContents(unsigned char * data
, unsigned int len
)
314 writeFull(fd
, data
, len
);
317 void createSymlink(const Path
& path
, const string
& target
)
319 Path p
= dstPath
+ path
;
320 nix::createSymlink(target
, p
);
325 void restorePath(const Path
& path
, Source
& source
)
329 parseDump(sink
, source
);