/* * tar archive manipulation functions */ #include #include #include #include "tar.h" enum { Blocksxfr = 32, }; /* exports */ char *thisnm, *lastnm; /* private data */ static uvlong outoff = 0; /* maintained by newarch, writetar */ unsigned checksum(Hblock *hp) { int i; uchar *cp, *csum, *end; i = ' ' * sizeof hp->chksum; /* pretend blank chksum field */ csum = (uchar *)hp->chksum; end = &hp->dummy[Tblock]; /* * Unixware gets this wrong; it adds *signed* chars. * i += (Uflag? *(schar *)cp: *cp); */ for (cp = hp->dummy; cp < csum; ) i += *cp++; /* skip checksum field */ for (cp += sizeof hp->chksum; cp < end; ) i += *cp++; return i; } void readtar(int in, char *buffer, long size) { int i; unsigned bytes; bytes = i = readn(in, buffer, size); if (i <= 0) sysfatal("archive read error: %r"); if (bytes % Tblock != 0) sysfatal("archive blocksize error"); if (bytes != size) { /* * buffering would be screwed up by only partially * filling tbuf, yet this might be the last (short) * record in a tar disk archive, so just zero the rest. */ fprint(2, "%s: warning: short archive block\n", argv0); memset(buffer + bytes, '\0', size - bytes); } } void newarch(void) { outoff = 0; } uvlong writetar(int outf, char *buffer, ulong size) { if (write(outf, buffer, size) < size) { fprint(2, "%s: archive write error: %r\n", argv0); fprint(2, "%s: archive seek offset: %llud\n", argv0, outoff); exits("write"); } outoff += size; return outoff; } ulong otoi(char *s) { int c; ulong ul = 0; while (isascii(*s) && isspace(*s)) s++; while ((c = *s++) >= '0' && c <= '7') { ul <<= 3; ul |= c - '0'; } return ul; } int getdir(Hblock *hp, int in, vlong *lenp) { *lenp = 0; readtar(in, (char*)hp, Tblock); if (hp->name[0] == '\0') { /* zero block indicates end-of-archive */ lastnm = strdup(thisnm); return 0; } *lenp = otoi(hp->size); if (otoi(hp->chksum) != checksum(hp)) sysfatal("directory checksum error"); if (lastnm != nil) free(lastnm); lastnm = thisnm; thisnm = strdup(hp->name); return 1; } uvlong passtar(Hblock *hp, int in, int outf, vlong len) { ulong bytes; vlong off; uvlong blks; char bigbuf[Blocksxfr*Tblock]; /* 2*(8192 == MAXFDATA) */ off = outoff; if (islink(hp->linkflag)) return off; for (blks = TAPEBLKS((uvlong)len); blks >= Blocksxfr; blks -= Blocksxfr) { readtar(in, bigbuf, sizeof bigbuf); off = writetar(outf, bigbuf, sizeof bigbuf); } if (blks > 0) { bytes = blks*Tblock; readtar(in, bigbuf, bytes); off = writetar(outf, bigbuf, bytes); } return off; } void putempty(int out) { static char buf[Tblock]; writetar(out, buf, sizeof buf); } /* emit zero blocks at end */ int closeout(int outf, char *, int prflag) { if (outf < 0) return -1; putempty(outf); putempty(outf); if (lastnm && prflag) fprint(2, " %s\n", lastnm); close(outf); /* guaranteed to succeed on plan 9 */ return -1; }