27 |
#endif |
#endif |
28 |
|
|
29 |
const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' }; |
const unsigned char UAR_MAGIC[] = { 0x99, 'U', 'A', 'R' }; |
30 |
|
const unsigned int UAR_MAX_SUPPORTED_VERSION = 0x01; |
31 |
|
|
32 |
struct uar_header |
struct uar_header |
33 |
{ |
{ |
52 |
{ |
{ |
53 |
char *loc; |
char *loc; |
54 |
uint64_t loclen; |
uint64_t loclen; |
55 |
} linkinfo; |
} link; |
56 |
} data; |
} data; |
57 |
mode_t mode; |
mode_t mode; |
58 |
time_t mtime; |
time_t mtime; |
69 |
bool is_stream; |
bool is_stream; |
70 |
uint8_t *buffer; /* Deprecated */ |
uint8_t *buffer; /* Deprecated */ |
71 |
FILE *stream; |
FILE *stream; |
72 |
|
uint64_t stream_size; |
73 |
int last_errno; |
int last_errno; |
74 |
char *err_file; |
char *err_file; |
75 |
uar_create_callback_t create_callback; |
uar_create_callback_t create_callback; |
101 |
return "success"; |
return "success"; |
102 |
case UAR_INVALID_MAGIC: |
case UAR_INVALID_MAGIC: |
103 |
return "invalid archive magic"; |
return "invalid archive magic"; |
104 |
|
case UAR_INVALID_ARCHIVE: |
105 |
|
return "invalid archive"; |
106 |
|
case UAR_UNSUPPORTED_VERSION: |
107 |
|
return "archive version is not supported"; |
108 |
case UAR_INVALID_FILE: |
case UAR_INVALID_FILE: |
109 |
return "invalid file"; |
return "invalid file"; |
110 |
case UAR_INVALID_PATH: |
case UAR_INVALID_PATH: |
167 |
uar->files = NULL; |
uar->files = NULL; |
168 |
uar->stream = NULL; |
uar->stream = NULL; |
169 |
uar->root = NULL; |
uar->root = NULL; |
170 |
|
uar->stream_size = 0; |
171 |
uar->last_errno = 0; |
uar->last_errno = 0; |
172 |
uar->err_file = NULL; |
uar->err_file = NULL; |
173 |
|
|
188 |
root->type = UF_DIR; |
root->type = UF_DIR; |
189 |
root->mode = S_IFDIR | 0755; |
root->mode = S_IFDIR | 0755; |
190 |
root->mtime = time (NULL); |
root->mtime = time (NULL); |
191 |
|
root->uid = getuid (); |
192 |
|
root->gid = getgid (); |
193 |
|
|
194 |
if (!uar_add_file_entry (uar, root)) |
if (!uar_add_file_entry (uar, root)) |
195 |
{ |
{ |
245 |
fclose (stream); |
fclose (stream); |
246 |
return false; |
return false; |
247 |
} |
} |
248 |
|
|
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 |
} |
} |
261 |
|
|
262 |
uar->stream = freopen (NULL, "rb", uar->stream); |
uar->stream = freopen (NULL, "rb", uar->stream); |
308 |
size -= buf_size; |
size -= buf_size; |
309 |
} |
} |
310 |
|
|
311 |
|
free (buf); |
312 |
fclose (stream); |
fclose (stream); |
313 |
return true; |
return true; |
314 |
} |
} |
315 |
|
|
316 |
struct uar_archive * |
struct uar_archive * |
317 |
uar_create_stream (void) |
uar_stream_create (void) |
318 |
{ |
{ |
319 |
struct uar_archive *uar = uar_create (); |
struct uar_archive *uar = uar_create (); |
320 |
int cerrno; |
int cerrno; |
337 |
cerrno = errno; |
cerrno = errno; |
338 |
uar_close (uar); |
uar_close (uar); |
339 |
errno = cerrno; |
errno = cerrno; |
340 |
|
uar = NULL; |
341 |
uar_create_stream_ret: |
uar_create_stream_ret: |
342 |
return uar; |
return uar; |
343 |
} |
} |
361 |
enum uar_error ecode = UAR_SUCCESS; |
enum uar_error ecode = UAR_SUCCESS; |
362 |
void *buffer = NULL; |
void *buffer = NULL; |
363 |
struct uar_file *file = NULL; |
struct uar_file *file = NULL; |
364 |
|
uint64_t uar_file_namelen = strlen (uar_filename); |
365 |
|
|
366 |
|
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 |
if (stinfo == NULL) |
if (stinfo == NULL) |
391 |
{ |
{ |
428 |
|
|
429 |
fseek (stream, 0, SEEK_SET); |
fseek (stream, 0, SEEK_SET); |
430 |
|
|
431 |
file = uar_file_create (uar_filename, 0, size, uar->header.size); |
file = uar_file_create (uar_filename, uar_file_namelen, size, |
432 |
|
uar->header.size); |
433 |
|
|
434 |
if (file == NULL) |
if (file == NULL) |
435 |
{ |
{ |
562 |
struct uar_file *entry_file |
struct uar_file *entry_file |
563 |
= uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL); |
= uar_stream_add_entry (uar, uar_fullpath, fs_fullpath, NULL); |
564 |
|
|
565 |
if (entry_file != NULL) |
if (entry_file != NULL && entry_file->type != UF_LINK) |
566 |
size += entry_file->data.size; |
size += entry_file->data.size; |
567 |
|
|
568 |
free (fs_fullpath); |
free (fs_fullpath); |
614 |
stinfo = &custom_stinfo; |
stinfo = &custom_stinfo; |
615 |
} |
} |
616 |
|
|
617 |
|
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 |
file = uar_file_create (uar_name, 0, 0, uar->header.size); |
file = uar_file_create (uar_name, 0, 0, uar->header.size); |
636 |
|
|
637 |
if (file == NULL) |
if (file == NULL) |
660 |
return NULL; |
return NULL; |
661 |
} |
} |
662 |
|
|
663 |
file->data.linkinfo.loclen = link_len; |
file->data.link.loclen = link_len; |
664 |
file->data.linkinfo.loc = malloc (link_len + 1); |
file->data.link.loc = malloc (link_len + 1); |
665 |
|
|
666 |
if (file->data.linkinfo.loc == NULL) |
if (file->data.link.loc == NULL) |
667 |
{ |
{ |
668 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name); |
uar_set_error (uar, UAR_OUT_OF_MEMORY, fs_name); |
669 |
uar_file_destroy (file); |
uar_file_destroy (file); |
670 |
return NULL; |
return NULL; |
671 |
} |
} |
672 |
|
|
673 |
memcpy (file->data.linkinfo.loc, link_buf, link_len); |
memcpy (file->data.link.loc, link_buf, link_len); |
674 |
file->data.linkinfo.loc[link_len] = 0; |
file->data.link.loc[link_len] = 0; |
675 |
|
|
676 |
if (!uar_add_file_entry (uar, file)) |
if (!uar_add_file_entry (uar, file)) |
677 |
{ |
{ |
719 |
} |
} |
720 |
} |
} |
721 |
else if (S_ISDIR (stinfo->st_mode)) |
else if (S_ISDIR (stinfo->st_mode)) |
722 |
{ |
file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo); |
723 |
file = uar_stream_add_dir (uar, uar_name, fs_name, stinfo); |
|
724 |
} |
else if (S_ISLNK (stinfo->st_mode)) |
725 |
|
file = uar_stream_add_link (uar, uar_name, fs_name, stinfo); |
726 |
else |
else |
727 |
{ |
{ |
728 |
file = uar_stream_add_link (uar, uar_name, fs_name, stinfo); |
uar_set_error (uar, UAR_INVALID_FILE, fs_name); |
729 |
|
return NULL; |
730 |
} |
} |
731 |
|
|
732 |
if (file != NULL) |
if (file != NULL) |
740 |
return file; |
return file; |
741 |
} |
} |
742 |
|
|
743 |
|
/* 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 |
|
struct uar_archive * |
788 |
|
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 * |
struct uar_archive * |
942 |
uar_open (const char *filename) |
uar_open (const char *filename) |
943 |
{ |
{ |
1065 |
return; |
return; |
1066 |
|
|
1067 |
if (file->type == UF_LINK) |
if (file->type == UF_LINK) |
1068 |
free (file->data.linkinfo.loc); |
free (file->data.link.loc); |
1069 |
|
|
1070 |
free (file->name); |
free (file->name); |
1071 |
free (file); |
free (file); |
1470 |
printf (" mode: %04o\n", file->mode); |
printf (" mode: %04o\n", file->mode); |
1471 |
|
|
1472 |
if (file->type == UF_LINK) |
if (file->type == UF_LINK) |
1473 |
printf (" points to: %s\n", file->data.linkinfo.loc); |
printf (" points to: %s\n", file->data.link.loc); |
1474 |
else |
else |
1475 |
printf (" size: %lu\n", file->data.size); |
printf (" size: %lu\n", file->data.size); |
1476 |
|
|
1659 |
uar_get_error_file (const struct uar_archive *uar) |
uar_get_error_file (const struct uar_archive *uar) |
1660 |
{ |
{ |
1661 |
return uar->err_file; |
return uar->err_file; |
1662 |
|
} |
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 |
} |
} |