1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <time.h>
6
7#include <fbl/alloc_checker.h>
8#include <fbl/unique_ptr.h>
9
10#include "util.h"
11
12static unsigned count = 0;
13template <size_t WriteOffset, size_t ReadOffset, size_t WriteSize>
14bool test_sparse(void) {
15    BEGIN_TEST;
16
17    char filename[20];
18    sprintf(filename, "::my_file_%u", count++);
19
20    int fd = emu_open(filename, O_RDWR | O_CREAT, 0644);
21    ASSERT_GT(fd, 0);
22
23    // Create a random write buffer of data
24    fbl::AllocChecker ac;
25    fbl::unique_ptr<uint8_t[]> wbuf(new (&ac) uint8_t[WriteSize]);
26    ASSERT_EQ(ac.check(), true);
27    unsigned int seed = static_cast<unsigned int>(time(NULL));
28    unittest_printf("Sparse test using seed: %u\n", seed);
29    for (size_t i = 0; i < WriteSize; i++) {
30        wbuf[i] = (uint8_t) rand_r(&seed);
31    }
32
33    // Dump write buffer to file
34    ASSERT_EQ(emu_pwrite(fd, &wbuf[0], WriteSize, WriteOffset), WriteSize);
35    // Reopen file
36    ASSERT_EQ(emu_close(fd), 0);
37    fd = emu_open(filename, O_RDWR, 0644);
38    ASSERT_GT(fd, 0);
39
40    // Access read buffer from file
41    constexpr size_t kFileSize = WriteOffset + WriteSize;
42    constexpr size_t kBytesToRead = (kFileSize - ReadOffset) > WriteSize ?
43                                     WriteSize : (kFileSize - ReadOffset);
44    static_assert(kBytesToRead > 0, "We want to test writing AND reading");
45    fbl::unique_ptr<uint8_t[]> rbuf(new (&ac) uint8_t[kBytesToRead]);
46    ASSERT_EQ(ac.check(), true);
47    ASSERT_EQ(emu_pread(fd, &rbuf[0], kBytesToRead, ReadOffset), kBytesToRead);
48
49    constexpr size_t kSparseLength = (ReadOffset < WriteOffset) ?
50                                      WriteOffset - ReadOffset : 0;
51
52    if (kSparseLength > 0) {
53        for (size_t i = 0; i < kSparseLength; i++) {
54            ASSERT_EQ(rbuf[i], 0, "This portion of file should be sparse; but isn't");
55        }
56    }
57
58    constexpr size_t kWbufOffset = (ReadOffset < WriteOffset) ?
59                                    0 : ReadOffset - WriteOffset;
60    constexpr size_t kValidLength = kBytesToRead - kSparseLength;
61
62    if (kValidLength > 0) {
63        for (size_t i = 0; i < kValidLength; i++) {
64            ASSERT_EQ(rbuf[kSparseLength + i], wbuf[kWbufOffset + i]);
65        }
66    }
67
68    ASSERT_EQ(emu_close(fd), 0);
69    ASSERT_EQ(run_fsck(), 0);
70    END_TEST;
71}
72
73constexpr size_t kBlockSize = 8192;
74constexpr size_t kDirectBlocks = 16;
75
76RUN_MINFS_TESTS(sparse_tests,
77    RUN_TEST_MEDIUM((test_sparse<0, 0, kBlockSize>))
78    RUN_TEST_MEDIUM((test_sparse<kBlockSize / 2, 0, kBlockSize>))
79    RUN_TEST_MEDIUM((test_sparse<kBlockSize / 2, kBlockSize, kBlockSize>))
80    RUN_TEST_MEDIUM((test_sparse<kBlockSize, 0, kBlockSize>))
81    RUN_TEST_MEDIUM((test_sparse<kBlockSize, kBlockSize / 2, kBlockSize>))
82
83    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks,
84                                 kBlockSize * kDirectBlocks - kBlockSize,
85                                 kBlockSize * 2>))
86    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks,
87                                 kBlockSize * kDirectBlocks - kBlockSize,
88                                 kBlockSize * 32>))
89    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks + kBlockSize,
90                                 kBlockSize * kDirectBlocks - kBlockSize,
91                                 kBlockSize * 32>))
92    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks + kBlockSize,
93                                 kBlockSize * kDirectBlocks + 2 * kBlockSize,
94                                 kBlockSize * 32>))
95)
96