1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @filesystem
9  * @brief test_filesystem
10  * Tests the fs_open flags
11  */
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/ztest.h>
15 #include <zephyr/fs/fs.h>
16 #include <string.h>
17 
18 /* Path for testr file should be provided by test runner and should start
19  * with mount point.
20  */
21 extern char *test_fs_open_flags_file_path;
22 
23 static const char something[] = "Something";
24 static char buffer[sizeof(something)];
25 #define RDWR_SIZE sizeof(something)
26 
27 struct test_state {
28 	/* Path to file */
29 	char *file_path;
30 	struct fs_file_t file;
31 	/* Read write buffer info */
32 	const char *write;
33 	int write_size;
34 	char *read;
35 	int read_size;
36 };
37 
38 /* ZEQ decides whether test completed successfully and prints appropriate
39  * information.
40  */
ZEQ(int ret,int expected)41 static void ZEQ(int ret, int expected)
42 {
43 	zassert_equal(ret, expected,
44 		      "FAILED: expected = %d, ret = %d, errno = %d\n",
45 		      expected, ret, errno);
46 	TC_PRINT("SUCCESS\n");
47 }
48 
49 /* NOTE: Below functions have C preprocessor redefinitions that automatically
50  * fill in the line parameter, so when invoking, do not provide the line
51  * parameter.
52  */
53 
54 /* Test fs_open, expected is the return value expected from fs_open */
ZOPEN(struct test_state * ts,int flags,int expected,int line)55 static void ZOPEN(struct test_state *ts, int flags, int expected, int line)
56 {
57 	TC_PRINT("# %d: OPEN %s with flags %x\n", line, ts->file_path, flags);
58 	ZEQ(fs_open(&ts->file, ts->file_path,  flags), expected);
59 }
60 
61 /* Close file. Will automatically fail test case if unsuccessful. */
ZCLOSE(struct test_state * ts,int line)62 static void ZCLOSE(struct test_state *ts, int line)
63 {
64 	TC_PRINT("# %d: CLOSE %s\n", line, ts->file_path);
65 	ZEQ(fs_close(&ts->file), 0);
66 }
67 
68 /* Attempt to write to file; expected is what fs_write is supposed to return */
ZWRITE(struct test_state * ts,int expected,int line)69 static void ZWRITE(struct test_state *ts, int expected, int line)
70 {
71 	TC_PRINT("# %d: WRITE %s\n", line, ts->file_path);
72 	ZEQ(fs_write(&ts->file, ts->write, ts->write_size), expected);
73 }
74 
75 /* Attempt to read from file; expected is what fs_read is supposed to return */
ZREAD(struct test_state * ts,int expected,int line)76 static void ZREAD(struct test_state *ts, int expected, int line)
77 {
78 	TC_PRINT("# %d: READ %s\n", line, ts->file_path);
79 	ZEQ(fs_read(&ts->file, ts->read, ts->read_size), expected);
80 }
81 
82 /* Unlink/delete file. Will automatically fail test case if unsuccessful. */
ZUNLINK(struct test_state * ts,int line)83 static void ZUNLINK(struct test_state *ts, int line)
84 {
85 	int ret;
86 
87 	TC_PRINT("# %d: UNLINK %s\n", line, ts->file_path);
88 	ret  = fs_unlink(ts->file_path);
89 	zassert((ret == 0 || ret == -ENOENT), "Done", "Failed");
90 	TC_PRINT("SUCCESS\n");
91 }
92 
93 /* Check file position; expected is a position file should be at. */
ZCHKPOS(struct test_state * ts,off_t expected,int line)94 static void ZCHKPOS(struct test_state *ts, off_t expected, int line)
95 {
96 	TC_PRINT("# %d: CHKPOS\n", line);
97 	ZEQ(fs_tell(&ts->file), expected);
98 }
99 
100 /* Rewind file. */
ZREWIND(struct test_state * ts,int line)101 static void ZREWIND(struct test_state *ts, int line)
102 {
103 	TC_PRINT("# %d: REWIND\n", line);
104 	ZEQ(fs_seek(&ts->file, 0, FS_SEEK_SET), 0);
105 }
106 
107 /* Banner definitions, print BEGIN/END banner with block number.
108  * Require definition of block variable with vale that will represent first
109  * test block number; the variable is automatically incremented by END.
110  */
111 #define ZBEGIN(info) \
112 	TC_PRINT("\n## BEGIN %d: %s (line %d)\n", block, info, __LINE__)
113 #define ZEND() TC_PRINT("## END %d\n", block++)
114 
115 /* C preprocessor redefinitions that automatically fill in line parameter */
116 #define ZOPEN(ts, flags, expected) ZOPEN(ts, flags, expected, __LINE__)
117 #define ZCLOSE(ts) ZCLOSE(ts, __LINE__)
118 #define ZWRITE(ts, expected) ZWRITE(ts, expected, __LINE__)
119 #define ZREAD(ts, expected) ZREAD(ts, expected, __LINE__)
120 #define ZUNLINK(ts) ZUNLINK(ts, __LINE__)
121 #define ZCHKPOS(ts, expected) ZCHKPOS(ts, expected, __LINE__)
122 #define ZREWIND(ts) ZREWIND(ts, __LINE__)
123 
124 /* Create empty file */
125 #define ZMKEMPTY(ts)			\
126 do {					\
127 	ZUNLINK(ts);			\
128 	ZOPEN(ts, FS_O_CREATE, 0);	\
129 	ZCLOSE(ts);			\
130 } while (0)
131 
test_fs_open_flags(void)132 void test_fs_open_flags(void)
133 {
134 	struct test_state ts = {
135 		*&test_fs_open_flags_file_path,
136 		{ 0 },
137 		something,
138 		RDWR_SIZE,
139 		buffer,
140 		RDWR_SIZE,
141 	};
142 	int block = 1;
143 
144 	fs_file_t_init(&ts.file);
145 
146 	ZBEGIN("Attempt open non-existent");
147 	ZOPEN(&ts, 0, -ENOENT);
148 	ZOPEN(&ts, FS_O_WRITE, -ENOENT);
149 	ZOPEN(&ts, FS_O_READ, -ENOENT);
150 	ZOPEN(&ts, FS_O_RDWR, -ENOENT);
151 	ZOPEN(&ts, FS_O_APPEND, -ENOENT);
152 	ZOPEN(&ts, FS_O_TRUNC, -EACCES);
153 	ZOPEN(&ts, FS_O_APPEND | FS_O_READ, -ENOENT);
154 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, -ENOENT);
155 	ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR, -ENOENT);
156 	ZOPEN(&ts, FS_O_TRUNC | FS_O_RDWR, -ENOENT);
157 	ZOPEN(&ts, FS_O_TRUNC | FS_O_APPEND, -EACCES);
158 	ZOPEN(&ts, FS_O_TRUNC | FS_O_RDWR | FS_O_APPEND, -ENOENT);
159 	ZEND();
160 
161 
162 	/* Attempt create new file with no read/write access and check
163 	 * operations on it.
164 	 */
165 	ZBEGIN("Attempt create new with no R/W access");
166 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
167 	ZOPEN(&ts, FS_O_CREATE | 0, 0);
168 	ZWRITE(&ts, -EACCES);
169 	ZREAD(&ts, -EACCES);
170 	ZCLOSE(&ts);
171 	ZUNLINK(&ts);
172 	#else
173 	TC_PRINT("Bypassed test\n");
174 	#endif
175 	ZEND();
176 
177 
178 	ZBEGIN("Attempt create new with READ access");
179 	ZOPEN(&ts, FS_O_CREATE | FS_O_READ, 0);
180 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
181 	ZWRITE(&ts, -EACCES);
182 	#else
183 	TC_PRINT("Write bypassed\n");
184 	#endif
185 	ZREAD(&ts, 0);
186 	ZCLOSE(&ts);
187 	ZUNLINK(&ts);
188 	ZEND();
189 
190 
191 	ZBEGIN("Attempt create new with WRITE access");
192 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
193 	ZWRITE(&ts, ts.write_size);
194 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
195 	ZREAD(&ts, -EACCES);
196 	#else
197 	TC_PRINT("Read bypassed\n");
198 	#endif
199 	ZCLOSE(&ts);
200 	ZUNLINK(&ts);
201 	ZEND();
202 
203 
204 	ZBEGIN("Attempt create new with R/W access");
205 	ZOPEN(&ts, FS_O_CREATE | FS_O_RDWR, 0);
206 	ZWRITE(&ts, ts.write_size);
207 	/* Read is done at the end of file, so 0 bytes will be read */
208 	ZREAD(&ts, 0);
209 	ZCLOSE(&ts);
210 	ZUNLINK(&ts);
211 	ZEND();
212 
213 
214 	ZBEGIN("Attempt open existing with no R/W access");
215 	ZMKEMPTY(&ts);
216 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
217 	ZOPEN(&ts, 0,  0);
218 	ZWRITE(&ts, -EACCES);
219 	ZREAD(&ts, -EACCES);
220 	ZCLOSE(&ts);
221 	#else
222 	TC_PRINT("Bypassed test\n");
223 	#endif
224 	ZUNLINK(&ts);
225 	ZEND();
226 
227 
228 	ZBEGIN("Attempt open existing with READ access");
229 	ZMKEMPTY(&ts);
230 	ZOPEN(&ts, FS_O_READ,  0);
231 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
232 	ZWRITE(&ts, -EACCES);
233 	#else
234 	TC_PRINT("Write bypassed\n");
235 	#endif
236 	/* File is empty */
237 	ZREAD(&ts, 0);
238 	ZCLOSE(&ts);
239 	ZUNLINK(&ts);
240 	ZEND();
241 
242 
243 	ZBEGIN("Attempt open existing with WRITE access");
244 	ZMKEMPTY(&ts);
245 	ZOPEN(&ts, FS_O_WRITE,  0);
246 	ZCHKPOS(&ts, 0);
247 	ZWRITE(&ts, ts.write_size);
248 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
249 	ZREAD(&ts, -EACCES);
250 	#else
251 	TC_PRINT("Read bypassed\n");
252 	#endif
253 	ZCLOSE(&ts);
254 	ZUNLINK(&ts);
255 	ZEND();
256 
257 
258 	ZBEGIN("Attempt open existing with R/W access");
259 	ZMKEMPTY(&ts);
260 	ZOPEN(&ts, FS_O_RDWR,  0);
261 	ZWRITE(&ts, ts.write_size);
262 	/* Read is done at the end of file, so 0 bytes will be read */
263 	ZREAD(&ts, 0);
264 	ZCLOSE(&ts);
265 	ZUNLINK(&ts);
266 	ZEND();
267 
268 
269 	ZBEGIN("Attempt append existing with no R/W access");
270 	ZMKEMPTY(&ts);
271 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
272 	ZOPEN(&ts, FS_O_APPEND,  0);
273 	ZCHKPOS(&ts, 0);
274 	ZWRITE(&ts, -EACCES);
275 	ZREAD(&ts, -EACCES);
276 	ZCLOSE(&ts);
277 	#else
278 	TC_PRINT("Test bypassed\n");
279 	#endif
280 	ZUNLINK(&ts);
281 	ZEND();
282 
283 
284 	ZBEGIN("Attempt append existing with READ access");
285 	ZMKEMPTY(&ts);
286 	ZOPEN(&ts, FS_O_APPEND | FS_O_READ,  0);
287 	ZCHKPOS(&ts, 0);
288 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
289 	ZWRITE(&ts, -EACCES);
290 	#else
291 	TC_PRINT("Write bypassed\n");
292 	#endif
293 	/* File is empty */
294 	ZREAD(&ts, 0);
295 	ZCLOSE(&ts);
296 	ZUNLINK(&ts);
297 	ZEND();
298 
299 
300 	ZBEGIN("Attempt append existing with WRITE access");
301 	ZMKEMPTY(&ts);
302 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE,  0);
303 	ZCHKPOS(&ts, 0);
304 	ZWRITE(&ts, ts.write_size);
305 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
306 	ZREAD(&ts, -EACCES);
307 	#else
308 	TC_PRINT("Read bypassed\n");
309 	#endif
310 	ZCLOSE(&ts);
311 	ZUNLINK(&ts);
312 	ZEND();
313 
314 
315 	ZBEGIN("Attempt append existing with R/W access");
316 	ZMKEMPTY(&ts);
317 	ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR,  0);
318 	ZCHKPOS(&ts, 0);
319 	ZWRITE(&ts, ts.write_size);
320 	/* Read is done at the end of file, so 0 bytes will be read */
321 	ZREAD(&ts, 0);
322 	ZCLOSE(&ts);
323 	ZUNLINK(&ts);
324 	ZEND();
325 
326 	/** FS_O_TRUNC tests */
327 	ZBEGIN("Attempt truncate a new file without write access");
328 	ZOPEN(&ts, FS_O_CREATE | FS_O_TRUNC, -EACCES);
329 	ZCLOSE(&ts);
330 	ZUNLINK(&ts);
331 	ZEND();
332 
333 	ZBEGIN("Attempt truncate a new file with write access");
334 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE | FS_O_TRUNC, 0);
335 	ZCLOSE(&ts);
336 	ZUNLINK(&ts);
337 	ZEND();
338 
339 	ZBEGIN("Attempt truncate existing with no write access");
340 	ZMKEMPTY(&ts);
341 	ZOPEN(&ts, FS_O_TRUNC, -EACCES);
342 	ZCLOSE(&ts);
343 	ZUNLINK(&ts);
344 	ZEND();
345 
346 	ZBEGIN("Attempt truncate existing with write access");
347 	ZMKEMPTY(&ts);
348 	ZOPEN(&ts, FS_O_TRUNC | FS_O_WRITE, 0);
349 	ZCLOSE(&ts);
350 	ZUNLINK(&ts);
351 	ZEND();
352 
353 	ZBEGIN("Attempt truncate existing with read access");
354 	ZMKEMPTY(&ts);
355 	ZOPEN(&ts, FS_O_READ | FS_O_TRUNC, -EACCES);
356 	ZCLOSE(&ts);
357 	ZUNLINK(&ts);
358 	ZEND();
359 
360 	ZBEGIN("Attempt truncate existing with R/W access");
361 	ZMKEMPTY(&ts);
362 	ZOPEN(&ts, FS_O_RDWR | FS_O_TRUNC, 0);
363 	ZCLOSE(&ts);
364 	ZUNLINK(&ts);
365 	ZEND();
366 
367 	ZBEGIN("Attempt read on truncated file but no read access");
368 	ZMKEMPTY(&ts);
369 	ZOPEN(&ts, FS_O_WRITE | FS_O_TRUNC, 0);
370 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
371 	ZREAD(&ts, -EACCES);
372 	#else
373 	TC_PRINT("Read bypassed\n");
374 	#endif
375 	ZCLOSE(&ts);
376 	ZUNLINK(&ts);
377 	ZEND();
378 
379 	ZBEGIN("Attempt append existing with WRITE access truncated file");
380 	ZMKEMPTY(&ts);
381 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE | FS_O_TRUNC,  0);
382 	ZCHKPOS(&ts, 0);
383 	ZWRITE(&ts, ts.write_size);
384 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
385 	ZREAD(&ts, -EACCES);
386 	#else
387 	TC_PRINT("Read bypassed\n");
388 	#endif
389 	ZCLOSE(&ts);
390 	ZUNLINK(&ts);
391 	ZEND();
392 
393 
394 	/* This is simple check by file position, not contents. Since writing
395 	 * same pattern twice, the position of file should be twice the
396 	 * ts.write_size.
397 	 */
398 	ZBEGIN("Check if append adds data to file");
399 	/* Prepare file */
400 	ZUNLINK(&ts);
401 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
402 	ZWRITE(&ts, ts.write_size);
403 	ZCLOSE(&ts);
404 
405 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
406 	ZCHKPOS(&ts, 0);
407 	ZWRITE(&ts, ts.write_size);
408 	ZCHKPOS(&ts, ts.write_size * 2);
409 	ZCLOSE(&ts);
410 	ZUNLINK(&ts);
411 	ZEND();
412 
413 
414 	ZBEGIN("Check if appended forwards file before write");
415 	/* Prepare file */
416 	ZUNLINK(&ts);
417 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
418 	ZWRITE(&ts, ts.write_size);
419 	ZCLOSE(&ts);
420 
421 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
422 	ZCHKPOS(&ts, 0);
423 	ZREWIND(&ts);
424 	ZWRITE(&ts, ts.write_size);
425 	ZCHKPOS(&ts, ts.write_size * 2);
426 	ZCLOSE(&ts);
427 	ZUNLINK(&ts);
428 	ZEND();
429 
430 
431 	ZBEGIN("Check if file is truncated with data");
432 	/* Prepare file */
433 	ZUNLINK(&ts);
434 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
435 	ZWRITE(&ts, ts.write_size);
436 	ZCLOSE(&ts);
437 
438 	/* Make sure file has the content */
439 	ZOPEN(&ts, FS_O_CREATE | FS_O_READ, 0);
440 	ZREAD(&ts, ts.write_size);
441 	ZCLOSE(&ts);
442 
443 	ZOPEN(&ts, FS_O_TRUNC | FS_O_RDWR, 0);
444 	ZCHKPOS(&ts, 0);
445 	ZREAD(&ts, 0);
446 	ZCLOSE(&ts);
447 	ZUNLINK(&ts);
448 	ZEND();
449 }
450