xref: /nrf52832-nimble/rt-thread/components/dfs/src/dfs_posix.c (revision 104654410c56c573564690304ae786df310c91fc)
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