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 <fcntl.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <sys/stat.h>
10#include <unistd.h>
11
12#include <zircon/syscalls.h>
13#include <fbl/alloc_checker.h>
14#include <fbl/unique_ptr.h>
15#include <unittest/unittest.h>
16
17#include "filesystems.h"
18
19template <size_t WriteOffset, size_t ReadOffset, size_t WriteSize>
20bool test_sparse(void) {
21    BEGIN_TEST;
22
23    int fd = open("::my_file", O_RDWR | O_CREAT, 0644);
24    ASSERT_GT(fd, 0);
25
26    // Create a random write buffer of data
27    fbl::AllocChecker ac;
28    fbl::unique_ptr<uint8_t[]> wbuf(new (&ac) uint8_t[WriteSize]);
29    ASSERT_EQ(ac.check(), true);
30    unsigned int seed = static_cast<unsigned int>(zx_ticks_get());
31    unittest_printf("Sparse test using seed: %u\n", seed);
32    for (size_t i = 0; i < WriteSize; i++) {
33        wbuf[i] = (uint8_t) rand_r(&seed);
34    }
35
36    // Dump write buffer to file
37    ASSERT_EQ(pwrite(fd, &wbuf[0], WriteSize, WriteOffset), WriteSize);
38
39    // Reopen file
40    ASSERT_EQ(close(fd), 0);
41    fd = open("::my_file", O_RDWR, 0644);
42    ASSERT_GT(fd, 0);
43
44    // Access read buffer from file
45    constexpr size_t kFileSize = WriteOffset + WriteSize;
46    constexpr size_t kBytesToRead = (kFileSize - ReadOffset) > WriteSize ?
47                                     WriteSize : (kFileSize - ReadOffset);
48    static_assert(kBytesToRead > 0, "We want to test writing AND reading");
49    fbl::unique_ptr<uint8_t[]> rbuf(new (&ac) uint8_t[kBytesToRead]);
50    ASSERT_EQ(ac.check(), true);
51    ASSERT_EQ(pread(fd, &rbuf[0], kBytesToRead, ReadOffset), kBytesToRead);
52
53    constexpr size_t kSparseLength = (ReadOffset < WriteOffset) ?
54                                      WriteOffset - ReadOffset : 0;
55
56    if (kSparseLength > 0) {
57        for (size_t i = 0; i < kSparseLength; i++) {
58            ASSERT_EQ(rbuf[i], 0, "This portion of file should be sparse; but isn't");
59        }
60    }
61
62    constexpr size_t kWbufOffset = (ReadOffset < WriteOffset) ?
63                                    0 : ReadOffset - WriteOffset;
64    constexpr size_t kValidLength = kBytesToRead - kSparseLength;
65
66    if (kValidLength > 0) {
67        for (size_t i = 0; i < kValidLength; i++) {
68            ASSERT_EQ(rbuf[kSparseLength + i], wbuf[kWbufOffset + i]);
69        }
70    }
71
72    // Clean up
73    ASSERT_EQ(close(fd), 0);
74    ASSERT_EQ(unlink("::my_file"), 0);
75    END_TEST;
76}
77
78constexpr size_t kBlockSize = 8192;
79constexpr size_t kDirectBlocks = 16;
80
81RUN_FOR_ALL_FILESYSTEMS(sparse_tests,
82    RUN_TEST_MEDIUM((test_sparse<0, 0, kBlockSize>))
83    RUN_TEST_MEDIUM((test_sparse<kBlockSize / 2, 0, kBlockSize>))
84    RUN_TEST_MEDIUM((test_sparse<kBlockSize / 2, kBlockSize, kBlockSize>))
85    RUN_TEST_MEDIUM((test_sparse<kBlockSize, 0, kBlockSize>))
86    RUN_TEST_MEDIUM((test_sparse<kBlockSize, kBlockSize / 2, kBlockSize>))
87
88    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks,
89                                 kBlockSize * kDirectBlocks - kBlockSize,
90                                 kBlockSize * 2>))
91    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks,
92                                 kBlockSize * kDirectBlocks - kBlockSize,
93                                 kBlockSize * 32>))
94    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks + kBlockSize,
95                                 kBlockSize * kDirectBlocks - kBlockSize,
96                                 kBlockSize * 32>))
97    RUN_TEST_MEDIUM((test_sparse<kBlockSize * kDirectBlocks + kBlockSize,
98                                 kBlockSize * kDirectBlocks + 2 * kBlockSize,
99                                 kBlockSize * 32>))
100)
101