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 * 2022-10-24 flybreak the first version
9 * 2023-02-01 xqyjlj fix cannot open the same file repeatedly in 'w' mode
10 */
11
12 #include <rthw.h>
13 #include <rtthread.h>
14 #include <dfs.h>
15 #include <dfs_fs.h>
16 #include <dfs_file.h>
17
18 #ifdef RT_USING_SMART
19 #include <lwp.h>
20 #include <lwp_user_mm.h>
21 #endif
22
23 #include "dfs_tmpfs.h"
24
25 #define DBG_TAG "tmpfs"
26 #define DBG_LVL DBG_INFO
27 #include <rtdbg.h>
28
_path_separate(const char * path,char * parent_path,char * file_name)29 static int _path_separate(const char *path, char *parent_path, char *file_name)
30 {
31 const char *path_p, *path_q;
32
33 RT_ASSERT(path[0] == '/');
34
35 file_name[0] = '\0';
36 path_p = path_q = &path[1];
37 __next_dir:
38 while (*path_q != '/' && *path_q != '\0')
39 {
40 path_q++;
41 }
42 if (path_q != path_p) /*sub dir*/
43 {
44 if (*path_q != '\0')
45 {
46 path_q++;
47 path_p = path_q;
48 goto __next_dir;
49 }
50 else /* Last level dir */
51 {
52 rt_memcpy(parent_path, path, path_p - path - 1);
53 parent_path[path_p - path - 1] = '\0';
54 rt_memcpy(file_name, path_p, path_q - path_p);
55 file_name[path_q - path_p] = '\0';
56 }
57 }
58 if (parent_path[0] == 0)
59 {
60 parent_path[0] = '/';
61 parent_path[1] = '\0';
62 }
63 LOG_D("parent_path: %s", parent_path);
64 LOG_D("file_name: %s", file_name);
65
66 return 0;
67 }
68
_get_subdir(const char * path,char * name)69 static int _get_subdir(const char *path, char *name)
70 {
71 const char *subpath = path;
72 while (*subpath == '/' && *subpath)
73 subpath ++;
74 while (*subpath != '/' && *subpath)
75 {
76 *name = *subpath;
77 name ++;
78 subpath ++;
79 }
80 return 0;
81 }
82
_free_subdir(struct tmpfs_file * dfile)83 static int _free_subdir(struct tmpfs_file *dfile)
84 {
85 struct tmpfs_file *file;
86 rt_list_t *list, *temp_list;
87 struct tmpfs_sb *superblock;
88
89 RT_ASSERT(dfile->type == TMPFS_TYPE_DIR);
90
91 rt_list_for_each_safe(list, temp_list, &dfile->subdirs)
92 {
93 file = rt_list_entry(list, struct tmpfs_file, sibling);
94 if (file->type == TMPFS_TYPE_DIR)
95 {
96 _free_subdir(file);
97 }
98 if (file->data != NULL)
99 {
100 /* TODO: fix for rt-smart */
101 rt_free(file->data);
102 }
103
104 superblock = file->sb;
105 RT_ASSERT(superblock != NULL);
106
107 rt_spin_lock(&superblock->lock);
108 rt_list_remove(&(file->sibling));
109 rt_spin_unlock(&superblock->lock);
110
111 rt_free(file);
112 }
113 return 0;
114 }
115
dfs_tmpfs_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)116 int dfs_tmpfs_mount(struct dfs_filesystem *fs,
117 unsigned long rwflag,
118 const void *data)
119 {
120 struct tmpfs_sb *superblock;
121
122 superblock = rt_calloc(1, sizeof(struct tmpfs_sb));
123 if (superblock)
124 {
125 superblock->df_size = sizeof(struct tmpfs_sb);
126 superblock->magic = TMPFS_MAGIC;
127 rt_list_init(&superblock->sibling);
128
129 superblock->root.name[0] = '/';
130 superblock->root.sb = superblock;
131 superblock->root.type = TMPFS_TYPE_DIR;
132 rt_list_init(&superblock->root.sibling);
133 rt_list_init(&superblock->root.subdirs);
134
135 rt_spin_lock_init(&superblock->lock);
136
137 fs->data = superblock;
138 }
139 else
140 {
141 return -1;
142 }
143
144 return RT_EOK;
145 }
146
dfs_tmpfs_unmount(struct dfs_filesystem * fs)147 int dfs_tmpfs_unmount(struct dfs_filesystem *fs)
148 {
149 struct tmpfs_sb *superblock;
150
151 superblock = (struct tmpfs_sb *)fs->data;
152 RT_ASSERT(superblock != NULL);
153
154 _free_subdir(&(superblock->root));
155 rt_free(superblock);
156
157 fs->data = NULL;
158
159 return RT_EOK;
160 }
161
dfs_tmpfs_statfs(struct dfs_filesystem * fs,struct statfs * buf)162 int dfs_tmpfs_statfs(struct dfs_filesystem *fs, struct statfs *buf)
163 {
164 struct tmpfs_sb *superblock;
165
166 superblock = (struct tmpfs_sb *)fs->data;
167 RT_ASSERT(superblock != NULL);
168 RT_ASSERT(buf != NULL);
169
170 buf->f_bsize = 512;
171 buf->f_blocks = (superblock->df_size + 511) / 512;
172 buf->f_bfree = 1;
173 buf->f_bavail = buf->f_bfree;
174
175 return RT_EOK;
176 }
177
dfs_tmpfs_ioctl(struct dfs_file * file,int cmd,void * args)178 int dfs_tmpfs_ioctl(struct dfs_file *file, int cmd, void *args)
179 {
180 struct tmpfs_file *d_file;
181 struct tmpfs_sb *superblock;
182
183 d_file = (struct tmpfs_file *)file->vnode->data;
184 RT_ASSERT(d_file != NULL);
185
186 superblock = d_file->sb;
187 RT_ASSERT(superblock != NULL);
188
189 switch (cmd)
190 {
191 #ifdef RT_USING_SMART
192 case RT_FIOMMAP2:
193 {
194 struct dfs_mmap2_args *mmap2 = (struct dfs_mmap2_args *)args;
195 if (mmap2)
196 {
197 if (mmap2->length > file->vnode->size)
198 {
199 return -RT_ENOMEM;
200 }
201 else if (mmap2->lwp == RT_NULL)
202 return -RT_EINVAL;
203
204 LOG_D("tmpfile mmap ptr:%x , size:%d\n", d_file->data, mmap2->length);
205 mmap2->ret = lwp_map_user_phy(mmap2->lwp, mmap2->addr, d_file->data, mmap2->length, 0);
206 }
207 return RT_EOK;
208 break;
209 }
210 #endif
211 default:
212 break;
213 }
214 return -EIO;
215 }
216
dfs_tmpfs_lookup(struct tmpfs_sb * superblock,const char * path,rt_size_t * size)217 struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock,
218 const char *path,
219 rt_size_t *size)
220 {
221 const char *subpath, *curpath, *filename = RT_NULL;
222 char subdir_name[TMPFS_NAME_MAX];
223 struct tmpfs_file *file, *curfile;
224 rt_list_t *list;
225
226 subpath = path;
227 while (*subpath == '/' && *subpath)
228 subpath ++;
229 if (! *subpath) /* is root directory */
230 {
231 *size = 0;
232 return &(superblock->root);
233 }
234
235 curpath = subpath;
236 curfile = &superblock->root;
237
238 find_subpath:
239 while (*subpath != '/' && *subpath)
240 subpath ++;
241
242 if (! *subpath) /* is last directory */
243 filename = curpath;
244 else
245 subpath ++; /* skip '/' */
246
247 memset(subdir_name, 0, TMPFS_NAME_MAX);
248 _get_subdir(curpath, subdir_name);
249
250 rt_spin_lock(&superblock->lock);
251
252 rt_list_for_each(list, &curfile->subdirs)
253 {
254 file = rt_list_entry(list, struct tmpfs_file, sibling);
255 if (filename) /* find file */
256 {
257 if (rt_strcmp(file->name, filename) == 0)
258 {
259 *size = file->size;
260
261 rt_spin_unlock(&superblock->lock);
262 return file;
263 }
264 }
265 else if (rt_strcmp(file->name, subdir_name) == 0)
266 {
267 *size = file->size;
268 curpath = subpath;
269 curfile = file;
270 rt_spin_unlock(&superblock->lock);
271 goto find_subpath;
272 }
273 }
274 rt_spin_unlock(&superblock->lock);
275 /* not found */
276 return NULL;
277 }
278
dfs_tmpfs_read(struct dfs_file * file,void * buf,size_t count)279 ssize_t dfs_tmpfs_read(struct dfs_file *file, void *buf, size_t count)
280 {
281 rt_size_t length;
282 struct tmpfs_file *d_file;
283
284 d_file = (struct tmpfs_file *)file->vnode->data;
285 RT_ASSERT(d_file != NULL);
286
287 if (count < file->vnode->size - file->pos)
288 length = count;
289 else
290 length = file->vnode->size - file->pos;
291
292 if (length > 0)
293 memcpy(buf, &(d_file->data[file->pos]), length);
294
295 /* update file current position */
296 file->pos += length;
297
298 return length;
299 }
300
301
dfs_tmpfs_write(struct dfs_file * fd,const void * buf,size_t count)302 ssize_t dfs_tmpfs_write(struct dfs_file *fd, const void *buf, size_t count)
303 {
304 struct tmpfs_file *d_file;
305 struct tmpfs_sb *superblock;
306
307 d_file = (struct tmpfs_file *)fd->vnode->data;
308 RT_ASSERT(d_file != NULL);
309
310 superblock = d_file->sb;
311 RT_ASSERT(superblock != NULL);
312
313 if (count + fd->pos > fd->vnode->size)
314 {
315 rt_uint8_t *ptr;
316 ptr = rt_realloc(d_file->data, fd->pos + count);
317 if (ptr == NULL)
318 {
319 rt_set_errno(-ENOMEM);
320 return 0;
321 }
322
323 superblock->df_size += (fd->pos - d_file->size + count);
324 /* update d_file and file size */
325 d_file->data = ptr;
326 d_file->size = fd->pos + count;
327 fd->vnode->size = d_file->size;
328 LOG_D("tmpfile ptr:%x, size:%d", ptr, d_file->size);
329 }
330
331 if (count > 0)
332 memcpy(d_file->data + fd->pos, buf, count);
333
334 /* update file current position */
335 fd->pos += count;
336
337 return count;
338 }
339
dfs_tmpfs_lseek(struct dfs_file * file,off_t offset)340 off_t dfs_tmpfs_lseek(struct dfs_file *file, off_t offset)
341 {
342 if (offset <= (off_t)file->vnode->size)
343 {
344 file->pos = offset;
345
346 return file->pos;
347 }
348
349 return -EIO;
350 }
351
dfs_tmpfs_close(struct dfs_file * file)352 int dfs_tmpfs_close(struct dfs_file *file)
353 {
354 RT_ASSERT(file->vnode->ref_count > 0);
355 if (file->vnode->ref_count > 1)
356 {
357 return 0;
358 }
359
360 file->vnode->data = NULL;
361
362 return RT_EOK;
363 }
364
dfs_tmpfs_open(struct dfs_file * file)365 int dfs_tmpfs_open(struct dfs_file *file)
366 {
367 rt_size_t size;
368 struct tmpfs_sb *superblock;
369 struct tmpfs_file *d_file, *p_file;
370 struct dfs_filesystem *fs;
371 char parent_path[DFS_PATH_MAX],file_name[TMPFS_NAME_MAX];
372
373 RT_ASSERT(file->vnode->ref_count > 0);
374 if (file->vnode->ref_count > 1)
375 {
376 if (file->vnode->type == FT_DIRECTORY
377 && !(file->flags & O_DIRECTORY))
378 {
379 return -ENOENT;
380 }
381 file->pos = 0;
382 return 0;
383 }
384
385 fs = file->vnode->fs;
386
387 superblock = (struct tmpfs_sb *)fs->data;
388 RT_ASSERT(superblock != NULL);
389
390 /* find file */
391 d_file = dfs_tmpfs_lookup(superblock, file->vnode->path, &size);
392 if (d_file == NULL && !(file->flags & O_CREAT))
393 return -ENOENT;
394
395 /* Creates a new file. */
396 if (file->flags & O_CREAT)
397 {
398 if (d_file == NULL)
399 {
400 /* find parent file */
401 _path_separate(file->vnode->path, parent_path, file_name);
402 if (file_name[0] == '\0') /* it's root dir */
403 return -ENOENT;
404
405 /* open parent directory */
406 p_file = dfs_tmpfs_lookup(superblock, parent_path, &size);
407 if (p_file == NULL)
408 return -ENOENT;
409
410 /* create a file entry */
411 d_file = (struct tmpfs_file *)rt_calloc(1, sizeof(struct tmpfs_file));
412 if (d_file == NULL)
413 {
414 return -ENOMEM;
415 }
416 superblock->df_size += sizeof(struct tmpfs_file);
417
418 strncpy(d_file->name, file_name, TMPFS_NAME_MAX);
419
420 rt_list_init(&(d_file->subdirs));
421 rt_list_init(&(d_file->sibling));
422 d_file->data = NULL;
423 d_file->size = 0;
424 d_file->sb = superblock;
425 if (file->flags & O_DIRECTORY)
426 {
427 d_file->type = TMPFS_TYPE_DIR;
428 }
429 else
430 {
431 d_file->type = TMPFS_TYPE_FILE;
432 }
433 rt_spin_lock(&superblock->lock);
434 rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
435 rt_spin_unlock(&superblock->lock);
436 }
437 }
438 /* Creates a new file.
439 * If the file is existing, it is truncated and overwritten.
440 */
441 if (file->flags & O_TRUNC)
442 {
443 d_file->size = 0;
444 if (d_file->data != NULL)
445 {
446 /* ToDo: fix for rt-smart. */
447 rt_free(d_file->data);
448 d_file->data = NULL;
449 }
450 }
451
452 /* fill file */
453 if (d_file->type == TMPFS_TYPE_DIR)
454 {
455 if (file->flags & O_DIRECTORY)
456 file->vnode->type = FT_DIRECTORY;
457 else
458 return -ENOMEM;
459 }
460 else
461 {
462 if (file->flags & O_DIRECTORY)
463 {
464 return -ENOMEM;
465 }
466 file->vnode->type = FT_DEVICE;
467 }
468
469 file->vnode->data = d_file;
470 file->vnode->size = d_file->size;
471 if (file->flags & O_APPEND)
472 {
473 file->pos = file->vnode->size;
474 }
475 else
476 {
477 file->pos = 0;
478 }
479
480 return 0;
481 }
482
dfs_tmpfs_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)483 int dfs_tmpfs_stat(struct dfs_filesystem *fs,
484 const char *path,
485 struct stat *st)
486 {
487 rt_size_t size;
488 struct tmpfs_file *d_file;
489 struct tmpfs_sb *superblock;
490
491 superblock = (struct tmpfs_sb *)fs->data;
492 d_file = dfs_tmpfs_lookup(superblock, path, &size);
493
494 if (d_file == NULL)
495 return -ENOENT;
496
497 st->st_dev = (dev_t)dfs_filesystem_lookup(fs->path);
498 st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
499 S_IWUSR | S_IWGRP | S_IWOTH;
500 if (d_file->type == TMPFS_TYPE_DIR)
501 {
502 st->st_mode &= ~S_IFREG;
503 st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
504 }
505
506 st->st_size = d_file->size;
507 st->st_mtime = 0;
508
509 return RT_EOK;
510 }
511
dfs_tmpfs_getdents(struct dfs_file * file,struct dirent * dirp,uint32_t count)512 int dfs_tmpfs_getdents(struct dfs_file *file,
513 struct dirent *dirp,
514 uint32_t count)
515 {
516 rt_size_t index, end;
517 struct dirent *d;
518 struct tmpfs_file *d_file, *n_file;
519 rt_list_t *list;
520 struct tmpfs_sb *superblock;
521
522 d_file = (struct tmpfs_file *)file->vnode->data;
523
524 superblock = d_file->sb;
525 RT_ASSERT(superblock != RT_NULL);
526
527 /* make integer count */
528 count = (count / sizeof(struct dirent));
529 if (count == 0)
530 return -EINVAL;
531
532 end = file->pos + count;
533 index = 0;
534 count = 0;
535
536 rt_list_for_each(list, &d_file->subdirs)
537 {
538 n_file = rt_list_entry(list, struct tmpfs_file, sibling);
539 if (index >= (rt_size_t)file->pos)
540 {
541 d = dirp + count;
542 if (d_file->type == TMPFS_TYPE_FILE)
543 {
544 d->d_type = DT_REG;
545 }
546 if (d_file->type == TMPFS_TYPE_DIR)
547 {
548 d->d_type = DT_DIR;
549 }
550 d->d_namlen = RT_NAME_MAX;
551 d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
552 rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX);
553
554 count += 1;
555 file->pos += 1;
556 }
557 index += 1;
558 if (index >= end)
559 {
560 break;
561 }
562 }
563
564 return count * sizeof(struct dirent);
565 }
566
dfs_tmpfs_unlink(struct dfs_filesystem * fs,const char * path)567 int dfs_tmpfs_unlink(struct dfs_filesystem *fs, const char *path)
568 {
569 rt_size_t size;
570 struct tmpfs_sb *superblock;
571 struct tmpfs_file *d_file;
572
573 superblock = (struct tmpfs_sb *)fs->data;
574 RT_ASSERT(superblock != NULL);
575
576 d_file = dfs_tmpfs_lookup(superblock, path, &size);
577 if (d_file == NULL)
578 return -ENOENT;
579
580 rt_spin_lock(&superblock->lock);
581 rt_list_remove(&(d_file->sibling));
582 rt_spin_unlock(&superblock->lock);
583
584 if (d_file->data != NULL)
585 rt_free(d_file->data);
586 rt_free(d_file);
587
588 return RT_EOK;
589 }
590
dfs_tmpfs_rename(struct dfs_filesystem * fs,const char * oldpath,const char * newpath)591 int dfs_tmpfs_rename(struct dfs_filesystem *fs,
592 const char *oldpath,
593 const char *newpath)
594 {
595 struct tmpfs_file *d_file, *p_file;
596 struct tmpfs_sb *superblock;
597 rt_size_t size;
598 char parent_path[DFS_PATH_MAX],file_name[TMPFS_NAME_MAX];
599
600 superblock = (struct tmpfs_sb *)fs->data;
601 RT_ASSERT(superblock != NULL);
602
603 d_file = dfs_tmpfs_lookup(superblock, newpath, &size);
604 if (d_file != NULL)
605 return -EEXIST;
606
607 d_file = dfs_tmpfs_lookup(superblock, oldpath, &size);
608 if (d_file == NULL)
609 return -ENOENT;
610
611 /* find parent file */
612 _path_separate(newpath, parent_path, file_name);
613 if (file_name[0] == '\0') /* it's root dir */
614 return -ENOENT;
615 /* open parent directory */
616 p_file = dfs_tmpfs_lookup(superblock, parent_path, &size);
617 RT_ASSERT(p_file != NULL);
618
619 rt_spin_lock(&superblock->lock);
620 rt_list_remove(&(d_file->sibling));
621 rt_spin_unlock(&superblock->lock);
622
623 strncpy(d_file->name, file_name, TMPFS_NAME_MAX);
624
625 rt_spin_lock(&superblock->lock);
626 rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling));
627 rt_spin_unlock(&superblock->lock);
628
629 return RT_EOK;
630 }
631
632 static const struct dfs_file_ops _tmp_fops =
633 {
634 dfs_tmpfs_open,
635 dfs_tmpfs_close,
636 dfs_tmpfs_ioctl,
637 dfs_tmpfs_read,
638 dfs_tmpfs_write,
639 NULL, /* flush */
640 dfs_tmpfs_lseek,
641 dfs_tmpfs_getdents,
642 };
643
644 static const struct dfs_filesystem_ops _tmpfs =
645 {
646 "tmp",
647 DFS_FS_FLAG_DEFAULT,
648 &_tmp_fops,
649
650 dfs_tmpfs_mount,
651 dfs_tmpfs_unmount,
652 NULL, /* mkfs */
653 dfs_tmpfs_statfs,
654
655 dfs_tmpfs_unlink,
656 dfs_tmpfs_stat,
657 dfs_tmpfs_rename,
658 };
659
dfs_tmpfs_init(void)660 int dfs_tmpfs_init(void)
661 {
662 /* register tmp file system */
663 dfs_register(&_tmpfs);
664
665 return 0;
666 }
667