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

Annotation of /trunk/uar/uar.c

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26