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