diff options
Diffstat (limited to 'futility/updater_archive.c')
-rw-r--r-- | futility/updater_archive.c | 142 |
1 files changed, 126 insertions, 16 deletions
diff --git a/futility/updater_archive.c b/futility/updater_archive.c index 7ce210f3..eaf745cc 100644 --- a/futility/updater_archive.c +++ b/futility/updater_archive.c @@ -82,6 +82,8 @@ struct archive { int (*has_entry)(void *handle, const char *name); int (*read_file)(void *handle, const char *fname, uint8_t **data, uint32_t *size); + int (*write_file)(void *handle, const char *fname, + uint8_t *data, uint32_t size); }; /* @@ -125,6 +127,8 @@ static int archive_fallback_walk( while ((ent = fts_read(fts_handle)) != NULL) { char *path = ent->fts_path + root_len; + if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL) + continue; while (*path == '/') path++; if (!*path) @@ -135,17 +139,23 @@ static int archive_fallback_walk( return 0; } +/* Callback for fallback drivers to get full path easily. */ +static const char *archive_fallback_get_path(void *handle, const char *fname, + char **temp_path) +{ + if (handle && *fname != '/') { + ASPRINTF(temp_path, "%s/%s", (char *)handle, fname); + return *temp_path; + } + return fname; +} + /* Callback for archive_has_entry on a general file system. */ static int archive_fallback_has_entry(void *handle, const char *fname) { int r; - const char *path = fname; char *temp_path = NULL; - - if (handle && *fname != '/') { - ASPRINTF(&temp_path, "%s/%s", (char *)handle, fname); - path = temp_path; - } + const char *path = archive_fallback_get_path(handle, fname, &temp_path); DEBUG("Checking %s", path); r = access(path, R_OK); @@ -158,21 +168,43 @@ static int archive_fallback_read_file(void *handle, const char *fname, uint8_t **data, uint32_t *size) { int r; - const char *path = fname; char *temp_path = NULL; + const char *path = archive_fallback_get_path(handle, fname, &temp_path); + DEBUG("Reading %s", path); *data = NULL; *size = 0; - if (handle && *fname != '/') { - ASPRINTF(&temp_path, "%s/%s", (char *)handle, fname); - path = temp_path; - } - DEBUG("Reading %s", path); r = vb2_read_file(path, data, size) != VB2_SUCCESS; free(temp_path); return r; } +/* Callback for archive_write_file on a general file system. */ +static int archive_fallback_write_file(void *handle, const char *fname, + uint8_t *data, uint32_t size) +{ + int r; + char *temp_path = NULL; + const char *path = archive_fallback_get_path(handle, fname, &temp_path); + + DEBUG("Writing %s", path); + if (strchr(path, '/')) { + char *dirname = strdup(path); + *strrchr(dirname, '/') = '\0'; + /* TODO(hungte): call mkdir(2) instead of shell invocation. */ + if (access(dirname, W_OK) != 0) { + char *command; + ASPRINTF(&command, "mkdir -p %s", dirname); + free(host_shell(command)); + free(command); + } + free(dirname); + } + r = vb2_write_file(path, data, size) != VB2_SUCCESS; + free(temp_path); + return r; +} + #ifdef HAVE_LIBZIP /* Callback for archive_open on a ZIP file. */ @@ -212,7 +244,10 @@ static int archive_zip_walk( if (num < 0) return 1; for (i = 0; i < num; i++) { - if (callback(zip_get_name(zip, i, 0), arg)) + const char *name = zip_get_name(zip, i, 0); + if (*name && name[strlen(name) - 1] == '/') + continue; + if (callback(name, arg)) break; } return 0; @@ -252,6 +287,33 @@ static int archive_zip_read_file(void *handle, const char *fname, zip_fclose(fp); return *data == NULL; } + +/* Callback for archive_zip_write_file on a ZIP file. */ +static int archive_zip_write_file(void *handle, const char *fname, + uint8_t *data, uint32_t size) +{ + struct zip *zip = (struct zip *)handle; + struct zip_source *src; + + DEBUG("Writing %s", fname); + assert(zip); + src = zip_source_buffer(zip, data, size, 0); + if (!src) { + ERROR("Internal error: cannot allocate buffer: %s", fname); + return 1; + } + + if (zip_file_add(zip, fname, src, ZIP_FL_OVERWRITE) < 0) { + zip_source_free(src); + ERROR("Internal error: failed to add: %s", fname); + return 1; + } + /* zip_source_free is not needed if zip_file_add success. */ +#if LIBZIP_VERSION_MAJOR >= 1 + zip_file_set_mtime(zip, zip_name_locate(zip, fname, 0), 0, 0); +#endif + return 0; +} #endif /* @@ -284,6 +346,7 @@ struct archive *archive_open(const char *path) ar->walk = archive_fallback_walk; ar->has_entry = archive_fallback_has_entry; ar->read_file = archive_fallback_read_file; + ar->write_file = archive_fallback_write_file; } else { #ifdef HAVE_LIBZIP DEBUG("Found file, use ZIP driver: %s", path); @@ -292,6 +355,7 @@ struct archive *archive_open(const char *path) ar->walk = archive_zip_walk; ar->has_entry = archive_zip_has_entry; ar->read_file = archive_zip_read_file; + ar->write_file = archive_zip_write_file; #else ERROR("Found file, but no drivers were enabled: %s", path); free(ar); @@ -332,11 +396,10 @@ int archive_has_entry(struct archive *ar, const char *name) } /* - * Traverses all entries within archive. + * Traverses all files within archive (directories are ignored). * For every entry, the path (relative the archive root) will be passed to * callback function, until the callback returns non-zero. * The arg argument will also be passed to callback. - * Be aware that some archive may not store "directory" type entries. * Returns 0 on success otherwise non-zero as failure. */ int archive_walk( @@ -364,6 +427,53 @@ int archive_read_file(struct archive *ar, const char *fname, } /* + * Writes a file into archive. + * If entry name (fname) is an absolute path (/file), always write into real + * file system. + * Returns 0 on success, otherwise non-zero as failure. + */ +int archive_write_file(struct archive *ar, const char *fname, + uint8_t *data, uint32_t size) +{ + if (!ar || *fname == '/') + return archive_fallback_write_file(NULL, fname, data, size); + return ar->write_file(ar->handle, fname, data, size); +} + +struct _copy_arg { + struct archive *from, *to; +}; + +/* Callback for archive_copy. */ +static int archive_copy_callback(const char *path, void *_arg) +{ + const struct _copy_arg *arg = (const struct _copy_arg*)_arg; + uint32_t size; + uint8_t *data; + int r; + + printf("Copying: %s\n", path); + if (archive_read_file(arg->from, path, &data, &size)) { + ERROR("Failed reading: %s", path); + return 1; + } + r = archive_write_file(arg->to, path, data, size); + DEBUG("result=%d", r); + free(data); + return r; +} + +/* + * Copies all entries from one archive to another. + * Returns 0 on success, otherwise non-zero as failure. + */ +int archive_copy(struct archive *from, struct archive *to) +{ + struct _copy_arg arg = { .from = from, .to = to }; + return archive_walk(from, &arg, archive_copy_callback); +} + +/* * -- End of archive implementations -- */ @@ -451,7 +561,7 @@ static int model_config_parse_setvars_file( /* Some legacy updaters may be still using ${MODEL_DIR}. */ if (str_startswith(v, ENV_VAR_MODEL_DIR)) { - ASPRINTF(&expand_path, "%s%s%s", "models/", cfg->name, + ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name, v + strlen(ENV_VAR_MODEL_DIR)); } |