]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/paqfs/mkpaqfs.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / cmd / paqfs / mkpaqfs.c
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
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
106 usage(void)
107 {
108         fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0);
109         exits("usage");
110 }
111
112 void
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 *
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 *
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 *
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
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
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
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
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
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
400 paqDirSize(PaqDir *d)
401 {
402         return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid);
403 }
404
405 void
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
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
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
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
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
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 *
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 *
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
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 }