--- /dev/null
+#include "common.h"
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <zlib.h>
+#include <libtar.h>
+#include <string.h>
+
+// for the sake of simplicity this stays global
+static gzFile gz_tar = NULL;
+
+int gzip_open(const char *pathname, int oflags, ...)
+{
+ if (gz_tar != NULL) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ // We assume oflags == O_RDONLY, since that's what we actually use.
+ // Also we don't care about the mode since we are lazy.
+ if (oflags != O_RDONLY) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ gz_tar = gzopen(pathname, "r");
+ if (gz_tar == NULL) {
+ _CLOGE("gzopen() failed with errno: %d\n", errno);
+ return -1;
+ }
+
+ return 1;
+}
+
+int gzip_close(__attribute__((unused)) int useless_fd)
+{
+ if (gz_tar == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int ret = gzclose(gz_tar);
+ if (ret != Z_OK)
+ _CLOGE("gzclose() failed with errno: %d\n", errno);
+
+ return ret;
+}
+
+ssize_t gzip_read(__attribute__((unused)) int useless_fd, void *buf, size_t len)
+{
+ if (gz_tar == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int ret = gzread(gz_tar, buf, len);
+ if (ret <= 0) {
+ if (gzeof(gz_tar))
+ _CLOGE("gzread() failed - EOF reached\n");
+ else
+ _CLOGE("gzread() failed with error: %s\n", gzerror(gz_tar, NULL));
+ }
+
+ return ret;
+}
+
+ssize_t gzip_write(__attribute__((unused)) int useless_fd, __attribute__((unused)) const void *buf, __attribute__((unused)) size_t len)
+{
+ // Again, we do not use this.
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int str_ends_with(const char *str, const char *suffix)
+{
+ size_t str_length = strlen(str);
+ size_t suffix_length = strlen(suffix);
+
+ return ((str_length > suffix_length) && (memcmp(str + str_length - suffix_length, suffix, suffix_length + 1) == 0));
+}
+
+static int tar_get_tartype(const char *tar_path, tartype_t **type)
+{
+ static tartype_t gzip_type = {gzip_open, gzip_close, gzip_read, gzip_write};
+
+ if (str_ends_with(tar_path, ".tar")) {
+ *type = NULL;
+ }
+ else if (str_ends_with(tar_path, ".tar.gz")) {
+ *type = &gzip_type;
+ }
+ else if (str_ends_with(tar_path, ".tgz")) {
+ *type = &gzip_type;
+ }
+ else {
+ _CLOGE("Unknown/unsupported file extension for delta - %s\n", tar_path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tar_init_with_type(TAR **tar, const char *tar_path, tartype_t *type)
+{
+ if (tar_open(tar, tar_path, type, O_RDONLY, 0, 0) < 0) {
+ *tar = NULL;
+ _CLOGE("tar_open() fail!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int util_file_untar(const char *tar_path, const char *dest_path, const char *extract_file_name)
+{
+ TAR *tar;
+ tartype_t *type;
+ DIR *dir;
+
+ if (tar_get_tartype(tar_path, &type)) {
+ _CLOGE("tar_get_tartype() error!\n");
+ return -1;
+ }
+
+ dir = opendir(dest_path);
+ if (dir == NULL) {
+ _CLOGE("Directory %s could not be opened, errno: %d\n", dest_path, errno);
+ return -1;
+ }
+ closedir(dir);
+
+ if (tar_init_with_type(&tar, tar_path, type)) {
+ _CLOGE("tar_init_with_type() error!\n");
+ return -1;
+ }
+
+ int ret = 0;
+ char extracted_file_path[MAX_BUFFER_SIZE];
+ snprintf(extracted_file_path, MAX_BUFFER_SIZE, "%s/%s", dest_path, extract_file_name);
+
+ for (;;)
+ {
+ switch (th_read(tar))
+ {
+ case -1:
+ _CLOGE("th_read() fail!\n");
+ ret = -1;
+ goto cleanup;
+ case 1:
+ _CLOGE("EOF reached - incorrect delta!\n");
+ ret = -1;
+ goto cleanup;
+ case 0:
+ break;
+ }
+
+ char *current_pathname = th_get_pathname(tar);
+
+ if (strcmp(current_pathname, extract_file_name) == 0) {
+ if (tar_extract_file(tar, extracted_file_path) < 0) {
+ _CLOGE("tar_extract_file() fail!\n");
+ ret = -1;
+ goto cleanup;
+ }
+ _CLOGI("%s successfully extracted from %s\n", extract_file_name, tar_path);
+ goto cleanup;
+ }
+
+ if (TH_ISREG(tar) && (tar_skip_regfile(tar) < 0)) {
+ _CLOGE("tar_skip_regfile() fail!\n");
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ tar_close(tar);
+ gz_tar = NULL;
+ return ret;
+}
\ No newline at end of file