1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2013-04-15     Bernard      the first version
9  * 2013-05-05     Bernard      remove CRC for ramfs persistence
10  * 2013-05-22     Bernard      fix the no entry issue.
11  */
12 
13 #include <rtthread.h>
14 #include <dfs.h>
15 #include <dfs_fs.h>
16 #include <dfs_file.h>
17 
18 #include "dfs_ramfs.h"
19 
dfs_ramfs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)20 int dfs_ramfs_mount(struct dfs_filesystem *fs,
21                     unsigned long          rwflag,
22                     const void            *data)
23 {
24     struct dfs_ramfs *ramfs;
25 
26     if (data == NULL)
27         return -EIO;
28 
29     ramfs = (struct dfs_ramfs *)data;
30     fs->data = ramfs;
31 
32     return RT_EOK;
33 }
34 
dfs_ramfs_unmount(struct dfs_filesystem * fs)35 int dfs_ramfs_unmount(struct dfs_filesystem *fs)
36 {
37     fs->data = NULL;
38 
39     return RT_EOK;
40 }
41 
dfs_ramfs_statfs(struct dfs_filesystem * fs,struct statfs * buf)42 int dfs_ramfs_statfs(struct dfs_filesystem *fs, struct statfs *buf)
43 {
44     struct dfs_ramfs *ramfs;
45 
46     ramfs = (struct dfs_ramfs *)fs->data;
47     RT_ASSERT(ramfs != NULL);
48     RT_ASSERT(buf != NULL);
49 
50     buf->f_bsize  = 512;
51     buf->f_blocks = ramfs->memheap.pool_size / 512;
52     buf->f_bfree  = ramfs->memheap.available_size / 512;
53 
54     return RT_EOK;
55 }
56 
dfs_ramfs_ioctl(struct dfs_file * file,int cmd,void * args)57 int dfs_ramfs_ioctl(struct dfs_file *file, int cmd, void *args)
58 {
59     return -EIO;
60 }
61 
dfs_ramfs_lookup(struct dfs_ramfs * ramfs,const char * path,rt_size_t * size)62 struct ramfs_dirent *dfs_ramfs_lookup(struct dfs_ramfs *ramfs,
63                                       const char       *path,
64                                       rt_size_t        *size)
65 {
66     const char *subpath;
67     struct ramfs_dirent *dirent;
68 
69     subpath = path;
70     while (*subpath == '/' && *subpath)
71         subpath ++;
72     if (! *subpath) /* is root directory */
73     {
74         *size = 0;
75 
76         return &(ramfs->root);
77     }
78 
79     for (dirent = rt_list_entry(ramfs->root.list.next, struct ramfs_dirent, list);
80          dirent != &(ramfs->root);
81          dirent = rt_list_entry(dirent->list.next, struct ramfs_dirent, list))
82     {
83         if (rt_strcmp(dirent->name, subpath) == 0)
84         {
85             *size = dirent->size;
86 
87             return dirent;
88         }
89     }
90 
91     /* not found */
92     return NULL;
93 }
94 
dfs_ramfs_read(struct dfs_file * file,void * buf,size_t count)95 int dfs_ramfs_read(struct dfs_file *file, void *buf, size_t count)
96 {
97     rt_size_t length;
98     struct ramfs_dirent *dirent;
99 
100     dirent = (struct ramfs_dirent *)file->vnode->data;
101     RT_ASSERT(dirent != NULL);
102 
103     if (count < file->vnode->size - file->pos)
104         length = count;
105     else
106         length = file->vnode->size - file->pos;
107 
108     if (length > 0)
109         rt_memcpy(buf, &(dirent->data[file->pos]), length);
110 
111     /* update file current position */
112     file->pos += length;
113 
114     return length;
115 }
116 
dfs_ramfs_write(struct dfs_file * fd,const void * buf,size_t count)117 int dfs_ramfs_write(struct dfs_file *fd, const void *buf, size_t count)
118 {
119     struct ramfs_dirent *dirent;
120     struct dfs_ramfs *ramfs;
121 
122     dirent = (struct ramfs_dirent *)fd->vnode->data;
123     RT_ASSERT(dirent != NULL);
124 
125     ramfs = dirent->fs;
126     RT_ASSERT(ramfs != NULL);
127 
128     if (count + fd->pos > fd->vnode->size)
129     {
130         rt_uint8_t *ptr;
131         ptr = rt_memheap_realloc(&(ramfs->memheap), dirent->data, fd->pos + count);
132         if (ptr == NULL)
133         {
134             rt_set_errno(-ENOMEM);
135 
136             return 0;
137         }
138 
139         /* update dirent and file size */
140         dirent->data = ptr;
141         dirent->size = fd->pos + count;
142         fd->vnode->size = dirent->size;
143     }
144 
145     if (count > 0)
146         rt_memcpy(dirent->data + fd->pos, buf, count);
147 
148     /* update file current position */
149     fd->pos += count;
150 
151     return count;
152 }
153 
dfs_ramfs_lseek(struct dfs_file * file,off_t offset)154 int dfs_ramfs_lseek(struct dfs_file *file, off_t offset)
155 {
156     if (offset <= (off_t)file->vnode->size)
157     {
158         file->pos = offset;
159 
160         return file->pos;
161     }
162 
163     return -EIO;
164 }
165 
dfs_ramfs_close(struct dfs_file * file)166 int dfs_ramfs_close(struct dfs_file *file)
167 {
168     RT_ASSERT(file->vnode->ref_count > 0);
169     if (file->vnode->ref_count > 1)
170     {
171         return 0;
172     }
173 
174     file->vnode->data = NULL;
175 
176     return RT_EOK;
177 }
178 
dfs_ramfs_open(struct dfs_file * file)179 int dfs_ramfs_open(struct dfs_file *file)
180 {
181     rt_size_t size;
182     struct dfs_ramfs *ramfs;
183     struct ramfs_dirent *dirent;
184     struct dfs_filesystem *fs;
185 
186     RT_ASSERT(file->vnode->ref_count > 0);
187     if (file->vnode->ref_count > 1)
188     {
189         if (file->vnode->type == FT_DIRECTORY
190                 && !(file->flags & O_DIRECTORY))
191         {
192             return -ENOENT;
193         }
194         file->pos = 0;
195         return 0;
196     }
197 
198     fs = file->vnode->fs;
199 
200     ramfs = (struct dfs_ramfs *)fs->data;
201     RT_ASSERT(ramfs != NULL);
202 
203     if (file->flags & O_DIRECTORY)
204     {
205         if (file->flags & O_CREAT)
206         {
207             return -ENOSPC;
208         }
209 
210         /* open directory */
211         dirent = dfs_ramfs_lookup(ramfs, file->vnode->path, &size);
212         if (dirent == NULL)
213             return -ENOENT;
214         if (dirent == &(ramfs->root)) /* it's root directory */
215         {
216             if (!(file->flags & O_DIRECTORY))
217             {
218                 return -ENOENT;
219             }
220         }
221         file->vnode->type = FT_DIRECTORY;
222     }
223     else
224     {
225         dirent = dfs_ramfs_lookup(ramfs, file->vnode->path, &size);
226         if (dirent == &(ramfs->root)) /* it's root directory */
227         {
228             return -ENOENT;
229         }
230 
231         if (dirent == NULL)
232         {
233             if (file->flags & O_CREAT || file->flags & O_WRONLY)
234             {
235                 char *name_ptr;
236 
237                 /* create a file entry */
238                 dirent = (struct ramfs_dirent *)
239                          rt_memheap_alloc(&(ramfs->memheap),
240                                           sizeof(struct ramfs_dirent));
241                 if (dirent == NULL)
242                 {
243                     return -ENOMEM;
244                 }
245 
246                 /* remove '/' separator */
247                 name_ptr = file->vnode->path;
248                 while (*name_ptr == '/' && *name_ptr)
249                 {
250                     name_ptr++;
251                 }
252                 strncpy(dirent->name, name_ptr, RAMFS_NAME_MAX);
253 
254                 rt_list_init(&(dirent->list));
255                 dirent->data = NULL;
256                 dirent->size = 0;
257                 dirent->fs = ramfs;
258                 file->vnode->type = FT_DIRECTORY;
259 
260                 /* add to the root directory */
261                 rt_list_insert_after(&(ramfs->root.list), &(dirent->list));
262             }
263             else
264                 return -ENOENT;
265         }
266 
267         /* Creates a new file.
268          * If the file is existing, it is truncated and overwritten.
269          */
270         if (file->flags & O_TRUNC)
271         {
272             dirent->size = 0;
273             if (dirent->data != NULL)
274             {
275                 rt_memheap_free(dirent->data);
276                 dirent->data = NULL;
277             }
278         }
279     }
280 
281     file->vnode->data = dirent;
282     file->vnode->size = dirent->size;
283     if (file->flags & O_APPEND)
284     {
285         file->pos = file->vnode->size;
286     }
287     else
288     {
289         file->pos = 0;
290     }
291 
292     return 0;
293 }
294 
dfs_ramfs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)295 int dfs_ramfs_stat(struct dfs_filesystem *fs,
296                    const char            *path,
297                    struct stat           *st)
298 {
299     rt_size_t size;
300     struct ramfs_dirent *dirent;
301     struct dfs_ramfs *ramfs;
302 
303     ramfs = (struct dfs_ramfs *)fs->data;
304     dirent = dfs_ramfs_lookup(ramfs, path, &size);
305 
306     if (dirent == NULL)
307         return -ENOENT;
308 
309     st->st_dev = 0;
310     st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
311                   S_IWUSR | S_IWGRP | S_IWOTH;
312 
313     st->st_size = dirent->size;
314     st->st_mtime = 0;
315 
316     return RT_EOK;
317 }
318 
dfs_ramfs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)319 int dfs_ramfs_getdents(struct dfs_file *file,
320                        struct dirent *dirp,
321                        uint32_t    count)
322 {
323     rt_size_t index, end;
324     struct dirent *d;
325     struct ramfs_dirent *dirent;
326     struct dfs_ramfs *ramfs;
327 
328     dirent = (struct ramfs_dirent *)file->vnode->data;
329 
330     ramfs  = dirent->fs;
331     RT_ASSERT(ramfs != RT_NULL);
332 
333     if (dirent != &(ramfs->root))
334         return -EINVAL;
335 
336     /* make integer count */
337     count = (count / sizeof(struct dirent));
338     if (count == 0)
339         return -EINVAL;
340 
341     end = file->pos + count;
342     index = 0;
343     count = 0;
344     for (dirent = rt_list_entry(dirent->list.next, struct ramfs_dirent, list);
345          dirent != &(ramfs->root) && index < end;
346          dirent = rt_list_entry(dirent->list.next, struct ramfs_dirent, list))
347     {
348         if (index >= (rt_size_t)file->pos)
349         {
350             d = dirp + count;
351             d->d_type = DT_REG;
352             d->d_namlen = RT_NAME_MAX;
353             d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
354             rt_strncpy(d->d_name, dirent->name, RAMFS_NAME_MAX);
355 
356             count += 1;
357             file->pos += 1;
358         }
359         index += 1;
360     }
361 
362     return count * sizeof(struct dirent);
363 }
364 
dfs_ramfs_unlink(struct dfs_filesystem * fs,const char * path)365 int dfs_ramfs_unlink(struct dfs_filesystem *fs, const char *path)
366 {
367     rt_size_t size;
368     struct dfs_ramfs *ramfs;
369     struct ramfs_dirent *dirent;
370 
371     ramfs = (struct dfs_ramfs *)fs->data;
372     RT_ASSERT(ramfs != NULL);
373 
374     dirent = dfs_ramfs_lookup(ramfs, path, &size);
375     if (dirent == NULL)
376         return -ENOENT;
377 
378     rt_list_remove(&(dirent->list));
379     if (dirent->data != NULL)
380         rt_memheap_free(dirent->data);
381     rt_memheap_free(dirent);
382 
383     return RT_EOK;
384 }
385 
dfs_ramfs_rename(struct dfs_filesystem * fs,const char * oldpath,const char * newpath)386 int dfs_ramfs_rename(struct dfs_filesystem *fs,
387                      const char            *oldpath,
388                      const char            *newpath)
389 {
390     struct ramfs_dirent *dirent;
391     struct dfs_ramfs *ramfs;
392     rt_size_t size;
393 
394     ramfs = (struct dfs_ramfs *)fs->data;
395     RT_ASSERT(ramfs != NULL);
396 
397     dirent = dfs_ramfs_lookup(ramfs, newpath, &size);
398     if (dirent != NULL)
399         return -EEXIST;
400 
401     dirent = dfs_ramfs_lookup(ramfs, oldpath, &size);
402     if (dirent == NULL)
403         return -ENOENT;
404 
405     strncpy(dirent->name, newpath, RAMFS_NAME_MAX);
406 
407     return RT_EOK;
408 }
409 
410 static const struct dfs_file_ops _ram_fops =
411 {
412     dfs_ramfs_open,
413     dfs_ramfs_close,
414     dfs_ramfs_ioctl,
415     dfs_ramfs_read,
416     dfs_ramfs_write,
417     NULL, /* flush */
418     dfs_ramfs_lseek,
419     dfs_ramfs_getdents,
420 };
421 
422 static const struct dfs_filesystem_ops _ramfs =
423 {
424     "ram",
425     DFS_FS_FLAG_DEFAULT,
426     &_ram_fops,
427 
428     dfs_ramfs_mount,
429     dfs_ramfs_unmount,
430     NULL, /* mkfs */
431     dfs_ramfs_statfs,
432 
433     dfs_ramfs_unlink,
434     dfs_ramfs_stat,
435     dfs_ramfs_rename,
436 };
437 
dfs_ramfs_init(void)438 int dfs_ramfs_init(void)
439 {
440     /* register ram file system */
441     dfs_register(&_ramfs);
442 
443     return 0;
444 }
445 INIT_COMPONENT_EXPORT(dfs_ramfs_init);
446 
dfs_ramfs_create(rt_uint8_t * pool,rt_size_t size)447 struct dfs_ramfs *dfs_ramfs_create(rt_uint8_t *pool, rt_size_t size)
448 {
449     struct dfs_ramfs *ramfs;
450     rt_uint8_t *data_ptr;
451     rt_err_t result;
452 
453     size  = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
454     ramfs = (struct dfs_ramfs *)pool;
455 
456     data_ptr = (rt_uint8_t *)(ramfs + 1);
457     size = size - sizeof(struct dfs_ramfs);
458     size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
459 
460     result = rt_memheap_init(&ramfs->memheap, "ramfs", data_ptr, size);
461     if (result != RT_EOK)
462         return NULL;
463     /* detach this memheap object from the system */
464     rt_object_detach((rt_object_t) & (ramfs->memheap));
465 
466     /* initialize ramfs object */
467     ramfs->magic = RAMFS_MAGIC;
468     ramfs->memheap.parent.type = RT_Object_Class_MemHeap | RT_Object_Class_Static;
469 
470     /* initialize root directory */
471     rt_memset(&(ramfs->root), 0x00, sizeof(ramfs->root));
472     rt_list_init(&(ramfs->root.list));
473     ramfs->root.size = 0;
474     strcpy(ramfs->root.name, ".");
475     ramfs->root.fs = ramfs;
476 
477     return ramfs;
478 }
479 
480