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