1 /*
2  * Copyright (c) 2019 Peter Bigot Consulting, LLC
3  * Copyright (c) 2023 Antmicro
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/fs/fs.h>
10 
11 #include "test_fs_util.h"
12 
13 /* Mount point for the tests should be provided by test runner.
14  * File system should be mounted before tests are started.
15  */
16 extern struct fs_mount_t *fs_dirops_test_mp;
17 
18 static struct testfs_bcmd test_hierarchy[] = {
19 	TESTFS_BCMD_FILE("f1", 1, 1),
20 	TESTFS_BCMD_FILE("f2", 2, 100),
21 	TESTFS_BCMD_ENTER_DIR("d1"),
22 	TESTFS_BCMD_FILE("d1f1", 11, 23),
23 	TESTFS_BCMD_FILE("d1f2", 12, 612),
24 	TESTFS_BCMD_EXIT_DIR(),
25 	TESTFS_BCMD_FILE("f3", 3, 10000),
26 	TESTFS_BCMD_END(),
27 };
28 
check_mkdir(struct fs_mount_t * mp)29 static int check_mkdir(struct fs_mount_t *mp)
30 {
31 	struct testfs_path dpath;
32 
33 	TC_PRINT("checking dir create unlink\n");
34 	zassert_equal(testfs_path_init(&dpath, mp,
35 				       "dir",
36 				       TESTFS_PATH_END),
37 		      dpath.path,
38 		      "root init failed");
39 
40 	zassert_equal(fs_mkdir(dpath.path),
41 		      0,
42 		      "mkdir failed");
43 
44 	struct fs_file_t file;
45 	struct testfs_path fpath;
46 
47 	fs_file_t_init(&file);
48 	zassert_equal(fs_open(&file,
49 			      testfs_path_extend(testfs_path_copy(&fpath,
50 								  &dpath),
51 						 "file",
52 						 TESTFS_PATH_END),
53 			      FS_O_CREATE | FS_O_RDWR),
54 		      0,
55 		      "creat in dir failed");
56 	zassert_equal(fs_close(&file), 0,
57 		      "close file failed");
58 
59 	struct fs_dirent stat;
60 
61 	zassert_equal(fs_stat(fpath.path, &stat), 0,
62 		      "stat file failed");
63 
64 	zassert_equal(fs_unlink(dpath.path),
65 		      -ENOTEMPTY,
66 		      "unlink bad failure");
67 
68 	zassert_equal(fs_unlink(fpath.path),
69 		      0,
70 		      "unlink file failed");
71 
72 	zassert_equal(fs_unlink(dpath.path),
73 		      0,
74 		      "unlink dir failed");
75 
76 	return TC_PASS;
77 }
78 
build_layout(struct fs_mount_t * mp,const struct testfs_bcmd * cp)79 static int build_layout(struct fs_mount_t *mp,
80 			const struct testfs_bcmd *cp)
81 {
82 	struct testfs_path path;
83 	struct fs_statvfs stat;
84 
85 	TC_PRINT("building layout on %s\n", mp->mnt_point);
86 
87 	zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
88 		      "statvfs failed");
89 
90 	TC_PRINT("before: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
91 		 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
92 
93 	zassert_equal(testfs_path_init(&path, mp, TESTFS_PATH_END),
94 		      path.path,
95 		      "root init failed");
96 
97 	zassert_equal(testfs_build(&path, cp),
98 		      0,
99 		      "Build_layout failed");
100 
101 	zassert_equal(fs_statvfs(mp->mnt_point, &stat), 0,
102 		      "statvfs failed");
103 
104 	TC_PRINT("after: bsize %lu ; frsize %lu ; blocks %lu ; bfree %lu\n",
105 		 stat.f_bsize, stat.f_frsize, stat.f_blocks, stat.f_bfree);
106 
107 	return TC_PASS;
108 }
109 
check_layout(struct fs_mount_t * mp,struct testfs_bcmd * layout)110 static int check_layout(struct fs_mount_t *mp,
111 			struct testfs_bcmd *layout)
112 {
113 	struct testfs_path path;
114 	struct testfs_bcmd *end_layout = testfs_bcmd_end(layout);
115 
116 	TC_PRINT("checking layout\n");
117 
118 	zassert_equal(testfs_path_init(&path, mp, TESTFS_PATH_END),
119 		      path.path,
120 		      "root init failed");
121 
122 	int rc = testfs_bcmd_verify_layout(&path, layout, end_layout);
123 
124 	zassert_true(rc >= 0, "layout check failed");
125 
126 	zassert_equal(rc, 0,
127 		      "layout found foreign");
128 
129 	struct testfs_bcmd *cp = layout;
130 
131 	while (!TESTFS_BCMD_IS_END(cp)) {
132 		if (cp->name != NULL) {
133 			TC_PRINT("verifying %s%s %u\n",
134 				 cp->name,
135 				 (cp->type == FS_DIR_ENTRY_DIR) ? "/" : "",
136 				 cp->size);
137 			zassert_true(cp->matched,
138 				     "Unmatched layout entry");
139 		}
140 		++cp;
141 	}
142 
143 	return TC_PASS;
144 }
145 
check_rename(struct fs_mount_t * mp)146 static int check_rename(struct fs_mount_t *mp)
147 {
148 	struct testfs_path root;
149 	struct testfs_path from_path;
150 	const char *from = from_path.path;
151 	struct testfs_path to_path;
152 	const char *to = to_path.path;
153 	struct fs_dirent stat;
154 	struct testfs_bcmd from_bcmd[] = {
155 		TESTFS_BCMD_FILE("f1f", 1, 8),  /* from f1f to f1t */
156 		TESTFS_BCMD_FILE("f2f", 2, 8),  /* from f2f to f2t */
157 		TESTFS_BCMD_FILE("f2t", 3, 8),  /* target for f2f clobber, replaced */
158 		TESTFS_BCMD_FILE("f3f", 4, 8),  /* from f3f to d1f/d1f2t */
159 		TESTFS_BCMD_FILE("f4f", 5, 8),  /* from f4f to d2t, reject */
160 		TESTFS_BCMD_ENTER_DIR("d1f"),   /* from d1f to d1t */
161 		TESTFS_BCMD_FILE("d1f1", 5, 16),
162 		TESTFS_BCMD_EXIT_DIR(),
163 		TESTFS_BCMD_ENTER_DIR("d2t"),  /* target for d1f, unchanged */
164 		TESTFS_BCMD_FILE("d2f1", 6, 16),
165 		TESTFS_BCMD_EXIT_DIR(),
166 		TESTFS_BCMD_END(),
167 	};
168 	struct testfs_bcmd *from_end_bcmd = testfs_bcmd_end(from_bcmd);
169 	struct testfs_bcmd to_bcmd[] = {
170 		TESTFS_BCMD_FILE("f1t", 1, 8),          /* from f1f to f1t */
171 		TESTFS_BCMD_FILE("f2t", 2, 8),          /* from f2f to f2t */
172 		TESTFS_BCMD_FILE("f4f", 5, 8),          /* from f4f to d2t, reject */
173 		TESTFS_BCMD_ENTER_DIR("d1t"),           /* from d1f to d1t */
174 		TESTFS_BCMD_FILE("d1f1", 5, 16),
175 		TESTFS_BCMD_FILE("d1f2t", 4, 8),        /* from f3f to d1f/d1f2t */
176 		TESTFS_BCMD_EXIT_DIR(),
177 		TESTFS_BCMD_ENTER_DIR("d2t"),           /* target for d1f, unchanged */
178 		TESTFS_BCMD_FILE("d2f1", 6, 16),
179 		TESTFS_BCMD_EXIT_DIR(),
180 		TESTFS_BCMD_END(),
181 	};
182 	struct testfs_bcmd *to_end_bcmd = testfs_bcmd_end(to_bcmd);
183 
184 	zassert_equal(testfs_path_init(&root, mp,
185 				       "rename",
186 				       TESTFS_PATH_END),
187 		      root.path,
188 		      "root init failed");
189 
190 	zassert_equal(fs_mkdir(root.path),
191 		      0,
192 		      "rename mkdir failed");
193 	zassert_equal(testfs_build(&root, from_bcmd),
194 		      0,
195 		      "rename build failed");
196 
197 	zassert_equal(testfs_bcmd_verify_layout(&root, from_bcmd, from_end_bcmd),
198 		      0,
199 		      "layout check failed");
200 
201 	testfs_path_extend(testfs_path_copy(&from_path, &root),
202 			   "nofile",
203 			   TESTFS_PATH_END);
204 	testfs_path_extend(testfs_path_copy(&to_path, &root),
205 			   "f1t",
206 			   TESTFS_PATH_END);
207 	TC_PRINT("%s => %s -ENOENT\n", from, to);
208 	zassert_equal(fs_rename(from, to),
209 		      -ENOENT,
210 		      "rename noent failed");
211 
212 	/* f1f => f1t : ok */
213 	testfs_path_extend(testfs_path_copy(&from_path, &root),
214 			   "f1f",
215 			   TESTFS_PATH_END);
216 	TC_PRINT("%s => %s ok\n", from, to);
217 	zassert_equal(fs_rename(from, to),
218 		      0,
219 		      "rename noent failed");
220 
221 	/* f2f => f2t : clobbers */
222 	testfs_path_extend(testfs_path_copy(&from_path, &root),
223 			   "f2f",
224 			   TESTFS_PATH_END);
225 	testfs_path_extend(testfs_path_copy(&to_path, &root),
226 			   "f2t",
227 			   TESTFS_PATH_END);
228 	TC_PRINT("%s => %s clobber ok\n", from, to);
229 	zassert_equal(fs_rename(from, to),
230 		      0,
231 		      "rename clobber failed");
232 	zassert_equal(fs_stat(from, &stat),
233 		      -ENOENT,
234 		      "rename clobber left from");
235 
236 	/* f3f => d1f/d1f2t : moves */
237 	testfs_path_extend(testfs_path_copy(&from_path, &root),
238 			   "f3f",
239 			   TESTFS_PATH_END);
240 	testfs_path_extend(testfs_path_copy(&to_path, &root),
241 			   "d1f", "d1f2t",
242 			   TESTFS_PATH_END);
243 	TC_PRINT("%s => %s move ok\n", from, to);
244 	zassert_equal(fs_rename(from, to),
245 		      0,
246 		      "rename to subdir failed");
247 	zassert_equal(fs_stat(from, &stat),
248 		      -ENOENT,
249 		      "rename to subdir left from");
250 
251 	/* d1f => d2t : reject */
252 	testfs_path_extend(testfs_path_copy(&from_path, &root),
253 			   "d1f",
254 			   TESTFS_PATH_END);
255 	testfs_path_extend(testfs_path_copy(&to_path, &root),
256 			   "d2t",
257 			   TESTFS_PATH_END);
258 	TC_PRINT("%s => %s -ENOTEMPTY\n", from, to);
259 	zassert_equal(fs_rename(from, to),
260 		      -ENOTEMPTY,
261 		      "rename to existing dir failed");
262 
263 	/* d1f => d1t : rename */
264 	testfs_path_extend(testfs_path_copy(&from_path, &root),
265 			   "d1f",
266 			   TESTFS_PATH_END);
267 	testfs_path_extend(testfs_path_copy(&to_path, &root),
268 			   "d1t",
269 			   TESTFS_PATH_END);
270 	TC_PRINT("%s => %s ok\n", from, to);
271 	zassert_equal(fs_rename(from, to),
272 		      0,
273 		      "rename to new dir failed");
274 	zassert_equal(fs_stat(from, &stat),
275 		      -ENOENT,
276 		      "rename to new dir left from");
277 
278 	zassert_equal(testfs_bcmd_verify_layout(&root, to_bcmd, to_end_bcmd),
279 		      0,
280 		      "layout verification failed");
281 
282 	struct testfs_bcmd *cp = to_bcmd;
283 
284 	while (cp != to_end_bcmd) {
285 		if (cp->name) {
286 			zassert_true(cp->matched, "foreign file retained");
287 		}
288 		++cp;
289 	}
290 
291 	return TC_PASS;
292 }
293 
test_fs_dirops(void)294 void test_fs_dirops(void)
295 {
296 	zassert_equal(fs_mount(fs_dirops_test_mp), 0,
297 		      "mount failed");
298 
299 	zassert_equal(check_mkdir(fs_dirops_test_mp), TC_PASS,
300 		      "check mkdir failed");
301 
302 	k_sleep(K_MSEC(100));   /* flush log messages */
303 	zassert_equal(build_layout(fs_dirops_test_mp, test_hierarchy), TC_PASS,
304 		      "build test hierarchy failed");
305 
306 	k_sleep(K_MSEC(100));   /* flush log messages */
307 	zassert_equal(check_layout(fs_dirops_test_mp, test_hierarchy), TC_PASS,
308 		      "check test hierarchy failed");
309 
310 	k_sleep(K_MSEC(100));   /* flush log messages */
311 	zassert_equal(check_rename(fs_dirops_test_mp), TC_PASS,
312 		      "check rename failed");
313 
314 	k_sleep(K_MSEC(100));   /* flush log messages */
315 	zassert_equal(fs_unmount(fs_dirops_test_mp), 0,
316 		      "unmount small failed");
317 }
318