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

Contents of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 33 - (show annotations)
Thu Aug 8 16:57:39 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 35823 byte(s)
fix(uar): better error messages during archive creation

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26