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