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