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#define UMOUNT_BIN "/system/bin/umount" 43#define VDC_BIN "/system/bin/vdc" 44 45enum Fs_Type { FS_UNKNOWN, FS_EXT4, FS_F2FS }; 46 47namespace android { 48 49class DataFileVerifier { 50 public: 51 DataFileVerifier(const char* file_name) { 52 strncpy(test_file_, file_name, FILENAME_MAX); 53 } 54 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 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 71 ~DataFileVerifier() { 72 unlink(test_file_); 73 } 74 75 private: 76 char test_file_[FILENAME_MAX]; 77}; 78 79namespace ext4 { 80bool 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 99bool 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 129bool 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 185class FsRecoveryTest : public ::testing::Test { 186 protected: 187 FsRecoveryTest() : fs_type(FS_UNKNOWN), blk_fd_(-1) {} 188 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 218 bool unmountCache() { 219 char *umount_argv[] = { 220 UMOUNT_BIN, 221 "/cache" 222 }; 223 int status; 224 return android_fork_execvp_ext(ARRAY_SIZE(umount_argv), umount_argv, 225 NULL, true, LOG_KLOG, false, NULL) >= 0; 226 } 227 228 bool mountAll() { 229 char *mountall_argv[] = { 230 VDC_BIN, 231 "storage", 232 "mountall" 233 }; 234 int status; 235 return android_fork_execvp_ext(ARRAY_SIZE(mountall_argv), mountall_argv, 236 NULL, true, LOG_KLOG, false, NULL) >= 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 280TEST_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 298TEST_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