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.h>
22 #include <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];
187
188 sprintf(mount_path, "%s/", path);
189 err = fs_opendir(&dir, mount_path);
190 } else {
191 err = fs_opendir(&dir, path);
192 }
193
194 if (err) {
195 return -ENOEXEC;
196 }
197
198 stat.st_dev = 0;
199 stat.st_ino = 0;
200 stat.st_nlink = 0;
201 stat.st_uid = getuid();
202 stat.st_gid = getgid();
203 stat.st_rdev = 0;
204 stat.st_atime = 0;
205 stat.st_mtime = 0;
206 stat.st_ctime = 0;
207 stat.st_mode = S_IFDIR | S_IRWX_DIR;
208 stat.st_size = 0;
209 stat.st_blksize = 0;
210 stat.st_blocks = 0;
211
212 filler(buf, ".", &stat, 0);
213 filler(buf, "..", &stat, 0);
214
215 do {
216 err = fs_readdir(&dir, &entry);
217 if (err) {
218 break;
219 }
220
221 if (entry.name[0] == DIR_END) {
222 break;
223 }
224
225 if (entry.type == FS_DIR_ENTRY_DIR) {
226 stat.st_mode = S_IFDIR | S_IRWX_DIR;
227 stat.st_size = 0;
228 } else {
229 stat.st_mode = S_IFREG | S_IRW_FILE;
230 stat.st_size = entry.size;
231 }
232
233 if (filler(buf, entry.name, &stat, 0)) {
234 break;
235 }
236
237 } while (1);
238
239 fs_closedir(&dir);
240
241 return err;
242
243 }
244
fuse_fs_access_create(const char * path,mode_t mode,struct fuse_file_info * fi)245 static int fuse_fs_access_create(const char *path, mode_t mode,
246 struct fuse_file_info *fi)
247 {
248 int err;
249 ssize_t handle;
250
251 ARG_UNUSED(mode);
252
253 if (is_mount_point(path)) {
254 return -ENOENT;
255 }
256
257 handle = get_new_file_handle();
258 if (handle < 0) {
259 return handle;
260 }
261
262 fi->fh = handle;
263
264 err = fs_open(&files[handle], path, FS_O_CREATE | FS_O_WRITE);
265 if (err != 0) {
266 release_file_handle(handle);
267 fi->fh = INVALID_FILE_HANDLE;
268 return err;
269 }
270
271 return 0;
272 }
273
fuse_fs_access_open(const char * path,struct fuse_file_info * fi)274 static int fuse_fs_access_open(const char *path, struct fuse_file_info *fi)
275 {
276 return fuse_fs_access_create(path, 0, fi);
277 }
278
fuse_fs_access_release(const char * path,struct fuse_file_info * fi)279 static int fuse_fs_access_release(const char *path, struct fuse_file_info *fi)
280 {
281 ARG_UNUSED(path);
282
283 if (fi->fh == INVALID_FILE_HANDLE) {
284 return -EINVAL;
285 }
286
287 fs_close(&files[fi->fh]);
288
289 release_file_handle(fi->fh);
290
291 return 0;
292 }
293
fuse_fs_access_read(const char * path,char * buf,size_t size,off_t off,struct fuse_file_info * fi)294 static int fuse_fs_access_read(const char *path, char *buf, size_t size,
295 off_t off, struct fuse_file_info *fi)
296 {
297 int err;
298
299 ARG_UNUSED(path);
300
301 if (fi->fh == INVALID_FILE_HANDLE) {
302 return -EINVAL;
303 }
304
305 err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
306 if (err != 0) {
307 return err;
308 }
309
310 err = fs_read(&files[fi->fh], buf, size);
311
312 return err;
313 }
314
fuse_fs_access_write(const char * path,const char * buf,size_t size,off_t off,struct fuse_file_info * fi)315 static int fuse_fs_access_write(const char *path, const char *buf, size_t size,
316 off_t off, struct fuse_file_info *fi)
317 {
318 int err;
319
320 ARG_UNUSED(path);
321
322 if (fi->fh == INVALID_FILE_HANDLE) {
323 return -EINVAL;
324 }
325
326 err = fs_seek(&files[fi->fh], off, FS_SEEK_SET);
327 if (err != 0) {
328 return err;
329 }
330
331 err = fs_write(&files[fi->fh], buf, size);
332
333 return err;
334 }
335
fuse_fs_access_ftruncate(const char * path,off_t size,struct fuse_file_info * fi)336 static int fuse_fs_access_ftruncate(const char *path, off_t size,
337 struct fuse_file_info *fi)
338 {
339 int err;
340
341 ARG_UNUSED(path);
342
343 if (fi->fh == INVALID_FILE_HANDLE) {
344 return -EINVAL;
345 }
346
347 err = fs_truncate(&files[fi->fh], size);
348
349 return err;
350 }
351
fuse_fs_access_truncate(const char * path,off_t size)352 static int fuse_fs_access_truncate(const char *path, off_t size)
353 {
354 int err;
355 static struct fs_file_t file;
356
357 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
358 if (err != 0) {
359 return err;
360 }
361
362 err = fs_truncate(&file, size);
363 if (err != 0) {
364 fs_close(&file);
365 return err;
366 }
367
368 err = fs_close(&file);
369
370 return err;
371 }
372
fuse_fs_access_mkdir(const char * path,mode_t mode)373 static int fuse_fs_access_mkdir(const char *path, mode_t mode)
374 {
375 ARG_UNUSED(mode);
376
377 return fs_mkdir(path);
378 }
379
fuse_fs_access_rmdir(const char * path)380 static int fuse_fs_access_rmdir(const char *path)
381 {
382 return fs_unlink(path);
383 }
384
fuse_fs_access_unlink(const char * path)385 static int fuse_fs_access_unlink(const char *path)
386 {
387 return fs_unlink(path);
388 }
389
fuse_fs_access_statfs(const char * path,struct statvfs * buf)390 static int fuse_fs_access_statfs(const char *path, struct statvfs *buf)
391 {
392 ARG_UNUSED(path);
393 ARG_UNUSED(buf);
394 return 0;
395 }
396
fuse_fs_access_utimens(const char * path,const struct timespec tv[2])397 static int fuse_fs_access_utimens(const char *path, const struct timespec tv[2])
398 {
399 /* dummy */
400 ARG_UNUSED(path);
401 ARG_UNUSED(tv);
402
403 return 0;
404 }
405
406
407 static struct fuse_operations fuse_fs_access_oper = {
408 .getattr = fuse_fs_access_getattr,
409 .readlink = NULL,
410 .getdir = NULL,
411 .mknod = NULL,
412 .mkdir = fuse_fs_access_mkdir,
413 .unlink = fuse_fs_access_unlink,
414 .rmdir = fuse_fs_access_rmdir,
415 .symlink = NULL,
416 .rename = NULL,
417 .link = NULL,
418 .chmod = NULL,
419 .chown = NULL,
420 .truncate = fuse_fs_access_truncate,
421 .utime = NULL,
422 .open = fuse_fs_access_open,
423 .read = fuse_fs_access_read,
424 .write = fuse_fs_access_write,
425 .statfs = fuse_fs_access_statfs,
426 .flush = NULL,
427 .release = fuse_fs_access_release,
428 .fsync = NULL,
429 .setxattr = NULL,
430 .getxattr = NULL,
431 .listxattr = NULL,
432 .removexattr = NULL,
433 .opendir = NULL,
434 .readdir = fuse_fs_access_readdir,
435 .releasedir = NULL,
436 .fsyncdir = NULL,
437 .init = NULL,
438 .destroy = NULL,
439 .access = NULL,
440 .create = fuse_fs_access_create,
441 .ftruncate = fuse_fs_access_ftruncate,
442 .fgetattr = NULL,
443 .lock = NULL,
444 .utimens = fuse_fs_access_utimens,
445 .bmap = NULL,
446 .flag_nullpath_ok = 0,
447 .flag_nopath = 0,
448 .flag_utime_omit_ok = 0,
449 .flag_reserved = 0,
450 .ioctl = NULL,
451 .poll = NULL,
452 .write_buf = NULL,
453 .read_buf = NULL,
454 .flock = NULL,
455 .fallocate = NULL,
456 };
457
fuse_fs_access_main(void * arg)458 static void *fuse_fs_access_main(void *arg)
459 {
460 ARG_UNUSED(arg);
461
462 char *argv[] = {
463 "",
464 "-f",
465 "-s",
466 (char *) fuse_mountpoint
467 };
468 int argc = ARRAY_SIZE(argv);
469
470 posix_print_trace("Mounting flash at %s/\n", fuse_mountpoint);
471 fuse_main(argc, argv, &fuse_fs_access_oper, NULL);
472
473 pthread_exit(0);
474 }
475
fuse_fs_access_init(void)476 static void fuse_fs_access_init(void)
477 {
478 int err;
479 struct stat st;
480 size_t i = 0;
481
482 while (i < ARRAY_SIZE(files)) {
483 fs_file_t_init(&files[i]);
484 ++i;
485 }
486
487 if (fuse_mountpoint == NULL) {
488 fuse_mountpoint = default_fuse_mountpoint;
489 }
490
491 if (stat(fuse_mountpoint, &st) < 0) {
492 if (mkdir(fuse_mountpoint, 0700) < 0) {
493 posix_print_error_and_exit("Failed to create"
494 " directory for flash mount point (%s): %s\n",
495 fuse_mountpoint, strerror(errno));
496 }
497 } else if (!S_ISDIR(st.st_mode)) {
498 posix_print_error_and_exit("%s is not a directory\n",
499 fuse_mountpoint);
500
501 }
502
503 err = pthread_create(&fuse_thread, NULL, fuse_fs_access_main, NULL);
504 if (err < 0) {
505 posix_print_error_and_exit(
506 "Failed to create thread for "
507 "fuse_fs_access_main\n");
508 }
509 }
510
fuse_fs_access_exit(void)511 static void fuse_fs_access_exit(void)
512 {
513 char *full_cmd;
514 const char cmd[] = "fusermount -uz ";
515
516 if (fuse_mountpoint == NULL) {
517 return;
518 }
519
520 full_cmd = malloc(strlen(cmd) + strlen(fuse_mountpoint) + 1);
521
522 sprintf(full_cmd, "%s%s", cmd, fuse_mountpoint);
523 if (system(full_cmd) < -1) {
524 printf("Failed to unmount fuse mount point\n");
525 }
526 free(full_cmd);
527
528 pthread_join(fuse_thread, NULL);
529 }
530
fuse_fs_access_options(void)531 static void fuse_fs_access_options(void)
532 {
533 static struct args_struct_t fuse_fs_access_options[] = {
534 { .manual = false,
535 .is_mandatory = false,
536 .is_switch = false,
537 .option = "flash-mount",
538 .name = "path",
539 .type = 's',
540 .dest = (void *)&fuse_mountpoint,
541 .call_when_found = NULL,
542 .descript = "Path to the directory where to mount flash" },
543 ARG_TABLE_ENDMARKER
544 };
545
546 native_add_command_line_opts(fuse_fs_access_options);
547 }
548
549 NATIVE_TASK(fuse_fs_access_options, PRE_BOOT_1, 1);
550 NATIVE_TASK(fuse_fs_access_init, PRE_BOOT_2, 1);
551 NATIVE_TASK(fuse_fs_access_exit, ON_EXIT, 1);
552