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