xref: /nrf52832-nimble/rt-thread/components/dfs/src/dfs_fs.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  * 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 FsApi
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 (%d).", 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         /* fint 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) && (strcmp((*ops)->name, filesystemtype) == 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_fd fd;
275 
276         if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0)
277         {
278             rt_free(fullpath);
279             rt_set_errno(-ENOTDIR);
280 
281             return -1;
282         }
283         dfs_file_close(&fd);
284     }
285 
286     /* check whether the file system mounted or not  in the filesystem table
287      * if it is unmounted yet, find out an empty entry */
288     dfs_lock();
289 
290     for (iter = &filesystem_table[0];
291             iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
292     {
293         /* check if it is an empty filesystem table entry? if it is, save fs */
294         if (iter->ops == NULL)
295             (fs == NULL) ? (fs = iter) : 0;
296         /* check if the PATH is mounted */
297         else if (strcmp(iter->path, path) == 0)
298         {
299             rt_set_errno(-EINVAL);
300             goto err1;
301         }
302     }
303 
304     if ((fs == NULL) && (iter == &filesystem_table[DFS_FILESYSTEMS_MAX]))
305     {
306         rt_set_errno(-ENOSPC);
307         LOG_E("There is no space to mount this file system (%s).", filesystemtype);
308         goto err1;
309     }
310 
311     /* register file system */
312     fs->path   = fullpath;
313     fs->ops    = *ops;
314     fs->dev_id = dev_id;
315     /* release filesystem_table lock */
316     dfs_unlock();
317 
318     /* open device, but do not check the status of device */
319     if (dev_id != NULL)
320     {
321         if (rt_device_open(fs->dev_id,
322                            RT_DEVICE_OFLAG_RDWR) != RT_EOK)
323         {
324             /* The underlaying device has error, clear the entry. */
325             dfs_lock();
326             memset(fs, 0, sizeof(struct dfs_filesystem));
327 
328             goto err1;
329         }
330     }
331 
332     /* call mount of this filesystem */
333     if ((*ops)->mount(fs, rwflag, data) < 0)
334     {
335         /* close device */
336         if (dev_id != NULL)
337             rt_device_close(fs->dev_id);
338 
339         /* mount failed */
340         dfs_lock();
341         /* clear filesystem table entry */
342         memset(fs, 0, sizeof(struct dfs_filesystem));
343 
344         goto err1;
345     }
346 
347     return 0;
348 
349 err1:
350     dfs_unlock();
351     rt_free(fullpath);
352 
353     return -1;
354 }
355 
356 /**
357  * this function will unmount a file system on specified path.
358  *
359  * @param specialfile the specified path which mounted a file system.
360  *
361  * @return 0 on successful or -1 on failed.
362  */
dfs_unmount(const char * specialfile)363 int dfs_unmount(const char *specialfile)
364 {
365     char *fullpath;
366     struct dfs_filesystem *iter;
367     struct dfs_filesystem *fs = NULL;
368 
369     fullpath = dfs_normalize_path(NULL, specialfile);
370     if (fullpath == NULL)
371     {
372         rt_set_errno(-ENOTDIR);
373 
374         return -1;
375     }
376 
377     /* lock filesystem */
378     dfs_lock();
379 
380     for (iter = &filesystem_table[0];
381             iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
382     {
383         /* check if the PATH is mounted */
384         if ((iter->path != NULL) && (strcmp(iter->path, fullpath) == 0))
385         {
386             fs = iter;
387             break;
388         }
389     }
390 
391     if (fs == NULL ||
392         fs->ops->unmount == NULL ||
393         fs->ops->unmount(fs) < 0)
394     {
395         goto err1;
396     }
397 
398     /* close device, but do not check the status of device */
399     if (fs->dev_id != NULL)
400         rt_device_close(fs->dev_id);
401 
402     if (fs->path != NULL)
403         rt_free(fs->path);
404 
405     /* clear this filesystem table entry */
406     memset(fs, 0, sizeof(struct dfs_filesystem));
407 
408     dfs_unlock();
409     rt_free(fullpath);
410 
411     return 0;
412 
413 err1:
414     dfs_unlock();
415     rt_free(fullpath);
416 
417     return -1;
418 }
419 
420 /**
421  * make a file system on the special device
422  *
423  * @param fs_name the file system name
424  * @param device_name the special device name
425  *
426  * @return 0 on successful, otherwise failed.
427  */
dfs_mkfs(const char * fs_name,const char * device_name)428 int dfs_mkfs(const char *fs_name, const char *device_name)
429 {
430     int index;
431     rt_device_t dev_id = NULL;
432 
433     /* check device name, and it should not be NULL */
434     if (device_name != NULL)
435         dev_id = rt_device_find(device_name);
436 
437     if (dev_id == NULL)
438     {
439         rt_set_errno(-ENODEV);
440         LOG_E("Device (%s) was not found", device_name);
441         return -1;
442     }
443 
444     /* lock file system */
445     dfs_lock();
446     /* find the file system operations */
447     for (index = 0; index <= DFS_FILESYSTEM_TYPES_MAX; index ++)
448     {
449         if (filesystem_operation_table[index] != NULL &&
450             strcmp(filesystem_operation_table[index]->name, fs_name) == 0)
451             break;
452     }
453     dfs_unlock();
454 
455     if (index <= DFS_FILESYSTEM_TYPES_MAX)
456     {
457         /* find file system operation */
458         const struct dfs_filesystem_ops *ops = filesystem_operation_table[index];
459         if (ops->mkfs == NULL)
460         {
461             LOG_E("The file system (%s) mkfs function was not implement", fs_name);
462             rt_set_errno(-ENOSYS);
463             return -1;
464         }
465 
466         return ops->mkfs(dev_id);
467     }
468 
469     LOG_E("File system (%s) was not found.", fs_name);
470 
471     return -1;
472 }
473 
474 /**
475  * this function will return the information about a mounted file system.
476  *
477  * @param path the path which mounted file system.
478  * @param buffer the buffer to save the returned information.
479  *
480  * @return 0 on successful, others on failed.
481  */
dfs_statfs(const char * path,struct statfs * buffer)482 int dfs_statfs(const char *path, struct statfs *buffer)
483 {
484     struct dfs_filesystem *fs;
485 
486     fs = dfs_filesystem_lookup(path);
487     if (fs != NULL)
488     {
489         if (fs->ops->statfs != NULL)
490             return fs->ops->statfs(fs, buffer);
491     }
492 
493     return -1;
494 }
495 
496 #ifdef RT_USING_DFS_MNTTABLE
dfs_mount_table(void)497 int dfs_mount_table(void)
498 {
499     int index = 0;
500 
501     while (1)
502     {
503         if (mount_table[index].path == NULL) break;
504 
505         if (dfs_mount(mount_table[index].device_name,
506                 mount_table[index].path,
507                 mount_table[index].filesystemtype,
508                 mount_table[index].rwflag,
509                 mount_table[index].data) != 0)
510         {
511             rt_kprintf("mount fs[%s] on %s failed.\n", mount_table[index].filesystemtype,
512                 mount_table[index].path);
513             return -RT_ERROR;
514         }
515 
516         index ++;
517     }
518     return 0;
519 }
520 INIT_ENV_EXPORT(dfs_mount_table);
521 #endif
522 
523 #ifdef RT_USING_FINSH
524 #include <finsh.h>
mkfs(const char * fs_name,const char * device_name)525 void mkfs(const char *fs_name, const char *device_name)
526 {
527     dfs_mkfs(fs_name, device_name);
528 }
529 FINSH_FUNCTION_EXPORT(mkfs, make a file system);
530 
df(const char * path)531 int df(const char *path)
532 {
533     int result;
534     int minor = 0;
535     long long cap;
536     struct statfs buffer;
537 
538     int unit_index = 0;
539     char *unit_str[] = {"KB", "MB", "GB"};
540 
541     result = dfs_statfs(path ? path : NULL, &buffer);
542     if (result != 0)
543     {
544         rt_kprintf("dfs_statfs failed.\n");
545         return -1;
546     }
547 
548     cap = ((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL;
549     for (unit_index = 0; unit_index < 2; unit_index ++)
550     {
551         if (cap < 1024) break;
552 
553         minor = (cap % 1024) * 10 / 1024; /* only one decimal point */
554         cap = cap / 1024;
555     }
556 
557     rt_kprintf("disk free: %d.%d %s [ %d block, %d bytes per block ]\n",
558         (unsigned long)cap, minor, unit_str[unit_index], buffer.f_bfree, buffer.f_bsize);
559     return 0;
560 }
561 FINSH_FUNCTION_EXPORT(df, get disk free);
562 #endif
563 
564 /* @} */
565