diff options
-rw-r--r-- | firmware/common/zip.c | 178 | ||||
-rw-r--r-- | firmware/include/zip.h | 4 |
2 files changed, 182 insertions, 0 deletions
diff --git a/firmware/common/zip.c b/firmware/common/zip.c index c2e5da6651..84b94c4fb9 100644 --- a/firmware/common/zip.c +++ b/firmware/common/zip.c | |||
@@ -22,9 +22,12 @@ | |||
22 | #include "zip.h" | 22 | #include "zip.h" |
23 | #include <string.h> | 23 | #include <string.h> |
24 | #include "file.h" | 24 | #include "file.h" |
25 | #include "dir.h" | ||
25 | #include "system.h" | 26 | #include "system.h" |
27 | #include "errno.h" | ||
26 | #include "core_alloc.h" | 28 | #include "core_alloc.h" |
27 | #include "timefuncs.h" | 29 | #include "timefuncs.h" |
30 | #include "pathfuncs.h" | ||
28 | #include "crc32.h" | 31 | #include "crc32.h" |
29 | #include "rbendian.h" | 32 | #include "rbendian.h" |
30 | 33 | ||
@@ -157,6 +160,16 @@ struct zip_mem { | |||
157 | off_t mem_size; | 160 | off_t mem_size; |
158 | }; | 161 | }; |
159 | 162 | ||
163 | struct zip_extract { | ||
164 | zip_callback cb; | ||
165 | void* ctx; | ||
166 | size_t name_offset; | ||
167 | size_t name_size; | ||
168 | char* name; | ||
169 | int file; | ||
170 | char path[MAX_PATH]; | ||
171 | }; | ||
172 | |||
160 | static int zip_read_ed(struct zip* z) { | 173 | static int zip_read_ed(struct zip* z) { |
161 | const off_t file_size = z->size(z); | 174 | const off_t file_size = z->size(z); |
162 | const off_t max_size = sizeof(struct zip_ed_disk) + ZIP_MAX_LENGTH; | 175 | const off_t max_size = sizeof(struct zip_ed_disk) + ZIP_MAX_LENGTH; |
@@ -606,6 +619,100 @@ static void zip_mem_init(struct zip_mem* z, int zip_handle, int mem_handle, cons | |||
606 | z->mem_size = mem_size; | 619 | z->mem_size = mem_size; |
607 | } | 620 | } |
608 | 621 | ||
622 | static int zip_extract_start(const struct zip_args* args, struct zip_extract* ze) { | ||
623 | size_t name_length; | ||
624 | const char* dir; | ||
625 | size_t dir_length; | ||
626 | |||
627 | if ((name_length = strlcpy(ze->name, args->name, ze->name_size)) >= ze->name_size) | ||
628 | return 5; | ||
629 | |||
630 | if ((dir_length = path_dirname(ze->name, &dir)) > 0) { | ||
631 | char c = ze->name[dir_length]; | ||
632 | |||
633 | ze->name[dir_length] = '\0'; | ||
634 | |||
635 | if (!dir_exists(ze->path)) { | ||
636 | const char* path = ze->name; | ||
637 | const char* name; | ||
638 | |||
639 | while (parse_path_component(&path, &name) > 0) { | ||
640 | size_t offset = path - ze->name; | ||
641 | char c = ze->name[offset]; | ||
642 | |||
643 | ze->name[offset] = '\0'; | ||
644 | |||
645 | if (mkdir(ze->path) < 0 && errno != EEXIST) | ||
646 | return 6; | ||
647 | |||
648 | ze->name[offset] = c; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | ze->name[dir_length] = c; | ||
653 | } | ||
654 | |||
655 | if (ze->name[name_length - 1] == PATH_SEPCH) { | ||
656 | if (mkdir(ze->path) < 0 && errno != EEXIST) | ||
657 | return 7; | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | if ((ze->file = creat(ze->path, 0666)) < 0) | ||
663 | return 8; | ||
664 | |||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static int zip_extract_data(const struct zip_args* args, struct zip_extract* ze) { | ||
669 | if (write(ze->file, args->block, args->block_size) != (ssize_t) args->block_size) { | ||
670 | return 9; | ||
671 | } | ||
672 | |||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static int zip_extract_end(const struct zip_args* args, struct zip_extract* ze) { | ||
677 | int rv; | ||
678 | |||
679 | if (ze->file >= 0) { | ||
680 | rv = close(ze->file); | ||
681 | |||
682 | ze->file = -1; | ||
683 | |||
684 | if (rv < 0) | ||
685 | return 10; | ||
686 | } | ||
687 | |||
688 | if (modtime(ze->path, args->mtime) < 0) | ||
689 | return 11; | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int zip_extract_callback(const struct zip_args* args, int pass, void* ctx) { | ||
695 | struct zip_extract* ze = ctx; | ||
696 | int rv; | ||
697 | |||
698 | if (ze->cb != NULL && (rv = ze->cb(args, pass, ze->ctx)) != 0) | ||
699 | return rv; | ||
700 | |||
701 | switch (pass) { | ||
702 | case ZIP_PASS_START: | ||
703 | return zip_extract_start(args, ze); | ||
704 | |||
705 | case ZIP_PASS_DATA: | ||
706 | return zip_extract_data(args, ze); | ||
707 | |||
708 | case ZIP_PASS_END: | ||
709 | return zip_extract_end(args, ze); | ||
710 | |||
711 | default: | ||
712 | return 1; | ||
713 | } | ||
714 | } | ||
715 | |||
609 | struct zip* zip_open(const char* name, bool try_mem) { | 716 | struct zip* zip_open(const char* name, bool try_mem) { |
610 | int file = -1; | 717 | int file = -1; |
611 | int mem_handle = -1; | 718 | int mem_handle = -1; |
@@ -692,6 +799,77 @@ read_entries: | |||
692 | return zip_read_entries(z); | 799 | return zip_read_entries(z); |
693 | } | 800 | } |
694 | 801 | ||
802 | int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx) { | ||
803 | int rv; | ||
804 | int ze_handle = -1; | ||
805 | struct zip_extract* ze; | ||
806 | char* path; | ||
807 | size_t size; | ||
808 | size_t length; | ||
809 | |||
810 | if (root == NULL || root[0] == '\0') | ||
811 | root = PATH_ROOTSTR; | ||
812 | |||
813 | if (root[0] != PATH_SEPCH) { | ||
814 | rv = -1; | ||
815 | goto bail; | ||
816 | } | ||
817 | |||
818 | if (!dir_exists(root)) { | ||
819 | rv = 1; | ||
820 | goto bail; | ||
821 | } | ||
822 | |||
823 | if ((ze_handle = zip_core_alloc(sizeof(struct zip_extract))) < 0) { | ||
824 | rv = 2; | ||
825 | goto bail; | ||
826 | } | ||
827 | |||
828 | ze = core_get_data(ze_handle); | ||
829 | ze->cb = cb; | ||
830 | ze->ctx = ctx; | ||
831 | ze->file = -1; | ||
832 | |||
833 | path = ze->path; | ||
834 | size = sizeof(ze->path); | ||
835 | length = strlcpy(path, root, size); | ||
836 | |||
837 | if (length >= size) { | ||
838 | rv = 3; | ||
839 | goto bail; | ||
840 | } | ||
841 | |||
842 | path += length; | ||
843 | size -= length; | ||
844 | |||
845 | if (path[-1] != PATH_SEPCH) { | ||
846 | length = strlcpy(path, PATH_SEPSTR, size); | ||
847 | |||
848 | if (length >= size) { | ||
849 | rv = 4; | ||
850 | goto bail; | ||
851 | } | ||
852 | |||
853 | path += length; | ||
854 | size -= length; | ||
855 | } | ||
856 | |||
857 | ze->name_offset = path - ze->path; | ||
858 | ze->name_size = size; | ||
859 | ze->name = path; | ||
860 | |||
861 | rv = zip_read_deep(z, zip_extract_callback, ze); | ||
862 | |||
863 | bail: | ||
864 | if (ze_handle >= 0) { | ||
865 | if (ze->file >= 0) | ||
866 | close(ze->file); | ||
867 | |||
868 | core_free(ze_handle); | ||
869 | } | ||
870 | return rv; | ||
871 | } | ||
872 | |||
695 | void zip_close(struct zip* z) { | 873 | void zip_close(struct zip* z) { |
696 | if (z == NULL) | 874 | if (z == NULL) |
697 | return; | 875 | return; |
diff --git a/firmware/include/zip.h b/firmware/include/zip.h index f6870a53d2..70d225cfd7 100644 --- a/firmware/include/zip.h +++ b/firmware/include/zip.h | |||
@@ -58,6 +58,10 @@ int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx); | |||
58 | // this can also pickup where a successful shallow read leftoff | 58 | // this can also pickup where a successful shallow read leftoff |
59 | int zip_read_deep(struct zip* z, zip_callback cb, void* ctx); | 59 | int zip_read_deep(struct zip* z, zip_callback cb, void* ctx); |
60 | 60 | ||
61 | // extract the contents to an existing directory | ||
62 | // this can also pickup where a successful shallow read leftoff | ||
63 | int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx); | ||
64 | |||
61 | // returns system resources | 65 | // returns system resources |
62 | void zip_close(struct zip* z); | 66 | void zip_close(struct zip* z); |
63 | 67 | ||