xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/dfs_uffs.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  * 2011-10-22     prife        the first version
9  * 2012-03-28     prife        use mtd device interface
10  * 2012-04-05     prife        update uffs with official repo and use uffs_UnMount/Mount
11  * 2017-04-12     lizhen9880   fix the f_bsize and f_blocks issue in function dfs_uffs_statfs
12  */
13 
14 #include <rtthread.h>
15 
16 #include <dfs_fs.h>
17 #include <dfs_file.h>
18 #include <rtdevice.h>
19 
20 #include "dfs_uffs.h"
21 
22 #include "uffs/uffs_fd.h" /* posix file api is here */
23 #include "uffs/uffs_mtb.h"
24 #include "uffs/uffs_mem.h"
25 #include "uffs/uffs_utils.h"
26 
27 /*
28  * RT-Thread DFS Interface for uffs
29  */
30 #define UFFS_DEVICE_MAX         2    /* the max partions on a nand deivce*/
31 #define UFFS_MOUNT_PATH_MAX     128  /* the mount point max length */
32 #define FILE_PATH_MAX           256  /* the longest file path */
33 
34 struct _nand_dev
35 {
36     struct rt_mtd_nand_device * dev;
37     struct uffs_StorageAttrSt storage;
38     uffs_Device uffs_dev;
39     uffs_MountTable mount_table;
40     char mount_path[UFFS_MOUNT_PATH_MAX];
41     void * data;  /* when uffs use static buf, it will save ptr here */
42 };
43 /* make sure the following struct var had been initilased to 0! */
44 static struct _nand_dev nand_part[UFFS_DEVICE_MAX] = {0};
45 
uffs_result_to_dfs(int result)46 static int uffs_result_to_dfs(int result)
47 {
48     int status = -1;
49 
50     result = result < 0 ? -result : result;
51     switch (result)
52     {
53     case UENOERR:/** no error */
54         break;
55     case UEACCES:/** Tried to open read-only file for writing, or files sharing mode
56                    does not allow specified operations, or given path is directory */
57         status = -EINVAL;
58         break;/* no suitable */
59     case UEEXIST:   /** _O_CREAT and _O_EXCL flags specified, but filename already exists */
60         status = -EEXIST;
61         break;
62     case UEINVAL:  /** Invalid oflag or pmode argument */
63         status = -EINVAL;
64         break;
65     case UEMFILE: /** No more file handles available(too many open files)  */
66         status = -1;
67         break;
68     case UENOENT: /** file or path not found */
69         status = -ENOENT;
70         break;
71     case UETIME: /** can't set file time */
72         status = -1;
73         break;
74     case UEBADF: /** invalid file handle */
75         status = -EBADF;
76         break;
77     case UENOMEM:/** no enough memory */
78         status = -ENOSPC;
79         break;
80     case UEIOERR: /** I/O error from lower level flash operation */
81         status = -EIO;
82         break;
83     case UENOTDIR: /** Not a directory */
84         status = -ENOTDIR;
85         break;
86     case UEISDIR: /** Is a directory */
87         status = -EISDIR;
88         break;
89     case UEUNKNOWN_ERR:
90     default:
91         status = -1;
92         break; /* unknown error! */
93     }
94 
95     return status;
96 }
97 
_device_init(uffs_Device * dev)98 static URET _device_init(uffs_Device *dev)
99 {
100     dev->attr->_private = NULL; // hook nand_chip data structure to attr->_private
101     dev->ops = (struct uffs_FlashOpsSt *)&nand_ops;
102 
103     return U_SUCC;
104 }
105 
_device_release(uffs_Device * dev)106 static URET _device_release(uffs_Device *dev)
107 {
108     return U_SUCC;
109 }
110 
init_uffs_fs(struct _nand_dev * nand_part)111 static int init_uffs_fs(
112     struct _nand_dev * nand_part)
113 {
114     uffs_MountTable * mtb;
115     struct rt_mtd_nand_device * nand;
116     struct uffs_StorageAttrSt * flash_storage;
117 
118     mtb = &nand_part->mount_table;
119     nand = nand_part->dev;
120     flash_storage = &nand_part->storage;
121 
122     /* setup nand storage attributes */
123     uffs_setup_storage(flash_storage, nand);
124 
125     /* register mount table */
126     if(mtb->dev)
127     {
128         /* set memory allocator for uffs */
129 #if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
130         uffs_MemSetupSystemAllocator(&mtb->dev->mem);
131 #endif
132         /* setup device init/release entry */
133         mtb->dev->Init = _device_init;
134         mtb->dev->Release = _device_release;
135         mtb->dev->attr = flash_storage;
136 
137         uffs_RegisterMountTable(mtb);
138     }
139     /* mount uffs partion on nand device */
140     return uffs_Mount(nand_part->mount_path) == U_SUCC ? 0 : -1;
141 }
142 
dfs_uffs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)143 static int dfs_uffs_mount(
144     struct dfs_filesystem* fs,
145     unsigned long rwflag,
146     const void* data)
147 {
148     rt_base_t index;
149     uffs_MountTable * mount_part;
150     struct rt_mtd_nand_device * dev;
151 
152     RT_ASSERT(rt_strlen(fs->path) < (UFFS_MOUNT_PATH_MAX-1));
153     dev = RT_MTD_NAND_DEVICE(fs->dev_id);
154 
155     /*1. find a empty entry in partition table */
156     for (index = 0; index < UFFS_DEVICE_MAX ; index ++)
157     {
158         if (nand_part[index].dev == RT_NULL)
159             break;
160     }
161     if (index == UFFS_DEVICE_MAX)
162         return -ENOENT;
163 
164     /*2. fill partition structure */
165     nand_part[index].dev = dev;
166 
167     /* make a right mount path for uffs, end with '/' */
168     rt_snprintf(nand_part[index].mount_path, UFFS_MOUNT_PATH_MAX, "%s/", fs->path);
169     if (nand_part[index].mount_path[1] == '/')
170         nand_part[index].mount_path[1] = 0;
171 
172     mount_part = &(nand_part[index].mount_table);
173     mount_part->mount   = nand_part[index].mount_path;
174     mount_part->dev = &(nand_part[index].uffs_dev);
175     rt_memset(mount_part->dev, 0, sizeof(uffs_Device));//in order to make uffs happy.
176     mount_part->dev->_private = dev;   /* save dev_id into uffs */
177     mount_part->start_block = dev->block_start;
178     mount_part->end_block = dev->block_end;
179     /*3. mount uffs */
180     if (init_uffs_fs(&nand_part[index]) < 0)
181     {
182         return uffs_result_to_dfs(uffs_get_error());
183     }
184     return 0;
185 }
186 
dfs_uffs_unmount(struct dfs_filesystem * fs)187 static int dfs_uffs_unmount(struct dfs_filesystem* fs)
188 {
189     rt_base_t index;
190     int result;
191 
192     /* find the device index and then unmount it */
193     for (index = 0; index < UFFS_DEVICE_MAX; index++)
194     {
195         if (nand_part[index].dev == RT_MTD_NAND_DEVICE(fs->dev_id))
196         {
197             nand_part[index].dev = RT_NULL;
198             result = uffs_UnMount(nand_part[index].mount_path);
199             if (result != U_SUCC)
200                 break;
201 
202             result = uffs_UnRegisterMountTable(& nand_part[index].mount_table);
203             return (result == U_SUCC) ? RT_EOK : -1;
204         }
205     }
206     return -ENOENT;
207 }
208 
dfs_uffs_mkfs(rt_device_t dev_id)209 static int dfs_uffs_mkfs(rt_device_t dev_id)
210 {
211     rt_base_t index;
212     rt_uint32_t block;
213     struct rt_mtd_nand_device * mtd;
214 
215     /*1. find the device index */
216     for (index = 0; index < UFFS_DEVICE_MAX; index++)
217     {
218         if (nand_part[index].dev == (struct rt_mtd_nand_device *)dev_id)
219             break;
220     }
221 
222     if (index == UFFS_DEVICE_MAX)
223     {
224         /* can't find device driver */
225         return -ENOENT;
226     }
227 
228     /*2. then unmount the partition */
229     uffs_Mount(nand_part[index].mount_path);
230     mtd = nand_part[index].dev;
231 
232     /*3. erase all blocks on the partition */
233     block = mtd->block_start;
234     for (; block <= mtd->block_end; block++)
235     {
236         rt_mtd_nand_erase_block(mtd, block);
237         if (rt_mtd_nand_check_block(mtd, block) != RT_EOK)
238         {
239             rt_kprintf("found bad block %d\n", block);
240             rt_mtd_nand_mark_badblock(mtd, block);
241         }
242     }
243 
244     /*4. remount it */
245     if (init_uffs_fs(&nand_part[index]) < 0)
246     {
247         return uffs_result_to_dfs(uffs_get_error());
248     }
249     return RT_EOK;
250 }
251 
dfs_uffs_statfs(struct dfs_filesystem * fs,struct statfs * buf)252 static int dfs_uffs_statfs(struct dfs_filesystem* fs,
253                     struct statfs *buf)
254 {
255     rt_base_t index;
256     struct rt_mtd_nand_device * mtd = RT_MTD_NAND_DEVICE(fs->dev_id);
257 
258     RT_ASSERT(mtd != RT_NULL);
259 
260     /* find the device index */
261     for (index = 0; index < UFFS_DEVICE_MAX; index++)
262     {
263         if (nand_part[index].dev == (void *)mtd)
264             break;
265     }
266     if (index == UFFS_DEVICE_MAX)
267         return -ENOENT;
268 
269     buf->f_bsize = mtd->page_size*mtd->pages_per_block;
270     buf->f_blocks = (mtd->block_end - mtd->block_start + 1);
271     buf->f_bfree = uffs_GetDeviceFree(&nand_part[index].uffs_dev)/buf->f_bsize ;
272 
273     return 0;
274 }
275 
dfs_uffs_open(struct dfs_fd * file)276 static int dfs_uffs_open(struct dfs_fd* file)
277 {
278     int fd;
279     int oflag, mode;
280     char * file_path;
281 
282     oflag = file->flags;
283     if (oflag & O_DIRECTORY)   /* operations about dir */
284     {
285         uffs_DIR * dir;
286 
287         if (oflag & O_CREAT)   /* create a dir*/
288         {
289             if (uffs_mkdir(file->path) < 0)
290                 return uffs_result_to_dfs(uffs_get_error());
291         }
292         /* open dir */
293         file_path = rt_malloc(FILE_PATH_MAX);
294         if(file_path == RT_NULL)
295             return -ENOMEM;
296 
297         if (file->path[0] == '/' && !(file->path[1] == 0))
298             rt_snprintf(file_path, FILE_PATH_MAX, "%s/", file->path);
299         else
300         {
301             file_path[0] = '/';
302             file_path[1] = 0;
303         }
304 
305         dir = uffs_opendir(file_path);
306 
307         if (dir == RT_NULL)
308         {
309             rt_free(file_path);
310             return uffs_result_to_dfs(uffs_get_error());
311         }
312         /* save this pointer,will used by  dfs_uffs_getdents*/
313         file->data = dir;
314         rt_free(file_path);
315         return RT_EOK;
316     }
317     /* regular file operations */
318     /* int uffs_open(const char *name, int oflag, ...); what is this?
319      * uffs_open can open dir!!  **/
320     mode = 0;
321     if (oflag & O_RDONLY) mode |= UO_RDONLY;
322     if (oflag & O_WRONLY) mode |= UO_WRONLY;
323     if (oflag & O_RDWR)   mode |= UO_RDWR;
324     /* Opens the file, if it is existing. If not, a new file is created. */
325     if (oflag & O_CREAT) mode |= UO_CREATE;
326     /* Creates a new file. If the file is existing, it is truncated and overwritten. */
327     if (oflag & O_TRUNC) mode |= UO_TRUNC;
328     /* Creates a new file. The function fails if the file is already existing. */
329     if (oflag & O_EXCL) mode |= UO_EXCL;
330 
331     fd = uffs_open(file->path, mode);
332     if (fd < 0)
333     {
334         return uffs_result_to_dfs(uffs_get_error());
335     }
336 
337     /* save this pointer, it will be used when calling read(), write(),
338      * flush(), seek(), and will be free when calling close()*/
339 
340     file->data = (void *)fd;
341     file->pos  = uffs_seek(fd, 0, USEEK_CUR);
342     file->size = uffs_seek(fd, 0, USEEK_END);
343     uffs_seek(fd, file->pos, USEEK_SET);
344 
345     if (oflag & O_APPEND)
346     {
347         file->pos = uffs_seek(fd, 0, USEEK_END);
348     }
349     return 0;
350 }
351 
dfs_uffs_close(struct dfs_fd * file)352 static int dfs_uffs_close(struct dfs_fd* file)
353 {
354     int oflag;
355     int fd;
356 
357     oflag = file->flags;
358     if (oflag & O_DIRECTORY)
359     {
360         /* operations about dir */
361         if (uffs_closedir((uffs_DIR *)(file->data)) < 0)
362             return uffs_result_to_dfs(uffs_get_error());
363 
364         return 0;
365     }
366     /* regular file operations */
367     fd = (int)(file->data);
368 
369     if (uffs_close(fd) == 0)
370         return 0;
371 
372     return uffs_result_to_dfs(uffs_get_error());
373 }
374 
dfs_uffs_ioctl(struct dfs_fd * file,int cmd,void * args)375 static int dfs_uffs_ioctl(struct dfs_fd * file, int cmd, void* args)
376 {
377     return -ENOSYS;
378 }
379 
dfs_uffs_read(struct dfs_fd * file,void * buf,size_t len)380 static int dfs_uffs_read(struct dfs_fd * file, void* buf, size_t len)
381 {
382     int fd;
383     int char_read;
384 
385     fd = (int)(file->data);
386     char_read = uffs_read(fd, buf, len);
387     if (char_read < 0)
388         return uffs_result_to_dfs(uffs_get_error());
389 
390     /* update position */
391     file->pos = uffs_seek(fd, 0, USEEK_CUR);
392     return char_read;
393 }
394 
dfs_uffs_write(struct dfs_fd * file,const void * buf,size_t len)395 static int dfs_uffs_write(struct dfs_fd* file,
396                    const void* buf,
397                    size_t len)
398 {
399     int fd;
400     int char_write;
401 
402     fd = (int)(file->data);
403 
404     char_write = uffs_write(fd, buf, len);
405     if (char_write < 0)
406         return uffs_result_to_dfs(uffs_get_error());
407 
408     /* update position */
409     file->pos = uffs_seek(fd, 0, USEEK_CUR);
410     return char_write;
411 }
412 
dfs_uffs_flush(struct dfs_fd * file)413 static int dfs_uffs_flush(struct dfs_fd* file)
414 {
415     int fd;
416     int result;
417 
418     fd = (int)(file->data);
419 
420     result = uffs_flush(fd);
421     if (result < 0 )
422         return uffs_result_to_dfs(uffs_get_error());
423     return 0;
424 }
425 
uffs_seekdir(uffs_DIR * dir,long offset)426 int uffs_seekdir(uffs_DIR *dir, long offset)
427 {
428     int i = 0;
429 
430     while(i < offset)
431     {
432         if (uffs_readdir(dir) == RT_NULL)
433             return -1;
434         i++;
435     }
436     return 0;
437 }
438 
439 
dfs_uffs_seek(struct dfs_fd * file,rt_off_t offset)440 static int dfs_uffs_seek(struct dfs_fd* file,
441                   rt_off_t offset)
442 {
443     int result;
444 
445     /* set offset as current offset */
446     if (file->type == FT_DIRECTORY)
447     {
448         uffs_rewinddir((uffs_DIR *)(file->data));
449         result = uffs_seekdir((uffs_DIR *)(file->data), offset/sizeof(struct dirent));
450         if (result >= 0)
451         {
452             file->pos = offset;
453             return offset;
454         }
455     }
456     else if (file->type == FT_REGULAR)
457     {
458         result = uffs_seek((int)(file->data), offset, USEEK_SET);
459         if (result >= 0)
460             return offset;
461     }
462 
463     return uffs_result_to_dfs(uffs_get_error());
464 }
465 
466 /* return the size of struct dirent*/
dfs_uffs_getdents(struct dfs_fd * file,struct dirent * dirp,uint32_t count)467 static int dfs_uffs_getdents(
468     struct dfs_fd* file,
469     struct dirent* dirp,
470     uint32_t count)
471 {
472     rt_uint32_t index;
473     char * file_path;
474     struct dirent* d;
475     uffs_DIR* dir;
476     struct uffs_dirent * uffs_d;
477 
478     dir = (uffs_DIR*)(file->data);
479     RT_ASSERT(dir != RT_NULL);
480 
481     /* round count, count is always 1 */
482     count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
483     if (count == 0) return -EINVAL;
484 
485     /* allocate file name */
486     file_path = rt_malloc(FILE_PATH_MAX);
487     if (file_path == RT_NULL)
488         return -ENOMEM;
489 
490     index = 0;
491     /* usually, the while loop should only be looped only once! */
492     while (1)
493     {
494         struct uffs_stat s;
495 
496         d = dirp + index;
497 
498         uffs_d = uffs_readdir(dir);
499         if (uffs_d == RT_NULL)
500         {
501             rt_free(file_path);
502             return (uffs_result_to_dfs(uffs_get_error()));
503         }
504 
505         if (file->path[0] == '/' && !(file->path[1] == 0))
506             rt_snprintf(file_path, FILE_PATH_MAX, "%s/%s", file->path, uffs_d->d_name);
507         else
508             rt_strncpy(file_path, uffs_d->d_name, FILE_PATH_MAX);
509 
510         uffs_stat(file_path, &s);
511         switch(s.st_mode & US_IFMT)   /* file type mark */
512         {
513         case US_IFREG: /* directory */
514             d->d_type = DT_REG;
515             break;
516         case US_IFDIR: /* regular file */
517             d->d_type = DT_DIR;
518             break;
519         case US_IFLNK: /* symbolic link */
520         case US_IREAD: /* read permission */
521         case US_IWRITE:/* write permission */
522         default:
523             d->d_type = DT_UNKNOWN;
524             break;
525         }
526 
527         /* write the rest args of struct dirent* dirp  */
528         d->d_namlen = rt_strlen(uffs_d->d_name);
529         d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
530         rt_strncpy(d->d_name, uffs_d->d_name, rt_strlen(uffs_d->d_name) + 1);
531 
532         index ++;
533         if (index * sizeof(struct dirent) >= count)
534             break;
535     }
536 
537     /* free file name buf */
538     rt_free(file_path);
539 
540     if (index == 0)
541         return uffs_result_to_dfs(uffs_get_error());
542 
543     file->pos += index * sizeof(struct dirent);
544 
545     return index * sizeof(struct dirent);
546 }
547 
dfs_uffs_unlink(struct dfs_filesystem * fs,const char * path)548 static int dfs_uffs_unlink(struct dfs_filesystem* fs, const char* path)
549 {
550     int result;
551     struct uffs_stat s;
552 
553     /* judge file type, dir is to be delete by uffs_rmdir, others by uffs_remove */
554     if (uffs_lstat(path, &s) < 0)
555     {
556         return uffs_result_to_dfs(uffs_get_error());
557     }
558 
559     switch(s.st_mode & US_IFMT)
560     {
561     case US_IFREG:
562         result = uffs_remove(path);
563         break;
564     case US_IFDIR:
565         result = uffs_rmdir(path);
566         break;
567     default:
568         /* unknown file type */
569         return -1;
570     }
571     if (result < 0)
572         return uffs_result_to_dfs(uffs_get_error());
573 
574     return 0;
575 }
576 
dfs_uffs_rename(struct dfs_filesystem * fs,const char * oldpath,const char * newpath)577 static int dfs_uffs_rename(
578     struct dfs_filesystem* fs,
579     const char* oldpath,
580     const char* newpath)
581 {
582     int result;
583 
584     result = uffs_rename(oldpath, newpath);
585     if (result < 0)
586         return uffs_result_to_dfs(uffs_get_error());
587 
588     return 0;
589 }
590 
dfs_uffs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)591 static int dfs_uffs_stat(struct dfs_filesystem* fs, const char *path, struct stat *st)
592 {
593     int result;
594     struct uffs_stat s;
595 
596     result = uffs_stat(path, &s);
597     if (result < 0)
598         return uffs_result_to_dfs(uffs_get_error());
599 
600     /* convert uffs stat to dfs stat structure */
601     /* FIXME, these field may not be the same */
602     st->st_dev  = 0;
603     st->st_mode = s.st_mode;
604     st->st_size = s.st_size;
605     st->st_mtime = s.st_mtime;
606 
607     return 0;
608 }
609 
610 static const struct dfs_file_ops dfs_uffs_fops =
611 {
612     dfs_uffs_open,
613     dfs_uffs_close,
614     dfs_uffs_ioctl,
615     dfs_uffs_read,
616     dfs_uffs_write,
617     dfs_uffs_flush,
618     dfs_uffs_seek,
619     dfs_uffs_getdents,
620 };
621 
622 static const struct dfs_filesystem_ops dfs_uffs_ops =
623 {
624     "uffs", /* file system type: uffs */
625 #if RTTHREAD_VERSION >= 10100
626     DFS_FS_FLAG_FULLPATH,
627 #else
628 #error "uffs can only work with rtthread whose version should >= 1.01\n"
629 #endif
630     &dfs_uffs_fops,
631 
632     dfs_uffs_mount,
633     dfs_uffs_unmount,
634     dfs_uffs_mkfs,
635     dfs_uffs_statfs,
636 
637     dfs_uffs_unlink,
638     dfs_uffs_stat,
639     dfs_uffs_rename,
640 };
641 
dfs_uffs_init(void)642 int dfs_uffs_init(void)
643 {
644     /* register uffs file system */
645     dfs_register(&dfs_uffs_ops);
646 
647     if (uffs_InitObjectBuf() == U_SUCC)
648     {
649         if (uffs_DirEntryBufInit() == U_SUCC)
650         {
651             uffs_InitGlobalFsLock();
652             return RT_EOK;
653         }
654     }
655     return -RT_ERROR;
656 }
657 INIT_COMPONENT_EXPORT(dfs_uffs_init);
658 
659