diff options
Diffstat (limited to 'install_action.cc')
-rw-r--r-- | install_action.cc | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/install_action.cc b/install_action.cc new file mode 100644 index 00000000..9c644df8 --- /dev/null +++ b/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 |