Example #1
0
int install_bundles(struct list *bundles, int current_version, struct manifest *mom)
{
	int ret;
	struct file *file;
	struct list *iter;
	struct list *to_install_bundles, *to_install_files;

	/* step 1: check bundle args are valid if so populate subs struct */
	ret = add_subscriptions(bundles, current_version, mom);

	if (ret) {
		if (ret == 1) {
			printf("bundle(s) already installed, exiting now\n");
		}
		ret = EBUNDLE_INSTALL;
		goto out;
	}

	subscription_versions_from_MoM(mom, 0);

	to_install_bundles = recurse_manifest(mom, NULL);
	if (!to_install_bundles) {
		printf("Error: Cannot load to install bundles\n");
		ret = ERECURSE_MANIFEST;
		goto out;
	}

	to_install_files = files_from_bundles(to_install_bundles);
	to_install_files = consolidate_files(to_install_files);

	/* step 2: download neccessary packs */

	(void)rm_staging_dir_contents("download");

	printf("Downloading packs...\n");
	(void)download_subscribed_packs(true);

	/* step 3: Add tracked bundles */
	read_subscriptions_alt();
	subscription_versions_from_MoM(mom, 0);
	mom->submanifests = recurse_manifest(mom, NULL);
	if (!mom->submanifests) {
		printf("Error: Cannot load installed bundles\n");
		ret = ERECURSE_MANIFEST;
		goto out;
	}

	mom->files = files_from_bundles(mom->submanifests);
	mom->files = consolidate_files(mom->files);

	/* step 4: Install all bundle(s) files into the fs */

	printf("Installing bundle(s) files...\n");
	iter = list_head(to_install_files);
	while (iter) {
		file = iter->data;
		iter = iter->next;

		if (file->is_deleted || file->do_not_update || ignore(file)) {
			continue;
		}

		ret = do_staging(file, mom);
		if (ret) {
			ret = verify_fix_path(file->filename, mom);
		}
		if (ret) {
			ret = EBUNDLE_INSTALL;
			goto out;
		}
	}

	iter = list_head(to_install_files);
	while (iter) {
		file = iter->data;
		iter = iter->next;

		if (file->is_deleted || file->do_not_update || ignore(file)) {
			continue;
		}

		/* This was staged by verify_fix_path */
		if (!file->staging) {
			file = search_file_in_manifest(mom, file->filename);
		}

		rename_staged_file_to_final(file);
	}

	sync();

	/* step 5: Run any scripts that are needed to complete update */
	run_scripts();

	ret = 0;
	printf("Bundle(s) installation done.\n");

out:
	free_subscriptions();
	return ret;
}
Example #2
0
/* 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, struct manifest *MoM)
{
	char *statfile = NULL, *tmp = NULL, *tmp2 = NULL;
	char *dir, *base, *rel_dir;
	char *tarcommand = NULL;
	char *original = NULL;
	char *target = NULL;
	char *targetpath = NULL;
	char *rename_target = NULL;
	char *rename_tmpdir = NULL;
	int ret;
	struct stat s;

	tmp = strdup(file->filename);
	tmp2 = strdup(file->filename);

	dir = dirname(tmp);
	base = basename(tmp2);

	rel_dir = dir;
	if (*dir == '/') {
		rel_dir = dir + 1;
	}

	string_or_die(&original, "%s/staged/%s", state_dir, file->hash);

	string_or_die(&targetpath, "%s%s", path_prefix, rel_dir);
	ret = stat(targetpath, &s);

	if ((ret == -1) && (errno == ENOENT)) {
		fprintf(stderr, "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)) {
		fprintf(stderr, "Error: Update target exists but is NOT a directory: %s\n", targetpath);
	}

	free_string(&targetpath);
	string_or_die(&target, "%s%s/.update.%s", path_prefix, rel_dir, base);
	ret = swupd_rm(target);
	if (ret < 0 && ret != -ENOENT) {
		fprintf(stderr, "Error: Failed to remove %s\n", target);
	}

	string_or_die(&statfile, "%s%s", path_prefix, file->filename);

	memset(&s, 0, sizeof(struct stat));
	ret = lstat(statfile, &s);
	if (ret == 0) {
		if ((file->is_dir && !S_ISDIR(s.st_mode)) ||
		    (file->is_link && !S_ISLNK(s.st_mode)) ||
		    (file->is_file && !S_ISREG(s.st_mode))) {
			//file type changed, move old out of the way for new
			ret = swupd_rm(statfile);
			if (ret < 0) {
				ret = -ETYPE_CHANGED_FILE_RM;
				goto out;
			}
		}
	}
	free_string(&statfile);

	if (file->is_dir || S_ISDIR(s.st_mode)) {
		/* In the btrfs only scenario there was an implicit
		 * "create_or_update_dir()" via un-tar-ing a directory.tar after
		 * download and the untar happens in the staging subvolume which
		 * then gets promoted to a "real" usable subvolume.  But for
		 * a live rootfs the directory needs copied out of staged
		 * and into the rootfs.  Tar is a way to copy with
		 * attributes and it includes internal logic that does the
		 * right thing to overlay a directory onto something
		 * pre-existing: */
		/* In order to avoid tar transforms with directories, rename
		 * the directory before and after the tar command */
		string_or_die(&rename_tmpdir, "%s/tmprenamedir", state_dir);
		ret = create_staging_renamedir(rename_tmpdir);
		if (ret) {
			goto out;
		}
		string_or_die(&rename_target, "%s/%s", rename_tmpdir, base);
		if (rename(original, rename_target)) {
			ret = -errno;
			goto out;
		}
		string_or_die(&tarcommand, TAR_COMMAND " -C '%s' " TAR_PERM_ATTR_ARGS " -cf - './%s' 2> /dev/null | " TAR_COMMAND " -C '%s%s' " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
			      rename_tmpdir, base, path_prefix, rel_dir);
		ret = system(tarcommand);
		if (WIFEXITED(ret)) {
			ret = WEXITSTATUS(ret);
		}
		free_string(&tarcommand);
		if (rename(rename_target, original)) {
			ret = -errno;
			goto out;
		}
		if (ret < 0) {
			ret = -EDIR_OVERWRITE;
			goto out;
		}
	} else { /* (!file->is_dir && !S_ISDIR(stat.st_mode)) */
		/* can't naively hard link(): Non-read-only files with same hash must remain
		 * separate copies otherwise modifications to one instance of the file
		 * propagate to all instances of the file perhaps causing subtle data corruption from
		 * a user's perspective.  In practice the rootfs is stateless and owned by us.
		 * Additionally cross-mount hardlinks fail and it's hard to know what an admin
		 * might have for overlaid mounts.  The use of tar is a simple way to copy, but
		 * inefficient.  So prefer hardlink and fall back if needed: */
		ret = -1;
		if (!file->is_config && !file->is_state && !file->use_xattrs) {
			ret = link(original, target);
		}
		if (ret < 0) {
			/* either the hardlink failed, or it was undesirable (config), do a tar-tar dance */
			/* In order to avoid tar transforms, rename the file
			 * before and after the tar command */
			string_or_die(&rename_target, "%s/staged/.update.%s", state_dir, base);
			ret = rename(original, rename_target);
			if (ret) {
				ret = -errno;
				goto out;
			}
			string_or_die(&tarcommand, TAR_COMMAND " -C '%s/staged' " TAR_PERM_ATTR_ARGS " -cf - '.update.%s' 2> /dev/null | " TAR_COMMAND " -C '%s%s' " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
				      state_dir, base, path_prefix, rel_dir);
			ret = system(tarcommand);
			if (WIFEXITED(ret)) {
				ret = WEXITSTATUS(ret);
			}
			free_string(&tarcommand);
			ret = rename(rename_target, original);
			if (ret) {
				ret = -errno;
				goto out;
			}
		}

		struct stat buf;
		int err;

		free_string(&file->staging);
		string_or_die(&file->staging, "%s%s/.update.%s", path_prefix, rel_dir, base);

		err = lstat(file->staging, &buf);
		if (err != 0) {
			free_string(&file->staging);
			ret = -EDOTFILE_WRITE;
			goto out;
		}
	}

out:
	free_string(&target);
	free_string(&original);
	free_string(&rename_target);
	free_string(&rename_tmpdir);
	free_string(&tmp);
	free_string(&tmp2);

	return ret;
}