1 /*
2 * Copyright (c) 2006-2024 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_file.h>
16 #include "dfs_private.h"
17 #ifdef RT_USING_SMART
18 #include <lwp.h>
19 #endif
20
21 #ifdef RT_USING_POSIX_STDIO
22 #include <posix/stdio.h>
23 #endif /* RT_USING_POSIX_STDIO */
24
25 /* Global variables */
26 const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
27 struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
28
29 /* device filesystem lock */
30 static struct rt_mutex fslock;
31 static struct rt_mutex fdlock;
32
33 #ifdef DFS_USING_WORKDIR
34 char working_directory[DFS_PATH_MAX] = {"/"};
35 #endif
36
37 static struct dfs_fdtable _fdtab;
38 static int fd_alloc(struct dfs_fdtable *fdt, int startfd);
39
40 /**
41 * @addtogroup group_device_virtual_file_system
42 * @{
43 */
44
45 /**
46 * this function will initialize device file system.
47 */
dfs_init(void)48 int dfs_init(void)
49 {
50 static rt_bool_t init_ok = RT_FALSE;
51
52 if (init_ok)
53 {
54 rt_kprintf("dfs already init.\n");
55 return 0;
56 }
57
58 /* init vnode hash table */
59 dfs_vnode_mgr_init();
60
61 /* clear filesystem operations table */
62 rt_memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
63 /* clear filesystem table */
64 rt_memset(filesystem_table, 0, sizeof(filesystem_table));
65 /* clean fd table */
66 rt_memset(&_fdtab, 0, sizeof(_fdtab));
67
68 /* create device filesystem lock */
69 rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_PRIO);
70 rt_mutex_init(&fdlock, "fdlock", RT_IPC_FLAG_PRIO);
71
72 #ifdef DFS_USING_WORKDIR
73 /* set current working directory */
74 rt_memset(working_directory, 0, sizeof(working_directory));
75 working_directory[0] = '/';
76 #endif
77
78 #ifdef RT_USING_DFS_TMPFS
79 {
80 extern int dfs_tmpfs_init(void);
81 dfs_tmpfs_init();
82 }
83 #endif
84
85 #ifdef RT_USING_DFS_DEVFS
86 {
87 extern int devfs_init(void);
88
89 /* if enable devfs, initialize and mount it as soon as possible */
90 devfs_init();
91
92 dfs_mount(NULL, "/dev", "devfs", 0, 0);
93 }
94 #if defined(RT_USING_DEV_BUS) && defined(RT_USING_DFS_TMPFS)
95 mkdir("/dev/shm", 0x777);
96 if (dfs_mount(RT_NULL, "/dev/shm", "tmp", 0, 0) != 0)
97 {
98 rt_kprintf("Dir /dev/shm mount failed!\n");
99 }
100 #endif
101 #endif
102
103 init_ok = RT_TRUE;
104
105 return 0;
106 }
107 INIT_PREV_EXPORT(dfs_init);
108
109 /**
110 * @brief this function will lock device file system.
111 * this lock (fslock) is used for protecting filesystem_operation_table and filesystem_table.
112 *
113 * @note please don't invoke it on ISR.
114 */
dfs_lock(void)115 void dfs_lock(void)
116 {
117 rt_err_t result = -RT_EBUSY;
118
119 while (result == -RT_EBUSY)
120 {
121 result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
122 }
123
124 if (result != RT_EOK)
125 {
126 RT_ASSERT(0);
127 }
128 }
129
130 /**
131 * @brief this function will lock file descriptors.
132 * this lock (fdlock) is used for protecting fd table (_fdtab).
133 *
134 * @note please don't invoke it on ISR.
135 */
dfs_file_lock(void)136 void dfs_file_lock(void)
137 {
138 rt_err_t result = -RT_EBUSY;
139
140 while (result == -RT_EBUSY)
141 {
142 result = rt_mutex_take(&fdlock, RT_WAITING_FOREVER);
143 }
144
145 if (result != RT_EOK)
146 {
147 RT_ASSERT(0);
148 }
149 }
150
151 /**
152 * @brief this function will unlock device file system.
153 *
154 * @note please don't invoke it on ISR.
155 */
dfs_unlock(void)156 void dfs_unlock(void)
157 {
158 rt_mutex_release(&fslock);
159 }
160
161 /**
162 * @brief this function will unlock fd table.
163 */
dfs_file_unlock(void)164 void dfs_file_unlock(void)
165 {
166 rt_mutex_release(&fdlock);
167 }
168
169 #ifdef DFS_USING_POSIX
170 /**
171 * @brief Expand the file descriptor table to accommodate a specific file descriptor.
172 *
173 * This function ensures that the file descriptor table in the given `dfs_fdtable` structure
174 * has sufficient capacity to include the specified file descriptor `fd`. If the table
175 * needs to be expanded, it reallocates memory and initializes new slots to `NULL`.
176 *
177 * @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
178 * @param fd The file descriptor that the table must accommodate.
179 * @return int
180 * - The input file descriptor `fd` if it is within the current or newly expanded table's capacity.
181 * - `-1` if the requested file descriptor exceeds `DFS_FD_MAX` or memory allocation fails.
182 */
fd_slot_expand(struct dfs_fdtable * fdt,int fd)183 static int fd_slot_expand(struct dfs_fdtable *fdt, int fd)
184 {
185 int nr;
186 int index;
187 struct dfs_file **fds = NULL;
188
189 /* If the file descriptor is already within the current capacity, no expansion is needed.*/
190 if (fd < fdt->maxfd)
191 {
192 return fd;
193 }
194
195 /* If the file descriptor exceeds the maximum allowable limit, return an error.*/
196 if (fd >= DFS_FD_MAX)
197 {
198 return -1;
199 }
200
201 /* Calculate the new capacity, rounding up to the nearest multiple of 4.*/
202 nr = ((fd + 4) & ~3);
203
204 /* Ensure the new capacity does not exceed the maximum limit.*/
205 if (nr > DFS_FD_MAX)
206 {
207 nr = DFS_FD_MAX;
208 }
209
210 /* Attempt to reallocate the file descriptor table to the new capacity.*/
211 fds = (struct dfs_file **)rt_realloc(fdt->fds, nr * sizeof(struct dfs_file *));
212 if (!fds)
213 {
214 return -1;
215 }
216
217 /* clean the new allocated fds */
218 for (index = fdt->maxfd; index < nr; index++)
219 {
220 fds[index] = NULL;
221 }
222
223 /* Update the file descriptor table and its capacity.*/
224 fdt->fds = fds;
225 fdt->maxfd = nr;
226
227 return fd;
228 }
229
230 /**
231 * @brief Allocate a file descriptor slot starting from a specified index.
232 *
233 * @param fdt fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
234 * @param startfd The starting index for the search for an empty slot.
235 * @return int
236 * - The index of the first available slot if successful.
237 * - `-1` if no slot is available or if table expansion fails
238 */
fd_slot_alloc(struct dfs_fdtable * fdt,int startfd)239 static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd)
240 {
241 int idx;
242
243 /* find an empty fd slot */
244 for (idx = startfd; idx < (int)fdt->maxfd; idx++)
245 {
246 if (fdt->fds[idx] == RT_NULL)
247 {
248 return idx;
249 }
250 }
251
252 idx = fdt->maxfd;
253 if (idx < startfd)
254 {
255 idx = startfd;
256 }
257 if (fd_slot_expand(fdt, idx) < 0)
258 {
259 return -1;
260 }
261 return idx;
262 }
263
264 /**
265 * @brief Allocate a new file descriptor and associate it with a newly allocated `struct dfs_file`.
266 *
267 * @param fdt Pointer to the `dfs_fdtable` structure representing the file descriptor table.
268 * @param startfd The starting index for searching an available file descriptor slot.
269 *
270 * @return
271 * - The index of the allocated file descriptor if successful.
272 * - `-1` if no slot is available or memory allocation fails.
273 */
fd_alloc(struct dfs_fdtable * fdt,int startfd)274 static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
275 {
276 int idx;
277 struct dfs_file *fd = NULL;
278
279 idx = fd_slot_alloc(fdt, startfd);
280
281 /* allocate 'struct dfs_file' */
282 if (idx < 0)
283 {
284 return -1;
285 }
286 fd = (struct dfs_file *)rt_calloc(1, sizeof(struct dfs_file));
287 if (!fd)
288 {
289 return -1;
290 }
291 fd->ref_count = 1;
292 fd->magic = DFS_FD_MAGIC;
293 fd->vnode = NULL;
294 fdt->fds[idx] = fd;
295
296 return idx;
297 }
298
299 /**
300 * @ingroup group_fs_file_descriptor
301 * This function will allocate a file descriptor.
302 *
303 * @return -1 on failed or the allocated file descriptor.
304 */
fdt_fd_new(struct dfs_fdtable * fdt)305 int fdt_fd_new(struct dfs_fdtable *fdt)
306 {
307 int idx;
308
309 /* lock filesystem */
310 dfs_file_lock();
311
312 /* find an empty fd entry */
313 idx = fd_alloc(fdt, DFS_STDIO_OFFSET);
314
315 /* can't find an empty fd entry */
316 if (idx < 0)
317 {
318 LOG_E("DFS fd new is failed! Could not found an empty fd entry.");
319 }
320
321 dfs_file_unlock();
322 return idx;
323 }
324
fd_new(void)325 int fd_new(void)
326 {
327 struct dfs_fdtable *fdt = NULL;
328
329 fdt = dfs_fdtable_get();
330 return fdt_fd_new(fdt);
331 }
332
333 /**
334 * @ingroup group_fs_file_descriptor
335 *
336 * This function will return a file descriptor structure according to file
337 * descriptor.
338 *
339 * @return NULL on on this file descriptor or the file descriptor structure
340 * pointer.
341 */
342
fdt_fd_get(struct dfs_fdtable * fdt,int fd)343 struct dfs_file *fdt_fd_get(struct dfs_fdtable* fdt, int fd)
344 {
345 struct dfs_file *d;
346
347 if (fd < 0 || fd >= (int)fdt->maxfd)
348 {
349 return NULL;
350 }
351
352 dfs_file_lock();
353 d = fdt->fds[fd];
354
355 /* check dfs_file valid or not */
356 if ((d == NULL) || (d->magic != DFS_FD_MAGIC))
357 {
358 dfs_file_unlock();
359 return NULL;
360 }
361
362 dfs_file_unlock();
363
364 return d;
365 }
366
fd_get(int fd)367 struct dfs_file *fd_get(int fd)
368 {
369 struct dfs_fdtable *fdt;
370
371 fdt = dfs_fdtable_get();
372 return fdt_fd_get(fdt, fd);
373 }
374
375 /**
376 * @ingroup group_fs_file_descriptor
377 *
378 * @brief This function will release the file descriptor.
379 *
380 * This function releases a file descriptor slot in the file descriptor table, decrements reference
381 * counts, and cleans up resources associated with the `dfs_file` and `dfs_vnode` structures when applicable.
382 *
383 */
fdt_fd_release(struct dfs_fdtable * fdt,int fd)384 void fdt_fd_release(struct dfs_fdtable* fdt, int fd)
385 {
386 struct dfs_file *fd_slot = NULL;
387
388 RT_ASSERT(fdt != NULL);
389
390 dfs_file_lock();
391
392 if ((fd < 0) || (fd >= fdt->maxfd))
393 {
394 dfs_file_unlock();
395 return;
396 }
397
398 fd_slot = fdt->fds[fd];
399 if (fd_slot == NULL)
400 {
401 dfs_file_unlock();
402 return;
403 }
404 fdt->fds[fd] = NULL;
405
406 /* check fd */
407 RT_ASSERT(fd_slot->magic == DFS_FD_MAGIC);
408
409 fd_slot->ref_count--;
410
411 /* clear this fd entry */
412 if (fd_slot->ref_count == 0)
413 {
414 struct dfs_vnode *vnode = fd_slot->vnode;
415 if (vnode)
416 {
417 vnode->ref_count--;
418 if(vnode->ref_count == 0)
419 {
420 rt_free(vnode);
421 fd_slot->vnode = RT_NULL;
422 }
423 }
424 rt_free(fd_slot);
425 }
426 dfs_file_unlock();
427 }
428
fd_release(int fd)429 void fd_release(int fd)
430 {
431 struct dfs_fdtable *fdt;
432
433 fdt = dfs_fdtable_get();
434 fdt_fd_release(fdt, fd);
435 }
436
437 /**
438 * @brief Duplicates a file descriptor.
439 *
440 * This function duplicates an existing file descriptor (`oldfd`) and returns
441 * a new file descriptor that refers to the same underlying file object.
442 *
443 * @param oldfd The file descriptor to duplicate. It must be a valid file
444 * descriptor within the range of allocated descriptors.
445 *
446 * @return The new file descriptor if successful, or a negative value
447 * (e.g., -1) if an error occurs.
448 *
449 * @see sys_dup2()
450 */
sys_dup(int oldfd)451 rt_err_t sys_dup(int oldfd)
452 {
453 int newfd = -1;
454 struct dfs_fdtable *fdt = NULL;
455
456 dfs_file_lock();
457 /* check old fd */
458 fdt = dfs_fdtable_get();
459 if ((oldfd < 0) || (oldfd >= fdt->maxfd))
460 {
461 goto exit;
462 }
463 if (!fdt->fds[oldfd])
464 {
465 goto exit;
466 }
467 /* get a new fd */
468 newfd = fd_slot_alloc(fdt, DFS_STDIO_OFFSET);
469 if (newfd >= 0)
470 {
471 fdt->fds[newfd] = fdt->fds[oldfd];
472 /* inc ref_count */
473 fdt->fds[newfd]->ref_count++;
474 }
475 exit:
476 dfs_file_unlock();
477 return newfd;
478 }
479
480 #endif /* DFS_USING_POSIX */
481
482 /**
483 * @ingroup group_fs_file_descriptor
484 *
485 * This function will return whether this file has been opend.
486 *
487 * @param pathname the file path name.
488 *
489 * @return 0 on file has been open successfully, -1 on open failed.
490 */
fd_is_open(const char * pathname)491 int fd_is_open(const char *pathname)
492 {
493 char *fullpath;
494 unsigned int index;
495 struct dfs_filesystem *fs;
496 struct dfs_file *fd;
497 struct dfs_fdtable *fdt;
498
499 fdt = dfs_fdtable_get();
500 fullpath = dfs_normalize_path(NULL, pathname);
501 if (fullpath != NULL)
502 {
503 char *mountpath;
504 fs = dfs_filesystem_lookup(fullpath);
505 if (fs == NULL)
506 {
507 /* can't find mounted file system */
508 rt_free(fullpath);
509
510 return -1;
511 }
512
513 /* get file path name under mounted file system */
514 if (fs->path[0] == '/' && fs->path[1] == '\0')
515 mountpath = fullpath;
516 else
517 mountpath = fullpath + strlen(fs->path);
518
519 dfs_lock();
520
521 for (index = 0; index < fdt->maxfd; index++)
522 {
523 fd = fdt->fds[index];
524 if (fd == NULL || fd->vnode->fops == NULL || fd->vnode->path == NULL) continue;
525
526 if (fd->vnode->fs == fs && strcmp(fd->vnode->path, mountpath) == 0)
527 {
528 /* found file in file descriptor table */
529 rt_free(fullpath);
530 dfs_unlock();
531
532 return 0;
533 }
534 }
535 dfs_unlock();
536
537 rt_free(fullpath);
538 }
539
540 return -1;
541 }
542
543 /**
544 * @brief Duplicates a file descriptor to a specified file descriptor.
545 *
546 * This function duplicates an existing file descriptor (`oldfd`) and assigns it
547 * to the specified file descriptor (`newfd`).
548 *
549 * @param oldfd The file descriptor to duplicate. It must be a valid and open file
550 * descriptor within the range of allocated descriptors.
551 * @param newfd The target file descriptor. If `newfd` is already in use, it will
552 * be closed before duplication. If `newfd` exceeds the current file
553 * descriptor table size, the table will be expanded to accommodate it.
554 *
555 * @return The value of `newfd` on success, or a negative value (e.g., -1) if an
556 * error occurs.
557 *
558 * @see sys_dup()
559 */
sys_dup2(int oldfd,int newfd)560 rt_err_t sys_dup2(int oldfd, int newfd)
561 {
562 struct dfs_fdtable *fdt = NULL;
563 int ret = 0;
564 int retfd = -1;
565
566 dfs_file_lock();
567 /* check old fd */
568 fdt = dfs_fdtable_get();
569 if ((oldfd < 0) || (oldfd >= fdt->maxfd))
570 {
571 goto exit;
572 }
573 if (!fdt->fds[oldfd])
574 {
575 goto exit;
576 }
577 if (newfd < 0)
578 {
579 goto exit;
580 }
581 if (newfd >= fdt->maxfd)
582 {
583 newfd = fd_slot_expand(fdt, newfd);
584 if (newfd < 0)
585 {
586 goto exit;
587 }
588 }
589 if (fdt->fds[newfd] == fdt->fds[oldfd])
590 {
591 /* ok, return newfd */
592 retfd = newfd;
593 goto exit;
594 }
595
596 if (fdt->fds[newfd])
597 {
598 ret = dfs_file_close(fdt->fds[newfd]);
599 if (ret < 0)
600 {
601 goto exit;
602 }
603 fd_release(newfd);
604 }
605
606 fdt->fds[newfd] = fdt->fds[oldfd];
607 /* inc ref_count */
608 fdt->fds[newfd]->ref_count++;
609 retfd = newfd;
610 exit:
611 dfs_file_unlock();
612 return retfd;
613 }
614
fd_get_fd_index_form_fdt(struct dfs_fdtable * fdt,struct dfs_file * file)615 static int fd_get_fd_index_form_fdt(struct dfs_fdtable *fdt, struct dfs_file *file)
616 {
617 int fd = -1;
618
619 if (file == RT_NULL)
620 {
621 return -1;
622 }
623
624 dfs_file_lock();
625
626 for(int index = 0; index < (int)fdt->maxfd; index++)
627 {
628 if(fdt->fds[index] == file)
629 {
630 fd = index;
631 break;
632 }
633 }
634
635 dfs_file_unlock();
636
637 return fd;
638 }
639
640 /**
641 * @brief get fd (index) by dfs file object.
642 *
643 */
fd_get_fd_index(struct dfs_file * file)644 int fd_get_fd_index(struct dfs_file *file)
645 {
646 struct dfs_fdtable *fdt;
647
648 fdt = dfs_fdtable_get();
649 return fd_get_fd_index_form_fdt(fdt, file);
650 }
651
652 /**
653 * @brief Associates a file descriptor with a file object.
654 *
655 * This function associates a given file descriptor (`fd`) with a specified
656 * file object (`file`) in the file descriptor table (`fdt`).
657 *
658 * @param fdt The file descriptor table to operate on. It must be a valid
659 * and initialized `dfs_fdtable` structure.
660 * @param fd The file descriptor to associate. It must be within the range
661 * of allocated file descriptors and currently unoccupied.
662 * @param file The file object to associate with the file descriptor. It must
663 * be a valid and initialized `dfs_file` structure.
664 *
665 * @return The value of `fd` on success, or -1 if an error occurs.
666 */
fd_associate(struct dfs_fdtable * fdt,int fd,struct dfs_file * file)667 int fd_associate(struct dfs_fdtable *fdt, int fd, struct dfs_file *file)
668 {
669 int retfd = -1;
670
671 if (!file)
672 {
673 return retfd;
674 }
675 if (!fdt)
676 {
677 return retfd;
678 }
679
680 dfs_file_lock();
681 /* check old fd */
682 if ((fd < 0) || (fd >= fdt->maxfd))
683 {
684 goto exit;
685 }
686
687 if (fdt->fds[fd])
688 {
689 goto exit;
690 }
691 /* inc ref_count */
692 file->ref_count++;
693 fdt->fds[fd] = file;
694 retfd = fd;
695 exit:
696 dfs_file_unlock();
697 return retfd;
698 }
699
700 /**
701 * @brief initialize a dfs file object.
702 *
703 */
fd_init(struct dfs_file * fd)704 void fd_init(struct dfs_file *fd)
705 {
706 if (fd)
707 {
708 fd->magic = DFS_FD_MAGIC;
709 fd->ref_count = 1;
710 fd->pos = 0;
711 fd->vnode = NULL;
712 fd->data = NULL;
713 }
714 }
715
716 /**
717 * this function will return a sub-path name under directory.
718 *
719 * @param directory the parent directory.
720 * @param filename the filename.
721 *
722 * @return the subdir pointer in filename
723 */
dfs_subdir(const char * directory,const char * filename)724 const char *dfs_subdir(const char *directory, const char *filename)
725 {
726 const char *dir;
727
728 if (strlen(directory) == strlen(filename)) /* it's a same path */
729 return NULL;
730
731 dir = filename + strlen(directory);
732 if ((*dir != '/') && (dir != filename))
733 {
734 dir --;
735 }
736
737 return dir;
738 }
739 RTM_EXPORT(dfs_subdir);
740
741 /**
742 * this function will normalize a path according to specified parent directory
743 * and file name.
744 *
745 * @param directory the parent path
746 * @param filename the file name
747 *
748 * @return the built full file path (absolute path)
749 */
dfs_normalize_path(const char * directory,const char * filename)750 char *dfs_normalize_path(const char *directory, const char *filename)
751 {
752 char *fullpath;
753 char *dst0, *dst, *src;
754
755 /* check parameters */
756 RT_ASSERT(filename != NULL);
757
758 #ifdef DFS_USING_WORKDIR
759 if (directory == NULL) /* shall use working directory */
760 {
761 #ifdef RT_USING_SMART
762 directory = lwp_getcwd();
763 #else
764 directory = &working_directory[0];
765 #endif
766 }
767 #else
768 if ((directory == NULL) && (filename[0] != '/'))
769 {
770 rt_kprintf(NO_WORKING_DIR);
771
772 return NULL;
773 }
774 #endif
775
776 if (filename[0] != '/') /* it's a absolute path, use it directly */
777 {
778 fullpath = (char *)rt_malloc(strlen(directory) + strlen(filename) + 2);
779
780 if (fullpath == NULL)
781 return NULL;
782
783 /* join path and file name */
784 rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
785 "%s/%s", directory, filename);
786 }
787 else
788 {
789 fullpath = rt_strdup(filename); /* copy string */
790
791 if (fullpath == NULL)
792 return NULL;
793 }
794
795 src = fullpath;
796 dst = fullpath;
797
798 dst0 = dst;
799 while (1)
800 {
801 char c = *src;
802
803 if (c == '.')
804 {
805 if (!src[1]) src++; /* '.' and ends */
806 else if (src[1] == '/')
807 {
808 /* './' case */
809 src += 2;
810
811 while ((*src == '/') && (*src != '\0'))
812 src++;
813 continue;
814 }
815 else if (src[1] == '.')
816 {
817 if (!src[2])
818 {
819 /* '..' and ends case */
820 src += 2;
821 goto up_one;
822 }
823 else if (src[2] == '/')
824 {
825 /* '../' case */
826 src += 3;
827
828 while ((*src == '/') && (*src != '\0'))
829 src++;
830 goto up_one;
831 }
832 }
833 }
834
835 /* copy up the next '/' and erase all '/' */
836 while ((c = *src++) != '\0' && c != '/')
837 *dst++ = c;
838
839 if (c == '/')
840 {
841 *dst++ = '/';
842 while (c == '/')
843 c = *src++;
844
845 src--;
846 }
847 else if (!c)
848 break;
849
850 continue;
851
852 up_one:
853 /* keep the topmost root directory */
854 if (dst - dst0 != 1 || dst[-1] != '/')
855 {
856 dst--;
857
858 if (dst < dst0)
859 {
860 rt_free(fullpath);
861 return NULL;
862 }
863 }
864 while (dst0 < dst && dst[-1] != '/')
865 dst--;
866 }
867
868 *dst = '\0';
869
870 /* remove '/' in the end of path if exist */
871 dst--;
872 if (dst > fullpath && (*dst == '/'))
873 *dst = '\0';
874
875 /* final check fullpath is not empty, for the special path of lwext "/.." */
876 if ('\0' == fullpath[0])
877 {
878 fullpath[0] = '/';
879 fullpath[1] = '\0';
880 }
881
882 return fullpath;
883 }
884 RTM_EXPORT(dfs_normalize_path);
885
886 /**
887 * This function will get the file descriptor table of current process.
888 */
dfs_fdtable_get(void)889 struct dfs_fdtable *dfs_fdtable_get(void)
890 {
891 struct dfs_fdtable *fdt;
892 #ifdef RT_USING_SMART
893 struct rt_lwp *lwp;
894
895 lwp = (struct rt_lwp *)rt_thread_self()->lwp;
896 if (lwp)
897 fdt = &lwp->fdt;
898 else
899 fdt = &_fdtab;
900 #else
901 fdt = &_fdtab;
902 #endif
903
904 return fdt;
905 }
906
907 #ifdef RT_USING_SMART
dfs_fdtable_get_pid(int pid)908 struct dfs_fdtable *dfs_fdtable_get_pid(int pid)
909 {
910 struct rt_lwp *lwp = RT_NULL;
911 struct dfs_fdtable *fdt = RT_NULL;
912
913 lwp_pid_lock_take();
914 lwp = lwp_from_pid_locked(pid);
915 if (lwp)
916 {
917 fdt = &lwp->fdt;
918 }
919 lwp_pid_lock_release();
920
921 return fdt;
922 }
923 #endif
924
dfs_fdtable_get_global(void)925 struct dfs_fdtable *dfs_fdtable_get_global(void)
926 {
927 return &_fdtab;
928 }
929
930 #ifdef RT_USING_FINSH
list_fd(void)931 int list_fd(void)
932 {
933 int index;
934 struct dfs_fdtable *fd_table;
935
936 fd_table = dfs_fdtable_get();
937 if (!fd_table) return -1;
938
939 dfs_lock();
940
941 rt_kprintf("fd type ref magic path\n");
942 rt_kprintf("-- ------ --- ----- ------\n");
943 for (index = 0; index < (int)fd_table->maxfd; index++)
944 {
945 struct dfs_file *fd = fd_table->fds[index];
946
947 if (fd && fd->vnode->fops)
948 {
949 rt_kprintf("%2d ", index);
950 if (fd->vnode->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir");
951 else if (fd->vnode->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
952 else if (fd->vnode->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket");
953 else if (fd->vnode->type == FT_USER) rt_kprintf("%-7.7s ", "user");
954 else if (fd->vnode->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device");
955 else rt_kprintf("%-8.8s ", "unknown");
956 rt_kprintf("%3d ", fd->vnode->ref_count);
957 rt_kprintf("%04x ", fd->magic);
958 if (fd->vnode->path)
959 {
960 rt_kprintf("%s\n", fd->vnode->path);
961 }
962 else
963 {
964 rt_kprintf("\n");
965 }
966 }
967 }
968 dfs_unlock();
969
970 return 0;
971 }
972
973 #ifdef RT_USING_SMART
lsofp(int pid)974 static int lsofp(int pid)
975 {
976 int index;
977 struct dfs_fdtable *fd_table = RT_NULL;
978
979 if (pid == (-1))
980 {
981 fd_table = dfs_fdtable_get();
982 if (!fd_table) return -1;
983 }
984 else
985 {
986 fd_table = dfs_fdtable_get_pid(pid);
987 if (!fd_table)
988 {
989 rt_kprintf("PID %s is not a applet(lwp)\n", pid);
990 return -1;
991 }
992 }
993
994 rt_kprintf("--- -- ------ ------ ----- ---------- ---------- ---------- ------\n");
995
996 rt_enter_critical();
997 for (index = 0; index < (int)fd_table->maxfd; index++)
998 {
999 struct dfs_file *fd = fd_table->fds[index];
1000
1001 if (fd && fd->vnode->fops)
1002 {
1003 if(pid == (-1))
1004 {
1005 rt_kprintf(" K ");
1006 }
1007 else
1008 {
1009 rt_kprintf("%3d ", pid);
1010 }
1011
1012 rt_kprintf("%2d ", index);
1013 if (fd->vnode->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir");
1014 else if (fd->vnode->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
1015 else if (fd->vnode->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket");
1016 else if (fd->vnode->type == FT_USER) rt_kprintf("%-7.7s ", "user");
1017 else if (fd->vnode->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device");
1018 else rt_kprintf("%-8.8s ", "unknown");
1019 rt_kprintf("%6d ", fd->vnode->ref_count);
1020 rt_kprintf("%04x 0x%.8x ", fd->magic, (int)(size_t)fd->vnode);
1021
1022 if(fd->vnode == RT_NULL)
1023 {
1024 rt_kprintf("0x%.8x 0x%.8x ", (int)0x00000000, (int)(size_t)fd);
1025 }
1026 else
1027 {
1028 rt_kprintf("0x%.8x 0x%.8x ", (int)(size_t)(fd->vnode->data), (int)(size_t)fd);
1029 }
1030
1031 if (fd->vnode->path)
1032 {
1033 rt_kprintf("%s \n", fd->vnode->path);
1034 }
1035 else
1036 {
1037 rt_kprintf("\n");
1038 }
1039 }
1040 }
1041 rt_exit_critical();
1042
1043 return 0;
1044 }
1045
lsof(int argc,char * argv[])1046 int lsof(int argc, char *argv[])
1047 {
1048 rt_kprintf("PID fd type fd-ref magic vnode vnode/data addr path \n");
1049
1050 if (argc == 1)
1051 {
1052 struct rt_list_node *node, *list;
1053 struct lwp_avl_struct *pids = lwp_get_pid_ary();
1054
1055 lsofp(-1);
1056
1057 for (int index = 0; index < RT_LWP_MAX_NR; index++)
1058 {
1059 struct rt_lwp *lwp = (struct rt_lwp *)pids[index].data;
1060
1061 if (lwp)
1062 {
1063 list = &lwp->t_grp;
1064 for (node = list->next; node != list; node = node->next)
1065 {
1066 lsofp(lwp_to_pid(lwp));
1067 }
1068 }
1069 }
1070 }
1071 else if (argc == 3)
1072 {
1073 if (argv[1][0] == '-' && argv[1][1] == 'p')
1074 {
1075 int pid = atoi(argv[2]);
1076 lsofp(pid);
1077 }
1078 }
1079
1080 return 0;
1081 }
1082 MSH_CMD_EXPORT(lsof, list open files);
1083 #endif /* RT_USING_SMART */
1084
1085 #endif
1086 /**@}*/
1087
1088