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 * 2009-05-27 Yi.qiu The first version 9 * 2018-02-07 Bernard Change the 3rd parameter of open/fcntl/ioctl to '...' 10 */ 11 12 #include <dfs.h> 13 #include <dfs_posix.h> 14 #include "dfs_private.h" 15 16 /** 17 * @addtogroup FsPosixApi 18 */ 19 20 /*@{*/ 21 22 /** 23 * this function is a POSIX compliant version, which will open a file and 24 * return a file descriptor according specified flags. 25 * 26 * @param file the path name of file. 27 * @param flags the file open flags. 28 * 29 * @return the non-negative integer on successful open, others for failed. 30 */ 31 int open(const char *file, int flags, ...) 32 { 33 int fd, result; 34 struct dfs_fd *d; 35 36 /* allocate a fd */ 37 fd = fd_new(); 38 if (fd < 0) 39 { 40 rt_set_errno(-ENOMEM); 41 42 return -1; 43 } 44 d = fd_get(fd); 45 46 result = dfs_file_open(d, file, flags); 47 if (result < 0) 48 { 49 /* release the ref-count of fd */ 50 fd_put(d); 51 fd_put(d); 52 53 rt_set_errno(result); 54 55 return -1; 56 } 57 58 /* release the ref-count of fd */ 59 fd_put(d); 60 61 return fd; 62 } 63 RTM_EXPORT(open); 64 65 /** 66 * this function is a POSIX compliant version, which will close the open 67 * file descriptor. 68 * 69 * @param fd the file descriptor. 70 * 71 * @return 0 on successful, -1 on failed. 72 */ 73 int close(int fd) 74 { 75 int result; 76 struct dfs_fd *d; 77 78 d = fd_get(fd); 79 if (d == NULL) 80 { 81 rt_set_errno(-EBADF); 82 83 return -1; 84 } 85 86 result = dfs_file_close(d); 87 fd_put(d); 88 89 if (result < 0) 90 { 91 rt_set_errno(result); 92 93 return -1; 94 } 95 96 fd_put(d); 97 98 return 0; 99 } 100 RTM_EXPORT(close); 101 102 /** 103 * this function is a POSIX compliant version, which will read specified data 104 * buffer length for an open file descriptor. 105 * 106 * @param fd the file descriptor. 107 * @param buf the buffer to save the read data. 108 * @param len the maximal length of data buffer 109 * 110 * @return the actual read data buffer length. If the returned value is 0, it 111 * may be reach the end of file, please check errno. 112 */ 113 #if defined(RT_USING_NEWLIB) && defined(_EXFUN) 114 _READ_WRITE_RETURN_TYPE _EXFUN(read, (int fd, void *buf, size_t len)) 115 #else 116 int read(int fd, void *buf, size_t len) 117 #endif 118 { 119 int result; 120 struct dfs_fd *d; 121 122 /* get the fd */ 123 d = fd_get(fd); 124 if (d == NULL) 125 { 126 rt_set_errno(-EBADF); 127 128 return -1; 129 } 130 131 result = dfs_file_read(d, buf, len); 132 if (result < 0) 133 { 134 fd_put(d); 135 rt_set_errno(result); 136 137 return -1; 138 } 139 140 /* release the ref-count of fd */ 141 fd_put(d); 142 143 return result; 144 } 145 RTM_EXPORT(read); 146 147 /** 148 * this function is a POSIX compliant version, which will write specified data 149 * buffer length for an open file descriptor. 150 * 151 * @param fd the file descriptor 152 * @param buf the data buffer to be written. 153 * @param len the data buffer length. 154 * 155 * @return the actual written data buffer length. 156 */ 157 #if defined(RT_USING_NEWLIB) && defined(_EXFUN) 158 _READ_WRITE_RETURN_TYPE _EXFUN(write, (int fd, const void *buf, size_t len)) 159 #else 160 int write(int fd, const void *buf, size_t len) 161 #endif 162 { 163 int result; 164 struct dfs_fd *d; 165 166 /* get the fd */ 167 d = fd_get(fd); 168 if (d == NULL) 169 { 170 rt_set_errno(-EBADF); 171 172 return -1; 173 } 174 175 result = dfs_file_write(d, buf, len); 176 if (result < 0) 177 { 178 fd_put(d); 179 rt_set_errno(result); 180 181 return -1; 182 } 183 184 /* release the ref-count of fd */ 185 fd_put(d); 186 187 return result; 188 } 189 RTM_EXPORT(write); 190 191 /** 192 * this function is a POSIX compliant version, which will seek the offset for 193 * an open file descriptor. 194 * 195 * @param fd the file descriptor. 196 * @param offset the offset to be seeked. 197 * @param whence the directory of seek. 198 * 199 * @return the current read/write position in the file, or -1 on failed. 200 */ 201 off_t lseek(int fd, off_t offset, int whence) 202 { 203 int result; 204 struct dfs_fd *d; 205 206 d = fd_get(fd); 207 if (d == NULL) 208 { 209 rt_set_errno(-EBADF); 210 211 return -1; 212 } 213 214 switch (whence) 215 { 216 case SEEK_SET: 217 break; 218 219 case SEEK_CUR: 220 offset += d->pos; 221 break; 222 223 case SEEK_END: 224 offset += d->size; 225 break; 226 227 default: 228 fd_put(d); 229 rt_set_errno(-EINVAL); 230 231 return -1; 232 } 233 234 if (offset < 0) 235 { 236 fd_put(d); 237 rt_set_errno(-EINVAL); 238 239 return -1; 240 } 241 result = dfs_file_lseek(d, offset); 242 if (result < 0) 243 { 244 fd_put(d); 245 rt_set_errno(result); 246 247 return -1; 248 } 249 250 /* release the ref-count of fd */ 251 fd_put(d); 252 253 return offset; 254 } 255 RTM_EXPORT(lseek); 256 257 /** 258 * this function is a POSIX compliant version, which will rename old file name 259 * to new file name. 260 * 261 * @param old the old file name. 262 * @param new the new file name. 263 * 264 * @return 0 on successful, -1 on failed. 265 * 266 * note: the old and new file name must be belong to a same file system. 267 */ 268 int rename(const char *old, const char *new) 269 { 270 int result; 271 272 result = dfs_file_rename(old, new); 273 if (result < 0) 274 { 275 rt_set_errno(result); 276 277 return -1; 278 } 279 280 return 0; 281 } 282 RTM_EXPORT(rename); 283 284 /** 285 * this function is a POSIX compliant version, which will unlink (remove) a 286 * specified path file from file system. 287 * 288 * @param pathname the specified path name to be unlinked. 289 * 290 * @return 0 on successful, -1 on failed. 291 */ 292 int unlink(const char *pathname) 293 { 294 int result; 295 296 result = dfs_file_unlink(pathname); 297 if (result < 0) 298 { 299 rt_set_errno(result); 300 301 return -1; 302 } 303 304 return 0; 305 } 306 RTM_EXPORT(unlink); 307 308 #ifndef _WIN32 /* we can not implement these functions */ 309 /** 310 * this function is a POSIX compliant version, which will get file information. 311 * 312 * @param file the file name 313 * @param buf the data buffer to save stat description. 314 * 315 * @return 0 on successful, -1 on failed. 316 */ 317 int stat(const char *file, struct stat *buf) 318 { 319 int result; 320 321 result = dfs_file_stat(file, buf); 322 if (result < 0) 323 { 324 rt_set_errno(result); 325 326 return -1; 327 } 328 329 return result; 330 } 331 RTM_EXPORT(stat); 332 333 /** 334 * this function is a POSIX compliant version, which will get file status. 335 * 336 * @param fildes the file description 337 * @param buf the data buffer to save stat description. 338 * 339 * @return 0 on successful, -1 on failed. 340 */ 341 int fstat(int fildes, struct stat *buf) 342 { 343 struct dfs_fd *d; 344 345 /* get the fd */ 346 d = fd_get(fildes); 347 if (d == NULL) 348 { 349 rt_set_errno(-EBADF); 350 351 return -1; 352 } 353 354 /* it's the root directory */ 355 buf->st_dev = 0; 356 357 buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | 358 S_IWUSR | S_IWGRP | S_IWOTH; 359 if (d->type == FT_DIRECTORY) 360 { 361 buf->st_mode &= ~S_IFREG; 362 buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; 363 } 364 365 buf->st_size = d->size; 366 buf->st_mtime = 0; 367 368 fd_put(d); 369 370 return RT_EOK; 371 } 372 RTM_EXPORT(fstat); 373 #endif 374 375 /** 376 * this function is a POSIX compliant version, which shall request that all data 377 * for the open file descriptor named by fildes is to be transferred to the storage 378 * device associated with the file described by fildes. 379 * 380 * @param fildes the file description 381 * 382 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno 383 * set to indicate the error. 384 */ 385 int fsync(int fildes) 386 { 387 int ret; 388 struct dfs_fd *d; 389 390 /* get the fd */ 391 d = fd_get(fildes); 392 if (d == NULL) 393 { 394 rt_set_errno(-EBADF); 395 return -1; 396 } 397 398 ret = dfs_file_flush(d); 399 400 fd_put(d); 401 return ret; 402 } 403 RTM_EXPORT(fsync); 404 405 /** 406 * this function is a POSIX compliant version, which shall perform a variety of 407 * control functions on devices. 408 * 409 * @param fildes the file description 410 * @param cmd the specified command 411 * @param data represents the additional information that is needed by this 412 * specific device to perform the requested function. 413 * 414 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno 415 * set to indicate the error. 416 */ 417 int fcntl(int fildes, int cmd, ...) 418 { 419 int ret = -1; 420 struct dfs_fd *d; 421 422 /* get the fd */ 423 d = fd_get(fildes); 424 if (d) 425 { 426 void* arg; 427 va_list ap; 428 429 va_start(ap, cmd); 430 arg = va_arg(ap, void*); 431 va_end(ap); 432 433 ret = dfs_file_ioctl(d, cmd, arg); 434 fd_put(d); 435 } 436 else ret = -EBADF; 437 438 if (ret < 0) 439 { 440 rt_set_errno(ret); 441 ret = -1; 442 } 443 444 return ret; 445 } 446 RTM_EXPORT(fcntl); 447 448 /** 449 * this function is a POSIX compliant version, which shall perform a variety of 450 * control functions on devices. 451 * 452 * @param fildes the file description 453 * @param cmd the specified command 454 * @param data represents the additional information that is needed by this 455 * specific device to perform the requested function. 456 * 457 * @return 0 on successful completion. Otherwise, -1 shall be returned and errno 458 * set to indicate the error. 459 */ 460 int ioctl(int fildes, int cmd, ...) 461 { 462 void* arg; 463 va_list ap; 464 465 va_start(ap, cmd); 466 arg = va_arg(ap, void*); 467 va_end(ap); 468 469 /* we use fcntl for this API. */ 470 return fcntl(fildes, cmd, arg); 471 } 472 RTM_EXPORT(ioctl); 473 474 /** 475 * this function is a POSIX compliant version, which will return the 476 * information about a mounted file system. 477 * 478 * @param path the path which mounted file system. 479 * @param buf the buffer to save the returned information. 480 * 481 * @return 0 on successful, others on failed. 482 */ 483 int statfs(const char *path, struct statfs *buf) 484 { 485 int result; 486 487 result = dfs_statfs(path, buf); 488 if (result < 0) 489 { 490 rt_set_errno(result); 491 492 return -1; 493 } 494 495 return result; 496 } 497 RTM_EXPORT(statfs); 498 499 /** 500 * this function is a POSIX compliant version, which will make a directory 501 * 502 * @param path the directory path to be made. 503 * @param mode 504 * 505 * @return 0 on successful, others on failed. 506 */ 507 int mkdir(const char *path, mode_t mode) 508 { 509 int fd; 510 struct dfs_fd *d; 511 int result; 512 513 fd = fd_new(); 514 if (fd == -1) 515 { 516 rt_set_errno(-ENOMEM); 517 518 return -1; 519 } 520 521 d = fd_get(fd); 522 523 result = dfs_file_open(d, path, O_DIRECTORY | O_CREAT); 524 525 if (result < 0) 526 { 527 fd_put(d); 528 fd_put(d); 529 rt_set_errno(result); 530 531 return -1; 532 } 533 534 dfs_file_close(d); 535 fd_put(d); 536 fd_put(d); 537 538 return 0; 539 } 540 RTM_EXPORT(mkdir); 541 542 #ifdef RT_USING_FINSH 543 #include <finsh.h> 544 FINSH_FUNCTION_EXPORT(mkdir, create a directory); 545 #endif 546 547 /** 548 * this function is a POSIX compliant version, which will remove a directory. 549 * 550 * @param pathname the path name to be removed. 551 * 552 * @return 0 on successful, others on failed. 553 */ 554 int rmdir(const char *pathname) 555 { 556 int result; 557 558 result = dfs_file_unlink(pathname); 559 if (result < 0) 560 { 561 rt_set_errno(result); 562 563 return -1; 564 } 565 566 return 0; 567 } 568 RTM_EXPORT(rmdir); 569 570 /** 571 * this function is a POSIX compliant version, which will open a directory. 572 * 573 * @param name the path name to be open. 574 * 575 * @return the DIR pointer of directory, NULL on open directory failed. 576 */ 577 DIR *opendir(const char *name) 578 { 579 struct dfs_fd *d; 580 int fd, result; 581 DIR *t; 582 583 t = NULL; 584 585 /* allocate a fd */ 586 fd = fd_new(); 587 if (fd == -1) 588 { 589 rt_set_errno(-ENOMEM); 590 591 return NULL; 592 } 593 d = fd_get(fd); 594 595 result = dfs_file_open(d, name, O_RDONLY | O_DIRECTORY); 596 if (result >= 0) 597 { 598 /* open successfully */ 599 t = (DIR *) rt_malloc(sizeof(DIR)); 600 if (t == NULL) 601 { 602 dfs_file_close(d); 603 fd_put(d); 604 } 605 else 606 { 607 memset(t, 0, sizeof(DIR)); 608 609 t->fd = fd; 610 } 611 fd_put(d); 612 613 return t; 614 } 615 616 /* open failed */ 617 fd_put(d); 618 fd_put(d); 619 rt_set_errno(result); 620 621 return NULL; 622 } 623 RTM_EXPORT(opendir); 624 625 /** 626 * this function is a POSIX compliant version, which will return a pointer 627 * to a dirent structure representing the next directory entry in the 628 * directory stream. 629 * 630 * @param d the directory stream pointer. 631 * 632 * @return the next directory entry, NULL on the end of directory or failed. 633 */ 634 struct dirent *readdir(DIR *d) 635 { 636 int result; 637 struct dfs_fd *fd; 638 639 fd = fd_get(d->fd); 640 if (fd == NULL) 641 { 642 rt_set_errno(-EBADF); 643 return NULL; 644 } 645 646 if (d->num) 647 { 648 struct dirent* dirent_ptr; 649 dirent_ptr = (struct dirent*)&d->buf[d->cur]; 650 d->cur += dirent_ptr->d_reclen; 651 } 652 653 if (!d->num || d->cur >= d->num) 654 { 655 /* get a new entry */ 656 result = dfs_file_getdents(fd, 657 (struct dirent*)d->buf, 658 sizeof(d->buf) - 1); 659 if (result <= 0) 660 { 661 fd_put(fd); 662 rt_set_errno(result); 663 664 return NULL; 665 } 666 667 d->num = result; 668 d->cur = 0; /* current entry index */ 669 } 670 671 fd_put(fd); 672 673 return (struct dirent *)(d->buf+d->cur); 674 } 675 RTM_EXPORT(readdir); 676 677 /** 678 * this function is a POSIX compliant version, which will return current 679 * location in directory stream. 680 * 681 * @param d the directory stream pointer. 682 * 683 * @return the current location in directory stream. 684 */ 685 long telldir(DIR *d) 686 { 687 struct dfs_fd *fd; 688 long result; 689 690 fd = fd_get(d->fd); 691 if (fd == NULL) 692 { 693 rt_set_errno(-EBADF); 694 695 return 0; 696 } 697 698 result = fd->pos - d->num + d->cur; 699 fd_put(fd); 700 701 return result; 702 } 703 RTM_EXPORT(telldir); 704 705 /** 706 * this function is a POSIX compliant version, which will set position of 707 * next directory structure in the directory stream. 708 * 709 * @param d the directory stream. 710 * @param offset the offset in directory stream. 711 */ 712 void seekdir(DIR *d, off_t offset) 713 { 714 struct dfs_fd *fd; 715 716 fd = fd_get(d->fd); 717 if (fd == NULL) 718 { 719 rt_set_errno(-EBADF); 720 721 return ; 722 } 723 724 /* seek to the offset position of directory */ 725 if (dfs_file_lseek(fd, offset) >= 0) 726 d->num = d->cur = 0; 727 fd_put(fd); 728 } 729 RTM_EXPORT(seekdir); 730 731 /** 732 * this function is a POSIX compliant version, which will reset directory 733 * stream. 734 * 735 * @param d the directory stream. 736 */ 737 void rewinddir(DIR *d) 738 { 739 struct dfs_fd *fd; 740 741 fd = fd_get(d->fd); 742 if (fd == NULL) 743 { 744 rt_set_errno(-EBADF); 745 746 return ; 747 } 748 749 /* seek to the beginning of directory */ 750 if (dfs_file_lseek(fd, 0) >= 0) 751 d->num = d->cur = 0; 752 fd_put(fd); 753 } 754 RTM_EXPORT(rewinddir); 755 756 /** 757 * this function is a POSIX compliant version, which will close a directory 758 * stream. 759 * 760 * @param d the directory stream. 761 * 762 * @return 0 on successful, -1 on failed. 763 */ 764 int closedir(DIR *d) 765 { 766 int result; 767 struct dfs_fd *fd; 768 769 fd = fd_get(d->fd); 770 if (fd == NULL) 771 { 772 rt_set_errno(-EBADF); 773 774 return -1; 775 } 776 777 result = dfs_file_close(fd); 778 fd_put(fd); 779 780 fd_put(fd); 781 rt_free(d); 782 783 if (result < 0) 784 { 785 rt_set_errno(result); 786 787 return -1; 788 } 789 else 790 return 0; 791 } 792 RTM_EXPORT(closedir); 793 794 #ifdef DFS_USING_WORKDIR 795 /** 796 * this function is a POSIX compliant version, which will change working 797 * directory. 798 * 799 * @param path the path name to be changed to. 800 * 801 * @return 0 on successful, -1 on failed. 802 */ 803 int chdir(const char *path) 804 { 805 char *fullpath; 806 DIR *d; 807 808 if (path == NULL) 809 { 810 dfs_lock(); 811 rt_kprintf("%s\n", working_directory); 812 dfs_unlock(); 813 814 return 0; 815 } 816 817 if (strlen(path) > DFS_PATH_MAX) 818 { 819 rt_set_errno(-ENOTDIR); 820 821 return -1; 822 } 823 824 fullpath = dfs_normalize_path(NULL, path); 825 if (fullpath == NULL) 826 { 827 rt_set_errno(-ENOTDIR); 828 829 return -1; /* build path failed */ 830 } 831 832 dfs_lock(); 833 d = opendir(fullpath); 834 if (d == NULL) 835 { 836 rt_free(fullpath); 837 /* this is a not exist directory */ 838 dfs_unlock(); 839 840 return -1; 841 } 842 843 /* close directory stream */ 844 closedir(d); 845 846 /* copy full path to working directory */ 847 strncpy(working_directory, fullpath, DFS_PATH_MAX); 848 /* release normalize directory path name */ 849 rt_free(fullpath); 850 851 dfs_unlock(); 852 853 return 0; 854 } 855 RTM_EXPORT(chdir); 856 857 #ifdef RT_USING_FINSH 858 FINSH_FUNCTION_EXPORT_ALIAS(chdir, cd, change current working directory); 859 #endif 860 #endif 861 862 /** 863 * this function is a POSIX compliant version, which shall check the file named 864 * by the pathname pointed to by the path argument for accessibility according 865 * to the bit pattern contained in amode. 866 * 867 * @param path the specified file/dir path. 868 * @param amode the value is either the bitwise-inclusive OR of the access 869 * permissions to be checked (R_OK, W_OK, X_OK) or the existence test (F_OK). 870 */ 871 int access(const char *path, int amode) 872 { 873 struct stat sb; 874 if (stat(path, &sb) < 0) 875 return -1; /* already sets errno */ 876 877 /* ignore R_OK,W_OK,X_OK condition */ 878 return 0; 879 } 880 881 /** 882 * this function is a POSIX compliant version, which will return current 883 * working directory. 884 * 885 * @param buf the returned current directory. 886 * @param size the buffer size. 887 * 888 * @return the returned current directory. 889 */ 890 char *getcwd(char *buf, size_t size) 891 { 892 #ifdef DFS_USING_WORKDIR 893 dfs_lock(); 894 strncpy(buf, working_directory, size); 895 dfs_unlock(); 896 #else 897 rt_kprintf(NO_WORKING_DIR); 898 #endif 899 900 return buf; 901 } 902 RTM_EXPORT(getcwd); 903 904 /* @} */ 905