diff --git a/src/helpers.c b/src/helpers.c index c4050636..5cfe241b 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -906,58 +906,14 @@ int link_or_rename(const char *orig, const char *dest) return 0; } -/* list the tarfile content, and verify it contains only one line equal to the expected hash. - * loop through all the content to detect the case where archive contains more than one file. - */ -static int check_tarfile_content(struct file *file, const char *tarfilename) +static int check_single_file_tarball(const char *tarfilename, struct file *file) { int err; - char *tarcommand; - FILE *tar; - int count = 0; + char *filename; - string_or_die(&tarcommand, TAR_COMMAND " -tf %s/download/%s.tar 2> /dev/null", state_dir, file->hash); - - err = access(tarfilename, R_OK); - if (err) { - goto free_tarcommand; - } - - tar = popen(tarcommand, "r"); - if (tar == NULL) { - err = -1; - goto free_tarcommand; - } - - while (!feof(tar)) { - char *c; - char buffer[PATH_MAXLEN]; - - if (fgets(buffer, PATH_MAXLEN, tar) == NULL) { - if (count != 1) { - err = -1; - } - break; - } - - c = strchr(buffer, '\n'); - if (c) { - *c = 0; - } - if (c && (c != buffer) && (*(c - 1) == '/')) { - /* strip trailing '/' from directory tar */ - *(c - 1) = 0; - } - if (strcmp(buffer, file->hash) != 0) { - err = -1; - break; - } - count++; - } - - pclose(tar); -free_tarcommand: - free_string(&tarcommand); + string_or_die(&filename, "%s%s", file->hash, file->is_dir ? "/" : ""); + err = archives_check_single_file_tarball(tarfilename, filename); + free(filename); return err; } @@ -1004,7 +960,7 @@ int untar_full_download(void *data) } free_string(&tar_dotfile); - err = check_tarfile_content(file, tarfile); + err = check_single_file_tarball(tarfile, file); if (err) { goto exit; } diff --git a/src/lib/archives.c b/src/lib/archives.c index 9509c68a..e082964b 100644 --- a/src/lib/archives.c +++ b/src/lib/archives.c @@ -86,6 +86,43 @@ static int copy_data(struct archive *ar, struct archive *aw) return 0; } +static int archive_from_filename(struct archive **a, const char *tarfile) +{ + int r = 0; + + if (!a) { + return -EINVAL; + } + + *a = archive_read_new(); + if (!*a) { + return -ENOMEM; + } + + r = archive_read_support_format_tar(*a); + if (_archive_check_err(*a, r)) { + goto error; + } + + r = archive_read_support_filter_all(*a); + if (_archive_check_err(*a, r)) { + goto error; + } + + r = archive_read_open_filename(*a, tarfile, 10240); + if (_archive_check_err(*a, r)) { + /* could not open archive for read */ + goto error; + } + + return 0; + +error: + archive_read_close(*a); + archive_read_free(*a); + return r; +} + int archives_extract_to(const char *tarfile, const char *outputdir) { struct archive *a, *ext; @@ -104,15 +141,9 @@ int archives_extract_to(const char *tarfile, const char *outputdir) flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; /* set up read */ - a = archive_read_new(); - r = archive_read_support_format_tar(a); - if (_archive_check_err(a, r)) { - goto out_read; - } - - r = archive_read_support_filter_all(a); - if (_archive_check_err(a, r)) { - goto out_read; + r = archive_from_filename(&a, tarfile); + if (r < 0) { + return r; } /* set up write */ @@ -127,12 +158,6 @@ int archives_extract_to(const char *tarfile, const char *outputdir) goto out; } - r = archive_read_open_filename(a, tarfile, 10240); - if (_archive_check_err(a, r)) { - /* could not open archive for read */ - goto out; - } - /* read and write loop */ for (;;) { r = archive_read_next_header(a, &entry); @@ -191,8 +216,37 @@ out: * error. If 'goto out' is called, we already have an error we are handling * so don't overwrite that returncode. */ archive_write_free(ext); -out_read: archive_read_close(a); archive_read_free(a); return r; } + +int archives_check_single_file_tarball(const char *tarfilename, const char *file) +{ + struct archive *a; + struct archive_entry *entry; + int r; + bool found = false; + + /* set up read */ + r = archive_from_filename(&a, tarfilename); + if (r < 0) { + return r; + } + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + if (!found && strcmp(archive_entry_pathname(entry), file) == 0) { + found = true; + } else { + found = false; + goto end; + } + archive_read_data_skip(a); + } + +end: + archive_read_close(a); + archive_read_free(a); + + return found ? 0 : -ENOENT; +} diff --git a/src/lib/archives.h b/src/lib/archives.h index 26551614..0d8f3da3 100644 --- a/src/lib/archives.h +++ b/src/lib/archives.h @@ -11,6 +11,12 @@ extern "C" { */ int archives_extract_to(const char *tarfile, const char *outputdir); +/* + * Check if this tarball is valid and if it contains only one file with the + * specified name. + */ +int archives_check_single_file_tarball(const char *tarfilename, const char *file); + #ifdef __cplusplus } #endif diff --git a/src/swupd.h b/src/swupd.h index dc1c65ba..7ff72674 100644 --- a/src/swupd.h +++ b/src/swupd.h @@ -11,6 +11,7 @@ #include #include +#include "lib/archives.h" #include "lib/formatter_json.h" #include "lib/list.h" #include "lib/log.h" @@ -18,7 +19,6 @@ #include "lib/progress.h" #include "lib/strings.h" #include "lib/sys.h" -#include "lib/archives.h" #include "manifest.h" #include "scripts.h" #include "swupd_curl.h"