From cd26ed8f2dd758d47820af4aac9006b2b755fa50 Mon Sep 17 00:00:00 2001 From: Archana Shinde Date: Sun, 28 Feb 2016 19:00:19 -0800 Subject: [PATCH] Add verify_fix_path to fix missing path while staging a file The verify_fix_path function takes a path and a consolidated MOM as its arguments. It breaks down the path into subpaths and checks if each subpath is missing. If found missing, the path is searched for in the consolidated manifest for its hash and downloaded synchronously. It returns success if all that subpaths are verified and fixed. --- include/swupd.h | 5 +- src/bundle.c | 2 +- src/download.c | 5 +- src/helpers.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ src/manifest.c | 18 ++++++++ src/staging.c | 7 ++- src/update.c | 6 +-- src/verify.c | 4 +- 8 files changed, 154 insertions(+), 13 deletions(-) diff --git a/include/swupd.h b/include/swupd.h index 2b04c5ca..9b085841 100644 --- a/include/swupd.h +++ b/include/swupd.h @@ -169,8 +169,9 @@ extern void try_delta(struct file *file); extern void full_download(struct file *file); extern int start_full_download(bool pipelining); extern struct list *end_full_download(void); +extern int untar_full_download(void *data); -extern int do_staging(struct file *file); +extern int do_staging(struct file *file, struct manifest *manifest); extern int rename_all_files_to_final(struct list *updates); extern int rename_staged_file_to_final(struct file *file); @@ -219,6 +220,7 @@ void free_file_data(void *data); void remove_files_in_manifest_from_fs(struct manifest *m); void deduplicate_files_from_manifest(struct manifest **m1, struct manifest *m2); bool manifest_has_component(struct manifest *manifest, const char *component); +extern struct file *search_file_in_manifest(struct manifest *manifest, const char *filename); extern char *mounted_dirs; extern void get_mounted_directories(void); @@ -243,6 +245,7 @@ extern void string_or_die(char **strp, const char *fmt, ...); void update_motd(int new_release); void delete_motd(void); extern int is_dirname_link(const char *fullname); +extern int verify_fix_path(char* targetpath, struct manifest *manifest); /* subscription.c */ struct list *free_list_file(struct list *item); diff --git a/src/bundle.c b/src/bundle.c index 7b40906f..46e70517 100644 --- a/src/bundle.c +++ b/src/bundle.c @@ -417,7 +417,7 @@ int install_bundles(char **bundles) continue; } - ret = do_staging(file); + ret = do_staging(file, mom); if (ret == 0) { rename_staged_file_to_final(file); } diff --git a/src/download.c b/src/download.c index 19a9244b..4a6dbc3e 100644 --- a/src/download.c +++ b/src/download.c @@ -243,7 +243,7 @@ free_tarcommand: /* This function will break if the same HASH.tar full file is downloaded * multiple times in parallel. */ -static void untar_full_download(void *data) +int untar_full_download(void *data) { struct file *file = data; char *tarfile; @@ -267,7 +267,7 @@ static void untar_full_download(void *data) free(tar_dotfile); free(tarfile); free(targetfile); - return; + return 0; } else { unlink(tarfile); unlink(targetfile); @@ -323,6 +323,7 @@ exit: if (err) { unlink_all_staged_content(file); } + return err; } static int perform_curl_io_and_complete(int *left) diff --git a/src/helpers.c b/src/helpers.c index 4a009c1a..719ed7fa 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "config.h" #include "swupd.h" @@ -662,3 +663,122 @@ int is_dirname_link(const char *fullname) free(real_path); return ret; } + +void free_path_data(void *data) +{ + char *path = (char *) data; + free(path); +} + +/* This function is meant to be called while staging file to fix any missing/incorrect paths. + * While staging a file, if its parent directory is missing, this would try to create the path + * by breaking it into sub-paths and fixing them top down. + * Here, target_MoM is the consolidated manifest for the version you are trying to update/verify. + */ +int verify_fix_path(char* targetpath, struct manifest *target_MoM) +{ + struct list *path_list = NULL; /* path_list contains the subparts in the path */ + char *path; + char *tmp = NULL, *target = NULL; + char *url = NULL; + struct stat sb; + int ret = 0; + struct file *file; + char *tar_dotfile = NULL; + struct list *list1 = NULL; + + /* This shouldn't happen */ + if (strcmp(targetpath, "/") == 0) { + return ret; + } + + /* Removing trailing '/' from the path */ + path = strdup(targetpath); + if (path[strlen(path) - 1] == '/') { + path[strlen(path) - 1] = '\0'; + } + + /* Breaking down the path into parts. + * eg. Path /usr/bin/foo will be broken into /usr,/usr/bin and /usr/bin/foo + */ + while (strcmp(path, "/") != 0) { + path_list = list_prepend_data(path_list, strdup(path)); + tmp = strdup(dirname(path)); + free(path); + path = tmp; + } + free(path); + + list1 = list_head(path_list); + while(list1) { + path = list1->data; + list1 = list1->next; + + target = mk_full_filename(path_prefix, path); + + /* Search for the file in the manifest, to get the hash for the file */ + file = search_file_in_manifest(target_MoM, path); + if (file == NULL) { + printf("Error: Path %s not found in any of the subscribed manifests" + "in verify_fix_path for path_prefix %s\n", path, path_prefix); + ret = -1; + goto end; + } + + if (file->is_deleted) { + printf("Error: Path %s found deleted in verify_fix_path\n", path); + ret = -1; + goto end; + } + + ret = stat(target, &sb); + if (ret == 0) { + if (verify_file(file, target)) { + continue; + } + printf("Hash did not match for path : %s\n", path); + } else if (ret == -1 && errno == ENOENT) { + printf("Path %s is missing on the file system\n", path); + } else { + goto end; + } + + string_or_die(&tar_dotfile, "%s/download/.%s.tar", STATE_DIR, file->hash); + + // clean up in case any prior download failed in a partial state + unlink(tar_dotfile); + + string_or_die(&url, "%s/%i/files/%s.tar", preferred_content_url, file->last_change, file->hash); + ret = swupd_curl_get_file(url, tar_dotfile, NULL, NULL, false); + + if (ret != 0) { + printf("Error: Failed to download file %s in verify_fix_path\n", file->filename); + unlink(tar_dotfile); + goto end; + } + if (untar_full_download(file) != 0) { + printf("Error: Failed to untar file %s\n", file->filename); + ret = -1; + goto end; + } + + ret = do_staging(file, target_MoM); + if ( ret != 0) { + printf("Error: Path %s failed to stage in verify_fix_path\n", path); + goto end; + } + + } +end: + if (target) { + free(target); + } + if (tar_dotfile) { + free(tar_dotfile); + } + if (url) { + free(url); + } + list_free_list_and_data(path_list, free_path_data); + return ret; +} diff --git a/src/manifest.c b/src/manifest.c index 6c756123..df4f8c54 100644 --- a/src/manifest.c +++ b/src/manifest.c @@ -1125,3 +1125,21 @@ bool manifest_has_component(struct manifest *manifest, const char *component) return false; } + +/* This performs a linear search through the files list. */ +struct file *search_file_in_manifest(struct manifest *manifest, const char *filename) +{ + struct list *iter = NULL; + struct file *file; + + iter = manifest->files; + while(iter) { + file = iter->data; + iter = iter->next; + + if (strcmp(file->filename, filename) == 0) { + return file; + } + } + return NULL; +} diff --git a/src/staging.c b/src/staging.c index acc8a969..f35fc4a1 100644 --- a/src/staging.c +++ b/src/staging.c @@ -65,7 +65,7 @@ static int create_staging_renamedir(char *rename_tmpdir) #warning "do_staging is currently not able to be run in parallel" /* Consider adding a remove_leftovers() that runs in verify/fix in order to * allow this function to mkdtemp create folders for parallel build */ -int do_staging(struct file *file) +int do_staging(struct file *file, struct manifest *MoM) { char *statfile = NULL, *tmp = NULL, *tmp2 = NULL; char *dir, *base, *rel_dir; @@ -94,10 +94,9 @@ int do_staging(struct file *file) string_or_die(&targetpath, "%s%s", path_prefix, rel_dir); ret = stat(targetpath, &s); - /* For now, just report on error conditions. Once we implement - * verify_fix_path(char *path, int targetversion), we'll want to call it here */ if ((ret == -1) && (errno == ENOENT)) { - printf("Error: Update target directory does not exist: %s\n", targetpath); + printf("Update target directory does not exist: %s. Trying to fix it\n", targetpath); + verify_fix_path(dir, MoM); } else if (!S_ISDIR(s.st_mode)) { printf("Error: Update target exists but is NOT a directory: %s\n", targetpath); } diff --git a/src/update.c b/src/update.c index 1c885fdf..5598db4c 100644 --- a/src/update.c +++ b/src/update.c @@ -89,7 +89,7 @@ static struct list *full_download_loop(struct list *updates, int isfailed) return end_full_download(); } -static int update_loop(struct list *updates) +static int update_loop(struct list *updates, struct manifest *server_manifest) { int ret; struct file *file; @@ -161,7 +161,7 @@ TRY_DOWNLOAD: /* for each file: check hash value; on mismatch delete and queue full download */ /* todo: hash check */ - ret = do_staging(file); + ret = do_staging(file, server_manifest); if (ret < 0) { printf("File staging failed: %s\n", file->filename); return ret; @@ -360,7 +360,7 @@ download_packs: /* Step 7: apply the update */ - ret = update_loop(updates); + ret = update_loop(updates, server_manifest); if (ret == 0) { ret = update_device_latest_version(server_version); printf("Update was applied.\n"); diff --git a/src/verify.c b/src/verify.c index 690eb27e..c435afb8 100644 --- a/src/verify.c +++ b/src/verify.c @@ -414,7 +414,7 @@ static void add_missing_files(struct manifest *official_manifest) } /* install the new file (on miscompare + fix) */ - ret = do_staging(file); + ret = do_staging(file, official_manifest); if (ret == 0) { rename_staged_file_to_final(file); } @@ -486,7 +486,7 @@ static void deal_with_hash_mismatches(struct manifest *official_manifest, bool r } /* install the new file (on miscompare + fix) */ - ret = do_staging(file); + ret = do_staging(file, official_manifest); if (ret == 0) { rename_staged_file_to_final(file); }