xref: /nrf52832-nimble/rt-thread/components/dfs/src/dfs.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2005-02-22     Bernard      The first version.
9  * 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_LWP
18 #include <lwp.h>
19 #endif
20 
21 #if defined(RT_USING_DFS_DEVFS) && defined(RT_USING_POSIX)
22 #include <libc.h>
23 #endif
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 
32 #ifdef DFS_USING_WORKDIR
33 char working_directory[DFS_PATH_MAX] = {"/"};
34 #endif
35 
36 static struct dfs_fdtable _fdtab;
37 static int  fd_alloc(struct dfs_fdtable *fdt, int startfd);
38 
39 /**
40  * @addtogroup DFS
41  */
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     /* clear filesystem operations table */
59     memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
60     /* clear filesystem table */
61     memset(filesystem_table, 0, sizeof(filesystem_table));
62     /* clean fd table */
63     memset(&_fdtab, 0, sizeof(_fdtab));
64 
65     /* create device filesystem lock */
66     rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
67 
68 #ifdef DFS_USING_WORKDIR
69     /* set current working directory */
70     memset(working_directory, 0, sizeof(working_directory));
71     working_directory[0] = '/';
72 #endif
73 
74 #ifdef RT_USING_DFS_DEVFS
75     {
76         extern int devfs_init(void);
77 
78         /* if enable devfs, initialize and mount it as soon as possible */
79         devfs_init();
80 
81         dfs_mount(NULL, "/dev", "devfs", 0, 0);
82     }
83 #endif
84 
85     init_ok = RT_TRUE;
86 
87     return 0;
88 }
89 INIT_PREV_EXPORT(dfs_init);
90 
91 /**
92  * this function will lock device file system.
93  *
94  * @note please don't invoke it on ISR.
95  */
dfs_lock(void)96 void dfs_lock(void)
97 {
98     rt_err_t result = -RT_EBUSY;
99 
100     while (result == -RT_EBUSY)
101     {
102         result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
103     }
104 
105     if (result != RT_EOK)
106     {
107         RT_ASSERT(0);
108     }
109 }
110 
111 /**
112  * this function will lock device file system.
113  *
114  * @note please don't invoke it on ISR.
115  */
dfs_unlock(void)116 void dfs_unlock(void)
117 {
118     rt_mutex_release(&fslock);
119 }
120 
fd_alloc(struct dfs_fdtable * fdt,int startfd)121 static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
122 {
123     int idx;
124 
125     /* find an empty fd entry */
126     for (idx = startfd; idx < fdt->maxfd; idx++)
127     {
128         if (fdt->fds[idx] == RT_NULL)
129             break;
130         if (fdt->fds[idx]->ref_count == 0)
131             break;
132     }
133 
134     /* allocate a larger FD container */
135     if (idx == fdt->maxfd && fdt->maxfd < DFS_FD_MAX)
136     {
137         int cnt, index;
138         struct dfs_fd **fds;
139 
140         /* increase the number of FD with 4 step length */
141         cnt = fdt->maxfd + 4;
142         cnt = cnt > DFS_FD_MAX? DFS_FD_MAX : cnt;
143 
144         fds = rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *));
145         if (fds == NULL) goto __exit; /* return fdt->maxfd */
146 
147         /* clean the new allocated fds */
148         for (index = fdt->maxfd; index < cnt; index ++)
149         {
150             fds[index] = NULL;
151         }
152 
153         fdt->fds   = fds;
154         fdt->maxfd = cnt;
155     }
156 
157     /* allocate  'struct dfs_fd' */
158     if (idx < fdt->maxfd &&fdt->fds[idx] == RT_NULL)
159     {
160         fdt->fds[idx] = rt_calloc(1, sizeof(struct dfs_fd));
161         if (fdt->fds[idx] == RT_NULL)
162             idx = fdt->maxfd;
163     }
164 
165 __exit:
166     return idx;
167 }
168 
169 /**
170  * @ingroup Fd
171  * This function will allocate a file descriptor.
172  *
173  * @return -1 on failed or the allocated file descriptor.
174  */
fd_new(void)175 int fd_new(void)
176 {
177     struct dfs_fd *d;
178     int idx;
179     struct dfs_fdtable *fdt;
180 
181     fdt = dfs_fdtable_get();
182     /* lock filesystem */
183     dfs_lock();
184 
185     /* find an empty fd entry */
186     idx = fd_alloc(fdt, 0);
187 
188     /* can't find an empty fd entry */
189     if (idx == fdt->maxfd)
190     {
191         idx = -(1 + DFS_FD_OFFSET);
192         LOG_E( "DFS fd new is failed! Could not found an empty fd entry.");
193         goto __result;
194     }
195 
196     d = fdt->fds[idx];
197     d->ref_count = 1;
198     d->magic = DFS_FD_MAGIC;
199 
200 __result:
201     dfs_unlock();
202     return idx + DFS_FD_OFFSET;
203 }
204 
205 /**
206  * @ingroup Fd
207  *
208  * This function will return a file descriptor structure according to file
209  * descriptor.
210  *
211  * @return NULL on on this file descriptor or the file descriptor structure
212  * pointer.
213  */
fd_get(int fd)214 struct dfs_fd *fd_get(int fd)
215 {
216     struct dfs_fd *d;
217     struct dfs_fdtable *fdt;
218 
219 #if defined(RT_USING_DFS_DEVFS) && defined(RT_USING_POSIX)
220     if ((0 <= fd) && (fd <= 2))
221         fd = libc_stdio_get_console();
222 #endif
223 
224     fdt = dfs_fdtable_get();
225     fd = fd - DFS_FD_OFFSET;
226     if (fd < 0 || fd >= fdt->maxfd)
227         return NULL;
228 
229     dfs_lock();
230     d = fdt->fds[fd];
231 
232     /* check dfs_fd valid or not */
233     if ((d == NULL) || (d->magic != DFS_FD_MAGIC))
234     {
235         dfs_unlock();
236         return NULL;
237     }
238 
239     /* increase the reference count */
240     d->ref_count ++;
241     dfs_unlock();
242 
243     return d;
244 }
245 
246 /**
247  * @ingroup Fd
248  *
249  * This function will put the file descriptor.
250  */
fd_put(struct dfs_fd * fd)251 void fd_put(struct dfs_fd *fd)
252 {
253     RT_ASSERT(fd != NULL);
254 
255     dfs_lock();
256 
257     fd->ref_count --;
258 
259     /* clear this fd entry */
260     if (fd->ref_count == 0)
261     {
262         int index;
263         struct dfs_fdtable *fdt;
264 
265         fdt = dfs_fdtable_get();
266         for (index = 0; index < fdt->maxfd; index ++)
267         {
268             if (fdt->fds[index] == fd)
269             {
270                 rt_free(fd);
271                 fdt->fds[index] = 0;
272                 break;
273             }
274         }
275     }
276     dfs_unlock();
277 }
278 
279 /**
280  * @ingroup Fd
281  *
282  * This function will return whether this file has been opend.
283  *
284  * @param pathname the file path name.
285  *
286  * @return 0 on file has been open successfully, -1 on open failed.
287  */
fd_is_open(const char * pathname)288 int fd_is_open(const char *pathname)
289 {
290     char *fullpath;
291     unsigned int index;
292     struct dfs_filesystem *fs;
293     struct dfs_fd *fd;
294     struct dfs_fdtable *fdt;
295 
296     fdt = dfs_fdtable_get();
297     fullpath = dfs_normalize_path(NULL, pathname);
298     if (fullpath != NULL)
299     {
300         char *mountpath;
301         fs = dfs_filesystem_lookup(fullpath);
302         if (fs == NULL)
303         {
304             /* can't find mounted file system */
305             rt_free(fullpath);
306 
307             return -1;
308         }
309 
310         /* get file path name under mounted file system */
311         if (fs->path[0] == '/' && fs->path[1] == '\0')
312             mountpath = fullpath;
313         else
314             mountpath = fullpath + strlen(fs->path);
315 
316         dfs_lock();
317 
318         for (index = 0; index < fdt->maxfd; index++)
319         {
320             fd = fdt->fds[index];
321             if (fd == NULL || fd->fops == NULL || fd->path == NULL) continue;
322 
323             if (fd->fs == fs && strcmp(fd->path, mountpath) == 0)
324             {
325                 /* found file in file descriptor table */
326                 rt_free(fullpath);
327                 dfs_unlock();
328 
329                 return 0;
330             }
331         }
332         dfs_unlock();
333 
334         rt_free(fullpath);
335     }
336 
337     return -1;
338 }
339 
340 /**
341  * this function will return a sub-path name under directory.
342  *
343  * @param directory the parent directory.
344  * @param filename the filename.
345  *
346  * @return the subdir pointer in filename
347  */
dfs_subdir(const char * directory,const char * filename)348 const char *dfs_subdir(const char *directory, const char *filename)
349 {
350     const char *dir;
351 
352     if (strlen(directory) == strlen(filename)) /* it's a same path */
353         return NULL;
354 
355     dir = filename + strlen(directory);
356     if ((*dir != '/') && (dir != filename))
357     {
358         dir --;
359     }
360 
361     return dir;
362 }
363 RTM_EXPORT(dfs_subdir);
364 
365 /**
366  * this function will normalize a path according to specified parent directory
367  * and file name.
368  *
369  * @param directory the parent path
370  * @param filename the file name
371  *
372  * @return the built full file path (absolute path)
373  */
dfs_normalize_path(const char * directory,const char * filename)374 char *dfs_normalize_path(const char *directory, const char *filename)
375 {
376     char *fullpath;
377     char *dst0, *dst, *src;
378 
379     /* check parameters */
380     RT_ASSERT(filename != NULL);
381 
382 #ifdef DFS_USING_WORKDIR
383     if (directory == NULL) /* shall use working directory */
384         directory = &working_directory[0];
385 #else
386     if ((directory == NULL) && (filename[0] != '/'))
387     {
388         rt_kprintf(NO_WORKING_DIR);
389 
390         return NULL;
391     }
392 #endif
393 
394     if (filename[0] != '/') /* it's a absolute path, use it directly */
395     {
396         fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2);
397 
398         if (fullpath == NULL)
399             return NULL;
400 
401         /* join path and file name */
402         rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
403             "%s/%s", directory, filename);
404     }
405     else
406     {
407         fullpath = rt_strdup(filename); /* copy string */
408 
409         if (fullpath == NULL)
410             return NULL;
411     }
412 
413     src = fullpath;
414     dst = fullpath;
415 
416     dst0 = dst;
417     while (1)
418     {
419         char c = *src;
420 
421         if (c == '.')
422         {
423             if (!src[1]) src ++; /* '.' and ends */
424             else if (src[1] == '/')
425             {
426                 /* './' case */
427                 src += 2;
428 
429                 while ((*src == '/') && (*src != '\0'))
430                     src ++;
431                 continue;
432             }
433             else if (src[1] == '.')
434             {
435                 if (!src[2])
436                 {
437                     /* '..' and ends case */
438                     src += 2;
439                     goto up_one;
440                 }
441                 else if (src[2] == '/')
442                 {
443                     /* '../' case */
444                     src += 3;
445 
446                     while ((*src == '/') && (*src != '\0'))
447                         src ++;
448                     goto up_one;
449                 }
450             }
451         }
452 
453         /* copy up the next '/' and erase all '/' */
454         while ((c = *src++) != '\0' && c != '/')
455             *dst ++ = c;
456 
457         if (c == '/')
458         {
459             *dst ++ = '/';
460             while (c == '/')
461                 c = *src++;
462 
463             src --;
464         }
465         else if (!c)
466             break;
467 
468         continue;
469 
470 up_one:
471         dst --;
472         if (dst < dst0)
473         {
474             rt_free(fullpath);
475             return NULL;
476         }
477         while (dst0 < dst && dst[-1] != '/')
478             dst --;
479     }
480 
481     *dst = '\0';
482 
483     /* remove '/' in the end of path if exist */
484     dst --;
485     if ((dst != fullpath) && (*dst == '/'))
486         *dst = '\0';
487 
488     /* final check fullpath is not empty, for the special path of lwext "/.." */
489     if ('\0' == fullpath[0])
490     {
491         fullpath[0] = '/';
492         fullpath[1] = '\0';
493     }
494 
495     return fullpath;
496 }
497 RTM_EXPORT(dfs_normalize_path);
498 
499 /**
500  * This function will get the file descriptor table of current process.
501  */
dfs_fdtable_get(void)502 struct dfs_fdtable* dfs_fdtable_get(void)
503 {
504     struct dfs_fdtable *fdt;
505 #ifdef RT_USING_LWP
506     struct rt_lwp *lwp;
507 
508     lwp = (struct rt_lwp *)rt_thread_self()->lwp;
509     if (lwp)
510         fdt = &lwp->fdt;
511     else
512         fdt = &_fdtab;
513 #else
514     fdt = &_fdtab;
515 #endif
516 
517     return fdt;
518 }
519 
520 #ifdef RT_USING_FINSH
521 #include <finsh.h>
list_fd(void)522 int list_fd(void)
523 {
524     int index;
525     struct dfs_fdtable *fd_table;
526 
527     fd_table = dfs_fdtable_get();
528     if (!fd_table) return -1;
529 
530     rt_enter_critical();
531 
532     rt_kprintf("fd type    ref magic  path\n");
533     rt_kprintf("-- ------  --- ----- ------\n");
534     for (index = 0; index < fd_table->maxfd; index ++)
535     {
536         struct dfs_fd *fd = fd_table->fds[index];
537 
538         if (fd && fd->fops)
539         {
540             rt_kprintf("%2d ", index);
541             if (fd->type == FT_DIRECTORY)    rt_kprintf("%-7.7s ", "dir");
542             else if (fd->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
543             else if (fd->type == FT_SOCKET)  rt_kprintf("%-7.7s ", "socket");
544             else if (fd->type == FT_USER)    rt_kprintf("%-7.7s ", "user");
545             else rt_kprintf("%-8.8s ", "unknown");
546             rt_kprintf("%3d ", fd->ref_count);
547             rt_kprintf("%04x  ", fd->magic);
548             if (fd->path)
549             {
550                 rt_kprintf("%s\n", fd->path);
551             }
552             else
553             {
554                 rt_kprintf("\n");
555             }
556         }
557     }
558     rt_exit_critical();
559 
560     return 0;
561 }
562 MSH_CMD_EXPORT(list_fd, list file descriptor);
563 #endif
564 /*@}*/
565 
566