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