xref: /nrf52832-nimble/rt-thread/components/dfs/src/dfs_file.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  * 2005-02-22     Bernard      The first version.
9  * 2011-12-08     Bernard      Merges rename patch from iamcacy.
10  * 2015-05-27     Bernard      Fix the fd clear issue.
11  * 2019-01-24     Bernard      Remove file repeatedly open check.
12  */
13 
14 #include <dfs.h>
15 #include <dfs_file.h>
16 #include <dfs_private.h>
17 
18 /**
19  * @addtogroup FileApi
20  */
21 
22 /*@{*/
23 
24 /**
25  * this function will open a file which specified by path with specified flags.
26  *
27  * @param fd the file descriptor pointer to return the corresponding result.
28  * @param path the specified file path.
29  * @param flags the flags for open operator.
30  *
31  * @return 0 on successful, -1 on failed.
32  */
dfs_file_open(struct dfs_fd * fd,const char * path,int flags)33 int dfs_file_open(struct dfs_fd *fd, const char *path, int flags)
34 {
35     struct dfs_filesystem *fs;
36     char *fullpath;
37     int result;
38 
39     /* parameter check */
40     if (fd == NULL)
41         return -EINVAL;
42 
43     /* make sure we have an absolute path */
44     fullpath = dfs_normalize_path(NULL, path);
45     if (fullpath == NULL)
46     {
47         return -ENOMEM;
48     }
49 
50     LOG_D("open file:%s", fullpath);
51 
52     /* find filesystem */
53     fs = dfs_filesystem_lookup(fullpath);
54     if (fs == NULL)
55     {
56         rt_free(fullpath); /* release path */
57 
58         return -ENOENT;
59     }
60 
61     LOG_D("open in filesystem:%s", fs->ops->name);
62     fd->fs    = fs;             /* set file system */
63     fd->fops  = fs->ops->fops;  /* set file ops */
64 
65     /* initialize the fd item */
66     fd->type  = FT_REGULAR;
67     fd->flags = flags;
68     fd->size  = 0;
69     fd->pos   = 0;
70     fd->data  = fs;
71 
72     if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
73     {
74         if (dfs_subdir(fs->path, fullpath) == NULL)
75             fd->path = rt_strdup("/");
76         else
77             fd->path = rt_strdup(dfs_subdir(fs->path, fullpath));
78         rt_free(fullpath);
79         LOG_D("Actual file path: %s", fd->path);
80     }
81     else
82     {
83         fd->path = fullpath;
84     }
85 
86     /* specific file system open routine */
87     if (fd->fops->open == NULL)
88     {
89         /* clear fd */
90         rt_free(fd->path);
91         fd->path = NULL;
92 
93         return -ENOSYS;
94     }
95 
96     if ((result = fd->fops->open(fd)) < 0)
97     {
98         /* clear fd */
99         rt_free(fd->path);
100         fd->path = NULL;
101 
102         LOG_E("open failed");
103 
104         return result;
105     }
106 
107     fd->flags |= DFS_F_OPEN;
108     if (flags & O_DIRECTORY)
109     {
110         fd->type = FT_DIRECTORY;
111         fd->flags |= DFS_F_DIRECTORY;
112     }
113 
114     LOG_I("open successful");
115     return 0;
116 }
117 
118 /**
119  * this function will close a file descriptor.
120  *
121  * @param fd the file descriptor to be closed.
122  *
123  * @return 0 on successful, -1 on failed.
124  */
dfs_file_close(struct dfs_fd * fd)125 int dfs_file_close(struct dfs_fd *fd)
126 {
127     int result = 0;
128 
129     if (fd == NULL)
130         return -ENXIO;
131 
132     if (fd->fops->close != NULL)
133         result = fd->fops->close(fd);
134 
135     /* close fd error, return */
136     if (result < 0)
137         return result;
138 
139     rt_free(fd->path);
140     fd->path = NULL;
141 
142     return result;
143 }
144 
145 /**
146  * this function will perform a io control on a file descriptor.
147  *
148  * @param fd the file descriptor.
149  * @param cmd the command to send to file descriptor.
150  * @param args the argument to send to file descriptor.
151  *
152  * @return 0 on successful, -1 on failed.
153  */
dfs_file_ioctl(struct dfs_fd * fd,int cmd,void * args)154 int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args)
155 {
156     if (fd == NULL)
157         return -EINVAL;
158 
159     /* regular file system fd */
160     if (fd->type == FT_REGULAR)
161     {
162         switch (cmd)
163         {
164         case F_GETFL:
165             return fd->flags; /* return flags */
166         case F_SETFL:
167             {
168                 int flags = (int)(rt_base_t)args;
169                 int mask  = O_NONBLOCK | O_APPEND;
170 
171                 flags &= mask;
172                 fd->flags &= ~mask;
173                 fd->flags |= flags;
174             }
175             return 0;
176         }
177     }
178 
179     if (fd->fops->ioctl != NULL)
180         return fd->fops->ioctl(fd, cmd, args);
181 
182     return -ENOSYS;
183 }
184 
185 /**
186  * this function will read specified length data from a file descriptor to a
187  * buffer.
188  *
189  * @param fd the file descriptor.
190  * @param buf the buffer to save the read data.
191  * @param len the length of data buffer to be read.
192  *
193  * @return the actual read data bytes or 0 on end of file or failed.
194  */
dfs_file_read(struct dfs_fd * fd,void * buf,size_t len)195 int dfs_file_read(struct dfs_fd *fd, void *buf, size_t len)
196 {
197     int result = 0;
198 
199     if (fd == NULL)
200         return -EINVAL;
201 
202     if (fd->fops->read == NULL)
203         return -ENOSYS;
204 
205     if ((result = fd->fops->read(fd, buf, len)) < 0)
206         fd->flags |= DFS_F_EOF;
207 
208     return result;
209 }
210 
211 /**
212  * this function will fetch directory entries from a directory descriptor.
213  *
214  * @param fd the directory descriptor.
215  * @param dirp the dirent buffer to save result.
216  * @param nbytes the available room in the buffer.
217  *
218  * @return the read dirent, others on failed.
219  */
dfs_file_getdents(struct dfs_fd * fd,struct dirent * dirp,size_t nbytes)220 int dfs_file_getdents(struct dfs_fd *fd, struct dirent *dirp, size_t nbytes)
221 {
222     /* parameter check */
223     if (fd == NULL || fd->type != FT_DIRECTORY)
224         return -EINVAL;
225 
226     if (fd->fops->getdents != NULL)
227         return fd->fops->getdents(fd, dirp, nbytes);
228 
229     return -ENOSYS;
230 }
231 
232 /**
233  * this function will unlink (remove) a specified path file from file system.
234  *
235  * @param path the specified path file to be unlinked.
236  *
237  * @return 0 on successful, -1 on failed.
238  */
dfs_file_unlink(const char * path)239 int dfs_file_unlink(const char *path)
240 {
241     int result;
242     char *fullpath;
243     struct dfs_filesystem *fs;
244 
245     /* Make sure we have an absolute path */
246     fullpath = dfs_normalize_path(NULL, path);
247     if (fullpath == NULL)
248     {
249         return -EINVAL;
250     }
251 
252     /* get filesystem */
253     if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
254     {
255         result = -ENOENT;
256         goto __exit;
257     }
258 
259     /* Check whether file is already open */
260     if (fd_is_open(fullpath) == 0)
261     {
262         result = -EBUSY;
263         goto __exit;
264     }
265 
266     if (fs->ops->unlink != NULL)
267     {
268         if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
269         {
270             if (dfs_subdir(fs->path, fullpath) == NULL)
271                 result = fs->ops->unlink(fs, "/");
272             else
273                 result = fs->ops->unlink(fs, dfs_subdir(fs->path, fullpath));
274         }
275         else
276             result = fs->ops->unlink(fs, fullpath);
277     }
278     else result = -ENOSYS;
279 
280 __exit:
281     rt_free(fullpath);
282     return result;
283 }
284 
285 /**
286  * this function will write some specified length data to file system.
287  *
288  * @param fd the file descriptor.
289  * @param buf the data buffer to be written.
290  * @param len the data buffer length
291  *
292  * @return the actual written data length.
293  */
dfs_file_write(struct dfs_fd * fd,const void * buf,size_t len)294 int dfs_file_write(struct dfs_fd *fd, const void *buf, size_t len)
295 {
296     if (fd == NULL)
297         return -EINVAL;
298 
299     if (fd->fops->write == NULL)
300         return -ENOSYS;
301 
302     return fd->fops->write(fd, buf, len);
303 }
304 
305 /**
306  * this function will flush buffer on a file descriptor.
307  *
308  * @param fd the file descriptor.
309  *
310  * @return 0 on successful, -1 on failed.
311  */
dfs_file_flush(struct dfs_fd * fd)312 int dfs_file_flush(struct dfs_fd *fd)
313 {
314     if (fd == NULL)
315         return -EINVAL;
316 
317     if (fd->fops->flush == NULL)
318         return -ENOSYS;
319 
320     return fd->fops->flush(fd);
321 }
322 
323 /**
324  * this function will seek the offset for specified file descriptor.
325  *
326  * @param fd the file descriptor.
327  * @param offset the offset to be sought.
328  *
329  * @return the current position after seek.
330  */
dfs_file_lseek(struct dfs_fd * fd,off_t offset)331 int dfs_file_lseek(struct dfs_fd *fd, off_t offset)
332 {
333     int result;
334 
335     if (fd == NULL)
336         return -EINVAL;
337 
338     if (fd->fops->lseek == NULL)
339         return -ENOSYS;
340 
341     result = fd->fops->lseek(fd, offset);
342 
343     /* update current position */
344     if (result >= 0)
345         fd->pos = result;
346 
347     return result;
348 }
349 
350 /**
351  * this function will get file information.
352  *
353  * @param path the file path.
354  * @param buf the data buffer to save stat description.
355  *
356  * @return 0 on successful, -1 on failed.
357  */
dfs_file_stat(const char * path,struct stat * buf)358 int dfs_file_stat(const char *path, struct stat *buf)
359 {
360     int result;
361     char *fullpath;
362     struct dfs_filesystem *fs;
363 
364     fullpath = dfs_normalize_path(NULL, path);
365     if (fullpath == NULL)
366     {
367         return -1;
368     }
369 
370     if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
371     {
372         LOG_E(
373                 "can't find mounted filesystem on this path:%s", fullpath);
374         rt_free(fullpath);
375 
376         return -ENOENT;
377     }
378 
379     if ((fullpath[0] == '/' && fullpath[1] == '\0') ||
380         (dfs_subdir(fs->path, fullpath) == NULL))
381     {
382         /* it's the root directory */
383         buf->st_dev   = 0;
384 
385         buf->st_mode  = S_IRUSR | S_IRGRP | S_IROTH |
386                         S_IWUSR | S_IWGRP | S_IWOTH;
387         buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
388 
389         buf->st_size    = 0;
390         buf->st_mtime   = 0;
391 
392         /* release full path */
393         rt_free(fullpath);
394 
395         return RT_EOK;
396     }
397     else
398     {
399         if (fs->ops->stat == NULL)
400         {
401             rt_free(fullpath);
402             LOG_E(
403                     "the filesystem didn't implement this function");
404 
405             return -ENOSYS;
406         }
407 
408         /* get the real file path and get file stat */
409         if (fs->ops->flags & DFS_FS_FLAG_FULLPATH)
410             result = fs->ops->stat(fs, fullpath, buf);
411         else
412             result = fs->ops->stat(fs, dfs_subdir(fs->path, fullpath), buf);
413     }
414 
415     rt_free(fullpath);
416 
417     return result;
418 }
419 
420 /**
421  * this function will rename an old path name to a new path name.
422  *
423  * @param oldpath the old path name.
424  * @param newpath the new path name.
425  *
426  * @return 0 on successful, -1 on failed.
427  */
dfs_file_rename(const char * oldpath,const char * newpath)428 int dfs_file_rename(const char *oldpath, const char *newpath)
429 {
430     int result;
431     struct dfs_filesystem *oldfs, *newfs;
432     char *oldfullpath, *newfullpath;
433 
434     result = RT_EOK;
435     newfullpath = NULL;
436     oldfullpath = NULL;
437 
438     oldfullpath = dfs_normalize_path(NULL, oldpath);
439     if (oldfullpath == NULL)
440     {
441         result = -ENOENT;
442         goto __exit;
443     }
444 
445     newfullpath = dfs_normalize_path(NULL, newpath);
446     if (newfullpath == NULL)
447     {
448         result = -ENOENT;
449         goto __exit;
450     }
451 
452     oldfs = dfs_filesystem_lookup(oldfullpath);
453     newfs = dfs_filesystem_lookup(newfullpath);
454 
455     if (oldfs == newfs)
456     {
457         if (oldfs->ops->rename == NULL)
458         {
459             result = -ENOSYS;
460         }
461         else
462         {
463             if (oldfs->ops->flags & DFS_FS_FLAG_FULLPATH)
464                 result = oldfs->ops->rename(oldfs, oldfullpath, newfullpath);
465             else
466                 /* use sub directory to rename in file system */
467                 result = oldfs->ops->rename(oldfs,
468                                             dfs_subdir(oldfs->path, oldfullpath),
469                                             dfs_subdir(newfs->path, newfullpath));
470         }
471     }
472     else
473     {
474         result = -EXDEV;
475     }
476 
477 __exit:
478     rt_free(oldfullpath);
479     rt_free(newfullpath);
480 
481     /* not at same file system, return EXDEV */
482     return result;
483 }
484 
485 #ifdef RT_USING_FINSH
486 #include <finsh.h>
487 
488 static struct dfs_fd fd;
489 static struct dirent dirent;
ls(const char * pathname)490 void ls(const char *pathname)
491 {
492     struct stat stat;
493     int length;
494     char *fullpath, *path;
495 
496     fullpath = NULL;
497     if (pathname == NULL)
498     {
499 #ifdef DFS_USING_WORKDIR
500         /* open current working directory */
501         path = rt_strdup(working_directory);
502 #else
503         path = rt_strdup("/");
504 #endif
505         if (path == NULL)
506             return ; /* out of memory */
507     }
508     else
509     {
510         path = (char *)pathname;
511     }
512 
513     /* list directory */
514     if (dfs_file_open(&fd, path, O_DIRECTORY) == 0)
515     {
516         rt_kprintf("Directory %s:\n", path);
517         do
518         {
519             memset(&dirent, 0, sizeof(struct dirent));
520             length = dfs_file_getdents(&fd, &dirent, sizeof(struct dirent));
521             if (length > 0)
522             {
523                 memset(&stat, 0, sizeof(struct stat));
524 
525                 /* build full path for each file */
526                 fullpath = dfs_normalize_path(path, dirent.d_name);
527                 if (fullpath == NULL)
528                     break;
529 
530                 if (dfs_file_stat(fullpath, &stat) == 0)
531                 {
532                     rt_kprintf("%-20s", dirent.d_name);
533                     if (S_ISDIR(stat.st_mode))
534                     {
535                         rt_kprintf("%-25s\n", "<DIR>");
536                     }
537                     else
538                     {
539                         rt_kprintf("%-25lu\n", stat.st_size);
540                     }
541                 }
542                 else
543                     rt_kprintf("BAD file: %s\n", dirent.d_name);
544                 rt_free(fullpath);
545             }
546         }while(length > 0);
547 
548         dfs_file_close(&fd);
549     }
550     else
551     {
552         rt_kprintf("No such directory\n");
553     }
554     if (pathname == NULL)
555         rt_free(path);
556 }
557 FINSH_FUNCTION_EXPORT(ls, list directory contents);
558 
rm(const char * filename)559 void rm(const char *filename)
560 {
561     if (dfs_file_unlink(filename) < 0)
562     {
563         rt_kprintf("Delete %s failed\n", filename);
564     }
565 }
566 FINSH_FUNCTION_EXPORT(rm, remove files or directories);
567 
cat(const char * filename)568 void cat(const char* filename)
569 {
570     uint32_t length;
571     char buffer[81];
572 
573     if (dfs_file_open(&fd, filename, O_RDONLY) < 0)
574     {
575         rt_kprintf("Open %s failed\n", filename);
576 
577         return;
578     }
579 
580     do
581     {
582         memset(buffer, 0, sizeof(buffer));
583         length = dfs_file_read(&fd, buffer, sizeof(buffer)-1 );
584         if (length > 0)
585         {
586             rt_kprintf("%s", buffer);
587         }
588     }while (length > 0);
589 
590     dfs_file_close(&fd);
591 }
592 FINSH_FUNCTION_EXPORT(cat, print file);
593 
594 #define BUF_SZ  4096
copyfile(const char * src,const char * dst)595 static void copyfile(const char *src, const char *dst)
596 {
597     struct dfs_fd src_fd;
598     rt_uint8_t *block_ptr;
599     rt_int32_t read_bytes;
600 
601     block_ptr = rt_malloc(BUF_SZ);
602     if (block_ptr == NULL)
603     {
604         rt_kprintf("out of memory\n");
605 
606         return;
607     }
608 
609     if (dfs_file_open(&src_fd, src, O_RDONLY) < 0)
610     {
611         rt_free(block_ptr);
612         rt_kprintf("Read %s failed\n", src);
613 
614         return;
615     }
616     if (dfs_file_open(&fd, dst, O_WRONLY | O_CREAT) < 0)
617     {
618         rt_free(block_ptr);
619         dfs_file_close(&src_fd);
620 
621         rt_kprintf("Write %s failed\n", dst);
622 
623         return;
624     }
625 
626     do
627     {
628         read_bytes = dfs_file_read(&src_fd, block_ptr, BUF_SZ);
629         if (read_bytes > 0)
630         {
631             int length;
632 
633             length = dfs_file_write(&fd, block_ptr, read_bytes);
634             if (length != read_bytes)
635             {
636                 /* write failed. */
637                 rt_kprintf("Write file data failed, errno=%d\n", length);
638                 break;
639             }
640         }
641     } while (read_bytes > 0);
642 
643     dfs_file_close(&src_fd);
644     dfs_file_close(&fd);
645     rt_free(block_ptr);
646 }
647 
648 extern int mkdir(const char *path, mode_t mode);
copydir(const char * src,const char * dst)649 static void copydir(const char * src, const char * dst)
650 {
651     struct dirent dirent;
652     struct stat stat;
653     int length;
654     struct dfs_fd cpfd;
655     if (dfs_file_open(&cpfd, src, O_DIRECTORY) < 0)
656     {
657         rt_kprintf("open %s failed\n", src);
658         return ;
659     }
660 
661     do
662     {
663         memset(&dirent, 0, sizeof(struct dirent));
664 
665         length = dfs_file_getdents(&cpfd, &dirent, sizeof(struct dirent));
666         if (length > 0)
667         {
668             char * src_entry_full = NULL;
669             char * dst_entry_full = NULL;
670 
671             if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0)
672                 continue;
673 
674             /* build full path for each file */
675             if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL)
676             {
677                 rt_kprintf("out of memory!\n");
678                 break;
679             }
680             if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL)
681             {
682                 rt_kprintf("out of memory!\n");
683                 rt_free(src_entry_full);
684                 break;
685             }
686 
687             memset(&stat, 0, sizeof(struct stat));
688             if (dfs_file_stat(src_entry_full, &stat) != 0)
689             {
690                 rt_kprintf("open file: %s failed\n", dirent.d_name);
691                 continue;
692             }
693 
694             if (S_ISDIR(stat.st_mode))
695             {
696                 mkdir(dst_entry_full, 0);
697                 copydir(src_entry_full, dst_entry_full);
698             }
699             else
700             {
701                 copyfile(src_entry_full, dst_entry_full);
702             }
703             rt_free(src_entry_full);
704             rt_free(dst_entry_full);
705         }
706     }while(length > 0);
707 
708     dfs_file_close(&cpfd);
709 }
710 
_get_path_lastname(const char * path)711 static const char *_get_path_lastname(const char *path)
712 {
713     char * ptr;
714     if ((ptr = strrchr(path, '/')) == NULL)
715         return path;
716 
717     /* skip the '/' then return */
718     return ++ptr;
719 }
copy(const char * src,const char * dst)720 void copy(const char *src, const char *dst)
721 {
722 #define FLAG_SRC_TYPE      0x03
723 #define FLAG_SRC_IS_DIR    0x01
724 #define FLAG_SRC_IS_FILE   0x02
725 #define FLAG_SRC_NON_EXSIT 0x00
726 
727 #define FLAG_DST_TYPE      0x0C
728 #define FLAG_DST_IS_DIR    0x04
729 #define FLAG_DST_IS_FILE   0x08
730 #define FLAG_DST_NON_EXSIT 0x00
731 
732     struct stat stat;
733     uint32_t flag = 0;
734 
735     /* check the staus of src and dst */
736     if (dfs_file_stat(src, &stat) < 0)
737     {
738         rt_kprintf("copy failed, bad %s\n", src);
739         return;
740     }
741     if (S_ISDIR(stat.st_mode))
742         flag |= FLAG_SRC_IS_DIR;
743     else
744         flag |= FLAG_SRC_IS_FILE;
745 
746     if (dfs_file_stat(dst, &stat) < 0)
747     {
748         flag |= FLAG_DST_NON_EXSIT;
749     }
750     else
751     {
752         if (S_ISDIR(stat.st_mode))
753             flag |= FLAG_DST_IS_DIR;
754         else
755             flag |= FLAG_DST_IS_FILE;
756     }
757 
758     //2. check status
759     if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
760     {
761         rt_kprintf("cp faild, cp dir to file is not permitted!\n");
762         return ;
763     }
764 
765     //3. do copy
766     if (flag & FLAG_SRC_IS_FILE)
767     {
768         if (flag & FLAG_DST_IS_DIR)
769         {
770             char * fdst;
771             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
772             if (fdst == NULL)
773             {
774                 rt_kprintf("out of memory\n");
775                 return;
776             }
777             copyfile(src, fdst);
778             rt_free(fdst);
779         }
780         else
781         {
782             copyfile(src, dst);
783         }
784     }
785     else //flag & FLAG_SRC_IS_DIR
786     {
787         if (flag & FLAG_DST_IS_DIR)
788         {
789             char * fdst;
790             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
791             if (fdst == NULL)
792             {
793                 rt_kprintf("out of memory\n");
794                 return;
795             }
796             mkdir(fdst, 0);
797             copydir(src, fdst);
798             rt_free(fdst);
799         }
800         else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT)
801         {
802             mkdir(dst, 0);
803             copydir(src, dst);
804         }
805         else
806         {
807             copydir(src, dst);
808         }
809     }
810 }
811 FINSH_FUNCTION_EXPORT(copy, copy file or dir)
812 
813 #endif
814 /* @} */
815 
816