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