ttapefs from plan9 - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 64f7506b34106955fbbd5f6bf944ecd839610672
 (DIR) parent 21b291a64e4f059744b4094a8f8e52784ad323f4
 (HTM) Author: rsc <devnull@localhost>
       Date:   Fri, 24 Feb 2006 21:17:00 +0000
       
       ttapefs from plan9
       
       Diffstat:
         A src/cmd/tapefs/32vfs.c              |     208 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/cpiofs.c             |     139 ++++++++++++++++++++++++++++++
         A src/cmd/tapefs/fs.c                 |     600 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/mkfile               |      13 +++++++++++++
         A src/cmd/tapefs/tapefs.h             |      95 ++++++++++++++++++++++++++++++
         A src/cmd/tapefs/tapfs.c              |     115 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/tarfs.c              |     144 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/tpfs.c               |     108 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/util.c               |     153 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/v10fs.c              |     209 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/v6fs.c               |     213 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/zip.h                |      83 +++++++++++++++++++++++++++++++
         A src/cmd/tapefs/zipfs.c              |     385 +++++++++++++++++++++++++++++++
       
       13 files changed, 2465 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/tapefs/32vfs.c b/src/cmd/tapefs/32vfs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/cpiofs.c b/src/cmd/tapefs/cpiofs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/fs.c b/src/cmd/tapefs/fs.c
       t@@ -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");
       +}
 (DIR) diff --git a/src/cmd/tapefs/mkfile b/src/cmd/tapefs/mkfile
       t@@ -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
 (DIR) diff --git a/src/cmd/tapefs/tapefs.h b/src/cmd/tapefs/tapefs.h
       t@@ -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*);
 (DIR) diff --git a/src/cmd/tapefs/tapfs.c b/src/cmd/tapefs/tapfs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/tarfs.c b/src/cmd/tapefs/tarfs.c
       t@@ -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);
       +}
 (DIR) diff --git a/src/cmd/tapefs/tpfs.c b/src/cmd/tapefs/tpfs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/util.c b/src/cmd/tapefs/util.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/v10fs.c b/src/cmd/tapefs/v10fs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/v6fs.c b/src/cmd/tapefs/v6fs.c
       t@@ -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;
       +}
 (DIR) diff --git a/src/cmd/tapefs/zip.h b/src/cmd/tapefs/zip.h
       t@@ -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;
       +};
 (DIR) diff --git a/src/cmd/tapefs/zipfs.c b/src/cmd/tapefs/zipfs.c
       t@@ -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);
       +}
       +