/[osn-commons]/trunk/uar/uar.c
ViewVC logotype

Contents of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 40 - (show annotations)
Sat Aug 10 14:44:20 2024 UTC (7 months, 2 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 40281 byte(s)
feat(uar): finalize extraction/creation/listing of archives
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, &times) != 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 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26