| Index: src/platform/update_engine/install_action.cc
|
| diff --git a/src/platform/update_engine/install_action.cc b/src/platform/update_engine/install_action.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9c644df8ef93350581f69bb1243007c33200d23b
|
| --- /dev/null
|
| +++ b/src/platform/update_engine/install_action.cc
|
| @@ -0,0 +1,249 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "update_engine/install_action.h"
|
| +#include <errno.h>
|
| +#include <vector>
|
| +#include <gflags/gflags.h>
|
| +#include "update_engine/filesystem_iterator.h"
|
| +#include "update_engine/gzip.h"
|
| +#include "update_engine/subprocess.h"
|
| +#include "update_engine/utils.h"
|
| +
|
| +DEFINE_string(mount_install_path, "",
|
| + "If set, the path to use when mounting the "
|
| + "destination device during install");
|
| +
|
| +using std::vector;
|
| +
|
| +namespace chromeos_update_engine {
|
| +
|
| +namespace {
|
| +const string kBspatchPath = "/usr/bin/bspatch";
|
| +}
|
| +
|
| +void InstallAction::PerformAction() {
|
| + ScopedActionCompleter completer(processor_, this);
|
| + // For now, do nothing other than pass what we need to to the output pipe
|
| + CHECK(HasInputObject());
|
| + const InstallPlan install_plan = GetInputObject();
|
| + if (HasOutputPipe())
|
| + SetOutputObject(install_plan.install_path);
|
| + if (install_plan.is_full_update) {
|
| + // No need to perform an install
|
| + completer.set_success(true);
|
| + return;
|
| + }
|
| + // We have a delta update.
|
| +
|
| + // Open delta file
|
| + DeltaDiffParser parser(install_plan.download_path);
|
| + if (!parser.valid()) {
|
| + LOG(ERROR) << "Unable to open delta file";
|
| + return;
|
| + }
|
| +
|
| + // Mount install fs
|
| + string mountpoint = FLAGS_mount_install_path;
|
| + if (mountpoint.empty()) {
|
| + // Set up dest_path_
|
| + char *mountpoint_temp = strdup("/tmp/install_mnt.XXXXXX");
|
| + CHECK(mountpoint_temp);
|
| + CHECK_EQ(mountpoint_temp, mkdtemp(mountpoint_temp));
|
| + CHECK_NE('\0', mountpoint_temp[0]);
|
| + mountpoint = mountpoint_temp;
|
| + free(mountpoint_temp);
|
| + }
|
| +
|
| + TEST_AND_RETURN(utils::MountFilesystem(install_plan.install_path,
|
| + mountpoint));
|
| +
|
| + // Automatically unmount the fs when this goes out of scope:
|
| + ScopedFilesystemUnmounter filesystem_unmounter(mountpoint);
|
| +
|
| + {
|
| + // iterate through existing fs, deleting unneeded files
|
| + FilesystemIterator iter(mountpoint,
|
| + utils::SetWithValue<string>("/lost+found"));
|
| + for (; !iter.IsEnd(); iter.Increment()) {
|
| + if (!parser.ContainsPath(iter.GetPartialPath())) {
|
| + VLOG(1) << "install removing local path: " << iter.GetFullPath();
|
| + TEST_AND_RETURN(utils::RecursiveUnlinkDir(iter.GetFullPath()));
|
| + }
|
| + }
|
| + TEST_AND_RETURN(!iter.IsErr());
|
| + }
|
| +
|
| + // iterate through delta metadata, writing files
|
| + DeltaDiffParserIterator iter = parser.Begin();
|
| + for (; iter != parser.End(); iter.Increment()) {
|
| + const DeltaArchiveManifest_File& file = iter.GetFile();
|
| + VLOG(1) << "Installing file: " << iter.path();
|
| + TEST_AND_RETURN(InstallFile(mountpoint, file, iter.path(), parser));
|
| + }
|
| +
|
| + completer.set_success(true);
|
| +}
|
| +
|
| +bool InstallAction::InstallFile(const std::string& mountpoint,
|
| + const DeltaArchiveManifest_File& file,
|
| + const std::string& path,
|
| + const DeltaDiffParser& parser) const {
|
| + // See what's already there
|
| + struct stat existing_stbuf;
|
| + int result = lstat((mountpoint + path).c_str(), &existing_stbuf);
|
| + TEST_AND_RETURN_FALSE_ERRNO((result == 0) || (errno == ENOENT));
|
| + bool exists = (result == 0);
|
| + // Create the proper file
|
| + if (S_ISDIR(file.mode())) {
|
| + if (!exists) {
|
| + TEST_AND_RETURN_FALSE_ERRNO(
|
| + (mkdir((mountpoint + path).c_str(), file.mode())) == 0);
|
| + }
|
| + } else if (S_ISLNK(file.mode())) {
|
| + InstallFileSymlink(mountpoint, file, path, parser, exists);
|
| + } else if (S_ISCHR(file.mode()) ||
|
| + S_ISBLK(file.mode()) ||
|
| + S_ISFIFO(file.mode()) ||
|
| + S_ISSOCK(file.mode())) {
|
| + InstallFileSpecialFile(mountpoint, file, path, parser, exists);
|
| + } else if (S_ISREG(file.mode())) {
|
| + InstallFileRegularFile(mountpoint, file, path, parser, exists);
|
| + } else {
|
| + // unknown mode type
|
| + TEST_AND_RETURN_FALSE(false);
|
| + }
|
| +
|
| + // chmod/chown new file
|
| + if (!S_ISLNK(file.mode()))
|
| + TEST_AND_RETURN_FALSE_ERRNO(chmod((mountpoint + path).c_str(), file.mode())
|
| + == 0);
|
| + TEST_AND_RETURN_FALSE(file.has_uid() && file.has_gid());
|
| + TEST_AND_RETURN_FALSE_ERRNO(lchown((mountpoint + path).c_str(),
|
| + file.uid(), file.gid()) == 0);
|
| + return true;
|
| +}
|
| +
|
| +bool InstallAction::InstallFileRegularFile(
|
| + const std::string& mountpoint,
|
| + const DeltaArchiveManifest_File& file,
|
| + const std::string& path,
|
| + const DeltaDiffParser& parser,
|
| + const bool exists) const {
|
| + if (!file.has_data_format())
|
| + return true;
|
| + TEST_AND_RETURN_FALSE(file.has_data_offset() && file.has_data_length());
|
| + if (file.data_format() == DeltaArchiveManifest_File_DataFormat_BSDIFF) {
|
| + // Expand with bspatch
|
| + string patch_path = utils::TempFilename(mountpoint + path + ".XXXXXX");
|
| + TEST_AND_RETURN_FALSE(file.has_data_length());
|
| + TEST_AND_RETURN_FALSE(parser.CopyDataToFile(
|
| + file.data_offset(),
|
| + static_cast<off_t>(file.data_length()), false,
|
| + patch_path));
|
| + string output_path = utils::TempFilename(mountpoint + path + ".XXXXXX");
|
| + int rc = 1;
|
| + vector<string> cmd;
|
| + cmd.push_back(kBspatchPath);
|
| + cmd.push_back(mountpoint + path);
|
| + cmd.push_back(output_path);
|
| + cmd.push_back(patch_path);
|
| + TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc));
|
| + TEST_AND_RETURN_FALSE(rc == 0);
|
| + TEST_AND_RETURN_FALSE_ERRNO(rename(output_path.c_str(),
|
| + (mountpoint + path).c_str()) == 0);
|
| + TEST_AND_RETURN_FALSE_ERRNO(unlink(patch_path.c_str()) == 0);
|
| + } else {
|
| + // Expand full data, decompressing if necessary
|
| + TEST_AND_RETURN_FALSE((file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL) ||
|
| + (file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL_GZ));
|
| + if (exists)
|
| + TEST_AND_RETURN_FALSE_ERRNO(unlink((mountpoint + path).c_str()) == 0);
|
| + TEST_AND_RETURN_FALSE(file.has_data_length());
|
| + const bool gzipped = file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL_GZ;
|
| + bool success =
|
| + parser.CopyDataToFile(file.data_offset(), file.data_length(),
|
| + gzipped,
|
| + mountpoint + path);
|
| + TEST_AND_RETURN_FALSE(success);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// char/block devices, fifos, and sockets:
|
| +bool InstallAction::InstallFileSpecialFile(
|
| + const std::string& mountpoint,
|
| + const DeltaArchiveManifest_File& file,
|
| + const std::string& path,
|
| + const DeltaDiffParser& parser,
|
| + const bool exists) const {
|
| + if (exists)
|
| + TEST_AND_RETURN_FALSE(unlink((mountpoint + path).c_str()) == 0);
|
| + dev_t dev = 0;
|
| + if (S_ISCHR(file.mode()) || S_ISBLK(file.mode())) {
|
| + vector<char> dev_proto;
|
| + TEST_AND_RETURN_FALSE(parser.ReadDataVector(file.data_offset(),
|
| + file.data_length(),
|
| + &dev_proto));
|
| + if (file.data_format() == DeltaArchiveManifest_File_DataFormat_FULL_GZ) {
|
| + TEST_AND_RETURN_FALSE(file.has_data_length());
|
| + {
|
| + vector<char> decompressed_dev_proto;
|
| + TEST_AND_RETURN_FALSE(GzipDecompress(dev_proto,
|
| + &decompressed_dev_proto));
|
| + dev_proto = decompressed_dev_proto;
|
| + }
|
| + } else {
|
| + TEST_AND_RETURN_FALSE(file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL);
|
| + }
|
| + LinuxDevice linux_device;
|
| + utils::HexDumpVector(dev_proto);
|
| + TEST_AND_RETURN_FALSE(linux_device.ParseFromArray(&dev_proto[0],
|
| + dev_proto.size()));
|
| + dev = makedev(linux_device.major(), linux_device.minor());
|
| + }
|
| + TEST_AND_RETURN_FALSE_ERRNO(mknod((mountpoint + path).c_str(),
|
| + file.mode(), dev) == 0);
|
| + return true;
|
| +}
|
| +// symlinks:
|
| +bool InstallAction::InstallFileSymlink(const std::string& mountpoint,
|
| + const DeltaArchiveManifest_File& file,
|
| + const std::string& path,
|
| + const DeltaDiffParser& parser,
|
| + const bool exists) const {
|
| + // If there's no data, we leave the symlink as is
|
| + if (!file.has_data_format())
|
| + return true; // No changes needed
|
| + TEST_AND_RETURN_FALSE((file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL) ||
|
| + (file.data_format() ==
|
| + DeltaArchiveManifest_File_DataFormat_FULL_GZ));
|
| + TEST_AND_RETURN_FALSE(file.has_data_offset() && file.has_data_length());
|
| + // We have data, and thus use it to create a symlink.
|
| + // First delete any existing symlink:
|
| + if (exists)
|
| + TEST_AND_RETURN_FALSE_ERRNO(unlink((mountpoint + path).c_str()) == 0);
|
| + vector<char> symlink_data;
|
| + TEST_AND_RETURN_FALSE(parser.ReadDataVector(file.data_offset(),
|
| + file.data_length(),
|
| + &symlink_data));
|
| + if (file.data_format() == DeltaArchiveManifest_File_DataFormat_FULL_GZ) {
|
| + vector<char> decompressed_symlink_data;
|
| + TEST_AND_RETURN_FALSE(GzipDecompress(symlink_data,
|
| + &decompressed_symlink_data));
|
| + symlink_data = decompressed_symlink_data;
|
| + }
|
| + symlink_data.push_back('\0');
|
| + TEST_AND_RETURN_FALSE_ERRNO(symlink(&symlink_data[0],
|
| + (mountpoint + path).c_str()) == 0);
|
| + return true;
|
| +}
|
| +
|
| +
|
| +} // namespace chromeos_update_engine
|
|
|