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