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