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