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
41static char UMOUNT_BIN[] = "/system/bin/umount";
42static char VDC_BIN[] = "/system/bin/vdc";
43
44enum Fs_Type { FS_UNKNOWN, FS_EXT4, FS_F2FS };
45
46namespace android {
47
48class 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
78namespace ext4 {
79bool 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
98bool 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
128bool 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
184class 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
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