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