From 64f7506b34106955fbbd5f6bf944ecd839610672 Mon Sep 17 00:00:00 2001 From: rsc Date: Fri, 24 Feb 2006 21:17:00 +0000 Subject: tapefs from plan9 --- src/cmd/tapefs/32vfs.c | 208 +++++++++++++++++ src/cmd/tapefs/cpiofs.c | 139 +++++++++++ src/cmd/tapefs/fs.c | 600 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/tapefs/mkfile | 13 ++ src/cmd/tapefs/tapefs.h | 95 ++++++++ src/cmd/tapefs/tapfs.c | 115 ++++++++++ src/cmd/tapefs/tarfs.c | 144 ++++++++++++ src/cmd/tapefs/tpfs.c | 108 +++++++++ src/cmd/tapefs/util.c | 153 ++++++++++++ src/cmd/tapefs/v10fs.c | 209 +++++++++++++++++ src/cmd/tapefs/v6fs.c | 213 +++++++++++++++++ src/cmd/tapefs/zip.h | 83 +++++++ src/cmd/tapefs/zipfs.c | 385 +++++++++++++++++++++++++++++++ 13 files changed, 2465 insertions(+) create mode 100644 src/cmd/tapefs/32vfs.c create mode 100644 src/cmd/tapefs/cpiofs.c create mode 100644 src/cmd/tapefs/fs.c create mode 100644 src/cmd/tapefs/mkfile create mode 100644 src/cmd/tapefs/tapefs.h create mode 100644 src/cmd/tapefs/tapfs.c create mode 100644 src/cmd/tapefs/tarfs.c create mode 100644 src/cmd/tapefs/tpfs.c create mode 100644 src/cmd/tapefs/util.c create mode 100644 src/cmd/tapefs/v10fs.c create mode 100644 src/cmd/tapefs/v6fs.c create mode 100644 src/cmd/tapefs/zip.h create mode 100644 src/cmd/tapefs/zipfs.c (limited to 'src/cmd/tapefs') diff --git a/src/cmd/tapefs/32vfs.c b/src/cmd/tapefs/32vfs.c new file mode 100644 index 00000000..55d5dbe0 --- /dev/null +++ b/src/cmd/tapefs/32vfs.c @@ -0,0 +1,208 @@ +/* + * Vax 32V Unix filesystem (same as pre-FFS Berkeley) + */ +#include +#include +#include +#include +#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; indata; i+=sizeof(struct v32dir)) { + if (i%BLSIZE==0) + cp = doread(r, i, BLSIZE); + dp = (struct v32dir *)(cp+i%BLSIZE); + ino = g2byte(dp->ino); + if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0) + continue; + if (ino==0) + continue; + f = iget(ino); + strncpy(name, dp->name, VNAMELEN); + name[VNAMELEN+1] = '\0'; + f.name = name; + popfile(r, f); + } + r->replete = 1; +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + static char buf[Maxbuf+BLSIZE]; + int bno, i; + + bno = off/BLSIZE; + off -= bno*BLSIZE; + if (cnt>Maxbuf) + error("count too large"); + if (off) + cnt += off; + i = 0; + while (cnt>0) { + getblk(r, bno, &buf[i*BLSIZE]); + cnt -= BLSIZE; + bno++; + i++; + } + return buf; +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} + +/* + * fetch an i-node + * -- no sanity check for now + * -- magic inode-to-disk-block stuff here + */ + +Fileinf +iget(int ino) +{ + char buf[BLSIZE]; + struct v32dinode *dp; + long flags, i; + Fileinf f; + + seek(tapefile, BLSIZE*((ino-1)/LINOPB + VSUPERB + 1), 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("Can't read inode"); + dp = ((struct v32dinode *)buf) + ((ino-1)%LINOPB); + flags = g2byte(dp->flags); + f.size = g4byte(dp->size); + if ((flags&VFMT)==VIFCHR || (flags&VFMT)==VIFBLK) + f.size = 0; + f.data = emalloc(VNADDR*sizeof(long)); + for (i = 0; i < VNADDR; i++) + ((long*)f.data)[i] = g3byte(dp->addr+3*i); + f.mode = flags & VMODE; + if ((flags&VFMT)==VIFDIR) + f.mode |= DMDIR; + f.uid = g2byte(dp->uid); + f.gid = g2byte(dp->gid); + f.mdate = g4byte(dp->mtime); + return f; +} + +void +getblk(Ram *r, long bno, char *buf) +{ + long dbno; + + if ((dbno = bmap(r, bno)) == 0) { + memset(buf, 0, BLSIZE); + return; + } + seek(tapefile, dbno*BLSIZE, 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("bad read"); +} + +/* + * logical to physical block + * only singly-indirect files for now + */ + +long +bmap(Ram *r, long bno) +{ + unsigned char indbuf[LNINDIR][sizeof(long)]; + + if (bno < VNADDR-3) + return ((long*)r->data)[bno]; + if (bno < VNADDR*LNINDIR) { + seek(tapefile, ((long *)r->data)[(bno-(VNADDR-3))/LNINDIR]*BLSIZE, 0); + if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE) + return 0; + return ((indbuf[bno%LNINDIR][1]<<8) + indbuf[bno%LNINDIR][0]); + } + return 0; +} diff --git a/src/cmd/tapefs/cpiofs.c b/src/cmd/tapefs/cpiofs.c new file mode 100644 index 00000000..fc11346d --- /dev/null +++ b/src/cmd/tapefs/cpiofs.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#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)0; p++, l--){ + r <<= 3; + r += *p-'0'; + } + return r; +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + seek(tapefile, r->addr+off, 0); + if (cnt>sizeof(dblock.tbuf)) + error("read too big"); + read(tapefile, dblock.tbuf, cnt); + return dblock.tbuf; +} + +void +popdir(Ram *r) +{ + USED(r); +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} diff --git a/src/cmd/tapefs/fs.c b/src/cmd/tapefs/fs.c new file mode 100644 index 00000000..66abe366 --- /dev/null +++ b/src/cmd/tapefs/fs.c @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#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; iqid.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.nwqidbusy = 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 && inext){ + if(!r->busy) + continue; + len = ramstat(r, (uchar*)buf+n, cnt-n); + if(len <= BIT16SZ) + break; + if(i >= off) + n += len; + i += len; + } + thdr.count = n; + return 0; + } + r = f->ram; + if(off >= r->ndata) + return 0; + r->atime = time(0); + n = cnt; + if(off+n > r->ndata) + n = r->ndata - off; + thdr.data = doread(r, off, n); + thdr.count = n; + return 0; +} + +char* +rwrite(Fid *f) +{ + Ram *r; + ulong off; + int cnt; + + r = f->ram; + if(dopermw(f->ram)==0) + return Eperm; + if(r->busy == 0) + return Enotexist; + off = rhdr.offset; + if(r->perm & DMAPPEND) + off = r->ndata; + cnt = rhdr.count; + if(r->qid.type & QTDIR) + return "file is a directory"; + if(off > 100*1024*1024) /* sanity check */ + return "write too big"; + dowrite(r, rhdr.data, off, cnt); + r->qid.vers++; + r->mtime = time(0); + thdr.count = cnt; + return 0; +} + +char * +rclunk(Fid *f) +{ + if(f->open) + f->ram->open--; + f->busy = 0; + f->open = 0; + f->ram = 0; + return 0; +} + +char * +rremove(Fid *f) +{ + USED(f); + return Eperm; +} + +char * +rstat(Fid *f) +{ + if(f->ram->busy == 0) + return Enotexist; + thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ); + return 0; +} + +char * +rwstat(Fid *f) +{ + if(f->ram->busy == 0) + return Enotexist; + return Eperm; +} + +int +ramstat(Ram *r, uchar *buf, int nbuf) +{ + Dir dir; + + dir.name = r->name; + dir.qid = r->qid; + dir.mode = r->perm; + dir.length = r->ndata; + dir.uid = r->user; + dir.gid = r->group; + dir.muid = r->user; + dir.atime = r->atime; + dir.mtime = r->mtime; + return convD2M(&dir, buf, nbuf); +} + +Fid * +newfid(int fid) +{ + Fid *f, *ff; + + ff = 0; + for(f = fids; f; f = f->next) + if(f->fid == fid) + return f; + else if(!ff && !f->busy) + ff = f; + if(ff){ + ff->fid = fid; + ff->open = 0; + ff->busy = 1; + } + f = emalloc(sizeof *f); + f->ram = 0; + f->fid = fid; + f->busy = 1; + f->open = 0; + f->next = fids; + fids = f; + return f; +} + +void +io(void) +{ + char *err; + int n, nerr; + char buf[ERRMAX]; + + errstr(buf, sizeof buf); + for(nerr=0, buf[0]='\0'; nerr<100; nerr++){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error + */ + n = read9pmsg(mfd[0], mdata, sizeof mdata); + if(n==0) + continue; + if(n < 0){ + if(buf[0]=='\0') + errstr(buf, sizeof buf); + continue; + } + nerr = 0; + buf[0] = '\0'; + if(convM2S(mdata, n, &rhdr) != n) + error("convert error in convM2S"); + + if(verbose) + fprint(2, "tapefs: <=%F\n", &rhdr);/**/ + + thdr.data = (char*)mdata + IOHDRSZ; + thdr.stat = mdata + IOHDRSZ; + if(!fcalls[rhdr.type]) + err = "bad fcall type"; + else + err = (*fcalls[rhdr.type])(newfid(rhdr.fid)); + if(err){ + thdr.type = Rerror; + thdr.ename = err; + }else{ + thdr.type = rhdr.type + 1; + thdr.fid = rhdr.fid; + } + thdr.tag = rhdr.tag; + n = convS2M(&thdr, mdata, messagesize); + if(n <= 0) + error("convert error in convS2M"); + if(verbose) + fprint(2, "tapefs: =>%F\n", &thdr);/**/ + if(write(mfd[1], mdata, n) != n) + error("mount write"); + } + if(buf[0]=='\0' || strstr(buf, "hungup")) + exits(""); + fprint(2, "%s: mount read: %s\n", argv0, buf); + exits(buf); +} + +int +perm(int p) +{ + if(p==Pwrite) + return 0; + return 1; +} + +void +error(char *s) +{ + fprint(2, "%s: %s: ", argv0, s); + perror(""); + exits(s); +} + +char* +estrdup(char *s) +{ + char *t; + + t = emalloc(strlen(s)+1); + strcpy(t, s); + return t; +} + +void * +emalloc(ulong n) +{ + void *p; + p = mallocz(n, 1); + if(!p) + error("out of memory"); + return p; +} + +void * +erealloc(void *p, ulong n) +{ + p = realloc(p, n); + if(!p) + error("out of memory"); + return p; +} + +void +usage(void) +{ + fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0); + exits("usage"); +} diff --git a/src/cmd/tapefs/mkfile b/src/cmd/tapefs/mkfile new file mode 100644 index 00000000..dcf8783a --- /dev/null +++ b/src/cmd/tapefs/mkfile @@ -0,0 +1,13 @@ +<$PLAN9/src/mkhdr + +TARG=tarfs tpfs v6fs 32vfs cpiofs tapfs v10fs zipfs +OFILES=\ + fs.$O\ + util.$O\ + +HFILES=tapefs.h + +BIN=$BIN/fs +<$PLAN9/src/mkmany + +zipfs.$O: zip.h diff --git a/src/cmd/tapefs/tapefs.h b/src/cmd/tapefs/tapefs.h new file mode 100644 index 00000000..ae5e1844 --- /dev/null +++ b/src/cmd/tapefs/tapefs.h @@ -0,0 +1,95 @@ +#define getpass tapefs_getpass + +#define g2byte(x) (((x)[1]<<8) + (x)[0]) /* little-endian */ +#define g3byte(x) (((x)[2]<<16) + ((x)[1]<<8) + (x)[0]) +#define g4byte(x) (((x)[3]<<24) + ((x)[2]<<16) + ((x)[1]<<8) + (x)[0]) + +enum +{ + OPERM = 0x3, /* mask of all permission types in open mode */ + Nram = 512, + Maxbuf = 8192, /* max buffer size */ +}; + +typedef struct Fid Fid; +typedef struct Ram Ram; + +struct Fid +{ + short busy; + short open; + short rclose; + int fid; + Fid *next; + char *user; + Ram *ram; +}; + +struct Ram +{ + char busy; + char open; + char replete; + Ram *parent; /* parent directory */ + Ram *child; /* first member of directory */ + Ram *next; /* next member of file's directory */ + Qid qid; + long perm; + char *name; + ulong atime; + ulong mtime; + char *user; + char *group; + vlong addr; + void *data; + long ndata; +}; + +enum +{ + Pexec = 1, + Pwrite = 2, + Pread = 4, + Pother = 1, + Pgroup = 8, + Powner = 64, +}; + +typedef struct idmap { + char *name; + int id; +} Idmap; + +typedef struct fileinf { + char *name; + vlong addr; + void *data; + vlong size; + int mode; + int uid; + int gid; + long mdate; +} Fileinf; + +extern ulong path; /* incremented for each new file */ +extern Ram *ram; +extern char *user; +extern Idmap *uidmap; +extern Idmap *gidmap; +extern int replete; +void error(char*); +void *erealloc(void*, ulong); +void *emalloc(ulong); +char *estrdup(char*); +void populate(char *); +void dotrunc(Ram*); +void docreate(Ram*); +char *doread(Ram*, vlong, long); +void dowrite(Ram*, char*, long, long); +int dopermw(Ram*); +Idmap *getpass(char*); +char *mapid(Idmap*,int); +Ram *poppath(Fileinf fi, int new); +Ram *popfile(Ram *dir, Fileinf fi); +void popdir(Ram*); +Ram *lookup(Ram*, char*); diff --git a/src/cmd/tapefs/tapfs.c b/src/cmd/tapefs/tapfs.c new file mode 100644 index 00000000..fb1738c2 --- /dev/null +++ b/src/cmd/tapefs/tapfs.c @@ -0,0 +1,115 @@ +#include +#include +#include "tapefs.h" + +/* + * File system for old tap tapes. + */ + +struct tap { + unsigned char name[32]; + unsigned char mode[1]; + unsigned char uid[1]; + unsigned char size[2]; + unsigned char tmod[4]; + unsigned char taddress[2]; + unsigned char unused[20]; + unsigned char checksum[2]; +} dir[192]; + +int tapefile; +char buffer[8192]; +long cvtime(unsigned char *); +extern int verbose; +extern int newtap; + +void +populate(char *name) +{ + int i, isabs; + struct tap *tpp; + Fileinf f; + + replete = 1; + tapefile = open(name, OREAD); + if (tapefile<0) + error("Can't open argument file"); + read(tapefile, dir, sizeof dir); + for (i=0, tpp=&dir[8]; i<192; i++, tpp++) { + unsigned char *sp = (unsigned char *)tpp; + int j, cksum = 0; + for (j=0; j<32; j++, sp+=2) + cksum += sp[0] + (sp[1]<<8); + cksum &= 0xFFFF; + if (cksum!=0) { + print("cksum failure\n"); + continue; + } + if (tpp->name[0]=='\0') + continue; + f.addr = tpp->taddress[0] + (tpp->taddress[1]<<8); + if (f.addr==0) + continue; + f.size = tpp->size[0] + (tpp->size[1]<<8); + f.mdate = cvtime(tpp->tmod); + f.mode = tpp->mode[0]&0777; + f.uid = tpp->uid[0]&0377; + isabs = tpp->name[0]=='/'; + f.name = (char *)tpp->name+isabs; + if (verbose) + print("%s mode %o uid %d, %s", f.name, f.mode, f.uid, ctime(f.mdate)); + poppath(f, 1); + } +} + +long +cvtime(unsigned char *tp) +{ + unsigned long t = (tp[1]<<24)+(tp[0]<<16)+(tp[3]<<8)+(tp[2]<<0); + if (!newtap) { + t /= 60; + t += 3*365*24*3600; + } + return t; +} + +void +popdir(Ram *r) +{ + USED(r); +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + if (cnt>sizeof(buffer)) + print("count too big\n"); + seek(tapefile, 512*r->addr+off, 0); + read(tapefile, buffer, cnt); + return buffer; +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} diff --git a/src/cmd/tapefs/tarfs.c b/src/cmd/tapefs/tarfs.c new file mode 100644 index 00000000..5bbd8b9c --- /dev/null +++ b/src/cmd/tapefs/tarfs.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#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))%s skipped\n", dblock.dbuf.name, + dblock.dbuf.linkname);*/ + f.size = 0; + blkno += 1; + continue; + } + f.name = dblock.dbuf.name+isabs; + if (f.name[0]=='\0') + fprint(1, "null name skipped\n"); + else + poppath(f, 1); + blkno += 1 + (f.size+TBLOCK-1)/TBLOCK; + } +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + seek(tapefile, TBLOCK*r->addr+off, 0); + if (cnt>sizeof(dblock.tbuf)) + error("read too big"); + read(tapefile, dblock.tbuf, cnt); + return dblock.tbuf; +} + +void +popdir(Ram *r) +{ + USED(r); +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} + +int +checksum() +{ + int i; + char *cp; + + for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++) + *cp = ' '; + i = 0; + for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++) + i += *cp&0xff; + return(i); +} diff --git a/src/cmd/tapefs/tpfs.c b/src/cmd/tapefs/tpfs.c new file mode 100644 index 00000000..02079a3a --- /dev/null +++ b/src/cmd/tapefs/tpfs.c @@ -0,0 +1,108 @@ +#include +#include +#include "tapefs.h" + +/* + * File system for tp tapes. dectape versions have 192 + * entries, magtape have 496. This treats the same + * by ignoring entries with bad header checksums + */ + +struct tp { + unsigned char name[32]; + unsigned char mode[2]; + unsigned char uid[1]; + unsigned char gid[1]; + unsigned char unused[1]; + unsigned char size[3]; + unsigned char tmod[4]; + unsigned char taddress[2]; + unsigned char unused2[16]; + unsigned char checksum[2]; +} dir[496+8]; + +char buffer[8192]; +int tapefile; + +void +populate(char *name) +{ + int i, isabs, badcksum, goodcksum; + struct tp *tpp; + Fileinf f; + + replete = 1; + tapefile = open(name, OREAD); + if (tapefile<0) + error("Can't open argument file"); + read(tapefile, dir, sizeof dir); + badcksum = goodcksum = 0; + for (i=0, tpp=&dir[8]; i<496; i++, tpp++) { + unsigned char *sp = (unsigned char *)tpp; + int j, cksum = 0; + for (j=0; j<32; j++, sp+=2) + cksum += sp[0] + (sp[1]<<8); + cksum &= 0xFFFF; + if (cksum!=0) { + badcksum++; + continue; + } + goodcksum++; + if (tpp->name[0]=='\0') + continue; + f.addr = tpp->taddress[0] + (tpp->taddress[1]<<8); + if (f.addr==0) + continue; + f.size = (tpp->size[0]<<16) + (tpp->size[1]<<0) + (tpp->size[2]<<8); + f.mdate = (tpp->tmod[2]<<0) + (tpp->tmod[3]<<8) + +(tpp->tmod[0]<<16) + (tpp->tmod[1]<<24); + f.mode = tpp->mode[0]&0777; + f.uid = tpp->uid[0]; + f.gid = tpp->gid[0]; + isabs = tpp->name[0]=='/'; + f.name = (char *)tpp->name+isabs; + poppath(f, 1); + } + fprint(2, "%d bad checksums, %d good\n", badcksum, goodcksum); +} + +void +popdir(Ram *r) +{ + USED(r); +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + if (cnt>sizeof(buffer)) + print("count too big\n"); + seek(tapefile, 512*r->addr+off, 0); + read(tapefile, buffer, cnt); + return buffer; +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} diff --git a/src/cmd/tapefs/util.c b/src/cmd/tapefs/util.c new file mode 100644 index 00000000..199235f3 --- /dev/null +++ b/src/cmd/tapefs/util.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include "tapefs.h" + +Idmap * +getpass(char *file) +{ + Biobuf *bp; + char *cp; + Idmap *up; + int nid, maxid; + char *line[4]; + + if ((bp = Bopen(file, OREAD)) == 0) + error("Can't open passwd/group"); + up = emalloc(1*sizeof(Idmap)); + maxid = 1; + nid = 0; + while ((cp = Brdline(bp, '\n'))) { + int nf; + cp[Blinelen(bp)-1] = 0; + nf = getfields(cp, line, 3, 0, ":\n"); + if (nf<3) { + fprint(2, "bad format in %s\n", file); + break; + } + if (nid>=maxid) { + maxid *= 2; + up = (Idmap *)erealloc(up, maxid*sizeof(Idmap)); + } + up[nid].id = atoi(line[2]); + up[nid].name = strdup(line[0]); + nid++; + } + Bterm(bp); + up[nid].name = 0; + return up; +} + +char * +mapid(Idmap *up, int id) +{ + char buf[16]; + + if (up) + while (up->name){ + if (up->id==id) + return strdup(up->name); + up++; + } + sprint(buf, "%d", id); + return strdup(buf); +} + +Ram * +poppath(Fileinf fi, int new) +{ + char *suffix; + Ram *dir, *ent; + Fileinf f; + + if (*fi.name=='\0') + return 0; + if (suffix=strrchr(fi.name, '/')){ + *suffix = 0; + suffix++; + if (*suffix=='\0'){ + fi.mode |= DMDIR; + return poppath(fi, 1); + } + f = fi; + f.size = 0; + f.addr = 0; + f.mode = 0555|DMDIR; + dir = poppath(f, 0); + if (dir==0) + dir = ram; + } else { + suffix = fi.name; + dir = ram; + if (strcmp(suffix, ".")==0) + return dir; + } + ent = lookup(dir, suffix); + fi.mode |= 0400; /* at least user read */ + if (ent){ + if (((fi.mode&DMDIR)!=0) != ((ent->qid.type&QTDIR)!=0)){ + fprint(2, "%s/%s directory botch\n", fi.name, suffix); + exits(""); + } + if (new) { + ent->ndata = fi.size; + ent->addr = fi.addr; + ent->data = fi.data; + ent->perm = fi.mode; + ent->mtime = fi.mdate; + ent->user = mapid(uidmap, fi.uid); + ent->group = mapid(gidmap, fi.gid); + } + } else { + fi.name = suffix; + ent = popfile(dir, fi); + } + return ent; +} + +Ram * +popfile(Ram *dir, Fileinf fi) +{ + Ram *ent = (Ram *)emalloc(sizeof(Ram)); + if (*fi.name=='\0') + return 0; + ent->busy = 1; + ent->open = 0; + ent->parent = dir; + ent->next = dir->child; + dir->child = ent; + ent->child = 0; + ent->qid.path = ++path; + ent->qid.vers = 0; + if(fi.mode&DMDIR) + ent->qid.type = QTDIR; + else + ent->qid.type = QTFILE; + ent->perm = fi.mode; + ent->name = estrdup(fi.name); + ent->atime = ent->mtime = fi.mdate; + ent->user = mapid(uidmap, fi.uid); + ent->group = mapid(gidmap, fi.gid); + ent->ndata = fi.size; + ent->data = fi.data; + ent->addr = fi.addr; + ent->replete |= replete; + return ent; +} + +Ram * +lookup(Ram *dir, char *name) +{ + Ram *r; + + if (dir==0) + return 0; + for (r=dir->child; r; r=r->next){ + if (r->busy==0 || strcmp(r->name, name)!=0) + continue; + return r; + } + return 0; +} diff --git a/src/cmd/tapefs/v10fs.c b/src/cmd/tapefs/v10fs.c new file mode 100644 index 00000000..64e6edd4 --- /dev/null +++ b/src/cmd/tapefs/v10fs.c @@ -0,0 +1,209 @@ +/* + * 10th edition 4K file system + */ +#include +#include +#include +#include +#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; indata; i+=sizeof(struct v10dir)) { + if (i%BLSIZE==0) + cp = doread(r, i, BLSIZE); + dp = (struct v10dir *)(cp+i%BLSIZE); + ino = g2byte(dp->ino); + if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0) + continue; + if (ino==0) + continue; + f = iget(ino); + strncpy(name, dp->name, VNAMELEN); + name[VNAMELEN+1] = '\0'; + f.name = name; + popfile(r, f); + } + r->replete = 1; +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + static char buf[Maxbuf+BLSIZE]; + int bno, i; + + bno = off/BLSIZE; + off -= bno*BLSIZE; + if (cnt>Maxbuf) + error("count too large"); + if (off) + cnt += off; + i = 0; + while (cnt>0) { + getblk(r, bno, &buf[i*BLSIZE]); + cnt -= BLSIZE; + bno++; + i++; + } + return buf+off; +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} + +/* + * fetch an i-node + * -- no sanity check for now + * -- magic inode-to-disk-block stuff here + */ + +Fileinf +iget(int ino) +{ + char buf[BLSIZE]; + struct v10dinode *dp; + long flags, i; + Fileinf f; + + seek(tapefile, BLSIZE*((ino-1)/LINOPB + VSUPERB + 1), 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("Can't read inode"); + dp = ((struct v10dinode *)buf) + ((ino-1)%LINOPB); + flags = g2byte(dp->flags); + f.size = g4byte(dp->size); + if ((flags&VFMT)==VIFCHR || (flags&VFMT)==VIFBLK) + f.size = 0; + f.data = emalloc(VNADDR*sizeof(long)); + for (i = 0; i < VNADDR; i++) + ((long*)f.data)[i] = g3byte(dp->addr+3*i); + f.mode = flags & VMODE; + if ((flags&VFMT)==VIFDIR) + f.mode |= DMDIR; + f.uid = g2byte(dp->uid); + f.gid = g2byte(dp->gid); + f.mdate = g4byte(dp->mtime); + return f; +} + +void +getblk(Ram *r, long bno, char *buf) +{ + long dbno; + + if ((dbno = bmap(r, bno)) == 0) { + memset(buf, 0, BLSIZE); + return; + } + seek(tapefile, dbno*BLSIZE, 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("bad read"); +} + +/* + * logical to physical block + * only singly-indirect files for now + */ + +long +bmap(Ram *r, long bno) +{ + unsigned char indbuf[LNINDIR][sizeof(long)]; + + if (bno < VNADDR-3) + return ((long*)r->data)[bno]; + if (bno < VNADDR*LNINDIR) { + seek(tapefile, ((long *)r->data)[(bno-(VNADDR-3))/LNINDIR+(VNADDR-3)]*BLSIZE, 0); + if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE) + return 0; + return ((indbuf[(bno-(VNADDR-3))%LNINDIR][2]<<16) + (indbuf[(bno-(VNADDR-3))%LNINDIR][1]<<8) + + indbuf[(bno-(VNADDR-3))%LNINDIR][0]); + } + return 0; +} diff --git a/src/cmd/tapefs/v6fs.c b/src/cmd/tapefs/v6fs.c new file mode 100644 index 00000000..46669de5 --- /dev/null +++ b/src/cmd/tapefs/v6fs.c @@ -0,0 +1,213 @@ +/* + * old (V6 and before) PDP-11 Unix filesystem + */ +#include +#include +#include +#include +#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; indata; i+=sizeof(struct v6dir)) { + if (i%BLSIZE==0) + cp = doread(r, i, BLSIZE); + dp = (struct v6dir *)(cp+i%BLSIZE); + ino = dp->ino[0] + (dp->ino[1]<<8); + if (strcmp(dp->name, ".")==0 || strcmp(dp->name, "..")==0) + continue; + if (ino==0) + continue; + f = iget(ino); + strncpy(name, dp->name, V6NAMELEN); + name[V6NAMELEN+1] = '\0'; + f.name = name; + popfile(r, f); + } + r->replete = 1; +} + +void +dotrunc(Ram *r) +{ + USED(r); +} + +void +docreate(Ram *r) +{ + USED(r); +} + +char * +doread(Ram *r, vlong off, long cnt) +{ + static char buf[Maxbuf+BLSIZE]; + int bno, i; + + bno = off/BLSIZE; + off -= bno*BLSIZE; + if (cnt>Maxbuf) + error("count too large"); + if (off) + cnt += off; + i = 0; + while (cnt>0) { + getblk(r, bno, &buf[i*BLSIZE]); + cnt -= BLSIZE; + bno++; + i++; + } + return buf; +} + +void +dowrite(Ram *r, char *buf, long off, long cnt) +{ + USED(r); USED(buf); USED(off); USED(cnt); +} + +int +dopermw(Ram *r) +{ + USED(r); + return 0; +} + +/* + * fetch an i-node + * -- no sanity check for now + * -- magic inode-to-disk-block stuff here + */ + +Fileinf +iget(int ino) +{ + char buf[BLSIZE]; + struct v6dinode *dp; + long flags, i; + Fileinf f; + + seek(tapefile, BLSIZE*((ino-1)/LINOPB + V6SUPERB + 1), 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("Can't read inode"); + dp = ((struct v6dinode *)buf) + ((ino-1)%LINOPB); + flags = (dp->flags[1]<<8) + dp->flags[0]; + f.size = (dp->hisize << 16) + (dp->losize[1]<<8) + dp->losize[0]; + if ((flags&V6FMT)==V6IFCHR || (flags&V6FMT)==V6IFBLK) + f.size = 0; + f.data = emalloc(V6NADDR*sizeof(ushort)); + for (i = 0; i < V6NADDR; i++) + ((ushort*)f.data)[i] = (dp->addr[i][1]<<8) + dp->addr[i][0]; + f.mode = flags & V6MODE; + if ((flags&V6FMT)==V6IFDIR) + f.mode |= DMDIR; + f.uid = dp->uid; + f.gid = dp->gid; + f.mdate = (dp->mtime[2]<<0) + (dp->mtime[3]<<8) + +(dp->mtime[0]<<16) + (dp->mtime[1]<<24); + return f; +} + +void +getblk(Ram *r, long bno, char *buf) +{ + long dbno; + + if ((dbno = bmap(r, bno)) == 0) { + memset(buf, 0, BLSIZE); + return; + } + seek(tapefile, dbno*BLSIZE, 0); + if (read(tapefile, buf, BLSIZE) != BLSIZE) + error("bad read"); +} + +/* + * logical to physical block + * only singly-indirect files for now + */ + +long +bmap(Ram *r, long bno) +{ + unsigned char indbuf[LNINDIR][2]; + + if (r->ndata <= V6NADDR*BLSIZE) { /* assume size predicts largeness of file */ + if (bno < V6NADDR) + return ((ushort*)r->data)[bno]; + return 0; + } + if (bno < V6NADDR*LNINDIR) { + seek(tapefile, ((ushort *)r->data)[bno/LNINDIR]*BLSIZE, 0); + if (read(tapefile, (char *)indbuf, BLSIZE) != BLSIZE) + return 0; + return ((indbuf[bno%LNINDIR][1]<<8) + indbuf[bno%LNINDIR][0]); + } + return 0; +} diff --git a/src/cmd/tapefs/zip.h b/src/cmd/tapefs/zip.h new file mode 100644 index 00000000..9b703de2 --- /dev/null +++ b/src/cmd/tapefs/zip.h @@ -0,0 +1,83 @@ +typedef struct ZipHead ZipHead; + +enum +{ + /* + * magic numbers + */ + ZHeader = 0x04034b50, + ZCHeader = 0x02014b50, + ZECHeader = 0x06054b50, + + /* + * "general purpose flag" bits + */ + ZEncrypted = 1 << 0, + ZTrailInfo = 1 << 3, /* uncsize, csize, and crc are in trailer */ + ZCompPatch = 1 << 5, /* compression patched data */ + + ZCrcPoly = 0xedb88320, + + /* + * compression method + */ + ZDeflate = 8, + + /* + * internal file attributes + */ + ZIsText = 1 << 0, + + /* + * file attribute interpretation, from high byte of version + */ + ZDos = 0, + ZAmiga = 1, + ZVMS = 2, + ZUnix = 3, + ZVMCMS = 4, + ZAtariST = 5, + ZOS2HPFS = 6, + ZMac = 7, + ZZsys = 8, + ZCPM = 9, + ZNtfs = 10, + + /* + * external attribute flags for ZDos + */ + ZDROnly = 0x01, + ZDHidden = 0x02, + ZDSystem = 0x04, + ZDVLable = 0x08, + ZDDir = 0x10, + ZDArch = 0x20, + + ZHeadSize = 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2, + ZHeadCrc = 4 + 2 + 2 + 2 + 2 + 2, + ZTrailSize = 4 + 4 + 4, + ZCHeadSize = 4 + 2 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4, + ZECHeadSize = 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2, +}; + +/* + * interesting info from a zip header + */ +struct ZipHead +{ + int madeos; /* version made by */ + int madevers; + int extos; /* version needed to extract */ + int extvers; + int flags; /* general purpose bit flag */ + int meth; + int modtime; + int moddate; + ulong crc; + ulong csize; + ulong uncsize; + int iattr; + ulong eattr; + ulong off; + char *file; +}; diff --git a/src/cmd/tapefs/zipfs.c b/src/cmd/tapefs/zipfs.c new file mode 100644 index 00000000..26bd4dc9 --- /dev/null +++ b/src/cmd/tapefs/zipfs.c @@ -0,0 +1,385 @@ +#include +#include +#include +#include +#include +#include +#include +#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); +} + -- cgit v1.2.3