1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <fcntl.h>
9 #include <zephyr/posix/unistd.h>
10 #include "test_fs.h"
11 
12 const char test_str[] = "hello world!";
13 int file = -1;
14 
test_file_open(void)15 static int test_file_open(void)
16 {
17 	int res;
18 
19 	res = open(TEST_FILE, O_CREAT | O_RDWR, 0660);
20 	if (res < 0) {
21 		TC_ERROR("Failed opening file: %d, errno=%d\n", res, errno);
22 		/* FIXME: restructure tests as per #46897 */
23 		__ASSERT_NO_MSG(res >= 0);
24 	}
25 
26 	file = res;
27 
28 	return TC_PASS;
29 }
30 
test_file_write(void)31 int test_file_write(void)
32 {
33 	ssize_t brw;
34 	off_t res;
35 
36 	res = lseek(file, 0, SEEK_SET);
37 	if (res != 0) {
38 		TC_PRINT("lseek failed [%d]\n", (int)res);
39 		close(file);
40 		file = -1;
41 		return TC_FAIL;
42 	}
43 
44 	brw = write(file, (char *)test_str, strlen(test_str));
45 	if (brw < 0) {
46 		TC_PRINT("Failed writing to file [%d]\n", (int)brw);
47 		close(file);
48 		file = -1;
49 		return TC_FAIL;
50 	}
51 
52 	if (brw < strlen(test_str)) {
53 		TC_PRINT("Unable to complete write. Volume full.\n");
54 		TC_PRINT("Number of bytes written: [%d]\n", (int)brw);
55 		close(file);
56 		file = -1;
57 		return TC_FAIL;
58 	}
59 
60 	return res;
61 }
62 
test_file_read(void)63 static int test_file_read(void)
64 {
65 	ssize_t brw;
66 	off_t res;
67 	char read_buff[80];
68 	size_t sz = strlen(test_str);
69 
70 	res = lseek(file, 0, SEEK_SET);
71 	if (res != 0) {
72 		TC_PRINT("lseek failed [%d]\n", (int)res);
73 		close(file);
74 		file = -1;
75 		return TC_FAIL;
76 	}
77 
78 	brw = read(file, read_buff, sz);
79 	if (brw < 0) {
80 		TC_PRINT("Failed reading file [%d]\n", (int)brw);
81 		close(file);
82 		file = -1;
83 		return TC_FAIL;
84 	}
85 
86 	read_buff[brw] = 0;
87 
88 	if (strcmp(test_str, read_buff)) {
89 		TC_PRINT("Error - Data read does not match data written\n");
90 		TC_PRINT("Data read:\"%s\"\n\n", read_buff);
91 		return TC_FAIL;
92 	}
93 
94 	/* Now test after non-zero lseek. */
95 
96 	res = lseek(file, 2, SEEK_SET);
97 	if (res != 2) {
98 		TC_PRINT("lseek failed [%d]\n", (int)res);
99 		close(file);
100 		file = -1;
101 		return TC_FAIL;
102 	}
103 
104 	brw = read(file, read_buff, sizeof(read_buff));
105 	if (brw < 0) {
106 		TC_PRINT("Failed reading file [%d]\n", (int)brw);
107 		close(file);
108 		file = -1;
109 		return TC_FAIL;
110 	}
111 
112 	/* Check for array overrun */
113 	brw = (brw < 80) ? brw : brw - 1;
114 
115 	read_buff[brw] = 0;
116 
117 	if (strcmp(test_str + 2, read_buff)) {
118 		TC_PRINT("Error - Data read does not match data written\n");
119 		TC_PRINT("Data read:\"%s\"\n\n", read_buff);
120 		return TC_FAIL;
121 	}
122 
123 	return TC_PASS;
124 }
125 
test_file_close(void)126 static int test_file_close(void)
127 {
128 	int res = 0;
129 
130 	if (file >= 0) {
131 		res = close(file);
132 		if (res < 0) {
133 			TC_ERROR("Failed closing file: %d, errno=%d\n", res, errno);
134 			/* FIXME: restructure tests as per #46897 */
135 			__ASSERT_NO_MSG(res == 0);
136 		}
137 
138 		file = -1;
139 	}
140 
141 	return res;
142 }
143 
test_file_fsync(void)144 static int test_file_fsync(void)
145 {
146 	int res = 0;
147 
148 	if (file < 0) {
149 		return res;
150 	}
151 
152 	res = fsync(file);
153 	if (res < 0) {
154 		TC_ERROR("Failed to sync file: %d, errno = %d\n", res, errno);
155 		res = TC_FAIL;
156 	}
157 
158 	close(file);
159 	file = -1;
160 	return res;
161 }
162 
163 #ifdef CONFIG_POSIX_SYNCHRONIZED_IO
test_file_fdatasync(void)164 static int test_file_fdatasync(void)
165 {
166 	int res = 0;
167 
168 	if (file < 0) {
169 		return res;
170 	}
171 
172 	res = fdatasync(file);
173 	if (res < 0) {
174 		TC_ERROR("Failed to sync file: %d, errno = %d\n", res, errno);
175 		res = TC_FAIL;
176 	}
177 
178 	close(file);
179 	file = -1;
180 	return res;
181 }
182 #endif /* CONFIG_POSIX_SYNCHRONIZED_IO */
183 
test_file_truncate(void)184 static int test_file_truncate(void)
185 {
186 	int res = 0;
187 	size_t truncate_size = sizeof(test_str) - 4;
188 
189 	if (file < 0) {
190 		return res;
191 	}
192 
193 	res = ftruncate(file, truncate_size);
194 	if (res) {
195 		TC_PRINT("Error truncating file [%d]\n", res);
196 		res = TC_FAIL;
197 	}
198 
199 	close(file);
200 	file = -1;
201 	return res;
202 }
203 
test_file_delete(void)204 static int test_file_delete(void)
205 {
206 	int res;
207 
208 	res = unlink(TEST_FILE);
209 	if (res) {
210 		TC_PRINT("Error deleting file [%d]\n", res);
211 		return res;
212 	}
213 
214 	return res;
215 }
216 
after_fn(void * unused)217 static void after_fn(void *unused)
218 {
219 	ARG_UNUSED(unused);
220 
221 	test_file_close();
222 	unlink(TEST_FILE);
223 }
224 
225 ZTEST_SUITE(posix_fs_file_test, NULL, test_mount, NULL, after_fn,
226 	    test_unmount);
227 
228 /**
229  * @brief Test for POSIX open API
230  *
231  * @details Test opens new file through POSIX open API.
232  */
ZTEST(posix_fs_file_test,test_fs_open)233 ZTEST(posix_fs_file_test, test_fs_open)
234 {
235 	/* FIXME: restructure tests as per #46897 */
236 	zassert_true(test_file_open() == TC_PASS);
237 }
238 
239 /**
240  * @brief Test for POSIX write API
241  *
242  * @details Test writes some data through POSIX write API.
243  */
ZTEST(posix_fs_file_test,test_fs_write)244 ZTEST(posix_fs_file_test, test_fs_write)
245 {
246 	/* FIXME: restructure tests as per #46897 */
247 	zassert_true(test_file_open() == TC_PASS);
248 	zassert_true(test_file_write() == TC_PASS);
249 }
250 
251 /**
252  * @brief Test for POSIX write API
253  *
254  * @details Test reads data back through POSIX read API.
255  */
ZTEST(posix_fs_file_test,test_fs_read)256 ZTEST(posix_fs_file_test, test_fs_read)
257 {
258 	/* FIXME: restructure tests as per #46897 */
259 	zassert_true(test_file_open() == TC_PASS);
260 	zassert_true(test_file_write() == TC_PASS);
261 	zassert_true(test_file_read() == TC_PASS);
262 }
263 
264 /**
265  * @brief Test for POSIX fsync API
266  *
267  * @details Test sync the file through POSIX fsync API.
268  */
ZTEST(posix_fs_file_test,test_fs_sync)269 ZTEST(posix_fs_file_test, test_fs_sync)
270 {
271 	/* FIXME: restructure tests as per #46897 */
272 	zassert_true(test_file_open() == TC_PASS);
273 	zassert_true(test_file_write() == TC_PASS);
274 	zassert_true(test_file_fsync() == TC_PASS);
275 }
276 
277 /**
278  * @brief Test for POSIX fdatasync API
279  *
280  * @details Test sync the file through POSIX fdatasync API.
281  */
ZTEST(posix_fs_file_test,test_fs_datasync)282 ZTEST(posix_fs_file_test, test_fs_datasync)
283 {
284 #ifdef CONFIG_POSIX_SYNCHRONIZED_IO
285 	/* FIXME: restructure tests as per #46897 */
286 	zassert_true(test_file_open() == TC_PASS);
287 	zassert_true(test_file_write() == TC_PASS);
288 	zassert_true(test_file_fdatasync() == TC_PASS);
289 #else
290 	ztest_test_skip();
291 #endif
292 }
293 
294 /**
295  * @brief Test for POSIX ftruncate API
296  *
297  * @details Test truncate the file through POSIX ftruncate API.
298  */
ZTEST(posix_fs_file_test,test_fs_truncate)299 ZTEST(posix_fs_file_test, test_fs_truncate)
300 {
301 	/* FIXME: restructure tests as per #46897 */
302 	zassert_true(test_file_open() == TC_PASS);
303 	zassert_true(test_file_write() == TC_PASS);
304 	zassert_true(test_file_truncate() == TC_PASS);
305 }
306 
307 /**
308  * @brief Test for POSIX close API
309  *
310  * @details Test closes the open file through POSIX close API.
311  */
ZTEST(posix_fs_file_test,test_fs_close)312 ZTEST(posix_fs_file_test, test_fs_close)
313 {
314 	/* FIXME: restructure tests as per #46897 */
315 	zassert_true(test_file_open() == TC_PASS);
316 	zassert_true(test_file_close() == TC_PASS);
317 }
318 
319 /**
320  * @brief Test for POSIX unlink API
321  *
322  * @details Test deletes a file through POSIX unlink API.
323  */
ZTEST(posix_fs_file_test,test_fs_unlink)324 ZTEST(posix_fs_file_test, test_fs_unlink)
325 {
326 	zassert_true(test_file_open() == TC_PASS);
327 	zassert_true(test_file_delete() == TC_PASS);
328 }
329 
ZTEST(posix_fs_file_test,test_fs_fd_leak)330 ZTEST(posix_fs_file_test, test_fs_fd_leak)
331 {
332 	const int reps =
333 	    MAX(CONFIG_POSIX_OPEN_MAX, CONFIG_ZVFS_OPEN_MAX) + 5;
334 
335 	for (int i = 0; i < reps; i++) {
336 		if (i > 0) {
337 			zassert_true(test_file_open() == TC_PASS);
338 		}
339 
340 		if (i < reps - 1) {
341 			zassert_true(test_file_close() == TC_PASS);
342 		}
343 	}
344 }
345 
ZTEST(posix_fs_file_test,test_file_open_truncate)346 ZTEST(posix_fs_file_test, test_file_open_truncate)
347 {
348 	struct stat buf = {0};
349 
350 	zassert_ok(test_file_open());
351 	zassert_ok(test_file_write());
352 	zassert_ok(test_file_close());
353 	file = open(TEST_FILE, O_RDWR | O_TRUNC);
354 	zassert_not_equal(file, -1,
355 			  "File open failed for truncate mode");
356 
357 	zassert_ok(test_file_close());
358 	zassert_ok(stat(TEST_FILE, &buf));
359 	zassert_equal(buf.st_size, 0, "Error: file is not truncated");
360 	zassert_ok(test_file_delete());
361 }
362