1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L
9 
10 #include "fs_priv.h"
11 
12 #include <errno.h>
13 #include <zephyr/kernel.h>
14 #include <limits.h>
15 #include <zephyr/posix/unistd.h>
16 #include <zephyr/posix/dirent.h>
17 #include <string.h>
18 #include <zephyr/sys/fdtable.h>
19 #include <zephyr/posix/sys/stat.h>
20 #include <zephyr/posix/fcntl.h>
21 #include <zephyr/fs/fs.h>
22 
23 int zvfs_fstat(int fd, struct stat *buf);
24 
25 BUILD_ASSERT(PATH_MAX >= MAX_FILE_NAME, "PATH_MAX is less than MAX_FILE_NAME");
26 
27 static struct posix_fs_desc desc_array[CONFIG_POSIX_OPEN_MAX];
28 
29 static struct fs_dirent fdirent;
30 static struct dirent pdirent;
31 
32 static struct fd_op_vtable fs_fd_op_vtable;
33 
posix_fs_alloc_obj(bool is_dir)34 static struct posix_fs_desc *posix_fs_alloc_obj(bool is_dir)
35 {
36 	int i;
37 	struct posix_fs_desc *ptr = NULL;
38 	unsigned int key = irq_lock();
39 
40 	for (i = 0; i < CONFIG_POSIX_OPEN_MAX; i++) {
41 		if (desc_array[i].used == false) {
42 			ptr = &desc_array[i];
43 			ptr->used = true;
44 			ptr->is_dir = is_dir;
45 			break;
46 		}
47 	}
48 	irq_unlock(key);
49 
50 	return ptr;
51 }
52 
posix_fs_free_obj(struct posix_fs_desc * ptr)53 static inline void posix_fs_free_obj(struct posix_fs_desc *ptr)
54 {
55 	ptr->used = false;
56 }
57 
posix_mode_to_zephyr(int mf)58 static int posix_mode_to_zephyr(int mf)
59 {
60 	int mode = (mf & O_CREAT) ? FS_O_CREATE : 0;
61 
62 	mode |= (mf & O_APPEND) ? FS_O_APPEND : 0;
63 	mode |= (mf & O_TRUNC) ? FS_O_TRUNC : 0;
64 
65 	switch (mf & O_ACCMODE) {
66 	case O_RDONLY:
67 		mode |= FS_O_READ;
68 		break;
69 	case O_WRONLY:
70 		mode |= FS_O_WRITE;
71 		break;
72 	case O_RDWR:
73 		mode |= FS_O_RDWR;
74 		break;
75 	default:
76 		break;
77 	}
78 
79 	return mode;
80 }
81 
zvfs_open(const char * name,int flags,int mode)82 int zvfs_open(const char *name, int flags, int mode)
83 {
84 	int rc, fd;
85 	struct posix_fs_desc *ptr = NULL;
86 	int zmode = posix_mode_to_zephyr(flags);
87 
88 	if (zmode < 0) {
89 		return zmode;
90 	}
91 
92 	fd = zvfs_reserve_fd();
93 	if (fd < 0) {
94 		return -1;
95 	}
96 
97 	ptr = posix_fs_alloc_obj(false);
98 	if (ptr == NULL) {
99 		rc = -EMFILE;
100 		goto out_err;
101 	}
102 
103 	fs_file_t_init(&ptr->file);
104 
105 	if (flags & O_CREAT) {
106 		flags &= ~O_CREAT;
107 
108 		rc = fs_open(&ptr->file, name, FS_O_CREATE | (mode & O_ACCMODE));
109 		if (rc < 0) {
110 			goto out_err;
111 		}
112 		rc = fs_close(&ptr->file);
113 		if (rc < 0) {
114 			goto out_err;
115 		}
116 	}
117 
118 	rc = fs_open(&ptr->file, name, zmode);
119 	if (rc < 0) {
120 		goto out_err;
121 	}
122 
123 	zvfs_finalize_fd(fd, ptr, &fs_fd_op_vtable);
124 
125 	goto out;
126 
127 out_err:
128 	if (ptr != NULL) {
129 		posix_fs_free_obj(ptr);
130 	}
131 
132 	zvfs_free_fd(fd);
133 	errno = -rc;
134 	return -1;
135 
136 out:
137 	return fd;
138 }
139 
fs_close_vmeth(void * obj)140 static int fs_close_vmeth(void *obj)
141 {
142 	struct posix_fs_desc *ptr = obj;
143 	int rc;
144 
145 	rc = fs_close(&ptr->file);
146 	posix_fs_free_obj(ptr);
147 
148 	return rc;
149 }
150 
fs_ioctl_vmeth(void * obj,unsigned int request,va_list args)151 static int fs_ioctl_vmeth(void *obj, unsigned int request, va_list args)
152 {
153 	int rc = 0;
154 	struct posix_fs_desc *ptr = obj;
155 
156 	switch (request) {
157 	case ZFD_IOCTL_FSYNC: {
158 		rc = fs_sync(&ptr->file);
159 		break;
160 	}
161 	case ZFD_IOCTL_LSEEK: {
162 		off_t offset;
163 		int whence;
164 
165 		offset = va_arg(args, off_t);
166 		whence = va_arg(args, int);
167 
168 		rc = fs_seek(&ptr->file, offset, whence);
169 		if (rc == 0) {
170 			rc = fs_tell(&ptr->file);
171 		}
172 		break;
173 	}
174 	case ZFD_IOCTL_TRUNCATE: {
175 		off_t length;
176 
177 		length = va_arg(args, off_t);
178 
179 		rc = fs_truncate(&ptr->file, length);
180 		if (rc < 0) {
181 			errno = -rc;
182 			return -1;
183 		}
184 		break;
185 	}
186 	default:
187 		errno = EOPNOTSUPP;
188 		return -1;
189 	}
190 
191 	if (rc < 0) {
192 		errno = -rc;
193 		return -1;
194 	}
195 
196 	return rc;
197 }
198 
199 /**
200  * @brief Write to a file.
201  *
202  * See IEEE 1003.1
203  */
fs_write_vmeth(void * obj,const void * buffer,size_t count)204 static ssize_t fs_write_vmeth(void *obj, const void *buffer, size_t count)
205 {
206 	ssize_t rc;
207 	struct posix_fs_desc *ptr = obj;
208 
209 	rc = fs_write(&ptr->file, buffer, count);
210 	if (rc < 0) {
211 		errno = -rc;
212 		return -1;
213 	}
214 
215 	return rc;
216 }
217 
218 /**
219  * @brief Read from a file.
220  *
221  * See IEEE 1003.1
222  */
fs_read_vmeth(void * obj,void * buffer,size_t count)223 static ssize_t fs_read_vmeth(void *obj, void *buffer, size_t count)
224 {
225 	ssize_t rc;
226 	struct posix_fs_desc *ptr = obj;
227 
228 	rc = fs_read(&ptr->file, buffer, count);
229 	if (rc < 0) {
230 		errno = -rc;
231 		return -1;
232 	}
233 
234 	return rc;
235 }
236 
237 static struct fd_op_vtable fs_fd_op_vtable = {
238 	.read = fs_read_vmeth,
239 	.write = fs_write_vmeth,
240 	.close = fs_close_vmeth,
241 	.ioctl = fs_ioctl_vmeth,
242 };
243 
244 /**
245  * @brief Open a directory stream.
246  *
247  * See IEEE 1003.1
248  */
opendir(const char * dirname)249 DIR *opendir(const char *dirname)
250 {
251 	int rc;
252 	struct posix_fs_desc *ptr;
253 
254 	ptr = posix_fs_alloc_obj(true);
255 	if (ptr == NULL) {
256 		errno = EMFILE;
257 		return NULL;
258 	}
259 
260 	fs_dir_t_init(&ptr->dir);
261 
262 	rc = fs_opendir(&ptr->dir, dirname);
263 	if (rc < 0) {
264 		posix_fs_free_obj(ptr);
265 		errno = -rc;
266 		return NULL;
267 	}
268 
269 	return ptr;
270 }
271 
272 /**
273  * @brief Close a directory stream.
274  *
275  * See IEEE 1003.1
276  */
closedir(DIR * dirp)277 int closedir(DIR *dirp)
278 {
279 	int rc;
280 	struct posix_fs_desc *ptr = dirp;
281 
282 	if (dirp == NULL) {
283 		errno = EBADF;
284 		return -1;
285 	}
286 
287 	rc = fs_closedir(&ptr->dir);
288 
289 	posix_fs_free_obj(ptr);
290 
291 	if (rc < 0) {
292 		errno = -rc;
293 		return -1;
294 	}
295 
296 	return 0;
297 }
298 
299 /**
300  * @brief Read a directory.
301  *
302  * See IEEE 1003.1
303  */
readdir(DIR * dirp)304 struct dirent *readdir(DIR *dirp)
305 {
306 	int rc;
307 	struct posix_fs_desc *ptr = dirp;
308 
309 	if (dirp == NULL) {
310 		errno = EBADF;
311 		return NULL;
312 	}
313 
314 	rc = fs_readdir(&ptr->dir, &fdirent);
315 	if (rc < 0) {
316 		errno = -rc;
317 		return NULL;
318 	}
319 
320 	if (fdirent.name[0] == 0) {
321 		/* assume end-of-dir, leave errno untouched */
322 		return NULL;
323 	}
324 
325 	rc = strlen(fdirent.name);
326 	rc = (rc < MAX_FILE_NAME) ? rc : (MAX_FILE_NAME - 1);
327 	(void)memcpy(pdirent.d_name, fdirent.name, rc);
328 
329 	/* Make sure the name is NULL terminated */
330 	pdirent.d_name[rc] = '\0';
331 	return &pdirent;
332 }
333 
334 /**
335  * @brief Rename a file.
336  *
337  * See IEEE 1003.1
338  */
rename(const char * old,const char * new)339 int rename(const char *old, const char *new)
340 {
341 	int rc;
342 
343 	rc = fs_rename(old, new);
344 	if (rc < 0) {
345 		errno = -rc;
346 		return -1;
347 	}
348 
349 	return 0;
350 }
351 
352 /**
353  * @brief Remove a directory entry.
354  *
355  * See IEEE 1003.1
356  */
unlink(const char * path)357 int unlink(const char *path)
358 {
359 	int rc;
360 
361 	rc = fs_unlink(path);
362 	if (rc < 0) {
363 		errno = -rc;
364 		return -1;
365 	}
366 	return 0;
367 }
368 
369 /**
370  * @brief Get file status.
371  *
372  * See IEEE 1003.1
373  */
stat(const char * path,struct stat * buf)374 int stat(const char *path, struct stat *buf)
375 {
376 	int rc;
377 	struct fs_statvfs stat_vfs;
378 	struct fs_dirent stat_file;
379 
380 	if (buf == NULL) {
381 		errno = EBADF;
382 		return -1;
383 	}
384 
385 	rc = fs_statvfs(path, &stat_vfs);
386 	if (rc < 0) {
387 		errno = -rc;
388 		return -1;
389 	}
390 
391 	rc = fs_stat(path, &stat_file);
392 	if (rc < 0) {
393 		errno = -rc;
394 		return -1;
395 	}
396 
397 	memset(buf, 0, sizeof(struct stat));
398 
399 	switch (stat_file.type) {
400 	case FS_DIR_ENTRY_FILE:
401 		buf->st_mode = S_IFREG;
402 		break;
403 	case FS_DIR_ENTRY_DIR:
404 		buf->st_mode = S_IFDIR;
405 		break;
406 	default:
407 		errno = EIO;
408 		return -1;
409 	}
410 	buf->st_size = stat_file.size;
411 	buf->st_blksize = stat_vfs.f_bsize;
412 	/*
413 	 * This is a best effort guess, as this information is not provided
414 	 * by the fs_stat function.
415 	 */
416 	buf->st_blocks = (stat_file.size + stat_vfs.f_bsize - 1) / stat_vfs.f_bsize;
417 
418 	return 0;
419 }
420 
421 /**
422  * @brief Make a directory.
423  *
424  * See IEEE 1003.1
425  */
mkdir(const char * path,mode_t mode)426 int mkdir(const char *path, mode_t mode)
427 {
428 	int rc;
429 
430 	ARG_UNUSED(mode);
431 
432 	rc = fs_mkdir(path);
433 	if (rc < 0) {
434 		errno = -rc;
435 		return -1;
436 	}
437 
438 	return 0;
439 }
440 
fstat(int fildes,struct stat * buf)441 int fstat(int fildes, struct stat *buf)
442 {
443 	return zvfs_fstat(fildes, buf);
444 }
445 #ifdef CONFIG_POSIX_FILE_SYSTEM_ALIAS_FSTAT
446 FUNC_ALIAS(fstat, _fstat, int);
447 #endif
448 
449 /**
450  * @brief Remove a directory.
451  *
452  * See IEEE 1003.1
453  */
rmdir(const char * path)454 int rmdir(const char *path)
455 {
456 	return unlink(path);
457 }
458