dav/tar.c

changeset 334
5f80c5d0e87f
child 336
6331271116d0
equal deleted inserted replaced
333:000cdd124115 334:5f80c5d0e87f
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2017 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "tar.h"
30
31 #include <string.h>
32 #include <ucx/string.h>
33 #include <libidav/utils.h>
34
35
36 const char* tar_error2str(TarError error) {
37 switch(error) {
38 case TAR_OK: return "ok";
39 case TAR_PATH_TOO_LONG: return "path too long";
40 case TAR_FILE_TOO_LARGE: return "file too large";
41 case TAR_CONTENT_TOO_LARGE: return "tar content too large";
42 case TAR_UNFINISHED_FILE: return "can't read a tar header at this position";
43 case TAR_CONTENT_BROKEN: return "tar content broken";
44 }
45 return "error";
46 }
47
48 TarOutputStream* tar_open(FILE *f) {
49 TarOutputStream *tar = malloc(sizeof(TarOutputStream));
50 tar->file= f;
51 tar->cur_filesize = 0;
52 tar->cur_written = 0;
53 tar->error = 0;
54 return tar;
55 }
56
57
58 static int add_header(TarOutputStream *tar, char *path, uint32_t mode, uint64_t size, time_t mtime, int type) {
59 // split path in prefix and name and check length
60 char *p = util_parent_path(path);
61 char *n = util_resource_name(path);
62 if(!p || !n) {
63 return -1;
64 }
65
66 sstr_t prefix = sstr(p);
67 sstr_t name = sstr(n);
68
69 if(prefix.ptr[prefix.length-1] == '/') {
70 prefix.length--;
71 }
72
73 if(prefix.length > 154) {
74 tar->error = TAR_PATH_TOO_LONG;
75 return -1;
76 }
77 if(name.length > 99) {
78 tar->error = TAR_PATH_TOO_LONG;
79 return -1;
80 }
81
82 // check file length
83 if(size >= 077777777777 ) {
84 tar->error = TAR_FILE_TOO_LARGE;
85 return -1;
86 }
87
88 // set header fields
89 TarHeader h;
90 memset(&h, 0, sizeof(TarHeader));
91
92 // name
93 memcpy(h.name, name.ptr, name.length);
94 // mode
95 snprintf(h.mode, 8, "%0.7o", mode);
96 h.mode[7] = ' ';
97 // uid/gid
98 memset(h.uid, '0', 16);
99 h.uid[7] = ' ';
100 h.gid[7] = ' ';
101 // size
102 snprintf(h.size, 12, "%0.11lo", size);
103 h.size[11] = ' ';
104 // mtime
105 uint64_t t = (uint64_t)mtime;
106 snprintf(h.mtime, 12, "%0.11lo", mtime);
107 h.mtime[11] = ' ';
108 // chksum
109 memset(h.chksum, ' ', 8);
110 // typeflag
111 h.typeflag = type;
112 // linkname - zeros
113 // magic
114 snprintf(h.magic, 6, "ustar");
115 // version
116 h.version[0] = '0';
117 h.version[1] = '0';
118 // uname/gname - zero
119 // devmajor/devminor
120 snprintf(h.devmajor, 16, "%0.15o", 0);
121 h.devmajor[7] = ' ';
122 h.devminor[7] = ' ';
123 // prefix
124 memcpy(h.prefix, prefix.ptr, prefix.length);
125
126 // compute checksum
127 uint8_t *header = (uint8_t*)&h;
128 uint32_t chksum = 0;
129 for(int i=0;i<512;i++) {
130 chksum += header[i];
131 }
132 snprintf(h.chksum, 8, "%0.7o", chksum);
133
134 fwrite(&h, 1, 512, tar->file);
135
136 return 0;
137 }
138
139
140 int tar_add_dir(TarOutputStream *tar, char *path, uint32_t mode, time_t mtime) {
141 return add_header(tar, path, mode, 0, mtime, TAR_TYPE_DIRECTORY);
142 }
143
144 int tar_begin_file(
145 TarOutputStream *tar,
146 char *path,
147 uint32_t mode,
148 uint64_t size,
149 time_t mtime)
150 {
151 if(add_header(tar, path, mode, size, mtime, TAR_TYPE_FILE)) {
152 return -1;
153 }
154
155 tar->cur_filesize = size;
156 tar->cur_written = 0;
157
158 return 0;
159 }
160
161 size_t tar_fwrite(const void *ptr, size_t s, size_t n, TarOutputStream *stream) {
162 size_t w = fwrite(ptr, s, n, stream->file);
163 stream->cur_written += w;
164 if(stream->cur_written > stream->cur_filesize) {
165 stream->error = TAR_CONTENT_TOO_LARGE;
166 return 0;
167 }
168 return w;
169 }
170
171 int tar_end_file(TarOutputStream *tar) {
172 size_t pad = 512 - tar->cur_written % 512;
173 char buf[512];
174 memset(buf, 0, 512);
175 fwrite(buf, 1, pad, tar->file);
176
177 tar->cur_filesize = 0;
178 tar->cur_written = 0;
179 return 0;
180 }
181
182 int tar_close(TarOutputStream *tar) {
183 char buf[512];
184 memset(buf, 0, 512);
185 fwrite(buf, 1, 512, tar->file);
186 return 0;
187 }
188
189 TarInputStream* tar_inputstream_open(FILE *f) {
190 TarInputStream *tar = malloc(sizeof(TarInputStream));
191 memset(tar, 0, sizeof(TarInputStream));
192 tar->file = f;
193 return tar;
194 }
195
196 TarEntry* tar_read_entry(TarInputStream *tar) {
197 if(tar->cur_read < tar->cur_entry.size) {
198 tar->error = TAR_UNFINISHED_FILE;
199 return NULL;
200 }
201
202 TarHeader h;
203 memset(&h, 0, sizeof(TarHeader));
204
205 if(fread(&h, 1, 512, tar->file) != 512) {
206 tar->error = TAR_ERROR;
207 return NULL;
208 }
209
210 // some checks
211 tar->error = TAR_CONTENT_BROKEN; // set error for all following returns
212
213 char *buf = (char*)&h;
214 uint64_t chksum = 0;
215 int chksumfield = 8 * 32;
216 for(int i=0;i<148;i++) {
217 chksum += buf[i];
218 }
219 chksum += chksumfield;
220 for(int i=156;i<512;i++) {
221 chksum += buf[i];
222 }
223
224 if(chksum != chksumfield) {
225 long long int cks = strtoll(h.chksum, NULL, 8);
226 if(cks != chksum) {
227 return NULL;
228 }
229 }
230
231 if(memcmp(h.magic, "ustar\0", 6)) {
232 return NULL;
233 }
234 if(memcmp(h.version, "00", 2)) {
235 return NULL;
236 }
237
238 // check if name and prefix are null terminated
239 int nameterm = 0;
240 int prefixterm = 0;
241 for(int i=0;i<100;i++) {
242 if(h.name[i] == 0) {
243 nameterm = 1;
244 break;
245 }
246 }
247 for(int i=0;i<155;i++) {
248 if(h.prefix[i] == 0) {
249 prefixterm = 1;
250 break;
251 }
252 }
253 if(!nameterm || !prefixterm) {
254 return NULL;
255 }
256
257 // get size
258 if(h.size[11] != ' ') {
259 return NULL;
260 }
261 long long int size = strtoll(h.size, NULL, 8);
262 if(size < 0) {
263 return NULL;
264 }
265
266 if(h.name[0] == 0) {
267 return NULL;
268 }
269
270 // get path
271 char *path = h.prefix[0] != 0 ? util_concat_path(h.prefix, h.name) : strdup(h.name);
272
273 if(tar->cur_entry.path) {
274 free(tar->cur_entry.path);
275 }
276
277 tar->cur_entry.path = path;
278 tar->cur_entry.size = (uint64_t)size;
279 tar->cur_entry.type = h.typeflag;
280 tar->cur_read = 0;
281
282 tar->error = TAR_OK;
283 return &tar->cur_entry;
284 }
285
286 size_t tar_fread(void *ptr, size_t s, size_t n, TarInputStream *stream) {
287 size_t bufsize = s*n;
288 size_t available = stream->cur_entry.size - stream->cur_read;
289
290 size_t nb = available > bufsize ? bufsize : available;
291 if(nb == 0) {
292 return 0;
293 }
294 size_t r = fread(ptr, 1, nb, stream->file);
295 if(r != nb) {
296 stream->error = TAR_ERROR;
297 return 0;
298 }
299
300 stream->cur_read += r;
301 if(stream->cur_read >= stream->cur_entry.size) {
302 // read padding
303 size_t pad = 512 - stream->cur_read % 512;
304 char buf[512];
305 if(fread(buf, 1, pad, stream->file) != pad) {
306 stream->error = TAR_ERROR;
307 return 0;
308 }
309 }
310
311 return r;
312 }
313
314 int tar_inputstream_close(TarInputStream *tar) {
315 if(tar->cur_entry.path) {
316 free(tar->cur_entry.path);
317 }
318 free(tar);
319 }

mercurial