1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  */
9 
10 #include <rtthread.h>
11 #include <dfs.h>
12 #include <dfs_fs.h>
13 #include <dfs_file.h>
14 
15 #include "dfs_romfs.h"
16 
dfs_romfs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)17 int dfs_romfs_mount(struct dfs_filesystem *fs, unsigned long rwflag, const void *data)
18 {
19     struct romfs_dirent *root_dirent;
20 
21     if (data == NULL)
22         return -EIO;
23 
24     root_dirent = (struct romfs_dirent *)data;
25     fs->data = root_dirent;
26 
27     return RT_EOK;
28 }
29 
dfs_romfs_unmount(struct dfs_filesystem * fs)30 int dfs_romfs_unmount(struct dfs_filesystem *fs)
31 {
32     return RT_EOK;
33 }
34 
dfs_romfs_ioctl(struct dfs_file * file,int cmd,void * args)35 int dfs_romfs_ioctl(struct dfs_file *file, int cmd, void *args)
36 {
37     int ret = RT_EOK;
38     struct romfs_dirent *dirent;
39 
40     dirent = (struct romfs_dirent *)file->vnode->data;
41     RT_ASSERT(dirent != NULL);
42 
43     switch (cmd)
44     {
45     case RT_FIOGETADDR:
46         {
47             *(rt_ubase_t*)args = (rt_ubase_t)dirent->data;
48             break;
49         }
50     case RT_FIOFTRUNCATE:
51         {
52             break;
53         }
54     default:
55         ret = -RT_EINVAL;
56         break;
57     }
58     return ret;
59 }
60 
check_dirent(struct romfs_dirent * dirent)61 rt_inline int check_dirent(struct romfs_dirent *dirent)
62 {
63     if ((dirent->type != ROMFS_DIRENT_FILE && dirent->type != ROMFS_DIRENT_DIR)
64         || dirent->size == ~0U)
65         return -1;
66     return 0;
67 }
68 
dfs_romfs_lookup(struct romfs_dirent * root_dirent,const char * path,rt_size_t * size)69 struct romfs_dirent *dfs_romfs_lookup(struct romfs_dirent *root_dirent, const char *path, rt_size_t *size)
70 {
71     rt_size_t index, found;
72     const char *subpath, *subpath_end;
73     struct romfs_dirent *dirent;
74     rt_size_t dirent_size;
75 
76     /* Check the root_dirent. */
77     if (check_dirent(root_dirent) != 0)
78         return NULL;
79 
80     if (path[0] == '/' && path[1] == '\0')
81     {
82         *size = root_dirent->size;
83         return root_dirent;
84     }
85 
86     /* goto root directory entries */
87     dirent = (struct romfs_dirent *)root_dirent->data;
88     dirent_size = root_dirent->size;
89 
90     /* get the end position of this subpath */
91     subpath_end = path;
92     /* skip /// */
93     while (*subpath_end && *subpath_end == '/')
94         subpath_end ++;
95     subpath = subpath_end;
96     while ((*subpath_end != '/') && *subpath_end)
97         subpath_end ++;
98 
99     while (dirent != NULL)
100     {
101         found = 0;
102 
103         /* search in folder */
104         for (index = 0; index < dirent_size; index ++)
105         {
106             if (check_dirent(&dirent[index]) != 0)
107                 return NULL;
108             if (rt_strlen(dirent[index].name) ==  (rt_size_t)(subpath_end - subpath) &&
109                     rt_strncmp(dirent[index].name, subpath, (subpath_end - subpath)) == 0)
110             {
111                 dirent_size = dirent[index].size;
112 
113                 /* skip /// */
114                 while (*subpath_end && *subpath_end == '/')
115                     subpath_end ++;
116                 subpath = subpath_end;
117                 while ((*subpath_end != '/') && *subpath_end)
118                     subpath_end ++;
119 
120                 if (!(*subpath))
121                 {
122                     *size = dirent_size;
123                     return &dirent[index];
124                 }
125 
126                 if (dirent[index].type == ROMFS_DIRENT_DIR)
127                 {
128                     /* enter directory */
129                     dirent = (struct romfs_dirent *)dirent[index].data;
130                     found = 1;
131                     break;
132                 }
133                 else
134                 {
135                     /* return file dirent */
136                     return &dirent[index];
137                 }
138             }
139         }
140 
141         if (!found)
142             break; /* not found */
143     }
144 
145     /* not found */
146     return NULL;
147 }
148 
dfs_romfs_read(struct dfs_file * file,void * buf,size_t count)149 ssize_t dfs_romfs_read(struct dfs_file *file, void *buf, size_t count)
150 {
151     rt_size_t length;
152     struct romfs_dirent *dirent;
153 
154     dirent = (struct romfs_dirent *)file->vnode->data;
155     RT_ASSERT(dirent != NULL);
156 
157     if (check_dirent(dirent) != 0)
158     {
159         return -EIO;
160     }
161 
162     if (count < file->vnode->size - file->pos)
163         length = count;
164     else
165         length = file->vnode->size - file->pos;
166 
167     if (length > 0)
168         rt_memcpy(buf, &(dirent->data[file->pos]), length);
169 
170     /* update file current position */
171     file->pos += length;
172 
173     return length;
174 }
175 
dfs_romfs_lseek(struct dfs_file * file,off_t offset)176 off_t dfs_romfs_lseek(struct dfs_file *file, off_t offset)
177 {
178     if (offset <= file->vnode->size)
179     {
180         file->pos = offset;
181         return file->pos;
182     }
183 
184     return -EIO;
185 }
186 
dfs_romfs_close(struct dfs_file * file)187 int dfs_romfs_close(struct dfs_file *file)
188 {
189     RT_ASSERT(file->vnode->ref_count > 0);
190     if (file->vnode->ref_count > 1)
191     {
192         return RT_EOK;
193     }
194     file->vnode->data = NULL;
195     return RT_EOK;
196 }
197 
dfs_romfs_open(struct dfs_file * file)198 int dfs_romfs_open(struct dfs_file *file)
199 {
200     rt_size_t size;
201     struct romfs_dirent *dirent;
202     struct romfs_dirent *root_dirent;
203     struct dfs_filesystem *fs;
204 
205     if (file->flags & (O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_RDWR))
206     {
207         return -EINVAL;
208     }
209 
210     RT_ASSERT(file->vnode->ref_count > 0);
211     if (file->vnode->ref_count > 1)
212     {
213         if (file->vnode->type == FT_DIRECTORY
214                 && !(file->flags & O_DIRECTORY))
215         {
216             return -ENOENT;
217         }
218         file->pos = 0;
219         return 0;
220     }
221 
222     fs = file->vnode->fs;
223     root_dirent = (struct romfs_dirent *)fs->data;
224 
225     if (check_dirent(root_dirent) != 0)
226     {
227         return -EIO;
228     }
229 
230     if (file->flags & (O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_RDWR))
231     {
232         return -EINVAL;
233     }
234 
235     dirent = dfs_romfs_lookup(root_dirent, file->vnode->path, &size);
236     if (dirent == NULL)
237     {
238         return -ENOENT;
239     }
240 
241     /* entry is a directory file type */
242     if (dirent->type == ROMFS_DIRENT_DIR)
243     {
244         if (!(file->flags & O_DIRECTORY))
245         {
246             return -ENOENT;
247         }
248         file->vnode->type = FT_DIRECTORY;
249     }
250     else
251     {
252         /* entry is a file, but open it as a directory */
253         if (file->flags & O_DIRECTORY)
254         {
255             return -ENOENT;
256         }
257         file->vnode->type = FT_REGULAR;
258     }
259 
260     file->vnode->data = dirent;
261     file->vnode->size = size;
262     file->pos = 0;
263 
264     return RT_EOK;
265 }
266 
dfs_romfs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)267 int dfs_romfs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
268 {
269     rt_size_t size;
270     struct romfs_dirent *dirent;
271     struct romfs_dirent *root_dirent;
272 
273     root_dirent = (struct romfs_dirent *)fs->data;
274     dirent = dfs_romfs_lookup(root_dirent, path, &size);
275 
276     if (dirent == NULL)
277     {
278         return -ENOENT;
279     }
280 
281     st->st_dev = 0;
282     st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
283                   S_IWUSR | S_IWGRP | S_IWOTH;
284 
285     if (dirent->type == ROMFS_DIRENT_DIR)
286     {
287         st->st_mode &= ~S_IFREG;
288         st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
289     }
290 
291     st->st_size = dirent->size;
292     st->st_mtime = 0;
293 
294     return RT_EOK;
295 }
296 
dfs_romfs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)297 int dfs_romfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
298 {
299     rt_size_t index;
300     rt_size_t len;
301     const char *name;
302     struct dirent *d;
303     struct romfs_dirent *dirent, *sub_dirent;
304 
305     dirent = (struct romfs_dirent *)file->vnode->data;
306     if (check_dirent(dirent) != 0)
307     {
308         return -EIO;
309     }
310     RT_ASSERT(dirent->type == ROMFS_DIRENT_DIR);
311 
312     /* enter directory */
313     dirent = (struct romfs_dirent *)dirent->data;
314 
315     /* make integer count */
316     count = (count / sizeof(struct dirent));
317     if (count == 0)
318     {
319         return -EINVAL;
320     }
321 
322     index = 0;
323     for (index = 0; index < count && file->pos < file->vnode->size; index++)
324     {
325         d = dirp + index;
326 
327         sub_dirent = &dirent[file->pos];
328         name = sub_dirent->name;
329 
330         /* fill dirent */
331         if (sub_dirent->type == ROMFS_DIRENT_DIR)
332             d->d_type = DT_DIR;
333         else
334             d->d_type = DT_REG;
335 
336         len = rt_strlen(name);
337         RT_ASSERT(len <= RT_UINT8_MAX);
338         d->d_namlen = (rt_uint8_t)len;
339         d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
340         rt_strncpy(d->d_name, name, DIRENT_NAME_MAX);
341 
342         /* move to next position */
343         ++ file->pos;
344     }
345 
346     return index * sizeof(struct dirent);
347 }
348 
349 static const struct dfs_file_ops _rom_fops =
350 {
351     dfs_romfs_open,
352     dfs_romfs_close,
353     dfs_romfs_ioctl,
354     dfs_romfs_read,
355     NULL,
356     NULL,
357     dfs_romfs_lseek,
358     dfs_romfs_getdents,
359     NULL,
360 };
361 static const struct dfs_filesystem_ops _romfs =
362 {
363     "rom",
364     DFS_FS_FLAG_DEFAULT,
365     &_rom_fops,
366 
367     dfs_romfs_mount,
368     dfs_romfs_unmount,
369     NULL,
370     NULL,
371 
372     NULL,
373     dfs_romfs_stat,
374     NULL,
375 };
376 
dfs_romfs_init(void)377 int dfs_romfs_init(void)
378 {
379     /* register rom file system */
380     dfs_register(&_romfs);
381     return 0;
382 }
383 INIT_COMPONENT_EXPORT(dfs_romfs_init);
384 
385 #ifndef RT_USING_DFS_ROMFS_USER_ROOT
386 static const unsigned char _dummy_dummy_txt[] =
387 {
388     0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x21, 0x0d, 0x0a,
389 };
390 
391 static const struct romfs_dirent _dummy[] =
392 {
393     {ROMFS_DIRENT_FILE, "dummy.txt", _dummy_dummy_txt, sizeof(_dummy_dummy_txt)},
394 };
395 
396 static const unsigned char _dummy_txt[] =
397 {
398     0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x21, 0x0d, 0x0a,
399 };
400 
401 rt_weak const struct romfs_dirent _root_dirent[] =
402 {
403     {ROMFS_DIRENT_DIR, "dummy", (rt_uint8_t *)_dummy, sizeof(_dummy) / sizeof(_dummy[0])},
404     {ROMFS_DIRENT_FILE, "dummy.txt", _dummy_txt, sizeof(_dummy_txt)},
405 };
406 
407 rt_weak const struct romfs_dirent romfs_root =
408 {
409     ROMFS_DIRENT_DIR, "/", (rt_uint8_t *)_root_dirent, sizeof(_root_dirent) / sizeof(_root_dirent[0])
410 };
411 #endif
412