| Alex Deymo | 20bdc70 | 2016-12-07 21:07:11 -0800 | [diff] [blame] | 1 | // | 
 | 2 | // Copyright (C) 2016 The Android Open Source Project | 
 | 3 | // | 
 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 | // you may not use this file except in compliance with the License. | 
 | 6 | // You may obtain a copy of the License at | 
 | 7 | // | 
 | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 | // | 
 | 10 | // Unless required by applicable law or agreed to in writing, software | 
 | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 | // See the License for the specific language governing permissions and | 
 | 14 | // limitations under the License. | 
 | 15 | // | 
 | 16 |  | 
 | 17 | #include "update_engine/payload_generator/mapfile_filesystem.h" | 
 | 18 |  | 
 | 19 | #include <algorithm> | 
 | 20 | #include <map> | 
 | 21 |  | 
 | 22 | #include <base/files/file_util.h> | 
 | 23 | #include <base/logging.h> | 
 | 24 | #include <base/strings/string_number_conversions.h> | 
 | 25 | #include <base/strings/string_split.h> | 
 | 26 | #include <brillo/make_unique_ptr.h> | 
 | 27 |  | 
 | 28 | #include "update_engine/common/utils.h" | 
 | 29 | #include "update_engine/payload_generator/extent_ranges.h" | 
 | 30 | #include "update_engine/payload_generator/extent_utils.h" | 
 | 31 | #include "update_engine/update_metadata.pb.h" | 
 | 32 |  | 
 | 33 | using std::string; | 
 | 34 | using std::vector; | 
 | 35 |  | 
 | 36 | namespace { | 
 | 37 | // The .map file is defined in terms of 4K blocks. | 
 | 38 | size_t kMapfileBlockSize = 4096; | 
 | 39 | }  // namespace | 
 | 40 |  | 
 | 41 | namespace chromeos_update_engine { | 
 | 42 |  | 
 | 43 | std::unique_ptr<MapfileFilesystem> MapfileFilesystem::CreateFromFile( | 
 | 44 |     const string& filename, const string& mapfile_filename) { | 
 | 45 |   if (filename.empty() || mapfile_filename.empty()) | 
 | 46 |     return nullptr; | 
 | 47 |  | 
 | 48 |   off_t file_size = utils::FileSize(filename); | 
 | 49 |   if (file_size < 0) | 
 | 50 |     return nullptr; | 
 | 51 |  | 
 | 52 |   if (file_size % kMapfileBlockSize) { | 
 | 53 |     LOG(ERROR) << "Image file " << filename << " has a size of " << file_size | 
 | 54 |                << " which is not multiple of " << kMapfileBlockSize; | 
 | 55 |     return nullptr; | 
 | 56 |   } | 
 | 57 |   off_t num_blocks = file_size / kMapfileBlockSize; | 
 | 58 |  | 
 | 59 |   if (!utils::FileExists(mapfile_filename.c_str())) { | 
 | 60 |     LOG(ERROR) << "File " << mapfile_filename << " doesn't exist"; | 
 | 61 |     return nullptr; | 
 | 62 |   } | 
 | 63 |  | 
 | 64 |   return brillo::make_unique_ptr( | 
 | 65 |       new MapfileFilesystem(mapfile_filename, num_blocks)); | 
 | 66 | } | 
 | 67 |  | 
 | 68 | MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename, | 
 | 69 |                                      off_t num_blocks) | 
 | 70 |     : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {} | 
 | 71 |  | 
 | 72 | size_t MapfileFilesystem::GetBlockSize() const { | 
 | 73 |   return kMapfileBlockSize; | 
 | 74 | } | 
 | 75 |  | 
 | 76 | size_t MapfileFilesystem::GetBlockCount() const { | 
 | 77 |   return num_blocks_; | 
 | 78 | } | 
 | 79 |  | 
 | 80 | bool MapfileFilesystem::GetFiles(vector<File>* files) const { | 
 | 81 |   files->clear(); | 
 | 82 |  | 
 | 83 |   string file_data; | 
 | 84 |   if (!base::ReadFileToString(base::FilePath(mapfile_filename_), &file_data)) { | 
 | 85 |     LOG(ERROR) << "Unable to read .map file: " << mapfile_filename_; | 
 | 86 |     return false; | 
 | 87 |   } | 
 | 88 |  | 
 | 89 |   // Iterate over all the lines in the file and generate one File entry per | 
 | 90 |   // line. | 
 | 91 |   vector<base::StringPiece> lines = base::SplitStringPiece( | 
 | 92 |       file_data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); | 
 | 93 |   for (const base::StringPiece& line : lines) { | 
 | 94 |     File mapped_file; | 
 | 95 |  | 
 | 96 |     mapped_file.extents = {}; | 
 | 97 |     size_t delim, last_delim = line.size(); | 
 | 98 |     while ((delim = line.rfind(' ', last_delim - 1)) != string::npos) { | 
 | 99 |       string blocks = | 
 | 100 |           line.substr(delim + 1, last_delim - (delim + 1)).as_string(); | 
 | 101 |       size_t dash = blocks.find('-', 0); | 
 | 102 |       uint64_t block_start, block_end; | 
 | 103 |       if (dash == string::npos && base::StringToUint64(blocks, &block_start)) { | 
 | 104 |         mapped_file.extents.push_back(ExtentForRange(block_start, 1)); | 
 | 105 |       } else if (dash != string::npos && | 
 | 106 |                  base::StringToUint64(blocks.substr(0, dash), &block_start) && | 
 | 107 |                  base::StringToUint64(blocks.substr(dash + 1), &block_end)) { | 
 | 108 |         if (block_end < block_start) { | 
 | 109 |           LOG(ERROR) << "End block " << block_end | 
 | 110 |                      << " is smaller than start block " << block_start | 
 | 111 |                      << std::endl | 
 | 112 |                      << line; | 
 | 113 |           return false; | 
 | 114 |         } | 
 | 115 |         if (block_end > static_cast<uint64_t>(num_blocks_)) { | 
 | 116 |           LOG(ERROR) << "The end block " << block_end | 
 | 117 |                      << " is past the end of the file of " << num_blocks_ | 
 | 118 |                      << " blocks" << std::endl | 
 | 119 |                      << line; | 
 | 120 |           return false; | 
 | 121 |         } | 
 | 122 |         mapped_file.extents.push_back( | 
 | 123 |             ExtentForRange(block_start, block_end - block_start + 1)); | 
 | 124 |       } else { | 
 | 125 |         // If we can't parse N or N-M, we assume the block is actually part of | 
 | 126 |         // the name of the file. | 
 | 127 |         break; | 
 | 128 |       } | 
 | 129 |       last_delim = delim; | 
 | 130 |     } | 
 | 131 |     // We parsed the blocks from the end of the line, so we need to reverse | 
 | 132 |     // the Extents in the file. | 
 | 133 |     std::reverse(mapped_file.extents.begin(), mapped_file.extents.end()); | 
 | 134 |  | 
 | 135 |     if (last_delim == string::npos) | 
 | 136 |       continue; | 
 | 137 |     mapped_file.name = line.substr(0, last_delim).as_string(); | 
 | 138 |  | 
 | 139 |     files->push_back(mapped_file); | 
 | 140 |   } | 
 | 141 |  | 
 | 142 |   return true; | 
 | 143 | } | 
 | 144 |  | 
 | 145 | bool MapfileFilesystem::LoadSettings(brillo::KeyValueStore* store) const { | 
 | 146 |   // Settings not supported in mapfile since the storage format is unknown. | 
 | 147 |   LOG(ERROR) << "mapfile doesn't support LoadSettings()."; | 
 | 148 |   return false; | 
 | 149 | } | 
 | 150 |  | 
 | 151 | }  // namespace chromeos_update_engine |