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