summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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));
}