1 /*
2 * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define FUSE_USE_VERSION 26
8
9 #undef _XOPEN_SOURCE
10 #define _XOPEN_SOURCE 700
11
12 #include <fuse.h>
13 #include <libgen.h>
14 #include <linux/limits.h>
15 #include <unistd.h>
16 #include <pthread.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/mount.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24
25 #include <zephyr/kernel.h>
26 #include <zephyr/fs/fs.h>
27
28 #include "cmdline.h"
29 #include "soc.h"
30
31 #define S_IRWX_DIR (0775)
32 #define S_IRW_FILE (0664)
33
34 #define NUMBER_OF_OPEN_FILES 128
35 #define INVALID_FILE_HANDLE (NUMBER_OF_OPEN_FILES + 1)
36
37 #define DIR_END '\0'
38
39 static struct fs_file_t files[NUMBER_OF_OPEN_FILES];
40 static uint8_t file_handles[NUMBER_OF_OPEN_FILES];
41
42 static pthread_t fuse_thread;
43
44 static const char default_fuse_mountpoint[] = "flash";
45
46 static const char *fuse_mountpoint;
47
get_new_file_handle(void)48 static ssize_t get_new_file_handle(void)
49 {
50 size_t idx;
51
52 for (idx = 0; idx < ARRAY_SIZE(file_handles); ++idx) {
53 if (file_handles[idx] == 0) {
54 ++file_handles[idx];
55 return idx;
56 }
57 }
58
59 return -ENOMEM;
60 }
61
release_file_handle(size_t handle)62 static void release_file_handle(size_t handle)
63 {
64 if (handle < ARRAY_SIZE(file_handles)) {
65 --file_handles[handle];
66 }
67 }
68
is_mount_point(const char * path)69 static bool is_mount_point(const char *path)
70 {
71 char dir_path[PATH_MAX];
72 size_t len;
73
74 len = strlen(path);
75 if (len >= sizeof(dir_path)) {
76 return false;
77 }
78
79 memcpy(dir_path, path, len);
80 dir_path[len] = '\0';
81 return strcmp(dirname(dir_path), "/") == 0;
82 }
83
fuse_fs_access_getattr(const char * path,struct stat * stat)84 static int fuse_fs_access_getattr(const char *path, struct stat *stat)
85 {
86 struct fs_dirent entry;
87 int err;
88
89 stat->st_dev = 0;
90 stat->st_ino = 0;
91 stat->st_nlink = 0;
92 stat->st_uid = getuid();
93 stat->st_gid = getgid();
94 stat->st_rdev = 0;
95 stat->st_blksize = 0;
96 stat->st_blocks = 0;
97 stat->st_atime = 0;
98 stat->st_mtime = 0;
99 stat->st_ctime = 0;
100
101 if ((strcmp(path, "/") == 0) || is_mount_point(path)) {
102 if (strstr(path, "/.") != NULL) {
103 return -ENOENT;
104 }
105 stat->st_mode = S_IFDIR | S_IRWX_DIR;
106 stat->st_size = 0;
107 return 0;
108 }
109
110 err = fs_stat(path, &entry);
111 if (err != 0) {
112 return err;
113 }
114
115 if (entry.type == FS_DIR_ENTRY_DIR) {
116 stat->st_mode = S_IFDIR | S_IRWX_DIR;
117 stat->st_size = 0;
118 } else {
119 stat->st_mode = S_IFREG | S_IRW_FILE;
120 stat->st_size = entry.size;
121 }
122
123 return 0;
124 }
125
fuse_fs_access_readmount(void * buf,fuse_fill_dir_t filler)126 static int fuse_fs_access_readmount(void *buf, fuse_fill_dir_t filler)
127 {
128 int mnt_nbr = 0;
129 const char *mnt_name;
130 struct stat stat;
131 int err;
132
133 stat.st_dev = 0;
134 stat.st_ino = 0;
135 stat.st_nlink = 0;
136 stat.st_uid = getuid();
137 stat.st_gid = getgid();
138 stat.st_rdev = 0;
139 stat.st_atime = 0;
140 stat.st_mtime = 0;
141 stat.st_ctime = 0;
142 stat.st_mode = S_IFDIR | S_IRWX_DIR;
143 stat.st_size = 0;
144 stat.st_blksize = 0;
145 stat.st_blocks = 0;
146
147 filler(buf, ".", &stat, 0);
148 filler(buf, "..", NULL, 0);
149
150 do {
151 err = fs_readmount(&mnt_nbr, &mnt_name);
152 if (err < 0) {
153 break;
154 }
155
156 filler(buf, &mnt_name[1], &stat, 0);
157
158 } while (true);
159
160 if (err == -ENOENT) {
161 err = 0;
162 }
163
164 return err;
165 }
166
fuse_fs_access_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t off,struct fuse_file_info * fi)167 static int fuse_fs_access_readdir(const char *path, void *buf,
168 fuse_fill_dir_t filler, off_t off,
169 struct fuse_file_info *fi)
170 {
171 struct fs_dir_t dir;
172 struct fs_dirent entry;
173 int err;
174 struct stat stat;
175
176 ARG_UNUSED(off);
177 ARG_UNUSED(fi);
178
179 if (strcmp(path, "/") == 0) {
180 return fuse_fs_access_readmount(buf, filler);
181 }
182
183 fs_dir_t_init(&dir);
184
185 if (is_mount_point(path)) {
186 /* File system API expects trailing slash for a mount point
187 * directory but FUSE strips the trailing slashes from
188 * directory names so add it back.
189 */
190 char mount_path[PATH_MAX] = {0};
191 size_t len = strlen(path);
192
193 if (len >= (PATH_MAX - 2)) {
194 return -ENOMEM;
195 }
196
197 memcpy(mount_path, path, len);
198 mount_path[len] = '/';
199 err = fs_opendir(&dir, mount_path);
200 } else {
201 err = fs_opendir(&dir, path);
202 }
203
204 if (err) {
205 return -ENOEXEC;
206 }
207
208 stat.st_dev = 0;
209 stat.st_ino = 0;
210 stat.st_nlink = 0;
211 stat.st_uid = getuid();
212 stat.st_gid = getgid();
213 stat.st_rdev = 0;
214 stat.st_atime = 0;
215 stat.st_mtime = 0;
216 stat.st_ctime = 0;
217 stat.st_mode = S_IFDIR | S_IRWX_DIR;
218 stat.st_size = 0;
219 stat.st_blksize = 0;
220 stat.st_blocks = 0;
221
222 filler(buf, ".", &stat, 0);
223 filler(buf, "..", &stat, 0);
224
225 do {
226 err = fs_readdir(&dir, &entry);
227 if (err) {
228 break;
229 }
230
231 if (entry.name[0] == DIR_END) {
232 break;
233 }
234
235 if (entry.type == FS_DIR_ENTRY_DIR) {
236 stat.st_mode = S_IFDIR | S_IRWX_DIR;
237 stat.st_size = 0;
238 } else {
239 stat.st_mode = S_IFREG | S_IRW_FILE;
240 stat.st_size = entry.size;
241 }
242
243 if (filler(buf, entry.name, &stat, 0)) {
244 break;
245 }
246
247 } while (1);
248
249 fs_closedir(&dir);
250
251 return err;
252
253 }
254
fuse_fs_access_create(const char * path,mode_t mode,struct fuse_file_info * fi)255 static int fuse_fs_access_create(const char *path, mode_t mode,
256 struct fuse_file_info *fi)
257 {
258 int err;
259 ssize_t handle;
260
261 ARG_UNUSED(mode);
262
263 if (is_mount_point(path)) {
264 return -ENOENT;
265 }
266
267 handle = get_new_file_handle();
268 if (handle < 0) {
269 return handle;
270 }
271
272 fi->fh = handle;
273
274 err = fs_open(&files[handle], path, FS_O_CREATE | FS_O_WRITE);
275 if (err != 0) {
276 release_file_handle(handle);
277 fi->fh = INVALID_FILE_HANDLE;
278 return err;
279 }
280
281 return 0;
282 }
283
fuse_fs_access_open(const char * path,struct fuse_file_info * fi)284 static int fuse_fs_access_open(const char *path, struct fuse_file_info *fi)
285 {
286 return fuse_fs_access_create(path, 0, fi);
287 }
288
fuse_fs_access_release(const char * path,struct fuse_file_info * fi)289 static int fuse_fs_access_release(const char *path, struct fuse_file_info *fi)
290 {
291 ARG_UNUSED(path);
292
293 if (fi->fh == INVALID_FILE_HANDLE) {
294 return -EINVAL;
295 }
296
297 fs_close(&files[fi->fh]);
298
299 release_file_handle(fi->fh);
300
301 return 0;
302 }
303
fuse_fs_access_read(const char * path,char * buf,size_t size,off_t off,struct fuse_file_info * fi)304 static int fuse_fs_access_read(const char *path, char *buf, size_t size,
305 off_t off, struct fuse_file_info *fi)
306 {
307 int err;
308
309 ARG_UNUSED(path);
310
311 if (fi->fh == INVALID_FILE_HANDLE) {
312 return -EINVAL;
313 }
314
315 err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
316 if (err != 0) {
317 return err;
318 }
319
320 err = fs_read(&files[fi->fh], buf, size);
321
322 return err;
323 }
324
fuse_fs_access_write(const char * path,const char * buf,size_t size,off_t off,struct fuse_file_info * fi)325 static int fuse_fs_access_write(const char *path, const char *buf, size_t size,
326 off_t off, struct fuse_file_info *fi)
327 {
328 int err;
329
330 ARG_UNUSED(path);
331
332 if (fi->fh == INVALID_FILE_HANDLE) {
333 return -EINVAL;
334 }
335
336 err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
337 if (err != 0) {
338 return err;
339 }
340
341 err = fs_write(&files[fi->fh], buf, size);
342
343 return err;
344 }
345
fuse_fs_access_ftruncate(const char * path,off_t size,struct fuse_file_info * fi)346 static int fuse_fs_access_ftruncate(const char *path, off_t size,
347 struct fuse_file_info *fi)
348 {
349 int err;
350
351 ARG_UNUSED(path);
352
353 if (fi->fh == INVALID_FILE_HANDLE) {
354 return -EINVAL;
355 }
356
357 err = fs_truncate(&files[fi->fh], size);
358
359 return err;
360 }
361
fuse_fs_access_truncate(const char * path,off_t size)362 static int fuse_fs_access_truncate(const char *path, off_t size)
363 {
364 int err;
365 static struct fs_file_t file;
366
367 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
368 if (err != 0) {
369 return err;
370 }
371
372 err = fs_truncate(&file, size);
373 if (err != 0) {
374 fs_close(&file);
375 return err;
376 }
377
378 err = fs_close(&file);
379
380 return err;
381 }
382
fuse_fs_access_mkdir(const char * path,mode_t mode)383 static int fuse_fs_access_mkdir(const char *path, mode_t mode)
384 {
385 ARG_UNUSED(mode);
386
387 return fs_mkdir(path);
388 }
389
fuse_fs_access_rmdir(const char * path)390 static int fuse_fs_access_rmdir(const char *path)
391 {
392 return fs_unlink(path);
393 }
394
fuse_fs_access_unlink(const char * path)395 static int fuse_fs_access_unlink(const char *path)
396 {
397 return fs_unlink(path);
398 }
399
fuse_fs_access_statfs(const char * path,struct statvfs * buf)400 static int fuse_fs_access_statfs(const char *path, struct statvfs *buf)
401 {
402 ARG_UNUSED(path);
403 ARG_UNUSED(buf);
404 return 0;
405 }
406
fuse_fs_access_utimens(const char * path,const struct timespec tv[2])407 static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2])
408 {
409 /* dummy */
410 ARG_UNUSED(path);
411 ARG_UNUSED(tv);
412
413 return 0;
414 }
415
416
417 static struct fuse_operations fuse_fs_access_oper = {
418 .getattr = fuse_fs_access_getattr,
419 .readlink = NULL,
420 .getdir = NULL,
421 .mknod = NULL,
422 .mkdir = fuse_fs_access_mkdir,
423 .unlink = fuse_fs_access_unlink,
424 .rmdir = fuse_fs_access_rmdir,
425 .symlink = NULL,
426 .rename = NULL,
427 .link = NULL,
428 .chmod = NULL,
429 .chown = NULL,
430 .truncate = fuse_fs_access_truncate,
431 .utime = NULL,
432 .open = fuse_fs_access_open,
433 .read = fuse_fs_access_read,
434 .write = fuse_fs_access_write,
435 .statfs = fuse_fs_access_statfs,
436 .flush = NULL,
437 .release = fuse_fs_access_release,
438 .fsync = NULL,
439 .setxattr = NULL,
440 .getxattr = NULL,
441 .listxattr = NULL,
442 .removexattr = NULL,
443 .opendir = NULL,
444 .readdir = fuse_fs_access_readdir,
445 .releasedir = NULL,
446 .fsyncdir = NULL,
447 .init = NULL,
448 .destroy = NULL,
449 .access = NULL,
450 .create = fuse_fs_access_create,
451 .ftruncate = fuse_fs_access_ftruncate,
452 .fgetattr = NULL,
453 .lock = NULL,
454 .utimens = fuse_fs_access_utimens,
455 .bmap = NULL,
456 .flag_nullpath_ok = 0,
457 .flag_nopath = 0,
458 .flag_utime_omit_ok = 0,
459 .flag_reserved = 0,
460 .ioctl = NULL,
461 .poll = NULL,
462 .write_buf = NULL,
463 .read_buf = NULL,
464 .flock = NULL,
465 .fallocate = NULL,
466 };
467
fuse_fs_access_main(void * arg)468 static void *fuse_fs_access_main(void *arg)
469 {
470 ARG_UNUSED(arg);
471
472 char *argv[] = {
473 "",
474 "-f",
475 "-s",
476 (char *) fuse_mountpoint
477 };
478 int argc = ARRAY_SIZE(argv);
479
480 posix_print_trace("Mounting flash at %s/\n", fuse_mountpoint);
481 fuse_main(argc, argv, &fuse_fs_access_oper, NULL);
482
483 pthread_exit(0);
484 }
485
fuse_fs_access_init(void)486 static void fuse_fs_access_init(void)
487 {
488 int err;
489 struct stat st;
490 size_t i = 0;
491
492 while (i < ARRAY_SIZE(files)) {
493 fs_file_t_init(&files[i]);
494 ++i;
495 }
496
497 if (fuse_mountpoint == NULL) {
498 fuse_mountpoint = default_fuse_mountpoint;
499 }
500
501 if (stat(fuse_mountpoint, &st) < 0) {
502 if (mkdir(fuse_mountpoint, 0700) < 0) {
503 posix_print_error_and_exit("Failed to create"
504 " directory for flash mount point (%s): %s\n",
505 fuse_mountpoint, strerror(errno));
506 }
507 } else if (!S_ISDIR(st.st_mode)) {
508 posix_print_error_and_exit("%s is not a directory\n",
509 fuse_mountpoint);
510
511 }
512
513 err = pthread_create(&fuse_thread, NULL, fuse_fs_access_main, NULL);
514 if (err < 0) {
515 posix_print_error_and_exit(
516 "Failed to create thread for "
517 "fuse_fs_access_main\n");
518 }
519 }
520
fuse_fs_access_exit(void)521 static void fuse_fs_access_exit(void)
522 {
523 char *full_cmd;
524 const char cmd[] = "fusermount -uz ";
525
526 if (fuse_mountpoint == NULL) {
527 return;
528 }
529
530 full_cmd = malloc(strlen(cmd) + strlen(fuse_mountpoint) + 1);
531
532 sprintf(full_cmd, "%s%s", cmd, fuse_mountpoint);
533 if (system(full_cmd) < -1) {
534 printf("Failed to unmount fuse mount point\n");
535 }
536 free(full_cmd);
537
538 pthread_join(fuse_thread, NULL);
539 }
540
fuse_fs_access_options(void)541 static void fuse_fs_access_options(void)
542 {
543 static struct args_struct_t fuse_fs_access_options[] = {
544 { .manual = false,
545 .is_mandatory = false,
546 .is_switch = false,
547 .option = "flash-mount",
548 .name = "path",
549 .type = 's',
550 .dest = (void *)&fuse_mountpoint,
551 .call_when_found = NULL,
552 .descript = "Path to the directory where to mount flash" },
553 ARG_TABLE_ENDMARKER
554 };
555
556 native_add_command_line_opts(fuse_fs_access_options);
557 }
558
559 NATIVE_TASK(fuse_fs_access_options, PRE_BOOT_1, 1);
560 NATIVE_TASK(fuse_fs_access_init, PRE_BOOT_2, 1);
561 NATIVE_TASK(fuse_fs_access_exit, ON_EXIT, 1);
562