summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2018-10-17 12:03:24 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-10-24 13:03:18 -0700
commite1cc2b8ef8d27945043daf0e961fa1ddd40acfd4 (patch)
tree81fc8437a622068c8e2ac844690f145c3e6370f9
parent9262fdd9651c44a0f1ca28ccd876543396baf1ce (diff)
downloadvboot-e1cc2b8ef8d27945043daf0e961fa1ddd40acfd4.tar.gz
futility: updater: Support --repack and --unpack
In order to make the firmware updater package more consistent file contents (for example, we don't want time stamps, and better if the files are always physically located in same order) we want to create and manipulate the ZIP based package directly using updater. BUG=chromium:875551 TEST=TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility BRANCH=None Change-Id: Ie4c5aafe51f633729de2879c73bf7074a695151f Signed-off-by: Hung-Te Lin <hungte@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1286173 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
-rw-r--r--futility/cmd_update.c10
-rw-r--r--futility/updater.c30
-rw-r--r--futility/updater.h16
-rw-r--r--futility/updater_archive.c142
4 files changed, 182 insertions, 16 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index 1b202c0f..f3bb29b9 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -29,6 +29,8 @@ static struct option const long_opts[] = {
{"model", 1, NULL, 'M'},
{"signature_id", 1, NULL, 'G'},
{"manifest", 0, NULL, 'A'},
+ {"repack", 1, NULL, 'k'},
+ {"unpack", 1, NULL, 'u'},
{"factory", 0, NULL, 'Y'},
{"force", 0, NULL, 'F'},
{"programmer", 1, NULL, 'p'},
@@ -56,6 +58,8 @@ static void print_help(int argc, char *argv[])
"-t, --try \tTry A/B update on reboot if possible\n"
"-a, --archive=PATH \tRead resources from archive\n"
" --manifest \tPrint out a JSON manifest and exit\n"
+ " --repack=DIR \tUpdates archive from DIR\n"
+ " --unpack=DIR \tExtracts archive to DIR\n"
"-p, --programmer=PRG\tChange AP (host) flashrom programmer\n"
" --quirks=LIST \tSpecify the quirks to apply\n"
" --list-quirks \tPrint all available quirks\n"
@@ -107,6 +111,12 @@ static int do_update(int argc, char *argv[])
case 'a':
args.archive = optarg;
break;
+ case 'k':
+ args.repack = optarg;
+ break;
+ case 'u':
+ args.unpack = optarg;
+ break;
case 'f':
args.quirks = optarg;
break;
diff --git a/futility/updater.c b/futility/updater.c
index 4ddf53db..d63549a4 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -1816,6 +1816,13 @@ int updater_setup_config(struct updater_config *cfg,
}
*do_update = 0;
}
+ if (arg->repack || arg->unpack) {
+ if (!arg->archive) {
+ ERROR("--{re,un}pack needs --archive.");
+ return ++errorcnt;
+ }
+ *do_update = 0;
+ }
/* Setup update mode. */
if (arg->try_update)
@@ -1882,6 +1889,29 @@ int updater_setup_config(struct updater_config *cfg,
return ++errorcnt;
}
+ /* Process archives which may not have valid contents. */
+ if (arg->repack || arg->unpack) {
+ const char *work_name = arg->repack ? arg->repack : arg->unpack;
+ struct archive *from, *to, *work;
+
+ work = archive_open(work_name);
+ if (arg->repack) {
+ from = work;
+ to = cfg->archive;
+ } else {
+ to = work;
+ from = cfg->archive;
+ }
+ if (!work) {
+ ERROR("Failed to open: %s", work_name);
+ return ++errorcnt;
+ }
+ errorcnt += !!archive_copy(from, to);
+ /* TODO(hungte) Update manifest after copied. */
+ archive_close(work);
+ return errorcnt;
+ }
+
/* Load images from archive. */
if (arg->archive) {
struct manifest *m = new_manifest_from_archive(cfg->archive);
diff --git a/futility/updater.h b/futility/updater.h
index 9c8502b1..5698f9ed 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -117,6 +117,7 @@ struct updater_config_arguments {
char *programmer, *model, *signature_id;
char *emulation, *sys_props, *write_protection;
char *output_dir;
+ char *repack, *unpack;
int is_factory, try_update, force_update, do_manifest, host_only;
int verbosity;
};
@@ -299,6 +300,21 @@ int archive_read_file(struct archive *ar, const char *fname,
uint8_t **data, uint32_t *size);
/*
+ * 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);
+
+/*
+ * 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);
+
+/*
* Creates a new manifest object by scanning files in archive.
* Returns the manifest on success, otherwise NULL for failure.
*/
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));
}