/* add a bundle_result to the results list */ static void add_bundle_file_result(char *bundlename, char *filename, double score) { struct bundle_result *bundle = NULL; struct file_result *file; struct list *ptr; ptr = results; while (ptr) { struct bundle_result *item; item = ptr->data; if (strcmp(item->bundle_name, bundlename) == 0) { bundle = item; break; } ptr = ptr->next; } if (!bundle) { bundle = calloc(sizeof(struct bundle_result), 1); ON_NULL_ABORT(bundle); results = list_append_data(results, bundle); strncpy(bundle->bundle_name, bundlename, BUNDLE_NAME_MAXLEN - 1); /* record if the bundle is tracked on the system */ bundle->is_tracked = is_tracked_bundle(bundlename); } file = calloc(sizeof(struct file_result), 1); ON_NULL_ABORT(file); file->filename = strdup_or_die(filename); bundle->files = list_append_data(bundle->files, file); file->score = score; bundle->score += score; }
/* recursively calculate size from a complete list of bundle_result structs */ static long calculate_size(char *bname, struct list *bundle_info, bool installed) { struct list *ptr; struct bundle_result *bi; long size = 0; ptr = list_head(bundle_info); while (ptr) { bi = ptr->data; ptr = ptr->next; if (bi->seen || strcmp(bname, bi->bundle_name) != 0) { continue; } /* If installed is true the initial call to this recursive * function was for an installed bundle. In this case we want * to calculate the contentsize of all included bundles (we * know they are all installed already) so we can report the * total installed size on the system. * * Otherwise only add the contentsize of bundles not already * installed on the system. */ if (installed || !is_tracked_bundle(bname)) { size += bi->topsize; bi->seen = true; } /* add bundle sizes for includes as well */ struct list *ptr2; ptr2 = list_head(bi->includes); while (ptr2) { char *inc = NULL; inc = ptr2->data; ptr2 = ptr2->next; /* recursively add included sizes */ size += calculate_size(inc, bundle_info, installed); } } return size; }
/* Bundle install one ore more bundles passed in bundles * param as a null terminated array of strings */ int install_bundles(char **bundles) { int lock_fd; int ret = 0; int current_version; struct manifest *mom; struct list *iter; struct sub *sub; struct file *file; /* step 1: initialize swupd and get current version from OS */ if (!init_globals()) { return EINIT_GLOBALS; } ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); /* first of all, make sure STATE_DIR is there, recreate if necessary*/ ret = create_required_dirs(); if (ret != 0) { printf("State directory %s cannot be recreated, aborting installation\n", STATE_DIR); goto clean_and_exit; } ret = load_manifests(current_version, current_version, "MoM", NULL, &mom); if (ret != 0) { printf("Cannot load official manifest MoM for version %i\n", current_version); ret = EMOM_NOTFOUND; goto clean_and_exit; } /* step 2: check bundle args are valid if so populate subs struct */ int i; for (i = 0; *bundles; ++bundles) { if (is_tracked_bundle(*bundles)) { printf("%s bundle already installed, skipping it\n", *bundles); continue; } if (!manifest_has_component(mom, *bundles)) { printf("%s bundle name is invalid, skipping it...\n", *bundles); continue; } if (component_subscribed(*bundles)) { continue; } create_and_append_subscription(*bundles); i++; printf("Added bundle %s for installation\n", *bundles); } if (i == 0) { printf("There are no pending bundles to install, exiting now\n"); ret = EBUNDLE_INSTALL; goto clean_manifest_and_exit; } subscription_versions_from_MoM(mom, 0); recurse_manifest(mom, NULL); consolidate_submanifests(mom); /* step 3: download neccessary packs */ ret = rm_staging_dir_contents("download"); printf("Downloading required packs...\n"); ret = download_subscribed_packs(0, current_version, true); if (ret != 0) { printf("pack downloads failed, cannot proceed with the installation, exiting.\n"); goto clean_subs_and_exit; } /* step 4: Install all bundle(s) files into the fs */ printf("Installing bundle(s) files...\n"); iter = list_head(mom->files); while (iter) { file = iter->data; iter = iter->next; if (file->is_deleted || file->do_not_update || ignore(file)) { continue; } ret = do_staging(file); if (ret == 0) { rename_staged_file_to_final(file); } } sync(); /* step 5: create bundle(s) subscription entries to track them * * Strictly speaking each manifest has an entry to write its own bundle filename * and thus tracking automagically, here just making sure. */ iter = list_head(subs); while (iter) { sub = iter->data; iter = iter->next; printf("Tracking %s bundle on the system\n", sub->component); ret = track_bundle_in_system(sub->component); if (ret != 0) { printf("Cannot track %s bundle on the system\n", sub->component); } } /* Run any scripts that are needed to complete update */ run_scripts(); ret = 0; printf("Bundle(s) installation done.\n"); clean_subs_and_exit: free_subscriptions(); clean_manifest_and_exit: free_manifest(mom); clean_and_exit: swupd_curl_cleanup(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); free_globals(); return ret; }
/* This function is a fresh new implementation for a bundle * remove without being tied to verify loop, this means * improved speed and space as well as more roubustness and * flexibility. What it does is basically: * * 1) Read MoM and load all submanifests except the one to be * removed and then consolidate them. * 2) Load the removed bundle submanifest. * 3) Order the file list by filename * 4) Deduplicate removed submanifest file list that happens * to be on the MoM (minus bundle to be removed). * 5) iterate over to be removed bundle submanifest file list * performing a unlink(2) for each filename. * 6) Done. */ int remove_bundle(const char *bundle_name) { int lock_fd; int ret = 0; int current_version = CURRENT_OS_VERSION; struct manifest *current_mom, *bundle_manifest; /* Initially we don't support format nor path_prefix * for bundle_rm but eventually that will be added, then * set_format_string() and init_globals() must be pulled out * to the caller to properly initialize in case those opts * passed to the command. */ set_format_string(NULL); if (!init_globals()) { return EINIT_GLOBALS; } ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } /* os-core bundle not allowed to be removed... * although this is going to be caught later because of all files * being marked as 'duplicated' and note removing anything * anyways, better catch here and return success, no extra work to be done. */ if (strcmp(bundle_name, "os-core") == 0) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } if (!is_tracked_bundle(bundle_name)) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } current_version = read_version_from_subvol_file(path_prefix); swupd_curl_set_current_version(current_version); /* first of all, make sure STATE_DIR is there, recreate if necessary*/ ret = create_required_dirs(); if (ret != 0) { printf("State directory %s cannot be recreated, aborting removal\n", STATE_DIR); goto out_free_curl; } ret = load_manifests(current_version, current_version, "MoM", NULL, ¤t_mom); if (ret != 0) { goto out_free_curl; } /* load all tracked bundles into memory */ read_subscriptions_alt(); /* now popout the one to be removed */ ret = unload_tracked_bundle(bundle_name); if (ret != 0) { goto out_free_mom; } subscription_versions_from_MoM(current_mom, 0); /* load all submanifest minus the one to be removed */ recurse_manifest(current_mom, NULL); consolidate_submanifests(current_mom); /* Now that we have the consolidated list of all files, load bundle to be removed submanifest*/ ret = load_bundle_manifest(bundle_name, current_version, &bundle_manifest); if (ret != 0) { goto out_free_mom; } /* deduplication needs file list sorted by filename, do so */ bundle_manifest->files = list_sort(bundle_manifest->files, file_sort_filename); deduplicate_files_from_manifest(&bundle_manifest, current_mom); printf("Deleting bundle files...\n"); remove_files_in_manifest_from_fs(bundle_manifest); printf("Untracking bundle from system...\n"); rm_bundle_file(bundle_name); printf("Success: Bundle removed\n"); free_manifest(bundle_manifest); out_free_mom: free_manifest(current_mom); out_free_curl: if (ret) { printf("Error: Bundle remove failed\n"); } swupd_curl_cleanup(); v_lockfile(lock_fd); dump_file_descriptor_leaks(); return ret; }
/* tristate return, -1 for errors, 1 for no errors but no new subscriptions, 0 for no errors and new subscriptions */ int add_subscriptions(struct list *bundles, int current_version, struct manifest *mom) { bool new_bundles = false; char *bundle; int ret; int retries = 0; int timeout = 10; struct file *file; struct list *iter; struct manifest *manifest; srand(time(NULL)); iter = list_head(bundles); while (iter) { bundle = iter->data; iter = iter->next; file = search_bundle_in_manifest(mom, bundle); if (!file) { printf("%s bundle name is invalid, skipping it...\n", bundle); continue; } retry_manifest_download: manifest = load_manifest(current_version, file->last_change, file, mom); if (!manifest) { if (retries < MAX_TRIES) { increment_retries(&retries, &timeout); goto retry_manifest_download; } printf("Unable to download manifest %s version %d, exiting now\n", bundle, file->last_change); ret = -1; goto out; } if (!manifest) { printf("Unable to load manifest %s version %d, exiting now\n", bundle, file->last_change); ret = -1; goto out; } if (manifest->includes) { ret = add_subscriptions(manifest->includes, current_version, mom); if (ret == -1) { free_manifest(manifest); goto out; } else if (ret == 0) { new_bundles = true; } } free_manifest(manifest); if (is_tracked_bundle(bundle)) { continue; } if (component_subscribed(bundle)) { continue; } create_and_append_subscription(bundle); new_bundles = true; } if (new_bundles) { ret = 0; } else { ret = 1; } out: return ret; }
/* This function is a fresh new implementation for a bundle * remove without being tied to verify loop, this means * improved speed and space as well as more roubustness and * flexibility. What it does is basically: * * 1) Read MoM and load all submanifests except the one to be * removed and then consolidate them. * 2) Load the removed bundle submanifest. * 3) Order the file list by filename * 4) Deduplicate removed submanifest file list that happens * to be on the MoM (minus bundle to be removed). * 5) iterate over to be removed bundle submanifest file list * performing a unlink(2) for each filename. * 6) Done. */ int remove_bundle(const char *bundle_name) { int lock_fd; int ret = 0; int current_version = CURRENT_OS_VERSION; struct manifest *current_mom, *bundle_manifest = NULL; ret = swupd_init(&lock_fd); if (ret != 0) { printf("Failed updater initialization, exiting now.\n"); return ret; } /* os-core bundle not allowed to be removed... * although this is going to be caught later because of all files * being marked as 'duplicated' and note removing anything * anyways, better catch here and return success, no extra work to be done. */ if (strcmp(bundle_name, "os-core") == 0) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } if (!is_tracked_bundle(bundle_name)) { ret = EBUNDLE_NOT_TRACKED; goto out_free_curl; } current_version = get_current_version(path_prefix); if (current_version < 0) { printf("Error: Unable to determine current OS version\n"); ret = ECURRENT_VERSION; goto out_free_curl; } swupd_curl_set_current_version(current_version); current_mom = load_mom(current_version); if (!current_mom) { printf("Unable to download/verify %d Manifest.MoM\n", current_version); ret = EMOM_NOTFOUND; goto out_free_curl; } if (!search_bundle_in_manifest(current_mom, bundle_name)) { printf("Bundle name is invalid, aborting removal\n"); ret = EBUNDLE_REMOVE; goto out_free_mom; } /* load all tracked bundles into memory */ read_subscriptions_alt(); /* now popout the one to be removed */ ret = unload_tracked_bundle(bundle_name); if (ret != 0) { goto out_free_mom; } subscription_versions_from_MoM(current_mom, 0); /* load all submanifest minus the one to be removed */ current_mom->submanifests = recurse_manifest(current_mom, NULL); if (!current_mom->submanifests) { printf("Error: Cannot load MoM sub-manifests\n"); ret = ERECURSE_MANIFEST; goto out_free_mom; } if (is_included(bundle_name, current_mom)) { printf("Error: bundle requested to be removed is required by other installed bundles\n"); ret = EBUNDLE_REMOVE; goto out_free_mom; } current_mom->files = files_from_bundles(current_mom->submanifests); current_mom->files = consolidate_files(current_mom->files); /* Now that we have the consolidated list of all files, load bundle to be removed submanifest*/ ret = load_bundle_manifest(bundle_name, current_version, &bundle_manifest); if (ret != 0 || !bundle_manifest) { printf("Error: Cannot load %s sub-manifest (ret = %d)\n", bundle_name, ret); goto out_free_mom; } /* deduplication needs file list sorted by filename, do so */ bundle_manifest->files = list_sort(bundle_manifest->files, file_sort_filename); deduplicate_files_from_manifest(&bundle_manifest, current_mom); printf("Deleting bundle files...\n"); remove_files_in_manifest_from_fs(bundle_manifest); printf("Untracking bundle from system...\n"); rm_bundle_file(bundle_name); printf("Success: Bundle removed\n"); free_manifest(bundle_manifest); out_free_mom: free_manifest(current_mom); out_free_curl: if (ret) { printf("Error: Bundle remove failed\n"); } swupd_deinit(lock_fd); return ret; }