dfs_file.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  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. #include <dfs.h>
  14. #include <dfs_file.h>
  15. #include <dfs_private.h>
  16. /**
  17. * @addtogroup FileApi
  18. */
  19. /*@{*/
  20. /**
  21. * this function will open a file which specified by path with specified flags.
  22. *
  23. * @param fd the file descriptor pointer to return the corresponding result.
  24. * @param path the specified file path.
  25. * @param flags the flags for open operator.
  26. *
  27. * @return 0 on successful, -1 on failed.
  28. */
  29. int dfs_file_open(struct dfs_fd *fd, const char *path, int flags)
  30. {
  31. struct dfs_filesystem *fs;
  32. char *fullpath;
  33. int result;
  34. /* parameter check */
  35. if (fd == NULL)
  36. return -EINVAL;
  37. /* make sure we have an absolute path */
  38. fullpath = dfs_normalize_path(NULL, path);
  39. if (fullpath == NULL)
  40. {
  41. return -ENOMEM;
  42. }
  43. LOG_D("open file:%s", fullpath);
  44. /* find filesystem */
  45. fs = dfs_filesystem_lookup(fullpath);
  46. if (fs == NULL)
  47. {
  48. rt_free(fullpath); /* release path */
  49. return -ENOENT;
  50. }
  51. LOG_D("open in filesystem:%s", fs->ops->name);
  52. fd->fs = fs; /* set file system */
  53. fd->fops = fs->ops->fops; /* set file ops */
  54. /* initialize the fd item */
  55. fd->type = FT_REGULAR;
  56. fd->flags = flags;
  57. fd->size = 0;
  58. fd->pos = 0;
  59. fd->data = fs;
  60. if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
  61. {
  62. if (dfs_subdir(fs->path, fullpath) == NULL)
  63. fd->path = rt_strdup("/");
  64. else
  65. fd->path = rt_strdup(dfs_subdir(fs->path, fullpath));
  66. rt_free(fullpath);
  67. LOG_D("Actual file path: %s", fd->path);
  68. }
  69. else
  70. {
  71. fd->path = fullpath;
  72. }
  73. /* specific file system open routine */
  74. if (fd->fops->open == NULL)
  75. {
  76. /* clear fd */
  77. rt_free(fd->path);
  78. fd->path = NULL;
  79. return -ENOSYS;
  80. }
  81. if ((result = fd->fops->open(fd)) < 0)
  82. {
  83. /* clear fd */
  84. rt_free(fd->path);
  85. fd->path = NULL;
  86. LOG_D("%s open failed", fullpath);
  87. return result;
  88. }
  89. fd->flags |= DFS_F_OPEN;
  90. if (flags & O_DIRECTORY)
  91. {
  92. fd->type = FT_DIRECTORY;
  93. fd->flags |= DFS_F_DIRECTORY;
  94. }
  95. LOG_D("open successful");
  96. return 0;
  97. }
  98. /**
  99. * this function will close a file descriptor.
  100. *
  101. * @param fd the file descriptor to be closed.
  102. *
  103. * @return 0 on successful, -1 on failed.
  104. */
  105. int dfs_file_close(struct dfs_fd *fd)
  106. {
  107. int result = 0;
  108. if (fd == NULL)
  109. return -ENXIO;
  110. if (fd->fops->close != NULL)
  111. result = fd->fops->close(fd);
  112. /* close fd error, return */
  113. if (result < 0)
  114. return result;
  115. rt_free(fd->path);
  116. fd->path = NULL;
  117. return result;
  118. }
  119. /**
  120. * this function will perform a io control on a file descriptor.
  121. *
  122. * @param fd the file descriptor.
  123. * @param cmd the command to send to file descriptor.
  124. * @param args the argument to send to file descriptor.
  125. *
  126. * @return 0 on successful, -1 on failed.
  127. */
  128. int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args)
  129. {
  130. if (fd == NULL)
  131. return -EINVAL;
  132. /* regular file system fd */
  133. if (fd->type == FT_REGULAR)
  134. {
  135. switch (cmd)
  136. {
  137. case F_GETFL:
  138. return fd->flags; /* return flags */
  139. case F_SETFL:
  140. {
  141. int flags = (int)(rt_base_t)args;
  142. int mask = O_NONBLOCK | O_APPEND;
  143. flags &= mask;
  144. fd->flags &= ~mask;
  145. fd->flags |= flags;
  146. }
  147. return 0;
  148. }
  149. }
  150. if (fd->fops->ioctl != NULL)
  151. return fd->fops->ioctl(fd, cmd, args);
  152. return -ENOSYS;
  153. }
  154. /**
  155. * this function will read specified length data from a file descriptor to a
  156. * buffer.
  157. *
  158. * @param fd the file descriptor.
  159. * @param buf the buffer to save the read data.
  160. * @param len the length of data buffer to be read.
  161. *
  162. * @return the actual read data bytes or 0 on end of file or failed.
  163. */
  164. int dfs_file_read(struct dfs_fd *fd, void *buf, size_t len)
  165. {
  166. int result = 0;
  167. if (fd == NULL)
  168. return -EINVAL;
  169. if (fd->fops->read == NULL)
  170. return -ENOSYS;
  171. if ((result = fd->fops->read(fd, buf, len)) < 0)
  172. fd->flags |= DFS_F_EOF;
  173. return result;
  174. }
  175. /**
  176. * this function will fetch directory entries from a directory descriptor.
  177. *
  178. * @param fd the directory descriptor.
  179. * @param dirp the dirent buffer to save result.
  180. * @param nbytes the available room in the buffer.
  181. *
  182. * @return the read dirent, others on failed.
  183. */
  184. int dfs_file_getdents(struct dfs_fd *fd, struct dirent *dirp, size_t nbytes)
  185. {
  186. /* parameter check */
  187. if (fd == NULL || fd->type != FT_DIRECTORY)
  188. return -EINVAL;
  189. if (fd->fops->getdents != NULL)
  190. return fd->fops->getdents(fd, dirp, nbytes);
  191. return -ENOSYS;
  192. }
  193. /**
  194. * this function will unlink (remove) a specified path file from file system.
  195. *
  196. * @param path the specified path file to be unlinked.
  197. *
  198. * @return 0 on successful, -1 on failed.
  199. */
  200. int dfs_file_unlink(const char *path)
  201. {
  202. int result;
  203. char *fullpath;
  204. struct dfs_filesystem *fs;
  205. /* Make sure we have an absolute path */
  206. fullpath = dfs_normalize_path(NULL, path);
  207. if (fullpath == NULL)
  208. {
  209. return -EINVAL;
  210. }
  211. /* get filesystem */
  212. if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
  213. {
  214. result = -ENOENT;
  215. goto __exit;
  216. }
  217. /* Check whether file is already open */
  218. if (fd_is_open(fullpath) == 0)
  219. {
  220. result = -EBUSY;
  221. goto __exit;
  222. }
  223. if (fs->ops->unlink != NULL)
  224. {
  225. if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH))
  226. {
  227. if (dfs_subdir(fs->path, fullpath) == NULL)
  228. result = fs->ops->unlink(fs, "/");
  229. else
  230. result = fs->ops->unlink(fs, dfs_subdir(fs->path, fullpath));
  231. }
  232. else
  233. result = fs->ops->unlink(fs, fullpath);
  234. }
  235. else result = -ENOSYS;
  236. __exit:
  237. rt_free(fullpath);
  238. return result;
  239. }
  240. /**
  241. * this function will write some specified length data to file system.
  242. *
  243. * @param fd the file descriptor.
  244. * @param buf the data buffer to be written.
  245. * @param len the data buffer length
  246. *
  247. * @return the actual written data length.
  248. */
  249. int dfs_file_write(struct dfs_fd *fd, const void *buf, size_t len)
  250. {
  251. if (fd == NULL)
  252. return -EINVAL;
  253. if (fd->fops->write == NULL)
  254. return -ENOSYS;
  255. return fd->fops->write(fd, buf, len);
  256. }
  257. /**
  258. * this function will flush buffer on a file descriptor.
  259. *
  260. * @param fd the file descriptor.
  261. *
  262. * @return 0 on successful, -1 on failed.
  263. */
  264. int dfs_file_flush(struct dfs_fd *fd)
  265. {
  266. if (fd == NULL)
  267. return -EINVAL;
  268. if (fd->fops->flush == NULL)
  269. return -ENOSYS;
  270. return fd->fops->flush(fd);
  271. }
  272. /**
  273. * this function will seek the offset for specified file descriptor.
  274. *
  275. * @param fd the file descriptor.
  276. * @param offset the offset to be sought.
  277. *
  278. * @return the current position after seek.
  279. */
  280. int dfs_file_lseek(struct dfs_fd *fd, off_t offset)
  281. {
  282. int result;
  283. if (fd == NULL)
  284. return -EINVAL;
  285. if (fd->fops->lseek == NULL)
  286. return -ENOSYS;
  287. result = fd->fops->lseek(fd, offset);
  288. /* update current position */
  289. if (result >= 0)
  290. fd->pos = result;
  291. return result;
  292. }
  293. /**
  294. * this function will get file information.
  295. *
  296. * @param path the file path.
  297. * @param buf the data buffer to save stat description.
  298. *
  299. * @return 0 on successful, -1 on failed.
  300. */
  301. int dfs_file_stat(const char *path, struct stat *buf)
  302. {
  303. int result;
  304. char *fullpath;
  305. struct dfs_filesystem *fs;
  306. fullpath = dfs_normalize_path(NULL, path);
  307. if (fullpath == NULL)
  308. {
  309. return -1;
  310. }
  311. if ((fs = dfs_filesystem_lookup(fullpath)) == NULL)
  312. {
  313. LOG_E(
  314. "can't find mounted filesystem on this path:%s", fullpath);
  315. rt_free(fullpath);
  316. return -ENOENT;
  317. }
  318. if ((fullpath[0] == '/' && fullpath[1] == '\0') ||
  319. (dfs_subdir(fs->path, fullpath) == NULL))
  320. {
  321. /* it's the root directory */
  322. buf->st_dev = 0;
  323. buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH |
  324. S_IWUSR | S_IWGRP | S_IWOTH;
  325. buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
  326. buf->st_size = 0;
  327. buf->st_mtime = 0;
  328. /* release full path */
  329. rt_free(fullpath);
  330. return RT_EOK;
  331. }
  332. else
  333. {
  334. if (fs->ops->stat == NULL)
  335. {
  336. rt_free(fullpath);
  337. LOG_E(
  338. "the filesystem didn't implement this function");
  339. return -ENOSYS;
  340. }
  341. /* get the real file path and get file stat */
  342. if (fs->ops->flags & DFS_FS_FLAG_FULLPATH)
  343. result = fs->ops->stat(fs, fullpath, buf);
  344. else
  345. result = fs->ops->stat(fs, dfs_subdir(fs->path, fullpath), buf);
  346. }
  347. rt_free(fullpath);
  348. return result;
  349. }
  350. /**
  351. * this function will rename an old path name to a new path name.
  352. *
  353. * @param oldpath the old path name.
  354. * @param newpath the new path name.
  355. *
  356. * @return 0 on successful, -1 on failed.
  357. */
  358. int dfs_file_rename(const char *oldpath, const char *newpath)
  359. {
  360. int result;
  361. struct dfs_filesystem *oldfs, *newfs;
  362. char *oldfullpath, *newfullpath;
  363. result = RT_EOK;
  364. newfullpath = NULL;
  365. oldfullpath = NULL;
  366. oldfullpath = dfs_normalize_path(NULL, oldpath);
  367. if (oldfullpath == NULL)
  368. {
  369. result = -ENOENT;
  370. goto __exit;
  371. }
  372. newfullpath = dfs_normalize_path(NULL, newpath);
  373. if (newfullpath == NULL)
  374. {
  375. result = -ENOENT;
  376. goto __exit;
  377. }
  378. oldfs = dfs_filesystem_lookup(oldfullpath);
  379. newfs = dfs_filesystem_lookup(newfullpath);
  380. if (oldfs == newfs)
  381. {
  382. if (oldfs->ops->rename == NULL)
  383. {
  384. result = -ENOSYS;
  385. }
  386. else
  387. {
  388. if (oldfs->ops->flags & DFS_FS_FLAG_FULLPATH)
  389. result = oldfs->ops->rename(oldfs, oldfullpath, newfullpath);
  390. else
  391. /* use sub directory to rename in file system */
  392. result = oldfs->ops->rename(oldfs,
  393. dfs_subdir(oldfs->path, oldfullpath),
  394. dfs_subdir(newfs->path, newfullpath));
  395. }
  396. }
  397. else
  398. {
  399. result = -EXDEV;
  400. }
  401. __exit:
  402. rt_free(oldfullpath);
  403. rt_free(newfullpath);
  404. /* not at same file system, return EXDEV */
  405. return result;
  406. }
  407. #ifdef RT_USING_FINSH
  408. #include <finsh.h>
  409. static struct dfs_fd fd;
  410. static struct dirent dirent;
  411. void ls(const char *pathname)
  412. {
  413. struct stat stat;
  414. int length;
  415. char *fullpath, *path;
  416. fullpath = NULL;
  417. if (pathname == NULL)
  418. {
  419. #ifdef DFS_USING_WORKDIR
  420. /* open current working directory */
  421. path = rt_strdup(working_directory);
  422. #else
  423. path = rt_strdup("/");
  424. #endif
  425. if (path == NULL)
  426. return ; /* out of memory */
  427. }
  428. else
  429. {
  430. path = (char *)pathname;
  431. }
  432. /* list directory */
  433. if (dfs_file_open(&fd, path, O_DIRECTORY) == 0)
  434. {
  435. rt_kprintf("Directory %s:\n", path);
  436. do
  437. {
  438. memset(&dirent, 0, sizeof(struct dirent));
  439. length = dfs_file_getdents(&fd, &dirent, sizeof(struct dirent));
  440. if (length > 0)
  441. {
  442. memset(&stat, 0, sizeof(struct stat));
  443. /* build full path for each file */
  444. fullpath = dfs_normalize_path(path, dirent.d_name);
  445. if (fullpath == NULL)
  446. break;
  447. if (dfs_file_stat(fullpath, &stat) == 0)
  448. {
  449. rt_kprintf("%-20s", dirent.d_name);
  450. if (S_ISDIR(stat.st_mode))
  451. {
  452. rt_kprintf("%-25s\n", "<DIR>");
  453. }
  454. else
  455. {
  456. rt_kprintf("%-25lu\n", stat.st_size);
  457. }
  458. }
  459. else
  460. rt_kprintf("BAD file: %s\n", dirent.d_name);
  461. rt_free(fullpath);
  462. }
  463. }
  464. while (length > 0);
  465. dfs_file_close(&fd);
  466. }
  467. else
  468. {
  469. rt_kprintf("No such directory\n");
  470. }
  471. if (pathname == NULL)
  472. rt_free(path);
  473. }
  474. FINSH_FUNCTION_EXPORT(ls, list directory contents);
  475. void rm(const char *filename)
  476. {
  477. if (dfs_file_unlink(filename) < 0)
  478. {
  479. rt_kprintf("Delete %s failed\n", filename);
  480. }
  481. }
  482. FINSH_FUNCTION_EXPORT(rm, remove files or directories);
  483. void cat(const char *filename)
  484. {
  485. uint32_t length;
  486. char buffer[81];
  487. if (dfs_file_open(&fd, filename, O_RDONLY) < 0)
  488. {
  489. rt_kprintf("Open %s failed\n", filename);
  490. return;
  491. }
  492. do
  493. {
  494. memset(buffer, 0, sizeof(buffer));
  495. length = dfs_file_read(&fd, buffer, sizeof(buffer) - 1);
  496. if (length > 0)
  497. {
  498. rt_kprintf("%s", buffer);
  499. }
  500. }
  501. while (length > 0);
  502. dfs_file_close(&fd);
  503. }
  504. FINSH_FUNCTION_EXPORT(cat, print file);
  505. #define BUF_SZ 4096
  506. static void copyfile(const char *src, const char *dst)
  507. {
  508. struct dfs_fd src_fd;
  509. rt_uint8_t *block_ptr;
  510. rt_int32_t read_bytes;
  511. block_ptr = (rt_uint8_t *)rt_malloc(BUF_SZ);
  512. if (block_ptr == NULL)
  513. {
  514. rt_kprintf("out of memory\n");
  515. return;
  516. }
  517. if (dfs_file_open(&src_fd, src, O_RDONLY) < 0)
  518. {
  519. rt_free(block_ptr);
  520. rt_kprintf("Read %s failed\n", src);
  521. return;
  522. }
  523. if (dfs_file_open(&fd, dst, O_WRONLY | O_CREAT) < 0)
  524. {
  525. rt_free(block_ptr);
  526. dfs_file_close(&src_fd);
  527. rt_kprintf("Write %s failed\n", dst);
  528. return;
  529. }
  530. do
  531. {
  532. read_bytes = dfs_file_read(&src_fd, block_ptr, BUF_SZ);
  533. if (read_bytes > 0)
  534. {
  535. int length;
  536. length = dfs_file_write(&fd, block_ptr, read_bytes);
  537. if (length != read_bytes)
  538. {
  539. /* write failed. */
  540. rt_kprintf("Write file data failed, errno=%d\n", length);
  541. break;
  542. }
  543. }
  544. }
  545. while (read_bytes > 0);
  546. dfs_file_close(&src_fd);
  547. dfs_file_close(&fd);
  548. rt_free(block_ptr);
  549. }
  550. extern int mkdir(const char *path, mode_t mode);
  551. static void copydir(const char *src, const char *dst)
  552. {
  553. struct dirent dirent;
  554. struct stat stat;
  555. int length;
  556. struct dfs_fd cpfd;
  557. if (dfs_file_open(&cpfd, src, O_DIRECTORY) < 0)
  558. {
  559. rt_kprintf("open %s failed\n", src);
  560. return ;
  561. }
  562. do
  563. {
  564. memset(&dirent, 0, sizeof(struct dirent));
  565. length = dfs_file_getdents(&cpfd, &dirent, sizeof(struct dirent));
  566. if (length > 0)
  567. {
  568. char *src_entry_full = NULL;
  569. char *dst_entry_full = NULL;
  570. if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0)
  571. continue;
  572. /* build full path for each file */
  573. if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL)
  574. {
  575. rt_kprintf("out of memory!\n");
  576. break;
  577. }
  578. if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL)
  579. {
  580. rt_kprintf("out of memory!\n");
  581. rt_free(src_entry_full);
  582. break;
  583. }
  584. memset(&stat, 0, sizeof(struct stat));
  585. if (dfs_file_stat(src_entry_full, &stat) != 0)
  586. {
  587. rt_kprintf("open file: %s failed\n", dirent.d_name);
  588. continue;
  589. }
  590. if (S_ISDIR(stat.st_mode))
  591. {
  592. mkdir(dst_entry_full, 0);
  593. copydir(src_entry_full, dst_entry_full);
  594. }
  595. else
  596. {
  597. copyfile(src_entry_full, dst_entry_full);
  598. }
  599. rt_free(src_entry_full);
  600. rt_free(dst_entry_full);
  601. }
  602. }
  603. while (length > 0);
  604. dfs_file_close(&cpfd);
  605. }
  606. static const char *_get_path_lastname(const char *path)
  607. {
  608. char *ptr;
  609. if ((ptr = (char *)strrchr(path, '/')) == NULL)
  610. return path;
  611. /* skip the '/' then return */
  612. return ++ptr;
  613. }
  614. void copy(const char *src, const char *dst)
  615. {
  616. #define FLAG_SRC_TYPE 0x03
  617. #define FLAG_SRC_IS_DIR 0x01
  618. #define FLAG_SRC_IS_FILE 0x02
  619. #define FLAG_SRC_NON_EXSIT 0x00
  620. #define FLAG_DST_TYPE 0x0C
  621. #define FLAG_DST_IS_DIR 0x04
  622. #define FLAG_DST_IS_FILE 0x08
  623. #define FLAG_DST_NON_EXSIT 0x00
  624. struct stat stat;
  625. uint32_t flag = 0;
  626. /* check the staus of src and dst */
  627. if (dfs_file_stat(src, &stat) < 0)
  628. {
  629. rt_kprintf("copy failed, bad %s\n", src);
  630. return;
  631. }
  632. if (S_ISDIR(stat.st_mode))
  633. flag |= FLAG_SRC_IS_DIR;
  634. else
  635. flag |= FLAG_SRC_IS_FILE;
  636. if (dfs_file_stat(dst, &stat) < 0)
  637. {
  638. flag |= FLAG_DST_NON_EXSIT;
  639. }
  640. else
  641. {
  642. if (S_ISDIR(stat.st_mode))
  643. flag |= FLAG_DST_IS_DIR;
  644. else
  645. flag |= FLAG_DST_IS_FILE;
  646. }
  647. //2. check status
  648. if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
  649. {
  650. rt_kprintf("cp faild, cp dir to file is not permitted!\n");
  651. return ;
  652. }
  653. //3. do copy
  654. if (flag & FLAG_SRC_IS_FILE)
  655. {
  656. if (flag & FLAG_DST_IS_DIR)
  657. {
  658. char *fdst;
  659. fdst = dfs_normalize_path(dst, _get_path_lastname(src));
  660. if (fdst == NULL)
  661. {
  662. rt_kprintf("out of memory\n");
  663. return;
  664. }
  665. copyfile(src, fdst);
  666. rt_free(fdst);
  667. }
  668. else
  669. {
  670. copyfile(src, dst);
  671. }
  672. }
  673. else //flag & FLAG_SRC_IS_DIR
  674. {
  675. if (flag & FLAG_DST_IS_DIR)
  676. {
  677. char *fdst;
  678. fdst = dfs_normalize_path(dst, _get_path_lastname(src));
  679. if (fdst == NULL)
  680. {
  681. rt_kprintf("out of memory\n");
  682. return;
  683. }
  684. mkdir(fdst, 0);
  685. copydir(src, fdst);
  686. rt_free(fdst);
  687. }
  688. else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT)
  689. {
  690. mkdir(dst, 0);
  691. copydir(src, dst);
  692. }
  693. else
  694. {
  695. copydir(src, dst);
  696. }
  697. }
  698. }
  699. FINSH_FUNCTION_EXPORT(copy, copy file or dir)
  700. #endif
  701. /* @} */