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_APPEND | FS_O_READ, -ENOENT);
153 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, -ENOENT);
154 	ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR, -ENOENT);
155 	ZEND();
156 
157 
158 	/* Attempt create new file with no read/write access and check
159 	 * operations on it.
160 	 */
161 	ZBEGIN("Attempt create new with no R/W access");
162 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
163 	ZOPEN(&ts, FS_O_CREATE | 0, 0);
164 	ZWRITE(&ts, -EACCES);
165 	ZREAD(&ts, -EACCES);
166 	ZCLOSE(&ts);
167 	ZUNLINK(&ts);
168 	#else
169 	TC_PRINT("Bypassed test\n");
170 	#endif
171 	ZEND();
172 
173 
174 	ZBEGIN("Attempt create new with READ access");
175 	ZOPEN(&ts, FS_O_CREATE | FS_O_READ, 0);
176 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
177 	ZWRITE(&ts, -EACCES);
178 	#else
179 	TC_PRINT("Write bypassed\n");
180 	#endif
181 	ZREAD(&ts, 0);
182 	ZCLOSE(&ts);
183 	ZUNLINK(&ts);
184 	ZEND();
185 
186 
187 	ZBEGIN("Attempt create new with WRITE access");
188 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
189 	ZWRITE(&ts, ts.write_size);
190 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
191 	ZREAD(&ts, -EACCES);
192 	#else
193 	TC_PRINT("Read bypassed\n");
194 	#endif
195 	ZCLOSE(&ts);
196 	ZUNLINK(&ts);
197 	ZEND();
198 
199 
200 	ZBEGIN("Attempt create new with R/W access");
201 	ZOPEN(&ts, FS_O_CREATE | FS_O_RDWR, 0);
202 	ZWRITE(&ts, ts.write_size);
203 	/* Read is done at the end of file, so 0 bytes will be read */
204 	ZREAD(&ts, 0);
205 	ZCLOSE(&ts);
206 	ZUNLINK(&ts);
207 	ZEND();
208 
209 
210 	ZBEGIN("Attempt open existing with no R/W access");
211 	ZMKEMPTY(&ts);
212 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
213 	ZOPEN(&ts, 0,  0);
214 	ZWRITE(&ts, -EACCES);
215 	ZREAD(&ts, -EACCES);
216 	ZCLOSE(&ts);
217 	#else
218 	TC_PRINT("Bypassed test\n");
219 	#endif
220 	ZUNLINK(&ts);
221 	ZEND();
222 
223 
224 	ZBEGIN("Attempt open existing with READ access");
225 	ZMKEMPTY(&ts);
226 	ZOPEN(&ts, FS_O_READ,  0);
227 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
228 	ZWRITE(&ts, -EACCES);
229 	#else
230 	TC_PRINT("Write bypassed\n");
231 	#endif
232 	/* File is empty */
233 	ZREAD(&ts, 0);
234 	ZCLOSE(&ts);
235 	ZUNLINK(&ts);
236 	ZEND();
237 
238 
239 	ZBEGIN("Attempt open existing with WRITE access");
240 	ZMKEMPTY(&ts);
241 	ZOPEN(&ts, FS_O_WRITE,  0);
242 	ZCHKPOS(&ts, 0);
243 	ZWRITE(&ts, ts.write_size);
244 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
245 	ZREAD(&ts, -EACCES);
246 	#else
247 	TC_PRINT("Read bypassed\n");
248 	#endif
249 	ZCLOSE(&ts);
250 	ZUNLINK(&ts);
251 	ZEND();
252 
253 
254 	ZBEGIN("Attempt open existing with R/W access");
255 	ZMKEMPTY(&ts);
256 	ZOPEN(&ts, FS_O_RDWR,  0);
257 	ZWRITE(&ts, ts.write_size);
258 	/* Read is done at the end of file, so 0 bytes will be read */
259 	ZREAD(&ts, 0);
260 	ZCLOSE(&ts);
261 	ZUNLINK(&ts);
262 	ZEND();
263 
264 
265 	ZBEGIN("Attempt append existing with no R/W access");
266 	ZMKEMPTY(&ts);
267 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_RW_IS_DEFAULT
268 	ZOPEN(&ts, FS_O_APPEND,  0);
269 	ZCHKPOS(&ts, 0);
270 	ZWRITE(&ts, -EACCES);
271 	ZREAD(&ts, -EACCES);
272 	ZCLOSE(&ts);
273 	#else
274 	TC_PRINT("Test bypassed\n");
275 	#endif
276 	ZUNLINK(&ts);
277 	ZEND();
278 
279 
280 	ZBEGIN("Attempt append existing with READ access");
281 	ZMKEMPTY(&ts);
282 	ZOPEN(&ts, FS_O_APPEND | FS_O_READ,  0);
283 	ZCHKPOS(&ts, 0);
284 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
285 	ZWRITE(&ts, -EACCES);
286 	#else
287 	TC_PRINT("Write bypassed\n");
288 	#endif
289 	/* File is empty */
290 	ZREAD(&ts, 0);
291 	ZCLOSE(&ts);
292 	ZUNLINK(&ts);
293 	ZEND();
294 
295 
296 	ZBEGIN("Attempt append existing with WRITE access");
297 	ZMKEMPTY(&ts);
298 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE,  0);
299 	ZCHKPOS(&ts, 0);
300 	ZWRITE(&ts, ts.write_size);
301 	#ifndef BYPASS_FS_OPEN_FLAGS_LFS_ASSERT_CRASH
302 	ZREAD(&ts, -EACCES);
303 	#else
304 	TC_PRINT("Read bypassed\n");
305 	#endif
306 	ZCLOSE(&ts);
307 	ZUNLINK(&ts);
308 	ZEND();
309 
310 
311 	ZBEGIN("Attempt append existing with R/W access");
312 	ZMKEMPTY(&ts);
313 	ZOPEN(&ts, FS_O_APPEND | FS_O_RDWR,  0);
314 	ZCHKPOS(&ts, 0);
315 	ZWRITE(&ts, ts.write_size);
316 	/* Read is done at the end of file, so 0 bytes will be read */
317 	ZREAD(&ts, 0);
318 	ZCLOSE(&ts);
319 	ZUNLINK(&ts);
320 	ZEND();
321 
322 
323 	/* This is simple check by file position, not contents. Since writing
324 	 * same pattern twice, the position of file should be twice the
325 	 * ts.write_size.
326 	 */
327 	ZBEGIN("Check if append adds data to file");
328 	/* Prepare file */
329 	ZUNLINK(&ts);
330 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
331 	ZWRITE(&ts, ts.write_size);
332 	ZCLOSE(&ts);
333 
334 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
335 	ZCHKPOS(&ts, 0);
336 	ZWRITE(&ts, ts.write_size);
337 	ZCHKPOS(&ts, ts.write_size * 2);
338 	ZCLOSE(&ts);
339 	ZUNLINK(&ts);
340 	ZEND();
341 
342 
343 	ZBEGIN("Check if appended forwards file before write");
344 	/* Prepare file */
345 	ZUNLINK(&ts);
346 	ZOPEN(&ts, FS_O_CREATE | FS_O_WRITE, 0);
347 	ZWRITE(&ts, ts.write_size);
348 	ZCLOSE(&ts);
349 
350 	ZOPEN(&ts, FS_O_APPEND | FS_O_WRITE, 0);
351 	ZCHKPOS(&ts, 0);
352 	ZREWIND(&ts);
353 	ZWRITE(&ts, ts.write_size);
354 	ZCHKPOS(&ts, ts.write_size * 2);
355 	ZCLOSE(&ts);
356 	ZUNLINK(&ts);
357 	ZEND();
358 }
359