xref: /haiku/src/tools/fs_shell/fuse.cpp (revision 981f1b1135291a4fca290fbdf69910dc2f24abdd)
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 
12 #include "fssh.h"
13 
14 #include "driver_settings.h"
15 #include "external_commands.h"
16 #include "fd.h"
17 #include "fssh_dirent.h"
18 #include "fssh_errno.h"
19 #include "fssh_errors.h"
20 #include "fssh_fcntl.h"
21 #include "fssh_fs_info.h"
22 #include "fssh_module.h"
23 #include "fssh_node_monitor.h"
24 #include "fssh_stat.h"
25 #include "fssh_string.h"
26 #include "fssh_type_constants.h"
27 #include "module.h"
28 #include "syscalls.h"
29 #include "vfs.h"
30 
31 
32 extern fssh_module_info *modules[];
33 
34 extern fssh_file_system_module_info gRootFileSystem;
35 
36 namespace FSShell {
37 
38 const char* kMountPoint = "/myfs";
39 
40 static mode_t sUmask = 0022;
41 
42 #define PRINTD(x) fprintf(stderr, x)
43 
44 
45 static fssh_status_t
46 init_kernel()
47 {
48 	fssh_status_t error;
49 
50 	// init module subsystem
51 	error = module_init(NULL);
52 	if (error != FSSH_B_OK) {
53 		fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
54 		return error;
55 	}
56 
57 	// init driver settings
58 	error = driver_settings_init();
59 	if (error != FSSH_B_OK) {
60 		fprintf(stderr, "initializing driver settings failed: %s\n",
61 			fssh_strerror(error));
62 		return error;
63 	}
64 
65 	// register built-in modules, i.e. the rootfs and the client FS
66 	register_builtin_module(&gRootFileSystem.info);
67 	for (int i = 0; modules[i]; i++)
68 		register_builtin_module(modules[i]);
69 
70 	// init VFS
71 	error = vfs_init(NULL);
72 	if (error != FSSH_B_OK) {
73 		fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
74 		return error;
75 	}
76 
77 	// init kernel IO context
78 	gKernelIOContext = (io_context*)vfs_new_io_context(NULL);
79 	if (!gKernelIOContext) {
80 		fprintf(stderr, "creating IO context failed!\n");
81 		return FSSH_B_NO_MEMORY;
82 	}
83 
84 	// mount root FS
85 	fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
86 	if (rootDev < 0) {
87 		fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev));
88 		return rootDev;
89 	}
90 
91 	// set cwd to "/"
92 	error = _kern_setcwd(-1, "/");
93 	if (error != FSSH_B_OK) {
94 		fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
95 		return error;
96 	}
97 
98 	// create mount point for the client FS
99 	error = _kern_create_dir(-1, kMountPoint, 0775);
100 	if (error != FSSH_B_OK) {
101 		fprintf(stderr, "creating mount point failed: %s\n",
102 			fssh_strerror(error));
103 		return error;
104 	}
105 
106 	return FSSH_B_OK;
107 }
108 
109 
110 static void
111 fromFsshStatToStat(struct fssh_stat* f_stbuf, struct stat* stbuf)
112 {
113 	stbuf->st_dev = f_stbuf->fssh_st_dev;
114 	stbuf->st_ino = f_stbuf->fssh_st_ino;
115 	stbuf->st_mode = f_stbuf->fssh_st_mode;
116 	stbuf->st_nlink = f_stbuf->fssh_st_nlink;
117 	stbuf->st_uid = f_stbuf->fssh_st_uid;
118 	stbuf->st_gid = f_stbuf->fssh_st_gid;
119 	stbuf->st_rdev = f_stbuf->fssh_st_rdev;
120 	stbuf->st_size = f_stbuf->fssh_st_size;
121 	stbuf->st_blksize = f_stbuf->fssh_st_blksize;
122 	stbuf->st_blocks = f_stbuf->fssh_st_blocks;
123 	stbuf->st_atime = f_stbuf->fssh_st_atime;
124 	stbuf->st_mtime = f_stbuf->fssh_st_mtime;
125 	stbuf->st_ctime = f_stbuf->fssh_st_ctime;
126 }
127 
128 #define _ERR(x) (-1*fssh_to_host_error(x))
129 
130 
131 // pragma mark - FUSE functions
132 
133 
134 //extern "C" {
135 
136 
137 int
138 fuse_getattr(const char* path, struct stat* stbuf)
139 {
140 	PRINTD("##getattr\n");
141 	struct fssh_stat f_stbuf;
142 	fssh_status_t status = _kern_read_stat(-1, path, false, &f_stbuf,
143 			sizeof(f_stbuf));
144 	fromFsshStatToStat(&f_stbuf, stbuf);
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 	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, from, to,
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(from, to));
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 //} // extern "C" {
351 
352 
353 struct fuse_operations fuse_cmds;
354 
355 
356 static void
357 initialiseFuseOps(struct fuse_operations* fuse_cmds)
358 {
359 	fuse_cmds->getattr	= fuse_getattr;
360 	fuse_cmds->access	= fuse_access;
361 	fuse_cmds->readlink	= fuse_readlink;
362 	fuse_cmds->readdir	= fuse_readdir;
363 	fuse_cmds->mknod	= fuse_mknod;
364 	fuse_cmds->mkdir	= fuse_mkdir;
365 	fuse_cmds->symlink	= fuse_symlink;
366 	fuse_cmds->unlink	= fuse_unlink;
367 	fuse_cmds->rmdir	= fuse_rmdir;
368 	fuse_cmds->rename	= fuse_rename;
369 	fuse_cmds->link		= fuse_link;
370 	fuse_cmds->chmod	= fuse_chmod;
371 	fuse_cmds->chown	= fuse_chown;
372 	fuse_cmds->truncate	= NULL;
373 	fuse_cmds->utimens	= NULL;
374 	fuse_cmds->open		= fuse_open;
375 	fuse_cmds->read		= fuse_read;
376 	fuse_cmds->write	= fuse_write;
377 	fuse_cmds->statfs	= NULL;
378 	fuse_cmds->release	= NULL;
379 	fuse_cmds->fsync	= NULL;
380 	fuse_cmds->destroy	= fuse_destroy;
381 }
382 
383 
384 static int
385 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName)
386 {
387 	fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
388 	if (fsDev < 0) {
389 		fprintf(stderr, "Error: Mounting FS failed: %s\n",
390 			fssh_strerror(fsDev));
391 		return 1;
392 	}
393 	const char* argv[5];
394 	argv[0] = (const char*)"bfs_shell";
395 	argv[1] = mntPoint;
396 	argv[2] = (const char*)"-d";
397 	argv[3] = (const char*)"-s";
398 	initialiseFuseOps(&fuse_cmds);
399 	return fuse_main(4, (char**)argv, &fuse_cmds, NULL);
400 }
401 
402 
403 }	// namespace FSShell
404 
405 
406 using namespace FSShell;
407 
408 
409 static void
410 print_usage_and_exit(const char* binName)
411 {
412 	fprintf(stderr,"Usage: %s <device> <mount point>\n", binName);
413 	exit(1);
414 }
415 
416 
417 int
418 main(int argc, const char* const* argv)
419 {
420 	if (argc < 2)
421 		print_usage_and_exit(argv[0]);
422 
423 	const char* device = argv[1];
424 	const char* mntPoint = argv[2];
425 
426 	if (!modules[0]) {
427 		fprintf(stderr, "Error: Couldn't find FS module!\n");
428 		return 1;
429 	}
430 
431 	fssh_status_t error = init_kernel();
432 	if (error != FSSH_B_OK) {
433 		fprintf(stderr, "Error: Initializing kernel failed: %s\n",
434 			fssh_strerror(error));
435 		return error;
436 	}
437 	const char* fsName = modules[0]->name;
438 	return fssh_fuse_session(device, mntPoint, fsName);
439 }
440 
441