1 //
2 // Copyright (C) 2020 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 <errno.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21
22 #include <chrono>
23 #include <iostream>
24 #include <random>
25 #include <string>
26 #include <thread>
27 #include <vector>
28
29 #include <android-base/file.h>
30 #include <android-base/parsedouble.h>
31 #include <android-base/stringprintf.h>
32 #include <android-base/strings.h>
33 #include <android-base/unique_fd.h>
34 #include <ext4_utils/ext4_utils.h>
35 #include <fstab/fstab.h>
36 #include <libdm/dm.h>
37 #include <libfiemap/image_manager.h>
38
39 using namespace std::chrono_literals;
40 using namespace std::string_literals;
41 using android::base::borrowed_fd;
42 using android::base::unique_fd;
43 using android::dm::DeviceMapper;
44 using android::dm::DmDeviceState;
45 using android::dm::DmTable;
46 using android::dm::DmTargetSnapshot;
47 using android::dm::SnapshotStorageMode;
48 using android::fiemap::ImageManager;
49 using android::fs_mgr::Fstab;
50
51 namespace android {
52 namespace snapshot {
53
usage()54 static void usage() {
55 std::cerr << "Usage:\n";
56 std::cerr << " create <orig-payload> <new-payload>\n";
57 std::cerr << "\n";
58 std::cerr << " Create a snapshot device containing the contents of\n";
59 std::cerr << " orig-payload, and then write the contents of new-payload.\n";
60 std::cerr << " The original files are not modified.\n";
61 std::cerr << "\n";
62 std::cerr << " merge <fail-rate>\n";
63 std::cerr << "\n";
64 std::cerr << " Merge the snapshot previously started by create, and wait\n";
65 std::cerr << " for it to complete. Once done, it is compared to the\n";
66 std::cerr << " new-payload for consistency. The original files are not \n";
67 std::cerr << " modified. If a fail-rate is passed (as a fraction between 0\n";
68 std::cerr << " and 100), every 10ms the device has that percent change of\n";
69 std::cerr << " injecting a kernel crash.\n";
70 std::cerr << "\n";
71 std::cerr << " check <new-payload>\n";
72 std::cerr << " Verify that all artifacts are correct after a merge\n";
73 std::cerr << " completes.\n";
74 std::cerr << "\n";
75 std::cerr << " cleanup\n";
76 std::cerr << " Remove all ImageManager artifacts from create/merge.\n";
77 }
78
79 class PowerTest final {
80 public:
81 PowerTest();
82 bool Run(int argc, char** argv);
83
84 private:
85 bool OpenImageManager();
86 bool Create(int argc, char** argv);
87 bool Merge(int argc, char** argv);
88 bool Check(int argc, char** argv);
89 bool Cleanup();
90 bool CleanupImage(const std::string& name);
91 bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
92 bool MapImages();
93 bool MapSnapshot(SnapshotStorageMode mode);
94 bool GetMergeStatus(DmTargetSnapshot::Status* status);
95
96 static constexpr char kSnapshotName[] = "snapshot-power-test";
97 static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
98 static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
99
100 DeviceMapper& dm_;
101 std::unique_ptr<ImageManager> images_;
102 std::string image_path_;
103 std::string cow_path_;
104 std::string snapshot_path_;
105 };
106
PowerTest()107 PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
108
109 bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
110 if (!OpenImageManager()) {
111 return false;
112 }
113
114 if (argc < 2) {
115 usage();
116 return false;
117 }
118 if (argv[1] == "create"s) {
119 return Create(argc, argv);
120 } else if (argv[1] == "merge"s) {
121 return Merge(argc, argv);
122 } else if (argv[1] == "check"s) {
123 return Check(argc, argv);
124 } else if (argv[1] == "cleanup"s) {
125 return Cleanup();
126 } else {
127 usage();
128 return false;
129 }
130 }
131
OpenImageManager()132 bool PowerTest::OpenImageManager() {
133 std::vector<std::string> dirs = {
134 "/data/gsi/test",
135 "/metadata/gsi/test",
136 };
137 for (const auto& dir : dirs) {
138 if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
139 std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
140 return false;
141 }
142 }
143
144 images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
145 if (!images_) {
146 std::cerr << "Could not open ImageManager\n";
147 return false;
148 }
149 return true;
150 }
151
Create(int argc,char ** argv)152 bool PowerTest::Create(int argc, char** argv) {
153 if (argc < 4) {
154 usage();
155 return false;
156 }
157
158 std::string first = argv[2];
159 std::string second = argv[3];
160
161 unique_fd second_fd(open(second.c_str(), O_RDONLY));
162 if (second_fd < 0) {
163 std::cerr << "open " << second << ": " << strerror(errno) << "\n";
164 return false;
165 }
166
167 if (!Cleanup()) {
168 return false;
169 }
170 if (!SetupImages(first, second_fd)) {
171 return false;
172 }
173 if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
174 return false;
175 }
176
177 struct stat s;
178 if (fstat(second_fd, &s)) {
179 std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
180 return false;
181 }
182
183 unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
184 if (snap_fd < 0) {
185 std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
186 return false;
187 }
188
189 uint8_t chunk[4096];
190 uint64_t written = 0;
191 while (written < s.st_size) {
192 uint64_t remaining = s.st_size - written;
193 size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
194 if (!android::base::ReadFully(second_fd, chunk, bytes)) {
195 std::cerr << "read " << second << ": " << strerror(errno) << "\n";
196 return false;
197 }
198 if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
199 std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
200 return false;
201 }
202 written += bytes;
203 }
204 if (fsync(snap_fd)) {
205 std::cerr << "fsync: " << strerror(errno) << "\n";
206 return false;
207 }
208
209 sync();
210
211 snap_fd = {};
212 if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
213 std::cerr << "could not delete dm device " << kSnapshotName << "\n";
214 return false;
215 }
216 if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
217 std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
218 return false;
219 }
220 if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
221 std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
222 return false;
223 }
224 return true;
225 }
226
Cleanup()227 bool PowerTest::Cleanup() {
228 if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
229 std::cerr << "could not delete dm device " << kSnapshotName << "\n";
230 return false;
231 }
232 if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
233 return false;
234 }
235 return true;
236 }
237
CleanupImage(const std::string & name)238 bool PowerTest::CleanupImage(const std::string& name) {
239 if (!images_->UnmapImageIfExists(name)) {
240 std::cerr << "failed to unmap " << name << "\n";
241 return false;
242 }
243 if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
244 std::cerr << "failed to delete " << name << "\n";
245 return false;
246 }
247 return true;
248 }
249
SetupImages(const std::string & first,borrowed_fd second_fd)250 bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
251 unique_fd first_fd(open(first.c_str(), O_RDONLY));
252 if (first_fd < 0) {
253 std::cerr << "open " << first << ": " << strerror(errno) << "\n";
254 return false;
255 }
256
257 struct stat s1, s2;
258 if (fstat(first_fd.get(), &s1)) {
259 std::cerr << "first stat: " << strerror(errno) << "\n";
260 return false;
261 }
262 if (fstat(second_fd.get(), &s2)) {
263 std::cerr << "second stat: " << strerror(errno) << "\n";
264 return false;
265 }
266
267 // Pick the bigger size of both images, rounding up to the nearest block.
268 uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
269 uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
270 uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
271 if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
272 std::cerr << "failed to create " << kSnapshotImageName << "\n";
273 return false;
274 }
275 // Use the same size for the cow.
276 if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
277 std::cerr << "failed to create " << kSnapshotCowName << "\n";
278 return false;
279 }
280 if (!MapImages()) {
281 return false;
282 }
283
284 unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
285 if (image_fd < 0) {
286 std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
287 return false;
288 }
289
290 uint8_t chunk[4096];
291 uint64_t written = 0;
292 while (written < s1.st_size) {
293 uint64_t remaining = s1.st_size - written;
294 size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
295 if (!android::base::ReadFully(first_fd, chunk, bytes)) {
296 std::cerr << "read: " << strerror(errno) << "\n";
297 return false;
298 }
299 if (!android::base::WriteFully(image_fd, chunk, bytes)) {
300 std::cerr << "write: " << strerror(errno) << "\n";
301 return false;
302 }
303 written += bytes;
304 }
305 if (fsync(image_fd)) {
306 std::cerr << "fsync: " << strerror(errno) << "\n";
307 return false;
308 }
309
310 // Zero the first block of the COW.
311 unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
312 if (cow_fd < 0) {
313 std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
314 return false;
315 }
316
317 memset(chunk, 0, sizeof(chunk));
318 if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
319 std::cerr << "read: " << strerror(errno) << "\n";
320 return false;
321 }
322 if (fsync(cow_fd)) {
323 std::cerr << "fsync: " << strerror(errno) << "\n";
324 return false;
325 }
326 return true;
327 }
328
MapImages()329 bool PowerTest::MapImages() {
330 if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
331 std::cerr << "failed to map " << kSnapshotImageName << "\n";
332 return false;
333 }
334 if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
335 std::cerr << "failed to map " << kSnapshotCowName << "\n";
336 return false;
337 }
338 return true;
339 }
340
MapSnapshot(SnapshotStorageMode mode)341 bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
342 uint64_t sectors;
343 {
344 unique_fd fd(open(image_path_.c_str(), O_RDONLY));
345 if (fd < 0) {
346 std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
347 return false;
348 }
349 sectors = get_block_device_size(fd) / 512;
350 }
351
352 DmTable table;
353 table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
354 if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
355 std::cerr << "failed to create snapshot device\n";
356 return false;
357 }
358 return true;
359 }
360
GetMergeStatus(DmTargetSnapshot::Status * status)361 bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
362 std::vector<DeviceMapper::TargetInfo> targets;
363 if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
364 std::cerr << "failed to get merge status\n";
365 return false;
366 }
367 if (targets.size() != 1) {
368 std::cerr << "merge device has wrong number of targets\n";
369 return false;
370 }
371 if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
372 std::cerr << "could not parse merge target status text\n";
373 return false;
374 }
375 return true;
376 }
377
GetUserdataBlockDeviceName()378 static std::string GetUserdataBlockDeviceName() {
379 Fstab fstab;
380 if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
381 return {};
382 }
383
384 auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
385 if (!entry) {
386 return {};
387 }
388
389 auto prefix = "/dev/block/"s;
390 if (!android::base::StartsWith(entry->blk_device, prefix)) {
391 return {};
392 }
393 return entry->blk_device.substr(prefix.size());
394 }
395
Merge(int argc,char ** argv)396 bool PowerTest::Merge(int argc, char** argv) {
397 // Start an f2fs GC to really stress things. :TODO: figure out data device
398 auto userdata_dev = GetUserdataBlockDeviceName();
399 if (userdata_dev.empty()) {
400 std::cerr << "could not locate userdata block device\n";
401 return false;
402 }
403
404 auto cmd =
405 android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
406 system(cmd.c_str());
407
408 if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
409 if (!MapImages()) {
410 return false;
411 }
412 if (!MapSnapshot(SnapshotStorageMode::Merge)) {
413 return false;
414 }
415 }
416
417 std::random_device r;
418 std::default_random_engine re(r());
419 std::uniform_real_distribution<double> dist(0.0, 100.0);
420
421 std::optional<double> failure_rate;
422 if (argc >= 3) {
423 double d;
424 if (!android::base::ParseDouble(argv[2], &d)) {
425 std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
426 return false;
427 }
428 failure_rate = d;
429 }
430
431 while (true) {
432 DmTargetSnapshot::Status status;
433 if (!GetMergeStatus(&status)) {
434 return false;
435 }
436 if (!status.error.empty()) {
437 std::cerr << "merge reported error: " << status.error << "\n";
438 return false;
439 }
440 if (status.sectors_allocated == status.metadata_sectors) {
441 break;
442 }
443
444 std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
445
446 if (failure_rate && *failure_rate >= dist(re)) {
447 system("echo 1 > /proc/sys/kernel/sysrq");
448 system("echo c > /proc/sysrq-trigger");
449 }
450
451 std::this_thread::sleep_for(10ms);
452 }
453
454 std::cout << "Merge completed.\n";
455 return true;
456 }
457
458 bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
459 if (argc < 3) {
460 std::cerr << "Expected argument: <new-image-path>\n";
461 return false;
462 }
463 std::string md_path, image_path;
464 std::string canonical_path = argv[2];
465
466 if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
467 std::cerr << "could not get dm-path for merge device\n";
468 return false;
469 }
470 if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
471 std::cerr << "could not get image path\n";
472 return false;
473 }
474
475 unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
476 if (md_fd < 0) {
477 std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
478 return false;
479 }
480 unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
481 if (image_fd < 0) {
482 std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
483 return false;
484 }
485 unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
486 if (canonical_fd < 0) {
487 std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
488 return false;
489 }
490
491 struct stat s;
492 if (fstat(canonical_fd, &s)) {
493 std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
494 return false;
495 }
496 uint64_t canonical_size = s.st_size;
497 uint64_t md_size = get_block_device_size(md_fd);
498 uint64_t image_size = get_block_device_size(image_fd);
499 if (image_size != md_size) {
500 std::cerr << "image size does not match merge device size\n";
501 return false;
502 }
503 if (canonical_size > image_size) {
504 std::cerr << "canonical size " << canonical_size << " is greater than image size "
505 << image_size << "\n";
506 return false;
507 }
508
509 constexpr size_t kBlockSize = 4096;
510 uint8_t canonical_buffer[kBlockSize];
511 uint8_t image_buffer[kBlockSize];
512 uint8_t md_buffer[kBlockSize];
513
514 uint64_t remaining = canonical_size;
515 uint64_t blockno = 0;
516 while (remaining) {
517 size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
518 if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
519 std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
520 return false;
521 }
522 if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
523 std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
524 return false;
525 }
526 if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
527 std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
528 return false;
529 }
530 if (memcmp(canonical_buffer, image_buffer, bytes)) {
531 std::cerr << "canonical and image differ at block " << blockno << "\n";
532 return false;
533 }
534 if (memcmp(canonical_buffer, md_buffer, bytes)) {
535 std::cerr << "canonical and image differ at block " << blockno << "\n";
536 return false;
537 }
538
539 remaining -= bytes;
540 blockno++;
541 }
542
543 std::cout << "Images all match.\n";
544 return true;
545 }
546
547 } // namespace snapshot
548 } // namespace android
549
main(int argc,char ** argv)550 int main(int argc, char** argv) {
551 android::snapshot::PowerTest test;
552
553 if (!test.Run(argc, argv)) {
554 std::cerr << "Unexpected error running test." << std::endl;
555 return 1;
556 }
557 fflush(stdout);
558 return 0;
559 }
560