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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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; 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 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 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 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); 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 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 } 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