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
sysMapFD(int fd,MemMapping * pMap)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].
sysMapBlockFile(const char * filename,MemMapping * pMap)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
sysMapFile(const char * fn,MemMapping * pMap)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 */
sysReleaseMap(MemMapping * pMap)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