aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2006-02-24 21:17:00 +0000
committerrsc <devnull@localhost>2006-02-24 21:17:00 +0000
commit64f7506b34106955fbbd5f6bf944ecd839610672 (patch)
treefa1efe1df3804519ef796042c457a34a2d9e187f /src
parent21b291a64e4f059744b4094a8f8e52784ad323f4 (diff)
downloadplan9port-64f7506b34106955fbbd5f6bf944ecd839610672.tar.gz
plan9port-64f7506b34106955fbbd5f6bf944ecd839610672.tar.bz2
plan9port-64f7506b34106955fbbd5f6bf944ecd839610672.zip
tapefs from plan9
Diffstat (limited to 'src')
-rw-r--r--src/cmd/tapefs/32vfs.c208
-rw-r--r--src/cmd/tapefs/cpiofs.c139
-rw-r--r--src/cmd/tapefs/fs.c600
-rw-r--r--src/cmd/tapefs/mkfile13
-rw-r--r--src/cmd/tapefs/tapefs.h95
-rw-r--r--src/cmd/tapefs/tapfs.c115
-rw-r--r--src/cmd/tapefs/tarfs.c144
-rw-r--r--src/cmd/tapefs/tpfs.c108
-rw-r--r--src/cmd/tapefs/util.c153
-rw-r--r--src/cmd/tapefs/v10fs.c209
-rw-r--r--src/cmd/tapefs/v6fs.c213
-rw-r--r--src/cmd/tapefs/zip.h83
-rw-r--r--src/cmd/tapefs/zipfs.c385
13 files changed, 2465 insertions, 0 deletions
diff --git a/src/cmd/tapefs/32vfs.c b/src/cmd/tapefs/32vfs.c
new file mode 100644
index 00000000..55d5dbe0
--- /dev/null
+++ b/src/cmd/tapefs/32vfs.c
@@ -0,0 +1,208 @@
+/*
+ * Vax 32V Unix filesystem (same as pre-FFS Berkeley)
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+/*
+ * v32 disk inode
+ */
+#define VNADDR 13
+#define VFMT 0160000
+#define VIFREG 0100000
+#define VIFDIR 0040000
+#define VIFCHR 0120000
+#define VIFBLK 0160000
+#define VMODE 0777
+#define VSUPERB 1
+#define VROOT 2 /* root inode */
+#define VNAMELEN 14
+#define BLSIZE 512
+#define LINOPB (BLSIZE/sizeof(struct v32dinode))
+#define LNINDIR (BLSIZE/sizeof(unsigned long))
+
+struct v32dinode {
+ unsigned char flags[2];
+ unsigned char nlinks[2];
+ unsigned char uid[2];
+ unsigned char gid[2];
+ unsigned char size[4];
+ unsigned char addr[40];
+ unsigned char atime[4];
+ unsigned char mtime[4];
+ unsigned char ctime[4];
+};
+
+struct v32dir {
+ uchar ino[2];
+ char name[VNAMELEN];
+};
+
+int tapefile;
+Fileinf iget(int ino);
+long bmap(Ram *r, long bno);
+void getblk(Ram *r, long bno, char *buf);
+
+void
+populate(char *name)
+{
+ Fileinf f;
+
+ replete = 0;
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ f = iget(VROOT);
+ ram->perm = f.mode;
+ ram->mtime = f.mdate;
+ ram->addr = f.addr;
+ ram->data = f.data;
+ ram->ndata = f.size;
+}
+
+void
+popdir(Ram *r)
+{
+ int i, ino;
+ char *cp;
+ struct v32dir *dp;
+ Fileinf f;
+ char name[VNAMELEN+1];
+
+ cp = 0;
+ for (i=0; i<r->ndata; i+=sizeof(struct v32dir)) {
+ if (i%BLSIZE==0)
+ cp = doread(r, i, BLSIZE);
+ dp = (struct v32dir *)(cp+i%BLSIZE);
+ ino = g2byte(dp->ino);
+ if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0)
+ continue;
+ if (ino==0)
+ continue;
+ f = iget(ino);
+ strncpy(name, dp->name, VNAMELEN);
+ name[VNAMELEN+1] = '\0';
+ f.name = name;
+ popfile(r, f);
+ }
+ r->replete = 1;
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ static char buf[Maxbuf+BLSIZE];
+ int bno, i;
+
+ bno = off/BLSIZE;
+ off -= bno*BLSIZE;
+ if (cnt>Maxbuf)
+ error("count too large");
+ if (off)
+ cnt += off;
+ i = 0;
+ while (cnt>0) {
+ getblk(r, bno, &buf[i*BLSIZE]);
+ cnt -= BLSIZE;
+ bno++;
+ i++;
+ }
+ return buf;
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
+
+/*
+ * fetch an i-node
+ * -- no sanity check for now
+ * -- magic inode-to-disk-block stuff here
+ */
+
+Fileinf
+iget(int ino)
+{
+ char buf[BLSIZE];
+ struct v32dinode *dp;
+ long flags, i;
+ Fileinf f;
+
+ seek(tapefile, BLSIZE*((ino-1)/LINOPB + VSUPERB + 1), 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("Can't read inode");
+ dp = ((struct v32dinode *)buf) + ((ino-1)%LINOPB);
+ flags = g2byte(dp->flags);
+ f.size = g4byte(dp->size);
+ if ((flags&VFMT)==VIFCHR || (flags&VFMT)==VIFBLK)
+ f.size = 0;
+ f.data = emalloc(VNADDR*sizeof(long));
+ for (i = 0; i < VNADDR; i++)
+ ((long*)f.data)[i] = g3byte(dp->addr+3*i);
+ f.mode = flags & VMODE;
+ if ((flags&VFMT)==VIFDIR)
+ f.mode |= DMDIR;
+ f.uid = g2byte(dp->uid);
+ f.gid = g2byte(dp->gid);
+ f.mdate = g4byte(dp->mtime);
+ return f;
+}
+
+void
+getblk(Ram *r, long bno, char *buf)
+{
+ long dbno;
+
+ if ((dbno = bmap(r, bno)) == 0) {
+ memset(buf, 0, BLSIZE);
+ return;
+ }
+ seek(tapefile, dbno*BLSIZE, 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("bad read");
+}
+
+/*
+ * logical to physical block
+ * only singly-indirect files for now
+ */
+
+long
+bmap(Ram *r, long bno)
+{
+ unsigned char indbuf[LNINDIR][sizeof(long)];
+
+ if (bno < VNADDR-3)
+ return ((long*)r->data)[bno];
+ if (bno < VNADDR*LNINDIR) {
+ seek(tapefile, ((long *)r->data)[(bno-(VNADDR-3))/LNINDIR]*BLSIZE, 0);
+ if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE)
+ return 0;
+ return ((indbuf[bno%LNINDIR][1]<<8) + indbuf[bno%LNINDIR][0]);
+ }
+ return 0;
+}
diff --git a/src/cmd/tapefs/cpiofs.c b/src/cmd/tapefs/cpiofs.c
new file mode 100644
index 00000000..fc11346d
--- /dev/null
+++ b/src/cmd/tapefs/cpiofs.c
@@ -0,0 +1,139 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+/*
+ * File system for cpio tapes (read-only)
+ */
+
+#define TBLOCK 512
+#define NBLOCK 40 /* maximum blocksize */
+#define DBLOCK 20 /* default blocksize */
+#define TNAMSIZ 100
+
+union hblock {
+ char dummy[TBLOCK];
+ char tbuf[Maxbuf];
+ struct header {
+ char magic[6];
+ char dev[6];
+ char ino[6];
+ char mode[6];
+ char uid[6];
+ char gid[6];
+ char nlink[6];
+ char rdev[6];
+ char mtime[11];
+ char namesize[6];
+ char size[11];
+ } dbuf;
+ struct hname {
+ struct header x;
+ char name[1];
+ } nbuf;
+} dblock;
+
+int tapefile;
+vlong getoct(char*, int);
+
+void
+populate(char *name)
+{
+ vlong offset;
+ long isabs, magic, namesize, mode;
+ Fileinf f;
+
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ replete = 1;
+ for (offset = 0;;) {
+ seek(tapefile, offset, 0);
+ if (read(tapefile, (char *)&dblock.dbuf, TBLOCK)<TBLOCK)
+ break;
+ magic = getoct(dblock.dbuf.magic, sizeof(dblock.dbuf.magic));
+ if (magic != 070707){
+ print("%lo\n", magic);
+ error("out of phase--get help");
+ }
+ if (dblock.nbuf.name[0]=='\0' || strcmp(dblock.nbuf.name, "TRAILER!!!")==0)
+ break;
+ mode = getoct(dblock.dbuf.mode, sizeof(dblock.dbuf.mode));
+ f.mode = mode&0777;
+ switch(mode & 0170000) {
+ case 0040000:
+ f.mode |= DMDIR;
+ break;
+ case 0100000:
+ break;
+ default:
+ f.mode = 0;
+ break;
+ }
+ f.uid = getoct(dblock.dbuf.uid, sizeof(dblock.dbuf.uid));
+ f.gid = getoct(dblock.dbuf.gid, sizeof(dblock.dbuf.gid));
+ f.size = getoct(dblock.dbuf.size, sizeof(dblock.dbuf.size));
+ f.mdate = getoct(dblock.dbuf.mtime, sizeof(dblock.dbuf.mtime));
+ namesize = getoct(dblock.dbuf.namesize, sizeof(dblock.dbuf.namesize));
+ f.addr = offset+sizeof(struct header)+namesize;
+ isabs = dblock.nbuf.name[0]=='/';
+ f.name = &dblock.nbuf.name[isabs];
+ poppath(f, 1);
+ offset += sizeof(struct header)+namesize+f.size;
+ }
+}
+
+vlong
+getoct(char *p, int l)
+{
+ vlong r;
+
+ for (r=0; l>0; p++, l--){
+ r <<= 3;
+ r += *p-'0';
+ }
+ return r;
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ seek(tapefile, r->addr+off, 0);
+ if (cnt>sizeof(dblock.tbuf))
+ error("read too big");
+ read(tapefile, dblock.tbuf, cnt);
+ return dblock.tbuf;
+}
+
+void
+popdir(Ram *r)
+{
+ USED(r);
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
diff --git a/src/cmd/tapefs/fs.c b/src/cmd/tapefs/fs.c
new file mode 100644
index 00000000..66abe366
--- /dev/null
+++ b/src/cmd/tapefs/fs.c
@@ -0,0 +1,600 @@
+#include <u.h>
+#include <libc.h>
+#include <authsrv.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+Fid *fids;
+Ram *ram;
+int mfd[2];
+char *user;
+uchar mdata[Maxbuf+IOHDRSZ];
+int messagesize = Maxbuf+IOHDRSZ;
+Fcall rhdr;
+Fcall thdr;
+ulong path;
+Idmap *uidmap;
+Idmap *gidmap;
+int replete;
+int verbose;
+int newtap; /* tap with time in sec */
+
+Fid * newfid(int);
+int ramstat(Ram*, uchar*, int);
+void io(void);
+void usage(void);
+int perm(int);
+
+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,
+ [Tauth] rauth,
+ [Tattach] rattach,
+ [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[] = "tapefs: 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";
+
+void
+notifyf(void *a, char *s)
+{
+ USED(a);
+ if(strncmp(s, "interrupt", 9) == 0)
+ noted(NCONT);
+ noted(NDFLT);
+}
+
+void
+main(int argc, char *argv[])
+{
+ Ram *r;
+ char *defmnt;
+ int p[2];
+ char buf[TICKREQLEN];
+
+ fmtinstall('F', fcallfmt);
+
+ defmnt = "/n/tapefs";
+ ARGBEGIN{
+ case 'm':
+ defmnt = ARGF();
+ break;
+ case 'p': /* password file */
+ uidmap = getpass(ARGF());
+ break;
+ case 'g': /* group file */
+ gidmap = getpass(ARGF());
+ break;
+ case 'v':
+ verbose++;
+
+ case 'n':
+ newtap++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc==0)
+ error("no file to mount");
+ user = getuser();
+ if(user == nil)
+ user = "dmr";
+ ram = r = (Ram *)emalloc(sizeof(Ram));
+ r->busy = 1;
+ r->data = 0;
+ r->ndata = 0;
+ r->perm = DMDIR | 0775;
+ r->qid.path = 0;
+ r->qid.vers = 0;
+ r->qid.type = QTDIR;
+ r->parent = 0;
+ r->child = 0;
+ r->next = 0;
+ r->user = user;
+ r->group = user;
+ r->atime = time(0);
+ r->mtime = r->atime;
+ r->replete = 0;
+ r->name = estrdup(".");
+ populate(argv[0]);
+ r->replete |= replete;
+ if(pipe(p) < 0)
+ error("pipe failed");
+ mfd[0] = mfd[1] = p[0];
+ notify(notifyf);
+
+ switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
+ case -1:
+ error("fork");
+ case 0:
+ close(p[1]);
+ notify(notifyf);
+ io();
+ break;
+ default:
+ close(p[0]); /* don't deadlock if child fails */
+ if(post9pservice(p[1], defmnt) < 0) {
+ sprint(buf, "post on `%s' failed", defmnt);
+ error(buf);
+ }
+ }
+ exits(0);
+}
+
+char*
+rversion(Fid *unused)
+{
+ Fid *f;
+
+ USED(unused);
+
+ if(rhdr.msize < 256)
+ return "version: message too small";
+ if(rhdr.msize > messagesize)
+ rhdr.msize = messagesize;
+ else
+ messagesize = rhdr.msize;
+ thdr.msize = messagesize;
+ if(strncmp(rhdr.version, "9P2000", 6) != 0)
+ return "unrecognized 9P version";
+ thdr.version = "9P2000";
+
+ for(f = fids; f; f = f->next)
+ if(f->busy)
+ rclunk(f);
+ return 0;
+}
+
+char*
+rauth(Fid *unused)
+{
+ USED(unused);
+
+ return Enoauth;
+}
+
+char*
+rflush(Fid *f)
+{
+ USED(f);
+ return 0;
+}
+
+char*
+rattach(Fid *f)
+{
+ /* no authentication! */
+ f->busy = 1;
+ f->rclose = 0;
+ f->ram = ram;
+ thdr.qid = f->ram->qid;
+ if(rhdr.uname[0])
+ f->user = strdup(rhdr.uname);
+ else
+ f->user = "none";
+ return 0;
+}
+
+char*
+rwalk(Fid *f)
+{
+ Fid *nf;
+ Ram *r;
+ char *err;
+ char *name;
+ Ram *dir;
+ int i;
+
+ nf = nil;
+ if(f->ram->busy == 0)
+ return Enotexist;
+ if(f->open)
+ return Eisopen;
+ if(rhdr.newfid != rhdr.fid){
+ nf = newfid(rhdr.newfid);
+ nf->busy = 1;
+ nf->open = 0;
+ nf->rclose = 0;
+ nf->ram = f->ram;
+ nf->user = f->user; /* no ref count; the leakage is minor */
+ f = nf;
+ }
+
+ thdr.nwqid = 0;
+ err = nil;
+ r = f->ram;
+
+ if(rhdr.nwname > 0){
+ for(i=0; i<rhdr.nwname; i++){
+ if((r->qid.type & QTDIR) == 0){
+ err = Enotdir;
+ break;
+ }
+ if(r->busy == 0){
+ err = Enotexist;
+ break;
+ }
+ r->atime = time(0);
+ name = rhdr.wname[i];
+ dir = r;
+ if(!perm(Pexec)){
+ err = Eperm;
+ break;
+ }
+ if(strcmp(name, "..") == 0){
+ r = dir->parent;
+ Accept:
+ if(i == MAXWELEM){
+ err = "name too long";
+ break;
+ }
+ thdr.wqid[thdr.nwqid++] = r->qid;
+ continue;
+ }
+ if(!dir->replete)
+ popdir(dir);
+ for(r=dir->child; r; r=r->next)
+ if(r->busy && strcmp(name, r->name)==0)
+ goto Accept;
+ break; /* file not found */
+ }
+
+ if(i==0 && err == nil)
+ err = Enotexist;
+ }
+
+ if(err!=nil || thdr.nwqid<rhdr.nwname){
+ if(nf){
+ nf->busy = 0;
+ nf->open = 0;
+ nf->ram = 0;
+ }
+ }else if(thdr.nwqid == rhdr.nwname)
+ f->ram = r;
+
+ return err;
+
+}
+
+char *
+ropen(Fid *f)
+{
+ Ram *r;
+ int mode, trunc;
+
+ if(f->open)
+ return Eisopen;
+ r = f->ram;
+ if(r->busy == 0)
+ return Enotexist;
+ if(r->perm & DMEXCL)
+ if(r->open)
+ return Excl;
+ mode = rhdr.mode;
+ if(r->qid.type & QTDIR){
+ if(mode != OREAD)
+ return Eperm;
+ thdr.qid = r->qid;
+ return 0;
+ }
+ if(mode & ORCLOSE)
+ return Eperm;
+ trunc = mode & OTRUNC;
+ mode &= OPERM;
+ if(mode==OWRITE || mode==ORDWR || trunc)
+ if(!perm(Pwrite))
+ return Eperm;
+ if(mode==OREAD || mode==ORDWR)
+ if(!perm(Pread))
+ return Eperm;
+ if(mode==OEXEC)
+ if(!perm(Pexec))
+ return Eperm;
+ if(trunc && (r->perm&DMAPPEND)==0){
+ r->ndata = 0;
+ dotrunc(r);
+ r->qid.vers++;
+ }
+ thdr.qid = r->qid;
+ thdr.iounit = messagesize-IOHDRSZ;
+ f->open = 1;
+ r->open++;
+ return 0;
+}
+
+char *
+rcreate(Fid *f)
+{
+ USED(f);
+
+ return Eperm;
+}
+
+char*
+rread(Fid *f)
+{
+ int i, len;
+ Ram *r;
+ char *buf;
+ uvlong off, end;
+ int n, cnt;
+
+ if(f->ram->busy == 0)
+ return Enotexist;
+ n = 0;
+ thdr.count = 0;
+ off = rhdr.offset;
+ end = rhdr.offset + rhdr.count;
+ cnt = rhdr.count;
+ if(cnt > messagesize-IOHDRSZ)
+ cnt = messagesize-IOHDRSZ;
+ buf = thdr.data;
+ if(f->ram->qid.type & QTDIR){
+ if(!f->ram->replete)
+ popdir(f->ram);
+ for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){
+ if(!r->busy)
+ continue;
+ len = ramstat(r, (uchar*)buf+n, cnt-n);
+ if(len <= BIT16SZ)
+ break;
+ if(i >= off)
+ n += len;
+ i += len;
+ }
+ thdr.count = n;
+ return 0;
+ }
+ r = f->ram;
+ if(off >= r->ndata)
+ return 0;
+ r->atime = time(0);
+ n = cnt;
+ if(off+n > r->ndata)
+ n = r->ndata - off;
+ thdr.data = doread(r, off, n);
+ thdr.count = n;
+ return 0;
+}
+
+char*
+rwrite(Fid *f)
+{
+ Ram *r;
+ ulong off;
+ int cnt;
+
+ r = f->ram;
+ if(dopermw(f->ram)==0)
+ return Eperm;
+ if(r->busy == 0)
+ return Enotexist;
+ off = rhdr.offset;
+ if(r->perm & DMAPPEND)
+ off = r->ndata;
+ cnt = rhdr.count;
+ if(r->qid.type & QTDIR)
+ return "file is a directory";
+ if(off > 100*1024*1024) /* sanity check */
+ return "write too big";
+ dowrite(r, rhdr.data, off, cnt);
+ r->qid.vers++;
+ r->mtime = time(0);
+ thdr.count = cnt;
+ return 0;
+}
+
+char *
+rclunk(Fid *f)
+{
+ if(f->open)
+ f->ram->open--;
+ f->busy = 0;
+ f->open = 0;
+ f->ram = 0;
+ return 0;
+}
+
+char *
+rremove(Fid *f)
+{
+ USED(f);
+ return Eperm;
+}
+
+char *
+rstat(Fid *f)
+{
+ if(f->ram->busy == 0)
+ return Enotexist;
+ thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
+ return 0;
+}
+
+char *
+rwstat(Fid *f)
+{
+ if(f->ram->busy == 0)
+ return Enotexist;
+ return Eperm;
+}
+
+int
+ramstat(Ram *r, uchar *buf, int nbuf)
+{
+ Dir dir;
+
+ dir.name = r->name;
+ dir.qid = r->qid;
+ dir.mode = r->perm;
+ dir.length = r->ndata;
+ dir.uid = r->user;
+ dir.gid = r->group;
+ dir.muid = r->user;
+ dir.atime = r->atime;
+ dir.mtime = r->mtime;
+ return convD2M(&dir, buf, nbuf);
+}
+
+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;
+ ff->open = 0;
+ ff->busy = 1;
+ }
+ f = emalloc(sizeof *f);
+ f->ram = 0;
+ f->fid = fid;
+ f->busy = 1;
+ f->open = 0;
+ f->next = fids;
+ fids = f;
+ return f;
+}
+
+void
+io(void)
+{
+ char *err;
+ int n, nerr;
+ char buf[ERRMAX];
+
+ errstr(buf, sizeof buf);
+ for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
+ /*
+ * reading from a pipe or a network device
+ * will give an error after a few eof reads
+ * however, we cannot tell the difference
+ * between a zero-length read and an interrupt
+ * on the processes writing to us,
+ * so we wait for the error
+ */
+ n = read9pmsg(mfd[0], mdata, sizeof mdata);
+ if(n==0)
+ continue;
+ if(n < 0){
+ if(buf[0]=='\0')
+ errstr(buf, sizeof buf);
+ continue;
+ }
+ nerr = 0;
+ buf[0] = '\0';
+ if(convM2S(mdata, n, &rhdr) != n)
+ error("convert error in convM2S");
+
+ if(verbose)
+ fprint(2, "tapefs: <=%F\n", &rhdr);/**/
+
+ thdr.data = (char*)mdata + IOHDRSZ;
+ thdr.stat = 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;
+ n = convS2M(&thdr, mdata, messagesize);
+ if(n <= 0)
+ error("convert error in convS2M");
+ if(verbose)
+ fprint(2, "tapefs: =>%F\n", &thdr);/**/
+ if(write(mfd[1], mdata, n) != n)
+ error("mount write");
+ }
+ if(buf[0]=='\0' || strstr(buf, "hungup"))
+ exits("");
+ fprint(2, "%s: mount read: %s\n", argv0, buf);
+ exits(buf);
+}
+
+int
+perm(int p)
+{
+ if(p==Pwrite)
+ return 0;
+ return 1;
+}
+
+void
+error(char *s)
+{
+ fprint(2, "%s: %s: ", argv0, s);
+ perror("");
+ exits(s);
+}
+
+char*
+estrdup(char *s)
+{
+ char *t;
+
+ t = emalloc(strlen(s)+1);
+ strcpy(t, s);
+ return t;
+}
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+ p = mallocz(n, 1);
+ if(!p)
+ error("out of memory");
+ return p;
+}
+
+void *
+erealloc(void *p, ulong n)
+{
+ p = realloc(p, n);
+ if(!p)
+ error("out of memory");
+ return p;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
+ exits("usage");
+}
diff --git a/src/cmd/tapefs/mkfile b/src/cmd/tapefs/mkfile
new file mode 100644
index 00000000..dcf8783a
--- /dev/null
+++ b/src/cmd/tapefs/mkfile
@@ -0,0 +1,13 @@
+<$PLAN9/src/mkhdr
+
+TARG=tarfs tpfs v6fs 32vfs cpiofs tapfs v10fs zipfs
+OFILES=\
+ fs.$O\
+ util.$O\
+
+HFILES=tapefs.h
+
+BIN=$BIN/fs
+<$PLAN9/src/mkmany
+
+zipfs.$O: zip.h
diff --git a/src/cmd/tapefs/tapefs.h b/src/cmd/tapefs/tapefs.h
new file mode 100644
index 00000000..ae5e1844
--- /dev/null
+++ b/src/cmd/tapefs/tapefs.h
@@ -0,0 +1,95 @@
+#define getpass tapefs_getpass
+
+#define g2byte(x) (((x)[1]<<8) + (x)[0]) /* little-endian */
+#define g3byte(x) (((x)[2]<<16) + ((x)[1]<<8) + (x)[0])
+#define g4byte(x) (((x)[3]<<24) + ((x)[2]<<16) + ((x)[1]<<8) + (x)[0])
+
+enum
+{
+ OPERM = 0x3, /* mask of all permission types in open mode */
+ Nram = 512,
+ Maxbuf = 8192, /* max buffer size */
+};
+
+typedef struct Fid Fid;
+typedef struct Ram Ram;
+
+struct Fid
+{
+ short busy;
+ short open;
+ short rclose;
+ int fid;
+ Fid *next;
+ char *user;
+ Ram *ram;
+};
+
+struct Ram
+{
+ char busy;
+ char open;
+ char replete;
+ Ram *parent; /* parent directory */
+ Ram *child; /* first member of directory */
+ Ram *next; /* next member of file's directory */
+ Qid qid;
+ long perm;
+ char *name;
+ ulong atime;
+ ulong mtime;
+ char *user;
+ char *group;
+ vlong addr;
+ void *data;
+ long ndata;
+};
+
+enum
+{
+ Pexec = 1,
+ Pwrite = 2,
+ Pread = 4,
+ Pother = 1,
+ Pgroup = 8,
+ Powner = 64,
+};
+
+typedef struct idmap {
+ char *name;
+ int id;
+} Idmap;
+
+typedef struct fileinf {
+ char *name;
+ vlong addr;
+ void *data;
+ vlong size;
+ int mode;
+ int uid;
+ int gid;
+ long mdate;
+} Fileinf;
+
+extern ulong path; /* incremented for each new file */
+extern Ram *ram;
+extern char *user;
+extern Idmap *uidmap;
+extern Idmap *gidmap;
+extern int replete;
+void error(char*);
+void *erealloc(void*, ulong);
+void *emalloc(ulong);
+char *estrdup(char*);
+void populate(char *);
+void dotrunc(Ram*);
+void docreate(Ram*);
+char *doread(Ram*, vlong, long);
+void dowrite(Ram*, char*, long, long);
+int dopermw(Ram*);
+Idmap *getpass(char*);
+char *mapid(Idmap*,int);
+Ram *poppath(Fileinf fi, int new);
+Ram *popfile(Ram *dir, Fileinf fi);
+void popdir(Ram*);
+Ram *lookup(Ram*, char*);
diff --git a/src/cmd/tapefs/tapfs.c b/src/cmd/tapefs/tapfs.c
new file mode 100644
index 00000000..fb1738c2
--- /dev/null
+++ b/src/cmd/tapefs/tapfs.c
@@ -0,0 +1,115 @@
+#include <u.h>
+#include <libc.h>
+#include "tapefs.h"
+
+/*
+ * File system for old tap tapes.
+ */
+
+struct tap {
+ unsigned char name[32];
+ unsigned char mode[1];
+ unsigned char uid[1];
+ unsigned char size[2];
+ unsigned char tmod[4];
+ unsigned char taddress[2];
+ unsigned char unused[20];
+ unsigned char checksum[2];
+} dir[192];
+
+int tapefile;
+char buffer[8192];
+long cvtime(unsigned char *);
+extern int verbose;
+extern int newtap;
+
+void
+populate(char *name)
+{
+ int i, isabs;
+ struct tap *tpp;
+ Fileinf f;
+
+ replete = 1;
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ read(tapefile, dir, sizeof dir);
+ for (i=0, tpp=&dir[8]; i<192; i++, tpp++) {
+ unsigned char *sp = (unsigned char *)tpp;
+ int j, cksum = 0;
+ for (j=0; j<32; j++, sp+=2)
+ cksum += sp[0] + (sp[1]<<8);
+ cksum &= 0xFFFF;
+ if (cksum!=0) {
+ print("cksum failure\n");
+ continue;
+ }
+ if (tpp->name[0]=='\0')
+ continue;
+ f.addr = tpp->taddress[0] + (tpp->taddress[1]<<8);
+ if (f.addr==0)
+ continue;
+ f.size = tpp->size[0] + (tpp->size[1]<<8);
+ f.mdate = cvtime(tpp->tmod);
+ f.mode = tpp->mode[0]&0777;
+ f.uid = tpp->uid[0]&0377;
+ isabs = tpp->name[0]=='/';
+ f.name = (char *)tpp->name+isabs;
+ if (verbose)
+ print("%s mode %o uid %d, %s", f.name, f.mode, f.uid, ctime(f.mdate));
+ poppath(f, 1);
+ }
+}
+
+long
+cvtime(unsigned char *tp)
+{
+ unsigned long t = (tp[1]<<24)+(tp[0]<<16)+(tp[3]<<8)+(tp[2]<<0);
+ if (!newtap) {
+ t /= 60;
+ t += 3*365*24*3600;
+ }
+ return t;
+}
+
+void
+popdir(Ram *r)
+{
+ USED(r);
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ if (cnt>sizeof(buffer))
+ print("count too big\n");
+ seek(tapefile, 512*r->addr+off, 0);
+ read(tapefile, buffer, cnt);
+ return buffer;
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
diff --git a/src/cmd/tapefs/tarfs.c b/src/cmd/tapefs/tarfs.c
new file mode 100644
index 00000000..5bbd8b9c
--- /dev/null
+++ b/src/cmd/tapefs/tarfs.c
@@ -0,0 +1,144 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+/*
+ * File system for tar tapes (read-only)
+ */
+
+#define TBLOCK 512
+#define NBLOCK 40 /* maximum blocksize */
+#define DBLOCK 20 /* default blocksize */
+#define TNAMSIZ 100
+
+union hblock {
+ char dummy[TBLOCK];
+ char tbuf[Maxbuf];
+ struct header {
+ char name[TNAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[TNAMSIZ];
+ } dbuf;
+} dblock;
+
+int tapefile;
+int checksum(void);
+
+void
+populate(char *name)
+{
+ long blkno, isabs, chksum, linkflg;
+ Fileinf f;
+
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ replete = 1;
+ for (blkno = 0;;) {
+ seek(tapefile, TBLOCK*blkno, 0);
+ if (read(tapefile, dblock.dummy, sizeof(dblock.dummy))<sizeof(dblock.dummy))
+ break;
+ if (dblock.dbuf.name[0]=='\0')
+ break;
+ f.addr = blkno+1;
+ f.mode = strtoul(dblock.dbuf.mode, 0, 8);
+ f.uid = strtoul(dblock.dbuf.uid, 0, 8);
+ f.gid = strtoul(dblock.dbuf.gid, 0, 8);
+ if((uchar)dblock.dbuf.size[0] == 0x80)
+ f.size = g8byte(dblock.dbuf.size+3);
+ else
+ f.size = strtoull(dblock.dbuf.size, 0, 8);
+ f.mdate = strtoul(dblock.dbuf.mtime, 0, 8);
+ chksum = strtoul(dblock.dbuf.chksum, 0, 8);
+ /* the mode test is ugly but sometimes necessary */
+ if (dblock.dbuf.linkflag == '5'
+ || (f.mode&0170000) == 040000
+ || strrchr(dblock.dbuf.name, '\0')[-1] == '/'){
+ f.mode |= DMDIR;
+ f.size = 0;
+ }
+ f.mode &= DMDIR|0777;
+ linkflg = dblock.dbuf.linkflag=='s' || dblock.dbuf.linkflag=='1';
+ isabs = dblock.dbuf.name[0]=='/';
+ if (chksum != checksum()){
+ fprint(1, "bad checksum on %.28s\n", dblock.dbuf.name);
+ exits("checksum");
+ }
+ if (linkflg) {
+ /*fprint(2, "link %s->%s skipped\n", dblock.dbuf.name,
+ dblock.dbuf.linkname);*/
+ f.size = 0;
+ blkno += 1;
+ continue;
+ }
+ f.name = dblock.dbuf.name+isabs;
+ if (f.name[0]=='\0')
+ fprint(1, "null name skipped\n");
+ else
+ poppath(f, 1);
+ blkno += 1 + (f.size+TBLOCK-1)/TBLOCK;
+ }
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ seek(tapefile, TBLOCK*r->addr+off, 0);
+ if (cnt>sizeof(dblock.tbuf))
+ error("read too big");
+ read(tapefile, dblock.tbuf, cnt);
+ return dblock.tbuf;
+}
+
+void
+popdir(Ram *r)
+{
+ USED(r);
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
+
+int
+checksum()
+{
+ int i;
+ char *cp;
+
+ for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
+ *cp = ' ';
+ i = 0;
+ for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
+ i += *cp&0xff;
+ return(i);
+}
diff --git a/src/cmd/tapefs/tpfs.c b/src/cmd/tapefs/tpfs.c
new file mode 100644
index 00000000..02079a3a
--- /dev/null
+++ b/src/cmd/tapefs/tpfs.c
@@ -0,0 +1,108 @@
+#include <u.h>
+#include <libc.h>
+#include "tapefs.h"
+
+/*
+ * File system for tp tapes. dectape versions have 192
+ * entries, magtape have 496. This treats the same
+ * by ignoring entries with bad header checksums
+ */
+
+struct tp {
+ unsigned char name[32];
+ unsigned char mode[2];
+ unsigned char uid[1];
+ unsigned char gid[1];
+ unsigned char unused[1];
+ unsigned char size[3];
+ unsigned char tmod[4];
+ unsigned char taddress[2];
+ unsigned char unused2[16];
+ unsigned char checksum[2];
+} dir[496+8];
+
+char buffer[8192];
+int tapefile;
+
+void
+populate(char *name)
+{
+ int i, isabs, badcksum, goodcksum;
+ struct tp *tpp;
+ Fileinf f;
+
+ replete = 1;
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ read(tapefile, dir, sizeof dir);
+ badcksum = goodcksum = 0;
+ for (i=0, tpp=&dir[8]; i<496; i++, tpp++) {
+ unsigned char *sp = (unsigned char *)tpp;
+ int j, cksum = 0;
+ for (j=0; j<32; j++, sp+=2)
+ cksum += sp[0] + (sp[1]<<8);
+ cksum &= 0xFFFF;
+ if (cksum!=0) {
+ badcksum++;
+ continue;
+ }
+ goodcksum++;
+ if (tpp->name[0]=='\0')
+ continue;
+ f.addr = tpp->taddress[0] + (tpp->taddress[1]<<8);
+ if (f.addr==0)
+ continue;
+ f.size = (tpp->size[0]<<16) + (tpp->size[1]<<0) + (tpp->size[2]<<8);
+ f.mdate = (tpp->tmod[2]<<0) + (tpp->tmod[3]<<8)
+ +(tpp->tmod[0]<<16) + (tpp->tmod[1]<<24);
+ f.mode = tpp->mode[0]&0777;
+ f.uid = tpp->uid[0];
+ f.gid = tpp->gid[0];
+ isabs = tpp->name[0]=='/';
+ f.name = (char *)tpp->name+isabs;
+ poppath(f, 1);
+ }
+ fprint(2, "%d bad checksums, %d good\n", badcksum, goodcksum);
+}
+
+void
+popdir(Ram *r)
+{
+ USED(r);
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ if (cnt>sizeof(buffer))
+ print("count too big\n");
+ seek(tapefile, 512*r->addr+off, 0);
+ read(tapefile, buffer, cnt);
+ return buffer;
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
diff --git a/src/cmd/tapefs/util.c b/src/cmd/tapefs/util.c
new file mode 100644
index 00000000..199235f3
--- /dev/null
+++ b/src/cmd/tapefs/util.c
@@ -0,0 +1,153 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <bio.h>
+#include "tapefs.h"
+
+Idmap *
+getpass(char *file)
+{
+ Biobuf *bp;
+ char *cp;
+ Idmap *up;
+ int nid, maxid;
+ char *line[4];
+
+ if ((bp = Bopen(file, OREAD)) == 0)
+ error("Can't open passwd/group");
+ up = emalloc(1*sizeof(Idmap));
+ maxid = 1;
+ nid = 0;
+ while ((cp = Brdline(bp, '\n'))) {
+ int nf;
+ cp[Blinelen(bp)-1] = 0;
+ nf = getfields(cp, line, 3, 0, ":\n");
+ if (nf<3) {
+ fprint(2, "bad format in %s\n", file);
+ break;
+ }
+ if (nid>=maxid) {
+ maxid *= 2;
+ up = (Idmap *)erealloc(up, maxid*sizeof(Idmap));
+ }
+ up[nid].id = atoi(line[2]);
+ up[nid].name = strdup(line[0]);
+ nid++;
+ }
+ Bterm(bp);
+ up[nid].name = 0;
+ return up;
+}
+
+char *
+mapid(Idmap *up, int id)
+{
+ char buf[16];
+
+ if (up)
+ while (up->name){
+ if (up->id==id)
+ return strdup(up->name);
+ up++;
+ }
+ sprint(buf, "%d", id);
+ return strdup(buf);
+}
+
+Ram *
+poppath(Fileinf fi, int new)
+{
+ char *suffix;
+ Ram *dir, *ent;
+ Fileinf f;
+
+ if (*fi.name=='\0')
+ return 0;
+ if (suffix=strrchr(fi.name, '/')){
+ *suffix = 0;
+ suffix++;
+ if (*suffix=='\0'){
+ fi.mode |= DMDIR;
+ return poppath(fi, 1);
+ }
+ f = fi;
+ f.size = 0;
+ f.addr = 0;
+ f.mode = 0555|DMDIR;
+ dir = poppath(f, 0);
+ if (dir==0)
+ dir = ram;
+ } else {
+ suffix = fi.name;
+ dir = ram;
+ if (strcmp(suffix, ".")==0)
+ return dir;
+ }
+ ent = lookup(dir, suffix);
+ fi.mode |= 0400; /* at least user read */
+ if (ent){
+ if (((fi.mode&DMDIR)!=0) != ((ent->qid.type&QTDIR)!=0)){
+ fprint(2, "%s/%s directory botch\n", fi.name, suffix);
+ exits("");
+ }
+ if (new) {
+ ent->ndata = fi.size;
+ ent->addr = fi.addr;
+ ent->data = fi.data;
+ ent->perm = fi.mode;
+ ent->mtime = fi.mdate;
+ ent->user = mapid(uidmap, fi.uid);
+ ent->group = mapid(gidmap, fi.gid);
+ }
+ } else {
+ fi.name = suffix;
+ ent = popfile(dir, fi);
+ }
+ return ent;
+}
+
+Ram *
+popfile(Ram *dir, Fileinf fi)
+{
+ Ram *ent = (Ram *)emalloc(sizeof(Ram));
+ if (*fi.name=='\0')
+ return 0;
+ ent->busy = 1;
+ ent->open = 0;
+ ent->parent = dir;
+ ent->next = dir->child;
+ dir->child = ent;
+ ent->child = 0;
+ ent->qid.path = ++path;
+ ent->qid.vers = 0;
+ if(fi.mode&DMDIR)
+ ent->qid.type = QTDIR;
+ else
+ ent->qid.type = QTFILE;
+ ent->perm = fi.mode;
+ ent->name = estrdup(fi.name);
+ ent->atime = ent->mtime = fi.mdate;
+ ent->user = mapid(uidmap, fi.uid);
+ ent->group = mapid(gidmap, fi.gid);
+ ent->ndata = fi.size;
+ ent->data = fi.data;
+ ent->addr = fi.addr;
+ ent->replete |= replete;
+ return ent;
+}
+
+Ram *
+lookup(Ram *dir, char *name)
+{
+ Ram *r;
+
+ if (dir==0)
+ return 0;
+ for (r=dir->child; r; r=r->next){
+ if (r->busy==0 || strcmp(r->name, name)!=0)
+ continue;
+ return r;
+ }
+ return 0;
+}
diff --git a/src/cmd/tapefs/v10fs.c b/src/cmd/tapefs/v10fs.c
new file mode 100644
index 00000000..64e6edd4
--- /dev/null
+++ b/src/cmd/tapefs/v10fs.c
@@ -0,0 +1,209 @@
+/*
+ * 10th edition 4K file system
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+/*
+ * v10 disk inode
+ */
+#define VNADDR 13
+#define VFMT 0160000
+#define VIFREG 0100000
+#define VIFDIR 0040000
+#define VIFCHR 0120000
+#define VIFBLK 0160000
+#define VMODE 0777
+#define VSUPERB 1
+#define VROOT 2 /* root inode */
+#define VNAMELEN 14
+#define BLSIZE 4096
+#define LINOPB (BLSIZE/sizeof(struct v10dinode))
+#define LNINDIR (BLSIZE/sizeof(unsigned long))
+
+struct v10dinode {
+ unsigned char flags[2];
+ unsigned char nlinks[2];
+ unsigned char uid[2];
+ unsigned char gid[2];
+ unsigned char size[4];
+ unsigned char addr[40];
+ unsigned char atime[4];
+ unsigned char mtime[4];
+ unsigned char ctime[4];
+};
+
+struct v10dir {
+ uchar ino[2];
+ char name[VNAMELEN];
+};
+
+int tapefile;
+Fileinf iget(int ino);
+long bmap(Ram *r, long bno);
+void getblk(Ram *r, long bno, char *buf);
+
+void
+populate(char *name)
+{
+ Fileinf f;
+
+ replete = 0;
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ f = iget(VROOT);
+ ram->perm = f.mode;
+ ram->mtime = f.mdate;
+ ram->addr = f.addr;
+ ram->data = f.data;
+ ram->ndata = f.size;
+}
+
+void
+popdir(Ram *r)
+{
+ int i, ino;
+ char *cp;
+ struct v10dir *dp;
+ Fileinf f;
+ char name[VNAMELEN+1];
+
+ cp = 0;
+ for (i=0; i<r->ndata; i+=sizeof(struct v10dir)) {
+ if (i%BLSIZE==0)
+ cp = doread(r, i, BLSIZE);
+ dp = (struct v10dir *)(cp+i%BLSIZE);
+ ino = g2byte(dp->ino);
+ if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0)
+ continue;
+ if (ino==0)
+ continue;
+ f = iget(ino);
+ strncpy(name, dp->name, VNAMELEN);
+ name[VNAMELEN+1] = '\0';
+ f.name = name;
+ popfile(r, f);
+ }
+ r->replete = 1;
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ static char buf[Maxbuf+BLSIZE];
+ int bno, i;
+
+ bno = off/BLSIZE;
+ off -= bno*BLSIZE;
+ if (cnt>Maxbuf)
+ error("count too large");
+ if (off)
+ cnt += off;
+ i = 0;
+ while (cnt>0) {
+ getblk(r, bno, &buf[i*BLSIZE]);
+ cnt -= BLSIZE;
+ bno++;
+ i++;
+ }
+ return buf+off;
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
+
+/*
+ * fetch an i-node
+ * -- no sanity check for now
+ * -- magic inode-to-disk-block stuff here
+ */
+
+Fileinf
+iget(int ino)
+{
+ char buf[BLSIZE];
+ struct v10dinode *dp;
+ long flags, i;
+ Fileinf f;
+
+ seek(tapefile, BLSIZE*((ino-1)/LINOPB + VSUPERB + 1), 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("Can't read inode");
+ dp = ((struct v10dinode *)buf) + ((ino-1)%LINOPB);
+ flags = g2byte(dp->flags);
+ f.size = g4byte(dp->size);
+ if ((flags&VFMT)==VIFCHR || (flags&VFMT)==VIFBLK)
+ f.size = 0;
+ f.data = emalloc(VNADDR*sizeof(long));
+ for (i = 0; i < VNADDR; i++)
+ ((long*)f.data)[i] = g3byte(dp->addr+3*i);
+ f.mode = flags & VMODE;
+ if ((flags&VFMT)==VIFDIR)
+ f.mode |= DMDIR;
+ f.uid = g2byte(dp->uid);
+ f.gid = g2byte(dp->gid);
+ f.mdate = g4byte(dp->mtime);
+ return f;
+}
+
+void
+getblk(Ram *r, long bno, char *buf)
+{
+ long dbno;
+
+ if ((dbno = bmap(r, bno)) == 0) {
+ memset(buf, 0, BLSIZE);
+ return;
+ }
+ seek(tapefile, dbno*BLSIZE, 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("bad read");
+}
+
+/*
+ * logical to physical block
+ * only singly-indirect files for now
+ */
+
+long
+bmap(Ram *r, long bno)
+{
+ unsigned char indbuf[LNINDIR][sizeof(long)];
+
+ if (bno < VNADDR-3)
+ return ((long*)r->data)[bno];
+ if (bno < VNADDR*LNINDIR) {
+ seek(tapefile, ((long *)r->data)[(bno-(VNADDR-3))/LNINDIR+(VNADDR-3)]*BLSIZE, 0);
+ if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE)
+ return 0;
+ return ((indbuf[(bno-(VNADDR-3))%LNINDIR][2]<<16) + (indbuf[(bno-(VNADDR-3))%LNINDIR][1]<<8)
+ + indbuf[(bno-(VNADDR-3))%LNINDIR][0]);
+ }
+ return 0;
+}
diff --git a/src/cmd/tapefs/v6fs.c b/src/cmd/tapefs/v6fs.c
new file mode 100644
index 00000000..46669de5
--- /dev/null
+++ b/src/cmd/tapefs/v6fs.c
@@ -0,0 +1,213 @@
+/*
+ * old (V6 and before) PDP-11 Unix filesystem
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "tapefs.h"
+
+/*
+ * v6 disk inode
+ */
+#define V6NADDR 8
+#define V6FMT 0160000
+#define V6IFREG 0100000
+#define V6IFDIR 0140000
+#define V6IFCHR 0120000
+#define V6IFBLK 0160000
+#define V6MODE 0777
+#define V6LARGE 010000
+#define V6SUPERB 1
+#define V6ROOT 1 /* root inode */
+#define V6NAMELEN 14
+#define BLSIZE 512
+#define LINOPB (BLSIZE/sizeof(struct v6dinode))
+#define LNINDIR (BLSIZE/sizeof(unsigned short))
+
+struct v6dinode {
+ unsigned char flags[2];
+ unsigned char nlinks;
+ unsigned char uid;
+ unsigned char gid;
+ unsigned char hisize;
+ unsigned char losize[2];
+ unsigned char addr[V6NADDR][2];
+ unsigned char atime[4]; /* pdp-11 order */
+ unsigned char mtime[4]; /* pdp-11 order */
+};
+
+struct v6dir {
+ uchar ino[2];
+ char name[V6NAMELEN];
+};
+
+int tapefile;
+Fileinf iget(int ino);
+long bmap(Ram *r, long bno);
+void getblk(Ram *r, long bno, char *buf);
+
+void
+populate(char *name)
+{
+ Fileinf f;
+
+ replete = 0;
+ tapefile = open(name, OREAD);
+ if (tapefile<0)
+ error("Can't open argument file");
+ f = iget(V6ROOT);
+ ram->perm = f.mode;
+ ram->mtime = f.mdate;
+ ram->addr = f.addr;
+ ram->data = f.data;
+ ram->ndata = f.size;
+}
+
+void
+popdir(Ram *r)
+{
+ int i, ino;
+ char *cp;
+ struct v6dir *dp;
+ Fileinf f;
+ char name[V6NAMELEN+1];
+
+ cp = 0;
+ for (i=0; i<r->ndata; i+=sizeof(struct v6dir)) {
+ if (i%BLSIZE==0)
+ cp = doread(r, i, BLSIZE);
+ dp = (struct v6dir *)(cp+i%BLSIZE);
+ ino = dp->ino[0] + (dp->ino[1]<<8);
+ if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0)
+ continue;
+ if (ino==0)
+ continue;
+ f = iget(ino);
+ strncpy(name, dp->name, V6NAMELEN);
+ name[V6NAMELEN+1] = '\0';
+ f.name = name;
+ popfile(r, f);
+ }
+ r->replete = 1;
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ static char buf[Maxbuf+BLSIZE];
+ int bno, i;
+
+ bno = off/BLSIZE;
+ off -= bno*BLSIZE;
+ if (cnt>Maxbuf)
+ error("count too large");
+ if (off)
+ cnt += off;
+ i = 0;
+ while (cnt>0) {
+ getblk(r, bno, &buf[i*BLSIZE]);
+ cnt -= BLSIZE;
+ bno++;
+ i++;
+ }
+ return buf;
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
+
+/*
+ * fetch an i-node
+ * -- no sanity check for now
+ * -- magic inode-to-disk-block stuff here
+ */
+
+Fileinf
+iget(int ino)
+{
+ char buf[BLSIZE];
+ struct v6dinode *dp;
+ long flags, i;
+ Fileinf f;
+
+ seek(tapefile, BLSIZE*((ino-1)/LINOPB + V6SUPERB + 1), 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("Can't read inode");
+ dp = ((struct v6dinode *)buf) + ((ino-1)%LINOPB);
+ flags = (dp->flags[1]<<8) + dp->flags[0];
+ f.size = (dp->hisize << 16) + (dp->losize[1]<<8) + dp->losize[0];
+ if ((flags&V6FMT)==V6IFCHR || (flags&V6FMT)==V6IFBLK)
+ f.size = 0;
+ f.data = emalloc(V6NADDR*sizeof(ushort));
+ for (i = 0; i < V6NADDR; i++)
+ ((ushort*)f.data)[i] = (dp->addr[i][1]<<8) + dp->addr[i][0];
+ f.mode = flags & V6MODE;
+ if ((flags&V6FMT)==V6IFDIR)
+ f.mode |= DMDIR;
+ f.uid = dp->uid;
+ f.gid = dp->gid;
+ f.mdate = (dp->mtime[2]<<0) + (dp->mtime[3]<<8)
+ +(dp->mtime[0]<<16) + (dp->mtime[1]<<24);
+ return f;
+}
+
+void
+getblk(Ram *r, long bno, char *buf)
+{
+ long dbno;
+
+ if ((dbno = bmap(r, bno)) == 0) {
+ memset(buf, 0, BLSIZE);
+ return;
+ }
+ seek(tapefile, dbno*BLSIZE, 0);
+ if (read(tapefile, buf, BLSIZE) != BLSIZE)
+ error("bad read");
+}
+
+/*
+ * logical to physical block
+ * only singly-indirect files for now
+ */
+
+long
+bmap(Ram *r, long bno)
+{
+ unsigned char indbuf[LNINDIR][2];
+
+ if (r->ndata <= V6NADDR*BLSIZE) { /* assume size predicts largeness of file */
+ if (bno < V6NADDR)
+ return ((ushort*)r->data)[bno];
+ return 0;
+ }
+ if (bno < V6NADDR*LNINDIR) {
+ seek(tapefile, ((ushort *)r->data)[bno/LNINDIR]*BLSIZE, 0);
+ if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE)
+ return 0;
+ return ((indbuf[bno%LNINDIR][1]<<8) + indbuf[bno%LNINDIR][0]);
+ }
+ return 0;
+}
diff --git a/src/cmd/tapefs/zip.h b/src/cmd/tapefs/zip.h
new file mode 100644
index 00000000..9b703de2
--- /dev/null
+++ b/src/cmd/tapefs/zip.h
@@ -0,0 +1,83 @@
+typedef struct ZipHead ZipHead;
+
+enum
+{
+ /*
+ * magic numbers
+ */
+ ZHeader = 0x04034b50,
+ ZCHeader = 0x02014b50,
+ ZECHeader = 0x06054b50,
+
+ /*
+ * "general purpose flag" bits
+ */
+ ZEncrypted = 1 << 0,
+ ZTrailInfo = 1 << 3, /* uncsize, csize, and crc are in trailer */
+ ZCompPatch = 1 << 5, /* compression patched data */
+
+ ZCrcPoly = 0xedb88320,
+
+ /*
+ * compression method
+ */
+ ZDeflate = 8,
+
+ /*
+ * internal file attributes
+ */
+ ZIsText = 1 << 0,
+
+ /*
+ * file attribute interpretation, from high byte of version
+ */
+ ZDos = 0,
+ ZAmiga = 1,
+ ZVMS = 2,
+ ZUnix = 3,
+ ZVMCMS = 4,
+ ZAtariST = 5,
+ ZOS2HPFS = 6,
+ ZMac = 7,
+ ZZsys = 8,
+ ZCPM = 9,
+ ZNtfs = 10,
+
+ /*
+ * external attribute flags for ZDos
+ */
+ ZDROnly = 0x01,
+ ZDHidden = 0x02,
+ ZDSystem = 0x04,
+ ZDVLable = 0x08,
+ ZDDir = 0x10,
+ ZDArch = 0x20,
+
+ ZHeadSize = 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2,
+ ZHeadCrc = 4 + 2 + 2 + 2 + 2 + 2,
+ ZTrailSize = 4 + 4 + 4,
+ ZCHeadSize = 4 + 2 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4,
+ ZECHeadSize = 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2,
+};
+
+/*
+ * interesting info from a zip header
+ */
+struct ZipHead
+{
+ int madeos; /* version made by */
+ int madevers;
+ int extos; /* version needed to extract */
+ int extvers;
+ int flags; /* general purpose bit flag */
+ int meth;
+ int modtime;
+ int moddate;
+ ulong crc;
+ ulong csize;
+ ulong uncsize;
+ int iattr;
+ ulong eattr;
+ ulong off;
+ char *file;
+};
diff --git a/src/cmd/tapefs/zipfs.c b/src/cmd/tapefs/zipfs.c
new file mode 100644
index 00000000..26bd4dc9
--- /dev/null
+++ b/src/cmd/tapefs/zipfs.c
@@ -0,0 +1,385 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <flate.h>
+#include <auth.h>
+#include <fcall.h>
+#include <ctype.h>
+#include "tapefs.h"
+#include "zip.h"
+
+#define FORCE_LOWER 1 /* force filenames to lower case */
+#define MUNGE_CR 1 /* replace '\r\n' with ' \n' */
+#define High64 (1LL<<63)
+
+/*
+ * File system for zip archives (read-only)
+ */
+
+enum {
+ IS_MSDOS = 0, /* creator OS (interpretation of external flags) */
+ IS_RDONLY = 1, /* file was readonly (external flags) */
+ IS_TEXT = 1, /* file was text (internal flags) */
+};
+
+typedef struct Block Block;
+struct Block{
+ uchar *pos;
+ uchar *limit;
+};
+
+static Biobuf *bin;
+static ulong *crctab;
+static ulong crc;
+
+static int findCDir(Biobuf *);
+static int header(Biobuf *, ZipHead *);
+static int cheader(Biobuf *, ZipHead *);
+/* static void trailer(Biobuf *, ZipHead *); */
+static char *getname(Biobuf *, int);
+static int blwrite(void *, void *, int);
+static ulong get4(Biobuf *);
+static int get2(Biobuf *);
+static int get1(Biobuf *);
+static long msdos2time(int, int);
+
+void
+populate(char *name)
+{
+ char *p;
+ Fileinf f;
+ ZipHead zh;
+ int ok, entries;
+
+ crctab = mkcrctab(ZCrcPoly);
+ ok = inflateinit();
+ if(ok != FlateOk)
+ sysfatal("inflateinit failed: %s", flateerr(ok));
+
+ bin = Bopen(name, OREAD);
+ if (bin == nil)
+ error("Can't open argument file");
+
+ entries = findCDir(bin);
+ if(entries < 0)
+ sysfatal("empty file");
+
+ while(entries-- > 0){
+ memset(&zh, 0, sizeof(zh));
+ if(!cheader(bin, &zh))
+ break;
+ f.addr = zh.off;
+ if(zh.iattr & IS_TEXT)
+ f.addr |= High64;
+ f.mode = (zh.madevers == IS_MSDOS && zh.eattr & IS_RDONLY)? 0444: 0644;
+ if (zh.meth == 0 && zh.uncsize == 0){
+ p = strchr(zh.file, '\0');
+ if(p > zh.file && p[-1] == '/')
+ f.mode |= (DMDIR | 0111);
+ }
+ f.uid = 0;
+ f.gid = 0;
+ f.size = zh.uncsize;
+ f.mdate = msdos2time(zh.modtime, zh.moddate);
+ f.name = zh.file + ((zh.file[0] == '/')? 1: 0);
+ poppath(f, 1);
+ free(zh.file);
+ }
+ return ;
+}
+
+void
+dotrunc(Ram *r)
+{
+ USED(r);
+}
+
+void
+docreate(Ram *r)
+{
+ USED(r);
+}
+
+char *
+doread(Ram *r, vlong off, long cnt)
+{
+ int i, err;
+ Block bs;
+ ZipHead zh;
+ static Qid oqid;
+ static char buf[Maxbuf];
+ static uchar *cache = nil;
+
+ if (cnt > Maxbuf)
+ sysfatal("file too big (>%d)", Maxbuf);
+
+ if (Bseek(bin, r->addr & 0x7FFFFFFFFFFFFFFFLL, 0) < 0)
+ sysfatal("seek failed");
+
+ memset(&zh, 0, sizeof(zh));
+ if (!header(bin, &zh))
+ sysfatal("cannot get local header");
+
+ switch(zh.meth){
+ case 0:
+ if (Bseek(bin, off, 1) < 0)
+ sysfatal("seek failed");
+ if (Bread(bin, buf, cnt) != cnt)
+ sysfatal("read failed");
+ break;
+ case 8:
+ if (r->qid.path != oqid.path){
+ oqid = r->qid;
+ if (cache)
+ free(cache);
+ cache = emalloc(r->ndata);
+
+ bs.pos = cache;
+ bs.limit = cache+r->ndata;
+ if ((err = inflate(&bs, blwrite, bin, (int(*)(void*))Bgetc)) != FlateOk)
+ sysfatal("inflate failed - %s", flateerr(err));
+
+ if (blockcrc(crctab, crc, cache, r->ndata) != zh.crc)
+ fprint(2, "%s - crc failed", r->name);
+
+ if ((r->addr & High64) && MUNGE_CR){
+ for (i = 0; i < r->ndata -1; i++)
+ if (cache[i] == '\r' && cache[i +1] == '\n')
+ cache[i] = ' ';
+ }
+ }
+ memcpy(buf, cache+off, cnt);
+ break;
+ default:
+ sysfatal("%d - unsupported compression method", zh.meth);
+ break;
+ }
+
+ return buf;
+}
+
+void
+popdir(Ram *r)
+{
+ USED(r);
+}
+
+void
+dowrite(Ram *r, char *buf, long off, long cnt)
+{
+ USED(r); USED(buf); USED(off); USED(cnt);
+}
+
+int
+dopermw(Ram *r)
+{
+ USED(r);
+ return 0;
+}
+
+/*************************************************/
+
+static int
+findCDir(Biobuf *bin)
+{
+ vlong ecoff;
+ long off;
+ int entries, zclen;
+
+ ecoff = Bseek(bin, -ZECHeadSize, 2);
+ if(ecoff < 0)
+ sysfatal("can't seek to header");
+
+ if(get4(bin) != ZECHeader)
+ sysfatal("bad magic number on directory");
+
+ get2(bin);
+ get2(bin);
+ get2(bin);
+ entries = get2(bin);
+ get4(bin);
+ off = get4(bin);
+ zclen = get2(bin);
+ while(zclen-- > 0)
+ get1(bin);
+
+ if(Bseek(bin, off, 0) != off)
+ sysfatal("can't seek to contents");
+
+ return entries;
+}
+
+
+static int
+header(Biobuf *bin, ZipHead *zh)
+{
+ ulong v;
+ int flen, xlen;
+
+ v = get4(bin);
+ if(v != ZHeader){
+ if(v == ZCHeader)
+ return 0;
+ sysfatal("bad magic on local header");
+ }
+ zh->extvers = get1(bin);
+ zh->extos = get1(bin);
+ zh->flags = get2(bin);
+ zh->meth = get2(bin);
+ zh->modtime = get2(bin);
+ zh->moddate = get2(bin);
+ zh->crc = get4(bin);
+ zh->csize = get4(bin);
+ zh->uncsize = get4(bin);
+ flen = get2(bin);
+ xlen = get2(bin);
+
+ zh->file = getname(bin, flen);
+
+ while(xlen-- > 0)
+ get1(bin);
+ return 1;
+}
+
+static int
+cheader(Biobuf *bin, ZipHead *zh)
+{
+ ulong v;
+ int flen, xlen, fclen;
+
+ v = get4(bin);
+ if(v != ZCHeader){
+ if(v == ZECHeader)
+ return 0;
+ sysfatal("bad magic number in file");
+ }
+ zh->madevers = get1(bin);
+ zh->madeos = get1(bin);
+ zh->extvers = get1(bin);
+ zh->extos = get1(bin);
+ zh->flags = get2(bin);
+ zh->meth = get2(bin);
+ zh->modtime = get2(bin);
+ zh->moddate = get2(bin);
+ zh->crc = get4(bin);
+ zh->csize = get4(bin);
+ zh->uncsize = get4(bin);
+ flen = get2(bin);
+ xlen = get2(bin);
+ fclen = get2(bin);
+ get2(bin); /* disk number start */
+ zh->iattr = get2(bin); /* 1 == is-text-file */
+ zh->eattr = get4(bin); /* 1 == readonly-file */
+ zh->off = get4(bin);
+
+ zh->file = getname(bin, flen);
+
+ while(xlen-- > 0)
+ get1(bin);
+
+ while(fclen-- > 0)
+ get1(bin);
+
+ return 1;
+}
+
+static int
+blwrite(void *vb, void *buf, int n)
+{
+ Block *b = vb;
+ if(n > b->limit - b->pos)
+ n = b->limit - b->pos;
+ memmove(b->pos, buf, n);
+ b->pos += n;
+ return n;
+}
+
+/*
+static void
+trailer(Biobuf *bin, ZipHead *zh)
+{
+ if(zh->flags & ZTrailInfo){
+ zh->crc = get4(bin);
+ zh->csize = get4(bin);
+ zh->uncsize = get4(bin);
+ }
+}
+*/
+
+static char*
+getname(Biobuf *bin, int len)
+{
+ char *s;
+ int i, c;
+
+ s = emalloc(len + 1);
+ for(i = 0; i < len; i++){
+ c = get1(bin);
+ if(FORCE_LOWER)
+ c = tolower(c);
+ s[i] = c;
+ }
+ s[i] = '\0';
+ return s;
+}
+
+
+static ulong
+get4(Biobuf *b)
+{
+ ulong v;
+ int i, c;
+
+ v = 0;
+ for(i = 0; i < 4; i++){
+ c = Bgetc(b);
+ if(c < 0)
+ sysfatal("unexpected eof");
+ v |= c << (i * 8);
+ }
+ return v;
+}
+
+static int
+get2(Biobuf *b)
+{
+ int i, c, v;
+
+ v = 0;
+ for(i = 0; i < 2; i++){
+ c = Bgetc(b);
+ if(c < 0)
+ sysfatal("unexpected eof");
+ v |= c << (i * 8);
+ }
+ return v;
+}
+
+static int
+get1(Biobuf *b)
+{
+ int c;
+
+ c = Bgetc(b);
+ if(c < 0)
+ sysfatal("unexpected eof");
+ return c;
+}
+
+static long
+msdos2time(int time, int date)
+{
+ Tm tm;
+
+ tm.hour = time >> 11;
+ tm.min = (time >> 5) & 63;
+ tm.sec = (time & 31) << 1;
+ tm.year = 80 + (date >> 9);
+ tm.mon = ((date >> 5) & 15) - 1;
+ tm.mday = date & 31;
+ tm.zone[0] = '\0';
+ tm.yday = 0;
+
+ return tm2sec(&tm);
+}
+