1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2009-05-27 Yi.qiu The first version
9 * 2018-02-07 Bernard Change the 3rd parameter of open/fcntl/ioctl to '...'
10 */
11
12 #include <dfs.h>
13 #include <dfs_posix.h>
14 #include "dfs_private.h"
15
16 /**
17 * @addtogroup FsPosixApi
18 */
19
20 /*@{*/
21
22 /**
23 * this function is a POSIX compliant version, which will open a file and
24 * return a file descriptor according specified flags.
25 *
26 * @param file the path name of file.
27 * @param flags the file open flags.
28 *
29 * @return the non-negative integer on successful open, others for failed.
30 */
open(const char * file,int flags,...)31 int open(const char *file, int flags, ...)
32 {
33 int fd, result;
34 struct dfs_fd *d;
35
36 /* allocate a fd */
37 fd = fd_new();
38 if (fd < 0)
39 {
40 rt_set_errno(-ENOMEM);
41
42 return -1;
43 }
44 d = fd_get(fd);
45
46 result = dfs_file_open(d, file, flags);
47 if (result < 0)
48 {
49 /* release the ref-count of fd */
50 fd_put(d);
51 fd_put(d);
52
53 rt_set_errno(result);
54
55 return -1;
56 }
57
58 /* release the ref-count of fd */
59 fd_put(d);
60
61 return fd;
62 }
63 RTM_EXPORT(open);
64
65 /**
66 * this function is a POSIX compliant version, which will close the open
67 * file descriptor.
68 *
69 * @param fd the file descriptor.
70 *
71 * @return 0 on successful, -1 on failed.
72 */
close(int fd)73 int close(int fd)
74 {
75 int result;
76 struct dfs_fd *d;
77
78 d = fd_get(fd);
79 if (d == NULL)
80 {
81 rt_set_errno(-EBADF);
82
83 return -1;
84 }
85
86 result = dfs_file_close(d);
87 fd_put(d);
88
89 if (result < 0)
90 {
91 rt_set_errno(result);
92
93 return -1;
94 }
95
96 fd_put(d);
97
98 return 0;
99 }
100 RTM_EXPORT(close);
101
102 /**
103 * this function is a POSIX compliant version, which will read specified data
104 * buffer length for an open file descriptor.
105 *
106 * @param fd the file descriptor.
107 * @param buf the buffer to save the read data.
108 * @param len the maximal length of data buffer
109 *
110 * @return the actual read data buffer length. If the returned value is 0, it
111 * may be reach the end of file, please check errno.
112 */
113 #if defined(RT_USING_NEWLIB) && defined(_EXFUN)
114 _READ_WRITE_RETURN_TYPE _EXFUN(read, (int fd, void *buf, size_t len))
115 #else
116 int read(int fd, void *buf, size_t len)
117 #endif
118 {
119 int result;
120 struct dfs_fd *d;
121
122 /* get the fd */
123 d = fd_get(fd);
124 if (d == NULL)
125 {
126 rt_set_errno(-EBADF);
127
128 return -1;
129 }
130
131 result = dfs_file_read(d, buf, len);
132 if (result < 0)
133 {
134 fd_put(d);
135 rt_set_errno(result);
136
137 return -1;
138 }
139
140 /* release the ref-count of fd */
141 fd_put(d);
142
143 return result;
144 }
145 RTM_EXPORT(read);
146
147 /**
148 * this function is a POSIX compliant version, which will write specified data
149 * buffer length for an open file descriptor.
150 *
151 * @param fd the file descriptor
152 * @param buf the data buffer to be written.
153 * @param len the data buffer length.
154 *
155 * @return the actual written data buffer length.
156 */
157 #if defined(RT_USING_NEWLIB) && defined(_EXFUN)
158 _READ_WRITE_RETURN_TYPE _EXFUN(write, (int fd, const void *buf, size_t len))
159 #else
160 int write(int fd, const void *buf, size_t len)
161 #endif
162 {
163 int result;
164 struct dfs_fd *d;
165
166 /* get the fd */
167 d = fd_get(fd);
168 if (d == NULL)
169 {
170 rt_set_errno(-EBADF);
171
172 return -1;
173 }
174
175 result = dfs_file_write(d, buf, len);
176 if (result < 0)
177 {
178 fd_put(d);
179 rt_set_errno(result);
180
181 return -1;
182 }
183
184 /* release the ref-count of fd */
185 fd_put(d);
186
187 return result;
188 }
189 RTM_EXPORT(write);
190
191 /**
192 * this function is a POSIX compliant version, which will seek the offset for
193 * an open file descriptor.
194 *
195 * @param fd the file descriptor.
196 * @param offset the offset to be seeked.
197 * @param whence the directory of seek.
198 *
199 * @return the current read/write position in the file, or -1 on failed.
200 */
lseek(int fd,off_t offset,int whence)201 off_t lseek(int fd, off_t offset, int whence)
202 {
203 int result;
204 struct dfs_fd *d;
205
206 d = fd_get(fd);
207 if (d == NULL)
208 {
209 rt_set_errno(-EBADF);
210
211 return -1;
212 }
213
214 switch (whence)
215 {
216 case SEEK_SET:
217 break;
218
219 case SEEK_CUR:
220 offset += d->pos;
221 break;
222
223 case SEEK_END:
224 offset += d->size;
225 break;
226
227 default:
228 fd_put(d);
229 rt_set_errno(-EINVAL);
230
231 return -1;
232 }
233
234 if (offset < 0)
235 {
236 fd_put(d);
237 rt_set_errno(-EINVAL);
238
239 return -1;
240 }
241 result = dfs_file_lseek(d, offset);
242 if (result < 0)
243 {
244 fd_put(d);
245 rt_set_errno(result);
246
247 return -1;
248 }
249
250 /* release the ref-count of fd */
251 fd_put(d);
252
253 return offset;
254 }
255 RTM_EXPORT(lseek);
256
257 /**
258 * this function is a POSIX compliant version, which will rename old file name
259 * to new file name.
260 *
261 * @param old the old file name.
262 * @param new the new file name.
263 *
264 * @return 0 on successful, -1 on failed.
265 *
266 * note: the old and new file name must be belong to a same file system.
267 */
rename(const char * old,const char * new)268 int rename(const char *old, const char *new)
269 {
270 int result;
271
272 result = dfs_file_rename(old, new);
273 if (result < 0)
274 {
275 rt_set_errno(result);
276
277 return -1;
278 }
279
280 return 0;
281 }
282 RTM_EXPORT(rename);
283
284 /**
285 * this function is a POSIX compliant version, which will unlink (remove) a
286 * specified path file from file system.
287 *
288 * @param pathname the specified path name to be unlinked.
289 *
290 * @return 0 on successful, -1 on failed.
291 */
unlink(const char * pathname)292 int unlink(const char *pathname)
293 {
294 int result;
295
296 result = dfs_file_unlink(pathname);
297 if (result < 0)
298 {
299 rt_set_errno(result);
300
301 return -1;
302 }
303
304 return 0;
305 }
306 RTM_EXPORT(unlink);
307
308 #ifndef _WIN32 /* we can not implement these functions */
309 /**
310 * this function is a POSIX compliant version, which will get file information.
311 *
312 * @param file the file name
313 * @param buf the data buffer to save stat description.
314 *
315 * @return 0 on successful, -1 on failed.
316 */
stat(const char * file,struct stat * buf)317 int stat(const char *file, struct stat *buf)
318 {
319 int result;
320
321 result = dfs_file_stat(file, buf);
322 if (result < 0)
323 {
324 rt_set_errno(result);
325
326 return -1;
327 }
328
329 return result;
330 }
331 RTM_EXPORT(stat);
332
333 /**
334 * this function is a POSIX compliant version, which will get file status.
335 *
336 * @param fildes the file description
337 * @param buf the data buffer to save stat description.
338 *
339 * @return 0 on successful, -1 on failed.
340 */
fstat(int fildes,struct stat * buf)341 int fstat(int fildes, struct stat *buf)
342 {
343 struct dfs_fd *d;
344
345 /* get the fd */
346 d = fd_get(fildes);
347 if (d == NULL)
348 {
349 rt_set_errno(-EBADF);
350
351 return -1;
352 }
353
354 /* it's the root directory */
355 buf->st_dev = 0;
356
357 buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
358 S_IWUSR | S_IWGRP | S_IWOTH;
359 if (d->type == FT_DIRECTORY)
360 {
361 buf->st_mode &= ~S_IFREG;
362 buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
363 }
364
365 buf->st_size = d->size;
366 buf->st_mtime = 0;
367
368 fd_put(d);
369
370 return RT_EOK;
371 }
372 RTM_EXPORT(fstat);
373 #endif
374
375 /**
376 * this function is a POSIX compliant version, which shall request that all data
377 * for the open file descriptor named by fildes is to be transferred to the storage
378 * device associated with the file described by fildes.
379 *
380 * @param fildes the file description
381 *
382 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno
383 * set to indicate the error.
384 */
fsync(int fildes)385 int fsync(int fildes)
386 {
387 int ret;
388 struct dfs_fd *d;
389
390 /* get the fd */
391 d = fd_get(fildes);
392 if (d == NULL)
393 {
394 rt_set_errno(-EBADF);
395 return -1;
396 }
397
398 ret = dfs_file_flush(d);
399
400 fd_put(d);
401 return ret;
402 }
403 RTM_EXPORT(fsync);
404
405 /**
406 * this function is a POSIX compliant version, which shall perform a variety of
407 * control functions on devices.
408 *
409 * @param fildes the file description
410 * @param cmd the specified command
411 * @param data represents the additional information that is needed by this
412 * specific device to perform the requested function.
413 *
414 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno
415 * set to indicate the error.
416 */
fcntl(int fildes,int cmd,...)417 int fcntl(int fildes, int cmd, ...)
418 {
419 int ret = -1;
420 struct dfs_fd *d;
421
422 /* get the fd */
423 d = fd_get(fildes);
424 if (d)
425 {
426 void* arg;
427 va_list ap;
428
429 va_start(ap, cmd);
430 arg = va_arg(ap, void*);
431 va_end(ap);
432
433 ret = dfs_file_ioctl(d, cmd, arg);
434 fd_put(d);
435 }
436 else ret = -EBADF;
437
438 if (ret < 0)
439 {
440 rt_set_errno(ret);
441 ret = -1;
442 }
443
444 return ret;
445 }
446 RTM_EXPORT(fcntl);
447
448 /**
449 * this function is a POSIX compliant version, which shall perform a variety of
450 * control functions on devices.
451 *
452 * @param fildes the file description
453 * @param cmd the specified command
454 * @param data represents the additional information that is needed by this
455 * specific device to perform the requested function.
456 *
457 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno
458 * set to indicate the error.
459 */
ioctl(int fildes,int cmd,...)460 int ioctl(int fildes, int cmd, ...)
461 {
462 void* arg;
463 va_list ap;
464
465 va_start(ap, cmd);
466 arg = va_arg(ap, void*);
467 va_end(ap);
468
469 /* we use fcntl for this API. */
470 return fcntl(fildes, cmd, arg);
471 }
472 RTM_EXPORT(ioctl);
473
474 /**
475 * this function is a POSIX compliant version, which will return the
476 * information about a mounted file system.
477 *
478 * @param path the path which mounted file system.
479 * @param buf the buffer to save the returned information.
480 *
481 * @return 0 on successful, others on failed.
482 */
statfs(const char * path,struct statfs * buf)483 int statfs(const char *path, struct statfs *buf)
484 {
485 int result;
486
487 result = dfs_statfs(path, buf);
488 if (result < 0)
489 {
490 rt_set_errno(result);
491
492 return -1;
493 }
494
495 return result;
496 }
497 RTM_EXPORT(statfs);
498
499 /**
500 * this function is a POSIX compliant version, which will make a directory
501 *
502 * @param path the directory path to be made.
503 * @param mode
504 *
505 * @return 0 on successful, others on failed.
506 */
mkdir(const char * path,mode_t mode)507 int mkdir(const char *path, mode_t mode)
508 {
509 int fd;
510 struct dfs_fd *d;
511 int result;
512
513 fd = fd_new();
514 if (fd == -1)
515 {
516 rt_set_errno(-ENOMEM);
517
518 return -1;
519 }
520
521 d = fd_get(fd);
522
523 result = dfs_file_open(d, path, O_DIRECTORY | O_CREAT);
524
525 if (result < 0)
526 {
527 fd_put(d);
528 fd_put(d);
529 rt_set_errno(result);
530
531 return -1;
532 }
533
534 dfs_file_close(d);
535 fd_put(d);
536 fd_put(d);
537
538 return 0;
539 }
540 RTM_EXPORT(mkdir);
541
542 #ifdef RT_USING_FINSH
543 #include <finsh.h>
544 FINSH_FUNCTION_EXPORT(mkdir, create a directory);
545 #endif
546
547 /**
548 * this function is a POSIX compliant version, which will remove a directory.
549 *
550 * @param pathname the path name to be removed.
551 *
552 * @return 0 on successful, others on failed.
553 */
rmdir(const char * pathname)554 int rmdir(const char *pathname)
555 {
556 int result;
557
558 result = dfs_file_unlink(pathname);
559 if (result < 0)
560 {
561 rt_set_errno(result);
562
563 return -1;
564 }
565
566 return 0;
567 }
568 RTM_EXPORT(rmdir);
569
570 /**
571 * this function is a POSIX compliant version, which will open a directory.
572 *
573 * @param name the path name to be open.
574 *
575 * @return the DIR pointer of directory, NULL on open directory failed.
576 */
opendir(const char * name)577 DIR *opendir(const char *name)
578 {
579 struct dfs_fd *d;
580 int fd, result;
581 DIR *t;
582
583 t = NULL;
584
585 /* allocate a fd */
586 fd = fd_new();
587 if (fd == -1)
588 {
589 rt_set_errno(-ENOMEM);
590
591 return NULL;
592 }
593 d = fd_get(fd);
594
595 result = dfs_file_open(d, name, O_RDONLY | O_DIRECTORY);
596 if (result >= 0)
597 {
598 /* open successfully */
599 t = (DIR *) rt_malloc(sizeof(DIR));
600 if (t == NULL)
601 {
602 dfs_file_close(d);
603 fd_put(d);
604 }
605 else
606 {
607 memset(t, 0, sizeof(DIR));
608
609 t->fd = fd;
610 }
611 fd_put(d);
612
613 return t;
614 }
615
616 /* open failed */
617 fd_put(d);
618 fd_put(d);
619 rt_set_errno(result);
620
621 return NULL;
622 }
623 RTM_EXPORT(opendir);
624
625 /**
626 * this function is a POSIX compliant version, which will return a pointer
627 * to a dirent structure representing the next directory entry in the
628 * directory stream.
629 *
630 * @param d the directory stream pointer.
631 *
632 * @return the next directory entry, NULL on the end of directory or failed.
633 */
readdir(DIR * d)634 struct dirent *readdir(DIR *d)
635 {
636 int result;
637 struct dfs_fd *fd;
638
639 fd = fd_get(d->fd);
640 if (fd == NULL)
641 {
642 rt_set_errno(-EBADF);
643 return NULL;
644 }
645
646 if (d->num)
647 {
648 struct dirent* dirent_ptr;
649 dirent_ptr = (struct dirent*)&d->buf[d->cur];
650 d->cur += dirent_ptr->d_reclen;
651 }
652
653 if (!d->num || d->cur >= d->num)
654 {
655 /* get a new entry */
656 result = dfs_file_getdents(fd,
657 (struct dirent*)d->buf,
658 sizeof(d->buf) - 1);
659 if (result <= 0)
660 {
661 fd_put(fd);
662 rt_set_errno(result);
663
664 return NULL;
665 }
666
667 d->num = result;
668 d->cur = 0; /* current entry index */
669 }
670
671 fd_put(fd);
672
673 return (struct dirent *)(d->buf+d->cur);
674 }
675 RTM_EXPORT(readdir);
676
677 /**
678 * this function is a POSIX compliant version, which will return current
679 * location in directory stream.
680 *
681 * @param d the directory stream pointer.
682 *
683 * @return the current location in directory stream.
684 */
telldir(DIR * d)685 long telldir(DIR *d)
686 {
687 struct dfs_fd *fd;
688 long result;
689
690 fd = fd_get(d->fd);
691 if (fd == NULL)
692 {
693 rt_set_errno(-EBADF);
694
695 return 0;
696 }
697
698 result = fd->pos - d->num + d->cur;
699 fd_put(fd);
700
701 return result;
702 }
703 RTM_EXPORT(telldir);
704
705 /**
706 * this function is a POSIX compliant version, which will set position of
707 * next directory structure in the directory stream.
708 *
709 * @param d the directory stream.
710 * @param offset the offset in directory stream.
711 */
seekdir(DIR * d,off_t offset)712 void seekdir(DIR *d, off_t offset)
713 {
714 struct dfs_fd *fd;
715
716 fd = fd_get(d->fd);
717 if (fd == NULL)
718 {
719 rt_set_errno(-EBADF);
720
721 return ;
722 }
723
724 /* seek to the offset position of directory */
725 if (dfs_file_lseek(fd, offset) >= 0)
726 d->num = d->cur = 0;
727 fd_put(fd);
728 }
729 RTM_EXPORT(seekdir);
730
731 /**
732 * this function is a POSIX compliant version, which will reset directory
733 * stream.
734 *
735 * @param d the directory stream.
736 */
rewinddir(DIR * d)737 void rewinddir(DIR *d)
738 {
739 struct dfs_fd *fd;
740
741 fd = fd_get(d->fd);
742 if (fd == NULL)
743 {
744 rt_set_errno(-EBADF);
745
746 return ;
747 }
748
749 /* seek to the beginning of directory */
750 if (dfs_file_lseek(fd, 0) >= 0)
751 d->num = d->cur = 0;
752 fd_put(fd);
753 }
754 RTM_EXPORT(rewinddir);
755
756 /**
757 * this function is a POSIX compliant version, which will close a directory
758 * stream.
759 *
760 * @param d the directory stream.
761 *
762 * @return 0 on successful, -1 on failed.
763 */
closedir(DIR * d)764 int closedir(DIR *d)
765 {
766 int result;
767 struct dfs_fd *fd;
768
769 fd = fd_get(d->fd);
770 if (fd == NULL)
771 {
772 rt_set_errno(-EBADF);
773
774 return -1;
775 }
776
777 result = dfs_file_close(fd);
778 fd_put(fd);
779
780 fd_put(fd);
781 rt_free(d);
782
783 if (result < 0)
784 {
785 rt_set_errno(result);
786
787 return -1;
788 }
789 else
790 return 0;
791 }
792 RTM_EXPORT(closedir);
793
794 #ifdef DFS_USING_WORKDIR
795 /**
796 * this function is a POSIX compliant version, which will change working
797 * directory.
798 *
799 * @param path the path name to be changed to.
800 *
801 * @return 0 on successful, -1 on failed.
802 */
chdir(const char * path)803 int chdir(const char *path)
804 {
805 char *fullpath;
806 DIR *d;
807
808 if (path == NULL)
809 {
810 dfs_lock();
811 rt_kprintf("%s\n", working_directory);
812 dfs_unlock();
813
814 return 0;
815 }
816
817 if (strlen(path) > DFS_PATH_MAX)
818 {
819 rt_set_errno(-ENOTDIR);
820
821 return -1;
822 }
823
824 fullpath = dfs_normalize_path(NULL, path);
825 if (fullpath == NULL)
826 {
827 rt_set_errno(-ENOTDIR);
828
829 return -1; /* build path failed */
830 }
831
832 dfs_lock();
833 d = opendir(fullpath);
834 if (d == NULL)
835 {
836 rt_free(fullpath);
837 /* this is a not exist directory */
838 dfs_unlock();
839
840 return -1;
841 }
842
843 /* close directory stream */
844 closedir(d);
845
846 /* copy full path to working directory */
847 strncpy(working_directory, fullpath, DFS_PATH_MAX);
848 /* release normalize directory path name */
849 rt_free(fullpath);
850
851 dfs_unlock();
852
853 return 0;
854 }
855 RTM_EXPORT(chdir);
856
857 #ifdef RT_USING_FINSH
858 FINSH_FUNCTION_EXPORT_ALIAS(chdir, cd, change current working directory);
859 #endif
860 #endif
861
862 /**
863 * this function is a POSIX compliant version, which shall check the file named
864 * by the pathname pointed to by the path argument for accessibility according
865 * to the bit pattern contained in amode.
866 *
867 * @param path the specified file/dir path.
868 * @param amode the value is either the bitwise-inclusive OR of the access
869 * permissions to be checked (R_OK, W_OK, X_OK) or the existence test (F_OK).
870 */
access(const char * path,int amode)871 int access(const char *path, int amode)
872 {
873 struct stat sb;
874 if (stat(path, &sb) < 0)
875 return -1; /* already sets errno */
876
877 /* ignore R_OK,W_OK,X_OK condition */
878 return 0;
879 }
880
881 /**
882 * this function is a POSIX compliant version, which will return current
883 * working directory.
884 *
885 * @param buf the returned current directory.
886 * @param size the buffer size.
887 *
888 * @return the returned current directory.
889 */
getcwd(char * buf,size_t size)890 char *getcwd(char *buf, size_t size)
891 {
892 #ifdef DFS_USING_WORKDIR
893 dfs_lock();
894 strncpy(buf, working_directory, size);
895 dfs_unlock();
896 #else
897 rt_kprintf(NO_WORKING_DIR);
898 #endif
899
900 return buf;
901 }
902 RTM_EXPORT(getcwd);
903
904 /* @} */
905