libfuse
passthrough.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#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#include <sys/time.h>
44#ifdef HAVE_SETXATTR
45#include <sys/xattr.h>
46#endif
47
48#include "passthrough_helpers.h"
49
50static int fill_dir_plus = 0;
51static int readdir_zero_ino;
52
53static void *xmp_init(struct fuse_conn_info *conn,
54 struct fuse_config *cfg)
55{
56 (void) conn;
57 cfg->use_ino = !readdir_zero_ino;
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 if (!cfg->auto_cache) {
74 cfg->entry_timeout = 0;
75 cfg->attr_timeout = 0;
76 cfg->negative_timeout = 0;
77 }
78
79 return NULL;
80}
81
82static int xmp_getattr(const char *path, struct stat *stbuf,
83 struct fuse_file_info *fi)
84{
85 (void) fi;
86 int res;
87
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
118
119static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
120 off_t offset, struct fuse_file_info *fi,
121 enum fuse_readdir_flags flags)
122{
123 DIR *dp;
124 struct dirent *de;
125
126 (void) offset;
127 (void) fi;
128 (void) flags;
129
130 dp = opendir(path);
131 if (dp == NULL)
132 return -errno;
133
134 while ((de = readdir(dp)) != NULL) {
135 struct stat st;
136 if (fill_dir_plus) {
137 fstatat(dirfd(dp), de->d_name, &st,
138 AT_SYMLINK_NOFOLLOW);
139 } else {
140 memset(&st, 0, sizeof(st));
141 st.st_ino = de->d_ino;
142 st.st_mode = de->d_type << 12;
143 }
144 if (readdir_zero_ino)
145 st.st_ino = 0;
146 if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
147 break;
148 }
149
150 closedir(dp);
151 return 0;
152}
153
154static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
155{
156 int res;
157
158 res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
159 if (res == -1)
160 return -errno;
161
162 return 0;
163}
164
165static int xmp_mkdir(const char *path, mode_t mode)
166{
167 int res;
168
169 res = mkdir(path, mode);
170 if (res == -1)
171 return -errno;
172
173 return 0;
174}
175
176static int xmp_unlink(const char *path)
177{
178 int res;
179
180 res = unlink(path);
181 if (res == -1)
182 return -errno;
183
184 return 0;
185}
186
187static int xmp_rmdir(const char *path)
188{
189 int res;
190
191 res = rmdir(path);
192 if (res == -1)
193 return -errno;
194
195 return 0;
196}
197
198static int xmp_symlink(const char *from, const char *to)
199{
200 int res;
201
202 res = symlink(from, to);
203 if (res == -1)
204 return -errno;
205
206 return 0;
207}
208
209static int xmp_rename(const char *from, const char *to, unsigned int flags)
210{
211 int res;
212
213 if (flags)
214 return -EINVAL;
215
216 res = rename(from, to);
217 if (res == -1)
218 return -errno;
219
220 return 0;
221}
222
223static int xmp_link(const char *from, const char *to)
224{
225 int res;
226
227 res = link(from, to);
228 if (res == -1)
229 return -errno;
230
231 return 0;
232}
233
234static int xmp_chmod(const char *path, mode_t mode,
235 struct fuse_file_info *fi)
236{
237 (void) fi;
238 int res;
239
240 res = chmod(path, mode);
241 if (res == -1)
242 return -errno;
243
244 return 0;
245}
246
247static int xmp_chown(const char *path, uid_t uid, gid_t gid,
248 struct fuse_file_info *fi)
249{
250 (void) fi;
251 int res;
252
253 res = lchown(path, uid, gid);
254 if (res == -1)
255 return -errno;
256
257 return 0;
258}
259
260static int xmp_truncate(const char *path, off_t size,
261 struct fuse_file_info *fi)
262{
263 int res;
264
265 if (fi != NULL)
266 res = ftruncate(fi->fh, size);
267 else
268 res = truncate(path, size);
269 if (res == -1)
270 return -errno;
271
272 return 0;
273}
274
275#ifdef HAVE_UTIMENSAT
276static int xmp_utimens(const char *path, const struct timespec ts[2],
277 struct fuse_file_info *fi)
278{
279 (void) fi;
280 int res;
281
282 /* don't use utime/utimes since they follow symlinks */
283 res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
284 if (res == -1)
285 return -errno;
286
287 return 0;
288}
289#endif
290
291static int xmp_create(const char *path, mode_t mode,
292 struct fuse_file_info *fi)
293{
294 int res;
295
296 res = open(path, fi->flags, mode);
297 if (res == -1)
298 return -errno;
299
300 fi->fh = res;
301 return 0;
302}
303
304static int xmp_open(const char *path, struct fuse_file_info *fi)
305{
306 int res;
307
308 res = open(path, fi->flags);
309 if (res == -1)
310 return -errno;
311
312 /* Enable direct_io when open has flags O_DIRECT to enjoy the feature
313 parallel_direct_writes (i.e., to get a shared lock, not exclusive lock,
314 for writes to the same file). */
315 if (fi->flags & O_DIRECT) {
316 fi->direct_io = 1;
318 }
319
320 fi->fh = res;
321 return 0;
322}
323
324static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
325 struct fuse_file_info *fi)
326{
327 int fd;
328 int res;
329
330 if(fi == NULL)
331 fd = open(path, O_RDONLY);
332 else
333 fd = fi->fh;
334
335 if (fd == -1)
336 return -errno;
337
338 res = pread(fd, buf, size, offset);
339 if (res == -1)
340 res = -errno;
341
342 if(fi == NULL)
343 close(fd);
344 return res;
345}
346
347static int xmp_write(const char *path, const char *buf, size_t size,
348 off_t offset, struct fuse_file_info *fi)
349{
350 int fd;
351 int res;
352
353 (void) fi;
354 if(fi == NULL)
355 fd = open(path, O_WRONLY);
356 else
357 fd = fi->fh;
358
359 if (fd == -1)
360 return -errno;
361
362 res = pwrite(fd, buf, size, offset);
363 if (res == -1)
364 res = -errno;
365
366 if(fi == NULL)
367 close(fd);
368 return res;
369}
370
371static int xmp_statfs(const char *path, struct statvfs *stbuf)
372{
373 int res;
374
375 res = statvfs(path, stbuf);
376 if (res == -1)
377 return -errno;
378
379 return 0;
380}
381
382static int xmp_release(const char *path, struct fuse_file_info *fi)
383{
384 (void) path;
385 close(fi->fh);
386 return 0;
387}
388
389static int xmp_fsync(const char *path, int isdatasync,
390 struct fuse_file_info *fi)
391{
392 /* Just a stub. This method is optional and can safely be left
393 unimplemented */
394
395 (void) path;
396 (void) isdatasync;
397 (void) fi;
398 return 0;
399}
400
401static int xmp_fallocate(const char *path, int mode,
402 off_t offset, off_t length, struct fuse_file_info *fi)
403{
404 int fd;
405 int res;
406
407 (void) fi;
408
409 if(fi == NULL)
410 fd = open(path, O_WRONLY);
411 else
412 fd = fi->fh;
413
414 if (fd == -1)
415 return -errno;
416
417 res = do_fallocate(fd, mode, offset, length);
418
419 if(fi == NULL)
420 close(fd);
421 return res;
422}
423
424#ifdef HAVE_SETXATTR
425/* xattr operations are optional and can safely be left unimplemented */
426static int xmp_setxattr(const char *path, const char *name, const char *value,
427 size_t size, int flags)
428{
429 int res = lsetxattr(path, name, value, size, flags);
430 if (res == -1)
431 return -errno;
432 return 0;
433}
434
435static int xmp_getxattr(const char *path, const char *name, char *value,
436 size_t size)
437{
438 int res = lgetxattr(path, name, value, size);
439 if (res == -1)
440 return -errno;
441 return res;
442}
443
444static int xmp_listxattr(const char *path, char *list, size_t size)
445{
446 int res = llistxattr(path, list, size);
447 if (res == -1)
448 return -errno;
449 return res;
450}
451
452static int xmp_removexattr(const char *path, const char *name)
453{
454 int res = lremovexattr(path, name);
455 if (res == -1)
456 return -errno;
457 return 0;
458}
459#endif /* HAVE_SETXATTR */
460
461#ifdef HAVE_COPY_FILE_RANGE
462static ssize_t xmp_copy_file_range(const char *path_in,
463 struct fuse_file_info *fi_in,
464 off_t offset_in, const char *path_out,
465 struct fuse_file_info *fi_out,
466 off_t offset_out, size_t len, int flags)
467{
468 int fd_in, fd_out;
469 ssize_t res;
470
471 if(fi_in == NULL)
472 fd_in = open(path_in, O_RDONLY);
473 else
474 fd_in = fi_in->fh;
475
476 if (fd_in == -1)
477 return -errno;
478
479 if(fi_out == NULL)
480 fd_out = open(path_out, O_WRONLY);
481 else
482 fd_out = fi_out->fh;
483
484 if (fd_out == -1) {
485 close(fd_in);
486 return -errno;
487 }
488
489 res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
490 flags);
491 if (res == -1)
492 res = -errno;
493
494 if (fi_out == NULL)
495 close(fd_out);
496 if (fi_in == NULL)
497 close(fd_in);
498
499 return res;
500}
501#endif
502
503static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
504{
505 int fd;
506 off_t res;
507
508 if (fi == NULL)
509 fd = open(path, O_RDONLY);
510 else
511 fd = fi->fh;
512
513 if (fd == -1)
514 return -errno;
515
516 res = lseek(fd, off, whence);
517 if (res == -1)
518 res = -errno;
519
520 if (fi == NULL)
521 close(fd);
522 return res;
523}
524
525#ifdef HAVE_STATX
526static int xmp_statx(const char *path, int flags, int mask, struct statx *stxbuf,
527 struct fuse_file_info *fi)
528{
529 int fd = -1;
530 int res;
531
532 if (fi)
533 fd = fi->fh;
534
535 res = statx(fd, path, flags | AT_SYMLINK_NOFOLLOW, mask, stxbuf);
536 if (res == -1)
537 return -errno;
538
539 return 0;
540}
541#endif
542
543static const struct fuse_operations xmp_oper = {
544 .init = xmp_init,
545 .getattr = xmp_getattr,
546 .access = xmp_access,
547 .readlink = xmp_readlink,
548 .readdir = xmp_readdir,
549 .mknod = xmp_mknod,
550 .mkdir = xmp_mkdir,
551 .symlink = xmp_symlink,
552 .unlink = xmp_unlink,
553 .rmdir = xmp_rmdir,
554 .rename = xmp_rename,
555 .link = xmp_link,
556 .chmod = xmp_chmod,
557 .chown = xmp_chown,
558 .truncate = xmp_truncate,
559#ifdef HAVE_UTIMENSAT
560 .utimens = xmp_utimens,
561#endif
562 .open = xmp_open,
563 .create = xmp_create,
564 .read = xmp_read,
565 .write = xmp_write,
566 .statfs = xmp_statfs,
567 .release = xmp_release,
568 .fsync = xmp_fsync,
569 .fallocate = xmp_fallocate,
570#ifdef HAVE_SETXATTR
571 .setxattr = xmp_setxattr,
572 .getxattr = xmp_getxattr,
573 .listxattr = xmp_listxattr,
574 .removexattr = xmp_removexattr,
575#endif
576#ifdef HAVE_COPY_FILE_RANGE
577 .copy_file_range = xmp_copy_file_range,
578#endif
579 .lseek = xmp_lseek,
580#ifdef HAVE_STATX
581 .statx = xmp_statx,
582#endif
583};
584
585int main(int argc, char *argv[])
586{
587 enum { MAX_ARGS = 10 };
588 int i,new_argc;
589 char *new_argv[MAX_ARGS];
590
591 umask(0);
592 /* Process the "--plus" option apart */
593 for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
594 if (!strcmp(argv[i], "--plus")) {
595 fill_dir_plus = FUSE_FILL_DIR_PLUS;
596 } else if (!strcmp(argv[i], "--readdir-zero-inodes")) {
597 // Return zero inodes from readdir
598 readdir_zero_ino = 1;
599 } else {
600 new_argv[new_argc++] = argv[i];
601 }
602 }
603 return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
604}
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_readdir_flags
Definition fuse.h:42
int32_t parallel_direct_writes
Definition fuse.h:312
int32_t use_ino
Definition fuse.h:198
double entry_timeout
Definition fuse.h:127
int32_t auto_cache
Definition fuse.h:253
double negative_timeout
Definition fuse.h:137
double attr_timeout
Definition fuse.h:143
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