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