• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 /*
19  * These file system recovery tests ensure the ability to recover from
20  * filesystem crashes in key blocks (e.g. superblock).
21  */
22 #include <assert.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <fs_mgr.h>
26 #include <gtest/gtest.h>
27 #include <logwrap/logwrap.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 #include <string>
32 
33 #include "cutils/properties.h"
34 #include <ext4_utils/ext4.h>
35 #include <ext4_utils/ext4_utils.h>
36 
37 #define LOG_TAG "fsRecoveryTest"
38 #include <utils/Log.h>
39 #include <testUtil.h>
40 
41 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
42 #define SB_OFFSET 1024
43 static char UMOUNT_BIN[] = "/system/bin/umount";
44 static char VDC_BIN[] = "/system/bin/vdc";
45 
46 enum Fs_Type { FS_UNKNOWN, FS_EXT4, FS_F2FS };
47 
48 namespace android {
49 
50 class DataFileVerifier {
51  public:
DataFileVerifier(const char * file_name)52   explicit DataFileVerifier(const char* file_name) {
53     strncpy(test_file_, file_name, FILENAME_MAX);
54   }
55 
verify_write()56   void verify_write() {
57     int write_fd = open(test_file_, O_CREAT | O_WRONLY, 0666);
58     ASSERT_TRUE(write_fd);
59     ASSERT_EQ(write(write_fd, "TEST", 4), 4);
60     close(write_fd);
61   }
62 
verify_read()63   void verify_read() {
64     char read_buff[4];
65     int read_fd = open(test_file_, O_RDONLY);
66     ASSERT_TRUE(read_fd);
67     ASSERT_EQ(read(read_fd, read_buff, sizeof(read_buff)), 4);
68     ASSERT_FALSE(strncmp(read_buff, "TEST", 4));
69     close(read_fd);
70   }
71 
~DataFileVerifier()72   ~DataFileVerifier() {
73     unlink(test_file_);
74   }
75 
76  private:
77   char test_file_[FILENAME_MAX];
78 };
79 
80 namespace ext4 {
getSuperBlock(const int blk_fd,struct ext4_super_block * sb)81 bool getSuperBlock(const int blk_fd, struct ext4_super_block* sb) {
82   if (lseek(blk_fd, SB_OFFSET, SEEK_SET) == -1) {
83     testPrintE("Cannot lseek to ext4 superblock to read");
84     return false;
85   }
86 
87   if (read(blk_fd, sb, sizeof(*sb)) != sizeof(*sb)) {
88     testPrintE("Cannot read ext4 superblock");
89     return false;
90   }
91 
92   if (sb->s_magic != 0xEF53) {
93     testPrintE("Invalid ext4 superblock magic");
94     return false;
95   }
96 
97   return true;
98 }
99 
setSbErrorBit(const int blk_fd)100 bool setSbErrorBit(const int blk_fd) {
101   // Read super block.
102   struct ext4_super_block sb;
103   if (!getSuperBlock(blk_fd, &sb)) {
104     return false;
105   }
106 
107   // Check that the detected errors bit is not set.
108   if (sb.s_state & 0x2) {
109     testPrintE("Ext4 superblock already corrupted");
110     return false;
111   }
112 
113   // Set the detected errors bit.
114   sb.s_state |= 0x2;
115 
116   // Write superblock.
117   if (lseek(blk_fd, SB_OFFSET, SEEK_SET) == -1) {
118       testPrintE("Cannot lseek to superblock to write\n");
119       return false;
120   }
121 
122   if (write(blk_fd, &sb, sizeof(sb)) != sizeof(sb)) {
123       testPrintE("Cannot write superblock\n");
124       return false;
125   }
126 
127   return true;
128 }
129 
corruptGdtFreeBlock(const int blk_fd)130 bool corruptGdtFreeBlock(const int blk_fd) {
131   // Read super block.
132   struct ext4_super_block sb;
133   if (!getSuperBlock(blk_fd, &sb)) {
134     return false;
135   }
136   // Make sure the block size is 2K or 4K.
137   if ((sb.s_log_block_size != 1) && (sb.s_log_block_size != 2)) {
138       testPrintE("Ext4 block size not 2K or 4K\n");
139       return false;
140   }
141   int block_size = 1 << (10 + sb.s_log_block_size);
142   int num_bgs = DIV_ROUND_UP(sb.s_blocks_count_lo, sb.s_blocks_per_group);
143 
144   if (sb.s_desc_size != sizeof(struct ext2_group_desc)) {
145     testPrintE("Can't handle ext4 block group descriptor size of %d",
146                sb.s_desc_size);
147     return false;
148   }
149 
150   // Read first block group descriptor, decrement free block count, and
151   // write it back out.
152   if (lseek(blk_fd, block_size, SEEK_SET) == -1) {
153     testPrintE("Cannot lseek to ext4 block group descriptor table to read");
154     return false;
155   }
156 
157   // Read in block group descriptors till we read one that has at least one free
158   // block.
159   struct ext2_group_desc gd;
160   for (int i = 0; i < num_bgs; i++) {
161     if (read(blk_fd, &gd, sizeof(gd)) != sizeof(gd)) {
162       testPrintE("Cannot read ext4 group descriptor %d", i);
163       return false;
164     }
165     if (gd.bg_free_blocks_count) {
166       break;
167     }
168   }
169 
170   gd.bg_free_blocks_count--;
171 
172   if (lseek(blk_fd, -sizeof(gd), SEEK_CUR) == -1) {
173     testPrintE("Cannot lseek to ext4 block group descriptor table to write");
174     return false;
175   }
176 
177   if (write(blk_fd, &gd, sizeof(gd)) != sizeof(gd)) {
178     testPrintE("Cannot write modified ext4 group descriptor");
179     return false;
180   }
181   return true;
182 }
183 
184 }  // namespace ext4
185 
186 class FsRecoveryTest : public ::testing::Test {
187  protected:
FsRecoveryTest()188   FsRecoveryTest() : fs_type(FS_UNKNOWN), blk_fd_(-1) {}
189 
setCacheInfoFromFstab()190   bool setCacheInfoFromFstab() {
191     fs_type = FS_UNKNOWN;
192 
193     android::fs_mgr::Fstab fstab;
194     if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
195       testPrintE("failed to open default fstab\n");
196     } else {
197       // Loop through entries looking for cache.
198       for (const auto& entry : fstab) {
199         if (entry.mount_point == "/cache") {
200           blk_path_ = entry.blk_device;
201           if (entry.fs_type == "ext4") {
202             fs_type = FS_EXT4;
203             break;
204           } else if (entry.fs_type == "f2fs") {
205             fs_type = FS_F2FS;
206             break;
207           }
208         }
209       }
210     }
211     return fs_type != FS_UNKNOWN;
212   }
213 
unmountCache()214   bool unmountCache() {
215     char cache_str[] = "/cache";
216     char *umount_argv[] = {
217       UMOUNT_BIN,
218       cache_str,
219     };
220     return logwrap_fork_execvp(ARRAY_SIZE(umount_argv), umount_argv, nullptr,
221                                false, LOG_KLOG, false, nullptr) >= 0;
222   }
223 
mountAll()224   bool mountAll() {
225     char storage_str[] = "storage";
226     char mountall_str[] = "mountall";
227     char *mountall_argv[] = {
228       VDC_BIN,
229       storage_str,
230       mountall_str,
231     };
232     return logwrap_fork_execvp(ARRAY_SIZE(mountall_argv), mountall_argv, nullptr,
233                                false, LOG_KLOG, false, nullptr) >= 0;
234   }
235 
getCacheBlkFd()236   int getCacheBlkFd() {
237     if (blk_fd_ == -1) {
238       blk_fd_ = open(blk_path_.c_str(), O_RDWR);
239     }
240     return blk_fd_;
241   }
242 
closeCacheBlkFd()243   void closeCacheBlkFd() {
244     if (blk_fd_ > -1) {
245       close(blk_fd_);
246     }
247     blk_fd_ = -1;
248   }
249 
assertCacheHealthy()250   void assertCacheHealthy() {
251     const char* test_file = "/cache/FsRecoveryTestGarbage.txt";
252     DataFileVerifier file_verify(test_file);
253     file_verify.verify_write();
254     file_verify.verify_read();
255   }
256 
SetUp()257   virtual void SetUp() {
258     assertCacheHealthy();
259     ASSERT_TRUE(setCacheInfoFromFstab());
260   }
261 
TearDown()262   virtual void TearDown() {
263     // Ensure /cache partition is accessible, mounted and healthy for other
264     // tests.
265     closeCacheBlkFd();
266     ASSERT_TRUE(mountAll());
267     assertCacheHealthy();
268   }
269 
270   Fs_Type fs_type;
271 
272  private:
273   std::string blk_path_;
274   int blk_fd_;
275 };
276 
TEST_F(FsRecoveryTest,EXT4_CorruptGdt)277 TEST_F(FsRecoveryTest, EXT4_CorruptGdt) {
278   if (fs_type != FS_EXT4) {
279     return;
280   }
281   // Setup test file in /cache.
282   const char* test_file = "/cache/CorruptGdtGarbage.txt";
283   DataFileVerifier file_verify(test_file);
284   file_verify.verify_write();
285   // Unmount and corrupt /cache gdt.
286   ASSERT_TRUE(unmountCache());
287   ASSERT_TRUE(ext4::corruptGdtFreeBlock(getCacheBlkFd()));
288   closeCacheBlkFd();
289   ASSERT_TRUE(mountAll());
290 
291   // Verify results.
292   file_verify.verify_read();
293 }
294 
TEST_F(FsRecoveryTest,EXT4_SetErrorBit)295 TEST_F(FsRecoveryTest, EXT4_SetErrorBit) {
296   if (fs_type != FS_EXT4) {
297     return;
298   }
299   // Setup test file in /cache.
300   const char* test_file = "/cache/ErrorBitGarbagetxt";
301   DataFileVerifier file_verify(test_file);
302   file_verify.verify_write();
303 
304   // Unmount and set /cache super block error bit.
305   ASSERT_TRUE(unmountCache());
306   ASSERT_TRUE(ext4::setSbErrorBit(getCacheBlkFd()));
307   closeCacheBlkFd();
308   ASSERT_TRUE(mountAll());
309 
310   // Verify results.
311   file_verify.verify_read();
312   struct ext4_super_block sb;
313   ASSERT_TRUE(ext4::getSuperBlock(getCacheBlkFd(), &sb));
314   // Verify e2fsck has recovered the error bit of sb.
315   ASSERT_FALSE(sb.s_state & 0x2);
316 }
317 }  // namespace android
318