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