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