xref: /plan9-contrib/sys/src/cmd/tarsplit/tarsub.c (revision ad6ca847b1a6a504acb0003cd6c5c6d92687369b)
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