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