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