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