"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.10.4/doc/html/fuse-3_810_82_2example_2notify__store__retrieve_8c_source.html" (9 Jun 2021, 75577 Bytes) of package /linux/misc/fuse-3.10.4.tar.xz:


Caution: In this restricted "Fossies" environment the current HTML page may not be correctly presentated and may have some non-functional links. You can here alternatively try to browse the pure source code or just view or download the uninterpreted raw source code. If the rendering is insufficient you may try to find and view the page on the project site itself.

libfuse
notify_store_retrieve.c
Go to the documentation of this file.
1 /*
2  FUSE: Filesystem in Userspace
3  Copyright (C) 2016 Nikolaus Rath <Nikolaus@rath.org>
4 
5  This program can be distributed under the terms of the GNU GPLv2.
6  See the file COPYING.
7 */
8 
61 #define FUSE_USE_VERSION 34
62 
63 #include <fuse_lowlevel.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <errno.h>
68 #include <fcntl.h>
69 #include <assert.h>
70 #include <stddef.h>
71 #include <unistd.h>
72 #include <pthread.h>
73 
74 /* We can't actually tell the kernel that there is no
75  timeout, so we just send a big value */
76 #define NO_TIMEOUT 500000
77 
78 #define MAX_STR_LEN 128
79 #define FILE_INO 2
80 #define FILE_NAME "current_time"
81 static char file_contents[MAX_STR_LEN];
82 static int lookup_cnt = 0;
83 static size_t file_size;
84 
85 /* Keep track if we ever stored data (==1), and
86  received it back correctly (==2) */
87 static int retrieve_status = 0;
88 
89 /* Command line parsing */
90 struct options {
91  int no_notify;
92  int update_interval;
93 };
94 static struct options options = {
95  .no_notify = 0,
96  .update_interval = 1,
97 };
98 
99 #define OPTION(t, p) \
100  { t, offsetof(struct options, p), 1 }
101 static const struct fuse_opt option_spec[] = {
102  OPTION("--no-notify", no_notify),
103  OPTION("--update-interval=%d", update_interval),
105 };
106 
107 static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
108  stbuf->st_ino = ino;
109  if (ino == FUSE_ROOT_ID) {
110  stbuf->st_mode = S_IFDIR | 0755;
111  stbuf->st_nlink = 1;
112  }
113 
114  else if (ino == FILE_INO) {
115  stbuf->st_mode = S_IFREG | 0444;
116  stbuf->st_nlink = 1;
117  stbuf->st_size = file_size;
118  }
119 
120  else
121  return -1;
122 
123  return 0;
124 }
125 
126 static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
127  const char *name) {
128  struct fuse_entry_param e;
129  memset(&e, 0, sizeof(e));
130 
131  if (parent != FUSE_ROOT_ID)
132  goto err_out;
133  else if (strcmp(name, FILE_NAME) == 0) {
134  e.ino = FILE_INO;
135  lookup_cnt++;
136  } else
137  goto err_out;
138 
139  e.attr_timeout = NO_TIMEOUT;
140  e.entry_timeout = NO_TIMEOUT;
141  if (tfs_stat(e.ino, &e.attr) != 0)
142  goto err_out;
143  fuse_reply_entry(req, &e);
144  return;
145 
146 err_out:
147  fuse_reply_err(req, ENOENT);
148 }
149 
150 static void tfs_forget (fuse_req_t req, fuse_ino_t ino,
151  uint64_t nlookup) {
152  (void) req;
153  if(ino == FILE_INO)
154  lookup_cnt -= nlookup;
155  else
156  assert(ino == FUSE_ROOT_ID);
157  fuse_reply_none(req);
158 }
159 
160 static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
161  struct fuse_file_info *fi) {
162  struct stat stbuf;
163 
164  (void) fi;
165 
166  memset(&stbuf, 0, sizeof(stbuf));
167  if (tfs_stat(ino, &stbuf) != 0)
168  fuse_reply_err(req, ENOENT);
169  else
170  fuse_reply_attr(req, &stbuf, NO_TIMEOUT);
171 }
172 
173 struct dirbuf {
174  char *p;
175  size_t size;
176 };
177 
178 static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
179  fuse_ino_t ino) {
180  struct stat stbuf;
181  size_t oldsize = b->size;
182  b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
183  b->p = (char *) realloc(b->p, b->size);
184  memset(&stbuf, 0, sizeof(stbuf));
185  stbuf.st_ino = ino;
186  fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
187  b->size);
188 }
189 
190 #define min(x, y) ((x) < (y) ? (x) : (y))
191 
192 static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
193  off_t off, size_t maxsize) {
194  if (off < bufsize)
195  return fuse_reply_buf(req, buf + off,
196  min(bufsize - off, maxsize));
197  else
198  return fuse_reply_buf(req, NULL, 0);
199 }
200 
201 static void tfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
202  off_t off, struct fuse_file_info *fi) {
203  (void) fi;
204 
205  if (ino != FUSE_ROOT_ID)
206  fuse_reply_err(req, ENOTDIR);
207  else {
208  struct dirbuf b;
209 
210  memset(&b, 0, sizeof(b));
211  dirbuf_add(req, &b, FILE_NAME, FILE_INO);
212  reply_buf_limited(req, b.p, b.size, off, size);
213  free(b.p);
214  }
215 }
216 
217 static void tfs_open(fuse_req_t req, fuse_ino_t ino,
218  struct fuse_file_info *fi) {
219 
220  /* Make cache persistent even if file is closed,
221  this makes it easier to see the effects */
222  fi->keep_cache = 1;
223 
224  if (ino == FUSE_ROOT_ID)
225  fuse_reply_err(req, EISDIR);
226  else if ((fi->flags & O_ACCMODE) != O_RDONLY)
227  fuse_reply_err(req, EACCES);
228  else if (ino == FILE_INO)
229  fuse_reply_open(req, fi);
230  else {
231  // This should not happen
232  fprintf(stderr, "Got open for non-existing inode!\n");
233  fuse_reply_err(req, ENOENT);
234  }
235 }
236 
237 static void tfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
238  off_t off, struct fuse_file_info *fi) {
239  (void) fi;
240 
241  assert(ino == FILE_INO);
242  reply_buf_limited(req, file_contents, file_size, off, size);
243 }
244 
245 static void tfs_retrieve_reply(fuse_req_t req, void *cookie, fuse_ino_t ino,
246  off_t offset, struct fuse_bufvec *data) {
247  struct fuse_bufvec bufv;
248  char buf[MAX_STR_LEN];
249  char *expected;
250  ssize_t ret;
251 
252  assert(ino == FILE_INO);
253  assert(offset == 0);
254  expected = (char*) cookie;
255 
256  bufv.count = 1;
257  bufv.idx = 0;
258  bufv.off = 0;
259  bufv.buf[0].size = MAX_STR_LEN;
260  bufv.buf[0].mem = buf;
261  bufv.buf[0].flags = 0;
262 
263  ret = fuse_buf_copy(&bufv, data, 0);
264  assert(ret > 0);
265  assert(strncmp(buf, expected, ret) == 0);
266  free(expected);
267  retrieve_status = 2;
268  fuse_reply_none(req);
269 }
270 
271 
272 static const struct fuse_lowlevel_ops tfs_oper = {
273  .lookup = tfs_lookup,
274  .getattr = tfs_getattr,
275  .readdir = tfs_readdir,
276  .open = tfs_open,
277  .read = tfs_read,
278  .forget = tfs_forget,
279  .retrieve_reply = tfs_retrieve_reply,
280 };
281 
282 static void update_fs(void) {
283  struct tm *now;
284  time_t t;
285  t = time(NULL);
286  now = localtime(&t);
287  assert(now != NULL);
288 
289  file_size = strftime(file_contents, MAX_STR_LEN,
290  "The current time is %H:%M:%S\n", now);
291  assert(file_size != 0);
292 }
293 
294 static void* update_fs_loop(void *data) {
295  struct fuse_session *se = (struct fuse_session*) data;
296  struct fuse_bufvec bufv;
297  int ret;
298 
299  while(1) {
300  update_fs();
301  if (!options.no_notify && lookup_cnt) {
302  /* Only send notification if the kernel
303  is aware of the inode */
304  bufv.count = 1;
305  bufv.idx = 0;
306  bufv.off = 0;
307  bufv.buf[0].size = file_size;
308  bufv.buf[0].mem = file_contents;
309  bufv.buf[0].flags = 0;
310 
311  /* This shouldn't fail, but apparently it sometimes
312  does - see https://github.com/libfuse/libfuse/issues/105 */
313  ret = fuse_lowlevel_notify_store(se, FILE_INO, 0, &bufv, 0);
314  if (-ret == ENODEV) {
315  // File system was unmounted
316  break;
317  }
318  else if (ret != 0) {
319  fprintf(stderr, "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
320  strerror(-ret), -ret);
321  abort();
322  }
323 
324  /* To make sure that everything worked correctly, ask the
325  kernel to send us back the stored data */
326  ret = fuse_lowlevel_notify_retrieve(se, FILE_INO, MAX_STR_LEN,
327  0, (void*) strdup(file_contents));
328  if (-ret == ENODEV) { // File system was unmounted
329  break;
330  }
331  assert(ret == 0);
332  if(retrieve_status == 0)
333  retrieve_status = 1;
334  }
335  sleep(options.update_interval);
336  }
337  return NULL;
338 }
339 
340 static void show_help(const char *progname)
341 {
342  printf("usage: %s [options] <mountpoint>\n\n", progname);
343  printf("File-system specific options:\n"
344  " --update-interval=<secs> Update-rate of file system contents\n"
345  " --no-notify Disable kernel notifications\n"
346  "\n");
347 }
348 
349 int main(int argc, char *argv[]) {
350  struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
351  struct fuse_session *se;
352  struct fuse_cmdline_opts opts;
353  struct fuse_loop_config config;
354  pthread_t updater;
355  int ret = -1;
356 
357  if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
358  return 1;
359 
360  if (fuse_parse_cmdline(&args, &opts) != 0)
361  return 1;
362  if (opts.show_help) {
363  show_help(argv[0]);
366  ret = 0;
367  goto err_out1;
368  } else if (opts.show_version) {
369  printf("FUSE library version %s\n", fuse_pkgversion());
371  ret = 0;
372  goto err_out1;
373  }
374 
375  /* Initial contents */
376  update_fs();
377 
378  se = fuse_session_new(&args, &tfs_oper,
379  sizeof(tfs_oper), NULL);
380  if (se == NULL)
381  goto err_out1;
382 
383  if (fuse_set_signal_handlers(se) != 0)
384  goto err_out2;
385 
386  if (fuse_session_mount(se, opts.mountpoint) != 0)
387  goto err_out3;
388 
389  fuse_daemonize(opts.foreground);
390 
391  /* Start thread to update file contents */
392  ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
393  if (ret != 0) {
394  fprintf(stderr, "pthread_create failed with %s\n",
395  strerror(ret));
396  goto err_out3;
397  }
398 
399  /* Block until ctrl+c or fusermount -u */
400  if (opts.singlethread)
401  ret = fuse_session_loop(se);
402  else {
403  config.clone_fd = opts.clone_fd;
404  config.max_idle_threads = opts.max_idle_threads;
405  ret = fuse_session_loop_mt(se, &config);
406  }
407 
408  assert(retrieve_status != 1);
410 err_out3:
412 err_out2:
414 err_out1:
415  free(opts.mountpoint);
416  fuse_opt_free_args(&args);
417 
418  return ret ? 1 : 0;
419 }
420 
421 
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
void fuse_lowlevel_help(void)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_lowlevel_version(void)
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name, const struct stat *stbuf, off_t off)
void fuse_session_destroy(struct fuse_session *se)
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
int fuse_daemonize(int foreground)
Definition: helper.c:225
unsigned int keep_cache
Definition: fuse_common.h:64
Definition: fuse_lowlevel.h:59
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
void fuse_cmdline_help(void)
Definition: helper.c:129
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:34
#define FUSE_OPT_END
Definition: fuse_opt.h:104
enum fuse_buf_flags flags
Definition: fuse_common.h:711
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
Definition: buffer.c:284
int fuse_reply_err(fuse_req_t req, int err)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:202
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
void fuse_reply_none(fuse_req_t req)
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:398
const char * fuse_pkgversion(void)
Definition: fuse.c:5117
void fuse_session_unmount(struct fuse_session *se)
struct fuse_buf buf[1]
Definition: fuse_common.h:762
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, off_t offset, struct fuse_bufvec *bufv, enum fuse_buf_copy_flags flags)
int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config)