summaryrefslogtreecommitdiff
path: root/sys/src/cmd/paqfs
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/paqfs
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/paqfs')
-rwxr-xr-xsys/src/cmd/paqfs/mkfile24
-rwxr-xr-xsys/src/cmd/paqfs/mkpaqfs.c512
-rwxr-xr-xsys/src/cmd/paqfs/paqfs.c1216
-rwxr-xr-xsys/src/cmd/paqfs/paqfs.h69
4 files changed, 1821 insertions, 0 deletions
diff --git a/sys/src/cmd/paqfs/mkfile b/sys/src/cmd/paqfs/mkfile
new file mode 100755
index 000000000..a5f813c80
--- /dev/null
+++ b/sys/src/cmd/paqfs/mkfile
@@ -0,0 +1,24 @@
+</$objtype/mkfile
+
+HFILES=paqfs.h
+
+TARG=mkpaqfs\
+ paqfs\
+
+OFILES=
+
+PROGS=${TARG:%=$O.%}
+
+BIN=/$objtype/bin
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+ ${TARG:%=%.c}\
+ ${TARG:%=/386/bin/%}\
+
+
+all:V:
+
+< /sys/src/cmd/mkmany
+
diff --git a/sys/src/cmd/paqfs/mkpaqfs.c b/sys/src/cmd/paqfs/mkpaqfs.c
new file mode 100755
index 000000000..1a0726f18
--- /dev/null
+++ b/sys/src/cmd/paqfs/mkpaqfs.c
@@ -0,0 +1,512 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <flate.h>
+#include <mp.h>
+#include <libsec.h>
+#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; i<ndir; i++) {
+ p = dirs + i;
+ free(nname);
+ nname = emallocz(strlen(name) + strlen(p->name) + 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);
+}
diff --git a/sys/src/cmd/paqfs/paqfs.c b/sys/src/cmd/paqfs/paqfs.c
new file mode 100755
index 000000000..bf640cc7b
--- /dev/null
+++ b/sys/src/cmd/paqfs/paqfs.c
@@ -0,0 +1,1216 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <bio.h>
+#include <mp.h>
+#include <libsec.h>
+#include <flate.h>
+
+#include "paqfs.h"
+
+enum
+{
+ OPERM = 0x3, /* mask of all permission types in open mode */
+ OffsetSize = 4, /* size in bytes of an offset */
+};
+
+typedef struct Fid Fid;
+typedef struct Paq Paq;
+typedef struct Block Block;
+
+struct Fid
+{
+ short busy;
+ short open;
+ int fid;
+ char *user;
+ ulong offset; /* for directory reading */
+
+ Paq *paq;
+ Fid *next;
+};
+
+struct Paq
+{
+ int ref;
+ Paq *up;
+ PaqDir *dir;
+ Qid qid;
+};
+
+struct Block
+{
+ int ref;
+ ulong addr; /* block byte address */
+ ulong age;
+ uchar *data;
+};
+
+enum
+{
+ Pexec = 1,
+ Pwrite = 2,
+ Pread = 4,
+ Pother = 1,
+ Pgroup = 8,
+ Powner = 64,
+};
+
+int noauth;
+Fid *fids;
+Fcall rhdr, thdr;
+int blocksize;
+int cachesize = 20;
+int mesgsize = 8*1024 + IOHDRSZ;
+Paq *root, *rootfile;
+Block *cache;
+ulong cacheage;
+Biobuf *bin;
+int qflag;
+
+Fid * newfid(int);
+void paqstat(PaqDir*, char*);
+void io(int fd);
+void *erealloc(void*, ulong);
+void *emalloc(ulong);
+void *emallocz(ulong n);
+char *estrdup(char*);
+void usage(void);
+ulong getl(uchar *p);
+int gets(uchar *p);
+char *getstr(uchar *p);
+PaqDir *getDir(uchar*);
+void getHeader(uchar *p, PaqHeader *b);
+void getBlock(uchar *p, PaqBlock *b);
+void getTrailer(uchar *p, PaqTrailer *b);
+void init(char*, int);
+void paqDirFree(PaqDir*);
+Qid paqDirQid(PaqDir *d);
+Paq *paqCpy(Paq *s);
+Paq *paqLookup(Paq *s, char *name);
+void paqFree(Paq*);
+Paq *paqWalk(Paq *s, char *name);
+int perm(PaqDir *s, char *user, int p);
+int dirRead(Fid*, uchar*, int);
+Block *blockLoad(ulong addr, int type);
+void blockFree(Block*);
+int checkDirSize(uchar *p, uchar *ep);
+int packDir(PaqDir*, uchar*, int);
+int blockRead(uchar *data, ulong addr, int type);
+void readHeader(PaqHeader *hdr, char *name, DigestState *ds);
+void readBlocks(char *name, DigestState *ds);
+void readTrailer(PaqTrailer *tlr, char *name, DigestState *ds);
+
+char *rflush(Fid*), *rversion(Fid*),
+ *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
+ *ropen(Fid*), *rcreate(Fid*),
+ *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
+ *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
+
+char *(*fcalls[])(Fid*) = {
+ [Tflush] rflush,
+ [Tversion] rversion,
+ [Tattach] rattach,
+ [Tauth] rauth,
+ [Twalk] rwalk,
+ [Topen] ropen,
+ [Tcreate] rcreate,
+ [Tread] rread,
+ [Twrite] rwrite,
+ [Tclunk] rclunk,
+ [Tremove] rremove,
+ [Tstat] rstat,
+ [Twstat] rwstat,
+};
+
+char Eperm[] = "permission denied";
+char Enotdir[] = "not a directory";
+char Enoauth[] = "authentication not required";
+char Enotexist[] = "file does not exist";
+char Einuse[] = "file in use";
+char Eexist[] = "file exists";
+char Enotowner[] = "not owner";
+char Eisopen[] = "file already open for I/O";
+char Excl[] = "exclusive use file already open";
+char Ename[] = "illegal name";
+char Erdonly[] = "read only file system";
+char Ebadblock[] = "bad block";
+char Eversion[] = "bad version of P9";
+char Edirtoobig[] = "directory entry too big";
+
+int debug;
+
+#pragma varargck type "V" uchar*
+
+static int
+sha1fmt(Fmt *f)
+{
+ int i;
+ uchar *v;
+
+ v = va_arg(f->args, uchar*);
+ if(v == nil){
+ fmtprint(f, "*");
+ }
+ else{
+ for(i = 0; i < SHA1dlen; i++)
+ fmtprint(f, "%2.2ux", v[i]);
+ }
+
+ return 0;
+}
+
+void
+main(int argc, char *argv[])
+{
+ int pfd[2];
+ int fd, mnt, srv, stdio, verify;
+ char buf[64], *mntpoint, *srvname, *p;
+
+ fmtinstall('V', sha1fmt);
+
+ mntpoint = "/n/paq";
+ srvname = "paqfs";
+ mnt = 1;
+ srv = stdio = verify = 0;
+
+ ARGBEGIN{
+ default:
+ usage();
+ case 'a':
+ noauth = 1;
+ break;
+ case 'c':
+ p = EARGF(usage());
+ cachesize = atoi(p);
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'i':
+ mnt = 0;
+ stdio = 1;
+ pfd[0] = 0;
+ pfd[1] = 1;
+ break;
+ case 'm':
+ mntpoint = EARGF(usage());
+ break;
+ case 'M':
+ p = EARGF(usage());
+ mesgsize = atoi(p);
+ if(mesgsize < 512)
+ mesgsize = 512;
+ if(mesgsize > 128*1024)
+ mesgsize = 128*1024;
+ break;
+ case 'p':
+ srv = 1;
+ mnt = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 's':
+ srv = 1;
+ mnt = 0;
+ break;
+ case 'S':
+ srvname = EARGF(usage());
+ break;
+ case 'v':
+ verify = 1;
+ break;
+ }ARGEND
+
+ if(argc != 1)
+ usage();
+
+ init(argv[0], verify);
+
+ if(!stdio){
+ if(pipe(pfd) < 0)
+ sysfatal("pipe: %r");
+ if(srv){
+ snprint(buf, sizeof buf, "#s/%s", srvname);
+ fd = create(buf, OWRITE, 0666);
+ if(fd < 0)
+ sysfatal("create %s: %r", buf);
+ if(fprint(fd, "%d", pfd[0]) < 0)
+ sysfatal("write %s: %r", buf);
+ }
+ }
+
+ if(debug)
+ fmtinstall('F', fcallfmt);
+ switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
+ case -1:
+ sysfatal("fork");
+ case 0:
+ close(pfd[0]);
+ io(pfd[1]);
+ break;
+ default:
+ close(pfd[1]); /* don't deadlock if child fails */
+ if(mnt && mount(pfd[0], -1, mntpoint, MREPL|MCREATE, "") < 0)
+ sysfatal("mount %s: %r", mntpoint);
+ }
+ exits(0);
+}
+
+char*
+rversion(Fid*)
+{
+ Fid *f;
+
+ for(f = fids; f; f = f->next)
+ if(f->busy)
+ rclunk(f);
+ if(rhdr.msize > mesgsize)
+ thdr.msize = mesgsize;
+ else
+ thdr.msize = rhdr.msize;
+ if(strcmp(rhdr.version, "9P2000") != 0)
+ return Eversion;
+ thdr.version = "9P2000";
+ return 0;
+}
+
+char*
+rauth(Fid*)
+{
+ return Enoauth;
+}
+
+char*
+rflush(Fid *f)
+{
+ USED(f);
+ return 0;
+}
+
+char*
+rattach(Fid *f)
+{
+ /* no authentication! */
+ f->busy = 1;
+ f->paq = paqCpy(root);
+ thdr.qid = f->paq->qid;
+ if(rhdr.uname[0])
+ f->user = estrdup(rhdr.uname);
+ else
+ f->user = "none";
+ return 0;
+}
+
+char*
+clone(Fid *f, Fid **res)
+{
+ Fid *nf;
+
+ if(f->open)
+ return Eisopen;
+ if(f->busy == 0)
+ return Enotexist;
+ nf = newfid(rhdr.newfid);
+ nf->busy = 1;
+ nf->open = 0;
+ nf->paq = paqCpy(f->paq);
+ nf->user = strdup(f->user);
+ *res = nf;
+ return 0;
+}
+
+char*
+rwalk(Fid *f)
+{
+ Paq *paq, *npaq;
+ Fid *nf;
+ int nqid, nwname;
+ Qid qid;
+ char *err;
+
+ if(f->busy == 0)
+ return Enotexist;
+ nf = nil;
+ if(rhdr.fid != rhdr.newfid){
+ err = clone(f, &nf);
+ if(err)
+ return err;
+ f = nf; /* walk the new fid */
+ }
+
+ nwname = rhdr.nwname;
+
+ /* easy case */
+ if(nwname == 0) {
+ thdr.nwqid = 0;
+ return 0;
+ }
+
+ paq = paqCpy(f->paq);
+ qid = paq->qid;
+ err = nil;
+
+ for(nqid = 0; nqid < nwname; nqid++){
+ if((qid.type & QTDIR) == 0){
+ err = Enotdir;
+ break;
+ }
+ if(!perm(paq->dir, f->user, Pexec)) {
+ err = Eperm;
+ break;
+ }
+ npaq = paqWalk(paq, rhdr.wname[nqid]);
+ if(npaq == nil) {
+ err = Enotexist;
+ break;
+ }
+ paqFree(paq);
+ paq = npaq;
+ qid = paq->qid;
+ thdr.wqid[nqid] = qid;
+ }
+
+ thdr.nwqid = nqid;
+
+ if(nqid == nwname){
+ /* success */
+ paqFree(f->paq);
+ f->paq = paq;
+ return 0;
+ }
+
+ paqFree(paq);
+ if(nf != nil)
+ rclunk(nf);
+
+ /* only error on the first element */
+ if(nqid == 0)
+ return err;
+
+ return 0;
+}
+
+char *
+ropen(Fid *f)
+{
+ int mode, trunc;
+
+ if(f->open)
+ return Eisopen;
+ if(f->busy == 0)
+ return Enotexist;
+ mode = rhdr.mode;
+ if(f->paq->qid.type & QTDIR){
+ if(mode != OREAD)
+ return Eperm;
+ thdr.qid = f->paq->qid;
+ return 0;
+ }
+ if(mode & ORCLOSE)
+ return Erdonly;
+ trunc = mode & OTRUNC;
+ mode &= OPERM;
+ if(mode==OWRITE || mode==ORDWR || trunc)
+ return Erdonly;
+ if(mode==OREAD)
+ if(!perm(f->paq->dir, f->user, Pread))
+ return Eperm;
+ if(mode==OEXEC)
+ if(!perm(f->paq->dir, f->user, Pexec))
+ return Eperm;
+ thdr.qid = f->paq->qid;
+ f->open = 1;
+ return 0;
+}
+
+char *
+rcreate(Fid *f)
+{
+ if(f->open)
+ return Eisopen;
+ if(f->busy == 0)
+ return Enotexist;
+ return Erdonly;
+}
+
+char *
+readdir(Fid *f)
+{
+ PaqDir *pd;
+ uchar *p, *ep;
+ ulong off;
+ int n, cnt, i;
+ uchar *buf;
+ Block *ptr, *b;
+
+ buf = (uchar*)thdr.data;
+ cnt = rhdr.count;
+ if(rhdr.offset == 0)
+ f->offset = 0;
+ off = f->offset;
+
+ if(rootfile && f->paq == root){
+ if(off != 0){
+ rhdr.count = 0;
+ return nil;
+ }
+ n = packDir(rootfile->dir, buf, cnt);
+ rhdr.count = n;
+ return nil;
+ }
+
+ ptr = blockLoad(f->paq->dir->offset, PointerBlock);
+ if(ptr == nil)
+ return Ebadblock;
+ i = off/blocksize;
+ off -= i*blocksize;
+
+ thdr.count = 0;
+ b = blockLoad(getl(ptr->data + i*4), DirBlock);
+ while(b != nil) {
+ p = b->data + off;
+ ep = b->data + blocksize;
+ if(checkDirSize(p, ep)) {
+ pd = getDir(p);
+ n = packDir(pd, buf, cnt);
+ paqDirFree(pd);
+ if(n == 0) {
+ blockFree(b);
+ if(thdr.count == 0) {
+ blockFree(ptr);
+ return Edirtoobig;
+ }
+ break;
+ }
+ off += gets(p);
+ cnt -= n;
+ buf += n;
+ thdr.count += n;
+ } else {
+ off = 0;
+ i++;
+ blockFree(b);
+ b = blockLoad(getl(ptr->data + i*4), DataBlock);
+ }
+ }
+ f->offset = i*blocksize + off;
+ blockFree(ptr);
+
+ return 0;
+}
+
+char*
+rread(Fid *f)
+{
+ PaqDir *pd;
+ uchar *buf;
+ vlong off;
+ ulong uoff;
+ int n, cnt, i;
+ Block *ptr, *b;
+
+ if(f->busy == 0)
+ return Enotexist;
+ if(f->paq->qid.type & QTDIR)
+ return readdir(f);
+ pd = f->paq->dir;
+ off = rhdr.offset;
+ buf = (uchar*)thdr.data;
+ cnt = rhdr.count;
+
+ thdr.count = 0;
+ if(off >= pd->length || cnt == 0)
+ return 0;
+
+ if(cnt > pd->length - off)
+ cnt = pd->length - off;
+
+ ptr = blockLoad(pd->offset, PointerBlock);
+ if(ptr == nil)
+ return Ebadblock;
+
+ i = off/blocksize;
+ uoff = off-i*blocksize;
+
+ while(cnt > 0) {
+ b = blockLoad(getl(ptr->data + i*4), DataBlock);
+ if(b == nil) {
+ blockFree(ptr);
+ return Ebadblock;
+ }
+ n = blocksize - uoff;
+ if(n > cnt)
+ n = cnt;
+ memmove(buf, b->data + uoff, n);
+ cnt -= n;
+ thdr.count += n;
+ buf += n;
+ uoff = 0;
+ i++;
+ blockFree(b);
+ }
+ blockFree(ptr);
+ return 0;
+}
+
+char*
+rwrite(Fid *f)
+{
+ if(f->busy == 0)
+ return Enotexist;
+ return Erdonly;
+}
+
+char *
+rclunk(Fid *f)
+{
+ f->busy = 0;
+ f->open = 0;
+ free(f->user);
+ paqFree(f->paq);
+ return 0;
+}
+
+char *
+rremove(Fid *f)
+{
+ rclunk(f);
+ return Erdonly;
+}
+
+char *
+rstat(Fid *f)
+{
+ if(f->busy == 0)
+ return Enotexist;
+ thdr.stat = (uchar*)thdr.data;
+ thdr.nstat = packDir(f->paq->dir, thdr.stat, mesgsize);
+ if(thdr.nstat == 0)
+ return Edirtoobig;
+ return 0;
+}
+
+char *
+rwstat(Fid *f)
+{
+ if(f->busy == 0)
+ return Enotexist;
+ return Erdonly;
+}
+
+Paq*
+paqCpy(Paq *s)
+{
+ s->ref++;
+ return s;
+}
+
+void
+paqFree(Paq *p)
+{
+ if(p == nil)
+ return;
+ p->ref--;
+ if(p->ref > 0)
+ return;
+assert(p != root);
+ paqFree(p->up);
+ paqDirFree(p->dir);
+ free(p);
+}
+
+void
+paqDirFree(PaqDir *pd)
+{
+ if(pd == nil)
+ return;
+ free(pd->name);
+ free(pd->uid);
+ free(pd->gid);
+ free(pd);
+}
+
+Qid
+paqDirQid(PaqDir *d)
+{
+ Qid q;
+
+ q.path = d->qid;
+ q.vers = 0;
+ q.type = d->mode >> 24;
+
+ return q;
+}
+
+int
+packDir(PaqDir *s, uchar *buf, int n)
+{
+ Dir dir;
+
+ memset(&dir, 0, sizeof(dir));
+ dir.qid = paqDirQid(s);
+ dir.mode = s->mode;
+ dir.atime = s->mtime;
+ dir.mtime = s->mtime;
+ dir.length = s->length;
+ dir.name = s->name;
+ dir.uid = s->uid;
+ dir.gid = s->gid;
+ dir.muid = s->uid;
+
+ n = convD2M(&dir, buf, n);
+ if(n < STATFIXLEN)
+ return 0;
+ return n;
+}
+
+Block *
+blockLoad(ulong addr, int type)
+{
+ ulong age;
+ int i, j;
+ Block *b;
+
+ if(addr == 0)
+ return nil;
+
+ cacheage++;
+
+ /* age has wraped */
+ if(cacheage == 0) {
+ for(i=0; i<cachesize; i++)
+ cache[i].age = 0;
+ }
+
+ j = -1;
+ age = ~0;
+ for(i=0; i<cachesize; i++) {
+ b = &cache[i];
+ if(b->age < age && b->ref == 0) {
+ age = b->age;
+ j = i;
+ }
+ if(b->addr != addr)
+ continue;
+ b->age = cacheage;
+ b->ref++;
+ return b;
+ }
+ if(j < 0)
+ sysfatal("no empty spots in cache!");
+ b = &cache[j];
+ assert(b->ref == 0);
+
+ if(!blockRead(b->data, addr, type)) {
+ b->addr = 0;
+ b->age = 0;
+ return nil;
+ }
+
+ b->age = cacheage;
+ b->addr = addr;
+ b->ref = 1;
+
+ return b;
+}
+
+void
+blockFree(Block *b)
+{
+ if(b == nil)
+ return;
+ if(--b->ref > 0)
+ return;
+ assert(b->ref == 0);
+}
+
+Paq*
+paqWalk(Paq *s, char *name)
+{
+ Block *ptr, *b;
+ uchar *p, *ep;
+ PaqDir *pd;
+ int i, n;
+ Paq *ss;
+
+ if(strcmp(name, "..") == 0)
+ return paqCpy(s->up);
+
+ if(rootfile && s == root){
+ if(strcmp(name, rootfile->dir->name) == 0)
+ return paqCpy(rootfile);
+ return nil;
+ }
+
+ ptr = blockLoad(s->dir->offset, PointerBlock);
+ if(ptr == nil)
+ return nil;
+
+ for(i=0; i<blocksize/4; i++) {
+ b = blockLoad(getl(ptr->data+i*4), DirBlock);
+ if(b == nil)
+ break;
+ p = b->data;
+ ep = p + blocksize;
+ while(checkDirSize(p, ep)) {
+ n = gets(p);
+ pd = getDir(p);
+ if(strcmp(pd->name, name) == 0) {
+ ss = emallocz(sizeof(Paq));
+ ss->ref = 1;
+ ss->up = paqCpy(s);
+ ss->dir = pd;
+ ss->qid = paqDirQid(pd);
+ blockFree(b);
+ blockFree(ptr);
+ return ss;
+ }
+ paqDirFree(pd);
+ p += n;
+ }
+ blockFree(b);
+ }
+
+ blockFree(ptr);
+ return nil;
+}
+
+Fid *
+newfid(int fid)
+{
+ Fid *f, *ff;
+
+ ff = 0;
+ for(f = fids; f; f = f->next)
+ if(f->fid == fid)
+ return f;
+ else if(!ff && !f->busy)
+ ff = f;
+ if(ff){
+ ff->fid = fid;
+ return ff;
+ }
+ f = emallocz(sizeof *f);
+ f->fid = fid;
+ f->next = fids;
+ fids = f;
+ return f;
+}
+
+void
+io(int fd)
+{
+ char *err;
+ int n, pid;
+ uchar *mdata;
+
+ mdata = emalloc(mesgsize);
+
+ pid = getpid();
+
+ for(;;){
+ n = read9pmsg(fd, mdata, mesgsize);
+ if(n < 0)
+ sysfatal("mount read");
+ if(n == 0)
+ break;
+ if(convM2S(mdata, n, &rhdr) == 0)
+ continue;
+
+ if(debug)
+ fprint(2, "paqfs %d:<-%F\n", pid, &rhdr);
+
+ thdr.data = (char*)mdata + IOHDRSZ;
+ if(!fcalls[rhdr.type])
+ err = "bad fcall type";
+ else
+ err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
+ if(err){
+ thdr.type = Rerror;
+ thdr.ename = err;
+ }else{
+ thdr.type = rhdr.type + 1;
+ thdr.fid = rhdr.fid;
+ }
+ thdr.tag = rhdr.tag;
+ if(debug)
+ fprint(2, "paqfs %d:->%F\n", pid, &thdr);/**/
+ n = convS2M(&thdr, mdata, mesgsize);
+ if(n == 0)
+ sysfatal("convS2M sysfatal on write");
+ if(write(fd, mdata, n) != n)
+ sysfatal("mount write");
+ }
+}
+
+int
+perm(PaqDir *s, char *user, int p)
+{
+ ulong perm = s->mode;
+
+ if((p*Pother) & perm)
+ return 1;
+ if((noauth || strcmp(user, s->gid)==0) && ((p*Pgroup) & perm))
+ return 1;
+ if((noauth || strcmp(user, s->uid)==0) && ((p*Powner) & perm))
+ return 1;
+ return 0;
+}
+
+void
+init(char *file, int verify)
+{
+ PaqHeader hdr;
+ PaqTrailer tlr;
+ Dir *dir;
+ int i;
+ uchar *p;
+ DigestState *ds = nil;
+ PaqDir *r;
+ Block *b;
+ ulong offset;
+
+ inflateinit();
+
+ bin = Bopen(file, OREAD);
+ if(bin == nil)
+ sysfatal("could not open file: %s: %r", file);
+ if(verify)
+ ds = sha1(0, 0, 0, 0);
+
+ readHeader(&hdr, file, ds);
+ blocksize = hdr.blocksize;
+
+ if(verify) {
+ readBlocks(file, ds);
+ } else {
+ dir = dirstat(file);
+ if(dir == nil)
+ sysfatal("could not stat file: %s: %r", file);
+ offset = dir->length - TrailerSize;
+ free(dir);
+ if(Bseek(bin, offset, 0) != offset)
+ sysfatal("could not seek to trailer: %s", file);
+ }
+
+ readTrailer(&tlr, file, ds);
+
+ /* asctime includes a newline - yuk */
+ if(!qflag){
+ fprint(2, "%s: %s", hdr.label, asctime(gmtime(hdr.time)));
+ fprint(2, "fingerprint: %V\n", tlr.sha1);
+ }
+
+ cache = emallocz(cachesize*sizeof(Block));
+ p = emalloc(cachesize*blocksize);
+ for(i=0; i<cachesize; i++) {
+ cache[i].data = p;
+ p += blocksize;
+ }
+
+ /* hand craft root */
+ b = blockLoad(tlr.root, DirBlock);
+ if(b == nil || !checkDirSize(b->data, b->data+blocksize))
+ sysfatal("could not read root block: %s", file);
+ r = getDir(b->data);
+ blockFree(b);
+ root = emallocz(sizeof(Paq));
+ root->qid = paqDirQid(r);
+ root->ref = 1;
+ root->dir = r;
+ root->up = root; /* parent of root is root */
+
+ /* craft root directory if root is a normal file */
+ if(!(root->qid.type&QTDIR)){
+ rootfile = root;
+ root = emallocz(sizeof(Paq));
+ root->qid = rootfile->qid;
+ root->qid.type |= QTDIR;
+ root->qid.path++;
+ root->ref = 1;
+ root->dir = emallocz(sizeof(PaqDir));
+ *root->dir = *r;
+ root->dir->mode |= DMDIR|0111;
+ root->up = root;
+ }
+}
+
+int
+blockRead(uchar *data, ulong addr, int type)
+{
+ uchar buf[BlockSize];
+ PaqBlock b;
+ uchar *cdat;
+
+ if(Bseek(bin, addr, 0) != addr){
+ fprint(2, "paqfs: seek %lud: %r\n", addr);
+ return 0;
+ }
+ if(Bread(bin, buf, BlockSize) != BlockSize){
+ fprint(2, "paqfs: read %d at %lud: %r\n", BlockSize, addr);
+ return 0;
+ }
+ getBlock(buf, &b);
+ if(b.magic != BlockMagic || b.size > blocksize || b.type != type){
+ fprint(2, "paqfs: bad block: magic %.8lux (want %.8ux) size %lud (max %d) type %ud (want %ud)\n",
+ b.magic, BlockMagic, b.size, blocksize, b.type, type);
+ return 0;
+ }
+
+ switch(b.encoding) {
+ default:
+ return 0;
+ case NoEnc:
+ if(Bread(bin, data, blocksize) < blocksize)
+ return 0;
+ break;
+ case DeflateEnc:
+ cdat = emalloc(b.size);
+ if(Bread(bin, cdat, b.size) < b.size) {
+ free(cdat);
+ return 0;
+ }
+ if(inflateblock(data, blocksize, cdat, b.size) < 0) {
+ fprint(2, "inflate error: %r\n");
+ free(cdat);
+ return 0;
+ }
+ free(cdat);
+ break;
+ }
+ if(adler32(0, data, blocksize) != b.adler32)
+ return 0;
+ return 1;
+}
+
+void
+readHeader(PaqHeader *hdr, char *name, DigestState *ds)
+{
+ uchar buf[HeaderSize];
+
+ if(Bread(bin, buf, HeaderSize) < HeaderSize)
+ sysfatal("could not read header: %s: %r", name);
+ if(ds)
+ sha1(buf, HeaderSize, 0, ds);
+ getHeader(buf, hdr);
+ if(hdr->magic != HeaderMagic)
+ sysfatal("bad header magic 0x%lux: %s", hdr->magic, name);
+ if(hdr->version != Version)
+ sysfatal("unknown file version: %s", name);
+}
+
+void
+readBlocks(char *name, DigestState *ds)
+{
+ uchar *buf;
+ PaqBlock b;
+
+ buf = emalloc(BlockSize+blocksize);
+
+ for(;;) {
+ if(Bread(bin, buf, 4) < 4)
+ sysfatal("could not read block: %s: %r", name);
+ Bseek(bin, -4, 1);
+ /* check if it is a data block */
+
+ if(getl(buf) != BlockMagic)
+ break;
+
+ if(Bread(bin, buf, BlockSize) < BlockSize)
+ sysfatal("could not read block: %s: %r", name);
+ if(ds)
+ sha1(buf, BlockSize, 0, ds);
+ getBlock(buf, &b);
+
+ if(b.size > blocksize)
+ sysfatal("bad block size: %lud: %s", b.size, name);
+ if(ds) {
+ if(Bread(bin, buf, b.size) < b.size)
+ sysfatal("sysfatal reading block: %s: %r", name);
+ sha1(buf, b.size, 0, ds);
+ } else
+ Bseek(bin, b.size, 1);
+ }
+
+ free(buf);
+}
+
+void
+readTrailer(PaqTrailer *tlr, char *name, DigestState *ds)
+{
+ uchar buf[TrailerSize];
+ uchar digest[SHA1dlen];
+
+ if(Bread(bin, buf, TrailerSize) < TrailerSize)
+ sysfatal("could not read trailer: %s: %r", name);
+ getTrailer(buf, tlr);
+ if(tlr->magic != TrailerMagic)
+ sysfatal("bad trailer magic: %s", name);
+ if(ds) {
+ sha1(buf, TrailerSize-SHA1dlen, digest, ds);
+ if(memcmp(digest, tlr->sha1, SHA1dlen) != 0)
+ sysfatal("bad sha1 digest: %s", name);
+ }
+}
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(!p)
+ sysfatal("out of memory");
+ return p;
+}
+
+void *
+emallocz(ulong n)
+{
+ void *p;
+
+ p = emalloc(n);
+ memset(p, 0, n);
+
+ return p;
+}
+
+void *
+erealloc(void *p, ulong n)
+{
+ p = realloc(p, n);
+ if(!p)
+ sysfatal("out of memory");
+ return p;
+}
+
+char *
+estrdup(char *s)
+{
+ s = strdup(s);
+ if(s == nil)
+ sysfatal("out of memory");
+ return s;
+}
+
+
+ulong
+getl(uchar *p)
+{
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+
+int
+gets(uchar *p)
+{
+ return (p[0]<<8) | p[1];
+}
+
+int
+checkDirSize(uchar *p, uchar *ep)
+{
+ int n;
+ int i;
+
+ if(ep-p < 2)
+ return 0;
+ n = gets(p);
+ if(p+n > ep)
+ return 0;
+ ep = p+n;
+ p += 22;
+ for(i=0; i<3; i++) {
+ if(p+2 > ep)
+ return 0;
+ n = gets(p);
+ if(p+n > ep)
+ return 0;
+ p += n;
+ }
+ return 1;
+}
+
+void
+getHeader(uchar *p, PaqHeader *h)
+{
+ h->magic = getl(p);
+ h->version = gets(p+4);
+ h->blocksize = gets(p+6);
+ if((h->magic>>16) == BigHeaderMagic){
+ h->magic = HeaderMagic;
+ h->version = gets(p+2);
+ h->blocksize = getl(p+4);
+ }
+ h->time = getl(p+8);
+ memmove(h->label, p+12, sizeof(h->label));
+ h->label[sizeof(h->label)-1] = 0;
+}
+
+void
+getTrailer(uchar *p, PaqTrailer *t)
+{
+ t->magic = getl(p);
+ t->root = getl(p+4);
+ memmove(t->sha1, p+8, SHA1dlen);
+}
+
+void
+getBlock(uchar *p, PaqBlock *b)
+{
+ b->magic = getl(p);
+ b->size = gets(p+4);
+ if((b->magic>>16) == BigBlockMagic){
+ b->magic = BlockMagic;
+ b->size = getl(p+2);
+ }
+ b->type = p[6];
+ b->encoding = p[7];
+ b->adler32 = getl(p+8);
+}
+
+PaqDir *
+getDir(uchar *p)
+{
+ PaqDir *pd;
+
+ pd = emallocz(sizeof(PaqDir));
+ pd->qid = getl(p+2);
+ pd->mode = getl(p+6);
+ pd->mtime = getl(p+10);
+ pd->length = getl(p+14);
+ pd->offset = getl(p+18);
+ p += 22;
+ pd->name = getstr(p);
+ p += gets(p);
+ pd->uid = getstr(p);
+ p += gets(p);
+ pd->gid = getstr(p);
+
+ return pd;
+}
+
+
+char *
+getstr(uchar *p)
+{
+ char *s;
+ int n;
+
+ n = gets(p);
+ s = emalloc(n+1);
+ memmove(s, p+2, n);
+ s[n] = 0;
+ return s;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-disv] [-c cachesize] [-m mountpoint] [-M mesgsize] paqfile\n", argv0);
+ exits("usage");
+}
+
diff --git a/sys/src/cmd/paqfs/paqfs.h b/sys/src/cmd/paqfs/paqfs.h
new file mode 100755
index 000000000..970ff9acc
--- /dev/null
+++ b/sys/src/cmd/paqfs/paqfs.h
@@ -0,0 +1,69 @@
+typedef struct PaqHeader PaqHeader;
+typedef struct PaqBlock PaqBlock;
+typedef struct PaqTrailer PaqTrailer;
+typedef struct PaqDir PaqDir;
+
+enum {
+ HeaderMagic = 0x529ab12b,
+ HeaderSize = 44,
+ BigHeaderMagic = 0x25a9,
+ BlockMagic = 0x198a1cbf,
+ BlockSize = 12,
+ BigBlockMagic = 0x91a8,
+ TrailerMagic = 0x6b46e688,
+ TrailerSize = 28,
+ Version = 1,
+ MaxBlockSize = 512*1024,
+ MinBlockSize = 512,
+ MinDirSize = 28,
+};
+
+/* block types */
+enum {
+ DirBlock,
+ DataBlock,
+ PointerBlock,
+};
+
+/* encodings */
+enum {
+ NoEnc,
+ DeflateEnc,
+};
+
+struct PaqHeader
+{
+ ulong magic;
+ ushort version;
+ ulong blocksize;
+ ulong time;
+ char label[32];
+};
+
+struct PaqBlock
+{
+ ulong magic;
+ ulong size; /* data size - always <= blocksize */
+ uchar type;
+ uchar encoding;
+ ulong adler32; /* applied to unencoded data */
+};
+
+struct PaqTrailer
+{
+ ulong magic;
+ ulong root;
+ uchar sha1[20];
+};
+
+struct PaqDir
+{
+ ulong qid;
+ ulong mode;
+ ulong mtime;
+ ulong length;
+ ulong offset; /* to pointer block */
+ char *name;
+ char *uid;
+ char *gid;
+};