1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <flate.h>
5 #include <mp.h>
6 #include <libsec.h>
7 #include "paqfs.h"
8
9 enum {
10 OffsetSize = 4, /* size of block offset */
11 };
12
13 void paqfs(char *root, char *label);
14 PaqDir *paqFile(char *name, Dir *dir);
15 PaqDir *paqDir(char *name, Dir *dir);
16 PaqDir *paqDirAlloc(Dir *d, ulong offset);
17 void paqDirFree(PaqDir *pd);
18 void writeHeader(char *label);
19 void writeTrailer(ulong root);
20 ulong writeBlock(uchar *buf, int type);
21 void usage(void);
22 void outWrite(void *buf, int n);
23 int paqDirSize(PaqDir *dir);
24 void putDir(uchar *p, PaqDir *dir);
25 void putHeader(uchar *p, PaqHeader *h);
26 void putBlock(uchar *p, PaqBlock *h);
27 void putTrailer(uchar *p, PaqTrailer *t);
28 void putl(uchar *p, ulong v);
29 void puts(uchar *p, int x);
30 uchar *putstr(uchar *p, char *s);
31 void *emallocz(int size);
32 void warn(char *fmt, ...);
33
34 int uflag=0; /* uncompressed */
35 long blocksize = 4*1024;
36
37 Biobuf *out;
38 DigestState *outdg;
39
40 void
main(int argc,char * argv[])41 main(int argc, char *argv[])
42 {
43 char *s, *ss;
44 char *outfile = nil;
45 char *label = nil;
46 char *file;
47
48 ARGBEGIN {
49 case 'u':
50 uflag=1;
51 break;
52 case 'o':
53 outfile = ARGF();
54 break;
55 case 'l':
56 label = ARGF();
57 if(label == nil)
58 usage();
59 break;
60 case 'b':
61 s = ARGF();
62 if(s) {
63 blocksize = strtoul(s, &ss, 0);
64 if(s == ss)
65 usage();
66 if(*ss == 'k')
67 blocksize *= 1024;
68 }
69 if(blocksize < MinBlockSize)
70 sysfatal("blocksize too small: must be at lease %d", MinBlockSize);
71 if(blocksize > MaxBlockSize)
72 sysfatal("blocksize too large: must be no greater than %d", MaxBlockSize);
73 break;
74 } ARGEND
75
76 if(outfile == nil) {
77 out = emallocz(sizeof(Biobuf));
78 Binit(out, 1, OWRITE);
79 } else {
80 out = Bopen(outfile, OWRITE|OTRUNC);
81 if(out == nil)
82 sysfatal("could not create file: %s: %r", outfile);
83 }
84
85 deflateinit();
86
87 file = argv[0];
88 if(file == nil)
89 file = ".";
90
91 if(label == nil) {
92 if(strrchr(file, '/'))
93 label = strrchr(file, '/') + 1;
94 else
95 label = file;
96 }
97
98 paqfs(file, label);
99
100 Bterm(out);
101
102 exits(0);
103 }
104
105 void
usage(void)106 usage(void)
107 {
108 fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0);
109 exits("usage");
110 }
111
112 void
paqfs(char * root,char * label)113 paqfs(char *root, char *label)
114 {
115 Dir *dir;
116 PaqDir *pd;
117 ulong offset;
118 uchar *buf;
119
120 dir = dirstat(root);
121 if(dir == nil)
122 sysfatal("could not stat root: %s: %r", root);
123 writeHeader(label);
124 if(dir->mode & DMDIR)
125 pd = paqDir(root, dir);
126 else
127 pd = paqFile(root, dir);
128 buf = emallocz(blocksize);
129 putDir(buf, pd);
130 offset = writeBlock(buf, DirBlock);
131 writeTrailer(offset);
132 paqDirFree(pd);
133 free(dir);
134 }
135
136
137 PaqDir *
paqFile(char * name,Dir * dir)138 paqFile(char *name, Dir *dir)
139 {
140 int fd, n, nn, nb;
141 vlong tot;
142 uchar *block, *pointer;
143 ulong offset;
144
145 fd = open(name, OREAD);
146 if(fd < 0) {
147 warn("could not open file: %s: %r", name);
148 return nil;
149 }
150
151 block = emallocz(blocksize);
152 pointer = emallocz(blocksize);
153 nb = 0;
154 n = 0;
155 tot = 0;
156 for(;;) {
157 nn = read(fd, block+n, blocksize-n);
158 if(nn < 0) {
159 warn("read failed: %s: %r", name);
160 goto Err;
161 }
162 tot += nn;
163 if(nn == 0) {
164 if(n == 0)
165 break;
166 /* pad out last block */
167 memset(block+n, 0, blocksize-n);
168 nn = blocksize - n;
169 }
170 n += nn;
171 if(n < blocksize)
172 continue;
173 if(nb >= blocksize/OffsetSize) {
174 warn("file too big for blocksize: %s", name);
175 goto Err;
176 }
177 offset = writeBlock(block, DataBlock);
178 putl(pointer+nb*OffsetSize, offset);
179 nb++;
180 n = 0;
181 }
182
183 offset = writeBlock(pointer, PointerBlock);
184
185 close(fd);
186 free(pointer);
187 free(block);
188 dir->length = tot;
189 return paqDirAlloc(dir, offset);
190 Err:
191 close(fd);
192 free(pointer);
193 free(block);
194 return nil;
195 }
196
197 PaqDir *
paqDir(char * name,Dir * dir)198 paqDir(char *name, Dir *dir)
199 {
200 Dir *dirs, *p;
201 PaqDir *pd;
202 int i, n, nb, fd, ndir;
203 uchar *block, *pointer;
204 char *nname;
205 ulong offset;
206
207 fd = open(name, OREAD);
208 if(fd < 0) {
209 warn("could not open directory: %s: %r", name);
210 return nil;
211 }
212
213 ndir = dirreadall(fd, &dirs);
214 close(fd);
215
216 if(ndir < 0) {
217 warn("could not read directory: %s: %r", name);
218 return nil;
219 }
220
221 block = emallocz(blocksize);
222 pointer = emallocz(blocksize);
223 nb = 0;
224 n = 0;
225 nname = nil;
226 pd = nil;
227
228 for(i=0; i<ndir; i++) {
229 p = dirs + i;
230 free(nname);
231 nname = emallocz(strlen(name) + strlen(p->name) + 2);
232 sprint(nname, "%s/%s", name, p->name);
233 if(p->mode & DMDIR)
234 pd = paqDir(nname, p);
235 else
236 pd = paqFile(nname, p);
237 if(pd == nil)
238 continue;
239
240 if(n+paqDirSize(pd) >= blocksize) {
241
242 /* zero fill the block */
243 memset(block+n, 0, blocksize-n);
244 offset = writeBlock(block, DirBlock);
245 n = 0;
246 if(nb >= blocksize/OffsetSize) {
247 warn("directory too big for blocksize: %s", nname);
248 goto Err;
249 }
250 putl(pointer+nb*OffsetSize, offset);
251 nb++;
252 }
253 if(n+paqDirSize(pd) >= blocksize) {
254 warn("directory entry does not fit in a block: %s", nname);
255 paqDirFree(pd);
256 continue;
257 }
258 putDir(block+n, pd);
259 n += paqDirSize(pd);
260 paqDirFree(pd);
261 pd = nil;
262 }
263
264 if(n > 0) {
265 /* zero fill the block */
266 memset(block+n, 0, blocksize-n);
267 offset = writeBlock(block, DirBlock);
268 if(nb >= blocksize/OffsetSize) {
269 warn("directory too big for blocksize: %s", nname);
270 goto Err;
271 }
272 putl(pointer+nb*OffsetSize, offset);
273 }
274 offset = writeBlock(pointer, PointerBlock);
275
276 free(nname);
277 free(dirs);
278 paqDirFree(pd);
279 free(block);
280 free(pointer);
281 return paqDirAlloc(dir, offset);
282 Err:
283 free(nname);
284 free(dirs);
285 paqDirFree(pd);
286 free(block);
287 free(pointer);
288 return nil;
289 }
290
291 PaqDir *
paqDirAlloc(Dir * dir,ulong offset)292 paqDirAlloc(Dir *dir, ulong offset)
293 {
294 PaqDir *pd;
295 static ulong qid = 1;
296
297 pd = emallocz(sizeof(PaqDir));
298
299 pd->name = strdup(dir->name);
300 pd->qid = qid++;
301 pd->mode = dir->mode & (DMDIR|DMAPPEND|0777);
302 pd->mtime = dir->mtime;
303 pd->length = dir->length;
304 pd->uid = strdup(dir->uid);
305 pd->gid = strdup(dir->gid);
306 pd->offset = offset;
307
308 return pd;
309 }
310
311 void
paqDirFree(PaqDir * pd)312 paqDirFree(PaqDir *pd)
313 {
314 if(pd == nil)
315 return;
316 free(pd->name);
317 free(pd->uid);
318 free(pd->gid);
319 free(pd);
320 }
321
322
323 void
writeHeader(char * label)324 writeHeader(char *label)
325 {
326 PaqHeader hdr;
327 uchar buf[HeaderSize];
328
329 memset(&hdr, 0, sizeof(hdr));
330 hdr.magic = HeaderMagic;
331 hdr.version = Version;
332 hdr.blocksize = blocksize;
333 hdr.time = time(nil);
334 strncpy(hdr.label, label, sizeof(hdr.label));
335 hdr.label[sizeof(hdr.label)-1] = 0;
336 putHeader(buf, &hdr);
337 outWrite(buf, sizeof(buf));
338 }
339
340 void
writeTrailer(ulong root)341 writeTrailer(ulong root)
342 {
343 PaqTrailer tlr;
344 uchar buf[TrailerSize];
345
346 memset(&tlr, 0, sizeof(tlr));
347 tlr.magic = TrailerMagic;
348 tlr.root = root;
349 putTrailer(buf, &tlr);
350 outWrite(buf, sizeof(buf));
351 }
352
353 ulong
writeBlock(uchar * b,int type)354 writeBlock(uchar *b, int type)
355 {
356 uchar *cb, *ob;
357 int n;
358 PaqBlock bh;
359 uchar buf[BlockSize];
360 ulong offset;
361
362 offset = Boffset(out);
363
364 bh.magic = BlockMagic;
365 bh.size = blocksize;
366 bh.type = type;
367 bh.encoding = NoEnc;
368 bh.adler32 = adler32(0, b, blocksize);
369 ob = b;
370
371 if(!uflag) {
372 cb = emallocz(blocksize);
373 n = deflateblock(cb, blocksize, b, blocksize, 6, 0);
374 if(n > 0 && n < blocksize) {
375 bh.encoding = DeflateEnc;
376 bh.size = n;
377 ob = cb;
378 }
379 }
380
381 putBlock(buf, &bh);
382 outWrite(buf, sizeof(buf));
383 outWrite(ob, bh.size);
384
385 if(ob != b)
386 free(ob);
387 return offset;
388 }
389
390
391 void
outWrite(void * buf,int n)392 outWrite(void *buf, int n)
393 {
394 if(Bwrite(out, buf, n) < n)
395 sysfatal("write failed: %r");
396 outdg = sha1((uchar*)buf, n, nil, outdg);
397 }
398
399 int
paqDirSize(PaqDir * d)400 paqDirSize(PaqDir *d)
401 {
402 return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid);
403 }
404
405 void
putHeader(uchar * p,PaqHeader * h)406 putHeader(uchar *p, PaqHeader *h)
407 {
408 if(h->blocksize < 65536){
409 putl(p, h->magic);
410 puts(p+4, h->version);
411 puts(p+6, h->blocksize);
412 }else{
413 assert(h->magic == HeaderMagic);
414 puts(p, BigHeaderMagic);
415 puts(p+2, h->version);
416 putl(p+4, h->blocksize);
417 }
418 putl(p+8, h->time);
419 memmove(p+12, h->label, sizeof(h->label));
420 }
421
422 void
putTrailer(uchar * p,PaqTrailer * h)423 putTrailer(uchar *p, PaqTrailer *h)
424 {
425 putl(p, h->magic);
426 putl(p+4, h->root);
427 outdg = sha1(p, 8, p+8, outdg);
428 }
429
430 void
putBlock(uchar * p,PaqBlock * b)431 putBlock(uchar *p, PaqBlock *b)
432 {
433 if(b->size < 65536){
434 putl(p, b->magic);
435 puts(p+4, b->size);
436 }else{
437 assert(b->magic == BlockMagic);
438 puts(p, BigBlockMagic);
439 putl(p+2, b->size);
440 }
441 p[6] = b->type;
442 p[7] = b->encoding;
443 putl(p+8, b->adler32);
444 }
445
446 void
putDir(uchar * p,PaqDir * d)447 putDir(uchar *p, PaqDir *d)
448 {
449 uchar *q;
450
451 puts(p, paqDirSize(d));
452 putl(p+2, d->qid);
453 putl(p+6, d->mode);
454 putl(p+10, d->mtime);
455 putl(p+14, d->length);
456 putl(p+18, d->offset);
457 q = putstr(p+22, d->name);
458 q = putstr(q, d->uid);
459 q = putstr(q, d->gid);
460 assert(q-p == paqDirSize(d));
461 }
462
463 void
putl(uchar * p,ulong v)464 putl(uchar *p, ulong v)
465 {
466 p[0] = v>>24;
467 p[1] = v>>16;
468 p[2] = v>>8;
469 p[3] = v;
470 }
471
472 void
puts(uchar * p,int v)473 puts(uchar *p, int v)
474 {
475 assert(v < (1<<16));
476 p[0] = v>>8;
477 p[1] = v;
478 }
479
480 uchar *
putstr(uchar * p,char * s)481 putstr(uchar *p, char *s)
482 {
483 int n = strlen(s);
484 puts(p, n+2);
485 memmove(p+2, s, n);
486 return p+2+n;
487 }
488
489
490 void *
emallocz(int size)491 emallocz(int size)
492 {
493 void *p;
494
495 p = malloc(size);
496 if(p == nil)
497 sysfatal("malloc failed");
498 memset(p, 0, size);
499 return p;
500 }
501
502 void
warn(char * fmt,...)503 warn(char *fmt, ...)
504 {
505 char buf[1024];
506 va_list arg;
507
508 va_start(arg, fmt);
509 vseprint(buf, buf+sizeof(buf), fmt, arg);
510 va_end(arg);
511 fprint(2, "%s: %s\n", argv0, buf);
512 }
513