1 /*
2  * Copyright (C) 2018 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 requied 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 
18 #define LOG_TAG "BowTest"
19 
20 #include <fstream>
21 #include <string>
22 
23 #include <assert.h>
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <linux/fs.h>
27 #include <linux/loop.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #include <android-base/stringprintf.h>
35 #include <android-base/unique_fd.h>
36 #include <gtest/gtest.h>
37 #include <libdm/dm.h>
38 #include <utils/Log.h>
39 
40 namespace android {
41 
42 using base::unique_fd;
43 using namespace dm;
44 
blockCheckpointsSupported()45 bool blockCheckpointsSupported() {
46   static bool supported = false;
47   static bool evaluated = false;
48 
49   if (evaluated) return supported;
50 
51   pid_t pid = fork();
52   EXPECT_NE(pid, -1);
53 
54   if (pid == 0) {
55     static const char* args[] = {"/system/bin/vdc", "checkpoint",
56                                  "supportsBlockCheckpoint", 0};
57     EXPECT_NE(execv(args[0], const_cast<char* const*>(args)), -1);
58   }
59 
60   int status;
61   EXPECT_NE(waitpid(pid, &status, 0), -1);
62 
63   supported = status == 1;
64   evaluated = true;
65   return supported;
66 }
67 
68 template <void (*Prepare)(std::string)>
69 class LoopbackTestFixture : public ::testing::Test {
70  protected:
SetUp()71   void SetUp() {
72     Prepare(loop_file_);
73 
74     // Get free loop device name
75     unique_fd cfd(open("/dev/loop-control", O_RDWR));
76     ASSERT_NE(cfd.get(), -1);
77     int i = ioctl(cfd, 0x4C82);  // LOOP_CTL_GET_FREE
78     ASSERT_GE(i, 0);
79     loop_device_ = std::string("/dev/block/loop") + std::to_string(i);
80 
81     // Associate loop device with file
82     unique_fd lfd(open(loop_device_.c_str(), O_RDWR));
83     ASSERT_NE(lfd.get(), -1);
84     unique_fd ffd(open(loop_file_.c_str(), O_RDWR));
85     ASSERT_NE(ffd.get(), -1);
86     ASSERT_EQ(ioctl(lfd.get(), LOOP_SET_FD, ffd.get()), 0);
87   }
88 
TearDown()89   void TearDown() {
90     unique_fd lfd(open(loop_device_.c_str(), O_RDWR));
91     EXPECT_NE(lfd.get(), -1);
92     EXPECT_EQ(ioctl(lfd, LOOP_CLR_FD, 0), 0);
93     EXPECT_EQ(remove(loop_file_.c_str()), 0);
94   }
95 
96   const static std::string loop_file_;
97 
98  public:
99   const static size_t sector_size_ = 512;
100   const static size_t loop_size_ = 4096 * sector_size_;
101   std::string loop_device_;
102 };
103 
104 template <void (*Prepare)(std::string)>
105 const std::string LoopbackTestFixture<Prepare>::loop_file_ =
106     "/data/local/tmp/bow_loop";
107 
PrepareBowDefault(std::string)108 void PrepareBowDefault(std::string) {}
109 
110 template <void (*PrepareLoop)(std::string),
111           void (*PrepareBow)(std::string) = PrepareBowDefault>
112 class BowTestFixture : public LoopbackTestFixture<PrepareLoop> {
GetTableStatus()113   std::string GetTableStatus() {
114     std::vector<DeviceMapper::TargetInfo> targets;
115     DeviceMapper& dm = DeviceMapper::Instance();
116     EXPECT_TRUE(dm.GetTableInfo("bow1", &targets));
117     EXPECT_EQ(targets.size(), 1);
118     return targets[0].data;
119   }
120 
121   bool torn_down_;
122 
123  protected:
SetUp()124   void SetUp() {
125     if (!blockCheckpointsSupported()) return;
126 
127     LoopbackTestFixture<PrepareLoop>::SetUp();
128     PrepareBow(loop_device_);
129 
130     torn_down_ = false;
131 
132     DmTable table;
133     table.AddTarget(std::make_unique<DmTargetBow>(0, loop_size_ / 512,
134                                                   loop_device_.c_str()));
135 
136     DeviceMapper& dm = DeviceMapper::Instance();
137     ASSERT_TRUE(dm.CreateDevice("bow1", table));
138     ASSERT_TRUE(dm.GetDmDevicePathByName("bow1", &bow_device_));
139   }
140 
TearDown()141   void TearDown() {
142     if (!blockCheckpointsSupported()) return;
143     BowTearDown();
144     LoopbackTestFixture<PrepareLoop>::TearDown();
145   }
146 
TornDown() const147   bool TornDown() const { return torn_down_; }
148 
149  public:
150   using LoopbackTestFixture<PrepareLoop>::loop_size_;
151   using LoopbackTestFixture<PrepareLoop>::sector_size_;
152   using LoopbackTestFixture<PrepareLoop>::loop_device_;
153 
BowTearDown()154   void BowTearDown() {
155     if (torn_down_) return;
156     torn_down_ = true;
157     EXPECT_TRUE(DeviceMapper::Instance().DeleteDevice("bow1"));
158   }
159 
SetState(int i)160   void SetState(int i) {
161     std::string state_file = "/sys" + bow_device_.substr(4) + "/bow/state";
162     std::ofstream(state_file) << i;
163 
164     int j;
165     std::ifstream(state_file) >> j;
166     EXPECT_EQ(i, j);
167   }
168 
169   enum SectorTypes {
170     INVALID,
171     SECTOR0,
172     SECTOR0_CURRENT,
173     UNCHANGED,
174     BACKUP,
175     FREE,
176     CHANGED,
177     TOP
178   };
179 
180   struct TableEntry {
181     SectorTypes type;
182     uint64_t offset;
183 
operator ==android::BowTestFixture::TableEntry184     bool operator==(const TableEntry& te) const {
185       return type == te.type && offset == te.offset;
186     }
187   };
188 
GetTable()189   std::vector<TableEntry> GetTable() {
190     std::string status = GetTableStatus();
191     std::istringstream i(status);
192     std::vector<TableEntry> table;
193     while (true) {
194       TableEntry te = {};
195       std::string s;
196       i >> s >> te.offset;
197       if (!i) {
198         EXPECT_EQ(s, "");
199         break;
200       }
201 
202       if (s == "Sector0:")
203         te.type = SECTOR0;
204       else if (s == "Sector0_current:")
205         te.type = SECTOR0_CURRENT;
206       else if (s == "Unchanged:")
207         te.type = UNCHANGED;
208       else if (s == "Backup:")
209         te.type = BACKUP;
210       else if (s == "Free:")
211         te.type = FREE;
212       else if (s == "Changed:")
213         te.type = CHANGED;
214       else if (s == "Top:")
215         te.type = TOP;
216       else
217         ADD_FAILURE();
218 
219       te.offset /= sector_size_ / 512;
220 
221       table.push_back(te);
222     }
223     return table;
224   }
225 
226   std::string bow_device_;
227 };
228 
PrepareFile(std::string loop_file)229 void PrepareFile(std::string loop_file) {
230   auto const& sector_size_ = LoopbackTestFixture<PrepareFile>::sector_size_;
231   auto const& loop_size_ = LoopbackTestFixture<PrepareFile>::loop_size_;
232 
233   unique_fd fd(
234       open(loop_file.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
235   ASSERT_NE(fd.get(), -1);
236   for (int i = 0; i < loop_size_ / sector_size_; ++i) {
237     char buffer[sector_size_] = {};
238     snprintf(buffer, sizeof(buffer), "Sector %d", i);
239     write(fd.get(), buffer, sizeof(buffer));
240   }
241 }
242 
243 class FileBowTestFixture : public BowTestFixture<&PrepareFile> {
SetUp()244   void SetUp() {
245     if (!blockCheckpointsSupported()) return;
246     BowTestFixture<&PrepareFile>::SetUp();
247     fd_ = unique_fd(open(bow_device_.c_str(), O_RDWR));
248     ASSERT_NE(fd_.get(), -1);
249   }
250 
TearDown()251   void TearDown() {
252     fd_ = unique_fd();
253     BowTestFixture<&PrepareFile>::TearDown();
254   }
255 
256   unique_fd fd_;
257 
258  public:
Discard(uint64_t offset,uint64_t length)259   void Discard(uint64_t offset, uint64_t length) {
260     uint64_t range[2] = {offset * sector_size_, length * sector_size_};
261     EXPECT_EQ(ioctl(fd_.get(), BLKDISCARD, range), 0);
262   }
263 
Write(SectorTypes type)264   int Write(SectorTypes type) {
265     for (auto i : GetTable())
266       if (i.type == type) {
267         EXPECT_NE(lseek(fd_, i.offset * sector_size_, SEEK_SET), -1);
268         EXPECT_EQ(write(fd_, "Changed", 8), 8);
269         return i.offset;
270       }
271     EXPECT_TRUE(false);
272     return -1;
273   }
274 
FindChanged(std::vector<TableEntry> const & free,int expected_changed)275   void FindChanged(std::vector<TableEntry> const& free, int expected_changed) {
276     unique_fd fd;
277     if (TornDown())
278       fd = unique_fd(open(loop_device_.c_str(), O_RDONLY));
279     else
280       fd = unique_fd(open(bow_device_.c_str(), O_RDONLY));
281     EXPECT_NE(fd.get(), -1);
282     if (fd.get() == -1) return;
283 
284     int changed = -1;
285     for (int i = 0; i < loop_size_ / sector_size_; ++i) {
286       if (i != expected_changed) {
287         auto type = SECTOR0;
288         for (auto j : free)
289           if (j.offset > i)
290             break;
291           else
292             type = j.type;
293         if (type == FREE) continue;
294       }
295 
296       char buffer[sector_size_];
297       EXPECT_NE(lseek(fd.get(), i * sector_size_, SEEK_SET), -1);
298       EXPECT_EQ(read(fd.get(), buffer, sizeof(buffer)), sizeof(buffer));
299       if (strcmp(buffer, "Changed") == 0) {
300         EXPECT_EQ(changed, -1);
301         changed = i;
302       } else {
303         std::string expected = "Sector " + std::to_string(i);
304         EXPECT_STREQ(buffer, expected.c_str());
305       }
306     }
307     EXPECT_EQ(changed, expected_changed);
308   }
309 
DumpSector0()310   void DumpSector0() {
311     char buffer[sector_size_];
312     lseek(fd_.get(), 0, SEEK_SET);
313     read(fd_.get(), buffer, sizeof(buffer));
314     for (int i = 0; i < sector_size_ * 2; ++i) {
315       std::cout << std::hex << std::setw(2) << std::setfill('0')
316                 << (int)buffer[i];
317       if (i % 32 == 31)
318         std::cout << std::endl;
319       else
320         std::cout << " ";
321     }
322   }
323 
WriteSubmit(SectorTypes type)324   void WriteSubmit(SectorTypes type) {
325     if (!blockCheckpointsSupported()) return;
326     Discard(1, 1);
327     Discard(3, 2);
328     auto free = GetTable();
329 
330     SetState(1);
331     auto changed = Write(type);
332 
333     SetState(2);
334     FindChanged(free, changed);
335   }
336 
WriteRestore(SectorTypes type)337   void WriteRestore(SectorTypes type) {
338     if (!blockCheckpointsSupported()) return;
339     Discard(1, 1);
340     Discard(3, 2);
341     auto free = GetTable();
342     SetState(1);
343     Write(type);
344     BowTearDown();
345     system(std::string("vdc checkpoint restoreCheckpoint " + loop_device_)
346                .c_str());
347     FindChanged(free, -1);
348   }
349 };
350 
TEST_F(FileBowTestFixture,discardVisible)351 TEST_F(FileBowTestFixture, discardVisible) {
352   if (!blockCheckpointsSupported()) return;
353   Discard(8, 1);
354   Discard(16, 1);
355   Discard(12, 1);
356   Discard(4, 1);
357 
358   std::vector<TableEntry> table = {
359       {UNCHANGED, 0},  {FREE, 4},
360       {UNCHANGED, 5},  {FREE, 8},
361       {UNCHANGED, 9},  {FREE, 12},
362       {UNCHANGED, 13}, {FREE, 16},
363       {UNCHANGED, 17}, {TOP, loop_size_ / sector_size_},
364   };
365 
366   EXPECT_EQ(GetTable(), table);
367 }
368 
TEST_F(FileBowTestFixture,writeSector0Submit)369 TEST_F(FileBowTestFixture, writeSector0Submit) {
370   SCOPED_TRACE("write submit SECTOR0");
371   WriteSubmit(SECTOR0);
372 }
373 
TEST_F(FileBowTestFixture,writeSector0Revert)374 TEST_F(FileBowTestFixture, writeSector0Revert) {
375   SCOPED_TRACE("write restore SECTOR0");
376   WriteRestore(SECTOR0);
377 }
378 
TEST_F(FileBowTestFixture,writeSector0_CurrentSubmit)379 TEST_F(FileBowTestFixture, writeSector0_CurrentSubmit) {
380   SCOPED_TRACE("write submit SECTOR0_CURRENT");
381   WriteSubmit(SECTOR0_CURRENT);
382 }
383 
TEST_F(FileBowTestFixture,writeSector0_CurrentRevert)384 TEST_F(FileBowTestFixture, writeSector0_CurrentRevert) {
385   SCOPED_TRACE("write restore SECTOR0_CURRENT");
386   WriteRestore(SECTOR0_CURRENT);
387 }
388 
TEST_F(FileBowTestFixture,writeUnchangedSubmit)389 TEST_F(FileBowTestFixture, writeUnchangedSubmit) {
390   SCOPED_TRACE("write submit UNCHANGED");
391   WriteSubmit(UNCHANGED);
392 }
393 
TEST_F(FileBowTestFixture,writeUnchangedRevert)394 TEST_F(FileBowTestFixture, writeUnchangedRevert) {
395   SCOPED_TRACE("write restore UNCHANGED");
396   WriteRestore(UNCHANGED);
397 }
398 
TEST_F(FileBowTestFixture,writeBackupSubmit)399 TEST_F(FileBowTestFixture, writeBackupSubmit) {
400   SCOPED_TRACE("write submit BACKUP");
401   WriteSubmit(BACKUP);
402 }
403 
TEST_F(FileBowTestFixture,writeBackupRevert)404 TEST_F(FileBowTestFixture, writeBackupRevert) {
405   SCOPED_TRACE("write restore BACKUP");
406   WriteRestore(BACKUP);
407 }
408 
TEST_F(FileBowTestFixture,writeFreeSubmit)409 TEST_F(FileBowTestFixture, writeFreeSubmit) {
410   SCOPED_TRACE("write submit FREE");
411   WriteSubmit(FREE);
412 }
413 
TEST_F(FileBowTestFixture,writeFreeRevert)414 TEST_F(FileBowTestFixture, writeFreeRevert) {
415   SCOPED_TRACE("write restore FREE");
416   WriteRestore(FREE);
417 }
418 
419 /* There are no changed sectors at start, so these can't work as is
420 TEST_F(BowTestFixture, writeChangedSubmit) {
421     SCOPED_TRACE("write submit CHANGED");
422     WriteSubmit(CHANGED);
423 }
424 
425 TEST_F(BowTestFixture, writeChangedRevert) {
426     SCOPED_TRACE("write restore CHANGED");
427     WriteRestore(CHANGED);
428 }
429 */
430 
PrepareFileSystem(std::string loop_file)431 void PrepareFileSystem(std::string loop_file) {
432   EXPECT_EQ(
433       system((std::string("dd if=/dev/zero bs=512 count=4096 of=") + loop_file)
434                  .c_str()),
435       0);
436 }
437 
438 #define MOUNT_POINT "/data/local/tmp/mount"
439 
SetupFileSystem(std::string loop_device)440 void SetupFileSystem(std::string loop_device) {
441   EXPECT_EQ(system((std::string("mke2fs ") + loop_device).c_str()), 0);
442   EXPECT_EQ(system("mkdir " MOUNT_POINT), 0);
443   EXPECT_EQ(
444       system((std::string("mount ") + loop_device + " " MOUNT_POINT).c_str()),
445       0);
446   EXPECT_EQ(system("echo Original > " MOUNT_POINT "/file"), 0);
447   EXPECT_EQ(system("umount -D " MOUNT_POINT), 0);
448   EXPECT_EQ(system("rmdir " MOUNT_POINT), 0);
449 }
450 
Trim()451 void Trim() {
452   unique_fd fd(open(MOUNT_POINT, O_RDONLY));
453   EXPECT_NE(fd.get(), -1);
454   struct fstrim_range range = {};
455   range.len = ULLONG_MAX;
456   EXPECT_EQ(ioctl(fd, FITRIM, &range), 0);
457 }
458 
459 typedef BowTestFixture<&PrepareFileSystem, &SetupFileSystem>
460     FileSystemBowTestFixture;
461 
TEST_F(FileSystemBowTestFixture,filesystemSubmit)462 TEST_F(FileSystemBowTestFixture, filesystemSubmit) {
463   if (!blockCheckpointsSupported()) return;
464   EXPECT_EQ(system("mkdir " MOUNT_POINT), 0);
465   EXPECT_EQ(
466       system((std::string("mount ") + bow_device_ + " " MOUNT_POINT).c_str()),
467       0);
468   Trim();
469   SetState(1);
470   EXPECT_EQ(system("echo Changed > " MOUNT_POINT "/file"), 0);
471   SetState(2);
472   EXPECT_EQ(system("umount -D " MOUNT_POINT), 0);
473   BowTearDown();
474   EXPECT_EQ(
475       system((std::string("mount ") + loop_device_ + " " MOUNT_POINT).c_str()),
476       0);
477   std::string contents;
478   std::ifstream(MOUNT_POINT "/file") >> contents;
479   EXPECT_EQ(contents, std::string("Changed"));
480   EXPECT_EQ(system("umount -D " MOUNT_POINT), 0);
481   EXPECT_EQ(system("rmdir " MOUNT_POINT), 0);
482 }
483 
TEST_F(FileSystemBowTestFixture,filesystemRevert)484 TEST_F(FileSystemBowTestFixture, filesystemRevert) {
485   if (!blockCheckpointsSupported()) return;
486   EXPECT_EQ(system("mkdir " MOUNT_POINT), 0);
487   EXPECT_EQ(
488       system((std::string("mount ") + bow_device_ + " " MOUNT_POINT).c_str()),
489       0);
490   Trim();
491   SetState(1);
492   EXPECT_EQ(system("echo Changed > " MOUNT_POINT "/file"), 0);
493   EXPECT_EQ(system("umount -D " MOUNT_POINT), 0);
494   BowTearDown();
495   system((std::string("vdc checkpoint restoreCheckpoint ") + loop_device_)
496              .c_str());
497   EXPECT_EQ(
498       system((std::string("mount ") + loop_device_ + " " MOUNT_POINT).c_str()),
499       0);
500   std::string contents;
501   std::ifstream(MOUNT_POINT "/file") >> contents;
502   EXPECT_EQ(contents, std::string("Original"));
503   EXPECT_EQ(system("umount -D " MOUNT_POINT), 0);
504   EXPECT_EQ(system("rmdir " MOUNT_POINT), 0);
505 }
506 
507 }  // namespace android
508