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