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 /* 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
71static 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
88static 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
99static 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
111struct xmp_dirp {
112 DIR *dp;
113 struct dirent *entry;
114 off_t offset;
115};
116
117static 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
137static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
138{
139 return (struct xmp_dirp *) (uintptr_t) fi->fh;
140}
141
142static 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
203static 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
212static 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
226static 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
237static 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
248static 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
259static 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
270static 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
285static 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
296static 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
311static 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
326static 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
343static 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
360static 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
372static 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
384static 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
397static 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
411 src->buf[0].fd = fi->fh;
412 src->buf[0].pos = offset;
413
414 *bufp = src;
415
416 return 0;
417}
418
419static 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
432static 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
440 dst.buf[0].fd = fi->fh;
441 dst.buf[0].pos = offset;
442
444}
445
446static 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
457static 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
474static 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
482static 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
503static 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 */
517static 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
526static 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
535static 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
543static 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
553static 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
563static 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
576static 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
595static 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
607static 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
657int main(int argc, char *argv[])
658{
659 umask(0);
660 return fuse_main(argc, argv, &xmp_oper, NULL);
661}
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:85
#define fuse_main(argc, argv, op, private_data)
Definition: fuse.h:913
fuse_fill_dir_flags
Definition: fuse.h:57
@ FUSE_FILL_DIR_PLUS
Definition: fuse.h:67
fuse_readdir_flags
Definition: fuse.h:42
@ FUSE_READDIR_PLUS
Definition: fuse.h:51
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition: buffer.c:22
@ FUSE_BUF_FD_SEEK
Definition: fuse_common.h:688
@ FUSE_BUF_IS_FD
Definition: fuse_common.h:679
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
Definition: fuse_common.h:739
enum fuse_buf_flags flags
Definition: fuse_common.h:757
off_t pos
Definition: fuse_common.h:778
size_t off
Definition: fuse_common.h:803
struct fuse_buf buf[1]
Definition: fuse_common.h:808
double entry_timeout
Definition: fuse.h:122
double negative_timeout
Definition: fuse.h:132
int nullpath_ok
Definition: fuse.h:276
int use_ino
Definition: fuse.h:193
double attr_timeout
Definition: fuse.h:138
uint64_t lock_owner
Definition: fuse_common.h:109
void *(* init)(struct fuse_conn_info *conn, struct fuse_config *cfg)
Definition: fuse.h:616