1 : #include <cerrno>
2 : #include <algorithm>
3 : #include <vector>
4 :
5 : #include <sys/types.h>
6 : #include <sys/stat.h>
7 : #include <unistd.h>
8 : #include <dirent.h>
9 : #include <fcntl.h>
10 :
11 : #include "archive.hh"
12 : #include "util.hh"
13 :
14 :
15 : namespace nix {
16 :
17 :
18 345 : static string archiveVersion1 = "nix-archive-1";
19 :
20 :
21 : static void writePadding(unsigned int len, DumpSink & sink)
22 21448 : {
23 21448 : if (len % 8) {
24 15997 : unsigned char zero[8];
25 15997 : memset(zero, 0, sizeof(zero));
26 15997 : sink(zero, 8 - (len % 8));
27 : }
28 : }
29 :
30 :
31 : static void writeInt(unsigned int n, DumpSink & sink)
32 21448 : {
33 21448 : unsigned char buf[8];
34 21448 : memset(buf, 0, sizeof(buf));
35 21448 : buf[0] = n & 0xff;
36 21448 : buf[1] = (n >> 8) & 0xff;
37 21448 : buf[2] = (n >> 16) & 0xff;
38 21448 : buf[3] = (n >> 24) & 0xff;
39 21448 : sink(buf, sizeof(buf));
40 : }
41 :
42 :
43 : static void writeString(const string & s, DumpSink & sink)
44 18597 : {
45 18597 : unsigned int len = s.length();
46 18597 : writeInt(len, sink);
47 18597 : sink((const unsigned char *) s.c_str(), len);
48 18597 : writePadding(len, sink);
49 : }
50 :
51 :
52 : static void dump(const string & path, DumpSink & sink);
53 :
54 :
55 : static void dumpEntries(const Path & path, DumpSink & sink)
56 80 : {
57 80 : Strings names = readDirectory(path);
58 80 : vector<string> names2(names.begin(), names.end());
59 80 : sort(names2.begin(), names2.end());
60 :
61 215 : for (vector<string>::iterator it = names2.begin();
62 : it != names2.end(); it++)
63 : {
64 135 : writeString("entry", sink);
65 135 : writeString("(", sink);
66 135 : writeString("name", sink);
67 135 : writeString(*it, sink);
68 135 : writeString("node", sink);
69 135 : dump(path + "/" + *it, sink);
70 135 : writeString(")", sink);
71 : }
72 : }
73 :
74 :
75 : static void dumpContents(const Path & path, unsigned int size,
76 : DumpSink & sink)
77 2851 : {
78 2851 : writeString("contents", sink);
79 2851 : writeInt(size, sink);
80 :
81 2851 : AutoCloseFD fd = open(path.c_str(), O_RDONLY);
82 2851 : if (fd == -1) throw SysError(format("opening file `%1%'") % path);
83 :
84 2851 : unsigned char buf[65536];
85 2851 : unsigned int left = size;
86 :
87 3201 : while (left > 0) {
88 350 : size_t n = left > sizeof(buf) ? sizeof(buf) : left;
89 350 : readFull(fd, buf, n);
90 350 : left -= n;
91 350 : sink(buf, n);
92 : }
93 :
94 2851 : writePadding(size, sink);
95 : }
96 :
97 :
98 : static void dump(const Path & path, DumpSink & sink)
99 2981 : {
100 2981 : struct stat st;
101 2981 : if (lstat(path.c_str(), &st))
102 1 : throw SysError(format("getting attributes of path `%1%'") % path);
103 :
104 2980 : writeString("(", sink);
105 :
106 2980 : if (S_ISREG(st.st_mode)) {
107 2851 : writeString("type", sink);
108 2851 : writeString("regular", sink);
109 2851 : if (st.st_mode & S_IXUSR) {
110 36 : writeString("executable", sink);
111 36 : writeString("", sink);
112 : }
113 2851 : dumpContents(path, st.st_size, sink);
114 : }
115 :
116 129 : else if (S_ISDIR(st.st_mode)) {
117 80 : writeString("type", sink);
118 80 : writeString("directory", sink);
119 80 : dumpEntries(path, sink);
120 : }
121 :
122 49 : else if (S_ISLNK(st.st_mode)) {
123 49 : writeString("type", sink);
124 49 : writeString("symlink", sink);
125 49 : writeString("target", sink);
126 49 : writeString(readLink(path), sink);
127 : }
128 :
129 0 : else throw Error("unknown file type: " + path);
130 :
131 2980 : writeString(")", sink);
132 : }
133 :
134 :
135 : void dumpPath(const Path & path, DumpSink & sink)
136 2846 : {
137 2846 : writeString(archiveVersion1, sink);
138 2846 : dump(path, sink);
139 : }
140 :
141 :
142 : static Error badArchive(string s)
143 0 : {
144 0 : return Error("bad archive: " + s);
145 : }
146 :
147 :
148 : static void readPadding(unsigned int len, RestoreSource & source)
149 534 : {
150 534 : if (len % 8) {
151 468 : unsigned char zero[8];
152 468 : unsigned int n = 8 - (len % 8);
153 468 : source(zero, n);
154 2495 : for (unsigned int i = 0; i < n; i++)
155 2027 : if (zero[i]) throw badArchive("non-zero padding");
156 : }
157 : }
158 :
159 : static unsigned int readInt(RestoreSource & source)
160 534 : {
161 534 : unsigned char buf[8];
162 534 : source(buf, sizeof(buf));
163 534 : if (buf[4] || buf[5] || buf[6] || buf[7])
164 0 : throw Error("implementation cannot deal with > 32-bit integers");
165 534 : return
166 : buf[0] |
167 : (buf[1] << 8) |
168 : (buf[2] << 16) |
169 : (buf[3] << 24);
170 : }
171 :
172 :
173 : static string readString(RestoreSource & source)
174 478 : {
175 478 : unsigned int len = readInt(source);
176 478 : char buf[len];
177 478 : source((unsigned char *) buf, len);
178 478 : readPadding(len, source);
179 478 : return string(buf, len);
180 : }
181 :
182 :
183 : static void skipGeneric(RestoreSource & source)
184 0 : {
185 0 : if (readString(source) == "(") {
186 0 : while (readString(source) != ")")
187 0 : skipGeneric(source);
188 : }
189 : }
190 :
191 :
192 : static void restore(const Path & path, RestoreSource & source);
193 :
194 :
195 : static void restoreEntry(const Path & path, RestoreSource & source)
196 12 : {
197 12 : string s, name;
198 :
199 12 : s = readString(source);
200 12 : if (s != "(") throw badArchive("expected open tag");
201 :
202 36 : while (1) {
203 36 : checkInterrupt();
204 :
205 36 : s = readString(source);
206 :
207 36 : if (s == ")") {
208 12 : break;
209 24 : } else if (s == "name") {
210 12 : name = readString(source);
211 12 : } else if (s == "node") {
212 12 : if (s == "") throw badArchive("entry name missing");
213 12 : restore(path + "/" + name, source);
214 : } else {
215 0 : throw badArchive("unknown field " + s);
216 0 : skipGeneric(source);
217 : }
218 : }
219 : }
220 :
221 :
222 : static void restoreContents(int fd, const Path & path, RestoreSource & source)
223 56 : {
224 56 : unsigned int size = readInt(source);
225 56 : unsigned int left = size;
226 112 : unsigned char buf[65536];
227 :
228 112 : while (left) {
229 56 : checkInterrupt();
230 56 : unsigned int n = sizeof(buf);
231 56 : if (n > left) n = left;
232 56 : source(buf, n);
233 56 : writeFull(fd, buf, n);
234 56 : left -= n;
235 : }
236 :
237 56 : readPadding(size, source);
238 : }
239 :
240 :
241 : static void restore(const Path & path, RestoreSource & source)
242 68 : {
243 68 : string s;
244 :
245 68 : s = readString(source);
246 68 : if (s != "(") throw badArchive("expected open tag");
247 :
248 68 : enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
249 68 : AutoCloseFD fd;
250 :
251 215 : while (1) {
252 215 : checkInterrupt();
253 :
254 215 : s = readString(source);
255 :
256 215 : if (s == ")") {
257 68 : break;
258 : }
259 :
260 147 : else if (s == "type") {
261 68 : if (type != tpUnknown)
262 0 : throw badArchive("multiple type fields");
263 68 : string t = readString(source);
264 :
265 68 : if (t == "regular") {
266 56 : type = tpRegular;
267 56 : fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
268 56 : if (fd == -1)
269 0 : throw SysError("creating file " + path);
270 : }
271 :
272 12 : else if (t == "directory") {
273 6 : type = tpDirectory;
274 6 : if (mkdir(path.c_str(), 0777) == -1)
275 0 : throw SysError("creating directory " + path);
276 : }
277 :
278 6 : else if (t == "symlink") {
279 6 : type = tpSymlink;
280 : }
281 :
282 0 : else throw badArchive("unknown file type " + t);
283 :
284 : }
285 :
286 79 : else if (s == "contents" && type == tpRegular) {
287 56 : restoreContents(fd, path, source);
288 : }
289 :
290 23 : else if (s == "executable" && type == tpRegular) {
291 5 : readString(source);
292 5 : struct stat st;
293 5 : if (fstat(fd, &st) == -1)
294 0 : throw SysError("fstat");
295 5 : if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
296 0 : throw SysError("fchmod");
297 : }
298 :
299 18 : else if (s == "entry" && type == tpDirectory) {
300 12 : restoreEntry(path, source);
301 : }
302 :
303 6 : else if (s == "target" && type == tpSymlink) {
304 6 : string target = readString(source);
305 6 : if (symlink(target.c_str(), path.c_str()) == -1)
306 0 : throw SysError("creating symlink " + path);
307 : }
308 :
309 : else {
310 0 : throw badArchive("unknown field " + s);
311 0 : skipGeneric(source);
312 : }
313 :
314 : }
315 : }
316 :
317 :
318 : void restorePath(const Path & path, RestoreSource & source)
319 56 : {
320 56 : if (readString(source) != archiveVersion1)
321 0 : throw badArchive("expected Nix archive");
322 56 : restore(path, source);
323 : }
324 :
325 :
326 345 : }
|