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