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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26