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