xref: /haiku/src/tools/fs_shell/fuse.cpp (revision 62f5ba006a08b0df30631375878effaf67ae5dbc)
1 /*
2  * Copyright 2009, Raghuram Nagireddy <raghuram87@gmail.com>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #define FUSE_USE_VERSION 27
7 
8 #include <fuse/fuse.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <syslog.h>
12 
13 #include "fssh.h"
14 
15 #include "driver_settings.h"
16 #include "external_commands.h"
17 #include "fd.h"
18 #include "fssh_dirent.h"
19 #include "fssh_errno.h"
20 #include "fssh_errors.h"
21 #include "fssh_fcntl.h"
22 #include "fssh_fs_info.h"
23 #include "fssh_module.h"
24 #include "fssh_node_monitor.h"
25 #include "fssh_stat.h"
26 #include "fssh_string.h"
27 #include "fssh_type_constants.h"
28 #include "module.h"
29 #include "syscalls.h"
30 #include "vfs.h"
31 
32 
33 extern fssh_module_info *modules[];
34 
35 extern fssh_file_system_module_info gRootFileSystem;
36 
37 namespace FSShell {
38 
39 const char* kMountPoint = "/myfs";
40 
41 static mode_t sUmask = 0022;
42 
43 #define PRINTD(x) if (gIsDebug) fprintf(stderr, x)
44 
45 bool gIsDebug = false;
46 
47 static fssh_status_t
48 init_kernel()
49 {
50 	fssh_status_t error;
51 
52 	// init module subsystem
53 	error = module_init(NULL);
54 	if (error != FSSH_B_OK) {
55 		fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
56 		return error;
57 	}
58 
59 	// init driver settings
60 	error = driver_settings_init();
61 	if (error != FSSH_B_OK) {
62 		fprintf(stderr, "initializing driver settings failed: %s\n",
63 			fssh_strerror(error));
64 		return error;
65 	}
66 
67 	// register built-in modules, i.e. the rootfs and the client FS
68 	register_builtin_module(&gRootFileSystem.info);
69 	for (int i = 0; modules[i]; i++)
70 		register_builtin_module(modules[i]);
71 
72 	// init VFS
73 	error = vfs_init(NULL);
74 	if (error != FSSH_B_OK) {
75 		fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
76 		return error;
77 	}
78 
79 	// init kernel IO context
80 	gKernelIOContext = (io_context*)vfs_new_io_context(NULL);
81 	if (!gKernelIOContext) {
82 		fprintf(stderr, "creating IO context failed!\n");
83 		return FSSH_B_NO_MEMORY;
84 	}
85 
86 	// mount root FS
87 	fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
88 	if (rootDev < 0) {
89 		fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev));
90 		return rootDev;
91 	}
92 
93 	// set cwd to "/"
94 	error = _kern_setcwd(-1, "/");
95 	if (error != FSSH_B_OK) {
96 		fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
97 		return error;
98 	}
99 
100 	// create mount point for the client FS
101 	error = _kern_create_dir(-1, kMountPoint, 0775);
102 	if (error != FSSH_B_OK) {
103 		fprintf(stderr, "creating mount point failed: %s\n",
104 			fssh_strerror(error));
105 		return error;
106 	}
107 
108 	return FSSH_B_OK;
109 }
110 
111 
112 static void
113 fromFsshStatToStat(struct fssh_stat* f_stbuf, struct stat* stbuf)
114 {
115 	stbuf->st_dev = f_stbuf->fssh_st_dev;
116 	stbuf->st_ino = f_stbuf->fssh_st_ino;
117 	stbuf->st_mode = f_stbuf->fssh_st_mode;
118 	stbuf->st_nlink = f_stbuf->fssh_st_nlink;
119 	stbuf->st_uid = f_stbuf->fssh_st_uid;
120 	stbuf->st_gid = f_stbuf->fssh_st_gid;
121 	stbuf->st_rdev = f_stbuf->fssh_st_rdev;
122 	stbuf->st_size = f_stbuf->fssh_st_size;
123 	stbuf->st_blksize = f_stbuf->fssh_st_blksize;
124 	stbuf->st_blocks = f_stbuf->fssh_st_blocks;
125 	stbuf->st_atime = f_stbuf->fssh_st_atime;
126 	stbuf->st_mtime = f_stbuf->fssh_st_mtime;
127 	stbuf->st_ctime = f_stbuf->fssh_st_ctime;
128 }
129 
130 #define _ERR(x) (-1 * fssh_to_host_error(x))
131 
132 
133 // pragma mark - FUSE functions
134 
135 
136 int
137 fuse_getattr(const char* path, struct stat* stbuf)
138 {
139 	PRINTD("##getattr\n");
140 	struct fssh_stat f_stbuf;
141 	fssh_status_t status = _kern_read_stat(-1, path, false, &f_stbuf,
142 			sizeof(f_stbuf));
143 	fromFsshStatToStat(&f_stbuf, stbuf);
144 	if (gIsDebug)
145 		printf("GETATTR returned: %d\n", status);
146 	return _ERR(status);
147 }
148 
149 
150 static int
151 fuse_access(const char* path, int mask)
152 {
153 	PRINTD("##access\n");
154 	return _ERR(_kern_access(path, mask));
155 }
156 
157 
158 static int
159 fuse_readlink(const char* path, char* buffer, size_t size)
160 {
161 	PRINTD("##readlink\n");
162 	fssh_size_t n_size = size - 1;
163 	fssh_status_t st = _kern_read_link(-1, path, buffer, &n_size);
164 	if (st >= FSSH_B_OK)
165 		buffer[n_size] = '\0';
166 	return _ERR(st);
167 }
168 
169 
170 static int
171 fuse_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
172 	off_t offset, struct fuse_file_info* fi)
173 {
174 	PRINTD("##readdir\n");
175 	int dfp = _kern_open_dir(-1, path);
176 	if (dfp < FSSH_B_OK)
177 		return _ERR(dfp);
178 
179 	fssh_ssize_t entriesRead = 0;
180 	struct fssh_stat f_st;
181 	struct stat st;
182 	char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
183 	fssh_dirent* dirEntry = (fssh_dirent*)buffer;
184 	while ((entriesRead = _kern_read_dir(dfp, dirEntry,
185 			sizeof(buffer), 1)) == 1) {
186 		fssh_memset(&st, 0, sizeof(st));
187 		fssh_memset(&f_st, 0, sizeof(f_st));
188 		fssh_status_t status = _kern_read_stat(dfp, dirEntry->d_name,
189 			false, &f_st, sizeof(f_st));
190 		if (status >= FSSH_B_OK) {
191 			fromFsshStatToStat(&f_st, &st);
192 			if (filler(buf, dirEntry->d_name, &st, 0))
193 				break;
194 		}
195 	}
196 	_kern_close(dfp);
197 	//TODO: check _kern_close
198 	return 0;
199 }
200 
201 
202 static int
203 fuse_mknod(const char* path, mode_t mode, dev_t rdev)
204 {
205 	PRINTD("##mknod\n");
206 	if (S_ISREG(mode)) {
207 		int fd = _kern_open(-1, path,
208 			FSSH_O_CREAT | FSSH_O_EXCL | FSSH_O_WRONLY, mode);
209 		if (fd >= FSSH_B_OK)
210 			return _ERR(_kern_close(fd));
211 		return _ERR(fd);
212 	} else if (S_ISFIFO(mode))
213 		return _ERR(FSSH_EINVAL);
214 	else
215 		return _ERR(FSSH_EINVAL);
216 }
217 
218 
219 static int
220 fuse_mkdir(const char* path, mode_t mode)
221 {
222 	PRINTD("##mkdir\n");
223 	return _ERR(_kern_create_dir(-1, path, mode));
224 }
225 
226 
227 static int
228 fuse_symlink(const char* from, const char* to)
229 {
230 	PRINTD("##symlink\n");
231 	return _ERR(_kern_create_symlink(-1, to, from,
232 		FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO));
233 }
234 
235 
236 static int
237 fuse_unlink(const char* path)
238 {
239 	PRINTD("##unlink\n");
240 	return _ERR(_kern_unlink(-1, path));
241 }
242 
243 
244 static int
245 fuse_rmdir(const char* path)
246 {
247 	PRINTD("##rmdir\n");
248 	return _ERR(_kern_remove_dir(-1, path));
249 }
250 
251 
252 static int
253 fuse_rename(const char* from, const char* to)
254 {
255 	PRINTD("##rename\n");
256 	return _ERR(_kern_rename(-1, from, -1, to));
257 }
258 
259 
260 static int
261 fuse_link(const char* from, const char* to)
262 {
263 	PRINTD("##link\n");
264 	return _ERR(_kern_create_link(to, from));
265 }
266 
267 
268 static int
269 fuse_chmod(const char* path, mode_t mode)
270 {
271 	PRINTD("##chmod\n");
272 	fssh_struct_stat st;
273 	st.fssh_st_mode = mode;
274 	return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st),
275 			FSSH_B_STAT_MODE));
276 }
277 
278 
279 static int
280 fuse_chown(const char* path, uid_t uid, gid_t gid)
281 {
282 	PRINTD("##chown\n");
283 	fssh_struct_stat st;
284 	st.fssh_st_uid = uid;
285 	st.fssh_st_gid = gid;
286 	return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st),
287 			FSSH_B_STAT_UID|FSSH_B_STAT_GID));
288 }
289 
290 
291 static int
292 fuse_open(const char* path, struct fuse_file_info* fi)
293 {
294 	PRINTD("##open\n");
295 	// TODO: Do we have a syscall similar to the open syscall in linux which
296 	// takes only two args: path and flags with no mask/perms?
297 	int fd = _kern_open(-1, path, fi->flags,
298 		(FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
299 	_kern_close(fd);
300 	if (fd < FSSH_B_OK)
301 		return _ERR(fd);
302 	else
303 		return 0;
304 }
305 
306 
307 static int
308 fuse_read(const char* path, char* buf, size_t size, off_t offset,
309 	struct fuse_file_info* fi)
310 {
311 	PRINTD("##read\n");
312 	int fd = _kern_open(-1, path, FSSH_O_RDONLY,
313 		(FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
314 	if (fd < FSSH_B_OK)
315 		return _ERR(fd);
316 
317 	int res = _kern_read(fd, offset, buf, size);
318 	_kern_close(fd);
319 	if (res < FSSH_B_OK)
320 		res = _ERR(res);
321 	return res;
322 }
323 
324 
325 static int
326 fuse_write(const char* path, const char* buf, size_t size, off_t offset,
327 	struct fuse_file_info* fi)
328 {
329 	PRINTD("##write\n");
330 	int fd = _kern_open(-1, path, FSSH_O_WRONLY,
331 		(FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
332 	if (fd < FSSH_B_OK)
333 		return _ERR(fd);
334 
335 	int res = _kern_write(fd, offset, buf, size);
336 	_kern_close(fd);
337 	if (res < FSSH_B_OK)
338 		res = _ERR(res);
339 	return res;
340 }
341 
342 
343 static void
344 fuse_destroy(void* priv_data)
345 {
346 	_kern_sync();
347 }
348 
349 
350 static fssh_dev_t
351 get_volume_id()
352 {
353 	struct fssh_stat st;
354 	fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
355 		sizeof(st));
356 	if (error != FSSH_B_OK)
357 		return error;
358 	return st.fssh_st_dev;
359 }
360 
361 
362 static int
363 fuse_statfs(const char *path __attribute__((unused)),
364                             struct statvfs *sfs)
365 {
366 	PRINTD("##statfs\n");
367 
368 	fssh_dev_t volumeID = get_volume_id();
369 	if (volumeID < 0)
370 		return _ERR(volumeID);
371 
372 	fssh_fs_info info;
373 	fssh_status_t status = _kern_read_fs_info(volumeID, &info);
374 	if (status != FSSH_B_OK)
375 		return _ERR(status);
376 
377 	sfs->f_bsize = sfs->f_frsize = info.block_size;
378 	sfs->f_blocks = info.total_blocks;
379 	sfs->f_bavail = sfs->f_bfree = info.free_blocks;
380 
381 	return 0;
382 }
383 
384 
385 struct fuse_operations gFUSEOperations;
386 
387 
388 static void
389 initialiseFuseOps(struct fuse_operations* fuseOps)
390 {
391 	fuseOps->getattr	= fuse_getattr;
392 	fuseOps->access		= fuse_access;
393 	fuseOps->readlink	= fuse_readlink;
394 	fuseOps->readdir	= fuse_readdir;
395 	fuseOps->mknod		= fuse_mknod;
396 	fuseOps->mkdir		= fuse_mkdir;
397 	fuseOps->symlink	= fuse_symlink;
398 	fuseOps->unlink		= fuse_unlink;
399 	fuseOps->rmdir		= fuse_rmdir;
400 	fuseOps->rename		= fuse_rename;
401 	fuseOps->link		= fuse_link;
402 	fuseOps->chmod		= fuse_chmod;
403 	fuseOps->chown		= fuse_chown;
404 	fuseOps->truncate	= NULL;
405 	fuseOps->utimens	= NULL;
406 	fuseOps->open		= fuse_open;
407 	fuseOps->read		= fuse_read;
408 	fuseOps->write		= fuse_write;
409 	fuseOps->statfs		= fuse_statfs;
410 	fuseOps->release	= NULL;
411 	fuseOps->fsync		= NULL;
412 	fuseOps->destroy	= fuse_destroy;
413 }
414 
415 
416 static int
417 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName)
418 {
419 	// Mount the volume in the root FS.
420 	fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
421 	if (fsDev < 0) {
422 		fprintf(stderr, "Error: Mounting FS failed: %s\n",
423 			fssh_strerror(fsDev));
424 		return 1;
425 	}
426 
427 	if (!gIsDebug) {
428 		bool isErr = false;
429 		fssh_dev_t volumeID = get_volume_id();
430 		if (volumeID < 0)
431 			isErr = true;
432 		fssh_fs_info info;
433 		if (!isErr) {
434 			fssh_status_t status = _kern_read_fs_info(volumeID, &info);
435 			if (status != FSSH_B_OK)
436 				isErr = true;
437 		}
438 		syslog(LOG_INFO, "Mounted %s (%s) to %s",
439 			device,
440 			isErr ? "unknown" : info.volume_name,
441 			mntPoint);
442 	}
443 
444 	// Run the fuse_main() loop.
445 	const char* argv[13];
446 	int fuseArgCount = 0;
447 
448 	argv[fuseArgCount++] = (const char*)"bfs_fuse";
449 	argv[fuseArgCount++] = mntPoint;
450 	argv[fuseArgCount++] = (const char*)"-s";
451 	if (gIsDebug)
452 		argv[fuseArgCount++] = (const char*)"-d";
453 
454 	initialiseFuseOps(&gFUSEOperations);
455 
456 	int ret = fuse_main(fuseArgCount, (char**)argv, &gFUSEOperations, NULL);
457 
458 	// Unmount the volume again.
459 	// Avoid a "busy" vnode.
460 	_kern_setcwd(-1, "/");
461 	fssh_status_t error = _kern_unmount(kMountPoint, 0);
462 	if (error != FSSH_B_OK) {
463 		if (gIsDebug)
464 			fprintf(stderr, "Error: Unmounting FS failed: %s\n", fssh_strerror(error));
465 		else
466 			syslog(LOG_INFO, "Error: Unmounting FS failed: %s", fssh_strerror(error));
467 		return 1;
468 	}
469 
470 	if (!gIsDebug)
471 		syslog(LOG_INFO, "UnMounted %s from %s", device, mntPoint);
472 
473 	return ret;
474 }
475 
476 
477 }	// namespace FSShell
478 
479 
480 using namespace FSShell;
481 
482 
483 static void
484 print_usage_and_exit(const char* binName)
485 {
486 	fprintf(stderr,"Usage: %s [-d] <device> <mount point>\n", binName);
487 	exit(1);
488 }
489 
490 
491 int
492 main(int argc, const char* const* argv)
493 {
494 	// eat options
495 	int argi = 1;
496 	while (argi < argc && argv[argi][0] == '-') {
497 		const char* arg = argv[argi++];
498 		if (strcmp(arg, "--help") == 0) {
499 			print_usage_and_exit(argv[0]);
500 		} else if (strcmp(arg, "-d") == 0) {
501 			gIsDebug = true;
502 		} else {
503 			print_usage_and_exit(argv[0]);
504 		}
505 	}
506 
507 	// get device
508 	if (argi >= argc)
509 		print_usage_and_exit(argv[0]);
510 	const char* device = argv[argi++];
511 	// get mountpoint
512 	if (argi >= argc)
513 		print_usage_and_exit(argv[0]);
514 	const char* mntPoint = argv[argi++];
515 	// more parameters are excess
516 	if (argi < argc)
517 		print_usage_and_exit(argv[0]);
518 
519 	if (!modules[0]) {
520 		fprintf(stderr, "Error: Couldn't find FS module!\n");
521 		return 1;
522 	}
523 
524 	fssh_status_t error = init_kernel();
525 	if (error != FSSH_B_OK) {
526 		fprintf(stderr, "Error: Initializing kernel failed: %s\n",
527 			fssh_strerror(error));
528 		return error;
529 	}
530 	const char* fsName = modules[0]->name;
531 	return fssh_fuse_session(device, mntPoint, fsName);
532 }
533 
534