1 /*
2 * tar archive manipulation functions
3 */
4
5 #include <u.h>
6 #include <libc.h>
7 #include <ctype.h>
8 #include "tar.h"
9
10 enum {
11 Blocksxfr = 32,
12 };
13
14 /* exports */
15 char *thisnm, *lastnm;
16
17 /* private data */
18 static uvlong outoff = 0; /* maintained by newarch, writetar */
19
20 unsigned
checksum(Hblock * hp)21 checksum(Hblock *hp)
22 {
23 int i;
24 uchar *cp, *csum, *end;
25
26 i = ' ' * sizeof hp->chksum; /* pretend blank chksum field */
27 csum = (uchar *)hp->chksum;
28 end = &hp->dummy[Tblock];
29 /*
30 * Unixware gets this wrong; it adds *signed* chars.
31 * i += (Uflag? *(schar *)cp: *cp);
32 */
33 for (cp = hp->dummy; cp < csum; )
34 i += *cp++;
35 /* skip checksum field */
36 for (cp += sizeof hp->chksum; cp < end; )
37 i += *cp++;
38 return i;
39 }
40
41 void
readtar(int in,char * buffer,long size)42 readtar(int in, char *buffer, long size)
43 {
44 int i;
45 unsigned bytes;
46
47 bytes = i = readn(in, buffer, size);
48 if (i <= 0)
49 sysfatal("archive read error: %r");
50 if (bytes % Tblock != 0)
51 sysfatal("archive blocksize error");
52 if (bytes != size) {
53 /*
54 * buffering would be screwed up by only partially
55 * filling tbuf, yet this might be the last (short)
56 * record in a tar disk archive, so just zero the rest.
57 */
58 fprint(2, "%s: warning: short archive block\n", argv0);
59 memset(buffer + bytes, '\0', size - bytes);
60 }
61 }
62
63 void
newarch(void)64 newarch(void)
65 {
66 outoff = 0;
67 }
68
69 uvlong
writetar(int outf,char * buffer,ulong size)70 writetar(int outf, char *buffer, ulong size)
71 {
72 if (write(outf, buffer, size) < size) {
73 fprint(2, "%s: archive write error: %r\n", argv0);
74 fprint(2, "%s: archive seek offset: %llud\n", argv0, outoff);
75 exits("write");
76 }
77 outoff += size;
78 return outoff;
79 }
80
81 ulong
otoi(char * s)82 otoi(char *s)
83 {
84 int c;
85 ulong ul = 0;
86
87 while (isascii(*s) && isspace(*s))
88 s++;
89 while ((c = *s++) >= '0' && c <= '7') {
90 ul <<= 3;
91 ul |= c - '0';
92 }
93 return ul;
94 }
95
96 int
getdir(Hblock * hp,int in,vlong * lenp)97 getdir(Hblock *hp, int in, vlong *lenp)
98 {
99 *lenp = 0;
100 readtar(in, (char*)hp, Tblock);
101 if (hp->name[0] == '\0') { /* zero block indicates end-of-archive */
102 lastnm = strdup(thisnm);
103 return 0;
104 }
105 *lenp = otoi(hp->size);
106 if (otoi(hp->chksum) != checksum(hp))
107 sysfatal("directory checksum error");
108 if (lastnm != nil)
109 free(lastnm);
110 lastnm = thisnm;
111 thisnm = strdup(hp->name);
112 return 1;
113 }
114
115 uvlong
passtar(Hblock * hp,int in,int outf,vlong len)116 passtar(Hblock *hp, int in, int outf, vlong len)
117 {
118 ulong bytes;
119 vlong off;
120 uvlong blks;
121 char bigbuf[Blocksxfr*Tblock]; /* 2*(8192 == MAXFDATA) */
122
123 off = outoff;
124 if (islink(hp->linkflag))
125 return off;
126 for (blks = TAPEBLKS((uvlong)len); blks >= Blocksxfr;
127 blks -= Blocksxfr) {
128 readtar(in, bigbuf, sizeof bigbuf);
129 off = writetar(outf, bigbuf, sizeof bigbuf);
130 }
131 if (blks > 0) {
132 bytes = blks*Tblock;
133 readtar(in, bigbuf, bytes);
134 off = writetar(outf, bigbuf, bytes);
135 }
136 return off;
137 }
138
139 void
putempty(int out)140 putempty(int out)
141 {
142 static char buf[Tblock];
143
144 writetar(out, buf, sizeof buf);
145 }
146
147 /* emit zero blocks at end */
148 int
closeout(int outf,char *,int prflag)149 closeout(int outf, char *, int prflag)
150 {
151 if (outf < 0)
152 return -1;
153 putempty(outf);
154 putempty(outf);
155 if (lastnm && prflag)
156 fprint(2, " %s\n", lastnm);
157 close(outf); /* guaranteed to succeed on plan 9 */
158 return -1;
159 }
160