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

Contents of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26