libfuse
passthrough_ll.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 
5  This program can be distributed under the terms of the GNU GPL.
6  See the file COPYING.
7 */
8 
37 #define _GNU_SOURCE
38 #define FUSE_USE_VERSION 31
39 
40 #include <fuse_lowlevel.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <stddef.h>
45 #include <stdbool.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <dirent.h>
49 #include <assert.h>
50 #include <errno.h>
51 #include <err.h>
52 #include <inttypes.h>
53 
54 /* We are re-using pointers to our `struct lo_inode` and `struct
55  lo_dirp` elements as inodes. This means that we must be able to
56  store uintptr_t values in a fuse_ino_t variable. The following
57  incantation checks this condition at compile time. */
58 #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus
59 _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t),
60  "fuse_ino_t too small to hold uintptr_t values!");
61 #else
62 struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \
63  { unsigned _uintptr_to_must_hold_fuse_ino_t:
64  ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); };
65 #endif
66 
67 struct lo_inode {
68  struct lo_inode *next;
69  struct lo_inode *prev;
70  int fd;
71  ino_t ino;
72  dev_t dev;
73  uint64_t nlookup;
74 };
75 
76 struct lo_data {
77  int debug;
78  int writeback;
79  struct lo_inode root;
80 };
81 
82 static const struct fuse_opt lo_opts[] = {
83  { "writeback",
84  offsetof(struct lo_data, writeback), 1 },
85  { "no_writeback",
86  offsetof(struct lo_data, writeback), 0 },
88 };
89 
90 static struct lo_data *lo_data(fuse_req_t req)
91 {
92  return (struct lo_data *) fuse_req_userdata(req);
93 }
94 
95 static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
96 {
97  if (ino == FUSE_ROOT_ID)
98  return &lo_data(req)->root;
99  else
100  return (struct lo_inode *) (uintptr_t) ino;
101 }
102 
103 static int lo_fd(fuse_req_t req, fuse_ino_t ino)
104 {
105  return lo_inode(req, ino)->fd;
106 }
107 
108 static bool lo_debug(fuse_req_t req)
109 {
110  return lo_data(req)->debug != 0;
111 }
112 
113 static void lo_init(void *userdata,
114  struct fuse_conn_info *conn)
115 {
116  struct lo_data *lo = (struct lo_data*) userdata;
117 
118  if(conn->capable & FUSE_CAP_EXPORT_SUPPORT)
119  conn->want |= FUSE_CAP_EXPORT_SUPPORT;
120 
121  if (lo->writeback &&
123  if (lo->debug)
124  fprintf(stderr, "lo_init: activating writeback\n");
126  }
127 }
128 
129 static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
130  struct fuse_file_info *fi)
131 {
132  int res;
133  struct stat buf;
134  (void) fi;
135 
136  res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
137  if (res == -1)
138  return (void) fuse_reply_err(req, errno);
139 
140  fuse_reply_attr(req, &buf, 1.0);
141 }
142 
143 static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
144 {
145  struct lo_inode *p;
146 
147  for (p = lo->root.next; p != &lo->root; p = p->next) {
148  if (p->ino == st->st_ino && p->dev == st->st_dev)
149  return p;
150  }
151  return NULL;
152 }
153 
154 static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
155  struct fuse_entry_param *e)
156 {
157  int newfd;
158  int res;
159  int saverr;
160  struct lo_inode *inode;
161 
162  memset(e, 0, sizeof(*e));
163  e->attr_timeout = 1.0;
164  e->entry_timeout = 1.0;
165 
166  newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
167  if (newfd == -1)
168  goto out_err;
169 
170  res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
171  if (res == -1)
172  goto out_err;
173 
174  inode = lo_find(lo_data(req), &e->attr);
175  if (inode) {
176  close(newfd);
177  newfd = -1;
178  } else {
179  struct lo_inode *prev = &lo_data(req)->root;
180  struct lo_inode *next = prev->next;
181  saverr = ENOMEM;
182  inode = calloc(1, sizeof(struct lo_inode));
183  if (!inode)
184  goto out_err;
185 
186  inode->fd = newfd;
187  inode->ino = e->attr.st_ino;
188  inode->dev = e->attr.st_dev;
189 
190  next->prev = inode;
191  inode->next = next;
192  inode->prev = prev;
193  prev->next = inode;
194  }
195  inode->nlookup++;
196  e->ino = (uintptr_t) inode;
197 
198  if (lo_debug(req))
199  fprintf(stderr, " %lli/%s -> %lli\n",
200  (unsigned long long) parent, name, (unsigned long long) e->ino);
201 
202  return 0;
203 
204 out_err:
205  saverr = errno;
206  if (newfd != -1)
207  close(newfd);
208  return saverr;
209 }
210 
211 static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
212 {
213  struct fuse_entry_param e;
214  int err;
215 
216  if (lo_debug(req))
217  fprintf(stderr, "lo_lookup(parent=%" PRIu64 ", name=%s)\n",
218  parent, name);
219 
220  err = lo_do_lookup(req, parent, name, &e);
221  if (err)
222  fuse_reply_err(req, err);
223  else
224  fuse_reply_entry(req, &e);
225 }
226 
227 static void lo_free(struct lo_inode *inode)
228 {
229  struct lo_inode *prev = inode->prev;
230  struct lo_inode *next = inode->next;
231 
232  next->prev = prev;
233  prev->next = next;
234  close(inode->fd);
235  free(inode);
236 }
237 
238 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
239 {
240  struct lo_inode *inode = lo_inode(req, ino);
241 
242  if (lo_debug(req)) {
243  fprintf(stderr, " forget %lli %lli -%lli\n",
244  (unsigned long long) ino, (unsigned long long) inode->nlookup,
245  (unsigned long long) nlookup);
246  }
247 
248  assert(inode->nlookup >= nlookup);
249  inode->nlookup -= nlookup;
250 
251  if (!inode->nlookup)
252  lo_free(inode);
253 
254  fuse_reply_none(req);
255 }
256 
257 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
258 {
259  char buf[PATH_MAX + 1];
260  int res;
261 
262  res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
263  if (res == -1)
264  return (void) fuse_reply_err(req, errno);
265 
266  if (res == sizeof(buf))
267  return (void) fuse_reply_err(req, ENAMETOOLONG);
268 
269  buf[res] = '\0';
270 
271  fuse_reply_readlink(req, buf);
272 }
273 
274 struct lo_dirp {
275  int fd;
276  DIR *dp;
277  struct dirent *entry;
278  off_t offset;
279 };
280 
281 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
282 {
283  return (struct lo_dirp *) (uintptr_t) fi->fh;
284 }
285 
286 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
287 {
288  int error = ENOMEM;
289  struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
290  if (d == NULL)
291  goto out_err;
292 
293  d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
294  if (d->fd == -1)
295  goto out_errno;
296 
297  d->dp = fdopendir(d->fd);
298  if (d->dp == NULL)
299  goto out_errno;
300 
301  d->offset = 0;
302  d->entry = NULL;
303 
304  fi->fh = (uintptr_t) d;
305  fuse_reply_open(req, fi);
306  return;
307 
308 out_errno:
309  error = errno;
310 out_err:
311  if (d) {
312  if (d->fd != -1)
313  close(d->fd);
314  free(d);
315  }
316  fuse_reply_err(req, error);
317 }
318 
319 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
320  off_t offset, struct fuse_file_info *fi, int plus)
321 {
322  struct lo_dirp *d = lo_dirp(fi);
323  char *buf;
324  char *p;
325  size_t rem;
326  int err;
327 
328  (void) ino;
329 
330  buf = calloc(1, size);
331  if (!buf)
332  return (void) fuse_reply_err(req, ENOMEM);
333 
334  if (offset != d->offset) {
335  seekdir(d->dp, offset);
336  d->entry = NULL;
337  d->offset = offset;
338  }
339  p = buf;
340  rem = size;
341  while (1) {
342  size_t entsize;
343  off_t nextoff;
344 
345  if (!d->entry) {
346  errno = 0;
347  d->entry = readdir(d->dp);
348  if (!d->entry) {
349  if (errno && rem == size) {
350  err = errno;
351  goto error;
352  }
353  break;
354  }
355  }
356  nextoff = telldir(d->dp);
357  if (plus) {
358  struct fuse_entry_param e;
359 
360  err = lo_do_lookup(req, ino, d->entry->d_name, &e);
361  if (err)
362  goto error;
363 
364  entsize = fuse_add_direntry_plus(req, p, rem,
365  d->entry->d_name,
366  &e, nextoff);
367  } else {
368  struct stat st = {
369  .st_ino = d->entry->d_ino,
370  .st_mode = d->entry->d_type << 12,
371  };
372  entsize = fuse_add_direntry(req, p, rem,
373  d->entry->d_name,
374  &st, nextoff);
375  }
376  if (entsize > rem)
377  break;
378 
379  p += entsize;
380  rem -= entsize;
381 
382  d->entry = NULL;
383  d->offset = nextoff;
384  }
385 
386  fuse_reply_buf(req, buf, size - rem);
387  free(buf);
388  return;
389 
390 error:
391  free(buf);
392  fuse_reply_err(req, err);
393 }
394 
395 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
396  off_t offset, struct fuse_file_info *fi)
397 {
398  lo_do_readdir(req, ino, size, offset, fi, 0);
399 }
400 
401 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
402  off_t offset, struct fuse_file_info *fi)
403 {
404  lo_do_readdir(req, ino, size, offset, fi, 1);
405 }
406 
407 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
408 {
409  struct lo_dirp *d = lo_dirp(fi);
410  (void) ino;
411  closedir(d->dp);
412  free(d);
413  fuse_reply_err(req, 0);
414 }
415 
416 static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
417  mode_t mode, struct fuse_file_info *fi)
418 {
419  int fd;
420  struct fuse_entry_param e;
421  int err;
422 
423  if (lo_debug(req))
424  fprintf(stderr, "lo_create(parent=%" PRIu64 ", name=%s)\n",
425  parent, name);
426 
427  fd = openat(lo_fd(req, parent), name,
428  (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
429  if (fd == -1)
430  return (void) fuse_reply_err(req, errno);
431 
432  fi->fh = fd;
433 
434  err = lo_do_lookup(req, parent, name, &e);
435  if (err)
436  fuse_reply_err(req, err);
437  else
438  fuse_reply_create(req, &e, fi);
439 }
440 
441 static void lo_open(fuse_req_t req, fuse_ino_t ino,
442  struct fuse_file_info *fi)
443 {
444  int fd;
445  char buf[64];
446 
447  if (lo_debug(req))
448  fprintf(stderr, "lo_open(ino=%" PRIu64 ", flags=%d)\n",
449  ino, fi->flags);
450 
451  /* With writeback cache, kernel may send read requests even
452  when userspace opened write-only */
453  if (lo_data(req)->writeback &&
454  (fi->flags & O_ACCMODE) == O_WRONLY) {
455  fi->flags &= ~O_ACCMODE;
456  fi->flags |= O_RDWR;
457  }
458 
459  /* With writeback cache, O_APPEND is handled by the kernel.
460  This breaks atomicity (since the file may change in the
461  underlying filesystem, so that the kernel's idea of the
462  end of the file isn't accurate anymore). In this example,
463  we just accept that. A more rigorous filesystem may want
464  to return an error here */
465  if (lo_data(req)->writeback && (fi->flags & O_APPEND))
466  fi->flags &= ~O_APPEND;
467 
468  sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
469  fd = open(buf, fi->flags & ~O_NOFOLLOW);
470  if (fd == -1)
471  return (void) fuse_reply_err(req, errno);
472 
473  fi->fh = fd;
474  fuse_reply_open(req, fi);
475 }
476 
477 static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
478 {
479  (void) ino;
480 
481  close(fi->fh);
482  fuse_reply_err(req, 0);
483 }
484 
485 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
486  off_t offset, struct fuse_file_info *fi)
487 {
488  struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
489 
490  if (lo_debug(req))
491  fprintf(stderr, "lo_read(ino=%" PRIu64 ", size=%zd, "
492  "off=%lu)\n", ino, size, (unsigned long) offset);
493 
495  buf.buf[0].fd = fi->fh;
496  buf.buf[0].pos = offset;
497 
499 }
500 
501 static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
502  struct fuse_bufvec *in_buf, off_t off,
503  struct fuse_file_info *fi)
504 {
505  (void) ino;
506  ssize_t res;
507  struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
508 
509  out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
510  out_buf.buf[0].fd = fi->fh;
511  out_buf.buf[0].pos = off;
512 
513  if (lo_debug(req))
514  fprintf(stderr, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n",
515  ino, out_buf.buf[0].size, (unsigned long) off);
516 
517  res = fuse_buf_copy(&out_buf, in_buf, 0);
518  if(res < 0)
519  fuse_reply_err(req, -res);
520  else
521  fuse_reply_write(req, (size_t) res);
522 }
523 
524 static struct fuse_lowlevel_ops lo_oper = {
525  .init = lo_init,
526  .lookup = lo_lookup,
527  .forget = lo_forget,
528  .getattr = lo_getattr,
529  .readlink = lo_readlink,
530  .opendir = lo_opendir,
531  .readdir = lo_readdir,
532  .readdirplus = lo_readdirplus,
533  .releasedir = lo_releasedir,
534  .create = lo_create,
535  .open = lo_open,
536  .release = lo_release,
537  .read = lo_read,
538  .write_buf = lo_write_buf
539 };
540 
541 int main(int argc, char *argv[])
542 {
543  struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
544  struct fuse_session *se;
545  struct fuse_cmdline_opts opts;
546  struct lo_data lo = { .debug = 0,
547  .writeback = 0 };
548  int ret = -1;
549 
550  lo.root.next = lo.root.prev = &lo.root;
551  lo.root.fd = -1;
552 
553  if (fuse_parse_cmdline(&args, &opts) != 0)
554  return 1;
555  if (opts.show_help) {
556  printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
559  ret = 0;
560  goto err_out1;
561  } else if (opts.show_version) {
562  printf("FUSE library version %s\n", fuse_pkgversion());
564  ret = 0;
565  goto err_out1;
566  }
567 
568  if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1)
569  return 1;
570 
571  lo.debug = opts.debug;
572  lo.root.fd = open("/", O_PATH);
573  lo.root.nlookup = 2;
574  if (lo.root.fd == -1)
575  err(1, "open(\"/\", O_PATH)");
576 
577  se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);
578  if (se == NULL)
579  goto err_out1;
580 
581  if (fuse_set_signal_handlers(se) != 0)
582  goto err_out2;
583 
584  if (fuse_session_mount(se, opts.mountpoint) != 0)
585  goto err_out3;
586 
587  fuse_daemonize(opts.foreground);
588 
589  /* Block until ctrl+c or fusermount -u */
590  if (opts.singlethread)
591  ret = fuse_session_loop(se);
592  else
593  ret = fuse_session_loop_mt(se, opts.clone_fd);
594 
596 err_out3:
598 err_out2:
600 err_out1:
601  free(opts.mountpoint);
602  fuse_opt_free_args(&args);
603 
604  while (lo.root.next != &lo.root)
605  lo_free(lo.root.next);
606  if (lo.root.fd >= 0)
607  close(lo.root.fd);
608 
609  return ret ? 1 : 0;
610 }
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:197
size_t off
Definition: fuse_common.h:668
unsigned capable
Definition: fuse_common.h:370
uint64_t fh
Definition: fuse_common.h:72
int fuse_reply_write(fuse_req_t req, size_t count)
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
struct stat attr
Definition: fuse_lowlevel.h:91
const char * fuse_pkgversion(void)
Definition: fuse.c:4926
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:255
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.h:59
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct fuse_entry_param *e, off_t off)
fuse_ino_t ino
Definition: fuse_lowlevel.h:67
int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
off_t pos
Definition: fuse_common.h:643
int fuse_daemonize(int foreground)
Definition: helper.c:220
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
Definition: buffer.c:22
void fuse_lowlevel_help(void)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition: buffer.c:281
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
int fuse_reply_err(fuse_req_t req, int err)
void fuse_cmdline_help(void)
Definition: helper.c:128
void * fuse_req_userdata(fuse_req_t req)
enum fuse_buf_flags flags
Definition: fuse_common.h:622
void fuse_reply_none(fuse_req_t req)
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_readlink(fuse_req_t req, const char *link)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:33
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, const struct fuse_file_info *fi)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
unsigned want
Definition: fuse_common.h:378
struct fuse_buf buf[1]
Definition: fuse_common.h:673
void fuse_lowlevel_version(void)
size_t size
Definition: fuse_common.h:617
double entry_timeout
#define FUSE_CAP_EXPORT_SUPPORT
Definition: fuse_common.h:144
double attr_timeout
Definition: fuse_lowlevel.h:97
#define FUSE_OPT_END
Definition: fuse_opt.h:104
void(* init)(void *userdata, struct fuse_conn_info *conn)
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)