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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (hide 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 rakinar2 23 #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 rakinar2 25 #include <strings.h>
16 rakinar2 23 #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 rakinar2 25 #define UAR_ROOT_DIR_NAME 0x01
29    
30 rakinar2 23 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 rakinar2 25 int __pad1;
52 rakinar2 23 char *name;
53     uint64_t namelen;
54 rakinar2 25 uint64_t offset;
55 rakinar2 23 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 rakinar2 25 int __pad2;
66 rakinar2 23 };
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 rakinar2 25 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 rakinar2 23 uar_open (const char *filename)
139     {
140     struct uar_archive *uar = NULL;
141     FILE *stream = NULL;
142     int cerrno;
143    
144     errno = 0;
145 rakinar2 25 uar = uar_create ();
146 rakinar2 23
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 rakinar2 25 file->name = malloc (file->namelen + 1);
219 rakinar2 23
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 rakinar2 25 file->name[file->namelen] = 0;
233 rakinar2 23 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 rakinar2 25 uar_file_destroy (struct uar_file *file)
259 rakinar2 23 {
260 rakinar2 25 if (file == NULL)
261 rakinar2 23 return;
262    
263 rakinar2 25 free (file->name);
264     free (file);
265 rakinar2 23 }
266    
267 rakinar2 25 void
268     uar_close (struct uar_archive *uar)
269 rakinar2 23 {
270     if (uar == NULL)
271 rakinar2 25 return;
272 rakinar2 23
273 rakinar2 25 free (uar->buffer);
274 rakinar2 23
275 rakinar2 25 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 rakinar2 23 }
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 rakinar2 25 int cerrno;
314 rakinar2 23 assert (namelen < PATH_MAX);
315    
316     file = malloc (sizeof (struct uar_file));
317    
318     if (file == NULL)
319     return NULL;
320    
321 rakinar2 25 bzero (file, sizeof (struct uar_file));
322    
323 rakinar2 23 file->type = UF_FILE;
324     file->mode = 0644;
325     file->name = malloc (namelen + 1);
326    
327     if (file->name == NULL)
328     {
329 rakinar2 25 cerrno = errno;
330 rakinar2 23 free (file);
331 rakinar2 25 errno = cerrno;
332 rakinar2 23 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 rakinar2 25 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 rakinar2 23 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 rakinar2 25 if (size != 0 && fread (uar->buffer + file->offset, size, 1, stream) != 1)
410 rakinar2 23 {
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 rakinar2 25 uar_add_dir (struct uar_archive *uar, const char *dname, const char *path)
437 rakinar2 23 {
438     assert (uar != NULL && "uar is NULL");
439 rakinar2 25 assert (dname != NULL && "dname is NULL");
440 rakinar2 23 assert (path != NULL && "path is NULL");
441     assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
442    
443 rakinar2 25 char *name = (char *) dname;
444     bool free_name = false;
445     int cerrno;
446     uint64_t namelen;
447 rakinar2 23
448 rakinar2 25 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 rakinar2 23 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 rakinar2 25 goto uar_add_dir_error;
514 rakinar2 23 }
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 rakinar2 25 goto uar_add_dir_error;
524 rakinar2 23 }
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 rakinar2 25 goto uar_add_dir_error;
537 rakinar2 23 }
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 rakinar2 25 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 rakinar2 23 }
558    
559     dir_file->type = UF_DIR;
560     dir_file->data.size = dir_size;
561    
562 rakinar2 25 uar_add_dir_end:
563     cerrno = errno;
564     closedir (dir);
565    
566     if (free_name)
567     free (name);
568    
569     errno = cerrno;
570 rakinar2 23 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 rakinar2 25 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 rakinar2 23 file->type == UF_DIR ? "/"
631     : file->type == UF_LINK ? "@"
632     : "");
633 rakinar2 25 printf (" offset: %lu\n", file->offset);
634 rakinar2 23 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 rakinar2 25 if (fwrite (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
691 rakinar2 23 {
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 rakinar2 25 return file->name[0] == UAR_ROOT_DIR_NAME
705     ? file->namelen == 1 ? "/" : file->name + 1
706     : file->name;
707 rakinar2 23 }
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 rakinar2 25 char *name = file->name;
727    
728     if (name[0] == UAR_ROOT_DIR_NAME)
729     name += 2;
730    
731 rakinar2 23 switch (file->type)
732     {
733     case UF_FILE:
734     {
735 rakinar2 25 FILE *stream = fopen (name, "wb");
736 rakinar2 23
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 rakinar2 25 if (file->namelen == 1
758     && file->name[0] == UAR_ROOT_DIR_NAME)
759     continue;
760    
761     if (mkdir (name, file->mode) != 0)
762 rakinar2 23 {
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