Rev 8791 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8791 | turbocat | 1 | /* |
2 | * Copyright (c) 2017 rxi |
||
3 | * |
||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
||
5 | * of this software and associated documentation files (the "Software"), to |
||
6 | * deal in the Software without restriction, including without limitation the |
||
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||
8 | * sell copies of the Software, and to permit persons to whom the Software is |
||
9 | * furnished to do so, subject to the following conditions: |
||
10 | * |
||
11 | * The above copyright notice and this permission notice shall be included in |
||
12 | * all copies or substantial portions of the Software. |
||
13 | * |
||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||
20 | * IN THE SOFTWARE. |
||
21 | */ |
||
22 | |||
23 | #include |
||
24 | #include |
||
25 | #include |
||
26 | #include |
||
27 | |||
28 | #include |
||
29 | |||
30 | #include "microtar.h" |
||
31 | |||
32 | typedef struct { |
||
33 | char name[100]; |
||
34 | char mode[8]; |
||
35 | char owner[8]; |
||
36 | char group[8]; |
||
37 | char size[12]; |
||
38 | char mtime[12]; |
||
39 | char checksum[8]; |
||
40 | char type; |
||
41 | char linkname[100]; |
||
42 | char _padding[255]; |
||
43 | } mtar_raw_header_t; |
||
44 | |||
45 | static void * mtar_memset( void * s, int c, size_t n ){ |
||
46 | unsigned char * p = ( unsigned char * ) s; |
||
47 | while ( n-- ){ |
||
48 | *p++ = ( unsigned char ) c; |
||
49 | } |
||
50 | return s; |
||
51 | } |
||
52 | |||
53 | #ifdef MTAR_OBJ |
||
54 | |||
55 | size_t (*fread)(void *restrict, size_t size, size_t count, FILE *restrict)=NULL; |
||
56 | size_t (*fwrite)(const void *restrict, size_t size, size_t count, FILE *restrict)=NULL; |
||
57 | int (*fclose)(FILE *)=NULL; |
||
58 | FILE* (*fopen)(const char *restrict, const char *restrict)=NULL; |
||
59 | int (*fseek)(FILE *, long, int)=NULL; |
||
60 | long (*ftell)(FILE *)=NULL; |
||
61 | int (*sprintf)(char* buffer, const char* format, ...)=NULL; |
||
62 | int (*sscanf)(const char*, const char *restrict, ...)=NULL; |
||
63 | int (*strcmp)(const char * s1, const char* s2)=NULL; |
||
64 | char* (*strchr)(const char* s, int c)=NULL; |
||
65 | char* (*strcpy)(char* s1, const char* s2)=NULL; |
||
66 | |||
67 | #endif |
||
68 | |||
69 | static unsigned round_up(unsigned n, unsigned incr) { |
||
70 | return n + (incr - n % incr) % incr; |
||
71 | } |
||
72 | |||
73 | |||
74 | static unsigned checksum(const mtar_raw_header_t* rh) { |
||
75 | unsigned i; |
||
76 | unsigned char *p = (unsigned char*) rh; |
||
77 | unsigned res = 256; |
||
78 | for (i = 0; i < offsetof(mtar_raw_header_t, checksum); i++) { |
||
79 | res += p[i]; |
||
80 | } |
||
81 | for (i = offsetof(mtar_raw_header_t, type); i < sizeof(*rh); i++) { |
||
82 | res += p[i]; |
||
83 | } |
||
84 | return res; |
||
85 | } |
||
86 | |||
87 | |||
88 | static int tread(mtar_t *tar, void *data, unsigned size) { |
||
89 | int err = tar->read(tar, data, size); |
||
90 | tar->pos += size; |
||
91 | return err; |
||
92 | } |
||
93 | |||
94 | |||
95 | static int twrite(mtar_t *tar, const void *data, unsigned size) { |
||
96 | |||
97 | int err = tar->write(tar, data, size); |
||
98 | tar->pos += size; |
||
99 | return err; |
||
100 | } |
||
101 | |||
102 | |||
103 | static int write_null_bytes(mtar_t *tar, int n) { |
||
104 | int i, err; |
||
105 | char nul = '\0'; |
||
106 | for (i = 0; i < n; i++) { |
||
107 | err = twrite(tar, &nul, 1); |
||
108 | if (err) { |
||
109 | return err; |
||
110 | } |
||
111 | } |
||
112 | return MTAR_ESUCCESS; |
||
113 | } |
||
114 | |||
115 | |||
116 | static int raw_to_header(mtar_header_t *h, const mtar_raw_header_t *rh) { |
||
117 | unsigned chksum1, chksum2; |
||
118 | |||
119 | /* If the checksum starts with a null byte we assume the record is NULL */ |
||
120 | if (*rh->checksum == '\0') { |
||
121 | return MTAR_ENULLRECORD; |
||
122 | } |
||
123 | |||
124 | /* Build and compare checksum */ |
||
125 | chksum1 = checksum(rh); |
||
126 | sscanf(rh->checksum, "%o", &chksum2); |
||
127 | if (chksum1 != chksum2) { |
||
128 | return MTAR_EBADCHKSUM; |
||
129 | } |
||
130 | |||
131 | /* Load raw header into header */ |
||
132 | sscanf(rh->mode, "%o", &h->mode); |
||
133 | sscanf(rh->owner, "%o", &h->owner); |
||
134 | sscanf(rh->size, "%o", &h->size); |
||
135 | sscanf(rh->mtime, "%o", &h->mtime); |
||
136 | h->type = rh->type; |
||
137 | strcpy(h->name, rh->name); |
||
138 | strcpy(h->linkname, rh->linkname); |
||
139 | |||
140 | return MTAR_ESUCCESS; |
||
141 | } |
||
142 | |||
143 | |||
144 | static int header_to_raw(mtar_raw_header_t *rh, const mtar_header_t *h) { |
||
145 | unsigned chksum; |
||
146 | |||
147 | /* Load header into raw header */ |
||
148 | mtar_memset(rh, 0, sizeof(*rh)); |
||
149 | sprintf(rh->mode, "%o", h->mode); |
||
150 | sprintf(rh->owner, "%o", h->owner); |
||
151 | sprintf(rh->size, "%o", h->size); |
||
152 | sprintf(rh->mtime, "%o", h->mtime); |
||
153 | rh->type = h->type ? h->type : MTAR_TREG; |
||
154 | strcpy(rh->name, h->name); |
||
155 | strcpy(rh->linkname, h->linkname); |
||
156 | |||
157 | /* Calculate and write checksum */ |
||
158 | chksum = checksum(rh); |
||
159 | sprintf(rh->checksum, "%06o", chksum); |
||
160 | rh->checksum[7] = ' '; |
||
161 | |||
162 | return MTAR_ESUCCESS; |
||
163 | } |
||
164 | |||
165 | |||
166 | const char* mtar_strerror(int err) { |
||
167 | switch (err) { |
||
168 | case MTAR_ESUCCESS : return "success"; |
||
169 | case MTAR_EFAILURE : return "failure"; |
||
170 | case MTAR_EOPENFAIL : return "could not open"; |
||
171 | case MTAR_EREADFAIL : return "could not read"; |
||
172 | case MTAR_EWRITEFAIL : return "could not write"; |
||
173 | case MTAR_ESEEKFAIL : return "could not seek"; |
||
174 | case MTAR_EBADCHKSUM : return "bad checksum"; |
||
175 | case MTAR_ENULLRECORD : return "null record"; |
||
176 | case MTAR_ENOTFOUND : return "file not found"; |
||
177 | } |
||
178 | return "unknown error"; |
||
179 | } |
||
180 | |||
181 | |||
182 | static int file_write(mtar_t *tar, const void *data, unsigned size) { |
||
183 | unsigned res = fwrite(data, 1, size, tar->stream); |
||
184 | return (res == size) ? MTAR_ESUCCESS : MTAR_EWRITEFAIL; |
||
185 | } |
||
186 | |||
187 | static int file_read(mtar_t *tar, void *data, unsigned size) { |
||
188 | unsigned res = fread(data, 1, size, tar->stream); |
||
189 | return (res == size) ? MTAR_ESUCCESS : MTAR_EREADFAIL; |
||
190 | } |
||
191 | |||
192 | static int file_seek(mtar_t *tar, unsigned offset) { |
||
193 | int res = fseek(tar->stream, offset, SEEK_SET); |
||
194 | return (res == 0) ? MTAR_ESUCCESS : MTAR_ESEEKFAIL; |
||
195 | } |
||
196 | |||
197 | static int file_close(mtar_t *tar) { |
||
198 | fclose(tar->stream); |
||
199 | return MTAR_ESUCCESS; |
||
200 | } |
||
201 | |||
202 | |||
203 | int mtar_open(mtar_t *tar, const char *filename, const char *mode) { |
||
204 | int err; |
||
205 | mtar_header_t h; |
||
206 | |||
207 | /* Init tar struct and functions */ |
||
208 | mtar_memset(tar, 0, sizeof(*tar)); |
||
209 | tar->write = file_write; |
||
210 | tar->read = file_read; |
||
211 | tar->seek = file_seek; |
||
212 | tar->close = file_close; |
||
213 | |||
214 | /* Assure mode is always binary */ |
||
215 | if ( strchr(mode, 'r') ) mode = "rb"; |
||
216 | if ( strchr(mode, 'w') ) mode = "wb"; |
||
217 | if ( strchr(mode, 'a') ) mode = "ab"; |
||
218 | /* Open file */ |
||
219 | tar->stream = fopen(filename, mode); |
||
220 | if (!tar->stream) { |
||
221 | return MTAR_EOPENFAIL; |
||
222 | } |
||
223 | /* Read first header to check it is valid if mode is `r` */ |
||
224 | if (*mode == 'r') { |
||
225 | err = mtar_read_header(tar, &h); |
||
226 | if (err != MTAR_ESUCCESS) { |
||
227 | mtar_close(tar); |
||
228 | return err; |
||
229 | } |
||
230 | } |
||
231 | |||
232 | /* Return ok */ |
||
233 | return MTAR_ESUCCESS; |
||
234 | } |
||
235 | |||
236 | |||
237 | int mtar_close(mtar_t *tar) { |
||
238 | return tar->close(tar); |
||
239 | } |
||
240 | |||
241 | |||
242 | int mtar_seek(mtar_t *tar, unsigned pos) { |
||
243 | int err = tar->seek(tar, pos); |
||
244 | tar->pos = pos; |
||
245 | return err; |
||
246 | } |
||
247 | |||
248 | |||
249 | int mtar_rewind(mtar_t *tar) { |
||
250 | tar->remaining_data = 0; |
||
251 | tar->last_header = 0; |
||
252 | return mtar_seek(tar, 0); |
||
253 | } |
||
254 | |||
255 | |||
256 | int mtar_next(mtar_t *tar) { |
||
257 | int err, n; |
||
258 | mtar_header_t h; |
||
259 | /* Load header */ |
||
260 | err = mtar_read_header(tar, &h); |
||
261 | if (err) { |
||
262 | return err; |
||
263 | } |
||
264 | /* Seek to next record */ |
||
265 | n = round_up(h.size, 512) + sizeof(mtar_raw_header_t); |
||
266 | return mtar_seek(tar, tar->pos + n); |
||
267 | } |
||
268 | |||
269 | |||
270 | int mtar_find(mtar_t *tar, const char *name, mtar_header_t *h) { |
||
271 | int err; |
||
272 | mtar_header_t header; |
||
273 | /* Start at beginning */ |
||
274 | err = mtar_rewind(tar); |
||
275 | if (err) { |
||
276 | return err; |
||
277 | } |
||
278 | /* Iterate all files until we hit an error or find the file */ |
||
279 | while ( (err = mtar_read_header(tar, &header)) == MTAR_ESUCCESS ) { |
||
280 | if ( !strcmp(header.name, name) ) { |
||
281 | if (h) { |
||
282 | *h = header; |
||
283 | } |
||
284 | return MTAR_ESUCCESS; |
||
285 | } |
||
286 | mtar_next(tar); |
||
287 | } |
||
288 | /* Return error */ |
||
289 | if (err == MTAR_ENULLRECORD) { |
||
290 | err = MTAR_ENOTFOUND; |
||
291 | } |
||
292 | return err; |
||
293 | } |
||
294 | |||
295 | |||
296 | int mtar_read_header(mtar_t *tar, mtar_header_t *h) { |
||
297 | int err; |
||
298 | mtar_raw_header_t rh; |
||
299 | /* Save header position */ |
||
300 | tar->last_header = tar->pos; |
||
301 | /* Read raw header */ |
||
302 | err = tread(tar, &rh, sizeof(rh)); |
||
303 | if (err) { |
||
304 | return err; |
||
305 | } |
||
306 | /* Seek back to start of header */ |
||
307 | err = mtar_seek(tar, tar->last_header); |
||
308 | if (err) { |
||
309 | return err; |
||
310 | } |
||
311 | /* Load raw header into header struct and return */ |
||
312 | return raw_to_header(h, &rh); |
||
313 | } |
||
314 | |||
315 | |||
316 | int mtar_read_data(mtar_t *tar, void *ptr, unsigned size) { |
||
317 | int err; |
||
318 | /* If we have no remaining data then this is the first read, we get the size, |
||
319 | * set the remaining data and seek to the beginning of the data */ |
||
320 | if (tar->remaining_data == 0) { |
||
321 | mtar_header_t h; |
||
322 | /* Read header */ |
||
323 | err = mtar_read_header(tar, &h); |
||
324 | if (err) { |
||
325 | return err; |
||
326 | } |
||
327 | /* Seek past header and init remaining data */ |
||
328 | err = mtar_seek(tar, tar->pos + sizeof(mtar_raw_header_t)); |
||
329 | if (err) { |
||
330 | return err; |
||
331 | } |
||
332 | tar->remaining_data = h.size; |
||
333 | } |
||
334 | /* Read data */ |
||
335 | err = tread(tar, ptr, size); |
||
336 | if (err) { |
||
337 | return err; |
||
338 | } |
||
339 | tar->remaining_data -= size; |
||
340 | /* If there is no remaining data we've finished reading and seek back to the |
||
341 | * header */ |
||
342 | if (tar->remaining_data == 0) { |
||
343 | return mtar_seek(tar, tar->last_header); |
||
344 | } |
||
345 | return MTAR_ESUCCESS; |
||
346 | } |
||
347 | |||
348 | |||
349 | int mtar_write_header(mtar_t *tar, const mtar_header_t *h) { |
||
350 | mtar_raw_header_t rh; |
||
351 | /* Build raw header and write */ |
||
352 | header_to_raw(&rh, h); |
||
353 | tar->remaining_data = h->size; |
||
354 | return twrite(tar, &rh, sizeof(rh)); |
||
355 | } |
||
356 | |||
357 | int mtar_write_file_header(mtar_t *tar, const char *name, unsigned size) { |
||
358 | mtar_header_t h; |
||
359 | /* Build header */ |
||
360 | mtar_memset(&h, 0, sizeof(h)); |
||
361 | strcpy(h.name, name); |
||
362 | h.size = size; |
||
363 | h.type = MTAR_TREG; |
||
364 | h.mode = 0664; |
||
365 | /* Write header */ |
||
366 | return mtar_write_header(tar, &h); |
||
367 | } |
||
368 | |||
369 | |||
370 | int mtar_write_dir_header(mtar_t *tar, const char *name) { |
||
371 | mtar_header_t h; |
||
372 | /* Build header */ |
||
373 | mtar_memset(&h, 0, sizeof(h)); |
||
374 | strcpy(h.name, name); |
||
375 | h.type = MTAR_TDIR; |
||
376 | h.mode = 0775; |
||
377 | /* Write header */ |
||
378 | return mtar_write_header(tar, &h); |
||
379 | } |
||
380 | |||
381 | |||
382 | int mtar_write_data(mtar_t *tar, const void *data, unsigned size) { |
||
383 | int err; |
||
384 | /* Write data */ |
||
385 | err = twrite(tar, data, size); |
||
386 | if (err) { |
||
387 | return err; |
||
388 | } |
||
389 | tar->remaining_data -= size; |
||
390 | /* Write padding if we've written all the data for this file */ |
||
391 | if (tar->remaining_data == 0) { |
||
392 | return write_null_bytes(tar, round_up(tar->pos, 512) - tar->pos); |
||
393 | } |
||
394 | return MTAR_ESUCCESS; |
||
395 | } |
||
396 | |||
397 | |||
398 | int mtar_finalize(mtar_t *tar) { |
||
399 | /* Write two NULL records */ |
||
400 | return write_null_bytes(tar, sizeof(mtar_raw_header_t) * 2); |
||
401 | } |
||
402 | |||
403 | /* Load libc.obj */ |
||
404 | |||
405 | #ifdef MTAR_OBJ |
||
406 | |||
407 | #include |
||
408 | |||
409 | int mtar_init(){ |
||
9095 | turbocat | 410 | ksys_dll_t *libc = _ksys_dlopen("/sys/lib/libc.obj"); |
8791 | turbocat | 411 | if(!libc){ |
412 | _ksys_debug_puts("mtar.obj: libc.obj not loaded!"); |
||
413 | return 1; |
||
414 | } |
||
415 | |||
9095 | turbocat | 416 | fread = _ksys_dlsym(libc, "fread"); |
417 | fwrite = _ksys_dlsym(libc, "fwrite"); |
||
418 | fclose = _ksys_dlsym(libc, "fclose"); |
||
419 | fopen = _ksys_dlsym(libc, "fopen"); |
||
420 | fseek = _ksys_dlsym(libc, "fseek"); |
||
421 | ftell = _ksys_dlsym(libc, "ftell"); |
||
422 | sprintf= _ksys_dlsym(libc, "sprintf"); |
||
423 | sscanf = _ksys_dlsym(libc, "sscanf"); |
||
424 | strcmp = _ksys_dlsym(libc, "strcmp"); |
||
425 | strchr = _ksys_dlsym(libc, "strchr"); |
||
426 | strcpy = _ksys_dlsym(libc, "strcpy"); |
||
8791 | turbocat | 427 | return 0; |
428 | } |
||
429 | |||
430 | |||
9095 | turbocat | 431 | ksys_dll_t EXPORTS[] = { |
8791 | turbocat | 432 | {"mtar_init", mtar_init}, |
433 | {"mtar_open", mtar_open}, |
||
434 | {"mtar_close", mtar_close}, |
||
435 | {"mtar_seek", mtar_seek}, |
||
436 | {"mtar_rewind", mtar_rewind}, |
||
437 | {"mtar_next", mtar_next}, |
||
438 | {"mtar_find", mtar_find}, |
||
439 | {"mtar_read_header", mtar_read_header}, |
||
440 | {"mtar_read_data", mtar_read_data}, |
||
441 | {"mtar_write_header", mtar_write_header}, |
||
442 | {"mtar_write_file_header", mtar_write_file_header}, |
||
443 | {"mtar_write_dir_header", mtar_write_dir_header}, |
||
444 | {"mtar_write_data",mtar_write_data}, |
||
445 | {"mtar_finalize", mtar_finalize}, |
||
446 | {"mtar_strerror", mtar_strerror}, |
||
447 | NULL |
||
448 | }; |
||
449 | |||
450 | #endif>>> |