Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}