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 * 2010-06-30 Bernard Optimize for RT-Thread RTOS
10 * 2011-03-12 Bernard fix the filesystem lookup issue.
11 * 2017-11-30 Bernard fix the filesystem_operation_table issue.
12 * 2017-12-05 Bernard fix the fs type search issue in mkfs.
13 */
14
15 #include <dfs_fs.h>
16 #include <dfs_file.h>
17 #include "dfs_private.h"
18
19 /**
20 * @addtogroup group_fs_api
21 * @{
22 */
23
24 /**
25 * this function will register a file system instance to device file system.
26 *
27 * @param ops the file system instance to be registered.
28 *
29 * @return 0 on successful, -1 on failed.
30 */
dfs_register(const struct dfs_filesystem_ops * ops)31 int dfs_register(const struct dfs_filesystem_ops *ops)
32 {
33 int ret = RT_EOK;
34 const struct dfs_filesystem_ops **empty = NULL;
35 const struct dfs_filesystem_ops **iter;
36
37 /* lock filesystem */
38 dfs_lock();
39 /* check if this filesystem was already registered */
40 for (iter = &filesystem_operation_table[0];
41 iter < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; iter ++)
42 {
43 /* find out an empty filesystem type entry */
44 if (*iter == NULL)
45 (empty == NULL) ? (empty = iter) : 0;
46 else if (strcmp((*iter)->name, ops->name) == 0)
47 {
48 rt_set_errno(-EEXIST);
49 ret = -1;
50 break;
51 }
52 }
53
54 /* save the filesystem's operations */
55 if (empty == NULL)
56 {
57 rt_set_errno(-ENOSPC);
58 LOG_E("There is no space to register this file system (%s).", ops->name);
59 ret = -1;
60 }
61 else if (ret == RT_EOK)
62 {
63 *empty = ops;
64 }
65
66 dfs_unlock();
67 return ret;
68 }
69
70 /**
71 * this function will return the file system mounted on specified path.
72 *
73 * @param path the specified path string.
74 *
75 * @return the found file system or NULL if no file system mounted on
76 * specified path
77 */
dfs_filesystem_lookup(const char * path)78 struct dfs_filesystem *dfs_filesystem_lookup(const char *path)
79 {
80 struct dfs_filesystem *iter;
81 struct dfs_filesystem *fs = NULL;
82 uint32_t fspath, prefixlen;
83
84 prefixlen = 0;
85
86 RT_ASSERT(path);
87
88 /* lock filesystem */
89 dfs_lock();
90
91 /* lookup it in the filesystem table */
92 for (iter = &filesystem_table[0];
93 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
94 {
95 if ((iter->path == NULL) || (iter->ops == NULL))
96 continue;
97
98 fspath = strlen(iter->path);
99 if ((fspath < prefixlen)
100 || (strncmp(iter->path, path, fspath) != 0))
101 continue;
102
103 /* check next path separator */
104 if (fspath > 1 && (strlen(path) > fspath) && (path[fspath] != '/'))
105 continue;
106
107 fs = iter;
108 prefixlen = fspath;
109 }
110
111 dfs_unlock();
112
113 return fs;
114 }
115
116 /**
117 * this function will return the mounted path for specified device.
118 *
119 * @param device the device object which is mounted.
120 *
121 * @return the mounted path or NULL if none device mounted.
122 */
dfs_filesystem_get_mounted_path(struct rt_device * device)123 const char *dfs_filesystem_get_mounted_path(struct rt_device *device)
124 {
125 const char *path = NULL;
126 struct dfs_filesystem *iter;
127
128 dfs_lock();
129 for (iter = &filesystem_table[0];
130 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
131 {
132 /* find the mounted device */
133 if (iter->ops == NULL) continue;
134 else if (iter->dev_id == device)
135 {
136 path = iter->path;
137 break;
138 }
139 }
140
141 /* release filesystem_table lock */
142 dfs_unlock();
143
144 return path;
145 }
146
147 /**
148 * this function will fetch the partition table on specified buffer.
149 *
150 * @param part the returned partition structure.
151 * @param buf the buffer contains partition table.
152 * @param pindex the index of partition table to fetch.
153 *
154 * @return RT_EOK on successful or -RT_ERROR on failed.
155 */
dfs_filesystem_get_partition(struct dfs_partition * part,uint8_t * buf,uint32_t pindex)156 int dfs_filesystem_get_partition(struct dfs_partition *part,
157 uint8_t *buf,
158 uint32_t pindex)
159 {
160 #define DPT_ADDRESS 0x1be /* device partition offset in Boot Sector */
161 #define DPT_ITEM_SIZE 16 /* partition item size */
162
163 uint8_t *dpt;
164 uint8_t type;
165
166 RT_ASSERT(part != NULL);
167 RT_ASSERT(buf != NULL);
168
169 dpt = buf + DPT_ADDRESS + pindex * DPT_ITEM_SIZE;
170
171 /* check if it is a valid partition table */
172 if ((*dpt != 0x80) && (*dpt != 0x00))
173 return -EIO;
174
175 /* get partition type */
176 type = *(dpt + 4);
177 if (type == 0)
178 return -EIO;
179
180 /* set partition information
181 * size is the number of 512-Byte */
182 part->type = type;
183 part->offset = *(dpt + 8) | *(dpt + 9) << 8 | *(dpt + 10) << 16 | *(dpt + 11) << 24;
184 part->size = *(dpt + 12) | *(dpt + 13) << 8 | *(dpt + 14) << 16 | *(dpt + 15) << 24;
185
186 rt_kprintf("found part[%d], begin: %d, size: ",
187 pindex, part->offset * 512);
188 if ((part->size >> 11) == 0)
189 rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */
190 else
191 {
192 unsigned int part_size;
193 part_size = part->size >> 11; /* MB */
194 if ((part_size >> 10) == 0)
195 rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n");
196 else
197 rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n");
198 }
199
200 return RT_EOK;
201 }
202
203 /**
204 * this function will mount a file system on a specified path.
205 *
206 * @param device_name the name of device which includes a file system.
207 * @param path the path to mount a file system
208 * @param filesystemtype the file system type
209 * @param rwflag the read/write etc. flag.
210 * @param data the private data(parameter) for this file system.
211 *
212 * @return 0 on successful or -1 on failed.
213 */
dfs_mount(const char * device_name,const char * path,const char * filesystemtype,unsigned long rwflag,const void * data)214 int dfs_mount(const char *device_name,
215 const char *path,
216 const char *filesystemtype,
217 unsigned long rwflag,
218 const void *data)
219 {
220 const struct dfs_filesystem_ops **ops;
221 struct dfs_filesystem *iter;
222 struct dfs_filesystem *fs = NULL;
223 char *fullpath = NULL;
224 rt_device_t dev_id;
225
226 /* open specific device */
227 if (device_name == NULL)
228 {
229 /* which is a non-device filesystem mount */
230 dev_id = NULL;
231 }
232 else if ((dev_id = rt_device_find(device_name)) == NULL)
233 {
234 /* no this device */
235 rt_set_errno(-ENODEV);
236 return -1;
237 }
238
239 /* find out the specific filesystem */
240 dfs_lock();
241
242 for (ops = &filesystem_operation_table[0];
243 ops < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; ops++)
244 if ((*ops != NULL) && (strncmp((*ops)->name, filesystemtype, strlen((*ops)->name)) == 0))
245 break;
246
247 dfs_unlock();
248
249 if (ops == &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX])
250 {
251 /* can't find filesystem */
252 rt_set_errno(-ENODEV);
253 return -1;
254 }
255
256 /* check if there is mount implementation */
257 if ((*ops == NULL) || ((*ops)->mount == NULL))
258 {
259 rt_set_errno(-ENOSYS);
260 return -1;
261 }
262
263 /* make full path for special file */
264 fullpath = dfs_normalize_path(NULL, path);
265 if (fullpath == NULL) /* not an abstract path */
266 {
267 rt_set_errno(-ENOTDIR);
268 return -1;
269 }
270
271 /* Check if the path exists or not, raw APIs call, fixme */
272 if ((strcmp(fullpath, "/") != 0) && (strcmp(fullpath, "/dev") != 0))
273 {
274 struct dfs_file fd;
275
276 fd_init(&fd);
277 if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0)
278 {
279 rt_free(fullpath);
280 rt_set_errno(-ENOTDIR);
281
282 return -1;
283 }
284 dfs_file_close(&fd);
285 }
286
287 /* check whether the file system mounted or not in the filesystem table
288 * if it is unmounted yet, find out an empty entry */
289 dfs_lock();
290
291 for (iter = &filesystem_table[0];
292 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
293 {
294 /* check if it is an empty filesystem table entry? if it is, save fs */
295 if (iter->ops == NULL)
296 (fs == NULL) ? (fs = iter) : 0;
297 /* check if the PATH is mounted */
298 else if (strcmp(iter->path, path) == 0)
299 {
300 rt_set_errno(-EINVAL);
301 goto err1;
302 }
303 }
304
305 if ((fs == NULL) && (iter == &filesystem_table[DFS_FILESYSTEMS_MAX]))
306 {
307 rt_set_errno(-ENOSPC);
308 LOG_E("There is no space to mount this file system (%s).", filesystemtype);
309 goto err1;
310 }
311
312 /* register file system */
313 fs->path = fullpath;
314 fs->ops = *ops;
315 fs->dev_id = dev_id;
316 /* For UFS, record the real filesystem name */
317 fs->data = (void *) filesystemtype;
318
319 /* release filesystem_table lock */
320 dfs_unlock();
321
322 /* open device, but do not check the status of device */
323 if (dev_id != NULL)
324 {
325 if (rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDWR) != RT_EOK &&
326 rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDONLY) != RT_EOK)
327 {
328 /* The underlying device has error, clear the entry. */
329 dfs_lock();
330 rt_memset(fs, 0, sizeof(struct dfs_filesystem));
331
332 goto err1;
333 }
334 }
335
336 /* call mount of this filesystem */
337 if ((*ops)->mount(fs, rwflag, data) < 0)
338 {
339 /* close device */
340 if (dev_id != NULL)
341 rt_device_close(fs->dev_id);
342
343 /* mount failed */
344 dfs_lock();
345 /* clear filesystem table entry */
346 rt_memset(fs, 0, sizeof(struct dfs_filesystem));
347
348 goto err1;
349 }
350
351 return 0;
352
353 err1:
354 dfs_unlock();
355 rt_free(fullpath);
356
357 return -1;
358 }
359
360 /**
361 * this function will unmount a file system on specified path.
362 *
363 * @param specialfile the specified path which mounted a file system.
364 *
365 * @return 0 on successful or -1 on failed.
366 */
dfs_unmount(const char * specialfile)367 int dfs_unmount(const char *specialfile)
368 {
369 char *fullpath;
370 struct dfs_filesystem *iter;
371 struct dfs_filesystem *fs = NULL;
372
373 fullpath = dfs_normalize_path(NULL, specialfile);
374 if (fullpath == NULL)
375 {
376 rt_set_errno(-ENOTDIR);
377
378 return -1;
379 }
380
381 /* lock filesystem */
382 dfs_lock();
383
384 for (iter = &filesystem_table[0];
385 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
386 {
387 /* check if the PATH is mounted */
388 if ((iter->path != NULL) && (strcmp(iter->path, fullpath) == 0))
389 {
390 fs = iter;
391 break;
392 }
393 }
394
395 if (fs == NULL ||
396 fs->ops->unmount == NULL ||
397 fs->ops->unmount(fs) < 0)
398 {
399 goto err1;
400 }
401
402 /* close device, but do not check the status of device */
403 if (fs->dev_id != NULL)
404 rt_device_close(fs->dev_id);
405
406 if (fs->path != NULL)
407 rt_free(fs->path);
408
409 /* clear this filesystem table entry */
410 rt_memset(fs, 0, sizeof(struct dfs_filesystem));
411
412 dfs_unlock();
413 rt_free(fullpath);
414
415 return 0;
416
417 err1:
418 dfs_unlock();
419 rt_free(fullpath);
420
421 return -1;
422 }
423
424 /**
425 * make a file system on the special device
426 *
427 * @param fs_name the file system name
428 * @param device_name the special device name
429 *
430 * @return 0 on successful, otherwise failed.
431 */
dfs_mkfs(const char * fs_name,const char * device_name)432 int dfs_mkfs(const char *fs_name, const char *device_name)
433 {
434 int index;
435 rt_device_t dev_id = NULL;
436
437 /* check device name, and it should not be NULL */
438 if (device_name != NULL)
439 dev_id = rt_device_find(device_name);
440
441 if (dev_id == NULL)
442 {
443 rt_set_errno(-ENODEV);
444 LOG_E("Device (%s) was not found", device_name);
445 return -1;
446 }
447
448 /* lock file system */
449 dfs_lock();
450 /* find the file system operations */
451 for (index = 0; index < DFS_FILESYSTEM_TYPES_MAX; index ++)
452 {
453 if (filesystem_operation_table[index] != NULL &&
454 strncmp(filesystem_operation_table[index]->name, fs_name,
455 strlen(filesystem_operation_table[index]->name)) == 0)
456 break;
457 }
458 dfs_unlock();
459
460 if (index < DFS_FILESYSTEM_TYPES_MAX)
461 {
462 /* find file system operation */
463 const struct dfs_filesystem_ops *ops = filesystem_operation_table[index];
464 if (ops->mkfs == NULL)
465 {
466 LOG_E("The file system (%s) mkfs function was not implement", fs_name);
467 rt_set_errno(-ENOSYS);
468 return -1;
469 }
470
471 return ops->mkfs(dev_id, fs_name);
472 }
473
474 LOG_E("File system (%s) was not found.", fs_name);
475
476 return -1;
477 }
478
479 /**
480 * this function will return the information about a mounted file system.
481 *
482 * @param path the path which mounted file system.
483 * @param buffer the buffer to save the returned information.
484 *
485 * @return 0 on successful, others on failed.
486 */
dfs_statfs(const char * path,struct statfs * buffer)487 int dfs_statfs(const char *path, struct statfs *buffer)
488 {
489 struct dfs_filesystem *fs;
490
491 fs = dfs_filesystem_lookup(path);
492 if (fs != NULL)
493 {
494 if (fs->ops->statfs != NULL)
495 return fs->ops->statfs(fs, buffer);
496 }
497
498 rt_set_errno(-ENOSYS);
499 return -1;
500 }
501
502 #ifdef RT_USING_DFS_MNTTABLE
dfs_mount_table(void)503 int dfs_mount_table(void)
504 {
505 int index = 0;
506
507 while (1)
508 {
509 if (mount_table[index].path == NULL) break;
510
511 if (dfs_mount(mount_table[index].device_name,
512 mount_table[index].path,
513 mount_table[index].filesystemtype,
514 mount_table[index].rwflag,
515 mount_table[index].data) != 0)
516 {
517 LOG_E("mount fs[%s] on %s failed.\n", mount_table[index].filesystemtype,
518 mount_table[index].path);
519 return -RT_ERROR;
520 }
521
522 index ++;
523 }
524 return 0;
525 }
526 INIT_ENV_EXPORT(dfs_mount_table);
527
dfs_mount_device(rt_device_t dev)528 int dfs_mount_device(rt_device_t dev)
529 {
530 int index = 0;
531
532 if(dev == RT_NULL)
533 {
534 rt_kprintf("the device is NULL to be mounted.\n");
535 return -RT_ERROR;
536 }
537
538 while (1)
539 {
540 if (mount_table[index].path == NULL) break;
541
542 if(strcmp(mount_table[index].device_name, dev->parent.name) == 0)
543 {
544 if (dfs_mount(mount_table[index].device_name,
545 mount_table[index].path,
546 mount_table[index].filesystemtype,
547 mount_table[index].rwflag,
548 mount_table[index].data) != 0)
549 {
550 LOG_E("mount fs[%s] device[%s] to %s failed.\n", mount_table[index].filesystemtype, dev->parent.name,
551 mount_table[index].path);
552 return -RT_ERROR;
553 } else {
554 LOG_D("mount fs[%s] device[%s] to %s ok.\n", mount_table[index].filesystemtype, dev->parent.name,
555 mount_table[index].path);
556 return RT_EOK;
557 }
558 }
559
560 index ++;
561 }
562
563 rt_kprintf("can't find device:%s to be mounted.\n", dev->parent.name);
564 return -RT_ERROR;
565 }
566
dfs_unmount_device(rt_device_t dev)567 int dfs_unmount_device(rt_device_t dev)
568 {
569 struct dfs_filesystem *iter;
570 struct dfs_filesystem *fs = NULL;
571
572 /* lock filesystem */
573 dfs_lock();
574
575 for (iter = &filesystem_table[0];
576 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
577 {
578 /* check if the PATH is mounted */
579 if (strcmp(iter->dev_id->parent.name, dev->parent.name) == 0)
580 {
581 fs = iter;
582 break;
583 }
584 }
585
586 if (fs == NULL ||
587 fs->ops->unmount == NULL ||
588 fs->ops->unmount(fs) < 0)
589 {
590 goto err1;
591 }
592
593 /* close device, but do not check the status of device */
594 if (fs->dev_id != NULL)
595 rt_device_close(fs->dev_id);
596
597 if (fs->path != NULL)
598 rt_free(fs->path);
599
600 /* clear this filesystem table entry */
601 rt_memset(fs, 0, sizeof(struct dfs_filesystem));
602
603 dfs_unlock();
604
605 return 0;
606
607 err1:
608 dfs_unlock();
609
610 return -1;
611 }
612
613 #endif
614
615 #ifdef RT_USING_FINSH
616 #include <finsh.h>
mkfs(const char * fs_name,const char * device_name)617 void mkfs(const char *fs_name, const char *device_name)
618 {
619 dfs_mkfs(fs_name, device_name);
620 }
621 FINSH_FUNCTION_EXPORT(mkfs, make a file system);
622
df(const char * path)623 int df(const char *path)
624 {
625 int result;
626 int minor = 0;
627 long long cap;
628 struct statfs buffer;
629
630 int unit_index = 0;
631 char *unit_str[] = {"KB", "MB", "GB"};
632
633 result = dfs_statfs(path ? path : NULL, &buffer);
634 if (result != 0)
635 {
636 if (rt_get_errno() == -ENOSYS)
637 rt_kprintf("The function is not implemented.\n");
638 else
639 rt_kprintf("statfs failed: errno=%d.\n", rt_get_errno());
640 return -1;
641 }
642
643 cap = ((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL;
644 for (unit_index = 0; unit_index < 2; unit_index ++)
645 {
646 if (cap < 1024) break;
647
648 minor = (cap % 1024) * 10 / 1024; /* only one decimal point */
649 cap = cap / 1024;
650 }
651
652 rt_kprintf("disk free: %d.%d %s [ %d block, %d bytes per block ]\n",
653 (unsigned long)cap, minor, unit_str[unit_index], buffer.f_bfree, buffer.f_bsize);
654 return 0;
655 }
656 FINSH_FUNCTION_EXPORT(df, get disk free);
657 #endif
658
659 /**@}*/
660