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 1151 : static string archiveVersion1 = "nix-archive-1";
19 :
20 :
21 1151 : PathFilter defaultPathFilter;
22 :
23 :
24 : static void dump(const string & path, Sink & sink, PathFilter & filter);
25 :
26 :
27 158 : static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
28 : {
29 158 : Strings names = readDirectory(path);
30 158 : vector<string> names2(names.begin(), names.end());
31 316 : sort(names2.begin(), names2.end());
32 :
33 420 : for (vector<string>::iterator i = names2.begin();
34 : i != names2.end(); ++i)
35 : {
36 262 : Path entry = path + "/" + *i;
37 262 : if (filter(entry)) {
38 256 : writeString("entry", sink);
39 512 : writeString("(", sink);
40 512 : writeString("name", sink);
41 256 : writeString(*i, sink);
42 256 : writeString("node", sink);
43 256 : dump(entry, sink, filter);
44 256 : writeString(")", sink);
45 : }
46 158 : }
47 158 : }
48 :
49 :
50 : static void dumpContents(const Path & path, unsigned int size,
51 5657 : Sink & sink)
52 : {
53 5657 : writeString("contents", sink);
54 5657 : writeInt(size, sink);
55 :
56 5657 : AutoCloseFD fd = open(path.c_str(), O_RDONLY);
57 11314 : if (fd == -1) throw SysError(format("opening file `%1%'") % path);
58 :
59 : unsigned char buf[65536];
60 5657 : unsigned int left = size;
61 :
62 11961 : while (left > 0) {
63 647 : size_t n = left > sizeof(buf) ? sizeof(buf) : left;
64 647 : readFull(fd, buf, n);
65 647 : left -= n;
66 647 : sink(buf, n);
67 : }
68 :
69 5657 : writePadding(size, sink);
70 5657 : }
71 :
72 :
73 5929 : static void dump(const Path & path, Sink & sink, PathFilter & filter)
74 : {
75 : struct stat st;
76 5929 : if (lstat(path.c_str(), &st))
77 0 : throw SysError(format("getting attributes of path `%1%'") % path);
78 :
79 5929 : writeString("(", sink);
80 :
81 5929 : if (S_ISREG(st.st_mode)) {
82 5657 : writeString("type", sink);
83 11314 : writeString("regular", sink);
84 5657 : if (st.st_mode & S_IXUSR) {
85 114 : writeString("executable", sink);
86 228 : writeString("", sink);
87 : }
88 5657 : dumpContents(path, st.st_size, sink);
89 : }
90 :
91 272 : else if (S_ISDIR(st.st_mode)) {
92 158 : writeString("type", sink);
93 316 : writeString("directory", sink);
94 158 : dumpEntries(path, sink, filter);
95 : }
96 :
97 114 : else if (S_ISLNK(st.st_mode)) {
98 114 : writeString("type", sink);
99 228 : writeString("symlink", sink);
100 228 : writeString("target", sink);
101 228 : writeString(readLink(path), sink);
102 : }
103 :
104 0 : else throw Error("unknown file type: " + path);
105 :
106 5929 : writeString(")", sink);
107 5929 : }
108 :
109 :
110 5673 : void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
111 : {
112 5673 : writeString(archiveVersion1, sink);
113 5673 : dump(path, sink, filter);
114 5673 : }
115 :
116 :
117 0 : static Error badArchive(string s)
118 : {
119 0 : return Error("bad archive: " + s);
120 : }
121 :
122 :
123 0 : static void skipGeneric(Source & source)
124 : {
125 0 : if (readString(source) == "(") {
126 0 : while (readString(source) != ")")
127 0 : skipGeneric(source);
128 : }
129 0 : }
130 :
131 :
132 : static void restore(const Path & path, Source & source);
133 :
134 :
135 26 : static void restoreEntry(const Path & path, Source & source)
136 : {
137 26 : string s, name;
138 :
139 26 : s = readString(source);
140 26 : if (s != "(") throw badArchive("expected open tag");
141 :
142 52 : while (1) {
143 78 : checkInterrupt();
144 :
145 78 : s = readString(source);
146 :
147 78 : if (s == ")") {
148 26 : break;
149 52 : } else if (s == "name") {
150 26 : name = readString(source);
151 26 : } else if (s == "node") {
152 26 : if (s == "") throw badArchive("entry name missing");
153 26 : restore(path + "/" + name, source);
154 : } else {
155 0 : throw badArchive("unknown field " + s);
156 : skipGeneric(source);
157 : }
158 26 : }
159 26 : }
160 :
161 :
162 133 : static void restoreContents(int fd, const Path & path, Source & source)
163 : {
164 133 : unsigned int size = readInt(source);
165 133 : unsigned int left = size;
166 : unsigned char buf[65536];
167 :
168 396 : while (left) {
169 130 : checkInterrupt();
170 130 : unsigned int n = sizeof(buf);
171 130 : if (n > left) n = left;
172 130 : source(buf, n);
173 130 : writeFull(fd, buf, n);
174 130 : left -= n;
175 : }
176 :
177 133 : readPadding(size, source);
178 133 : }
179 :
180 :
181 157 : static void restore(const Path & path, Source & source)
182 : {
183 157 : string s;
184 :
185 157 : s = readString(source);
186 157 : if (s != "(") throw badArchive("expected open tag");
187 :
188 157 : enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
189 157 : AutoCloseFD fd;
190 :
191 365 : while (1) {
192 522 : checkInterrupt();
193 :
194 522 : s = readString(source);
195 :
196 522 : if (s == ")") {
197 157 : break;
198 : }
199 :
200 365 : else if (s == "type") {
201 157 : if (type != tpUnknown)
202 0 : throw badArchive("multiple type fields");
203 157 : string t = readString(source);
204 :
205 157 : if (t == "regular") {
206 133 : type = tpRegular;
207 133 : fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
208 133 : if (fd == -1)
209 0 : throw SysError("creating file " + path);
210 : }
211 :
212 24 : else if (t == "directory") {
213 12 : type = tpDirectory;
214 12 : if (mkdir(path.c_str(), 0777) == -1)
215 0 : throw SysError("creating directory " + path);
216 : }
217 :
218 12 : else if (t == "symlink") {
219 12 : type = tpSymlink;
220 : }
221 :
222 0 : else throw badArchive("unknown file type " + t);
223 :
224 : }
225 :
226 208 : else if (s == "contents" && type == tpRegular) {
227 133 : restoreContents(fd, path, source);
228 : }
229 :
230 75 : else if (s == "executable" && type == tpRegular) {
231 37 : readString(source);
232 : struct stat st;
233 37 : if (fstat(fd, &st) == -1)
234 0 : throw SysError("fstat");
235 37 : if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
236 0 : throw SysError("fchmod");
237 : }
238 :
239 38 : else if (s == "entry" && type == tpDirectory) {
240 26 : restoreEntry(path, source);
241 : }
242 :
243 12 : else if (s == "target" && type == tpSymlink) {
244 12 : string target = readString(source);
245 12 : if (symlink(target.c_str(), path.c_str()) == -1)
246 0 : throw SysError("creating symlink " + path);
247 : }
248 :
249 : else {
250 0 : throw badArchive("unknown field " + s);
251 : skipGeneric(source);
252 : }
253 :
254 157 : }
255 157 : }
256 :
257 :
258 131 : void restorePath(const Path & path, Source & source)
259 : {
260 131 : if (readString(source) != archiveVersion1)
261 0 : throw badArchive("expected Nix archive");
262 131 : restore(path, source);
263 131 : }
264 :
265 0 :
266 1106 : }
|