1 /* 2 * Copyright 2006 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 "SysUtil.h" 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdint.h> 22 #include <sys/mman.h> 23 #include <sys/stat.h> 24 #include <sys/types.h> 25 26 #include <algorithm> 27 #include <string> 28 #include <vector> 29 30 #include <android-base/file.h> 31 #include <android-base/logging.h> 32 #include <android-base/strings.h> 33 #include <android-base/unique_fd.h> 34 35 static bool sysMapFD(int fd, MemMapping* pMap) { 36 CHECK(pMap != nullptr); 37 38 struct stat sb; 39 if (fstat(fd, &sb) == -1) { 40 PLOG(ERROR) << "fstat(" << fd << ") failed"; 41 return false; 42 } 43 44 void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 45 if (memPtr == MAP_FAILED) { 46 PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed"; 47 return false; 48 } 49 50 pMap->addr = static_cast<unsigned char*>(memPtr); 51 pMap->length = sb.st_size; 52 pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) }); 53 54 return true; 55 } 56 57 // A "block map" which looks like this (from uncrypt/uncrypt.cpp): 58 // 59 // /dev/block/platform/msm_sdcc.1/by-name/userdata # block device 60 // 49652 4096 # file size in bytes, block size 61 // 3 # count of block ranges 62 // 1000 1008 # block range 0 63 // 2100 2102 # ... block range 1 64 // 30 33 # ... block range 2 65 // 66 // Each block range represents a half-open interval; the line "30 33" 67 // reprents the blocks [30, 31, 32]. 68 static int sysMapBlockFile(const char* filename, MemMapping* pMap) { 69 CHECK(pMap != nullptr); 70 71 std::string content; 72 if (!android::base::ReadFileToString(filename, &content)) { 73 PLOG(ERROR) << "Failed to read " << filename; 74 return -1; 75 } 76 77 std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); 78 if (lines.size() < 4) { 79 LOG(ERROR) << "Block map file is too short: " << lines.size(); 80 return -1; 81 } 82 83 size_t size; 84 unsigned int blksize; 85 if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) { 86 LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; 87 return -1; 88 } 89 90 size_t range_count; 91 if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { 92 LOG(ERROR) << "Failed to parse block map header: " << lines[2]; 93 return -1; 94 } 95 96 size_t blocks; 97 if (blksize != 0) { 98 blocks = ((size - 1) / blksize) + 1; 99 } 100 if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 || 101 lines.size() != 3 + range_count) { 102 LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize 103 << ", range_count " << range_count << ", lines " << lines.size(); 104 return -1; 105 } 106 107 // Reserve enough contiguous address space for the whole file. 108 void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 109 if (reserve == MAP_FAILED) { 110 PLOG(ERROR) << "failed to reserve address space"; 111 return -1; 112 } 113 114 const std::string& block_dev = lines[0]; 115 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY))); 116 if (fd == -1) { 117 PLOG(ERROR) << "failed to open block device " << block_dev; 118 munmap(reserve, blocks * blksize); 119 return -1; 120 } 121 122 pMap->ranges.resize(range_count); 123 124 unsigned char* next = static_cast<unsigned char*>(reserve); 125 size_t remaining_size = blocks * blksize; 126 bool success = true; 127 for (size_t i = 0; i < range_count; ++i) { 128 const std::string& line = lines[i + 3]; 129 130 size_t start, end; 131 if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) { 132 LOG(ERROR) << "failed to parse range " << i << " in block map: " << line; 133 success = false; 134 break; 135 } 136 size_t length = (end - start) * blksize; 137 if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { 138 LOG(ERROR) << "unexpected range in block map: " << start << " " << end; 139 success = false; 140 break; 141 } 142 143 void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 144 static_cast<off64_t>(start) * blksize); 145 if (addr == MAP_FAILED) { 146 PLOG(ERROR) << "failed to map block " << i; 147 success = false; 148 break; 149 } 150 pMap->ranges[i].addr = addr; 151 pMap->ranges[i].length = length; 152 153 next += length; 154 remaining_size -= length; 155 } 156 if (success && remaining_size != 0) { 157 LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size; 158 success = false; 159 } 160 if (!success) { 161 munmap(reserve, blocks * blksize); 162 return -1; 163 } 164 165 pMap->addr = static_cast<unsigned char*>(reserve); 166 pMap->length = size; 167 168 LOG(INFO) << "mmapped " << range_count << " ranges"; 169 170 return 0; 171 } 172 173 int sysMapFile(const char* fn, MemMapping* pMap) { 174 if (fn == nullptr || pMap == nullptr) { 175 LOG(ERROR) << "Invalid argument(s)"; 176 return -1; 177 } 178 179 *pMap = {}; 180 181 if (fn[0] == '@') { 182 if (sysMapBlockFile(fn + 1, pMap) != 0) { 183 LOG(ERROR) << "Map of '" << fn << "' failed"; 184 return -1; 185 } 186 } else { 187 // This is a regular file. 188 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY))); 189 if (fd == -1) { 190 PLOG(ERROR) << "Unable to open '" << fn << "'"; 191 return -1; 192 } 193 194 if (!sysMapFD(fd, pMap)) { 195 LOG(ERROR) << "Map of '" << fn << "' failed"; 196 return -1; 197 } 198 } 199 return 0; 200 } 201 202 /* 203 * Release a memory mapping. 204 */ 205 void sysReleaseMap(MemMapping* pMap) { 206 std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) { 207 if (munmap(range.addr, range.length) == -1) { 208 PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed"; 209 } 210 }); 211 pMap->ranges.clear(); 212 } 213