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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 33 - (hide annotations)
Thu Aug 8 16:57:39 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 35823 byte(s)
fix(uar): better error messages during archive creation

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26