1 |
#define _XOPEN_SOURCE 500 |
2 |
|
3 |
#include "uar.h" |
4 |
#include "malloc.h" |
5 |
|
6 |
#include <assert.h> |
7 |
#include <dirent.h> |
8 |
#include <errno.h> |
9 |
#include <fcntl.h> |
10 |
#include <stdbool.h> |
11 |
#include <stdint.h> |
12 |
#include <stdio.h> |
13 |
#include <stdlib.h> |
14 |
#include <string.h> |
15 |
#include <strings.h> |
16 |
#include <sys/stat.h> |
17 |
#include <sys/types.h> |
18 |
#include <time.h> |
19 |
#include <unistd.h> |
20 |
#include <utime.h> |
21 |
|
22 |
#if defined(__linux__) |
23 |
# include <linux/limits.h> |
24 |
#elif defined(__APPLE__) |
25 |
# include <sys/syslimits.h> |
26 |
#else |
27 |
# include <limits.h> |
28 |
#endif |
29 |
|
30 |
const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' }; |
31 |
const unsigned int UAR_MAX_SUPPORTED_VERSION = 0x01; |
32 |
|
33 |
struct uar_header |
34 |
{ |
35 |
uint8_t magic[4]; |
36 |
uint16_t version; |
37 |
uint32_t flags; |
38 |
uint64_t nfiles; |
39 |
uint64_t size; |
40 |
} __attribute__ ((packed)); |
41 |
|
42 |
/* TODO: Fix alignment */ |
43 |
struct uar_file |
44 |
{ |
45 |
enum uar_file_type type; |
46 |
char *name; |
47 |
uint64_t namelen; |
48 |
uint64_t offset; |
49 |
union |
50 |
{ |
51 |
uint64_t size; |
52 |
struct |
53 |
{ |
54 |
char *loc; |
55 |
uint64_t loclen; |
56 |
} link; |
57 |
} data; |
58 |
mode_t mode; |
59 |
time_t mtime; |
60 |
uid_t uid; |
61 |
gid_t gid; |
62 |
}; |
63 |
|
64 |
struct uar_archive |
65 |
{ |
66 |
struct uar_header header; |
67 |
struct uar_file **files; |
68 |
struct uar_file *root; |
69 |
enum uar_error ecode; |
70 |
FILE *stream; |
71 |
uint64_t stream_size; |
72 |
int last_errno; |
73 |
char *err_file; |
74 |
uint64_t data_start; |
75 |
uar_create_callback_t create_callback; |
76 |
uar_extract_callback_t extract_callback; |
77 |
}; |
78 |
|
79 |
void |
80 |
uar_set_create_callback (struct uar_archive *uar, |
81 |
uar_create_callback_t callback) |
82 |
{ |
83 |
uar->create_callback = callback; |
84 |
} |
85 |
|
86 |
void |
87 |
uar_set_extract_callback (struct uar_archive *uar, |
88 |
uar_extract_callback_t callback) |
89 |
{ |
90 |
uar->extract_callback = callback; |
91 |
} |
92 |
|
93 |
static void |
94 |
uar_set_error (struct uar_archive *uar, enum uar_error ecode, |
95 |
const char *err_file) |
96 |
{ |
97 |
uar->ecode = ecode; |
98 |
uar->last_errno = errno; |
99 |
free (uar->err_file); |
100 |
uar->err_file = err_file == NULL ? NULL : strdup (err_file); |
101 |
} |
102 |
|
103 |
const char * |
104 |
uar_strerror (const struct uar_archive *restrict uar) |
105 |
{ |
106 |
switch (uar->ecode) |
107 |
{ |
108 |
case UAR_SUCCESS: |
109 |
return "success"; |
110 |
case UAR_INVALID_MAGIC: |
111 |
return "invalid archive magic"; |
112 |
case UAR_INVALID_ARCHIVE: |
113 |
return "invalid archive"; |
114 |
case UAR_UNSUPPORTED_VERSION: |
115 |
return "archive version is not supported"; |
116 |
case UAR_INVALID_FILE: |
117 |
return "invalid file"; |
118 |
case UAR_INVALID_PATH: |
119 |
return "invalid path string"; |
120 |
case UAR_IO_ERROR: |
121 |
return "archive I/O error"; |
122 |
case UAR_OUT_OF_MEMORY: |
123 |
return "out of memory"; |
124 |
case UAR_INVALID_ARGUMENT: |
125 |
return "invalid argument"; |
126 |
case UAR_INVALID_OPERATION: |
127 |
return "invalid operation"; |
128 |
case UAR_SYSTEM_ERROR: |
129 |
return "system error"; |
130 |
case UAR_SYSCALL_ERROR: |
131 |
return strerror (uar->last_errno); |
132 |
default: |
133 |
return "unknown error"; |
134 |
} |
135 |
} |
136 |
|
137 |
bool |
138 |
uar_has_error (const struct uar_archive *restrict uar) |
139 |
{ |
140 |
return uar->ecode != UAR_SUCCESS; |
141 |
} |
142 |
|
143 |
/* TODO: Use static storage for path */ |
144 |
static char * |
145 |
path_concat (const char *p1, const char *p2, size_t len1, size_t len2) |
146 |
{ |
147 |
char *path = malloc (len1 + len2 + 2); |
148 |
|
149 |
if (path == NULL) |
150 |
return NULL; |
151 |
|
152 |
strncpy (path, p1, len1); |
153 |
path[len1] = '/'; |
154 |
strncpy (path + len1 + 1, p2, len2); |
155 |
path[len1 + len2 + 1] = 0; |
156 |
return path; |
157 |
} |
158 |
|
159 |
struct uar_archive * |
160 |
uar_create (void) |
161 |
{ |
162 |
struct uar_archive *uar = malloc (sizeof (struct uar_archive)); |
163 |
|
164 |
if (uar == NULL) |
165 |
return NULL; |
166 |
|
167 |
uar->ecode = UAR_SUCCESS; |
168 |
uar->header.size = 0; |
169 |
memcpy (uar->header.magic, UAR_MAGIC, 4); |
170 |
uar->header.version = 1; |
171 |
uar->header.flags = 0; |
172 |
uar->header.nfiles = 0; |
173 |
uar->files = NULL; |
174 |
uar->stream = NULL; |
175 |
uar->root = NULL; |
176 |
uar->stream_size = 0; |
177 |
uar->last_errno = 0; |
178 |
uar->err_file = NULL; |
179 |
|
180 |
return uar; |
181 |
} |
182 |
|
183 |
static struct uar_file * |
184 |
uar_initialize_root (struct uar_archive *uar) |
185 |
{ |
186 |
struct uar_file *root = uar_file_create ("/", 1, 0, 0); |
187 |
|
188 |
if (root == NULL) |
189 |
{ |
190 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, "/"); |
191 |
return NULL; |
192 |
} |
193 |
|
194 |
root->type = UF_DIR; |
195 |
root->mode = S_IFDIR | 0755; |
196 |
root->mtime = time (NULL); |
197 |
root->uid = getuid (); |
198 |
root->gid = getgid (); |
199 |
|
200 |
if (!uar_add_file_entry (uar, root)) |
201 |
{ |
202 |
uar_file_destroy (root); |
203 |
return NULL; |
204 |
} |
205 |
|
206 |
uar->root = root; |
207 |
|
208 |
return root; |
209 |
} |
210 |
|
211 |
static bool |
212 |
uar_initialize (struct uar_archive *uar) |
213 |
{ |
214 |
if (uar_initialize_root (uar) == NULL) |
215 |
return false; |
216 |
|
217 |
return true; |
218 |
} |
219 |
|
220 |
bool |
221 |
uar_stream_write (struct uar_archive *uar, const char *filename) |
222 |
{ |
223 |
if (uar == NULL || uar->stream == NULL) |
224 |
return false; |
225 |
|
226 |
FILE *stream = fopen (filename, "wb"); |
227 |
|
228 |
if (fwrite (&uar->header, 1, sizeof (struct uar_header), stream) |
229 |
!= sizeof (struct uar_header)) |
230 |
{ |
231 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
232 |
fclose (stream); |
233 |
return false; |
234 |
} |
235 |
|
236 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
237 |
{ |
238 |
struct uar_file *file = uar->files[i]; |
239 |
|
240 |
if (fwrite (file, 1, sizeof (struct uar_file), stream) |
241 |
!= sizeof (struct uar_file)) |
242 |
{ |
243 |
uar_set_error (uar, UAR_SYSCALL_ERROR, file->name); |
244 |
fclose (stream); |
245 |
return false; |
246 |
} |
247 |
|
248 |
if (fwrite (file->name, 1, file->namelen, stream) != file->namelen) |
249 |
{ |
250 |
uar_set_error (uar, UAR_SYSCALL_ERROR, file->name); |
251 |
fclose (stream); |
252 |
return false; |
253 |
} |
254 |
|
255 |
if (file->type == UF_LINK) |
256 |
{ |
257 |
if (fwrite (file->data.link.loc, 1, file->data.link.loclen, |
258 |
stream) |
259 |
!= file->data.link.loclen) |
260 |
{ |
261 |
uar_set_error (uar, UAR_SYSCALL_ERROR, file->name); |
262 |
fclose (stream); |
263 |
return false; |
264 |
} |
265 |
} |
266 |
} |
267 |
|
268 |
uar->stream = freopen (NULL, "rb", uar->stream); |
269 |
|
270 |
if (uar->stream == NULL) |
271 |
{ |
272 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
273 |
fclose (stream); |
274 |
return false; |
275 |
} |
276 |
|
277 |
size_t buf_size |
278 |
= uar->header.size >= (1024 * 1024) ? 1024 * 1024 : uar->header.size; |
279 |
uint8_t *buf = malloc (buf_size); |
280 |
|
281 |
if (buf == NULL) |
282 |
{ |
283 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
284 |
fclose (stream); |
285 |
return false; |
286 |
} |
287 |
|
288 |
uint64_t size = uar->header.size; |
289 |
|
290 |
while (size > 0 && !feof (uar->stream)) |
291 |
{ |
292 |
if (size < buf_size) |
293 |
buf_size = size; |
294 |
|
295 |
if (fread (buf, 1, buf_size, uar->stream) != buf_size) |
296 |
{ |
297 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
298 |
fclose (stream); |
299 |
free (buf); |
300 |
return false; |
301 |
} |
302 |
|
303 |
if (fwrite (buf, 1, buf_size, stream) != buf_size) |
304 |
{ |
305 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
306 |
fclose (stream); |
307 |
free (buf); |
308 |
return false; |
309 |
} |
310 |
|
311 |
if (size < buf_size) |
312 |
buf_size = size; |
313 |
else |
314 |
size -= buf_size; |
315 |
} |
316 |
|
317 |
free (buf); |
318 |
fclose (stream); |
319 |
return true; |
320 |
} |
321 |
|
322 |
struct uar_archive * |
323 |
uar_stream_create (void) |
324 |
{ |
325 |
struct uar_archive *uar = uar_create (); |
326 |
int cerrno; |
327 |
|
328 |
if (uar == NULL) |
329 |
return NULL; |
330 |
|
331 |
uar->stream = tmpfile (); |
332 |
|
333 |
if (uar->stream == NULL) |
334 |
goto uar_create_stream_error; |
335 |
|
336 |
if (!uar_initialize (uar)) |
337 |
goto uar_create_stream_error; |
338 |
|
339 |
goto uar_create_stream_ret; |
340 |
|
341 |
uar_create_stream_error: |
342 |
cerrno = errno; |
343 |
uar_close (uar); |
344 |
errno = cerrno; |
345 |
uar = NULL; |
346 |
uar_create_stream_ret: |
347 |
return uar; |
348 |
} |
349 |
|
350 |
struct uar_file * |
351 |
uar_stream_add_file (struct uar_archive *uar, const char *uar_filename, |
352 |
const char *fs_filename, struct stat *stinfo) |
353 |
{ |
354 |
assert (uar != NULL && "uar is NULL"); |
355 |
assert (uar_filename != NULL && "uar_filename is NULL"); |
356 |
assert (fs_filename != NULL && "fs_filename is NULL"); |
357 |
|
358 |
if (uar->root == NULL) |
359 |
{ |
360 |
if (uar_initialize_root (uar) == NULL) |
361 |
return NULL; |
362 |
} |
363 |
|
364 |
struct stat custom_stinfo = { 0 }; |
365 |
enum uar_error ecode = UAR_SUCCESS; |
366 |
void *buffer = NULL; |
367 |
struct uar_file *file = NULL; |
368 |
uint64_t uar_file_namelen = strlen (uar_filename); |
369 |
|
370 |
if (uar_file_namelen > PATH_MAX) |
371 |
{ |
372 |
uar_set_error (uar, UAR_INVALID_PATH, fs_filename); |
373 |
return NULL; |
374 |
} |
375 |
|
376 |
bool contains_dot_dot |
377 |
= (uar_file_namelen > 3 && uar_filename[0] == '.' |
378 |
&& uar_filename[1] == '.' && uar_filename[2] == '/'); |
379 |
|
380 |
if ((uar_file_namelen > 2 && uar_filename[0] == '.' |
381 |
&& uar_filename[1] == '/') |
382 |
|| contains_dot_dot) |
383 |
{ |
384 |
if (uar->create_callback != NULL) |
385 |
uar->create_callback ( |
386 |
uar, NULL, uar_filename, fs_filename, UAR_ELEVEL_WARNING, |
387 |
contains_dot_dot ? "removing leading '..'" |
388 |
: "removing leading '.'"); |
389 |
|
390 |
uar_filename = uar_filename + 1 + (uar_file_namelen == 1); |
391 |
uar_file_namelen -= 1 + (uar_file_namelen == 1); |
392 |
} |
393 |
|
394 |
if (stinfo == NULL) |
395 |
{ |
396 |
if (lstat (fs_filename, &custom_stinfo) != 0) |
397 |
{ |
398 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_filename); |
399 |
|
400 |
if (uar->create_callback != NULL) |
401 |
uar->create_callback (uar, NULL, uar_filename, |
402 |
fs_filename, UAR_ELEVEL_WARNING, |
403 |
strerror (errno)); |
404 |
|
405 |
return NULL; |
406 |
} |
407 |
else |
408 |
stinfo = &custom_stinfo; |
409 |
} |
410 |
|
411 |
FILE *stream = fopen (fs_filename, "rb"); |
412 |
|
413 |
if (stream == NULL) |
414 |
{ |
415 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_filename); |
416 |
|
417 |
if (uar->create_callback != NULL) |
418 |
uar->create_callback (uar, NULL, uar_filename, fs_filename, |
419 |
UAR_ELEVEL_WARNING, strerror (errno)); |
420 |
|
421 |
return NULL; |
422 |
} |
423 |
|
424 |
fseek (stream, 0, SEEK_END); |
425 |
long size = ftell (stream); |
426 |
|
427 |
if (size < 0) |
428 |
{ |
429 |
ecode = UAR_SYSCALL_ERROR; |
430 |
goto uar_stream_add_file_end; |
431 |
} |
432 |
|
433 |
fseek (stream, 0, SEEK_SET); |
434 |
|
435 |
file = uar_file_create (uar_filename, uar_file_namelen, size, |
436 |
uar->header.size); |
437 |
|
438 |
if (file == NULL) |
439 |
{ |
440 |
ecode = UAR_SYSCALL_ERROR; |
441 |
goto uar_stream_add_file_end; |
442 |
} |
443 |
|
444 |
file->mode = stinfo->st_mode; |
445 |
file->data.size = size; |
446 |
file->mtime = stinfo->st_mtime; |
447 |
uar->header.size += size; |
448 |
uar->root->data.size += size; |
449 |
|
450 |
if (!uar_add_file_entry (uar, file)) |
451 |
{ |
452 |
uar_file_destroy (file); |
453 |
fclose (stream); |
454 |
return NULL; |
455 |
} |
456 |
|
457 |
buffer = malloc (size); |
458 |
|
459 |
if (buffer == NULL) |
460 |
{ |
461 |
ecode = UAR_OUT_OF_MEMORY; |
462 |
goto uar_stream_add_file_end; |
463 |
} |
464 |
|
465 |
if (size != 0 && fread (buffer, 1, size, stream) != (size_t) size) |
466 |
{ |
467 |
ecode = UAR_SYSCALL_ERROR; |
468 |
goto uar_stream_add_file_end; |
469 |
} |
470 |
|
471 |
if (fwrite (buffer, 1, size, uar->stream) != (size_t) size) |
472 |
{ |
473 |
ecode = UAR_SYSCALL_ERROR; |
474 |
goto uar_stream_add_file_end; |
475 |
} |
476 |
|
477 |
if (ecode == UAR_SUCCESS && uar->create_callback != NULL) |
478 |
{ |
479 |
uar->create_callback (uar, file, uar_filename, fs_filename, |
480 |
UAR_ELEVEL_NONE, NULL); |
481 |
} |
482 |
|
483 |
uar_stream_add_file_end: |
484 |
if (ecode != UAR_SUCCESS && file != NULL) |
485 |
uar_file_destroy (file); |
486 |
|
487 |
if (buffer != NULL) |
488 |
free (buffer); |
489 |
|
490 |
uar_set_error (uar, ecode, fs_filename); |
491 |
fclose (stream); |
492 |
return ecode == UAR_SUCCESS ? file : NULL; |
493 |
} |
494 |
|
495 |
struct uar_file * |
496 |
uar_stream_add_dir (struct uar_archive *uar, const char *uar_dirname, |
497 |
const char *fs_dirname, struct stat *stinfo) |
498 |
{ |
499 |
struct stat custom_stinfo = { 0 }; |
500 |
enum uar_error ecode = UAR_SUCCESS; |
501 |
struct uar_file *file = NULL; |
502 |
uint64_t size = 0; |
503 |
DIR *dir = NULL; |
504 |
|
505 |
if (stinfo == NULL) |
506 |
{ |
507 |
if (lstat (fs_dirname, &custom_stinfo) != 0) |
508 |
{ |
509 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_dirname); |
510 |
|
511 |
if (uar->create_callback != NULL) |
512 |
uar->create_callback (uar, NULL, uar_dirname, |
513 |
fs_dirname, UAR_ELEVEL_WARNING, |
514 |
strerror (errno)); |
515 |
|
516 |
return NULL; |
517 |
} |
518 |
else |
519 |
stinfo = &custom_stinfo; |
520 |
} |
521 |
|
522 |
file = uar_file_create (uar_dirname, 0, 0, uar->header.size); |
523 |
|
524 |
if (file == NULL) |
525 |
{ |
526 |
ecode = UAR_OUT_OF_MEMORY; |
527 |
goto uar_stream_add_dir_error; |
528 |
} |
529 |
|
530 |
file->type = UF_DIR; |
531 |
file->mode = stinfo->st_mode; |
532 |
file->mtime = stinfo->st_mtime; |
533 |
|
534 |
if (!uar_add_file_entry (uar, file)) |
535 |
{ |
536 |
ecode = UAR_OUT_OF_MEMORY; |
537 |
goto uar_stream_add_dir_error; |
538 |
} |
539 |
|
540 |
dir = opendir (fs_dirname); |
541 |
|
542 |
if (dir == NULL) |
543 |
{ |
544 |
if (uar->create_callback != NULL) |
545 |
uar->create_callback (uar, NULL, uar_dirname, fs_dirname, |
546 |
UAR_ELEVEL_WARNING, strerror (errno)); |
547 |
|
548 |
ecode = UAR_SYSCALL_ERROR; |
549 |
goto uar_stream_add_dir_error; |
550 |
} |
551 |
|
552 |
struct dirent *entry = NULL; |
553 |
|
554 |
while ((entry = readdir (dir)) != NULL) |
555 |
{ |
556 |
if (strcmp (entry->d_name, ".") == 0 |
557 |
|| strcmp (entry->d_name, "..") == 0) |
558 |
continue; |
559 |
|
560 |
size_t dname_len = strlen (entry->d_name); |
561 |
char *fs_fullpath = path_concat (fs_dirname, entry->d_name, |
562 |
strlen (fs_dirname), dname_len); |
563 |
char *uar_fullpath = path_concat (uar_dirname, entry->d_name, |
564 |
strlen (uar_dirname), dname_len); |
565 |
|
566 |
struct uar_file *entry_file |
567 |
= uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL); |
568 |
|
569 |
if (entry_file != NULL && entry_file->type != UF_LINK) |
570 |
size += entry_file->data.size; |
571 |
|
572 |
free (fs_fullpath); |
573 |
free (uar_fullpath); |
574 |
} |
575 |
|
576 |
file->data.size = size; |
577 |
|
578 |
if (ecode == UAR_SUCCESS && uar->create_callback != NULL) |
579 |
{ |
580 |
uar->create_callback (uar, file, uar_dirname, fs_dirname, |
581 |
UAR_ELEVEL_NONE, NULL); |
582 |
} |
583 |
|
584 |
goto uar_stream_add_dir_ret; |
585 |
uar_stream_add_dir_error: |
586 |
uar_set_error (uar, ecode, fs_dirname); |
587 |
uar_stream_add_dir_ret: |
588 |
if (dir != NULL) |
589 |
closedir (dir); |
590 |
|
591 |
if (ecode != UAR_SUCCESS && file != NULL) |
592 |
uar_file_destroy (file); |
593 |
|
594 |
return ecode == UAR_SUCCESS ? file : NULL; |
595 |
} |
596 |
|
597 |
struct uar_file * |
598 |
uar_stream_add_link (struct uar_archive *uar, const char *uar_name, |
599 |
const char *fs_name, struct stat *stinfo) |
600 |
{ |
601 |
struct stat custom_stinfo = { 0 }; |
602 |
struct uar_file *file = NULL; |
603 |
|
604 |
if (stinfo == NULL) |
605 |
{ |
606 |
if (lstat (fs_name, &custom_stinfo) != 0) |
607 |
{ |
608 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name); |
609 |
|
610 |
if (uar->create_callback != NULL) |
611 |
uar->create_callback (uar, NULL, uar_name, fs_name, |
612 |
UAR_ELEVEL_WARNING, |
613 |
strerror (errno)); |
614 |
|
615 |
return NULL; |
616 |
} |
617 |
else |
618 |
stinfo = &custom_stinfo; |
619 |
} |
620 |
|
621 |
uint64_t uar_file_namelen = strlen (uar_name); |
622 |
|
623 |
bool contains_dot_dot = (uar_file_namelen > 3 && uar_name[0] == '.' |
624 |
&& uar_name[1] == '.' && uar_name[2] == '/'); |
625 |
|
626 |
if ((uar_file_namelen > 2 && uar_name[0] == '.' && uar_name[1] == '/') |
627 |
|| contains_dot_dot) |
628 |
{ |
629 |
if (uar->create_callback != NULL) |
630 |
uar->create_callback ( |
631 |
uar, NULL, uar_name, fs_name, UAR_ELEVEL_WARNING, |
632 |
contains_dot_dot ? "removing leading '..'" |
633 |
: "removing leading '.'"); |
634 |
|
635 |
uar_name = uar_name + 1 + (uar_file_namelen == 1); |
636 |
uar_file_namelen -= 1 + (uar_file_namelen == 1); |
637 |
} |
638 |
|
639 |
file = uar_file_create (uar_name, 0, 0, uar->header.size); |
640 |
|
641 |
if (file == NULL) |
642 |
{ |
643 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name); |
644 |
return NULL; |
645 |
} |
646 |
|
647 |
file->type = UF_LINK; |
648 |
file->mode = stinfo->st_mode; |
649 |
file->mtime = stinfo->st_mtime; |
650 |
|
651 |
char link_buf[PATH_MAX] = { 0 }; |
652 |
|
653 |
ssize_t link_len = readlink (fs_name, link_buf, PATH_MAX); |
654 |
|
655 |
if (link_len == -1) |
656 |
{ |
657 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name); |
658 |
uar_file_destroy (file); |
659 |
|
660 |
if (uar->create_callback != NULL) |
661 |
uar->create_callback (uar, NULL, uar_name, fs_name, |
662 |
UAR_ELEVEL_WARNING, strerror (errno)); |
663 |
|
664 |
return NULL; |
665 |
} |
666 |
|
667 |
file->data.link.loclen = link_len; |
668 |
file->data.link.loc = malloc (link_len + 1); |
669 |
|
670 |
if (file->data.link.loc == NULL) |
671 |
{ |
672 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name); |
673 |
uar_file_destroy (file); |
674 |
return NULL; |
675 |
} |
676 |
|
677 |
memcpy (file->data.link.loc, link_buf, link_len); |
678 |
file->data.link.loc[link_len] = 0; |
679 |
|
680 |
if (!uar_add_file_entry (uar, file)) |
681 |
{ |
682 |
uar_file_destroy (file); |
683 |
return NULL; |
684 |
} |
685 |
|
686 |
if (uar->create_callback != NULL && file != NULL) |
687 |
uar->create_callback (uar, file, uar_name, fs_name, UAR_ELEVEL_NONE, |
688 |
NULL); |
689 |
|
690 |
return file; |
691 |
} |
692 |
|
693 |
struct uar_file * |
694 |
uar_stream_add_entry (struct uar_archive *uar, const char *uar_name, |
695 |
const char *fs_name, struct stat *stinfo) |
696 |
{ |
697 |
assert (uar != NULL && "uar is NULL"); |
698 |
assert (uar_name != NULL && "uar_name is NULL"); |
699 |
assert (fs_name != NULL && "fs_name is NULL"); |
700 |
|
701 |
struct stat custom_stinfo = { 0 }; |
702 |
struct uar_file *file = NULL; |
703 |
|
704 |
if (stinfo == NULL) |
705 |
{ |
706 |
if (lstat (fs_name, &custom_stinfo) != 0) |
707 |
{ |
708 |
uar_set_error (uar, UAR_SYSCALL_ERROR, fs_name); |
709 |
return NULL; |
710 |
} |
711 |
else |
712 |
stinfo = &custom_stinfo; |
713 |
} |
714 |
|
715 |
if (S_ISREG (stinfo->st_mode)) |
716 |
{ |
717 |
file = uar_stream_add_file (uar, uar_name, fs_name, stinfo); |
718 |
|
719 |
if (file == NULL) |
720 |
{ |
721 |
return NULL; |
722 |
} |
723 |
} |
724 |
else if (S_ISDIR (stinfo->st_mode)) |
725 |
file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo); |
726 |
|
727 |
else if (S_ISLNK (stinfo->st_mode)) |
728 |
file = uar_stream_add_link (uar, uar_name, fs_name, stinfo); |
729 |
else |
730 |
{ |
731 |
uar_set_error (uar, UAR_INVALID_FILE, fs_name); |
732 |
return NULL; |
733 |
} |
734 |
|
735 |
if (file != NULL) |
736 |
{ |
737 |
file->mode = stinfo->st_mode; |
738 |
file->mtime = stinfo->st_mtime; |
739 |
file->uid = stinfo->st_uid; |
740 |
file->gid = stinfo->st_gid; |
741 |
} |
742 |
|
743 |
return file; |
744 |
} |
745 |
|
746 |
/* Validate the UAR archive header. */ |
747 |
bool |
748 |
uar_stream_header_validate (struct uar_archive *uar) |
749 |
{ |
750 |
/* Compare magic to ensure it's a valid UAR archive. */ |
751 |
if (memcmp (uar->header.magic, UAR_MAGIC, sizeof (UAR_MAGIC)) != 0) |
752 |
{ |
753 |
uar_set_error (uar, UAR_INVALID_MAGIC, NULL); |
754 |
return false; |
755 |
} |
756 |
|
757 |
/* Check if the version is supported. */ |
758 |
if (uar->header.version > UAR_MAX_SUPPORTED_VERSION) |
759 |
{ |
760 |
uar_set_error (uar, UAR_UNSUPPORTED_VERSION, NULL); |
761 |
return false; |
762 |
} |
763 |
|
764 |
/* Check if the data block size is valid, to prevent buffer overflow. If |
765 |
it's larger than the stream size, it's invalid. This could be because |
766 |
the archive is corrupted, or it's a malicious archive. */ |
767 |
if (uar->header.size > (uar->stream_size - sizeof (struct uar_header))) |
768 |
{ |
769 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
770 |
return false; |
771 |
} |
772 |
|
773 |
/* At the moment, UAR doesn't support any flags. */ |
774 |
if (uar->header.flags != 0) |
775 |
{ |
776 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
777 |
return false; |
778 |
} |
779 |
|
780 |
/* Check if the file is big enough to hold n number of files. */ |
781 |
if (uar->header.nfiles * sizeof (struct uar_file) > uar->header.size) |
782 |
{ |
783 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
784 |
return false; |
785 |
} |
786 |
|
787 |
return true; |
788 |
} |
789 |
|
790 |
struct uar_archive * |
791 |
uar_stream_open (const char *filename) |
792 |
{ |
793 |
struct uar_archive *uar; |
794 |
FILE *stream = fopen (filename, "rb"); |
795 |
|
796 |
if (stream == NULL) |
797 |
return NULL; |
798 |
|
799 |
uar = uar_create (); |
800 |
|
801 |
if (uar == NULL) |
802 |
return NULL; |
803 |
|
804 |
uar->stream = stream; |
805 |
|
806 |
fseek (stream, 0, SEEK_END); |
807 |
long size = ftell (stream); |
808 |
|
809 |
if (size < 0 || size > INT64_MAX) |
810 |
{ |
811 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
812 |
return uar; |
813 |
} |
814 |
|
815 |
fseek (stream, 0, SEEK_SET); |
816 |
|
817 |
if (((size_t) size) < sizeof (struct uar_header)) |
818 |
{ |
819 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
820 |
return uar; |
821 |
} |
822 |
|
823 |
if (fread (&uar->header, 1, sizeof (struct uar_header), stream) |
824 |
!= sizeof (struct uar_header)) |
825 |
{ |
826 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
827 |
return uar; |
828 |
} |
829 |
|
830 |
uar->stream_size = size; |
831 |
|
832 |
if (!uar_stream_header_validate (uar)) |
833 |
return uar; |
834 |
|
835 |
uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *)); |
836 |
|
837 |
if (uar->files == NULL) |
838 |
{ |
839 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
840 |
return uar; |
841 |
} |
842 |
|
843 |
uint64_t file_block_size |
844 |
= sizeof (struct uar_header) |
845 |
+ (uar->header.nfiles * sizeof (struct uar_file)); |
846 |
uint64_t data_block_start = file_block_size; |
847 |
|
848 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
849 |
{ |
850 |
struct uar_file *file = malloc (sizeof (struct uar_file)); |
851 |
|
852 |
if (file == NULL) |
853 |
{ |
854 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
855 |
return uar; |
856 |
} |
857 |
|
858 |
if (fread (file, 1, sizeof (struct uar_file), stream) |
859 |
!= sizeof (struct uar_file)) |
860 |
{ |
861 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
862 |
free (file); |
863 |
return uar; |
864 |
} |
865 |
|
866 |
/* Right after the file structure, the name of the file is stored, |
867 |
with the length of the name stored in the namelen field. |
868 |
First, we need to check if the namelen is valid. |
869 |
*/ |
870 |
|
871 |
if (file->namelen > PATH_MAX || file->namelen == 0 |
872 |
|| file_block_size + file->namelen > ((uint64_t) size)) |
873 |
{ |
874 |
/* At a later stage, we might want to rather call a callback |
875 |
function instead. */ |
876 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
877 |
free (file); |
878 |
return uar; |
879 |
} |
880 |
|
881 |
data_block_start += file->namelen; |
882 |
file->name = malloc (file->namelen + 1); |
883 |
|
884 |
if (file->name == NULL) |
885 |
{ |
886 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
887 |
free (file); |
888 |
return uar; |
889 |
} |
890 |
|
891 |
if (fread (file->name, 1, file->namelen, stream) != file->namelen) |
892 |
{ |
893 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
894 |
free (file->name); |
895 |
free (file); |
896 |
return uar; |
897 |
} |
898 |
|
899 |
file->name[file->namelen] = 0; |
900 |
|
901 |
/* Next, we need to check if the file is a link. If it is, we need |
902 |
to read the link location. */ |
903 |
|
904 |
if (file->type == UF_LINK) |
905 |
{ |
906 |
if (file->data.link.loclen > PATH_MAX |
907 |
|| file_block_size + file->data.link.loclen |
908 |
> ((uint64_t) size)) |
909 |
{ |
910 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
911 |
free (file->name); |
912 |
free (file); |
913 |
return uar; |
914 |
} |
915 |
|
916 |
data_block_start += file->data.link.loclen; |
917 |
file->data.link.loc = malloc (file->data.link.loclen + 1); |
918 |
|
919 |
if (file->data.link.loc == NULL) |
920 |
{ |
921 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
922 |
free (file->name); |
923 |
free (file); |
924 |
return uar; |
925 |
} |
926 |
|
927 |
if (fread (file->data.link.loc, 1, file->data.link.loclen, |
928 |
stream) |
929 |
!= file->data.link.loclen) |
930 |
{ |
931 |
uar_set_error (uar, UAR_SYSCALL_ERROR, NULL); |
932 |
free (file->name); |
933 |
free (file->data.link.loc); |
934 |
free (file); |
935 |
return uar; |
936 |
} |
937 |
|
938 |
file->data.link.loc[file->data.link.loclen] = 0; |
939 |
} |
940 |
|
941 |
uar->files[i] = file; |
942 |
} |
943 |
|
944 |
uar->data_start = data_block_start; |
945 |
return uar; |
946 |
} |
947 |
|
948 |
static bool |
949 |
uar_stream_extract_file (struct uar_archive *uar, struct uar_file *file, |
950 |
const char *path) |
951 |
{ |
952 |
FILE *stream = fopen (path, "wb"); |
953 |
uint8_t buffer[1024]; |
954 |
uint64_t size = file->data.size; |
955 |
|
956 |
if (stream == NULL) |
957 |
{ |
958 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
959 |
perror ("fopen"); |
960 |
|
961 |
if (uar->extract_callback != NULL) |
962 |
uar->extract_callback (uar, file, file->name, path, |
963 |
UAR_ELEVEL_WARNING, strerror (errno)); |
964 |
|
965 |
return false; |
966 |
} |
967 |
|
968 |
if (fseek (uar->stream, uar->data_start + file->offset, SEEK_SET) != 0) |
969 |
{ |
970 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
971 |
|
972 |
if (uar->extract_callback != NULL) |
973 |
uar->extract_callback (uar, file, file->name, path, |
974 |
UAR_ELEVEL_WARNING, strerror (errno)); |
975 |
|
976 |
fclose (stream); |
977 |
return false; |
978 |
} |
979 |
|
980 |
while (size > 0) |
981 |
{ |
982 |
size_t read_size = size > sizeof (buffer) ? sizeof (buffer) : size; |
983 |
|
984 |
if (fread (buffer, 1, read_size, uar->stream) != read_size) |
985 |
{ |
986 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
987 |
|
988 |
if (uar->extract_callback != NULL) |
989 |
uar->extract_callback (uar, file, file->name, path, |
990 |
UAR_ELEVEL_WARNING, |
991 |
strerror (errno)); |
992 |
|
993 |
fclose (stream); |
994 |
return false; |
995 |
} |
996 |
|
997 |
if (fwrite (buffer, 1, read_size, stream) != read_size) |
998 |
{ |
999 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1000 |
|
1001 |
if (uar->extract_callback != NULL) |
1002 |
uar->extract_callback (uar, file, file->name, path, |
1003 |
UAR_ELEVEL_WARNING, |
1004 |
strerror (errno)); |
1005 |
|
1006 |
fclose (stream); |
1007 |
return false; |
1008 |
} |
1009 |
|
1010 |
size -= read_size; |
1011 |
} |
1012 |
|
1013 |
if (fchmod (fileno (stream), file->mode) != 0) |
1014 |
{ |
1015 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1016 |
|
1017 |
if (uar->extract_callback != NULL) |
1018 |
uar->extract_callback (uar, file, file->name, path, |
1019 |
UAR_ELEVEL_WARNING, strerror (errno)); |
1020 |
|
1021 |
fclose (stream); |
1022 |
return false; |
1023 |
} |
1024 |
|
1025 |
fclose (stream); |
1026 |
return true; |
1027 |
} |
1028 |
|
1029 |
bool |
1030 |
uar_stream_extract (struct uar_archive *uar, const char *dest) |
1031 |
{ |
1032 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1033 |
{ |
1034 |
struct uar_file *file = uar->files[i]; |
1035 |
char *name = file->name; |
1036 |
size_t diff = 0; |
1037 |
bool is_root = strcmp (file->name, "/") == 0; |
1038 |
|
1039 |
if (!is_root) |
1040 |
{ |
1041 |
if (name[0] == '/') |
1042 |
diff += 1; |
1043 |
|
1044 |
if (strncmp (name, "./", 2) == 0) |
1045 |
diff += 2; |
1046 |
else if (strncmp (name, "../", 3) == 0) |
1047 |
diff += 3; |
1048 |
else if (strcmp (name, "..") == 0) |
1049 |
diff += 2; |
1050 |
else if (strcmp (name, ".") == 0) |
1051 |
diff += 1; |
1052 |
} |
1053 |
|
1054 |
char *path |
1055 |
= is_root ? (char *) dest |
1056 |
: path_concat (dest, file->name + diff, strlen (dest), |
1057 |
file->namelen - diff); |
1058 |
|
1059 |
if (path == NULL) |
1060 |
{ |
1061 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, file->name); |
1062 |
return false; |
1063 |
} |
1064 |
|
1065 |
if (!is_root) |
1066 |
{ |
1067 |
switch (file->type) |
1068 |
{ |
1069 |
case UF_FILE: |
1070 |
if (!uar_stream_extract_file (uar, file, path)) |
1071 |
{ |
1072 |
free (path); |
1073 |
return false; |
1074 |
} |
1075 |
break; |
1076 |
case UF_DIR: |
1077 |
if (mkdir (path, file->mode) != 0) |
1078 |
{ |
1079 |
uar_set_error (uar, UAR_SYSCALL_ERROR, |
1080 |
path); |
1081 |
|
1082 |
if (uar->extract_callback != NULL) |
1083 |
uar->extract_callback ( |
1084 |
uar, file, file->name, path, |
1085 |
UAR_ELEVEL_WARNING, |
1086 |
strerror (errno)); |
1087 |
|
1088 |
free (path); |
1089 |
return false; |
1090 |
} |
1091 |
break; |
1092 |
case UF_LINK: |
1093 |
if (symlink (file->data.link.loc, path) != 0) |
1094 |
{ |
1095 |
uar_set_error (uar, UAR_SYSCALL_ERROR, |
1096 |
path); |
1097 |
|
1098 |
if (uar->extract_callback != NULL) |
1099 |
uar->extract_callback ( |
1100 |
uar, file, file->name, path, |
1101 |
UAR_ELEVEL_WARNING, |
1102 |
strerror (errno)); |
1103 |
|
1104 |
free (path); |
1105 |
return false; |
1106 |
} |
1107 |
break; |
1108 |
default: |
1109 |
uar_set_error (uar, UAR_INVALID_FILE, file->name); |
1110 |
|
1111 |
if (uar->extract_callback != NULL) |
1112 |
uar->extract_callback (uar, file, file->name, |
1113 |
path, UAR_ELEVEL_WARNING, |
1114 |
strerror (errno)); |
1115 |
|
1116 |
free (path); |
1117 |
return false; |
1118 |
} |
1119 |
} |
1120 |
|
1121 |
struct utimbuf times |
1122 |
= { .actime = time (NULL), .modtime = file->mtime }; |
1123 |
|
1124 |
if (utime (path, ×) != 0) |
1125 |
{ |
1126 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1127 |
|
1128 |
if (uar->extract_callback != NULL) |
1129 |
uar->extract_callback (uar, file, file->name, path, |
1130 |
UAR_ELEVEL_WARNING, |
1131 |
strerror (errno)); |
1132 |
|
1133 |
if (!is_root) |
1134 |
free (path); |
1135 |
|
1136 |
return false; |
1137 |
} |
1138 |
|
1139 |
if (chown (path, file->uid, file->gid) != 0) |
1140 |
{ |
1141 |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1142 |
|
1143 |
if (uar->extract_callback != NULL) |
1144 |
uar->extract_callback (uar, file, file->name, path, |
1145 |
UAR_ELEVEL_WARNING, |
1146 |
strerror (errno)); |
1147 |
|
1148 |
if (!is_root) |
1149 |
free (path); |
1150 |
|
1151 |
return false; |
1152 |
} |
1153 |
|
1154 |
if (uar->extract_callback != NULL) |
1155 |
uar->extract_callback (uar, file, file->name, path, |
1156 |
UAR_ELEVEL_NONE, NULL); |
1157 |
|
1158 |
if (!is_root) |
1159 |
free (path); |
1160 |
} |
1161 |
|
1162 |
return true; |
1163 |
} |
1164 |
|
1165 |
void |
1166 |
uar_file_destroy (struct uar_file *file) |
1167 |
{ |
1168 |
if (file == NULL) |
1169 |
return; |
1170 |
|
1171 |
if (file->type == UF_LINK) |
1172 |
free (file->data.link.loc); |
1173 |
|
1174 |
free (file->name); |
1175 |
free (file); |
1176 |
} |
1177 |
|
1178 |
void |
1179 |
uar_close (struct uar_archive *uar) |
1180 |
{ |
1181 |
if (uar == NULL) |
1182 |
return; |
1183 |
|
1184 |
fclose (uar->stream); |
1185 |
|
1186 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1187 |
{ |
1188 |
struct uar_file *file = uar->files[i]; |
1189 |
uar_file_destroy (file); |
1190 |
} |
1191 |
|
1192 |
free (uar->err_file); |
1193 |
free (uar->files); |
1194 |
free (uar); |
1195 |
} |
1196 |
|
1197 |
bool |
1198 |
uar_add_file_entry (struct uar_archive *restrict uar, struct uar_file *file) |
1199 |
{ |
1200 |
if (uar == NULL || file == NULL) |
1201 |
{ |
1202 |
uar_set_error (uar, UAR_INVALID_ARGUMENT, NULL); |
1203 |
return false; |
1204 |
} |
1205 |
|
1206 |
uar->files = realloc (uar->files, |
1207 |
(uar->header.nfiles + 1) * sizeof (struct uar_file)); |
1208 |
|
1209 |
if (uar->files == NULL) |
1210 |
{ |
1211 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
1212 |
return false; |
1213 |
} |
1214 |
|
1215 |
uar->files[uar->header.nfiles] = file; |
1216 |
uar->header.nfiles++; |
1217 |
|
1218 |
return true; |
1219 |
} |
1220 |
|
1221 |
struct uar_file * |
1222 |
uar_file_create (const char *name, uint64_t namelen, uint64_t size, |
1223 |
uint32_t offset) |
1224 |
{ |
1225 |
struct uar_file *file; |
1226 |
int cerrno; |
1227 |
bool abs = false; |
1228 |
|
1229 |
if (namelen == 0) |
1230 |
namelen = strlen (name); |
1231 |
|
1232 |
if (namelen >= PATH_MAX) |
1233 |
{ |
1234 |
errno = ENAMETOOLONG; |
1235 |
return NULL; |
1236 |
} |
1237 |
|
1238 |
abs = name[0] == '/'; |
1239 |
namelen += (abs ? 0 : 1); |
1240 |
|
1241 |
file = malloc (sizeof (struct uar_file)); |
1242 |
|
1243 |
if (file == NULL) |
1244 |
return NULL; |
1245 |
|
1246 |
bzero (file, sizeof (struct uar_file)); |
1247 |
|
1248 |
file->type = UF_FILE; |
1249 |
file->mode = 0644; |
1250 |
file->mtime = 0; |
1251 |
file->uid = 0; |
1252 |
file->gid = 0; |
1253 |
file->name = malloc (namelen + 1); |
1254 |
|
1255 |
if (file->name == NULL) |
1256 |
{ |
1257 |
cerrno = errno; |
1258 |
free (file); |
1259 |
errno = cerrno; |
1260 |
return NULL; |
1261 |
} |
1262 |
|
1263 |
if (!abs) |
1264 |
file->name[0] = '/'; |
1265 |
|
1266 |
strncpy (file->name + (abs ? 0 : 1), name, namelen); |
1267 |
file->name[namelen] = 0; |
1268 |
file->namelen = namelen; |
1269 |
file->data.size = size; |
1270 |
file->offset = offset; |
1271 |
|
1272 |
return file; |
1273 |
} |
1274 |
|
1275 |
void |
1276 |
uar_file_set_mode (struct uar_file *file, mode_t mode) |
1277 |
{ |
1278 |
file->mode = mode; |
1279 |
} |
1280 |
|
1281 |
static void |
1282 |
uar_debug_print_file_contents (const struct uar_archive *uar, |
1283 |
struct uar_file *file) |
1284 |
{ |
1285 |
printf (" contents:\n"); |
1286 |
printf ("==================\n"); |
1287 |
fflush (stdout); |
1288 |
|
1289 |
if (fseek (uar->stream, uar->data_start + file->offset, SEEK_SET) != 0) |
1290 |
{ |
1291 |
perror ("fseek"); |
1292 |
return; |
1293 |
} |
1294 |
|
1295 |
uint8_t buffer[1024]; |
1296 |
|
1297 |
while (file->data.size > 0) |
1298 |
{ |
1299 |
size_t size = file->data.size > sizeof (buffer) ? sizeof (buffer) |
1300 |
: file->data.size; |
1301 |
|
1302 |
if (fread (buffer, 1, size, uar->stream) != size) |
1303 |
{ |
1304 |
perror ("read"); |
1305 |
return; |
1306 |
} |
1307 |
|
1308 |
for (size_t i = 0; i < size; i++) |
1309 |
{ |
1310 |
if (buffer[i] == '\n') |
1311 |
putchar ('\n'); |
1312 |
else |
1313 |
putchar (buffer[i]); |
1314 |
} |
1315 |
|
1316 |
file->data.size -= size; |
1317 |
} |
1318 |
|
1319 |
putchar ('\n'); |
1320 |
printf ("==================\n"); |
1321 |
} |
1322 |
|
1323 |
void |
1324 |
uar_debug_print (const struct uar_archive *uar, bool print_file_contents) |
1325 |
{ |
1326 |
printf ("uar_archive:\n"); |
1327 |
printf (" magic: %02x %02x %02x %02x\n", uar->header.magic[0], |
1328 |
uar->header.magic[1], uar->header.magic[2], uar->header.magic[3]); |
1329 |
printf (" version: %u\n", uar->header.version); |
1330 |
printf (" flags: %u\n", uar->header.flags); |
1331 |
printf (" nfiles: %lu\n", uar->header.nfiles); |
1332 |
printf (" size: %lu\n", uar->header.size); |
1333 |
|
1334 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1335 |
{ |
1336 |
struct uar_file *file = uar->files[i]; |
1337 |
|
1338 |
printf (" %s[%lu]:\n", |
1339 |
file->type == UF_FILE ? "file" |
1340 |
: file->type == UF_DIR ? "directory" |
1341 |
: "link", |
1342 |
i); |
1343 |
printf (" name: \033[1m%s%s\033[0m\n", uar_file_get_name (file), |
1344 |
file->type == UF_DIR |
1345 |
? file->name[0] == '/' && file->namelen == 1 ? "" : "/" |
1346 |
: file->type == UF_LINK ? "@" |
1347 |
: ""); |
1348 |
printf (" offset: %lu\n", file->offset); |
1349 |
printf (" mode: %04o\n", file->mode); |
1350 |
|
1351 |
if (file->type == UF_LINK) |
1352 |
printf (" points to: %s\n", file->data.link.loc); |
1353 |
else |
1354 |
printf (" size: %lu\n", file->data.size); |
1355 |
|
1356 |
if (file->type == UF_FILE && print_file_contents) |
1357 |
uar_debug_print_file_contents (uar, file); |
1358 |
} |
1359 |
} |
1360 |
|
1361 |
bool |
1362 |
uar_stream_iterate (struct uar_archive *uar, |
1363 |
bool (*callback) (struct uar_file *file, void *data), |
1364 |
void *data) |
1365 |
{ |
1366 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1367 |
{ |
1368 |
struct uar_file *file = uar->files[i]; |
1369 |
|
1370 |
if (!callback (file, data)) |
1371 |
return false; |
1372 |
} |
1373 |
|
1374 |
return true; |
1375 |
} |
1376 |
|
1377 |
const char * |
1378 |
uar_file_get_name (const struct uar_file *file) |
1379 |
{ |
1380 |
return file->name; |
1381 |
} |
1382 |
|
1383 |
enum uar_file_type |
1384 |
uar_file_get_type (const struct uar_file *file) |
1385 |
{ |
1386 |
return file->type; |
1387 |
} |
1388 |
|
1389 |
mode_t |
1390 |
uar_file_get_mode (const struct uar_file *file) |
1391 |
{ |
1392 |
return file->mode; |
1393 |
} |
1394 |
|
1395 |
uint64_t |
1396 |
uar_file_get_size (const struct uar_file *file) |
1397 |
{ |
1398 |
if (file->type == UF_LINK) |
1399 |
return 0; |
1400 |
|
1401 |
return file->data.size; |
1402 |
} |
1403 |
|
1404 |
uint64_t |
1405 |
uar_file_get_namelen (const struct uar_file *file) |
1406 |
{ |
1407 |
return file->namelen; |
1408 |
} |
1409 |
|
1410 |
uint64_t |
1411 |
uar_get_file_count (const struct uar_archive *restrict uar) |
1412 |
{ |
1413 |
return uar->header.nfiles; |
1414 |
} |
1415 |
|
1416 |
time_t |
1417 |
uar_file_get_mtime (const struct uar_file *file) |
1418 |
{ |
1419 |
return file->mtime; |
1420 |
} |
1421 |
|
1422 |
const char * |
1423 |
uar_get_error_file (const struct uar_archive *uar) |
1424 |
{ |
1425 |
return uar->err_file; |
1426 |
} |
1427 |
|
1428 |
uid_t |
1429 |
uar_file_get_uid (const struct uar_file *file) |
1430 |
{ |
1431 |
return file->uid; |
1432 |
} |
1433 |
|
1434 |
gid_t |
1435 |
uar_file_get_gid (const struct uar_file *file) |
1436 |
{ |
1437 |
return file->gid; |
1438 |
} |