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