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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26