dfs.c 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. /*
  2. * Copyright (c) 2006-2025 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. * 2017-12-11 Bernard Use rt_free to instead of free in fd_is_open().
  10. * 2018-03-20 Heyuanjie dynamic allocation FD
  11. */
  12. #include <dfs.h>
  13. #include <dfs_fs.h>
  14. #include <dfs_dentry.h>
  15. #include <dfs_file.h>
  16. #include <dfs_mnt.h>
  17. #include <rtservice.h>
  18. #include "dfs_private.h"
  19. #define DBG_TAG "DFS"
  20. #define DBG_LVL DBG_INFO
  21. #include <rtdbg.h>
  22. #ifdef RT_USING_SMART
  23. #include <lwp.h>
  24. #endif
  25. #ifdef DFS_USING_WORKDIR
  26. char working_directory[DFS_PATH_MAX] = {"/"};
  27. #endif
  28. static rt_bool_t _dfs_init_ok = RT_FALSE;
  29. /* device filesystem lock */
  30. static struct rt_mutex fslock;
  31. static struct rt_mutex fdlock;
  32. static struct dfs_fdtable _fdtab = {0};
  33. /**
  34. * @brief Expand the file descriptor table to accommodate a specified file descriptor
  35. *
  36. * This function ensures that the file descriptor table in the given `dfs_fdtable` structure
  37. * has sufficient capacity to include the specified file descriptor `fd`. If the table
  38. * needs to be expanded, it reallocates memory and initializes new slots to `NULL`.
  39. *
  40. * @param[in,out] fdt Pointer to the file descriptor table to be expanded
  41. * @param[in] fd The file descriptor that needs to be accommodated
  42. *
  43. * @return int The input file descriptor if successful (fd >= 0),
  44. * -1 if expansion failed or fd exceeds maximum allowed value (DFS_FD_MAX)
  45. *
  46. * @note Expand table size to next multiple of 4 (but not exceeding DFS_FD_MAX)
  47. */
  48. static int _fdt_slot_expand(struct dfs_fdtable *fdt, int fd)
  49. {
  50. int nr;
  51. int index;
  52. struct dfs_file **fds = NULL;
  53. if (fd < fdt->maxfd)
  54. {
  55. return fd;
  56. }
  57. if (fd >= DFS_FD_MAX)
  58. {
  59. return -1;
  60. }
  61. nr = ((fd + 4) & ~3);
  62. if (nr > DFS_FD_MAX)
  63. {
  64. nr = DFS_FD_MAX;
  65. }
  66. fds = (struct dfs_file **)rt_realloc(fdt->fds, nr * sizeof(struct dfs_file *));
  67. if (!fds)
  68. {
  69. return -1;
  70. }
  71. /* clean the new allocated fds */
  72. for (index = fdt->maxfd; index < nr; index++)
  73. {
  74. fds[index] = NULL;
  75. }
  76. fdt->fds = fds;
  77. fdt->maxfd = nr;
  78. return fd;
  79. }
  80. /**
  81. * @brief Allocate an available file descriptor slot in the file descriptor table
  82. *
  83. * @param[in,out] fdt Pointer to the file descriptor table to allocate from
  84. * @param[in] startfd The starting file descriptor index to begin searching
  85. *
  86. * @return int The allocated file descriptor index if successful (>= 0),
  87. * -1 if allocation failed (table expansion failed)
  88. *
  89. * @note If no empty slot found, expand the table and return the new index.
  90. */
  91. static int _fdt_slot_alloc(struct dfs_fdtable *fdt, int startfd)
  92. {
  93. int idx;
  94. /* find an empty fd slot */
  95. for (idx = startfd; idx < (int)fdt->maxfd; idx++)
  96. {
  97. if (fdt->fds[idx] == RT_NULL)
  98. {
  99. return idx;
  100. }
  101. }
  102. idx = fdt->maxfd;
  103. if (idx < startfd)
  104. {
  105. idx = startfd;
  106. }
  107. if (_fdt_slot_expand(fdt, idx) < 0)
  108. {
  109. return -1;
  110. }
  111. return idx;
  112. }
  113. /**
  114. * @brief Allocate a file descriptor from the file descriptor table
  115. *
  116. * @param[in,out] fdt Pointer to the file descriptor table to allocate from
  117. * @param[in] startfd The starting file descriptor index to begin searching
  118. *
  119. * @return int The allocated file descriptor index if successful (>= 0),
  120. * -1 if allocation failed
  121. *
  122. * @note This is a wrapper function that calls _fdt_slot_alloc() to perform
  123. * the actual allocation. It maintains the same behavior as _fdt_slot_alloc().
  124. */
  125. static int _fdt_fd_alloc(struct dfs_fdtable *fdt, int startfd)
  126. {
  127. int idx;
  128. idx = _fdt_slot_alloc(fdt, startfd);
  129. return idx;
  130. }
  131. /**
  132. * this function will lock device file system.
  133. *
  134. * @note please don't invoke it on ISR.
  135. */
  136. rt_err_t dfs_lock(void)
  137. {
  138. rt_err_t result = -RT_EBUSY;
  139. while (result == -RT_EBUSY)
  140. {
  141. result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
  142. }
  143. return result;
  144. }
  145. /**
  146. * this function will unlock device file system.
  147. *
  148. * @note please don't invoke it on ISR.
  149. */
  150. void dfs_unlock(void)
  151. {
  152. rt_mutex_release(&fslock);
  153. }
  154. /** @addtogroup group_device_virtual_file_system
  155. *
  156. *
  157. * @{
  158. */
  159. /**
  160. * @brief Lock the file descriptor table mutex
  161. *
  162. * @return rt_err_t RT_EOK if lock acquired successfully,
  163. * -RT_ENOSYS if filesystem not initialized,
  164. * -RT_EBUSY if mutex is already locked (retries until acquired)
  165. *
  166. * @note This function will block indefinitely until the lock is acquired.
  167. * Should not be called from interrupt service routines.
  168. */
  169. rt_err_t dfs_file_lock(void)
  170. {
  171. rt_err_t result = -RT_EBUSY;
  172. if (!_dfs_init_ok)
  173. {
  174. return -RT_ENOSYS;
  175. }
  176. while (result == -RT_EBUSY)
  177. {
  178. result = rt_mutex_take(&fdlock, RT_WAITING_FOREVER);
  179. }
  180. return result;
  181. }
  182. /**
  183. * @brief Unlock the file descriptor table mutex
  184. *
  185. * @note This function releases the lock acquired by dfs_file_lock().
  186. * Should be called in the same context as the corresponding lock.
  187. * Should not be called from interrupt service routines.
  188. */
  189. void dfs_file_unlock(void)
  190. {
  191. rt_mutex_release(&fdlock);
  192. }
  193. /**
  194. * this function will initialize device file system.
  195. */
  196. int dfs_init(void)
  197. {
  198. if (_dfs_init_ok)
  199. {
  200. LOG_E("DFS was already initialized.\n");
  201. return 0;
  202. }
  203. /* create device filesystem lock */
  204. rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
  205. rt_mutex_init(&fdlock, "fdlock", RT_IPC_FLAG_FIFO);
  206. /* Initialize dentry system */
  207. dfs_dentry_init();
  208. _dfs_init_ok = RT_TRUE;
  209. return 0;
  210. }
  211. INIT_PREV_EXPORT(dfs_init);
  212. /**
  213. * @brief Create and initialize a new file descriptor structure
  214. *
  215. * @return struct dfs_file* Pointer to the newly created file descriptor structure,
  216. * NULL if memory allocation failed
  217. */
  218. struct dfs_file* dfs_file_create(void)
  219. {
  220. struct dfs_file *file;
  221. file = (struct dfs_file *)rt_calloc(1, sizeof(struct dfs_file));
  222. if (file)
  223. {
  224. file->magic = DFS_FD_MAGIC;
  225. file->ref_count = 1;
  226. rt_mutex_init(&file->pos_lock, "fpos", RT_IPC_FLAG_PRIO);
  227. }
  228. return file;
  229. }
  230. /**
  231. * @brief Destroy and free a file descriptor structure
  232. *
  233. * @param[in] file Pointer to the file descriptor structure to be destroyed
  234. */
  235. void dfs_file_destroy(struct dfs_file *file)
  236. {
  237. rt_mutex_detach(&file->pos_lock);
  238. if (file->mmap_context)
  239. {
  240. rt_free(file->mmap_context);
  241. }
  242. rt_free(file);
  243. }
  244. /**
  245. * @ingroup group_fs_file_descriptor
  246. *
  247. * @brief Allocate a new file descriptor in the file descriptor table
  248. *
  249. * @param[in,out] fdt Pointer to the file descriptor table to allocate from
  250. *
  251. * @return int The allocated file descriptor index if successful (>= 0),
  252. * -RT_ENOSYS if filesystem lock failed,
  253. * -1 if allocation failed (no empty slots or memory allocation failed)
  254. */
  255. int fdt_fd_new(struct dfs_fdtable *fdt)
  256. {
  257. int idx = -1;
  258. /* lock filesystem */
  259. if (dfs_file_lock() != RT_EOK)
  260. {
  261. return -RT_ENOSYS;
  262. }
  263. /* find an empty fd entry */
  264. idx = _fdt_fd_alloc(fdt, (fdt == &_fdtab) ? DFS_STDIO_OFFSET : 0);
  265. /* can't find an empty fd entry */
  266. if (idx < 0)
  267. {
  268. LOG_E("DFS fd new is failed! Could not found an empty fd entry.");
  269. }
  270. else if (!fdt->fds[idx])
  271. {
  272. struct dfs_file *file;
  273. file = dfs_file_create();
  274. if (file)
  275. {
  276. fdt->fds[idx] = file;
  277. LOG_D("allocate a new fd @ %d", idx);
  278. }
  279. else
  280. {
  281. fdt->fds[idx] = RT_NULL;
  282. idx = -1;
  283. }
  284. }
  285. else
  286. {
  287. LOG_E("DFS not found an empty fds entry.");
  288. idx = -1;
  289. }
  290. dfs_file_unlock();
  291. return idx;
  292. }
  293. /**
  294. * @brief Release a file descriptor from the file descriptor table
  295. *
  296. * @param[in,out] fdt Pointer to the file descriptor table
  297. * @param[in] fd The file descriptor to be released
  298. */
  299. void fdt_fd_release(struct dfs_fdtable *fdt, int fd)
  300. {
  301. if (fd < fdt->maxfd)
  302. {
  303. struct dfs_file *file;
  304. file = fdt_get_file(fdt, fd);
  305. if (file && file->ref_count == 1)
  306. {
  307. dfs_file_destroy(file);
  308. }
  309. else
  310. {
  311. rt_atomic_sub(&(file->ref_count), 1);
  312. }
  313. fdt->fds[fd] = RT_NULL;
  314. }
  315. }
  316. /**
  317. * @ingroup group_fs_file_descriptor
  318. *
  319. * This function will return a file descriptor structure according to file
  320. * descriptor.
  321. *
  322. * @return NULL on on this file descriptor or the file descriptor structure
  323. * pointer.
  324. */
  325. struct dfs_file *fdt_get_file(struct dfs_fdtable *fdt, int fd)
  326. {
  327. struct dfs_file *f;
  328. if (fd < 0 || fd >= (int)fdt->maxfd)
  329. {
  330. return NULL;
  331. }
  332. f = fdt->fds[fd];
  333. /* check file valid or not */
  334. if ((f == NULL) || (f->magic != DFS_FD_MAGIC))
  335. {
  336. return NULL;
  337. }
  338. return f;
  339. }
  340. /**
  341. * @brief Associate a file structure with a file descriptor in the file descriptor table
  342. *
  343. * @param[in,out] fdt Pointer to the file descriptor table
  344. * @param[in] fd The file descriptor to associate with
  345. * @param[in] file Pointer to the file structure to associate
  346. *
  347. * @return int The file descriptor if successful (>= 0),
  348. * -1 if association failed (invalid parameters or fd already in use),
  349. * -RT_ENOSYS if filesystem lock failed
  350. */
  351. int fdt_fd_associate_file(struct dfs_fdtable *fdt, int fd, struct dfs_file *file)
  352. {
  353. int retfd = -1;
  354. if (!file)
  355. {
  356. return retfd;
  357. }
  358. if (!fdt)
  359. {
  360. return retfd;
  361. }
  362. if (dfs_file_lock() != RT_EOK)
  363. {
  364. return -RT_ENOSYS;
  365. }
  366. /* check old fd */
  367. if ((fd < 0) || (fd >= fdt->maxfd))
  368. {
  369. goto exit;
  370. }
  371. if (fdt->fds[fd])
  372. {
  373. goto exit;
  374. }
  375. /* inc ref_count */
  376. rt_atomic_add(&(file->ref_count), 1);
  377. fdt->fds[fd] = file;
  378. retfd = fd;
  379. exit:
  380. dfs_file_unlock();
  381. return retfd;
  382. }
  383. /**
  384. * @brief Allocate a new file descriptor in current process's file descriptor table
  385. *
  386. * @return int The allocated file descriptor index if successful (>= 0),
  387. * -1 if allocation failed
  388. */
  389. int fd_new(void)
  390. {
  391. struct dfs_fdtable *fdt;
  392. fdt = dfs_fdtable_get();
  393. return fdt_fd_new(fdt);
  394. }
  395. /**
  396. * @ingroup group_fs_file_descriptor
  397. *
  398. * This function will put the file descriptor.
  399. */
  400. void fd_release(int fd)
  401. {
  402. struct dfs_fdtable *fdt;
  403. fdt = dfs_fdtable_get();
  404. fdt_fd_release(fdt, fd);
  405. }
  406. /**
  407. * @brief Get file structure by file descriptor from current process's fd table
  408. *
  409. * @param[in] fd The file descriptor to lookup
  410. *
  411. * @return struct dfs_file* Pointer to the file structure if found,
  412. * NULL if invalid fd or file not found
  413. */
  414. struct dfs_file *fd_get(int fd)
  415. {
  416. struct dfs_fdtable *fdt;
  417. fdt = dfs_fdtable_get();
  418. return fdt_get_file(fdt, fd);
  419. }
  420. /**
  421. * This function will get the file descriptor table of current process.
  422. */
  423. struct dfs_fdtable *dfs_fdtable_get(void)
  424. {
  425. struct dfs_fdtable *fdt;
  426. #ifdef RT_USING_SMART
  427. struct rt_lwp *lwp = NULL;
  428. rt_thread_t thread = rt_thread_self();
  429. if (thread)
  430. {
  431. lwp = (struct rt_lwp *)thread->lwp;
  432. }
  433. if (lwp)
  434. fdt = &lwp->fdt;
  435. else
  436. fdt = &_fdtab;
  437. #else
  438. fdt = &_fdtab;
  439. #endif
  440. return fdt;
  441. }
  442. #ifdef RT_USING_SMART
  443. /**
  444. * @brief Get file structure by file descriptor from current process's fd table
  445. *
  446. * @param[in] fd The file descriptor to lookup
  447. *
  448. * @return struct dfs_file* Pointer to the file structure if found,
  449. * NULL if invalid fd or file not found
  450. */
  451. struct dfs_fdtable *dfs_fdtable_get_from_pid(int pid)
  452. {
  453. struct rt_lwp *lwp = RT_NULL;
  454. struct dfs_fdtable *fdt = RT_NULL;
  455. lwp_pid_lock_take();
  456. lwp = lwp_from_pid_locked(pid);
  457. if (lwp)
  458. {
  459. fdt = &lwp->fdt;
  460. }
  461. lwp_pid_lock_release();
  462. return fdt;
  463. }
  464. #endif
  465. /**
  466. * @brief Get the global file descriptor table
  467. *
  468. * @return struct dfs_fdtable* Pointer to the global file descriptor table
  469. */
  470. struct dfs_fdtable *dfs_fdtable_get_global(void)
  471. {
  472. return &_fdtab;
  473. }
  474. /**
  475. * @brief Dup the specified fd_src from fdt_src to fdt_dst.
  476. *
  477. * @param[out] fdt_dst is the fd table for destination, if empty, use global (_fdtab).
  478. *
  479. * @param[in] fdt_src is the fd table for source, if empty, use global (_fdtab).
  480. *
  481. * @param[in] fd_src is the fd in the designate fdt_src table.
  482. *
  483. * @return -1 on failed or the allocated file descriptor.
  484. */
  485. int dfs_fdtable_dup(struct dfs_fdtable *fdt_dst, struct dfs_fdtable *fdt_src, int fd_src)
  486. {
  487. int newfd = -1;
  488. if (dfs_file_lock() != RT_EOK)
  489. {
  490. return -RT_ENOSYS;
  491. }
  492. if (fdt_src == NULL)
  493. {
  494. fdt_src = &_fdtab;
  495. }
  496. if (fdt_dst == NULL)
  497. {
  498. fdt_dst = &_fdtab;
  499. }
  500. /* check fd */
  501. if ((fd_src < 0) || (fd_src >= fdt_src->maxfd))
  502. {
  503. goto _EXIT;
  504. }
  505. if (!fdt_src->fds[fd_src])
  506. {
  507. goto _EXIT;
  508. }
  509. /* get a new fd*/
  510. newfd = fdt_fd_new(fdt_dst);
  511. if (newfd >= 0)
  512. {
  513. fdt_dst->fds[newfd]->mode = fdt_src->fds[fd_src]->mode;
  514. fdt_dst->fds[newfd]->flags = fdt_src->fds[fd_src]->flags;
  515. fdt_dst->fds[newfd]->fops = fdt_src->fds[fd_src]->fops;
  516. fdt_dst->fds[newfd]->dentry = dfs_dentry_ref(fdt_src->fds[fd_src]->dentry);
  517. fdt_dst->fds[newfd]->vnode = fdt_src->fds[fd_src]->vnode;
  518. fdt_dst->fds[newfd]->mmap_context = RT_NULL;
  519. fdt_dst->fds[newfd]->data = fdt_src->fds[fd_src]->data;
  520. /*
  521. * dma-buf/socket fd is without dentry, so should used the vnode reference.
  522. */
  523. if (!fdt_dst->fds[newfd]->dentry)
  524. {
  525. rt_atomic_add(&(fdt_dst->fds[newfd]->vnode->ref_count), 1);
  526. }
  527. }
  528. _EXIT:
  529. dfs_file_unlock();
  530. return newfd;
  531. }
  532. /**
  533. * @brief drop fd from the fd table.
  534. *
  535. * @param fdt is the fd table, if empty, use global (_fdtab).
  536. *
  537. * @param fd is the fd in the designate fd table.
  538. *
  539. * @return -1 on failed the drop file descriptor.
  540. */
  541. int dfs_fdtable_drop_fd(struct dfs_fdtable *fdt, int fd)
  542. {
  543. int err = 0;
  544. if (fdt == NULL)
  545. {
  546. fdt = &_fdtab;
  547. }
  548. if (dfs_file_lock() != RT_EOK)
  549. {
  550. return -RT_ENOSYS;
  551. }
  552. err = dfs_file_close(fdt->fds[fd]);
  553. if (!err)
  554. {
  555. fdt_fd_release(fdt, fd);
  556. }
  557. dfs_file_unlock();
  558. return err;
  559. }
  560. /**
  561. * @brief Duplicate a file descriptor in the current process's file descriptor table
  562. *
  563. * @param[in] oldfd The file descriptor to duplicate
  564. * @param[in] startfd The starting index to search for an available file descriptor slot
  565. *
  566. * @return int The new file descriptor if successful (>=0),
  567. * -1 if failed (invalid fd or allocation failed),
  568. * -RT_ENOSYS if filesystem lock failed
  569. */
  570. int dfs_dup(int oldfd, int startfd)
  571. {
  572. int newfd = -1;
  573. struct dfs_fdtable *fdt = NULL;
  574. if (dfs_file_lock() != RT_EOK)
  575. {
  576. return -RT_ENOSYS;
  577. }
  578. /* check old fd */
  579. fdt = dfs_fdtable_get();
  580. if ((oldfd < 0) || (oldfd >= fdt->maxfd))
  581. {
  582. rt_set_errno(-EBADF);
  583. goto exit;
  584. }
  585. if (!fdt->fds[oldfd])
  586. {
  587. goto exit;
  588. }
  589. /* get a new fd */
  590. newfd = _fdt_slot_alloc(fdt, startfd);
  591. if (newfd >= 0)
  592. {
  593. fdt->fds[newfd] = fdt->fds[oldfd];
  594. /* inc ref_count */
  595. rt_atomic_add(&(fdt->fds[newfd]->ref_count), 1);
  596. }
  597. exit:
  598. dfs_file_unlock();
  599. return newfd;
  600. }
  601. /**
  602. * @brief Duplicate a file descriptor from current process to target file descriptor table
  603. *
  604. * @param[in] oldfd is the fd in current process.
  605. * @param[in,out] fdtab is the fd table to dup, if empty, use global (_fdtab).
  606. *
  607. * @return -1 on failed or the allocated file descriptor.
  608. */
  609. int dfs_dup_to(int oldfd, struct dfs_fdtable *fdtab)
  610. {
  611. int newfd = -1;
  612. struct dfs_fdtable *fdt = NULL;
  613. if (dfs_file_lock() != RT_EOK)
  614. {
  615. return -RT_ENOSYS;
  616. }
  617. if (fdtab == NULL)
  618. {
  619. fdtab = &_fdtab;
  620. }
  621. /* check old fd */
  622. fdt = dfs_fdtable_get();
  623. if ((oldfd < 0) || (oldfd >= fdt->maxfd))
  624. {
  625. goto exit;
  626. }
  627. if (!fdt->fds[oldfd])
  628. {
  629. goto exit;
  630. }
  631. /* get a new fd*/
  632. newfd = _fdt_slot_alloc(fdtab, DFS_STDIO_OFFSET);
  633. if (newfd >= 0)
  634. {
  635. fdtab->fds[newfd] = fdt->fds[oldfd];
  636. /* inc ref_count */
  637. rt_atomic_add(&(fdtab->fds[newfd]->ref_count), 1);
  638. }
  639. exit:
  640. dfs_file_unlock();
  641. return newfd;
  642. }
  643. /**
  644. * @brief Duplicate a file descriptor from source table to current process
  645. *
  646. * @param[in] oldfd is the fd in the designate fd table.
  647. * @param[in,out] fdtab is the fd table for oldfd, if empty, use global (_fdtab).
  648. *
  649. * @return -1 on failed or the allocated file descriptor.
  650. */
  651. int dfs_dup_from(int oldfd, struct dfs_fdtable *fdtab)
  652. {
  653. int newfd = -1;
  654. struct dfs_file *file;
  655. if (dfs_file_lock() != RT_EOK)
  656. {
  657. return -RT_ENOSYS;
  658. }
  659. if (fdtab == NULL)
  660. {
  661. fdtab = &_fdtab;
  662. }
  663. /* check old fd */
  664. if ((oldfd < 0) || (oldfd >= fdtab->maxfd))
  665. {
  666. goto exit;
  667. }
  668. if (!fdtab->fds[oldfd])
  669. {
  670. goto exit;
  671. }
  672. /* get a new fd*/
  673. newfd = fd_new();
  674. file = fd_get(newfd);
  675. if (newfd >= 0 && file)
  676. {
  677. file->mode = fdtab->fds[oldfd]->mode;
  678. file->flags = fdtab->fds[oldfd]->flags;
  679. file->fops = fdtab->fds[oldfd]->fops;
  680. file->dentry = dfs_dentry_ref(fdtab->fds[oldfd]->dentry);
  681. file->vnode = fdtab->fds[oldfd]->vnode;
  682. file->mmap_context = RT_NULL;
  683. file->data = fdtab->fds[oldfd]->data;
  684. }
  685. dfs_file_close(fdtab->fds[oldfd]);
  686. exit:
  687. fdt_fd_release(fdtab, oldfd);
  688. dfs_file_unlock();
  689. return newfd;
  690. }
  691. /**
  692. * @brief System call to duplicate a file descriptor
  693. *
  694. * @param[in] oldfd The file descriptor to duplicate
  695. *
  696. * @return sysret_t/int The new file descriptor if successful (>=0),
  697. * negative error code if failed
  698. */
  699. #ifdef RT_USING_SMART
  700. sysret_t sys_dup(int oldfd)
  701. #else
  702. int sys_dup(int oldfd)
  703. #endif
  704. {
  705. int err = 0;
  706. int newfd = dfs_dup(oldfd, (dfs_fdtable_get() == &_fdtab) ? DFS_STDIO_OFFSET : 0);
  707. if(newfd < 0)
  708. {
  709. err = rt_get_errno();
  710. }
  711. #ifdef RT_USING_SMART
  712. return err < 0 ? err : newfd;
  713. #else
  714. return err < 0 ? err : newfd;
  715. #endif
  716. }
  717. /**
  718. * @brief System call to duplicate a file descriptor to a specific descriptor number
  719. *
  720. * @param[in] oldfd The file descriptor to duplicate
  721. * @param[in] newfd The desired file descriptor number
  722. *
  723. * @return rt_err_t The new file descriptor number if successful (>=0),
  724. * -RT_ENOSYS if filesystem lock failed,
  725. * -1 if operation failed (invalid descriptors or allocation failed)
  726. */
  727. rt_err_t sys_dup2(int oldfd, int newfd)
  728. {
  729. struct dfs_fdtable *fdt = NULL;
  730. int ret = 0;
  731. int retfd = -1;
  732. if (dfs_file_lock() != RT_EOK)
  733. {
  734. return -RT_ENOSYS;
  735. }
  736. /* check old fd */
  737. fdt = dfs_fdtable_get();
  738. if ((oldfd < 0) || (oldfd >= fdt->maxfd))
  739. {
  740. goto exit;
  741. }
  742. if (!fdt->fds[oldfd])
  743. {
  744. goto exit;
  745. }
  746. if (newfd < 0)
  747. {
  748. goto exit;
  749. }
  750. if (newfd >= fdt->maxfd)
  751. {
  752. newfd = _fdt_slot_expand(fdt, newfd);
  753. if (newfd < 0)
  754. {
  755. goto exit;
  756. }
  757. }
  758. if (fdt->fds[newfd] == fdt->fds[oldfd])
  759. {
  760. /* ok, return newfd */
  761. retfd = newfd;
  762. goto exit;
  763. }
  764. if (fdt->fds[newfd])
  765. {
  766. ret = dfs_file_close(fdt->fds[newfd]);
  767. if (ret < 0)
  768. {
  769. goto exit;
  770. }
  771. fd_release(newfd);
  772. }
  773. fdt->fds[newfd] = fdt->fds[oldfd];
  774. /* inc ref_count */
  775. rt_atomic_add(&(fdt->fds[newfd]->ref_count), 1);
  776. retfd = newfd;
  777. exit:
  778. dfs_file_unlock();
  779. return retfd;
  780. }
  781. /**
  782. * @brief Get the subdirectory path relative to a parent directory
  783. *
  784. * @param[in] directory The parent directory path
  785. * @param[in] filename The full path including parent directory and subpath
  786. *
  787. * @return const char* Pointer to the subdirectory portion of filename,
  788. * NULL if paths are identical or invalid
  789. */
  790. const char *dfs_subdir(const char *directory, const char *filename)
  791. {
  792. const char *dir;
  793. if (strlen(directory) == strlen(filename)) /* it's a same path */
  794. return NULL;
  795. dir = filename + strlen(directory);
  796. if ((*dir != '/') && (dir != filename))
  797. {
  798. dir--;
  799. }
  800. return dir;
  801. }
  802. RTM_EXPORT(dfs_subdir);
  803. /**
  804. * @brief Normalize a path by combining directory and filename into an absolute path
  805. *
  806. * @param[in] directory The parent directory path (NULL means use working directory)
  807. * @param[in] filename The filename or relative path to be normalized
  808. *
  809. * @return char* The normalized absolute path (must be freed by caller),
  810. * NULL if path is invalid or memory allocation fails
  811. *
  812. * @note This function will:
  813. * - Handle working directory when directory is NULL
  814. * - Join directory and filename with proper separators
  815. * - Resolve . and .. in paths
  816. * - Remove redundant slashes
  817. * - Ensure path starts with /
  818. * - Allocate memory for the returned path string
  819. */
  820. char *dfs_normalize_path(const char *directory, const char *filename)
  821. {
  822. char *fullpath;
  823. char *dst0, *dst, *src;
  824. /* check parameters */
  825. RT_ASSERT(filename != NULL);
  826. #ifdef DFS_USING_WORKDIR
  827. if (directory == NULL) /* shall use working directory */
  828. {
  829. #ifdef RT_USING_SMART
  830. directory = lwp_getcwd();
  831. #else
  832. directory = &working_directory[0];
  833. #endif
  834. }
  835. #else
  836. if ((directory == NULL) && (filename[0] != '/'))
  837. {
  838. rt_kprintf(NO_WORKING_DIR);
  839. return NULL;
  840. }
  841. #endif
  842. if (filename[0] != '/')
  843. {
  844. int path_len;
  845. path_len = strlen(directory) + strlen(filename) + 2;
  846. if (path_len > DFS_PATH_MAX)
  847. {
  848. return NULL;
  849. }
  850. fullpath = (char *)rt_malloc(path_len);
  851. if (fullpath == NULL)
  852. {
  853. return NULL;
  854. }
  855. /* join path and file name */
  856. rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
  857. "%s/%s", directory, filename);
  858. }
  859. else /* it's a absolute path, use it directly */
  860. {
  861. fullpath = rt_strdup(filename); /* copy string */
  862. if (fullpath == NULL)
  863. return NULL;
  864. }
  865. /* Initialize source and destination pointers to start of path */
  866. src = fullpath;
  867. dst = fullpath;
  868. /* Save initial position for boundary checking */
  869. dst0 = dst;
  870. while (1)
  871. {
  872. char c = *src;
  873. /* Handle '.' and '..' path components */
  874. if (c == '.')
  875. {
  876. /* Single dot at end of path */
  877. if (!src[1])
  878. src++; /* '.' and ends, Skip single dot */
  879. else if (src[1] == '/')
  880. {
  881. /* './' case */
  882. src += 2;
  883. /* Skip consecutive slashes */
  884. while ((*src == '/') && (*src != '\0'))
  885. src++;
  886. continue;
  887. }
  888. else if (src[1] == '.')
  889. {
  890. /* Parent directory reference */
  891. if (!src[2])
  892. {
  893. /* '..' and ends case */
  894. src += 2;
  895. goto up_one;
  896. }
  897. else if (src[2] == '/')
  898. {
  899. /* '../' case: parent directory reference */
  900. src += 3;
  901. /* Skip consecutive slashes */
  902. while ((*src == '/') && (*src != '\0'))
  903. src++;
  904. goto up_one;
  905. }
  906. }
  907. }
  908. /* copy up the next '/' and erase all '/' */
  909. while ((c = *src++) != '\0' && c != '/')
  910. *dst++ = c;
  911. if (c == '/')
  912. {
  913. /* Add single slash */
  914. *dst++ = '/';
  915. while (c == '/')
  916. c = *src++;
  917. src--;
  918. }
  919. else if (!c)
  920. break; /* End of string */
  921. continue;
  922. /* Handle parent directory reference */
  923. up_one:
  924. /* Move back one directory level */
  925. dst--;
  926. if (dst < dst0) /* Check for path traversal underflow */
  927. {
  928. rt_free(fullpath);
  929. return NULL;
  930. }
  931. /* Find previous directory separator */
  932. while (dst0 < dst && dst[-1] != '/')
  933. dst--;
  934. }
  935. /* Null-terminate the path */
  936. *dst = '\0';
  937. /* remove '/' in the end of path if exist */
  938. dst--;
  939. if (dst > fullpath && (*dst == '/'))
  940. *dst = '\0';
  941. /* final check fullpath is not empty, for the special path of lwext "/.." */
  942. if ('\0' == fullpath[0])
  943. {
  944. fullpath[0] = '/';
  945. fullpath[1] = '\0';
  946. }
  947. return fullpath;
  948. }
  949. RTM_EXPORT(dfs_normalize_path);
  950. #ifdef RT_USING_FINSH
  951. #include <finsh.h>
  952. /**
  953. * @brief List all open file descriptors in the current process
  954. *
  955. * @return int 0 on success, -1 if failed to get file descriptor table
  956. */
  957. int list_fd(void)
  958. {
  959. int index;
  960. struct dfs_fdtable *fd_table;
  961. fd_table = dfs_fdtable_get();
  962. if (!fd_table) return -1;
  963. rt_enter_critical();
  964. rt_kprintf("fd type ref magic path\n");
  965. rt_kprintf("-- ------ --- ----- ------\n");
  966. for (index = 0; index < (int)fd_table->maxfd; index++)
  967. {
  968. struct dfs_file *file = fd_table->fds[index];
  969. if (file && file->vnode)
  970. {
  971. rt_kprintf("%2d ", index);
  972. if (file->vnode->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir");
  973. else if (file->vnode->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
  974. else if (file->vnode->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket");
  975. else if (file->vnode->type == FT_USER) rt_kprintf("%-7.7s ", "user");
  976. else if (file->vnode->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device");
  977. else rt_kprintf("%-8.8s ", "unknown");
  978. rt_kprintf("%3d ", file->ref_count);
  979. rt_kprintf("%04x ", file->magic);
  980. if (file->dentry)
  981. {
  982. rt_kprintf("%s%s\n", file->dentry->mnt->fullpath, file->dentry->pathname);
  983. }
  984. else
  985. {
  986. rt_kprintf("\n");
  987. }
  988. }
  989. }
  990. rt_exit_critical();
  991. return 0;
  992. }
  993. MSH_CMD_EXPORT(list_fd, list file descriptor);
  994. /**
  995. * @brief Dump all file descriptors information in the global file descriptor table
  996. *
  997. * @param[in] argc Number of command line arguments (unused)
  998. * @param[in] argv Array of command line arguments (unused)
  999. *
  1000. * @return int 0 on success,
  1001. * -RT_ENOSYS if failed to acquire file system lock
  1002. */
  1003. int dfs_fd_dump(int argc, char** argv)
  1004. {
  1005. int index;
  1006. if (dfs_file_lock() != RT_EOK)
  1007. {
  1008. return -RT_ENOSYS;
  1009. }
  1010. for (index = 0; index < _fdtab.maxfd; index++)
  1011. {
  1012. struct dfs_file *file = _fdtab.fds[index];
  1013. if (file)
  1014. {
  1015. char* fullpath = dfs_dentry_full_path(file->dentry);
  1016. if (fullpath)
  1017. {
  1018. printf("[%d] - %s, ref_count %zd\n", index,
  1019. fullpath, (size_t)rt_atomic_load(&(file->ref_count)));
  1020. rt_free(fullpath);
  1021. }
  1022. else
  1023. {
  1024. printf("[%d] - %s, ref_count %zd\n", index,
  1025. file->dentry->pathname, (size_t)rt_atomic_load(&(file->ref_count)));
  1026. }
  1027. }
  1028. }
  1029. dfs_file_unlock();
  1030. return 0;
  1031. }
  1032. MSH_CMD_EXPORT_ALIAS(dfs_fd_dump, fd_dump, fd dump);
  1033. #ifdef PKG_USING_DLOG
  1034. /**
  1035. * @brief Control the DFS (Device File System) debug logging functionality
  1036. *
  1037. * @param[in] argc Number of command line arguments (must be 2)
  1038. * @param[in] argv Array of command line arguments:
  1039. * - argv[1]: "on" to enable logging, "off" to disable logging
  1040. *
  1041. * @return int Always returns 0 (success)
  1042. *
  1043. * @note When enabled, this function activates logging for multiple DFS components:
  1044. * - dfs: Core DFS functionality
  1045. * - dfs_file: File operations
  1046. * - dentry: Directory entries
  1047. * - vnode: Virtual nodes
  1048. * - mnt: Mount points
  1049. * - rom: ROM filesystem
  1050. * - devfs: Device filesystem
  1051. */
  1052. int dfs_dlog(int argc, char** argv)
  1053. {
  1054. if (argc == 2)
  1055. {
  1056. if (strcmp(argv[1], "on") == 0)
  1057. {
  1058. dlog_session_start();
  1059. dlog_participant("dfs");
  1060. dlog_participant("dfs_file");
  1061. dlog_participant("dentry");
  1062. dlog_participant("vnode");
  1063. dlog_participant("mnt");
  1064. dlog_participant("rom");
  1065. dlog_participant("devfs");
  1066. }
  1067. else if (strcmp(argv[1], "off") == 0)
  1068. {
  1069. dlog_session_stop();
  1070. }
  1071. }
  1072. return 0;
  1073. }
  1074. MSH_CMD_EXPORT(dfs_dlog, dfs dlog on|off);
  1075. #endif
  1076. #endif
  1077. /** @} */