• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   FUSE: Filesystem in Userspace
3   Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4   Copyright (C) 2011       Sebastian Pipping <sebastian@pipping.org>
5 
6   This program can be distributed under the terms of the GNU GPLv2.
7   See the file COPYING.
8 */
9 
10 /** @file
11  *
12  * This file system mirrors the existing file system hierarchy of the
13  * system, starting at the root file system. This is implemented by
14  * just "passing through" all requests to the corresponding user-space
15  * libc functions. Its performance is terrible.
16  *
17  * Compile with
18  *
19  *     gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough
20  *
21  * ## Source code ##
22  * \include passthrough.c
23  */
24 
25 
26 #define FUSE_USE_VERSION 31
27 
28 #define _GNU_SOURCE
29 
30 #ifdef linux
31 /* For pread()/pwrite()/utimensat() */
32 #define _XOPEN_SOURCE 700
33 #endif
34 
35 #include <fuse.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <dirent.h>
42 #include <errno.h>
43 #ifdef __FreeBSD__
44 #include <sys/socket.h>
45 #include <sys/un.h>
46 #endif
47 #include <sys/time.h>
48 #ifdef HAVE_SETXATTR
49 #include <sys/xattr.h>
50 #endif
51 
52 #include "passthrough_helpers.h"
53 
54 static int fill_dir_plus = 0;
55 
xmp_init(struct fuse_conn_info * conn,struct fuse_config * cfg)56 static void *xmp_init(struct fuse_conn_info *conn,
57 		      struct fuse_config *cfg)
58 {
59 	(void) conn;
60 	cfg->use_ino = 1;
61 
62 	/* Pick up changes from lower filesystem right away. This is
63 	   also necessary for better hardlink support. When the kernel
64 	   calls the unlink() handler, it does not know the inode of
65 	   the to-be-removed entry and can therefore not invalidate
66 	   the cache of the associated inode - resulting in an
67 	   incorrect st_nlink value being reported for any remaining
68 	   hardlinks to this inode. */
69 	cfg->entry_timeout = 0;
70 	cfg->attr_timeout = 0;
71 	cfg->negative_timeout = 0;
72 
73 	return NULL;
74 }
75 
xmp_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)76 static int xmp_getattr(const char *path, struct stat *stbuf,
77 		       struct fuse_file_info *fi)
78 {
79 	(void) fi;
80 	int res;
81 
82 	res = lstat(path, stbuf);
83 	if (res == -1)
84 		return -errno;
85 
86 	return 0;
87 }
88 
xmp_access(const char * path,int mask)89 static int xmp_access(const char *path, int mask)
90 {
91 	int res;
92 
93 	res = access(path, mask);
94 	if (res == -1)
95 		return -errno;
96 
97 	return 0;
98 }
99 
xmp_readlink(const char * path,char * buf,size_t size)100 static int xmp_readlink(const char *path, char *buf, size_t size)
101 {
102 	int res;
103 
104 	res = readlink(path, buf, size - 1);
105 	if (res == -1)
106 		return -errno;
107 
108 	buf[res] = '\0';
109 	return 0;
110 }
111 
112 
xmp_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)113 static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
114 		       off_t offset, struct fuse_file_info *fi,
115 		       enum fuse_readdir_flags flags)
116 {
117 	DIR *dp;
118 	struct dirent *de;
119 
120 	(void) offset;
121 	(void) fi;
122 	(void) flags;
123 
124 	dp = opendir(path);
125 	if (dp == NULL)
126 		return -errno;
127 
128 	while ((de = readdir(dp)) != NULL) {
129 		struct stat st;
130 		memset(&st, 0, sizeof(st));
131 		st.st_ino = de->d_ino;
132 		st.st_mode = de->d_type << 12;
133 		if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
134 			break;
135 	}
136 
137 	closedir(dp);
138 	return 0;
139 }
140 
xmp_mknod(const char * path,mode_t mode,dev_t rdev)141 static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
142 {
143 	int res;
144 
145 	res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
146 	if (res == -1)
147 		return -errno;
148 
149 	return 0;
150 }
151 
xmp_mkdir(const char * path,mode_t mode)152 static int xmp_mkdir(const char *path, mode_t mode)
153 {
154 	int res;
155 
156 	res = mkdir(path, mode);
157 	if (res == -1)
158 		return -errno;
159 
160 	return 0;
161 }
162 
xmp_unlink(const char * path)163 static int xmp_unlink(const char *path)
164 {
165 	int res;
166 
167 	res = unlink(path);
168 	if (res == -1)
169 		return -errno;
170 
171 	return 0;
172 }
173 
xmp_rmdir(const char * path)174 static int xmp_rmdir(const char *path)
175 {
176 	int res;
177 
178 	res = rmdir(path);
179 	if (res == -1)
180 		return -errno;
181 
182 	return 0;
183 }
184 
xmp_symlink(const char * from,const char * to)185 static int xmp_symlink(const char *from, const char *to)
186 {
187 	int res;
188 
189 	res = symlink(from, to);
190 	if (res == -1)
191 		return -errno;
192 
193 	return 0;
194 }
195 
xmp_rename(const char * from,const char * to,unsigned int flags)196 static int xmp_rename(const char *from, const char *to, unsigned int flags)
197 {
198 	int res;
199 
200 	if (flags)
201 		return -EINVAL;
202 
203 	res = rename(from, to);
204 	if (res == -1)
205 		return -errno;
206 
207 	return 0;
208 }
209 
xmp_link(const char * from,const char * to)210 static int xmp_link(const char *from, const char *to)
211 {
212 	int res;
213 
214 	res = link(from, to);
215 	if (res == -1)
216 		return -errno;
217 
218 	return 0;
219 }
220 
xmp_chmod(const char * path,mode_t mode,struct fuse_file_info * fi)221 static int xmp_chmod(const char *path, mode_t mode,
222 		     struct fuse_file_info *fi)
223 {
224 	(void) fi;
225 	int res;
226 
227 	res = chmod(path, mode);
228 	if (res == -1)
229 		return -errno;
230 
231 	return 0;
232 }
233 
xmp_chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)234 static int xmp_chown(const char *path, uid_t uid, gid_t gid,
235 		     struct fuse_file_info *fi)
236 {
237 	(void) fi;
238 	int res;
239 
240 	res = lchown(path, uid, gid);
241 	if (res == -1)
242 		return -errno;
243 
244 	return 0;
245 }
246 
xmp_truncate(const char * path,off_t size,struct fuse_file_info * fi)247 static int xmp_truncate(const char *path, off_t size,
248 			struct fuse_file_info *fi)
249 {
250 	int res;
251 
252 	if (fi != NULL)
253 		res = ftruncate(fi->fh, size);
254 	else
255 		res = truncate(path, size);
256 	if (res == -1)
257 		return -errno;
258 
259 	return 0;
260 }
261 
262 #ifdef HAVE_UTIMENSAT
xmp_utimens(const char * path,const struct timespec ts[2],struct fuse_file_info * fi)263 static int xmp_utimens(const char *path, const struct timespec ts[2],
264 		       struct fuse_file_info *fi)
265 {
266 	(void) fi;
267 	int res;
268 
269 	/* don't use utime/utimes since they follow symlinks */
270 	res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
271 	if (res == -1)
272 		return -errno;
273 
274 	return 0;
275 }
276 #endif
277 
xmp_create(const char * path,mode_t mode,struct fuse_file_info * fi)278 static int xmp_create(const char *path, mode_t mode,
279 		      struct fuse_file_info *fi)
280 {
281 	int res;
282 
283 	res = open(path, fi->flags, mode);
284 	if (res == -1)
285 		return -errno;
286 
287 	fi->fh = res;
288 	return 0;
289 }
290 
xmp_open(const char * path,struct fuse_file_info * fi)291 static int xmp_open(const char *path, struct fuse_file_info *fi)
292 {
293 	int res;
294 
295 	res = open(path, fi->flags);
296 	if (res == -1)
297 		return -errno;
298 
299 	fi->fh = res;
300 	return 0;
301 }
302 
xmp_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)303 static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
304 		    struct fuse_file_info *fi)
305 {
306 	int fd;
307 	int res;
308 
309 	if(fi == NULL)
310 		fd = open(path, O_RDONLY);
311 	else
312 		fd = fi->fh;
313 
314 	if (fd == -1)
315 		return -errno;
316 
317 	res = pread(fd, buf, size, offset);
318 	if (res == -1)
319 		res = -errno;
320 
321 	if(fi == NULL)
322 		close(fd);
323 	return res;
324 }
325 
xmp_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)326 static int xmp_write(const char *path, const char *buf, size_t size,
327 		     off_t offset, struct fuse_file_info *fi)
328 {
329 	int fd;
330 	int res;
331 
332 	(void) fi;
333 	if(fi == NULL)
334 		fd = open(path, O_WRONLY);
335 	else
336 		fd = fi->fh;
337 
338 	if (fd == -1)
339 		return -errno;
340 
341 	res = pwrite(fd, buf, size, offset);
342 	if (res == -1)
343 		res = -errno;
344 
345 	if(fi == NULL)
346 		close(fd);
347 	return res;
348 }
349 
xmp_statfs(const char * path,struct statvfs * stbuf)350 static int xmp_statfs(const char *path, struct statvfs *stbuf)
351 {
352 	int res;
353 
354 	res = statvfs(path, stbuf);
355 	if (res == -1)
356 		return -errno;
357 
358 	return 0;
359 }
360 
xmp_release(const char * path,struct fuse_file_info * fi)361 static int xmp_release(const char *path, struct fuse_file_info *fi)
362 {
363 	(void) path;
364 	close(fi->fh);
365 	return 0;
366 }
367 
xmp_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)368 static int xmp_fsync(const char *path, int isdatasync,
369 		     struct fuse_file_info *fi)
370 {
371 	/* Just a stub.	 This method is optional and can safely be left
372 	   unimplemented */
373 
374 	(void) path;
375 	(void) isdatasync;
376 	(void) fi;
377 	return 0;
378 }
379 
380 #ifdef HAVE_POSIX_FALLOCATE
xmp_fallocate(const char * path,int mode,off_t offset,off_t length,struct fuse_file_info * fi)381 static int xmp_fallocate(const char *path, int mode,
382 			off_t offset, off_t length, struct fuse_file_info *fi)
383 {
384 	int fd;
385 	int res;
386 
387 	(void) fi;
388 
389 	if (mode)
390 		return -EOPNOTSUPP;
391 
392 	if(fi == NULL)
393 		fd = open(path, O_WRONLY);
394 	else
395 		fd = fi->fh;
396 
397 	if (fd == -1)
398 		return -errno;
399 
400 	res = -posix_fallocate(fd, offset, length);
401 
402 	if(fi == NULL)
403 		close(fd);
404 	return res;
405 }
406 #endif
407 
408 #ifdef HAVE_SETXATTR
409 /* xattr operations are optional and can safely be left unimplemented */
xmp_setxattr(const char * path,const char * name,const char * value,size_t size,int flags)410 static int xmp_setxattr(const char *path, const char *name, const char *value,
411 			size_t size, int flags)
412 {
413 	int res = lsetxattr(path, name, value, size, flags);
414 	if (res == -1)
415 		return -errno;
416 	return 0;
417 }
418 
xmp_getxattr(const char * path,const char * name,char * value,size_t size)419 static int xmp_getxattr(const char *path, const char *name, char *value,
420 			size_t size)
421 {
422 	int res = lgetxattr(path, name, value, size);
423 	if (res == -1)
424 		return -errno;
425 	return res;
426 }
427 
xmp_listxattr(const char * path,char * list,size_t size)428 static int xmp_listxattr(const char *path, char *list, size_t size)
429 {
430 	int res = llistxattr(path, list, size);
431 	if (res == -1)
432 		return -errno;
433 	return res;
434 }
435 
xmp_removexattr(const char * path,const char * name)436 static int xmp_removexattr(const char *path, const char *name)
437 {
438 	int res = lremovexattr(path, name);
439 	if (res == -1)
440 		return -errno;
441 	return 0;
442 }
443 #endif /* HAVE_SETXATTR */
444 
445 #ifdef HAVE_COPY_FILE_RANGE
xmp_copy_file_range(const char * path_in,struct fuse_file_info * fi_in,off_t offset_in,const char * path_out,struct fuse_file_info * fi_out,off_t offset_out,size_t len,int flags)446 static ssize_t xmp_copy_file_range(const char *path_in,
447 				   struct fuse_file_info *fi_in,
448 				   off_t offset_in, const char *path_out,
449 				   struct fuse_file_info *fi_out,
450 				   off_t offset_out, size_t len, int flags)
451 {
452 	int fd_in, fd_out;
453 	ssize_t res;
454 
455 	if(fi_in == NULL)
456 		fd_in = open(path_in, O_RDONLY);
457 	else
458 		fd_in = fi_in->fh;
459 
460 	if (fd_in == -1)
461 		return -errno;
462 
463 	if(fi_out == NULL)
464 		fd_out = open(path_out, O_WRONLY);
465 	else
466 		fd_out = fi_out->fh;
467 
468 	if (fd_out == -1) {
469 		close(fd_in);
470 		return -errno;
471 	}
472 
473 	res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
474 			      flags);
475 	if (res == -1)
476 		res = -errno;
477 
478 	if (fi_out == NULL)
479 		close(fd_out);
480 	if (fi_in == NULL)
481 		close(fd_in);
482 
483 	return res;
484 }
485 #endif
486 
xmp_lseek(const char * path,off_t off,int whence,struct fuse_file_info * fi)487 static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
488 {
489 	int fd;
490 	off_t res;
491 
492 	if (fi == NULL)
493 		fd = open(path, O_RDONLY);
494 	else
495 		fd = fi->fh;
496 
497 	if (fd == -1)
498 		return -errno;
499 
500 	res = lseek(fd, off, whence);
501 	if (res == -1)
502 		res = -errno;
503 
504 	if (fi == NULL)
505 		close(fd);
506 	return res;
507 }
508 
509 static const struct fuse_operations xmp_oper = {
510 	.init           = xmp_init,
511 	.getattr	= xmp_getattr,
512 	.access		= xmp_access,
513 	.readlink	= xmp_readlink,
514 	.readdir	= xmp_readdir,
515 	.mknod		= xmp_mknod,
516 	.mkdir		= xmp_mkdir,
517 	.symlink	= xmp_symlink,
518 	.unlink		= xmp_unlink,
519 	.rmdir		= xmp_rmdir,
520 	.rename		= xmp_rename,
521 	.link		= xmp_link,
522 	.chmod		= xmp_chmod,
523 	.chown		= xmp_chown,
524 	.truncate	= xmp_truncate,
525 #ifdef HAVE_UTIMENSAT
526 	.utimens	= xmp_utimens,
527 #endif
528 	.open		= xmp_open,
529 	.create 	= xmp_create,
530 	.read		= xmp_read,
531 	.write		= xmp_write,
532 	.statfs		= xmp_statfs,
533 	.release	= xmp_release,
534 	.fsync		= xmp_fsync,
535 #ifdef HAVE_POSIX_FALLOCATE
536 	.fallocate	= xmp_fallocate,
537 #endif
538 #ifdef HAVE_SETXATTR
539 	.setxattr	= xmp_setxattr,
540 	.getxattr	= xmp_getxattr,
541 	.listxattr	= xmp_listxattr,
542 	.removexattr	= xmp_removexattr,
543 #endif
544 #ifdef HAVE_COPY_FILE_RANGE
545 	.copy_file_range = xmp_copy_file_range,
546 #endif
547 	.lseek		= xmp_lseek,
548 };
549 
main(int argc,char * argv[])550 int main(int argc, char *argv[])
551 {
552 	enum { MAX_ARGS = 10 };
553 	int i,new_argc;
554 	char *new_argv[MAX_ARGS];
555 
556 	umask(0);
557 			/* Process the "--plus" option apart */
558 	for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
559 		if (!strcmp(argv[i], "--plus")) {
560 			fill_dir_plus = FUSE_FILL_DIR_PLUS;
561 		} else {
562 			new_argv[new_argc++] = argv[i];
563 		}
564 	}
565 	return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
566 }
567