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