1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "tar.h"
30
31 #include <string.h>
32 #include <cx/string.h>
33 #include <libidav/utils.h>
34
35
36 const char* tar_error2str(TarError error) {
37 switch(error) {
38 default:
break;
39 case TAR_OK:
return "ok";
40 case TAR_PATH_TOO_LONG:
return "path too long";
41 case TAR_FILE_TOO_LARGE:
return "file too large";
42 case TAR_CONTENT_TOO_LARGE:
return "tar content too large";
43 case TAR_UNFINISHED_FILE:
return "can''t read a tar header at this position";
44 case TAR_CONTENT_BROKEN:
return "tar content broken";
45 }
46 return "error";
47 }
48
49 TarOutputStream* tar_open(
FILE *f) {
50 TarOutputStream *tar = malloc(
sizeof(TarOutputStream));
51 tar->file= f;
52 tar->cur_filesize =
0;
53 tar->cur_written =
0;
54 tar->error =
0;
55 return tar;
56 }
57
58
59 static int add_header(TarOutputStream *tar,
char *path,
uint32_t mode,
uint64_t size,
time_t mtime,
int type) {
60
61 char *p = util_parent_path(path);
62 const char *n = util_resource_name(path);
63 if(!p || !n) {
64 return -
1;
65 }
66
67 cxstring prefix = cx_str(p);
68 cxstring name = cx_str(n);
69
70 if(prefix.ptr[prefix.length-
1] ==
'/') {
71 prefix.length--;
72 }
73
74 if(prefix.length >
154) {
75 tar->error =
TAR_PATH_TOO_LONG;
76 free(p);
77 return -
1;
78 }
79 if(name.length >
99) {
80 tar->error =
TAR_PATH_TOO_LONG;
81 free(p);
82 return -
1;
83 }
84
85
86 if(size >=
077777777777 ) {
87 tar->error =
TAR_FILE_TOO_LARGE;
88 free(p);
89 return -
1;
90 }
91
92
93 TarHeader h;
94 memset(&h,
0,
sizeof(TarHeader));
95
96
97 memcpy(h.name, name.ptr, name.length);
98
99 snprintf(h.mode,
8,
"%07o", mode);
100 h.mode[
7] =
' ';
101
102 memset(h.uid,
'0',
8);
103 h.uid[
7] =
' ';
104 memset(h.gid,
'0',
8);
105 h.gid[
7] =
' ';
106
107 snprintf(h.size,
12,
"%011lo", (
unsigned long)size);
108 h.size[
11] =
' ';
109
110 uint64_t t = (
uint64_t)mtime;
111 snprintf(h.mtime,
12,
"%011lo", mtime);
112 h.mtime[
11] =
' ';
113
114 memset(h.chksum,
' ',
8);
115
116 h.typeflag = type;
117
118
119 snprintf(h.magic,
6,
"ustar");
120
121 h.version[
0] =
'0';
122 h.version[
1] =
'0';
123
124
125 char *devbuf = (
char*)h.devmajor;
126 snprintf(devbuf,
16,
"%015o",
0);
127 h.devmajor[
7] =
' ';
128 h.devminor[
7] =
' ';
129
130 memcpy(h.prefix, prefix.ptr, prefix.length);
131
132
133 uint8_t *header = (
uint8_t*)&h;
134 uint32_t chksum =
0;
135 for(
int i=
0;i<
512;i++) {
136 chksum += header[i];
137 }
138 snprintf(h.chksum,
8,
"%07o", chksum);
139
140 fwrite(&h,
1,
512, tar->file);
141
142 free(p);
143
144 return 0;
145 }
146
147
148 int tar_add_dir(TarOutputStream *tar,
char *path,
uint32_t mode,
time_t mtime) {
149 return add_header(tar, path, mode,
0, mtime,
TAR_TYPE_DIRECTORY);
150 }
151
152 int tar_begin_file(
153 TarOutputStream *tar,
154 char *path,
155 uint32_t mode,
156 uint64_t size,
157 time_t mtime)
158 {
159 if(add_header(tar, path, mode, size, mtime,
TAR_TYPE_FILE)) {
160 return -
1;
161 }
162
163 tar->cur_filesize = size;
164 tar->cur_written =
0;
165
166 return 0;
167 }
168
169 size_t tar_fwrite(
const void *ptr,
size_t s,
size_t n, TarOutputStream *stream) {
170 size_t w = fwrite(ptr, s, n, stream->file);
171 stream->cur_written += w;
172 if(stream->cur_written > stream->cur_filesize) {
173 stream->error =
TAR_CONTENT_TOO_LARGE;
174 return 0;
175 }
176 return w;
177 }
178
179 int tar_end_file(TarOutputStream *tar) {
180 size_t pad =
512 - tar->cur_written %
512;
181 char buf[
512];
182 memset(buf,
0,
512);
183 fwrite(buf,
1, pad, tar->file);
184
185 tar->cur_filesize =
0;
186 tar->cur_written =
0;
187 return 0;
188 }
189
190 int tar_close(TarOutputStream *tar) {
191 char buf[
512];
192 memset(buf,
0,
512);
193 fwrite(buf,
1,
512, tar->file);
194 return 0;
195 }
196
197 TarInputStream* tar_inputstream_open(
FILE *f) {
198 TarInputStream *tar = malloc(
sizeof(TarInputStream));
199 memset(tar,
0,
sizeof(TarInputStream));
200 tar->file = f;
201 return tar;
202 }
203
204 TarEntry* tar_read_entry(TarInputStream *tar) {
205 if(tar->cur_read < tar->cur_entry.size) {
206 tar->error =
TAR_UNFINISHED_FILE;
207 return NULL;
208 }
209
210 TarHeader h;
211 memset(&h,
0,
sizeof(TarHeader));
212
213 if(fread(&h,
1,
512, tar->file) !=
512) {
214 tar->error =
TAR_ERROR;
215 return NULL;
216 }
217
218
219 tar->error =
TAR_CONTENT_BROKEN;
220
221 char *buf = (
char*)&h;
222 uint64_t chksum =
0;
223 int chksumfield =
8 *
32;
224 for(
int i=
0;i<
148;i++) {
225 chksum += buf[i];
226 }
227 chksum += chksumfield;
228 for(
int i=
156;i<
512;i++) {
229 chksum += buf[i];
230 }
231
232 if(chksum != chksumfield) {
233 long long int cks = strtoll(h.chksum,
NULL,
8);
234 if(cks != chksum) {
235 return NULL;
236 }
237 }
238
239 if(memcmp(h.magic,
"ustar\0",
6)) {
240 if(!memcmp(h.magic,
"\0\0\0\0",
4)) {
241 tar->error =
TAR_OK;
242 }
243 return NULL;
244 }
245 if(memcmp(h.version,
"00",
2)) {
246 return NULL;
247 }
248
249
250 int nameterm =
0;
251 int prefixterm =
0;
252 for(
int i=
0;i<
100;i++) {
253 if(h.name[i] ==
0) {
254 nameterm =
1;
255 break;
256 }
257 }
258 for(
int i=
0;i<
155;i++) {
259 if(h.prefix[i] ==
0) {
260 prefixterm =
1;
261 break;
262 }
263 }
264 if(!nameterm || !prefixterm) {
265 return NULL;
266 }
267
268
269 if(!(h.size[
11] ==
' ' || h.size[
11] ==
0)) {
270 return NULL;
271 }
272 long long int size = strtoll(h.size,
NULL,
8);
273 if(size <
0) {
274 return NULL;
275 }
276
277 if(h.name[
0] ==
0) {
278 return NULL;
279 }
280
281
282 char *path = h.prefix[
0] !=
0 ? util_concat_path(h.prefix, h.name) : strdup(h.name);
283
284 if(tar->cur_entry.path) {
285 free(tar->cur_entry.path);
286 }
287
288 tar->cur_entry.path = path;
289 tar->cur_entry.size = (
uint64_t)size;
290 tar->cur_entry.type = h.typeflag;
291 tar->cur_read =
0;
292
293 tar->error =
TAR_OK;
294 return &tar->cur_entry;
295 }
296
297 size_t tar_fread(
void *ptr,
size_t s,
size_t n, TarInputStream *stream) {
298 size_t bufsize = s*n;
299 size_t available = stream->cur_entry.size - stream->cur_read;
300
301 size_t nb = available > bufsize ? bufsize : available;
302 if(nb ==
0) {
303 return 0;
304 }
305 size_t r = fread(ptr,
1, nb, stream->file);
306 if(r != nb) {
307 stream->error =
TAR_ERROR;
308 return 0;
309 }
310
311 stream->cur_read += r;
312 if(stream->cur_read >= stream->cur_entry.size) {
313
314 size_t pad =
512 - stream->cur_read %
512;
315 char buf[
512];
316 if(fread(buf,
1, pad, stream->file) != pad) {
317 stream->error =
TAR_ERROR;
318 return 0;
319 }
320 }
321
322 return r;
323 }
324
325 int tar_seek(TarInputStream *stream,
long offset,
int whence) {
326 return fseek(stream->file, offset, whence);
327 }
328
329 int tar_inputstream_close(TarInputStream *tar) {
330 if(tar->cur_entry.path) {
331 free(tar->cur_entry.path);
332 }
333 free(tar);
334 return 0;
335 }
336