1 /*
2  * Copyright (c) 2006-2024 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 #include <unistd.h>
18 
19 #define DFS_VNODE_HASH_NR 128
20 
21 /*dfs vnode manager, for saving and searching vnodes.*/
22 struct dfs_vnode_mgr
23 {
24     struct rt_mutex lock; /* mutex for protecting dfs vnode lists */
25     rt_list_t head[DFS_VNODE_HASH_NR]; /* a group of dfs vnode lists, the dfs vnode is inserted to one of the lists
26                                         according to path string's hash-value mod DFS_VNODE_HASH_NR. */
27 };
28 
29 static struct dfs_vnode_mgr dfs_fm;
30 
dfs_fm_lock(void)31 void dfs_fm_lock(void)
32 {
33     rt_mutex_take(&dfs_fm.lock, RT_WAITING_FOREVER);
34 }
35 
dfs_fm_unlock(void)36 void dfs_fm_unlock(void)
37 {
38     rt_mutex_release(&dfs_fm.lock);
39 }
40 
41 /**
42  * @brief Initialize dfs vnode manager structure, including a lock and hash tables for vnode.
43  *
44  */
dfs_vnode_mgr_init(void)45 void dfs_vnode_mgr_init(void)
46 {
47     int i = 0;
48 
49     rt_mutex_init(&dfs_fm.lock, "dfs_mgr", RT_IPC_FLAG_PRIO);
50     for (i = 0; i < DFS_VNODE_HASH_NR; i++)
51     {
52         rt_list_init(&dfs_fm.head[i]);
53     }
54 }
55 
56 /**
57  * @brief Initialize a DFS vnode structure.
58  *
59  * @param vnode Pointer to the DFS vnode structure to be initialized.
60  *              The caller must ensure this is a valid, allocated structure.
61  * @param type The type of the vnode, representing its role or category (e.g., regular file, directory).
62  * @param fops Pointer to the file operations structure associated with this vnode.
63  *             This structure defines the behavior of the vnode for operations such as open, read, write, etc.
64  *             If `fops` is NULL, the vnode will have no associated file operations.
65  *
66  * @return 0 on success, or a negative error code on failure.
67  *
68  * @note The caller should ensure that:
69  *       - The `vnode` pointer is valid and properly allocated.
70  *       - The `fops` pointer (if not NULL) points to a valid `struct dfs_file_ops`
71  *         instance, where all necessary function pointers are properly set.
72  */
dfs_vnode_init(struct dfs_vnode * vnode,int type,const struct dfs_file_ops * fops)73 int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops *fops)
74 {
75     if (vnode)
76     {
77         rt_memset(vnode, 0, sizeof(struct dfs_vnode));
78         vnode->type = type;
79         vnode->fops = fops;
80 
81         rt_list_init(&(vnode->list));
82         vnode->ref_count = 1;
83     }
84     return 0;
85 }
86 
87 /* BKDR Hash Function */
bkdr_hash(const char * str)88 static unsigned int bkdr_hash(const char *str)
89 {
90     unsigned int seed = 131; /* 31 131 1313 13131 131313 etc..*/
91     unsigned int hash = 0;
92 
93     while (*str)
94     {
95         hash = hash * seed + (*str++);
96     }
97 
98     return (hash % DFS_VNODE_HASH_NR);
99 }
100 
101 /**
102  * @brief Find a DFS vnode by its path.
103  *
104  * This function searches for a vnode in the vnode hash table using the specified path.
105  * If found, it returns a pointer to the vnode and updates the hash head if required.
106  *
107  * @param path The file path to search for. This should be a valid null-terminated string.
108  * @param hash_head Pointer to a location where the hash table head associated with the vnode
109  *                  can be stored. This can be NULL if the hash head is not needed.
110  *
111  * @return Pointer to the DFS vnode if found, or NULL if no vnode matches the specified path.
112  *
113  * @note The caller must ensure that:
114  *       - The `path` pointer is valid and points to a properly null-terminated string.
115  *       - If `hash_head` is not NULL, it points to a valid location to store the hash head.
116  */
dfs_vnode_find(const char * path,rt_list_t ** hash_head)117 static struct dfs_vnode *dfs_vnode_find(const char *path, rt_list_t **hash_head)
118 {
119     struct dfs_vnode *vnode = NULL;
120     int hash = bkdr_hash(path);
121     rt_list_t *hh;
122 
123     hh = dfs_fm.head[hash].next;
124 
125     if (hash_head)
126     {
127         *hash_head = &dfs_fm.head[hash];
128     }
129 
130     while (hh != &dfs_fm.head[hash])
131     {
132         vnode = rt_container_of(hh, struct dfs_vnode, list);
133         if (rt_strcmp(path, vnode->fullpath) == 0)
134         {
135             /* found */
136             return vnode;
137         }
138         hh = hh->next;
139     }
140     return NULL;
141 }
142 
143 /**
144  * @addtogroup group_fs_file_api
145  * @{
146  */
147 
148 /**
149  * This function will return whether this file has been opend.
150  *
151  * @param pathname the file path name.
152  *
153  * @return 0 on file has been open successfully, -1 on open failed.
154  */
dfs_file_is_open(const char * pathname)155 int dfs_file_is_open(const char *pathname)
156 {
157     char *fullpath = NULL;
158     struct dfs_vnode *vnode = NULL;
159     int ret = 0;
160 
161     fullpath = dfs_normalize_path(NULL, pathname);
162 
163     dfs_fm_lock();
164     vnode = dfs_vnode_find(fullpath, NULL);
165     if (vnode)
166     {
167         ret = 1;
168     }
169     dfs_fm_unlock();
170 
171     rt_free(fullpath);
172     return ret;
173 }
174 
175 
176 /**
177  * this function will open a file which specified by path with specified flags.
178  *
179  * @param fd the file descriptor pointer to return the corresponding result.
180  * @param path the specified file path.
181  * @param flags the flags for open operator.
182  *
183  * @return 0 on successful, -1 on failed.
184  */
dfs_file_open(struct dfs_file * fd,const char * path,int flags)185 int dfs_file_open(struct dfs_file *fd, const char *path, int flags)
186 {
187     struct dfs_filesystem *fs;
188     char *fullpath;
189     int result;
190     struct dfs_vnode *vnode = NULL;
191     rt_list_t *hash_head;
192 
193     /* parameter check */
194     if (fd == NULL)
195         return -EINVAL;
196 
197     /* make sure we have an absolute path */
198     fullpath = dfs_normalize_path(NULL, path);
199     if (fullpath == NULL)
200     {
201         return -ENOMEM;
202     }
203 
204     LOG_D("open file:%s", fullpath);
205 
206     dfs_fm_lock();
207     /* vnode find */
208     vnode = dfs_vnode_find(fullpath, &hash_head);
209     if (vnode)
210     {
211         vnode->ref_count++;
212         fd->pos   = 0;
213         fd->vnode = vnode;
214         dfs_fm_unlock();
215         rt_free(fullpath); /* release path */
216     }
217     else
218     {
219         /* find filesystem */
220         fs = dfs_filesystem_lookup(fullpath);
221         if (fs == NULL)
222         {
223             dfs_fm_unlock();
224             rt_free(fullpath); /* release path */
225             return -ENOENT;
226         }
227 
228         vnode = rt_calloc(1, sizeof(struct dfs_vnode));
229         if (!vnode)
230         {
231             dfs_fm_unlock();
232             rt_free(fullpath); /* release path */
233             return -ENOMEM;
234         }
235         vnode->ref_count = 1;
236 
237         LOG_D("open in filesystem:%s", fs->ops->name);
238         vnode->fs    = fs;             /* set file system */
239         vnode->fops  = fs->ops->fops;  /* set file ops */
240 
241         /* initialize the fd item */
242         vnode->type  = FT_REGULAR;
243         vnode->flags = 0;
244 
245         if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
246         {
247             if (dfs_subdir(fs->path, fullpath) == NULL)
248                 vnode->path = rt_strdup("/");
249             else
250                 vnode->path = rt_strdup(dfs_subdir(fs->path, fullpath));
251             LOG_D("Actual file path: %s", vnode->path);
252         }
253         else
254         {
255             vnode->path = fullpath;
256         }
257         vnode->fullpath = fullpath;
258 
259         /* specific file system open routine */
260         if (vnode->fops->open == NULL)
261         {
262             dfs_fm_unlock();
263             /* clear fd */
264             if (vnode->path != vnode->fullpath)
265             {
266                 rt_free(vnode->fullpath);
267             }
268             rt_free(vnode->path);
269             rt_free(vnode);
270 
271             return -ENOSYS;
272         }
273 
274         fd->pos   = 0;
275         fd->vnode = vnode;
276 
277         /* insert vnode to hash */
278         rt_list_insert_after(hash_head, &vnode->list);
279     }
280 
281     fd->flags = flags;
282 
283     if ((result = vnode->fops->open(fd)) < 0)
284     {
285         vnode->ref_count--;
286         if (vnode->ref_count == 0)
287         {
288             /* remove from hash */
289             rt_list_remove(&vnode->list);
290             /* clear fd */
291             if (vnode->path != vnode->fullpath)
292             {
293                 rt_free(vnode->fullpath);
294             }
295             rt_free(vnode->path);
296             fd->vnode = NULL;
297             rt_free(vnode);
298         }
299 
300         dfs_fm_unlock();
301         LOG_D("%s open failed", fullpath);
302 
303         return result;
304     }
305 
306     fd->flags |= DFS_F_OPEN;
307     if (flags & O_DIRECTORY)
308     {
309         fd->vnode->type = FT_DIRECTORY;
310         fd->flags |= DFS_F_DIRECTORY;
311     }
312     dfs_fm_unlock();
313 
314     LOG_D("open successful");
315     return 0;
316 }
317 
318 /**
319  * this function will close a file descriptor.
320  *
321  * @param fd the file descriptor to be closed.
322  *
323  * @return 0 on successful, -1 on failed.
324  */
dfs_file_close(struct dfs_file * fd)325 int dfs_file_close(struct dfs_file *fd)
326 {
327     struct dfs_vnode *vnode = NULL;
328     int result = 0;
329 
330     if (fd == NULL)
331     {
332         return -ENXIO;
333     }
334 
335     if (fd->ref_count == 1)
336     {
337         dfs_fm_lock();
338         vnode = fd->vnode;
339 
340         if (vnode->ref_count <= 0)
341         {
342             dfs_fm_unlock();
343             return -ENXIO;
344         }
345 
346         if (vnode->fops->close != NULL)
347         {
348             result = vnode->fops->close(fd);
349         }
350 
351         if (vnode->ref_count == 1)
352         {
353             /* remove from hash */
354             rt_list_remove(&vnode->list);
355             fd->vnode = NULL;
356 
357             if (vnode->path != vnode->fullpath)
358             {
359                 rt_free(vnode->fullpath);
360             }
361             rt_free(vnode->path);
362             rt_free(vnode);
363         }
364         dfs_fm_unlock();
365     }
366 
367     return result;
368 }
369 
370 /**
371  * this function will perform an io control on a file descriptor.
372  *
373  * @param fd the file descriptor.
374  * @param cmd the command to send to file descriptor.
375  * @param args the argument to send to file descriptor.
376  *        - When `cmd` is `F_SETFL`, an additional integer argument specifies the new status flags.
377  *
378  * @return 0 on successful, -1 on failed.
379  */
dfs_file_ioctl(struct dfs_file * fd,int cmd,void * args)380 int dfs_file_ioctl(struct dfs_file *fd, int cmd, void *args)
381 {
382     if (fd == NULL)
383     {
384         return -EINVAL;
385     }
386 
387     /* regular file system fd */
388     if (fd->vnode->type == FT_REGULAR || fd->vnode->type == FT_DEVICE)
389     {
390         switch (cmd)
391         {
392         case F_GETFL:
393             return fd->flags; /* return flags */
394         case F_SETFL:
395             {
396                 int flags = (int)(rt_base_t)args;
397                 int mask  = O_NONBLOCK | O_APPEND;
398 
399                 flags &= mask;
400                 fd->flags &= ~mask;
401                 fd->flags |= flags;
402             }
403             return 0;
404         }
405     }
406 
407     if (fd->vnode->fops->ioctl != NULL)
408     {
409         return fd->vnode->fops->ioctl(fd, cmd, args);
410     }
411 
412     return -ENOSYS;
413 }
414 
415 /**
416  * this function will read specified length data from a file descriptor to a
417  * buffer.
418  *
419  * @param fd the file descriptor.
420  * @param buf the buffer to save the read data.
421  * @param len the length of data buffer to be read.
422  *
423  * @return the actual read data bytes or 0 on end of file or failed.
424  */
dfs_file_read(struct dfs_file * fd,void * buf,size_t len)425 ssize_t dfs_file_read(struct dfs_file *fd, void *buf, size_t len)
426 {
427     int result = 0;
428 
429     if (fd == NULL)
430     {
431         return -EINVAL;
432     }
433 
434     if (fd->vnode->fops->read == NULL)
435     {
436         return -ENOSYS;
437     }
438 
439     if ((result = fd->vnode->fops->read(fd, buf, len)) < 0)
440     {
441         fd->flags |= DFS_F_EOF;
442     }
443 
444     return result;
445 }
446 
447 /**
448  * this function will fetch directory entries from a directory descriptor.
449  *
450  * @param fd the directory descriptor.
451  * @param dirp the dirent buffer to save result.
452  * @param nbytes the available room in the buffer.
453  *
454  * @return the read dirent, others on failed.
455  */
dfs_file_getdents(struct dfs_file * fd,struct dirent * dirp,size_t nbytes)456 int dfs_file_getdents(struct dfs_file *fd, struct dirent *dirp, size_t nbytes)
457 {
458     /* parameter check */
459     if (fd == NULL)
460     {
461         return -EINVAL;
462     }
463 
464     if (fd->vnode->type != FT_DIRECTORY)
465     {
466         return -EINVAL;
467     }
468 
469     if (fd->vnode->fops->getdents != NULL)
470     {
471         return fd->vnode->fops->getdents(fd, dirp, nbytes);
472     }
473 
474     return -ENOSYS;
475 }
476 
477 /**
478  * this function will unlink (remove) a specified path file from file system.
479  *
480  * @param path the specified path file to be unlinked.
481  *
482  * @return 0 on successful, -1 on failed.
483  */
dfs_file_unlink(const char * path)484 int dfs_file_unlink(const char *path)
485 {
486     int result;
487     char *fullpath;
488     struct dfs_filesystem *fs;
489 
490     /* Make sure we have an absolute path */
491     fullpath = dfs_normalize_path(NULL, path);
492     if (fullpath == NULL)
493     {
494         return -EINVAL;
495     }
496 
497     /* Check whether file is already open */
498     if (dfs_file_is_open(fullpath))
499     {
500         result = -EBUSY;
501         goto __exit;
502     }
503 
504     /* get filesystem */
505     if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
506     {
507         result = -ENOENT;
508         goto __exit;
509     }
510 
511     if (fs->ops->unlink != NULL)
512     {
513         if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
514         {
515             if (dfs_subdir(fs->path, fullpath) == NULL)
516                 result = fs->ops->unlink(fs, "/");
517             else
518                 result = fs->ops->unlink(fs, dfs_subdir(fs->path, fullpath));
519         }
520         else
521             result = fs->ops->unlink(fs, fullpath);
522     }
523     else result = -ENOSYS;
524 
525 __exit:
526     rt_free(fullpath);
527     return result;
528 }
529 
530 /**
531  * this function will write some specified length data to file system.
532  *
533  * @param fd the file descriptor.
534  * @param buf the data buffer to be written.
535  * @param len the data buffer length
536  *
537  * @return the actual written data length.
538  */
dfs_file_write(struct dfs_file * fd,const void * buf,size_t len)539 ssize_t dfs_file_write(struct dfs_file *fd, const void *buf, size_t len)
540 {
541     if (fd == NULL)
542     {
543         return -EINVAL;
544     }
545 
546     if (fd->vnode->fops->write == NULL)
547     {
548         return -ENOSYS;
549     }
550 
551     return fd->vnode->fops->write(fd, buf, len);
552 }
553 
554 /**
555  * this function will flush buffer on a file descriptor.
556  *
557  * @param fd the file descriptor.
558  *
559  * @return 0 on successful, -1 on failed.
560  */
dfs_file_flush(struct dfs_file * fd)561 int dfs_file_flush(struct dfs_file *fd)
562 {
563     if (fd == NULL)
564         return -EINVAL;
565 
566     if (fd->vnode->fops->flush == NULL)
567         return -ENOSYS;
568 
569     return fd->vnode->fops->flush(fd);
570 }
571 
572 /**
573  * this function will seek the offset for specified file descriptor.
574  *
575  * @param fd the file descriptor.
576  * @param offset the offset to be sought.
577  *
578  * @return the current position after seek.
579  */
dfs_file_lseek(struct dfs_file * fd,off_t offset)580 off_t dfs_file_lseek(struct dfs_file *fd, off_t offset)
581 {
582     int result;
583 
584     if (fd == NULL)
585         return -EINVAL;
586 
587     if (fd->vnode->fops->lseek == NULL)
588         return -ENOSYS;
589 
590     result = fd->vnode->fops->lseek(fd, offset);
591 
592     /* update current position */
593     if (result >= 0)
594         fd->pos = result;
595 
596     return result;
597 }
598 
599 /**
600  * this function will get file information.
601  *
602  * @param path the file path.
603  * @param buf the data buffer to save stat description.
604  *
605  * @return 0 on successful, -1 on failed.
606  */
dfs_file_stat(const char * path,struct stat * buf)607 int dfs_file_stat(const char *path, struct stat *buf)
608 {
609     int result;
610     char *fullpath;
611     struct dfs_filesystem *fs;
612 
613     fullpath = dfs_normalize_path(NULL, path);
614     if (fullpath == NULL)
615     {
616         return -1;
617     }
618 
619     if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
620     {
621         LOG_E("can't find mounted filesystem on this path:%s", fullpath);
622         rt_free(fullpath);
623 
624         return -ENOENT;
625     }
626 
627     if (fs->ops->stat == NULL)
628     {
629         rt_free(fullpath);
630         LOG_E("the filesystem didn't implement this function");
631 
632         return -ENOSYS;
633     }
634     /* get the real file path and get file stat */
635     if (fs->ops->flags & DFS_FS_FLAG_FULLPATH)
636     {
637         result = fs->ops->stat(fs, fullpath, buf);
638     }
639     else
640     {
641         const char *subdir = dfs_subdir(fs->path, fullpath);
642         subdir = subdir ? subdir : "/";
643         result = fs->ops->stat(fs, subdir, buf);
644     }
645 
646     rt_free(fullpath);
647 
648     return result;
649 }
650 
651 /**
652  * this function will rename an old path name to a new path name.
653  *
654  * @param oldpath the old path name.
655  * @param newpath the new path name.
656  *
657  * @return 0 on successful, -1 on failed.
658  */
dfs_file_rename(const char * oldpath,const char * newpath)659 int dfs_file_rename(const char *oldpath, const char *newpath)
660 {
661     int result = RT_EOK;
662     struct dfs_filesystem *oldfs = NULL, *newfs = NULL;
663     char *oldfullpath = NULL, *newfullpath = NULL;
664 
665     newfullpath = NULL;
666     oldfullpath = NULL;
667 
668     oldfullpath = dfs_normalize_path(NULL, oldpath);
669     if (oldfullpath == NULL)
670     {
671         result = -ENOENT;
672         goto __exit;
673     }
674 
675     if (dfs_file_is_open((const char *)oldfullpath))
676     {
677         result = -EBUSY;
678         goto __exit;
679     }
680 
681     newfullpath = dfs_normalize_path(NULL, newpath);
682     if (newfullpath == NULL)
683     {
684         result = -ENOENT;
685         goto __exit;
686     }
687 
688     oldfs = dfs_filesystem_lookup(oldfullpath);
689     newfs = dfs_filesystem_lookup(newfullpath);
690 
691     if (oldfs == newfs)
692     {
693         if (oldfs->ops->rename == NULL)
694         {
695             result = -ENOSYS;
696         }
697         else
698         {
699             if (oldfs->ops->flags & DFS_FS_FLAG_FULLPATH)
700                 result = oldfs->ops->rename(oldfs, oldfullpath, newfullpath);
701             else
702                 /* use sub directory to rename in file system */
703                 result = oldfs->ops->rename(oldfs,
704                                             dfs_subdir(oldfs->path, oldfullpath),
705                                             dfs_subdir(newfs->path, newfullpath));
706         }
707     }
708     else
709     {
710         result = -EXDEV;
711     }
712 
713 __exit:
714     if (oldfullpath)
715     {
716         rt_free(oldfullpath);
717     }
718     if (newfullpath)
719     {
720         rt_free(newfullpath);
721     }
722 
723     /* not at same file system, return EXDEV */
724     return result;
725 }
726 
727 /**
728  * this function is will cause the regular file referenced by fd
729  * to be truncated to a size of precisely length bytes.
730  *
731  * @param fd the file descriptor.
732  * @param length the length to be truncated.
733  *
734  * @return the status of truncated.
735  */
dfs_file_ftruncate(struct dfs_file * fd,off_t length)736 int dfs_file_ftruncate(struct dfs_file *fd, off_t length)
737 {
738     int result;
739 
740     /* fd is null or not a regular file system fd, or length is invalid */
741     if (fd == NULL || fd->vnode->type != FT_REGULAR || length < 0)
742         return -EINVAL;
743 
744     if (fd->vnode->fops->ioctl == NULL)
745         return -ENOSYS;
746 
747     result = fd->vnode->fops->ioctl(fd, RT_FIOFTRUNCATE, (void*)&length);
748 
749     /* update current size */
750     if (result == 0)
751         fd->vnode->size = length;
752 
753     return result;
754 }
755 
756 #ifdef RT_USING_SMART
dfs_file_mmap2(struct dfs_file * fd,struct dfs_mmap2_args * mmap2)757 int dfs_file_mmap2(struct dfs_file *fd, struct dfs_mmap2_args *mmap2)
758 {
759     int ret = 0;
760 
761     if (fd && mmap2)
762     {
763         if (fd->vnode->type != FT_DEVICE || !fd->vnode->fops->ioctl)
764         {
765             rt_set_errno(EINVAL);
766         }
767         else if (fd->vnode->type == FT_DEVICE && fd->vnode->fops->ioctl)
768         {
769             ret = fd->vnode->fops->ioctl(fd, RT_FIOMMAP2, mmap2);
770             if (ret != 0)
771             {
772                 ret = ret > 0? ret : -ret;
773                 rt_set_errno(ret);
774             }
775         }
776     }
777 
778     return ret;
779 }
780 #endif
781 
782 #ifdef RT_USING_FINSH
783 #include <finsh.h>
784 
ls(const char * pathname)785 void ls(const char *pathname)
786 {
787     struct dfs_file fd;
788     struct dirent dirent;
789     struct stat stat;
790     int length;
791     char *fullpath, *path;
792 
793     fullpath = NULL;
794     if (pathname == NULL)
795     {
796 #ifdef DFS_USING_WORKDIR
797         /* open current working directory */
798         path = rt_strdup(working_directory);
799 #else
800         path = rt_strdup("/");
801 #endif
802         if (path == NULL)
803             return ; /* out of memory */
804     }
805     else
806     {
807         path = (char *)pathname;
808     }
809 
810     fd_init(&fd);
811     /* list directory */
812     if (dfs_file_open(&fd, path, O_DIRECTORY) == 0)
813     {
814         rt_kprintf("Directory %s:\n", path);
815         do
816         {
817             rt_memset(&dirent, 0, sizeof(struct dirent));
818             length = dfs_file_getdents(&fd, &dirent, sizeof(struct dirent));
819             if (length > 0)
820             {
821                 rt_memset(&stat, 0, sizeof(struct stat));
822 
823                 /* build full path for each file */
824                 fullpath = dfs_normalize_path(path, dirent.d_name);
825                 if (fullpath == NULL)
826                     break;
827 
828                 if (dfs_file_stat(fullpath, &stat) == 0)
829                 {
830                     rt_kprintf("%-20s", dirent.d_name);
831                     if (S_ISDIR(stat.st_mode))
832                     {
833                         rt_kprintf("%-25s\n", "<DIR>");
834                     }
835                     else
836                     {
837                         rt_kprintf("%-25lu\n", (unsigned long)stat.st_size);
838                     }
839                 }
840                 else
841                     rt_kprintf("BAD file: %s\n", dirent.d_name);
842                 rt_free(fullpath);
843             }
844         }
845         while (length > 0);
846 
847         dfs_file_close(&fd);
848     }
849     else
850     {
851         rt_kprintf("No such directory\n");
852     }
853     if (pathname == NULL)
854         rt_free(path);
855 }
856 FINSH_FUNCTION_EXPORT(ls, list directory contents);
857 
rm(const char * filename)858 void rm(const char *filename)
859 {
860     if (dfs_file_unlink(filename) < 0)
861     {
862         rt_kprintf("Delete %s failed\n", filename);
863     }
864 }
865 FINSH_FUNCTION_EXPORT(rm, remove files or directories);
866 
cat(const char * filename)867 void cat(const char *filename)
868 {
869     struct dfs_file fd;
870     int length = 0;
871     char buffer[81];
872 
873     fd_init(&fd);
874     if (dfs_file_open(&fd, filename, O_RDONLY) < 0)
875     {
876         rt_kprintf("Open %s failed\n", filename);
877 
878         return;
879     }
880 
881     do
882     {
883         rt_memset(buffer, 0x0, sizeof(buffer));
884         length = dfs_file_read(&fd, (void *)buffer, sizeof(buffer) - 1);
885         if (length > 0)
886         {
887             buffer[length] = '\0';
888             rt_device_t out_device = rt_console_get_device();
889             rt_device_write(out_device, 0, (void *)buffer, length);
890         }
891     } while (length > 0);
892     rt_kprintf("\n");
893 
894     dfs_file_close(&fd);
895 }
896 FINSH_FUNCTION_EXPORT(cat, print file);
897 
898 #ifdef DFS_USING_POSIX
899 #define BUF_SZ  4096
copyfile(const char * src,const char * dst)900 static void copyfile(const char *src, const char *dst)
901 {
902     struct dfs_file fd;
903     struct dfs_file src_fd;
904     rt_uint8_t *block_ptr;
905     rt_int32_t read_bytes;
906 
907     block_ptr = (rt_uint8_t *)rt_malloc(BUF_SZ);
908     if (block_ptr == NULL)
909     {
910         rt_kprintf("out of memory\n");
911 
912         return;
913     }
914 
915     fd_init(&src_fd);
916     if (dfs_file_open(&src_fd, src, O_RDONLY) < 0)
917     {
918         rt_free(block_ptr);
919         rt_kprintf("Read %s failed\n", src);
920 
921         return;
922     }
923     fd_init(&fd);
924     if (dfs_file_open(&fd, dst, O_WRONLY | O_CREAT | O_TRUNC) < 0)
925     {
926         rt_free(block_ptr);
927         dfs_file_close(&src_fd);
928 
929         rt_kprintf("Write %s failed\n", dst);
930 
931         return;
932     }
933 
934     do
935     {
936         read_bytes = dfs_file_read(&src_fd, block_ptr, BUF_SZ);
937         if (read_bytes > 0)
938         {
939             int length;
940 
941             length = dfs_file_write(&fd, block_ptr, read_bytes);
942             if (length != read_bytes)
943             {
944                 /* write failed. */
945                 rt_kprintf("Write file data failed, errno=%d\n", length);
946                 break;
947             }
948         }
949     }
950     while (read_bytes > 0);
951 
952     dfs_file_close(&src_fd);
953     dfs_file_close(&fd);
954     rt_free(block_ptr);
955 }
956 
957 extern int mkdir(const char *path, mode_t mode);
copydir(const char * src,const char * dst)958 static void copydir(const char *src, const char *dst)
959 {
960     struct dirent dirent;
961     struct stat stat;
962     int length;
963     struct dfs_file cpfd;
964     if (dfs_file_open(&cpfd, src, O_DIRECTORY) < 0)
965     {
966         rt_kprintf("open %s failed\n", src);
967         return ;
968     }
969 
970     do
971     {
972         rt_memset(&dirent, 0, sizeof(struct dirent));
973 
974         length = dfs_file_getdents(&cpfd, &dirent, sizeof(struct dirent));
975         if (length > 0)
976         {
977             char *src_entry_full = NULL;
978             char *dst_entry_full = NULL;
979 
980             if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0)
981                 continue;
982 
983             /* build full path for each file */
984             if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL)
985             {
986                 rt_kprintf("out of memory!\n");
987                 break;
988             }
989             if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL)
990             {
991                 rt_kprintf("out of memory!\n");
992                 rt_free(src_entry_full);
993                 break;
994             }
995 
996             rt_memset(&stat, 0, sizeof(struct stat));
997             if (dfs_file_stat(src_entry_full, &stat) != 0)
998             {
999                 rt_kprintf("open file: %s failed\n", dirent.d_name);
1000                 continue;
1001             }
1002 
1003             if (S_ISDIR(stat.st_mode))
1004             {
1005                 mkdir(dst_entry_full, 0);
1006                 copydir(src_entry_full, dst_entry_full);
1007             }
1008             else
1009             {
1010                 copyfile(src_entry_full, dst_entry_full);
1011             }
1012             rt_free(src_entry_full);
1013             rt_free(dst_entry_full);
1014         }
1015     }
1016     while (length > 0);
1017 
1018     dfs_file_close(&cpfd);
1019 }
1020 
_get_path_lastname(const char * path)1021 static const char *_get_path_lastname(const char *path)
1022 {
1023     char *ptr;
1024     if ((ptr = (char *)strrchr(path, '/')) == NULL)
1025         return path;
1026 
1027     /* skip the '/' then return */
1028     return ++ptr;
1029 }
1030 
copy(const char * src,const char * dst)1031 void copy(const char *src, const char *dst)
1032 {
1033 #define FLAG_SRC_TYPE      0x03
1034 #define FLAG_SRC_IS_DIR    0x01
1035 #define FLAG_SRC_IS_FILE   0x02
1036 #define FLAG_SRC_NON_EXSIT 0x00
1037 
1038 #define FLAG_DST_TYPE      0x0C
1039 #define FLAG_DST_IS_DIR    0x04
1040 #define FLAG_DST_IS_FILE   0x08
1041 #define FLAG_DST_NON_EXSIT 0x00
1042 
1043     struct stat stat;
1044     uint32_t flag = 0;
1045 
1046     /* check the staus of src and dst */
1047     if (dfs_file_stat(src, &stat) < 0)
1048     {
1049         rt_kprintf("copy failed, bad %s\n", src);
1050         return;
1051     }
1052     if (S_ISDIR(stat.st_mode))
1053         flag |= FLAG_SRC_IS_DIR;
1054     else
1055         flag |= FLAG_SRC_IS_FILE;
1056 
1057     if (dfs_file_stat(dst, &stat) < 0)
1058     {
1059         flag |= FLAG_DST_NON_EXSIT;
1060     }
1061     else
1062     {
1063         if (S_ISDIR(stat.st_mode))
1064             flag |= FLAG_DST_IS_DIR;
1065         else
1066             flag |= FLAG_DST_IS_FILE;
1067     }
1068 
1069     /*2. check status*/
1070     if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
1071     {
1072         rt_kprintf("cp faild, cp dir to file is not permitted!\n");
1073         return ;
1074     }
1075 
1076     /*3. do copy*/
1077     if (flag & FLAG_SRC_IS_FILE)
1078     {
1079         if (flag & FLAG_DST_IS_DIR)
1080         {
1081             char *fdst;
1082             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
1083             if (fdst == NULL)
1084             {
1085                 rt_kprintf("out of memory\n");
1086                 return;
1087             }
1088             copyfile(src, fdst);
1089             rt_free(fdst);
1090         }
1091         else
1092         {
1093             copyfile(src, dst);
1094         }
1095     }
1096     else /*flag & FLAG_SRC_IS_DIR*/
1097     {
1098         if (flag & FLAG_DST_IS_DIR)
1099         {
1100             char *fdst;
1101             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
1102             if (fdst == NULL)
1103             {
1104                 rt_kprintf("out of memory\n");
1105                 return;
1106             }
1107             mkdir(fdst, 0);
1108             copydir(src, fdst);
1109             rt_free(fdst);
1110         }
1111         else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT)
1112         {
1113             mkdir(dst, 0);
1114             copydir(src, dst);
1115         }
1116         else
1117         {
1118             copydir(src, dst);
1119         }
1120     }
1121 }
1122 FINSH_FUNCTION_EXPORT(copy, copy file or dir)
1123 #endif /* DFS_USING_POSIX */
1124 
1125 #endif /* RT_USING_FINSH */
1126 /**@}*/
1127 
1128