#include #include #include "sac.h" #include "sacfs.h" enum { NCACHE = 1024, /* must be power of two */ OffsetSize = 4, /* size of block offset */ }; int warn(char *); int seen(Dir *); void usage(void); void outwrite(void *buf, int n, long off); void putl(void *p, uint v); void *emalloc(int size); void sacfs(char *root); void sacfile(char *name, Dir *dir, SacDir *sd); void sacdir(char *name, Dir *dir, SacDir *sd); int uflag=0; int fflag=0; long blocksize = 4*1024; struct Out { int fd; long size; } out; typedef struct Cache Cache; struct Cache { Dir* cache; int n; int max; } cache[NCACHE]; void main(int argc, char *argv[]) { char *s, *ss; char *outfile = nil; ARGBEGIN { case 'u': uflag=1; break; case 'o': outfile = ARGF(); break; case 'b': s = ARGF(); if(s) { blocksize = strtoul(s, &ss, 0); if(s == ss) usage(); if(*ss == 'k') blocksize *= 1024; } if(blocksize < sizeof(SacDir)) sysfatal("blocksize too small"); break; } ARGEND if(outfile == nil) { sysfatal("still to do: need to create temp file"); } else { out.fd = create(outfile, OWRITE|OTRUNC, 0664); if(out.fd < 0) sysfatal("could not create file: %s: %r", outfile); } if(argc==0) sacfs("."); else sacfs(argv[0]); close(out.fd); exits(0); } void usage(void) { fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0); exits("usage"); } void sacfs(char *root) { Dir dir; long offset; SacHeader hdr; SacDir sd; if(dirstat(root, &dir) < 0) sysfatal("could not stat root: %s: %r", root); offset = out.size; out.size += sizeof(SacHeader) + sizeof(SacDir); memset(&hdr, 0, sizeof(hdr)); putl(hdr.magic, Magic); putl(hdr.blocksize, blocksize); if(dir.mode & CHDIR) sacdir(root, &dir, &sd); else sacfile(root, &dir, &sd); putl(hdr.length, out.size); outwrite(&hdr, sizeof(hdr), offset); outwrite(&sd, sizeof(sd), offset+sizeof(SacHeader)); } void setsd(SacDir *sd, Dir *dir, long length, long blocks) { static qid = 1; memset(sd, 0, sizeof(SacDir)); memmove(sd->name, dir->name, NAMELEN); memmove(sd->uid, dir->uid, NAMELEN); memmove(sd->gid, dir->gid, NAMELEN); putl(sd->qid, qid++|(dir->mode&CHDIR)); putl(sd->mode, dir->mode); putl(sd->atime, dir->atime); putl(sd->mtime, dir->mtime); putl(sd->length, length); putl(sd->blocks, blocks); } void sacfile(char *name, Dir *dir, SacDir *sd) { int fd, i, n, nn; long nblock; uchar *blocks; uchar *buf, *cbuf; long offset; long block; fd = open(name, OREAD); if(fd < 0) sysfatal("could not open file: %s: %r", name); nblock = (dir->length + blocksize-1)/blocksize; blocks = emalloc((nblock+1)*OffsetSize); buf = emalloc(blocksize); cbuf = emalloc(blocksize); offset = out.size; out.size += (nblock+1)*OffsetSize; for(i=0; ilength, offset); close(fd); free(buf); free(cbuf); free(blocks); } void sacdir(char *name, Dir *dir, SacDir *sd) { Dir buf[100], *dirs, *p, b; char file[512]; int i, n, nn, per; SacDir *sds; int ndir, fd, nblock; long offset, block; uchar *blocks, *cbuf; fd = open(name, OREAD); if(fd < 0) sysfatal("could not open file: %s: %r", name); dirs = 0; ndir = 0; while((n=dirread(fd, buf, sizeof buf)) > 0) { dirs = realloc(dirs, ndir*sizeof(Dir) + n); if(dirs == nil) sysfatal("malloc failed"); n /= sizeof(Dir); p = buf; for(i=0; iname, ".") == 0 || strcmp(p->name, "..") == 0) continue; sprint(file, "%s/%s", name, p->name); if(dirstat(file, &b) < 0) { sysfatal("stat failed: %s: %r", file); continue; } if(b.qid.path != p->qid.path || b.dev != p->dev || b.type != p->type) continue; /* file is hidden */ if(seen(p)) fprint(2, "warning: multiple copies of %s\n", name); dirs[ndir++] = *p; } } close(fd); per = blocksize/sizeof(SacDir); nblock = (ndir+per-1)/per; sds = emalloc(nblock*per*sizeof(SacDir)); p = dirs; for(i=0; iname); if(p->mode & CHDIR) sacdir(file, p, sds+i); else sacfile(file, p, sds+i); } free(dirs); blocks = emalloc((nblock+1)*OffsetSize); offset = out.size; out.size += (nblock+1)*OffsetSize; n = per*sizeof(SacDir); cbuf = emalloc(n); for(i=0; i (ndir-i*per)*sizeof(SacDir)) n = (ndir-i*per)*sizeof(SacDir); nn = sac(cbuf, (uchar*)(sds+i*per), n); if(nn < 0 || uflag) { outwrite(sds+i*per, n, out.size); out.size += n; } else { block = -block; outwrite(cbuf, nn, out.size); out.size += nn; } putl(blocks+i*OffsetSize, block); } free(cbuf); putl(blocks+i*OffsetSize, out.size); outwrite(blocks, (nblock+1)*OffsetSize, offset); setsd(sd, dir, ndir, offset); free(sds); free(blocks); } int seen(Dir *dir) { Dir *dp; int i; Cache *c; c = &cache[dir->qid.path&(NCACHE-1)]; dp = c->cache; for(i=0; in; i++, dp++) if(dir->qid.path == dp->qid.path && dir->type == dp->type && dir->dev == dp->dev) return 1; if(c->n == c->max){ c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir)); if(cache == 0) sysfatal("malloc failure"); } c->cache[c->n++] = *dir; return 0; } void outwrite(void *buf, int n, long offset) { if(seek(out.fd, offset, 0) < 0) sysfatal("seek failed: %r"); if(write(out.fd, buf, n) < n) sysfatal("write failed: %r"); } void putl(void *p, uint v) { uchar *a; a = p; a[0] = v>>24; a[1] = v>>16; a[2] = v>>8; a[3] = v; } void * emalloc(int size) { void *p; p = malloc(size); if(p == nil) sysfatal("malloc failed"); memset(p, 0, size); return p; }