1 /*
2  * Copyright (c) 2006-2023, 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_dentry.h>
14 #include <dfs_file.h>
15 #include <dfs_mnt.h>
16 
17 #include "dfs_romfs.h"
18 #include <errno.h>
19 #include <fcntl.h>
20 
21 #include <rtdbg.h>
22 
23 static const struct dfs_file_ops _rom_fops;
24 
25 static const mode_t romfs_modemap[] =
26 {
27     S_IFREG  | 0644,    /* regular file */
28     S_IFDIR  | 0644,    /* directory */
29     0,                  /* hard link */
30     S_IFLNK  | 0777,    /* symlink */
31     S_IFBLK  | 0600,    /* blockdev */
32     S_IFCHR  | 0600,    /* chardev */
33     S_IFSOCK | 0644,    /* socket */
34     S_IFIFO  | 0644     /* FIFO */
35 };
36 
dfs_romfs_mount(struct dfs_mnt * mnt,unsigned long rwflag,const void * data)37 static int dfs_romfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const void *data)
38 {
39     struct romfs_dirent *root_dirent;
40 
41     if (data == NULL)
42         return -1;
43 
44     root_dirent = (struct romfs_dirent *)data;
45     mnt->data = root_dirent;
46 
47     return 0;
48 }
49 
dfs_romfs_umount(struct dfs_mnt * fs)50 static int dfs_romfs_umount(struct dfs_mnt *fs)
51 {
52     return RT_EOK;
53 }
54 
dfs_romfs_ioctl(struct dfs_file * file,int cmd,void * args)55 int dfs_romfs_ioctl(struct dfs_file *file, int cmd, void *args)
56 {
57     int ret = RT_EOK;
58     struct romfs_dirent *dirent;
59 
60     dirent = (struct romfs_dirent *)file->data;
61     RT_ASSERT(dirent != NULL);
62 
63     switch (cmd)
64     {
65     case RT_FIOGETADDR:
66         {
67             *(rt_ubase_t*)args = (rt_ubase_t)dirent->data;
68             break;
69         }
70     case RT_FIOFTRUNCATE:
71         {
72             break;
73         }
74     default:
75         ret = -RT_EINVAL;
76         break;
77     }
78     return ret;
79 }
80 
check_dirent(struct romfs_dirent * dirent)81 rt_inline int check_dirent(struct romfs_dirent *dirent)
82 {
83     if (dirent == NULL
84         ||(dirent->type != ROMFS_DIRENT_FILE && dirent->type != ROMFS_DIRENT_DIR)
85         || dirent->size == ~0)
86         return -1;
87     return 0;
88 }
89 
__dfs_romfs_lookup(struct romfs_dirent * root_dirent,const char * path,rt_size_t * size)90 struct romfs_dirent *__dfs_romfs_lookup(struct romfs_dirent *root_dirent, const char *path, rt_size_t *size)
91 {
92     rt_size_t index, found;
93     const char *subpath, *subpath_end;
94     struct romfs_dirent *dirent;
95     rt_size_t dirent_size;
96 
97     /* Check the root_dirent. */
98     if (check_dirent(root_dirent) != 0)
99         return NULL;
100 
101     if (path[0] == '/' && path[1] == '\0')
102     {
103         *size = root_dirent->size;
104         return root_dirent;
105     }
106 
107     /* goto root directy entries */
108     dirent = (struct romfs_dirent *)root_dirent->data;
109     dirent_size = root_dirent->size;
110 
111     /* get the end position of this subpath */
112     subpath_end = path;
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     while (dirent != NULL)
121     {
122         found = 0;
123 
124         /* search in folder */
125         for (index = 0; index < dirent_size; index ++)
126         {
127             if (check_dirent(&dirent[index]) != 0)
128                 return NULL;
129             if (rt_strlen(dirent[index].name) == (subpath_end - subpath) &&
130                     rt_strncmp(dirent[index].name, subpath, (subpath_end - subpath)) == 0)
131             {
132                 dirent_size = dirent[index].size;
133 
134                 /* skip /// */
135                 while (*subpath_end && *subpath_end == '/')
136                     subpath_end ++;
137                 subpath = subpath_end;
138                 while ((*subpath_end != '/') && *subpath_end)
139                     subpath_end ++;
140 
141                 if (!(*subpath))
142                 {
143                     *size = dirent_size;
144                     return &dirent[index];
145                 }
146 
147                 if (dirent[index].type == ROMFS_DIRENT_DIR)
148                 {
149                     /* enter directory */
150                     dirent = (struct romfs_dirent *)dirent[index].data;
151                     found = 1;
152                     break;
153                 }
154                 else
155                 {
156                     /* return file dirent */
157                     return &dirent[index];
158                 }
159             }
160         }
161 
162         if (!found)
163             break; /* not found */
164     }
165 
166     /* not found */
167     return NULL;
168 }
169 
dfs_romfs_lookup(struct dfs_dentry * dentry)170 static struct dfs_vnode *dfs_romfs_lookup (struct dfs_dentry *dentry)
171 {
172     rt_size_t size;
173     struct dfs_vnode *vnode = RT_NULL;
174     struct romfs_dirent *root_dirent = RT_NULL, *dirent = RT_NULL;
175 
176     RT_ASSERT(dentry != RT_NULL);
177     RT_ASSERT(dentry->mnt != RT_NULL);
178 
179     root_dirent = (struct romfs_dirent *)dentry->mnt->data;
180     if (check_dirent(root_dirent) == 0)
181     {
182         /* create a vnode */
183         DLOG(msg, "rom", "vnode", DLOG_MSG, "dfs_vnode_create()");
184         vnode = dfs_vnode_create();
185         if (vnode)
186         {
187             dirent = __dfs_romfs_lookup(root_dirent, dentry->pathname, &size);
188             if (dirent)
189             {
190                 vnode->nlink = 1;
191                 vnode->size = dirent->size;
192                 if (dirent->type == ROMFS_DIRENT_DIR)
193                 {
194                     vnode->mode = romfs_modemap[ROMFS_DIRENT_DIR] | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
195                     vnode->type = FT_DIRECTORY;
196                 }
197                 else if (dirent->type == ROMFS_DIRENT_FILE)
198                 {
199                     vnode->mode = romfs_modemap[ROMFS_DIRENT_FILE] | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
200                     vnode->type = FT_REGULAR;
201                 }
202 
203                 DLOG(msg, "rom", "rom", DLOG_MSG, "vnode->data = dirent");
204                 vnode->data = dirent;
205                 vnode->mnt = dentry->mnt;
206             }
207             else
208             {
209                 /* no-entry */
210                 DLOG(msg, "rom", "vnode", DLOG_MSG, "dfs_vnode_destroy, no-dentry");
211                 dfs_vnode_destroy(vnode);
212                 vnode = RT_NULL;
213             }
214         }
215     }
216 
217     return vnode;
218 }
219 
dfs_romfs_free_vnode(struct dfs_vnode * vnode)220 static int dfs_romfs_free_vnode(struct dfs_vnode *vnode)
221 {
222     /* nothing to be freed */
223     if (vnode->ref_count <= 1)
224     {
225         vnode->data = NULL;
226     }
227 
228     return 0;
229 }
230 
dfs_romfs_read(struct dfs_file * file,void * buf,size_t count,off_t * pos)231 static ssize_t dfs_romfs_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
232 {
233     rt_size_t length;
234     struct romfs_dirent *dirent;
235 
236     dirent = (struct romfs_dirent *)file->vnode->data;
237     RT_ASSERT(dirent != NULL);
238 
239     if (check_dirent(dirent) != 0)
240     {
241         return -EIO;
242     }
243 
244     if (count < file->vnode->size - *pos)
245         length = count;
246     else
247         length = file->vnode->size - *pos;
248 
249     if (length > 0)
250         memcpy(buf, &(dirent->data[*pos]), length);
251 
252     /* update file current position */
253     *pos += length;
254 
255     return length;
256 }
257 
dfs_romfs_close(struct dfs_file * file)258 static int dfs_romfs_close(struct dfs_file *file)
259 {
260     return RT_EOK;
261 }
262 
dfs_romfs_open(struct dfs_file * file)263 int dfs_romfs_open(struct dfs_file *file)
264 {
265     rt_size_t size;
266     struct romfs_dirent *dirent;
267     struct romfs_dirent *root_dirent;
268     struct dfs_mnt *mnt;
269 
270     if (file->flags & (O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_RDWR))
271     {
272         return -EINVAL;
273     }
274 
275     mnt = file->dentry->mnt;
276     RT_ASSERT(mnt != RT_NULL);
277 
278     root_dirent = (struct romfs_dirent *)mnt->data;
279     if (check_dirent(root_dirent) != 0)
280     {
281         return -EIO;
282     }
283 
284     /* get rom dirent */
285     dirent = __dfs_romfs_lookup(root_dirent, file->dentry->pathname, &size);
286     if (dirent == NULL)
287     {
288         return -ENOENT;
289     }
290 
291     file->data = dirent;
292     file->fops = &_rom_fops;
293     file->fpos = 0;
294 
295     return RT_EOK;
296 }
297 
dfs_romfs_stat(struct dfs_dentry * dentry,struct stat * st)298 static int dfs_romfs_stat(struct dfs_dentry *dentry, struct stat *st)
299 {
300     rt_err_t ret = dfs_file_lock();
301     if (ret == RT_EOK)
302     {
303         st->st_dev = 0;
304         st->st_mode = dentry->vnode->mode;
305         st->st_size = dentry->vnode->size;
306         st->st_nlink = dentry->vnode->nlink;
307         st->st_mtime = 0;
308 
309         dfs_file_unlock();
310     }
311 
312     return RT_EOK;
313 }
314 
dfs_romfs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)315 static int dfs_romfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_t count)
316 {
317     rt_size_t index;
318     const char *name;
319     struct dirent *d;
320     struct romfs_dirent *dirent, *sub_dirent;
321 
322     dirent = (struct romfs_dirent *)file->vnode->data;
323     if (check_dirent(dirent) != 0)
324     {
325         return -EIO;
326     }
327     RT_ASSERT(dirent->type == ROMFS_DIRENT_DIR);
328 
329     /* enter directory */
330     dirent = (struct romfs_dirent *)dirent->data;
331 
332     /* make integer count */
333     count = (count / sizeof(struct dirent));
334     if (count == 0)
335     {
336         return -EINVAL;
337     }
338 
339     index = 0;
340     for (index = 0; index < count && file->fpos < file->vnode->size; index++)
341     {
342         d = dirp + index;
343 
344         sub_dirent = &dirent[file->fpos];
345         name = sub_dirent->name;
346 
347         /* fill dirent */
348         if (sub_dirent->type == ROMFS_DIRENT_DIR)
349             d->d_type = DT_DIR;
350         else
351             d->d_type = DT_REG;
352 
353         d->d_namlen = rt_strlen(name);
354         d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
355         rt_strncpy(d->d_name, name, DIRENT_NAME_MAX);
356 
357         /* move to next position */
358         ++ file->fpos;
359     }
360 
361     return index * sizeof(struct dirent);
362 }
363 
364 static const struct dfs_file_ops _rom_fops =
365 {
366     .open             = dfs_romfs_open,
367     .close            = dfs_romfs_close,
368     .ioctl            = dfs_romfs_ioctl,
369     .lseek            = generic_dfs_lseek,
370     .read             = dfs_romfs_read,
371     .getdents         = dfs_romfs_getdents,
372 };
373 
374 static const struct dfs_filesystem_ops _romfs_ops =
375 {
376     .name             ="rom",
377     .flags            = 0,
378     .default_fops     = &_rom_fops,
379     .mount            = dfs_romfs_mount,
380     .umount           = dfs_romfs_umount,
381     .stat             = dfs_romfs_stat,
382     .lookup           = dfs_romfs_lookup,
383     .free_vnode       = dfs_romfs_free_vnode
384 };
385 
386 static struct dfs_filesystem_type _romfs =
387 {
388     .fs_ops           = &_romfs_ops,
389 };
390 
dfs_romfs_init(void)391 int dfs_romfs_init(void)
392 {
393     /* register rom file system */
394     dfs_register(&_romfs);
395 
396     return 0;
397 }
398 INIT_COMPONENT_EXPORT(dfs_romfs_init);
399