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