#include #include #include #include #include #include #include "paqfs.h" enum { OffsetSize = 4, /* size of block offset */ }; void paqfs(char *root, char *label); PaqDir *paqFile(char *name, Dir *dir); PaqDir *paqDir(char *name, Dir *dir); PaqDir *paqDirAlloc(Dir *d, ulong offset); void paqDirFree(PaqDir *pd); void writeHeader(char *label); void writeTrailer(ulong root); ulong writeBlock(uchar *buf, int type); void usage(void); void outWrite(void *buf, int n); int paqDirSize(PaqDir *dir); void putDir(uchar *p, PaqDir *dir); void putHeader(uchar *p, PaqHeader *h); void putBlock(uchar *p, PaqBlock *h); void putTrailer(uchar *p, PaqTrailer *t); void putl(uchar *p, ulong v); void puts(uchar *p, int x); uchar *putstr(uchar *p, char *s); void *emallocz(int size); void warn(char *fmt, ...); int uflag=0; /* uncompressed */ long blocksize = 4*1024; Biobuf *out; DigestState *outdg; void main(int argc, char *argv[]) { char *s, *ss; char *outfile = nil; char *label = nil; char *file; ARGBEGIN { case 'u': uflag=1; break; case 'o': outfile = ARGF(); break; case 'l': label = ARGF(); if(label == nil) usage(); break; case 'b': s = ARGF(); if(s) { blocksize = strtoul(s, &ss, 0); if(s == ss) usage(); if(*ss == 'k') blocksize *= 1024; } if(blocksize < MinBlockSize) sysfatal("blocksize too small: must be at lease %d", MinBlockSize); if(blocksize > MaxBlockSize) sysfatal("blocksize too large: must be no greater than %d", MaxBlockSize); break; } ARGEND if(outfile == nil) { out = emallocz(sizeof(Biobuf)); Binit(out, 1, OWRITE); } else { out = Bopen(outfile, OWRITE|OTRUNC); if(out == nil) sysfatal("could not create file: %s: %r", outfile); } deflateinit(); file = argv[0]; if(file == nil) file = "."; if(label == nil) { if(strrchr(file, '/')) label = strrchr(file, '/') + 1; else label = file; } paqfs(file, label); Bterm(out); exits(0); } void usage(void) { fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0); exits("usage"); } void paqfs(char *root, char *label) { Dir *dir; PaqDir *pd; ulong offset; uchar *buf; dir = dirstat(root); if(dir == nil) sysfatal("could not stat root: %s: %r", root); writeHeader(label); if(dir->mode & DMDIR) pd = paqDir(root, dir); else pd = paqFile(root, dir); buf = emallocz(blocksize); putDir(buf, pd); offset = writeBlock(buf, DirBlock); writeTrailer(offset); paqDirFree(pd); free(dir); } PaqDir * paqFile(char *name, Dir *dir) { int fd, n, nn, nb; vlong tot; uchar *block, *pointer; ulong offset; fd = open(name, OREAD); if(fd < 0) { warn("could not open file: %s: %r", name); return nil; } block = emallocz(blocksize); pointer = emallocz(blocksize); nb = 0; n = 0; tot = 0; for(;;) { nn = read(fd, block+n, blocksize-n); if(nn < 0) { warn("read failed: %s: %r", name); goto Err; } tot += nn; if(nn == 0) { if(n == 0) break; /* pad out last block */ memset(block+n, 0, blocksize-n); nn = blocksize - n; } n += nn; if(n < blocksize) continue; if(nb >= blocksize/OffsetSize) { warn("file too big for blocksize: %s", name); goto Err; } offset = writeBlock(block, DataBlock); putl(pointer+nb*OffsetSize, offset); nb++; n = 0; } offset = writeBlock(pointer, PointerBlock); close(fd); free(pointer); free(block); dir->length = tot; return paqDirAlloc(dir, offset); Err: close(fd); free(pointer); free(block); return nil; } PaqDir * paqDir(char *name, Dir *dir) { Dir *dirs, *p; PaqDir *pd; int i, n, nb, fd, ndir; uchar *block, *pointer; char *nname; ulong offset; fd = open(name, OREAD); if(fd < 0) { warn("could not open directory: %s: %r", name); return nil; } ndir = dirreadall(fd, &dirs); close(fd); if(ndir < 0) { warn("could not read directory: %s: %r", name); return nil; } block = emallocz(blocksize); pointer = emallocz(blocksize); nb = 0; n = 0; nname = nil; pd = nil; for(i=0; iname) + 2); sprint(nname, "%s/%s", name, p->name); if(p->mode & DMDIR) pd = paqDir(nname, p); else pd = paqFile(nname, p); if(pd == nil) continue; if(n+paqDirSize(pd) >= blocksize) { /* zero fill the block */ memset(block+n, 0, blocksize-n); offset = writeBlock(block, DirBlock); n = 0; if(nb >= blocksize/OffsetSize) { warn("directory too big for blocksize: %s", nname); goto Err; } putl(pointer+nb*OffsetSize, offset); nb++; } if(n+paqDirSize(pd) >= blocksize) { warn("directory entry does not fit in a block: %s", nname); paqDirFree(pd); continue; } putDir(block+n, pd); n += paqDirSize(pd); paqDirFree(pd); pd = nil; } if(n > 0) { /* zero fill the block */ memset(block+n, 0, blocksize-n); offset = writeBlock(block, DirBlock); if(nb >= blocksize/OffsetSize) { warn("directory too big for blocksize: %s", nname); goto Err; } putl(pointer+nb*OffsetSize, offset); } offset = writeBlock(pointer, PointerBlock); free(nname); free(dirs); paqDirFree(pd); free(block); free(pointer); return paqDirAlloc(dir, offset); Err: free(nname); free(dirs); paqDirFree(pd); free(block); free(pointer); return nil; } PaqDir * paqDirAlloc(Dir *dir, ulong offset) { PaqDir *pd; static ulong qid = 1; pd = emallocz(sizeof(PaqDir)); pd->name = strdup(dir->name); pd->qid = qid++; pd->mode = dir->mode & (DMDIR|DMAPPEND|0777); pd->mtime = dir->mtime; pd->length = dir->length; pd->uid = strdup(dir->uid); pd->gid = strdup(dir->gid); pd->offset = offset; return pd; } void paqDirFree(PaqDir *pd) { if(pd == nil) return; free(pd->name); free(pd->uid); free(pd->gid); free(pd); } void writeHeader(char *label) { PaqHeader hdr; uchar buf[HeaderSize]; memset(&hdr, 0, sizeof(hdr)); hdr.magic = HeaderMagic; hdr.version = Version; hdr.blocksize = blocksize; hdr.time = time(nil); strncpy(hdr.label, label, sizeof(hdr.label)); hdr.label[sizeof(hdr.label)-1] = 0; putHeader(buf, &hdr); outWrite(buf, sizeof(buf)); } void writeTrailer(ulong root) { PaqTrailer tlr; uchar buf[TrailerSize]; memset(&tlr, 0, sizeof(tlr)); tlr.magic = TrailerMagic; tlr.root = root; putTrailer(buf, &tlr); outWrite(buf, sizeof(buf)); } ulong writeBlock(uchar *b, int type) { uchar *cb, *ob; int n; PaqBlock bh; uchar buf[BlockSize]; ulong offset; offset = Boffset(out); bh.magic = BlockMagic; bh.size = blocksize; bh.type = type; bh.encoding = NoEnc; bh.adler32 = adler32(0, b, blocksize); ob = b; if(!uflag) { cb = emallocz(blocksize); n = deflateblock(cb, blocksize, b, blocksize, 6, 0); if(n > 0 && n < blocksize) { bh.encoding = DeflateEnc; bh.size = n; ob = cb; } } putBlock(buf, &bh); outWrite(buf, sizeof(buf)); outWrite(ob, bh.size); if(ob != b) free(ob); return offset; } void outWrite(void *buf, int n) { if(Bwrite(out, buf, n) < n) sysfatal("write failed: %r"); outdg = sha1((uchar*)buf, n, nil, outdg); } int paqDirSize(PaqDir *d) { return MinDirSize + strlen(d->name) + strlen(d->uid) + strlen(d->gid); } void putHeader(uchar *p, PaqHeader *h) { if(h->blocksize < 65536){ putl(p, h->magic); puts(p+4, h->version); puts(p+6, h->blocksize); }else{ assert(h->magic == HeaderMagic); puts(p, BigHeaderMagic); puts(p+2, h->version); putl(p+4, h->blocksize); } putl(p+8, h->time); memmove(p+12, h->label, sizeof(h->label)); } void putTrailer(uchar *p, PaqTrailer *h) { putl(p, h->magic); putl(p+4, h->root); outdg = sha1(p, 8, p+8, outdg); } void putBlock(uchar *p, PaqBlock *b) { if(b->size < 65536){ putl(p, b->magic); puts(p+4, b->size); }else{ assert(b->magic == BlockMagic); puts(p, BigBlockMagic); putl(p+2, b->size); } p[6] = b->type; p[7] = b->encoding; putl(p+8, b->adler32); } void putDir(uchar *p, PaqDir *d) { uchar *q; puts(p, paqDirSize(d)); putl(p+2, d->qid); putl(p+6, d->mode); putl(p+10, d->mtime); putl(p+14, d->length); putl(p+18, d->offset); q = putstr(p+22, d->name); q = putstr(q, d->uid); q = putstr(q, d->gid); assert(q-p == paqDirSize(d)); } void putl(uchar *p, ulong v) { p[0] = v>>24; p[1] = v>>16; p[2] = v>>8; p[3] = v; } void puts(uchar *p, int v) { assert(v < (1<<16)); p[0] = v>>8; p[1] = v; } uchar * putstr(uchar *p, char *s) { int n = strlen(s); puts(p, n+2); memmove(p+2, s, n); return p+2+n; } void * emallocz(int size) { void *p; p = malloc(size); if(p == nil) sysfatal("malloc failed"); memset(p, 0, size); return p; } void warn(char *fmt, ...) { char buf[1024]; va_list arg; va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%s: %s\n", argv0, buf); }