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