/* * read a V7 PDP-11 dump tape as a filesystem */ #include <stdio.h> #include <rf.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> /* * PDP-11 integers */ #define p11s(c) ((c)[0]|((c)[1]<<8)) #define p11l(c) ((c)[2]|((c)[3]<<8)|((c)[0]<<16)|((c)[1]<<24)) /* wretched FP-11 */ #define P11S 2 /* size of PDP-11 short */ #define P11L 4 /* size of PDP-11 long */ /* * PDP-11 disk inode offsets */ #define DI_MODE 0 /* (short) */ #define DI_NLINK 2 /* (short) */ #define DI_UID 4 /* (short) */ #define DI_GID 6 /* (short) */ #define DI_SIZE 8 /* (long) */ #define DI_ADDR 12 /* 40 bytes */ #define DI_ATIME 52 /* (long) */ #define DI_MTIME 56 /* (long) */ #define DI_CTIME 60 /* (long) */ #define DILEN 64 /* * PDP-11 directory entry */ #define D_INO 0 /* (short) */ #define D_NAME 2 /* 14 characters */ #define DNSIZE 14 #define DLEN 16 /* * dump records */ #define TBSIZE 512 #define MAGIC (int)60011 #define TS_TAPE 1 #define TS_INODE 2 #define TS_BITS 3 #define TS_ADDR 4 #define TS_END 5 #define TS_CLRI 6 struct spcl11 { /* header as found on the tape */ unsigned char c_type[P11S]; unsigned char c_date[P11L]; unsigned char c_ddate[P11L]; unsigned char c_volume[P11S]; unsigned char c_tapea[P11L]; unsigned char c_inumber[P11S]; unsigned char c_magic[P11S]; unsigned char c_checksum[P11S]; unsigned char c_dinode[DILEN]; unsigned char c_count[P11S]; unsigned char c_addr[TBSIZE]; }; struct spcl { /* header we use internally */ int c_type; int c_inumber; unsigned char c_dinode[DILEN]; int c_count; char c_addr[TBSIZE]; }; /* * our data */ typedef unsigned int taddr_t; #define MAXINO 10000 /* dynamic would be better */ static taddr_t iaddr[MAXINO]; /* offset of TS_INODE for this file */ static int devfd; static Rfile *root; int fserrno; struct spcl *getspcl(); taddr_t curtblock(); Rfile *getino(); char *malloc(); long lseek(); /* * init */ Rfile * fsinit(argc, argv) int argc; char **argv; { if (argc <= 1) rfpanic("no device\n"); if ((devfd = open(argv[1], 0)) < 0) rfpanic("%s: cannot open\n", argv[1]); scantape(); if ((root = getino(2)) == NULL) rfpanic("no root\n"); return (root); } /* * look up a file */ Rfile * fswalk(df, name) Rfile *df; char *name; { int ino; if ((ino = dsearch(df, name)) == 0) { fserrno = ENOENT; return (NULL); } if (df == root) { /* "." and ".." magic */ if (strcmp(name, ".") == 0) return (df); if (strcmp(name, "..") == 0) { fserrno = 0; /* pseudo-error */ return (NULL); } } return (getino(ino)); } /* * all done with a file */ fsdone(f) Rfile *f; { free(f->fs); free((char *)f); return (0); } /* * read data */ int fsread(f, off, buf, len) register Rfile *f; long off; char *buf; int len; { char blk[TBSIZE]; int rest; long bno; switch (f->mode & S_IFMT) { case S_IFREG: case S_IFDIR: break; default: return (0); } if (off >= f->size) return (0); if (off + len > f->size) len = f->size - off; bno = off / TBSIZE; if (getblk(f, bno, blk) == 0) return (-1); rest = (bno + 1)*TBSIZE - off; if (len > rest) len = rest; memcpy(buf, blk + (off % TBSIZE), len); return (len); } /* * read a piece of a directory * -- cheap out for now: just return one */ int fsdirread(f, off, buf, len, offp) register Rfile *f; long off; char *buf; int len; long *offp; { int stlen; register unsigned char *de; unsigned char blk[TBSIZE]; unsigned char one[TBSIZE]; int n, ino; if (off % DLEN) { fserrno = EINVAL; return (-1); } stlen = len; de = &blk[TBSIZE]; for (; off < f->size; de += DLEN, off += DLEN) { if (de >= &blk[TBSIZE]) { if (getblk(f, off/TBSIZE, blk) == 0) break; de = &blk[off%TBSIZE]; } ino = p11s(&de[D_INO]); if (ino == 0) continue; n = sprintf(one, "%d\t%.14s", ino, &de[D_NAME]); n++; /* need the NUL too */ if (n > len) break; memcpy(buf, one, n); len -= n; buf += n; } *offp = off; return (stlen - len); } /* * search a directory */ int dsearch(f, name) Rfile *f; char *name; { unsigned char dbuf[TBSIZE]; register unsigned char *de; register int ino; register long b, size; for (b = 0, size = f->size; size > 0; b++, size -= TBSIZE) { if (getblk(f, b, dbuf) == 0) continue; for (de = dbuf; de < &dbuf[TBSIZE]; de += DLEN) { ino = p11s(&de[D_INO]); if (ino == 0) continue; if (strncmp(&de[D_NAME], name, DNSIZE) == 0) return (ino); } } return (0); } /* * retrieve file data */ Rfile * getino(i) int i; { register Rfile *f; register struct spcl *sp; long nblocks; if (i >= MAXINO || i < 0 || iaddr[i] == 0) { rflog("%d: bad i-number\n", i); fserrno = EINVAL; return (NULL); } lseek(devfd, (long)iaddr[i] * TBSIZE, 0); if ((sp = getspcl()) == NULL || sp->c_type != TS_INODE) { rflog("%d: can't find header\n", i); fserrno = EIO; return (NULL); } if (sp->c_inumber != i) { rflog("%d: found header for %d instead\n", i, sp->c_inumber); fserrno = EIO; return (NULL); } if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL) { rflog("%d: no memory for header\n", i); fserrno = ENOMEM; return (NULL); } f->ino = i; f->dev = 0; f->mode = p11s(&sp->c_dinode[DI_MODE]); /* understood same bits */ f->nlink = p11s(&sp->c_dinode[DI_NLINK]); f->uid = p11s(&sp->c_dinode[DI_UID]); f->gid = p11s(&sp->c_dinode[DI_GID]); f->rdev = 0; f->size = p11l(&sp->c_dinode[DI_SIZE]); f->ta = p11l(&sp->c_dinode[DI_ATIME]); f->tm = p11l(&sp->c_dinode[DI_MTIME]); f->tc = p11l(&sp->c_dinode[DI_CTIME]); nblocks = (f->size + TBSIZE - 1)/TBSIZE; if ((f->fs = malloc(nblocks * sizeof(taddr_t))) == NULL) { rflog("%d: no mem for %d block addresses\n", i, nblocks); fserrno = ENOMEM; free((char *)f); return (NULL); } scanfile(sp, (taddr_t *)f->fs, nblocks); return (f); } /* * read a TBSIZE block from file */ getblk(f, bno, buf) register Rfile *f; long bno; char *buf; { if (bno * TBSIZE > f->size) { fserrno = 0; return (0); } lseek(devfd, (long)((taddr_t *)f->fs)[bno] * TBSIZE, 0); if (read(devfd, buf, TBSIZE) != TBSIZE) { rflog("%d: bno %ld: errno %d\n", f->ino, bno, errno); fserrno = errno; return (0); } return (1); } /* * scan the tape, * remember where all the file records are */ scantape() { register struct spcl *sp; register int i; for (;;) { if ((sp = getspcl()) == NULL) { rflog("unexpected tape EOF\n"); return; } switch (sp->c_type) { case TS_END: return; case TS_TAPE: continue; case TS_BITS: case TS_CLRI: lseek(devfd, (long)sp->c_count * TBSIZE, 1); continue; case TS_ADDR: rflog("unexpected TS_ADDR i %d\n", sp->c_inumber); continue; default: rflog("ill spcl type %d i %d\n", sp->c_type, sp->c_inumber); continue; case TS_INODE: i = sp->c_inumber; if (i <= 0 || i >= MAXINO) rflog("ill TS_FILE i %d\n", i); else iaddr[i] = curtblock() - 1; scanfile(sp, (taddr_t *)NULL, (p11l(&sp->c_dinode[DI_SIZE])+TBSIZE-1)/TBSIZE); continue; } } } /* * skip lightly through a file, * remembering where the blocks are * ip is an array of tape addresses; * n is the length of the array, if present, * which is the same as the number of blocks expected * call with the TS_INODE record, * since that contains the first list of blocks */ scanfile(sp, ip, n) register struct spcl *sp; register taddr_t *ip; register int n; { register int i; taddr_t tblock; tblock = curtblock(); while (n) { for (i = 0; i < sp->c_count; i++) { if (sp->c_count == 0) { /* hole */ if (ip) *ip++ = 0; } else { /* real */ if (ip) *ip++ = tblock; tblock++; } if (--n <= 0) break; } lseek(devfd, (long)tblock*TBSIZE, 0); /* past data */ if (n <= 0) return; /* * more: get TS_ADDR */ if ((sp = getspcl()) == NULL) { rflog("unexpected tape EOF\n"); return; } if (sp->c_type != TS_ADDR) { rflog("wanted TS_ADDR, got type %d i %d\n", sp->c_type, sp->c_inumber); unspcl(); return; } tblock++; /* count the header we just read */ } } struct spcl * getspcl() { char buf[TBSIZE]; static struct spcl s; register struct spcl11 *tp; int nbad; nbad = 0; tp = (struct spcl11 *)buf; for (;;) { if (read(devfd, buf, TBSIZE) != TBSIZE) { rflog("tape scan read, errno %d\n", errno); return (NULL); } if (p11s(tp->c_magic) != MAGIC) { nbad++; continue; } s.c_type = p11s(tp->c_type); s.c_inumber = p11s(tp->c_inumber); s.c_count = p11s(tp->c_count); /* a sanity check or two would go well here */ memcpy(s.c_dinode, tp->c_dinode, DILEN); memcpy(s.c_addr, tp->c_addr, s.c_count); if (nbad) rflog("skipped %d to type %d i %d\n", nbad, s.c_type, s.c_inumber); return (&s); } } /* * reread the current header: cheap hack */ unspcl() { lseek(devfd, (long)-TBSIZE, 1); } taddr_t curtblock() { return (lseek(devfd, 0L, 1)/TBSIZE); }