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