• 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 "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