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

Contents of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 30 - (show annotations)
Tue Aug 6 14:24:41 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 19972 byte(s)
fix(uar): io logging
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 <unistd.h>
19
20 #if defined(__linux__)
21 # include <linux/limits.h>
22 #elif defined(__APPLE__)
23 # include <sys/syslimits.h>
24 #else
25 # include <limits.h>
26 #endif
27
28 #define UAR_ROOT_DIR_NAME 0x01
29
30 const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' };
31
32 struct uar_header
33 {
34 uint8_t magic[4];
35 uint16_t version;
36 uint32_t flags;
37 uint64_t nfiles;
38 uint64_t size;
39 } __attribute__ ((packed));
40
41 struct uar_file
42 {
43 enum uar_file_type type;
44 int __pad1;
45 char *name;
46 uint64_t namelen;
47 uint64_t offset;
48 union
49 {
50 uint64_t size;
51 struct
52 {
53 char *loc;
54 uint64_t loclen;
55 } linkinfo;
56 } data;
57 mode_t mode;
58 int __pad2;
59 };
60
61 struct uar_archive
62 {
63 struct uar_header header;
64 struct uar_file **files;
65 enum uar_error ecode;
66 bool is_stream;
67 uint8_t *buffer;
68 };
69
70 static void *
71 uar_set_error (struct uar_archive *uar, enum uar_error ecode)
72 {
73 uar->ecode = ecode;
74 return NULL;
75 }
76
77 const char *
78 uar_strerror (const struct uar_archive *restrict uar)
79 {
80 switch (uar->ecode)
81 {
82 case UAR_SUCCESS:
83 return "success";
84 case UAR_INVALID_MAGIC:
85 return "invalid archive magic";
86 case UAR_INVALID_FILE:
87 return "invalid file";
88 case UAR_INVALID_PATH:
89 return "invalid path string";
90 case UAR_IO_ERROR:
91 return "archive I/O error";
92 case UAR_OUT_OF_MEMORY:
93 return "out of memory";
94 case UAR_INVALID_ARGUMENT:
95 return "invalid argument";
96 case UAR_INVALID_OPERATION:
97 return "invalid operation";
98 default:
99 return "unknown error";
100 }
101 }
102
103 bool
104 uar_has_error (const struct uar_archive *restrict uar)
105 {
106 return uar->ecode != UAR_SUCCESS;
107 }
108
109 struct uar_archive *
110 uar_create (void)
111 {
112 struct uar_archive *uar = malloc (sizeof (struct uar_archive));
113
114 if (uar == NULL)
115 return NULL;
116
117 uar->is_stream = false;
118 uar->buffer = NULL;
119 uar->ecode = UAR_SUCCESS;
120 uar->header.size = 0;
121 memcpy (uar->header.magic, UAR_MAGIC, 4);
122 uar->header.version = 1;
123 uar->header.flags = 0;
124 uar->header.nfiles = 0;
125 uar->files = NULL;
126
127 return uar;
128 }
129
130 struct uar_archive *
131 uar_open (const char *filename)
132 {
133 struct uar_archive *uar = NULL;
134 FILE *stream = NULL;
135 int cerrno;
136
137 errno = 0;
138 uar = uar_create ();
139
140 if (uar == NULL)
141 return NULL;
142
143 stream = fopen (filename, "rb");
144
145 if (stream == NULL)
146 {
147 uar_set_error (uar, UAR_IO_ERROR);
148 goto uar_open_ret;
149 }
150
151 fseek (stream, 0, SEEK_END);
152 size_t size = ftell (stream);
153 fseek (stream, 0, SEEK_SET);
154
155 if (fread (&uar->header, sizeof (struct uar_header), 1, stream) != 1)
156 {
157 uar_set_error (uar, UAR_IO_ERROR);
158 goto uar_open_ret;
159 }
160
161 if (memcmp (uar->header.magic, UAR_MAGIC, 4) != 0)
162 {
163 uar_set_error (uar, UAR_INVALID_MAGIC);
164 goto uar_open_ret;
165 }
166
167 uint64_t filearr_size = uar->header.nfiles * sizeof (struct uar_file);
168
169 if (filearr_size > size)
170 {
171 uar_set_error (uar, UAR_IO_ERROR);
172 goto uar_open_ret;
173 }
174
175 if (uar->header.size > size)
176 {
177 uar_set_error (uar, UAR_IO_ERROR);
178 goto uar_open_ret;
179 }
180
181 uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *));
182
183 if (uar->files == NULL)
184 {
185 uar_set_error (uar, UAR_OUT_OF_MEMORY);
186 goto uar_open_ret;
187 }
188
189 for (uint64_t i = 0; i < uar->header.nfiles; i++)
190 {
191 struct uar_file *file = malloc (sizeof (struct uar_file));
192
193 if (file == NULL)
194 {
195 uar_set_error (uar, UAR_OUT_OF_MEMORY);
196 goto uar_open_ret;
197 }
198
199 if (fread (file, sizeof (struct uar_file), 1, stream) != 1)
200 {
201 uar_set_error (uar, UAR_IO_ERROR);
202 goto uar_open_ret;
203 }
204
205 if (file->namelen > PATH_MAX)
206 {
207 uar_set_error (uar, UAR_INVALID_PATH);
208 goto uar_open_ret;
209 }
210
211 file->name = malloc (file->namelen + 1);
212
213 if (file->name == NULL)
214 {
215 uar_set_error (uar, UAR_OUT_OF_MEMORY);
216 goto uar_open_ret;
217 }
218
219 if (fread (file->name, 1, file->namelen, stream) != file->namelen)
220 {
221 uar_set_error (uar, UAR_IO_ERROR);
222 goto uar_open_ret;
223 }
224
225 file->name[file->namelen] = 0;
226 uar->files[i] = file;
227 }
228
229 uar->buffer = malloc (uar->header.size + 1);
230
231 if (uar->buffer == NULL)
232 {
233 uar_set_error (uar, UAR_OUT_OF_MEMORY);
234 goto uar_open_ret;
235 }
236
237 if (fread (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
238 {
239 uar_set_error (uar, UAR_IO_ERROR);
240 goto uar_open_ret;
241 }
242
243 uar_open_ret:
244 cerrno = errno;
245 fclose (stream);
246 errno = cerrno;
247 return uar;
248 }
249
250 void
251 uar_file_destroy (struct uar_file *file)
252 {
253 if (file == NULL)
254 return;
255
256 free (file->name);
257 free (file);
258 }
259
260 void
261 uar_close (struct uar_archive *uar)
262 {
263 if (uar == NULL)
264 return;
265
266 free (uar->buffer);
267
268 for (uint64_t i = 0; i < uar->header.nfiles; i++)
269 {
270 struct uar_file *file = uar->files[i];
271 uar_file_destroy (file);
272 }
273
274 free (uar->files);
275 free (uar);
276 }
277
278 bool
279 uar_add_file_entry (struct uar_archive *restrict uar, struct uar_file *file)
280 {
281 if (uar == NULL || file == NULL)
282 {
283 uar_set_error (uar, UAR_INVALID_ARGUMENT);
284 return false;
285 }
286
287 uar->files = realloc (uar->files,
288 (uar->header.nfiles + 1) * sizeof (struct uar_file));
289
290 if (uar->files == NULL)
291 {
292 uar_set_error (uar, UAR_OUT_OF_MEMORY);
293 return false;
294 }
295
296 uar->files[uar->header.nfiles] = file;
297 uar->header.nfiles++;
298 return true;
299 }
300
301 struct uar_file *
302 uar_file_create (const char *name, uint64_t namelen, uint64_t size,
303 uint32_t offset)
304 {
305 struct uar_file *file;
306 int cerrno;
307 assert (namelen < PATH_MAX);
308
309 file = malloc (sizeof (struct uar_file));
310
311 if (file == NULL)
312 return NULL;
313
314 bzero (file, sizeof (struct uar_file));
315
316 file->type = UF_FILE;
317 file->mode = 0644;
318 file->name = malloc (namelen + 1);
319
320 if (file->name == NULL)
321 {
322 cerrno = errno;
323 free (file);
324 errno = cerrno;
325 return NULL;
326 }
327
328 file->name[namelen] = 0;
329 file->namelen = namelen;
330 file->data.size = size;
331 file->offset = offset;
332
333 strncpy (file->name, name, namelen);
334 return file;
335 }
336
337 struct uar_file *
338 uar_add_file (struct uar_archive *restrict uar, const char *name,
339 const char *path)
340 {
341 assert (uar != NULL && "uar is NULL");
342 assert (name != NULL && "name is NULL");
343 assert (path != NULL && "path is NULL");
344 assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
345
346 uint64_t namelen = strlen (name);
347
348 if (namelen >= PATH_MAX)
349 {
350 uar_set_error (uar, UAR_INVALID_PATH);
351 return NULL;
352 }
353
354 FILE *stream = fopen (path, "rb");
355
356 if (stream == NULL)
357 {
358 uar_set_error (uar, UAR_IO_ERROR);
359 return NULL;
360 }
361
362 fseek (stream, 0, SEEK_END);
363 long size = ftell (stream);
364
365 if (size < 0)
366 {
367 uar_set_error (uar, UAR_IO_ERROR);
368 fclose (stream);
369 return NULL;
370 }
371
372 fseek (stream, 0, SEEK_SET);
373
374 struct uar_file *file
375 = uar_file_create (name, namelen, size, uar->header.size);
376
377 if (file == NULL)
378 {
379 uar_set_error (uar, UAR_OUT_OF_MEMORY);
380 fclose (stream);
381 return NULL;
382 }
383
384 uar->header.size += size;
385
386 if (!uar_add_file_entry (uar, file))
387 {
388 uar_file_destroy (file);
389 fclose (stream);
390 return NULL;
391 }
392
393 uar->buffer = realloc (uar->buffer, uar->header.size);
394
395 if (uar->buffer == NULL)
396 {
397 uar_set_error (uar, UAR_OUT_OF_MEMORY);
398 fclose (stream);
399 return NULL;
400 }
401
402 if (size != 0 && fread (uar->buffer + file->offset, size, 1, stream) != 1)
403 {
404 uar_set_error (uar, UAR_IO_ERROR);
405 fclose (stream);
406 return NULL;
407 }
408
409 fclose (stream);
410 return file;
411 }
412
413 static char *
414 path_concat (const char *p1, const char *p2, size_t len1, size_t len2)
415 {
416 char *path = malloc (len1 + len2 + 2);
417
418 if (path == NULL)
419 return NULL;
420
421 strncpy (path, p1, len1);
422 path[len1] = '/';
423 strncpy (path + len1 + 1, p2, len2);
424 path[len1 + len2 + 1] = 0;
425 return path;
426 }
427
428 struct uar_file *
429 uar_add_dir (struct uar_archive *uar, const char *dname, const char *path,
430 bool (*callback) (struct uar_file *file, const char *fullname,
431 const char *fullpath))
432 {
433 assert (uar != NULL && "uar is NULL");
434 assert (dname != NULL && "dname is NULL");
435 assert (path != NULL && "path is NULL");
436 assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
437
438 char *name = (char *) dname;
439 bool free_name = false;
440 int cerrno;
441 uint64_t namelen;
442
443 if (strcmp (name, ".") == 0)
444 {
445 name = malloc (2);
446 name[0] = UAR_ROOT_DIR_NAME;
447 name[1] = 0;
448 free_name = true;
449 namelen = 1;
450 }
451 else
452 namelen = strlen (name);
453
454 if (namelen >= PATH_MAX)
455 {
456 uar_set_error (uar, UAR_INVALID_PATH);
457 return NULL;
458 }
459
460 DIR *dir = opendir (path);
461 struct dirent *entry = NULL;
462
463 if (dir == NULL)
464 {
465 uar_set_error (uar, UAR_INVALID_FILE);
466 return NULL;
467 }
468
469 struct uar_file *dir_file
470 = uar_file_create (name, namelen, 0, uar->header.size);
471 uint64_t dir_size = 0;
472
473 dir_file->type = UF_DIR;
474
475 if (callback != NULL && !callback (dir_file, name, path))
476 {
477 uar_set_error (uar, UAR_SUCCESS);
478 uar_file_destroy (dir_file);
479 return NULL;
480 }
481
482 if (!uar_add_file_entry (uar, dir_file))
483 {
484 uar_file_destroy (dir_file);
485 return NULL;
486 }
487
488 while ((entry = readdir (dir)) != NULL)
489 {
490 if (strcmp (entry->d_name, ".") == 0
491 || strcmp (entry->d_name, "..") == 0)
492 continue;
493
494 struct stat stinfo = { 0 };
495
496 if (256 + namelen >= PATH_MAX)
497 {
498 uar_set_error (uar, UAR_INVALID_PATH);
499 uar_file_destroy (dir_file);
500 closedir (dir);
501 return NULL;
502 }
503
504 uint64_t dnamelen = strlen (entry->d_name);
505
506 char *fullpath
507 = path_concat (path, entry->d_name, strlen (path), dnamelen);
508 assert (fullpath != NULL);
509
510 char *fullname
511 = path_concat (name, entry->d_name, namelen, dnamelen);
512 assert (fullname != NULL);
513
514 if (stat (fullpath, &stinfo) != 0)
515 {
516 uar_set_error (uar, UAR_IO_ERROR);
517 goto uar_add_dir_error;
518 }
519
520 if (S_ISREG (stinfo.st_mode))
521 {
522 struct uar_file *file
523 = uar_add_file (uar, fullname, fullpath);
524
525 if (file == NULL)
526 {
527 goto uar_add_dir_error;
528 }
529
530 if (callback != NULL
531 && !callback (file, fullname, fullpath))
532 {
533 uar_set_error (uar, UAR_SUCCESS);
534 goto uar_add_dir_error;
535 }
536
537 file->mode = stinfo.st_mode & 07777;
538 dir_size += file->data.size;
539 }
540 else if (S_ISDIR (stinfo.st_mode))
541 {
542 struct uar_file *direntry
543 = uar_add_dir (uar, fullname, fullpath, callback);
544
545 if (direntry == NULL)
546 {
547 goto uar_add_dir_error;
548 }
549
550 direntry->mode = stinfo.st_mode & 07777;
551 dir_size += direntry->data.size;
552 }
553 else
554 assert (false && "Not supported");
555
556 free (fullpath);
557 free (fullname);
558
559 continue;
560
561 uar_add_dir_error:
562 cerrno = errno;
563 uar_file_destroy (dir_file);
564 free (fullpath);
565 free (fullname);
566 errno = cerrno;
567 goto uar_add_dir_end;
568 }
569
570 dir_file->data.size = dir_size;
571
572 uar_add_dir_end:
573 cerrno = errno;
574 closedir (dir);
575
576 if (free_name)
577 free (name);
578
579 errno = cerrno;
580 return dir_file;
581 }
582
583 void
584 uar_file_set_mode (struct uar_file *file, mode_t mode)
585 {
586 file->mode = mode;
587 }
588
589 static void
590 uar_debug_print_file (const struct uar_archive *uar, struct uar_file *file,
591 bool print_contents)
592 {
593 printf (" size: %lu\n", file->data.size);
594
595 if (print_contents)
596 {
597 printf (" contents:\n");
598 printf ("==================\n");
599 fflush (stdout);
600
601 ssize_t size = write (STDOUT_FILENO, uar->buffer + file->offset,
602 file->data.size);
603
604 if (size == -1 || ((uint64_t) size) != file->data.size)
605 {
606 perror ("write");
607 return;
608 }
609
610 putchar ('\n');
611 printf ("==================\n");
612 }
613 }
614
615 void
616 uar_debug_print (const struct uar_archive *uar, bool print_file_contents)
617 {
618 printf ("uar_archive:\n");
619 printf (" magic: %02x %02x %02x %02x\n", uar->header.magic[0],
620 uar->header.magic[1], uar->header.magic[2], uar->header.magic[3]);
621 printf (" version: %u\n", uar->header.version);
622 printf (" flags: %u\n", uar->header.flags);
623 printf (" nfiles: %lu\n", uar->header.nfiles);
624 printf (" size: %lu\n", uar->header.size);
625 printf (" stream?: %i\n", uar->is_stream);
626
627 for (uint64_t i = 0; i < uar->header.nfiles; i++)
628 {
629 struct uar_file *file = uar->files[i];
630
631 printf (" %s[%lu]:\n",
632 file->type == UF_FILE ? "file"
633 : file->type == UF_DIR ? "directory"
634 : "link",
635 i);
636 printf (" name: \033[1m%s%s%s\033[0m\n",
637 file->name[0] == UAR_ROOT_DIR_NAME ? "/" : "",
638 file->name[0] == UAR_ROOT_DIR_NAME ? file->name + 2
639 : file->name,
640 file->type == UF_DIR ? "/"
641 : file->type == UF_LINK ? "@"
642 : "");
643 printf (" offset: %lu\n", file->offset);
644 printf (" mode: %04o\n", file->mode);
645
646 switch (file->type)
647 {
648 case UF_FILE:
649 uar_debug_print_file (uar, file, print_file_contents);
650 break;
651
652 case UF_DIR:
653 printf (" size: %lu\n", file->data.size);
654 break;
655
656 default:
657 printf (" info: unknown file type\n");
658 break;
659 }
660 }
661 }
662
663 bool
664 uar_write (struct uar_archive *uar, const char *filename)
665 {
666 FILE *stream = fopen (filename, "wb");
667
668 if (stream == NULL)
669 {
670 uar_set_error (uar, UAR_IO_ERROR);
671 return false;
672 }
673
674 if (fwrite (&uar->header, sizeof (struct uar_header), 1, stream) != 1)
675 {
676 uar_set_error (uar, UAR_IO_ERROR);
677 fclose (stream);
678 return false;
679 }
680
681 for (uint64_t i = 0; i < uar->header.nfiles; i++)
682 {
683 struct uar_file *file = uar->files[i];
684
685 if (fwrite (file, sizeof (struct uar_file), 1, stream) != 1)
686 {
687 uar_set_error (uar, UAR_IO_ERROR);
688 fclose (stream);
689 return false;
690 }
691
692 if (fwrite (file->name, 1, file->namelen, stream) != file->namelen)
693 {
694 uar_set_error (uar, UAR_IO_ERROR);
695 fclose (stream);
696 return false;
697 }
698 }
699
700 if (fwrite (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
701 {
702 uar_set_error (uar, UAR_IO_ERROR);
703 fclose (stream);
704 return false;
705 }
706
707 fclose (stream);
708 return true;
709 }
710
711 const char *
712 uar_get_file_name (const struct uar_file *file)
713 {
714 return file->name[0] == UAR_ROOT_DIR_NAME
715 ? file->namelen == 1 ? "/" : file->name + 1
716 : file->name;
717 }
718
719 enum uar_file_type
720 uar_get_entry_type (const struct uar_file *file)
721 {
722 return file->type;
723 }
724
725 bool
726 uar_extract (struct uar_archive *uar, const char *cwd,
727 bool (*callback) (struct uar_file *file))
728 {
729 if (cwd != NULL && chdir (cwd) != 0)
730 {
731 uar_set_error (uar, UAR_SYSTEM_ERROR);
732 return false;
733 }
734
735 for (uint64_t i = 0; i < uar->header.nfiles; i++)
736 {
737 struct uar_file *file = uar->files[i];
738
739 if (callback != NULL && !callback (file))
740 return false;
741
742 char *name = file->name;
743
744 if (name[0] == UAR_ROOT_DIR_NAME)
745 name += 2;
746
747 switch (file->type)
748 {
749 case UF_FILE:
750 {
751 FILE *stream = fopen (name, "wb");
752
753 if (stream == NULL)
754 {
755 uar_set_error (uar, UAR_IO_ERROR);
756 return false;
757 }
758
759 if (fwrite (uar->buffer + file->offset, 1,
760 file->data.size, stream)
761 != file->data.size)
762 {
763 uar_set_error (uar, UAR_IO_ERROR);
764 return false;
765 }
766
767 fchmod (fileno (stream), file->mode);
768 fclose (stream);
769 }
770 break;
771
772 case UF_DIR:
773 if (file->namelen == 1
774 && file->name[0] == UAR_ROOT_DIR_NAME)
775 continue;
776
777 if (mkdir (name, file->mode) != 0)
778 {
779 uar_set_error (uar, UAR_SYSTEM_ERROR);
780 return false;
781 }
782
783 break;
784
785 default:
786 assert (false && "unknown file type");
787 return false;
788 }
789 }
790
791 return true;
792 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26