1 /*
2 * Copyright (c) 2023 Antmicro
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/fs/fs.h>
9 #include <zephyr/fs/ext2.h>
10 #include "utils.h"
11 #include "../../common/test_fs_util.h"
12
calculate_blocks(uint32_t freeb,uint32_t B)13 uint32_t calculate_blocks(uint32_t freeb, uint32_t B)
14 {
15 uint32_t blocks = 0;
16
17 if (freeb <= 12) {
18 return freeb;
19 }
20
21 blocks += 12;
22 freeb -= 12 + 1; /* direct blocks + top block of first level table */
23
24 if (freeb <= B) {
25 return blocks + freeb;
26 }
27
28 blocks += B;
29 freeb -= B + 1; /* 1st level blocks + top block of second level table */
30
31 if (freeb <= B * (B + 1)) {
32 uint32_t n = freeb / (B + 1);
33 uint32_t r = freeb % (B + 1);
34 uint32_t partial = r > 0 ? 1 : 0;
35
36 return blocks + n * B + r - partial;
37 }
38 return blocks;
39 /* TODO: revisit this and extend when 3rd level blocks will be possible */
40 }
41
write_to_file(const char * file_path,uint32_t bytes_to_write)42 static void write_to_file(const char *file_path, uint32_t bytes_to_write)
43 {
44 int64_t ret = 0;
45 struct fs_file_t file;
46 struct fs_dirent entry;
47
48 fs_file_t_init(&file);
49 ret = fs_open(&file, file_path, FS_O_RDWR | FS_O_CREATE);
50 zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
51
52 ret = testfs_write_incrementing(&file, 0, bytes_to_write);
53 zassert_equal(ret, bytes_to_write, "Different number of bytes written %ld (expected %ld)",
54 ret, bytes_to_write);
55
56 ret = fs_close(&file);
57 zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
58
59
60 /* Check file size */
61 ret = fs_stat(file_path, &entry);
62 zassert_equal(ret, 0, "File stat failed (ret=%d)", ret);
63 zassert_equal(entry.size, bytes_to_write,
64 "Wrong file size %d (expected %d)", entry.size,
65 bytes_to_write);
66
67 fs_file_t_init(&file);
68 ret = fs_open(&file, file_path, FS_O_READ);
69 zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
70
71
72 ret = testfs_verify_incrementing(&file, 0, bytes_to_write);
73 zassert_equal(ret, bytes_to_write, "Different number of bytes read %ld (expected %ld)",
74 ret, bytes_to_write);
75
76 ret = fs_close(&file);
77 zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
78 }
79
truncate_file(const char * file_path,uint32_t new_size)80 static void truncate_file(const char *file_path, uint32_t new_size)
81 {
82 int64_t ret = 0;
83 struct fs_file_t file;
84 struct fs_dirent entry;
85
86 fs_file_t_init(&file);
87
88 TC_PRINT("Truncating to %d\n", new_size);
89
90 ret = fs_open(&file, file_path, FS_O_RDWR);
91 zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
92
93 ret = fs_truncate(&file, new_size);
94 zassert_equal(ret, 0, "File truncate failed (ret=%d)", ret);
95
96 ret = fs_stat(file_path, &entry);
97 zassert_equal(ret, 0, "File stat failed (ret=%d)", ret);
98 zassert_equal(entry.size, new_size,
99 "Wrong file size %d (expected %d)", entry.size, new_size);
100
101 ret = fs_seek(&file, 0, FS_SEEK_SET);
102 zassert_equal(ret, 0, "File seek failed (ret=%d)", ret);
103
104 ret = testfs_verify_incrementing(&file, 0, new_size);
105 zassert_equal(ret, new_size, "Different number of bytes read %ld (expected %ld)",
106 ret, new_size);
107
108 ret = fs_close(&file);
109 zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
110 }
111
writing_test(struct ext2_cfg * config)112 void writing_test(struct ext2_cfg *config)
113 {
114 int64_t ret = 0;
115 struct fs_statvfs sbuf;
116 struct fs_mount_t *mp = &testfs_mnt;
117 static const char *file_path = "/sml/file";
118
119 ret = fs_mkfs(FS_EXT2, (uintptr_t)mp->storage_dev, config, 0);
120 zassert_equal(ret, 0, "Failed to mkfs with 2K blocks");
121
122 mp->flags = FS_MOUNT_FLAG_NO_FORMAT;
123 ret = fs_mount(mp);
124 zassert_equal(ret, 0, "Mount failed (ret=%d)", ret);
125
126 ret = fs_statvfs(mp->mnt_point, &sbuf);
127 zassert_equal(ret, 0, "Expected success (ret=%d)", ret);
128
129 /* Calculate how many numbers will be written (use all available memory) */
130 uint32_t freeb = sbuf.f_bfree;
131 uint32_t bsize = sbuf.f_bsize;
132 uint32_t available_blocks = calculate_blocks(freeb, bsize / sizeof(uint32_t));
133
134 uint32_t bytes_to_write = bsize * available_blocks;
135
136 TC_PRINT("Available blocks: %d\nBlock size: %d\nBytes_to_write: %d\n",
137 available_blocks, bsize, bytes_to_write);
138
139 write_to_file(file_path, bytes_to_write);
140
141 uint32_t new_size = bytes_to_write;
142
143 while (new_size > 1) {
144 new_size = new_size / 8 * 3;
145 truncate_file(file_path, new_size);
146 }
147
148 ret = fs_unmount(mp);
149 zassert_equal(ret, 0, "Unmount failed (ret=%d)", ret);
150 }
151
ZTEST(ext2tests,test_indirect_block_removal)152 ZTEST(ext2tests, test_indirect_block_removal)
153 {
154 int64_t ret = 0;
155 struct fs_statvfs sbuf;
156 struct fs_mount_t *mp = &testfs_mnt;
157 static const char *file_path = "/sml/file";
158
159 ret = fs_mkfs(FS_EXT2, (uintptr_t)mp->storage_dev, NULL, 0);
160 zassert_equal(ret, 0, "Failed to mkfs with 2K blocks");
161
162 mp->flags = FS_MOUNT_FLAG_NO_FORMAT;
163 ret = fs_mount(mp);
164 zassert_equal(ret, 0, "Mount failed (ret=%d)", ret);
165
166 ret = fs_statvfs(mp->mnt_point, &sbuf);
167 zassert_equal(ret, 0, "Expected success (ret=%d)", ret);
168
169 uint32_t bsize = sbuf.f_bsize;
170
171 uint32_t bytes_to_write = bsize * 13;
172
173 TC_PRINT("Block size: %d\nBytes_to_write: %d\n", bsize, bytes_to_write);
174
175 write_to_file(file_path, bytes_to_write);
176
177 /* First truncate will remove all blocks under first indirect block.
178 * Second truncate will remove rest of blocks;
179 * it will check if first indirect block was removed properly.
180 */
181 uint32_t sizes[] = {12 * bsize, 0};
182
183 for (int i = 0; i < ARRAY_SIZE(sizes); i++) {
184 uint32_t new_size = sizes[i];
185
186 truncate_file(file_path, new_size);
187 }
188
189 ret = fs_unmount(mp);
190 zassert_equal(ret, 0, "Unmount failed (ret=%d)", ret);
191 }
192
ZTEST(ext2tests,test_write_big_file)193 ZTEST(ext2tests, test_write_big_file)
194 {
195 writing_test(NULL);
196 }
197
198 #if defined(CONFIG_APP_TEST_BIG)
ZTEST(ext2tests,test_write_big_file_2K)199 ZTEST(ext2tests, test_write_big_file_2K)
200 {
201 struct ext2_cfg config = {
202 .block_size = 2048,
203 .fs_size = 0x2000000,
204 .bytes_per_inode = 0,
205 .volume_name[0] = 0,
206 .set_uuid = false,
207 };
208
209 writing_test(&config);
210 }
211
ZTEST(ext2tests,test_write_big_file_4K)212 ZTEST(ext2tests, test_write_big_file_4K)
213 {
214 struct ext2_cfg config = {
215 .block_size = 4096,
216 .fs_size = 0x8000000,
217 .bytes_per_inode = 0,
218 .volume_name[0] = 0,
219 .set_uuid = false,
220 };
221
222 writing_test(&config);
223 }
224 #endif
225