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. This implementation is a little more sophisticated
16 * than the one in passthrough.c, so performance is not quite as bad.
17 *
18 * Compile with:
19 *
20 * gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh
21 *
22 * ## Source code ##
23 * \include passthrough_fh.c
24 */
25
26 #define FUSE_USE_VERSION 31
27
28 #define _GNU_SOURCE
29
30 #include <fuse.h>
31
32 #ifdef HAVE_LIBULOCKMGR
33 #include <ulockmgr.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <sys/time.h>
45 #ifdef HAVE_SETXATTR
46 #include <sys/xattr.h>
47 #endif
48 #include <sys/file.h> /* flock(2) */
49
xmp_init(struct fuse_conn_info * conn,struct fuse_config * cfg)50 static void *xmp_init(struct fuse_conn_info *conn,
51 struct fuse_config *cfg)
52 {
53 (void) conn;
54 cfg->use_ino = 1;
55 cfg->nullpath_ok = 1;
56
57 /* Pick up changes from lower filesystem right away. This is
58 also necessary for better hardlink support. When the kernel
59 calls the unlink() handler, it does not know the inode of
60 the to-be-removed entry and can therefore not invalidate
61 the cache of the associated inode - resulting in an
62 incorrect st_nlink value being reported for any remaining
63 hardlinks to this inode. */
64 cfg->entry_timeout = 0;
65 cfg->attr_timeout = 0;
66 cfg->negative_timeout = 0;
67
68 return NULL;
69 }
70
xmp_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)71 static int xmp_getattr(const char *path, struct stat *stbuf,
72 struct fuse_file_info *fi)
73 {
74 int res;
75
76 (void) path;
77
78 if(fi)
79 res = fstat(fi->fh, stbuf);
80 else
81 res = lstat(path, stbuf);
82 if (res == -1)
83 return -errno;
84
85 return 0;
86 }
87
xmp_access(const char * path,int mask)88 static int xmp_access(const char *path, int mask)
89 {
90 int res;
91
92 res = access(path, mask);
93 if (res == -1)
94 return -errno;
95
96 return 0;
97 }
98
xmp_readlink(const char * path,char * buf,size_t size)99 static int xmp_readlink(const char *path, char *buf, size_t size)
100 {
101 int res;
102
103 res = readlink(path, buf, size - 1);
104 if (res == -1)
105 return -errno;
106
107 buf[res] = '\0';
108 return 0;
109 }
110
111 struct xmp_dirp {
112 DIR *dp;
113 struct dirent *entry;
114 off_t offset;
115 };
116
xmp_opendir(const char * path,struct fuse_file_info * fi)117 static int xmp_opendir(const char *path, struct fuse_file_info *fi)
118 {
119 int res;
120 struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
121 if (d == NULL)
122 return -ENOMEM;
123
124 d->dp = opendir(path);
125 if (d->dp == NULL) {
126 res = -errno;
127 free(d);
128 return res;
129 }
130 d->offset = 0;
131 d->entry = NULL;
132
133 fi->fh = (unsigned long) d;
134 return 0;
135 }
136
get_dirp(struct fuse_file_info * fi)137 static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
138 {
139 return (struct xmp_dirp *) (uintptr_t) fi->fh;
140 }
141
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)142 static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
143 off_t offset, struct fuse_file_info *fi,
144 enum fuse_readdir_flags flags)
145 {
146 struct xmp_dirp *d = get_dirp(fi);
147
148 (void) path;
149 if (offset != d->offset) {
150 #ifndef __FreeBSD__
151 seekdir(d->dp, offset);
152 #else
153 /* Subtract the one that we add when calling
154 telldir() below */
155 seekdir(d->dp, offset-1);
156 #endif
157 d->entry = NULL;
158 d->offset = offset;
159 }
160 while (1) {
161 struct stat st;
162 off_t nextoff;
163 enum fuse_fill_dir_flags fill_flags = 0;
164
165 if (!d->entry) {
166 d->entry = readdir(d->dp);
167 if (!d->entry)
168 break;
169 }
170 #ifdef HAVE_FSTATAT
171 if (flags & FUSE_READDIR_PLUS) {
172 int res;
173
174 res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
175 AT_SYMLINK_NOFOLLOW);
176 if (res != -1)
177 fill_flags |= FUSE_FILL_DIR_PLUS;
178 }
179 #endif
180 if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
181 memset(&st, 0, sizeof(st));
182 st.st_ino = d->entry->d_ino;
183 st.st_mode = d->entry->d_type << 12;
184 }
185 nextoff = telldir(d->dp);
186 #ifdef __FreeBSD__
187 /* Under FreeBSD, telldir() may return 0 the first time
188 it is called. But for libfuse, an offset of zero
189 means that offsets are not supported, so we shift
190 everything by one. */
191 nextoff++;
192 #endif
193 if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
194 break;
195
196 d->entry = NULL;
197 d->offset = nextoff;
198 }
199
200 return 0;
201 }
202
xmp_releasedir(const char * path,struct fuse_file_info * fi)203 static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
204 {
205 struct xmp_dirp *d = get_dirp(fi);
206 (void) path;
207 closedir(d->dp);
208 free(d);
209 return 0;
210 }
211
xmp_mknod(const char * path,mode_t mode,dev_t rdev)212 static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
213 {
214 int res;
215
216 if (S_ISFIFO(mode))
217 res = mkfifo(path, mode);
218 else
219 res = mknod(path, mode, rdev);
220 if (res == -1)
221 return -errno;
222
223 return 0;
224 }
225
xmp_mkdir(const char * path,mode_t mode)226 static int xmp_mkdir(const char *path, mode_t mode)
227 {
228 int res;
229
230 res = mkdir(path, mode);
231 if (res == -1)
232 return -errno;
233
234 return 0;
235 }
236
xmp_unlink(const char * path)237 static int xmp_unlink(const char *path)
238 {
239 int res;
240
241 res = unlink(path);
242 if (res == -1)
243 return -errno;
244
245 return 0;
246 }
247
xmp_rmdir(const char * path)248 static int xmp_rmdir(const char *path)
249 {
250 int res;
251
252 res = rmdir(path);
253 if (res == -1)
254 return -errno;
255
256 return 0;
257 }
258
xmp_symlink(const char * from,const char * to)259 static int xmp_symlink(const char *from, const char *to)
260 {
261 int res;
262
263 res = symlink(from, to);
264 if (res == -1)
265 return -errno;
266
267 return 0;
268 }
269
xmp_rename(const char * from,const char * to,unsigned int flags)270 static int xmp_rename(const char *from, const char *to, unsigned int flags)
271 {
272 int res;
273
274 /* When we have renameat2() in libc, then we can implement flags */
275 if (flags)
276 return -EINVAL;
277
278 res = rename(from, to);
279 if (res == -1)
280 return -errno;
281
282 return 0;
283 }
284
xmp_link(const char * from,const char * to)285 static int xmp_link(const char *from, const char *to)
286 {
287 int res;
288
289 res = link(from, to);
290 if (res == -1)
291 return -errno;
292
293 return 0;
294 }
295
xmp_chmod(const char * path,mode_t mode,struct fuse_file_info * fi)296 static int xmp_chmod(const char *path, mode_t mode,
297 struct fuse_file_info *fi)
298 {
299 int res;
300
301 if(fi)
302 res = fchmod(fi->fh, mode);
303 else
304 res = chmod(path, mode);
305 if (res == -1)
306 return -errno;
307
308 return 0;
309 }
310
xmp_chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)311 static int xmp_chown(const char *path, uid_t uid, gid_t gid,
312 struct fuse_file_info *fi)
313 {
314 int res;
315
316 if (fi)
317 res = fchown(fi->fh, uid, gid);
318 else
319 res = lchown(path, uid, gid);
320 if (res == -1)
321 return -errno;
322
323 return 0;
324 }
325
xmp_truncate(const char * path,off_t size,struct fuse_file_info * fi)326 static int xmp_truncate(const char *path, off_t size,
327 struct fuse_file_info *fi)
328 {
329 int res;
330
331 if(fi)
332 res = ftruncate(fi->fh, size);
333 else
334 res = truncate(path, size);
335
336 if (res == -1)
337 return -errno;
338
339 return 0;
340 }
341
342 #ifdef HAVE_UTIMENSAT
xmp_utimens(const char * path,const struct timespec ts[2],struct fuse_file_info * fi)343 static int xmp_utimens(const char *path, const struct timespec ts[2],
344 struct fuse_file_info *fi)
345 {
346 int res;
347
348 /* don't use utime/utimes since they follow symlinks */
349 if (fi)
350 res = futimens(fi->fh, ts);
351 else
352 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
353 if (res == -1)
354 return -errno;
355
356 return 0;
357 }
358 #endif
359
xmp_create(const char * path,mode_t mode,struct fuse_file_info * fi)360 static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
361 {
362 int fd;
363
364 fd = open(path, fi->flags, mode);
365 if (fd == -1)
366 return -errno;
367
368 fi->fh = fd;
369 return 0;
370 }
371
xmp_open(const char * path,struct fuse_file_info * fi)372 static int xmp_open(const char *path, struct fuse_file_info *fi)
373 {
374 int fd;
375
376 fd = open(path, fi->flags);
377 if (fd == -1)
378 return -errno;
379
380 fi->fh = fd;
381 return 0;
382 }
383
xmp_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)384 static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
385 struct fuse_file_info *fi)
386 {
387 int res;
388
389 (void) path;
390 res = pread(fi->fh, buf, size, offset);
391 if (res == -1)
392 res = -errno;
393
394 return res;
395 }
396
xmp_read_buf(const char * path,struct fuse_bufvec ** bufp,size_t size,off_t offset,struct fuse_file_info * fi)397 static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
398 size_t size, off_t offset, struct fuse_file_info *fi)
399 {
400 struct fuse_bufvec *src;
401
402 (void) path;
403
404 src = malloc(sizeof(struct fuse_bufvec));
405 if (src == NULL)
406 return -ENOMEM;
407
408 *src = FUSE_BUFVEC_INIT(size);
409
410 src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
411 src->buf[0].fd = fi->fh;
412 src->buf[0].pos = offset;
413
414 *bufp = src;
415
416 return 0;
417 }
418
xmp_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)419 static int xmp_write(const char *path, const char *buf, size_t size,
420 off_t offset, struct fuse_file_info *fi)
421 {
422 int res;
423
424 (void) path;
425 res = pwrite(fi->fh, buf, size, offset);
426 if (res == -1)
427 res = -errno;
428
429 return res;
430 }
431
xmp_write_buf(const char * path,struct fuse_bufvec * buf,off_t offset,struct fuse_file_info * fi)432 static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
433 off_t offset, struct fuse_file_info *fi)
434 {
435 struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
436
437 (void) path;
438
439 dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
440 dst.buf[0].fd = fi->fh;
441 dst.buf[0].pos = offset;
442
443 return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
444 }
445
xmp_statfs(const char * path,struct statvfs * stbuf)446 static int xmp_statfs(const char *path, struct statvfs *stbuf)
447 {
448 int res;
449
450 res = statvfs(path, stbuf);
451 if (res == -1)
452 return -errno;
453
454 return 0;
455 }
456
xmp_flush(const char * path,struct fuse_file_info * fi)457 static int xmp_flush(const char *path, struct fuse_file_info *fi)
458 {
459 int res;
460
461 (void) path;
462 /* This is called from every close on an open file, so call the
463 close on the underlying filesystem. But since flush may be
464 called multiple times for an open file, this must not really
465 close the file. This is important if used on a network
466 filesystem like NFS which flush the data/metadata on close() */
467 res = close(dup(fi->fh));
468 if (res == -1)
469 return -errno;
470
471 return 0;
472 }
473
xmp_release(const char * path,struct fuse_file_info * fi)474 static int xmp_release(const char *path, struct fuse_file_info *fi)
475 {
476 (void) path;
477 close(fi->fh);
478
479 return 0;
480 }
481
xmp_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)482 static int xmp_fsync(const char *path, int isdatasync,
483 struct fuse_file_info *fi)
484 {
485 int res;
486 (void) path;
487
488 #ifndef HAVE_FDATASYNC
489 (void) isdatasync;
490 #else
491 if (isdatasync)
492 res = fdatasync(fi->fh);
493 else
494 #endif
495 res = fsync(fi->fh);
496 if (res == -1)
497 return -errno;
498
499 return 0;
500 }
501
502 #ifdef HAVE_POSIX_FALLOCATE
xmp_fallocate(const char * path,int mode,off_t offset,off_t length,struct fuse_file_info * fi)503 static int xmp_fallocate(const char *path, int mode,
504 off_t offset, off_t length, struct fuse_file_info *fi)
505 {
506 (void) path;
507
508 if (mode)
509 return -EOPNOTSUPP;
510
511 return -posix_fallocate(fi->fh, offset, length);
512 }
513 #endif
514
515 #ifdef HAVE_SETXATTR
516 /* 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)517 static int xmp_setxattr(const char *path, const char *name, const char *value,
518 size_t size, int flags)
519 {
520 int res = lsetxattr(path, name, value, size, flags);
521 if (res == -1)
522 return -errno;
523 return 0;
524 }
525
xmp_getxattr(const char * path,const char * name,char * value,size_t size)526 static int xmp_getxattr(const char *path, const char *name, char *value,
527 size_t size)
528 {
529 int res = lgetxattr(path, name, value, size);
530 if (res == -1)
531 return -errno;
532 return res;
533 }
534
xmp_listxattr(const char * path,char * list,size_t size)535 static int xmp_listxattr(const char *path, char *list, size_t size)
536 {
537 int res = llistxattr(path, list, size);
538 if (res == -1)
539 return -errno;
540 return res;
541 }
542
xmp_removexattr(const char * path,const char * name)543 static int xmp_removexattr(const char *path, const char *name)
544 {
545 int res = lremovexattr(path, name);
546 if (res == -1)
547 return -errno;
548 return 0;
549 }
550 #endif /* HAVE_SETXATTR */
551
552 #ifdef HAVE_LIBULOCKMGR
xmp_lock(const char * path,struct fuse_file_info * fi,int cmd,struct flock * lock)553 static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
554 struct flock *lock)
555 {
556 (void) path;
557
558 return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
559 sizeof(fi->lock_owner));
560 }
561 #endif
562
xmp_flock(const char * path,struct fuse_file_info * fi,int op)563 static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
564 {
565 int res;
566 (void) path;
567
568 res = flock(fi->fh, op);
569 if (res == -1)
570 return -errno;
571
572 return 0;
573 }
574
575 #ifdef HAVE_COPY_FILE_RANGE
xmp_copy_file_range(const char * path_in,struct fuse_file_info * fi_in,off_t off_in,const char * path_out,struct fuse_file_info * fi_out,off_t off_out,size_t len,int flags)576 static ssize_t xmp_copy_file_range(const char *path_in,
577 struct fuse_file_info *fi_in,
578 off_t off_in, const char *path_out,
579 struct fuse_file_info *fi_out,
580 off_t off_out, size_t len, int flags)
581 {
582 ssize_t res;
583 (void) path_in;
584 (void) path_out;
585
586 res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
587 flags);
588 if (res == -1)
589 return -errno;
590
591 return res;
592 }
593 #endif
594
xmp_lseek(const char * path,off_t off,int whence,struct fuse_file_info * fi)595 static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
596 {
597 off_t res;
598 (void) path;
599
600 res = lseek(fi->fh, off, whence);
601 if (res == -1)
602 return -errno;
603
604 return res;
605 }
606
607 static const struct fuse_operations xmp_oper = {
608 .init = xmp_init,
609 .getattr = xmp_getattr,
610 .access = xmp_access,
611 .readlink = xmp_readlink,
612 .opendir = xmp_opendir,
613 .readdir = xmp_readdir,
614 .releasedir = xmp_releasedir,
615 .mknod = xmp_mknod,
616 .mkdir = xmp_mkdir,
617 .symlink = xmp_symlink,
618 .unlink = xmp_unlink,
619 .rmdir = xmp_rmdir,
620 .rename = xmp_rename,
621 .link = xmp_link,
622 .chmod = xmp_chmod,
623 .chown = xmp_chown,
624 .truncate = xmp_truncate,
625 #ifdef HAVE_UTIMENSAT
626 .utimens = xmp_utimens,
627 #endif
628 .create = xmp_create,
629 .open = xmp_open,
630 .read = xmp_read,
631 .read_buf = xmp_read_buf,
632 .write = xmp_write,
633 .write_buf = xmp_write_buf,
634 .statfs = xmp_statfs,
635 .flush = xmp_flush,
636 .release = xmp_release,
637 .fsync = xmp_fsync,
638 #ifdef HAVE_POSIX_FALLOCATE
639 .fallocate = xmp_fallocate,
640 #endif
641 #ifdef HAVE_SETXATTR
642 .setxattr = xmp_setxattr,
643 .getxattr = xmp_getxattr,
644 .listxattr = xmp_listxattr,
645 .removexattr = xmp_removexattr,
646 #endif
647 #ifdef HAVE_LIBULOCKMGR
648 .lock = xmp_lock,
649 #endif
650 .flock = xmp_flock,
651 #ifdef HAVE_COPY_FILE_RANGE
652 .copy_file_range = xmp_copy_file_range,
653 #endif
654 .lseek = xmp_lseek,
655 };
656
main(int argc,char * argv[])657 int main(int argc, char *argv[])
658 {
659 umask(0);
660 return fuse_main(argc, argv, &xmp_oper, NULL);
661 }
662