/* * File system for tar archives (read-only) */ #include #include #include #include #include "tapefs.h" /* fundamental constants */ enum { Tblock = 512, Namsiz = 100, Maxpfx = 155, /* from POSIX */ Maxname = Namsiz + 1 + Maxpfx, Binsize = 0x80, /* flag in size[0], from gnu: positive binary size */ Binnegsz = 0xff, /* flag in size[0]: negative binary size */ }; /* POSIX link flags */ enum { LF_PLAIN1 = '\0', LF_PLAIN2 = '0', LF_LINK = '1', LF_SYMLINK1 = '2', LF_SYMLINK2 = 's', /* 4BSD used this */ LF_CHR = '3', LF_BLK = '4', LF_DIR = '5', LF_FIFO = '6', LF_CONTIG = '7', /* 'A' - 'Z' are reserved for custom implementations */ }; typedef union { char dummy[Tblock]; char tbuf[Maxbuf]; struct Header { char name[Namsiz]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char linkflag; char linkname[Namsiz]; /* rest are defined by POSIX's ustar format; see p1003.2b */ char magic[6]; /* "ustar" */ char version[2]; char uname[32]; char gname[32]; char devmajor[8]; char devminor[8]; char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */ }; } Hdr; Hdr dblock; int tapefile; int checksum(void); static int isustar(Hdr *hp) { return strcmp(hp->magic, "ustar") == 0; } /* * s is at most n bytes long, but need not be NUL-terminated. * if shorter than n bytes, all bytes after the first NUL must also * be NUL. */ static int strnlen(char *s, int n) { return s[n - 1] != '\0'? n: strlen(s); } /* set fullname from header */ static char * tarname(Hdr *hp) { int pfxlen, namlen; static char fullname[Maxname+1]; namlen = strnlen(hp->name, sizeof hp->name); if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */ memmove(fullname, hp->name, namlen); fullname[namlen] = '\0'; return fullname; } /* posix name: name is in two pieces */ pfxlen = strnlen(hp->prefix, sizeof hp->prefix); memmove(fullname, hp->prefix, pfxlen); fullname[pfxlen] = '/'; memmove(fullname + pfxlen + 1, hp->name, namlen); fullname[pfxlen + 1 + namlen] = '\0'; return fullname; } void populate(char *name) { long chksum, linkflg; vlong blkno; char *fname; Fileinf f; Hdr *hp; tapefile = open(name, OREAD); if (tapefile < 0) error("Can't open argument file"); replete = 1; hp = &dblock; for (blkno = 0; ; blkno++) { seek(tapefile, Tblock*blkno, 0); if (readn(tapefile, hp->dummy, sizeof hp->dummy) < sizeof hp->dummy) break; fname = tarname(hp); if (fname[0] == '\0') break; /* crack header */ f.addr = blkno + 1; f.mode = strtoul(hp->mode, 0, 8); f.uid = strtoul(hp->uid, 0, 8); f.gid = strtoul(hp->gid, 0, 8); if((uchar)hp->size[0] == 0x80) f.size = b8byte(hp->size+3); else f.size = strtoull(hp->size, 0, 8); f.mdate = strtoul(hp->mtime, 0, 8); chksum = strtoul(hp->chksum, 0, 8); /* the mode test is ugly but sometimes necessary */ if (hp->linkflag == LF_DIR || (f.mode&0170000) == 040000 || strrchr(fname, '\0')[-1] == '/'){ f.mode |= DMDIR; f.size = 0; } f.mode &= DMDIR | 0777; /* make file name safe, canonical and free of . and .. */ while (fname[0] == '/') /* don't allow absolute paths */ ++fname; cleanname(fname); while (strncmp(fname, "../", 3) == 0) fname += 3; /* reject links */ linkflg = hp->linkflag == LF_SYMLINK1 || hp->linkflag == LF_SYMLINK2 || hp->linkflag == LF_LINK; if (chksum != checksum()){ fprint(2, "%s: bad checksum on %.28s at offset %lld\n", argv0, fname, Tblock*blkno); exits("checksum"); } if (linkflg) { /*fprint(2, "link %s->%s skipped\n", fname, hp->linkname);*/ f.size = 0; } else { /* accept this file */ f.name = fname; if (f.name[0] == '\0') fprint(2, "%s: null name skipped\n", argv0); else poppath(f, 1); blkno += (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) { int n; seek(tapefile, Tblock*r->addr + off, 0); if (cnt > sizeof dblock.tbuf) error("read too big"); n = readn(tapefile, dblock.tbuf, cnt); if (n != cnt) memset(dblock.tbuf + n, 0, cnt - n); 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(void) { int i, n; uchar *cp; memset(dblock.chksum, ' ', sizeof dblock.chksum); cp = (uchar *)dblock.dummy; i = 0; for (n = Tblock; n-- > 0; ) i += *cp++; return i; }