diff options
Diffstat (limited to '')
-rw-r--r-- | updater/blockimg.cpp | 100 |
1 files changed, 74 insertions, 26 deletions
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 838845673..07c3c7b52 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -53,6 +53,7 @@ #include <ziparchive/zip_archive.h> #include "edify/expr.h" +#include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "otautil/print_sha1.h" @@ -69,6 +70,7 @@ static constexpr size_t BLOCKSIZE = 4096; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; static constexpr mode_t STASH_FILE_MODE = 0600; +static constexpr mode_t MARKER_DIRECTORY_MODE = 0700; static CauseCode failure_type = kNoCause; static bool is_retry = false; @@ -109,7 +111,7 @@ static bool ParseLastCommandFile(size_t* last_command_index) { return false; } - if (!android::base::ParseInt(lines[0], last_command_index)) { + if (!android::base::ParseUint(lines[0], last_command_index)) { LOG(ERROR) << "Failed to parse integer in: " << lines[0]; return false; } @@ -166,26 +168,37 @@ static bool UpdateLastCommandIndex(size_t command_index, const std::string& comm return true; } -static bool SetPartitionUpdatedMarker(const std::string& marker) { +bool SetUpdatedMarker(const std::string& marker) { + auto dirname = android::base::Dirname(marker); + auto res = mkdir(dirname.c_str(), MARKER_DIRECTORY_MODE); + if (res == -1 && errno != EEXIST) { + PLOG(ERROR) << "Failed to create directory for marker: " << dirname; + return false; + } + if (!android::base::WriteStringToFile("", marker)) { PLOG(ERROR) << "Failed to write to marker file " << marker; return false; } - if (!FsyncDir(android::base::Dirname(marker))) { + if (!FsyncDir(dirname)) { return false; } - LOG(INFO) << "Wrote partition updated marker to " << marker; + LOG(INFO) << "Wrote updated marker to " << marker; return true; } -static bool discard_blocks(int fd, off64_t offset, uint64_t size) { - // Don't discard blocks unless the update is a retry run. - if (!is_retry) { +static bool discard_blocks(int fd, off64_t offset, uint64_t size, bool force = false) { + // Don't discard blocks unless the update is a retry run or force == true + if (!is_retry && !force) { return true; } uint64_t args[2] = { static_cast<uint64_t>(offset), size }; if (ioctl(fd, BLKDISCARD, &args) == -1) { + // On devices that does not support BLKDISCARD, ignore the error. + if (errno == EOPNOTSUPP) { + return true; + } PLOG(ERROR) << "BLKDISCARD ioctl failed"; return false; } @@ -874,7 +887,7 @@ static int CreateStash(State* state, size_t maxblocks, const std::string& base) size_t max_stash_size = maxblocks * BLOCKSIZE; if (res == -1) { LOG(INFO) << "creating stash " << dirname; - res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE); + res = mkdir_recursively(dirname, STASH_DIRECTORY_MODE, false, nullptr); if (res != 0) { ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(), @@ -1399,7 +1412,10 @@ static int PerformCommandDiff(CommandParameters& params) { // We expect the output of the patcher to fill the tgt ranges exactly. if (!writer.Finished()) { - LOG(ERROR) << "range sink underrun?"; + LOG(ERROR) << "Failed to fully write target blocks (range sink underrun): Missing " + << writer.AvailableSpace() << " bytes"; + failure_type = kPatchApplicationFailure; + return -1; } } else { LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " [" @@ -1445,14 +1461,9 @@ static int PerformCommandErase(CommandParameters& params) { LOG(INFO) << " erasing " << tgt.blocks() << " blocks"; for (const auto& [begin, end] : tgt) { - uint64_t blocks[2]; - // offset in bytes - blocks[0] = begin * static_cast<uint64_t>(BLOCKSIZE); - // length in bytes - blocks[1] = (end - begin) * static_cast<uint64_t>(BLOCKSIZE); - - if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) { - PLOG(ERROR) << "BLKDISCARD ioctl failed"; + off64_t offset = static_cast<off64_t>(begin) * BLOCKSIZE; + size_t size = (end - begin) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, size, true /* force */)) { return -1; } } @@ -1514,7 +1525,7 @@ static int PerformCommandComputeHashTree(CommandParameters& params) { // Starts the hash_tree computation. HashTreeBuilder builder(BLOCKSIZE, hash_function); - if (!builder.Initialize(source_ranges.blocks() * BLOCKSIZE, salt)) { + if (!builder.Initialize(static_cast<int64_t>(source_ranges.blocks()) * BLOCKSIZE, salt)) { LOG(ERROR) << "Failed to initialize hash tree computation, source " << source_ranges.ToString() << ", salt " << salt_hex; return -1; @@ -1570,6 +1581,43 @@ using CommandFunction = std::function<int(CommandParameters&)>; using CommandMap = std::unordered_map<Command::Type, CommandFunction>; +static bool Sha1DevicePath(const std::string& path, uint8_t digest[SHA_DIGEST_LENGTH]) { + auto device_name = android::base::Basename(path); + auto dm_target_name_path = "/sys/block/" + device_name + "/dm/name"; + + struct stat sb; + if (stat(dm_target_name_path.c_str(), &sb) == 0) { + // This is a device mapper target. Use partition name as part of the hash instead. Do not + // include extents as part of the hash, because the size of a partition may be shrunk after + // the patches are applied. + std::string dm_target_name; + if (!android::base::ReadFileToString(dm_target_name_path, &dm_target_name)) { + PLOG(ERROR) << "Cannot read " << dm_target_name_path; + return false; + } + SHA1(reinterpret_cast<const uint8_t*>(dm_target_name.data()), dm_target_name.size(), digest); + return true; + } + + if (errno != ENOENT) { + // This is a device mapper target, but its name cannot be retrieved. + PLOG(ERROR) << "Cannot get dm target name for " << path; + return false; + } + + // This doesn't appear to be a device mapper target, but if its name starts with dm-, something + // else might have gone wrong. + if (android::base::StartsWith(device_name, "dm-")) { + LOG(WARNING) << "Device " << path << " starts with dm- but is not mapped by device-mapper."; + } + + // Stash directory should be different for each partition to avoid conflicts when updating + // multiple partitions at the same time, so we use the hash of the block device name as the base + // directory. + SHA1(reinterpret_cast<const uint8_t*>(path.data()), path.size(), digest); + return true; +} + static Value* PerformBlockImageUpdate(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv, const CommandMap& command_map, bool dryrun) { @@ -1654,12 +1702,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return StringValue(""); } - // Stash directory should be different for each partition to avoid conflicts when updating - // multiple partitions at the same time, so we use the hash of the block device name as the base - // directory. uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<const uint8_t*>(blockdev_filename->data.data()), - blockdev_filename->data.size(), digest); + if (!Sha1DevicePath(blockdev_filename->data, digest)) { + return StringValue(""); + } params.stashbase = print_sha1(digest); // Possibly do return early on retry, by checking the marker. If the update on this partition has @@ -1869,8 +1915,10 @@ pbiudone: const char* partition = strrchr(blockdev_filename->data.c_str(), '/'); if (partition != nullptr && *(partition + 1) != 0) { - fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, params.written * BLOCKSIZE); - fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, params.stashed * BLOCKSIZE); + fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1, + static_cast<uint64_t>(params.written) * BLOCKSIZE); + fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1, + static_cast<uint64_t>(params.stashed) * BLOCKSIZE); fflush(cmd_pipe); } // Delete stash only after successfully completing the update, as it may contain blocks needed @@ -1881,7 +1929,7 @@ pbiudone: // Create a marker on /cache partition, which allows skipping the update on this partition on // retry. The marker will be removed once booting into normal boot, or before starting next // fresh install. - if (!SetPartitionUpdatedMarker(updated_marker)) { + if (!SetUpdatedMarker(updated_marker)) { LOG(WARNING) << "Failed to set updated marker; continuing"; } } |