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 
writing_test(struct ext2_cfg * config)42 void writing_test(struct ext2_cfg *config)
43 {
44 	int64_t ret = 0;
45 	struct fs_file_t file;
46 	struct fs_statvfs sbuf;
47 	struct fs_dirent entry;
48 	struct fs_mount_t *mp = &testfs_mnt;
49 	static const char *file_path = "/sml/file";
50 
51 	ret = fs_mkfs(FS_EXT2, (uintptr_t)mp->storage_dev, config, 0);
52 	zassert_equal(ret, 0, "Failed to mkfs with 2K blocks");
53 
54 	mp->flags = FS_MOUNT_FLAG_NO_FORMAT;
55 	ret = fs_mount(mp);
56 	zassert_equal(ret, 0, "Mount failed (ret=%d)", ret);
57 
58 	fs_file_t_init(&file);
59 	ret = fs_open(&file, file_path, FS_O_RDWR | FS_O_CREATE);
60 	zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
61 
62 	ret = fs_statvfs(mp->mnt_point, &sbuf);
63 	zassert_equal(ret, 0, "Expected success (ret=%d)", ret);
64 
65 
66 	/* Calculate how many numbers will be written (use all available memory) */
67 	uint32_t freeb = sbuf.f_bfree;
68 	uint32_t bsize = sbuf.f_bsize;
69 	uint32_t available_blocks = calculate_blocks(freeb, bsize / sizeof(uint32_t));
70 
71 	uint32_t bytes_to_write = bsize * available_blocks;
72 
73 	TC_PRINT("Available blocks: %d\nBlock size: %d\nBytes_to_write: %d\n",
74 			available_blocks, bsize, bytes_to_write);
75 
76 	ret = testfs_write_incrementing(&file, 0, available_blocks * bsize);
77 	zassert_equal(ret, bytes_to_write, "Different number of bytes written %ld (expected %ld)",
78 			ret, bytes_to_write);
79 
80 	ret = fs_close(&file);
81 	zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
82 
83 
84 	/* Check file size */
85 	ret = fs_stat(file_path, &entry);
86 	zassert_equal(ret, 0, "File stat failed (ret=%d)", ret);
87 	zassert_equal(entry.size, bytes_to_write,
88 			"Wrong file size %d (expected %d)", entry.size,
89 			bytes_to_write);
90 
91 	fs_file_t_init(&file);
92 	ret = fs_open(&file, file_path, FS_O_READ);
93 	zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
94 
95 
96 	ret = testfs_verify_incrementing(&file, 0, available_blocks * bsize);
97 	zassert_equal(ret, bytes_to_write, "Different number of bytes read %ld (expected %ld)",
98 			ret, bytes_to_write);
99 
100 	ret = fs_close(&file);
101 	zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
102 
103 	uint32_t new_size = bytes_to_write;
104 
105 	while (new_size > 1) {
106 		new_size = new_size / 8 * 3;
107 
108 		TC_PRINT("Truncating to %d\n", new_size);
109 
110 		ret = fs_open(&file, file_path, FS_O_RDWR);
111 		zassert_equal(ret, 0, "File open failed (ret=%d)", ret);
112 
113 		ret = fs_truncate(&file, new_size);
114 		zassert_equal(ret, 0, "File truncate failed (ret=%d)", ret);
115 
116 		ret = fs_stat(file_path, &entry);
117 		zassert_equal(ret, 0, "File stat failed (ret=%d)", ret);
118 		zassert_equal(entry.size, new_size,
119 				"Wrong file size %d (expected %d)", entry.size, new_size);
120 
121 		ret = fs_seek(&file, 0, FS_SEEK_SET);
122 		zassert_equal(ret, 0, "File seek failed (ret=%d)", ret);
123 
124 		ret = testfs_verify_incrementing(&file, 0, new_size);
125 		zassert_equal(ret, new_size, "Different number of bytes read %ld (expected %ld)",
126 				ret, new_size);
127 
128 		ret = fs_close(&file);
129 		zassert_equal(ret, 0, "File close failed (ret=%d)", ret);
130 	}
131 
132 	ret = fs_unmount(mp);
133 	zassert_equal(ret, 0, "Unmount failed (ret=%d)", ret);
134 }
135 
ZTEST(ext2tests,test_write_big_file)136 ZTEST(ext2tests, test_write_big_file)
137 {
138 	writing_test(NULL);
139 }
140 
141 #if defined(CONFIG_APP_TEST_BIG)
ZTEST(ext2tests,test_write_big_file_2K)142 ZTEST(ext2tests, test_write_big_file_2K)
143 {
144 	struct ext2_cfg config = {
145 		.block_size = 2048,
146 		.fs_size = 0x2000000,
147 		.bytes_per_inode = 0,
148 		.volume_name[0] = 0,
149 		.set_uuid = false,
150 	};
151 
152 	writing_test(&config);
153 }
154 
ZTEST(ext2tests,test_write_big_file_4K)155 ZTEST(ext2tests, test_write_big_file_4K)
156 {
157 	struct ext2_cfg config = {
158 		.block_size = 4096,
159 		.fs_size = 0x8000000,
160 		.bytes_per_inode = 0,
161 		.volume_name[0] = 0,
162 		.set_uuid = false,
163 	};
164 
165 	writing_test(&config);
166 }
167 #endif
168