xref: /plan9/sys/src/cmd/paqfs/mkpaqfs.c (revision fe853e2326f51910bb38886e9bfc22ecdef993d7)
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