Commit Diff


commit - 21b291a64e4f059744b4094a8f8e52784ad323f4
commit + 64f7506b34106955fbbd5f6bf944ecd839610672
blob - /dev/null
blob + 55d5dbe0fb354615233361829194fb0d9eb9d92b (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + fc11346dbcf428004a4d34270b0b3dea88056dfd (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 66abe36627008da9252f1bef9f616e84e495383b (mode 644)
--- /dev/null
+++ 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");
+}
blob - /dev/null
blob + dcf8783ac6a252a87f54fbb530dc25ced096639d (mode 644)
--- /dev/null
+++ 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
blob - /dev/null
blob + ae5e1844796334a66d952642b77c2248f51bb10f (mode 644)
--- /dev/null
+++ 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*);
blob - /dev/null
blob + fb1738c2d793a74a16942d21b8940f21522a69f1 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 5bbd8b9c3dc466f130d247548d505805ba54299d (mode 644)
--- /dev/null
+++ 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);
+}
blob - /dev/null
blob + 02079a3ae579d4da01f3bc03313158a2c650714f (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 199235f386d27713b5801031b4324740b54c87c9 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 64e6edd47b3c752c824014a42b052ce1e1ab1448 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 46669de51a44bb7ab26727382249f8c66bf96277 (mode 644)
--- /dev/null
+++ 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;
+}
blob - /dev/null
blob + 9b703de281c09d152c2ba8bf7488a4e03cf6b7a9 (mode 644)
--- /dev/null
+++ 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;
+};
blob - /dev/null
blob + 26bd4dc9f55a720a89c9e02c001470bd4270d970 (mode 644)
--- /dev/null
+++ 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);
+}
+