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/memory/ptr_util.h> 25 #include <base/strings/string_number_conversions.h> 26 #include <base/strings/string_split.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 base::WrapUnique(new MapfileFilesystem(mapfile_filename, num_blocks)); 65 } 66 67 MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename, 68 off_t num_blocks) 69 : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {} 70 71 size_t MapfileFilesystem::GetBlockSize() const { 72 return kMapfileBlockSize; 73 } 74 75 size_t MapfileFilesystem::GetBlockCount() const { 76 return num_blocks_; 77 } 78 79 bool MapfileFilesystem::GetFiles(vector<File>* files) const { 80 files->clear(); 81 82 string file_data; 83 if (!base::ReadFileToString(base::FilePath(mapfile_filename_), &file_data)) { 84 LOG(ERROR) << "Unable to read .map file: " << mapfile_filename_; 85 return false; 86 } 87 88 // Iterate over all the lines in the file and generate one File entry per 89 // line. 90 vector<base::StringPiece> lines = base::SplitStringPiece( 91 file_data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); 92 for (const base::StringPiece& line : lines) { 93 File mapped_file; 94 95 mapped_file.extents = {}; 96 size_t delim, last_delim = line.size(); 97 while ((delim = line.rfind(' ', last_delim - 1)) != string::npos) { 98 string blocks = 99 line.substr(delim + 1, last_delim - (delim + 1)).as_string(); 100 size_t dash = blocks.find('-', 0); 101 uint64_t block_start, block_end; 102 if (dash == string::npos && base::StringToUint64(blocks, &block_start)) { 103 mapped_file.extents.push_back(ExtentForRange(block_start, 1)); 104 } else if (dash != string::npos && 105 base::StringToUint64(blocks.substr(0, dash), &block_start) && 106 base::StringToUint64(blocks.substr(dash + 1), &block_end)) { 107 if (block_end < block_start) { 108 LOG(ERROR) << "End block " << block_end 109 << " is smaller than start block " << block_start 110 << std::endl 111 << line; 112 return false; 113 } 114 if (block_end > static_cast<uint64_t>(num_blocks_)) { 115 LOG(ERROR) << "The end block " << block_end 116 << " is past the end of the file of " << num_blocks_ 117 << " blocks" << std::endl 118 << line; 119 return false; 120 } 121 mapped_file.extents.push_back( 122 ExtentForRange(block_start, block_end - block_start + 1)); 123 } else { 124 // If we can't parse N or N-M, we assume the block is actually part of 125 // the name of the file. 126 break; 127 } 128 last_delim = delim; 129 } 130 // We parsed the blocks from the end of the line, so we need to reverse 131 // the Extents in the file. 132 std::reverse(mapped_file.extents.begin(), mapped_file.extents.end()); 133 134 if (last_delim == string::npos) 135 continue; 136 mapped_file.name = line.substr(0, last_delim).as_string(); 137 138 files->push_back(mapped_file); 139 } 140 141 return true; 142 } 143 144 bool MapfileFilesystem::LoadSettings(brillo::KeyValueStore* store) const { 145 // Settings not supported in mapfile since the storage format is unknown. 146 LOG(ERROR) << "mapfile doesn't support LoadSettings()."; 147 return false; 148 } 149 150 } // namespace chromeos_update_engine 151