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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 30 - (hide 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 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     struct uar_file
42     {
43     enum uar_file_type type;
44 rakinar2 25 int __pad1;
45 rakinar2 23 char *name;
46     uint64_t namelen;
47 rakinar2 25 uint64_t offset;
48 rakinar2 23 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 rakinar2 25 int __pad2;
59 rakinar2 23 };
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 rakinar2 25 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 rakinar2 23 uar_open (const char *filename)
132     {
133     struct uar_archive *uar = NULL;
134     FILE *stream = NULL;
135     int cerrno;
136    
137     errno = 0;
138 rakinar2 25 uar = uar_create ();
139 rakinar2 23
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 rakinar2 25 file->name = malloc (file->namelen + 1);
212 rakinar2 23
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 rakinar2 25 file->name[file->namelen] = 0;
226 rakinar2 23 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 rakinar2 25 uar_file_destroy (struct uar_file *file)
252 rakinar2 23 {
253 rakinar2 25 if (file == NULL)
254 rakinar2 23 return;
255    
256 rakinar2 25 free (file->name);
257     free (file);
258 rakinar2 23 }
259    
260 rakinar2 25 void
261     uar_close (struct uar_archive *uar)
262 rakinar2 23 {
263     if (uar == NULL)
264 rakinar2 25 return;
265 rakinar2 23
266 rakinar2 25 free (uar->buffer);
267 rakinar2 23
268 rakinar2 25 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 rakinar2 23 }
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 rakinar2 25 int cerrno;
307 rakinar2 23 assert (namelen < PATH_MAX);
308    
309     file = malloc (sizeof (struct uar_file));
310    
311     if (file == NULL)
312     return NULL;
313    
314 rakinar2 25 bzero (file, sizeof (struct uar_file));
315    
316 rakinar2 23 file->type = UF_FILE;
317     file->mode = 0644;
318     file->name = malloc (namelen + 1);
319    
320     if (file->name == NULL)
321     {
322 rakinar2 25 cerrno = errno;
323 rakinar2 23 free (file);
324 rakinar2 25 errno = cerrno;
325 rakinar2 23 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 rakinar2 25 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 rakinar2 23 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 rakinar2 25 if (size != 0 && fread (uar->buffer + file->offset, size, 1, stream) != 1)
403 rakinar2 23 {
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 rakinar2 30 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 rakinar2 23 {
433     assert (uar != NULL && "uar is NULL");
434 rakinar2 25 assert (dname != NULL && "dname is NULL");
435 rakinar2 23 assert (path != NULL && "path is NULL");
436     assert (!uar->is_stream && "uar in non-stream mode is not supported yet");
437    
438 rakinar2 25 char *name = (char *) dname;
439     bool free_name = false;
440     int cerrno;
441     uint64_t namelen;
442 rakinar2 23
443 rakinar2 25 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 rakinar2 23 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 rakinar2 30 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 rakinar2 23 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 rakinar2 25 goto uar_add_dir_error;
518 rakinar2 23 }
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 rakinar2 25 goto uar_add_dir_error;
528 rakinar2 23 }
529    
530 rakinar2 30 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 rakinar2 23 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 rakinar2 30 = uar_add_dir (uar, fullname, fullpath, callback);
544 rakinar2 23
545     if (direntry == NULL)
546     {
547 rakinar2 25 goto uar_add_dir_error;
548 rakinar2 23 }
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 rakinar2 25 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 rakinar2 23 }
569    
570     dir_file->data.size = dir_size;
571    
572 rakinar2 25 uar_add_dir_end:
573     cerrno = errno;
574     closedir (dir);
575    
576     if (free_name)
577     free (name);
578    
579     errno = cerrno;
580 rakinar2 23 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 rakinar2 25 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 rakinar2 23 file->type == UF_DIR ? "/"
641     : file->type == UF_LINK ? "@"
642     : "");
643 rakinar2 25 printf (" offset: %lu\n", file->offset);
644 rakinar2 23 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 rakinar2 25 if (fwrite (uar->buffer, 1, uar->header.size, stream) != uar->header.size)
701 rakinar2 23 {
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 rakinar2 25 return file->name[0] == UAR_ROOT_DIR_NAME
715     ? file->namelen == 1 ? "/" : file->name + 1
716     : file->name;
717 rakinar2 23 }
718    
719 rakinar2 30 enum uar_file_type
720     uar_get_entry_type (const struct uar_file *file)
721     {
722     return file->type;
723     }
724    
725 rakinar2 23 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 rakinar2 25 char *name = file->name;
743    
744     if (name[0] == UAR_ROOT_DIR_NAME)
745     name += 2;
746    
747 rakinar2 23 switch (file->type)
748     {
749     case UF_FILE:
750     {
751 rakinar2 25 FILE *stream = fopen (name, "wb");
752 rakinar2 23
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 rakinar2 25 if (file->namelen == 1
774     && file->name[0] == UAR_ROOT_DIR_NAME)
775     continue;
776    
777     if (mkdir (name, file->mode) != 0)
778 rakinar2 23 {
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