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 
54 static int fill_dir_plus = 0;
55 
56 static 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 
76 static 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 
89 static 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 
100 static 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 
113 static 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 
141 static 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 
152 static 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 
163 static 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 
174 static 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 
185 static 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 
196 static 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 
210 static 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 
221 static 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 
234 static 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 
247 static 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
263 static 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 
278 static 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 
291 static 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 
303 static 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 
326 static 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 
350 static 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 
361 static int xmp_release(const char *path, struct fuse_file_info *fi)
362 {
363  (void) path;
364  close(fi->fh);
365  return 0;
366 }
367 
368 static 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
381 static 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 */
410 static 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 
419 static 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 
428 static 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 
436 static 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
446 static 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 
487 static 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 
509 static 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 
550 int 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