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