libfuse
test_write_cache.c
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 GPL.
6  See the file COPYING.
7 */
8 
9 
10 #define FUSE_USE_VERSION 30
11 
12 #include <config.h>
13 #include <fuse_lowlevel.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <assert.h>
20 #include <stddef.h>
21 #include <unistd.h>
22 #include <pthread.h>
23 
24 #ifndef __linux__
25 #include <limits.h>
26 #else
27 #include <linux/limits.h>
28 #endif
29 
30 #define FILE_INO 2
31 #define FILE_NAME "write_me"
32 
33 /* Command line parsing */
34 struct options {
35  int writeback;
36  int data_size;
37 } options = {
38  .writeback = 0,
39  .data_size = 4096,
40 };
41 
42 #define OPTION(t, p) \
43  { t, offsetof(struct options, p), 1 }
44 static const struct fuse_opt option_spec[] = {
45  OPTION("writeback_cache", writeback),
46  OPTION("--data-size=%d", data_size),
48 };
49 static int got_write;
50 
51 static void tfs_init (void *userdata, struct fuse_conn_info *conn)
52 {
53  (void) userdata;
54 
55  if(options.writeback) {
56  assert(conn->capable & FUSE_CAP_WRITEBACK_CACHE);
58  }
59 }
60 
61 static int tfs_stat(fuse_ino_t ino, struct stat *stbuf) {
62  stbuf->st_ino = ino;
63  if (ino == FUSE_ROOT_ID) {
64  stbuf->st_mode = S_IFDIR | 0755;
65  stbuf->st_nlink = 1;
66  }
67 
68  else if (ino == FILE_INO) {
69  stbuf->st_mode = S_IFREG | 0222;
70  stbuf->st_nlink = 1;
71  stbuf->st_size = 0;
72  }
73 
74  else
75  return -1;
76 
77  return 0;
78 }
79 
80 static void tfs_lookup(fuse_req_t req, fuse_ino_t parent,
81  const char *name) {
82  struct fuse_entry_param e;
83  memset(&e, 0, sizeof(e));
84 
85  if (parent != FUSE_ROOT_ID)
86  goto err_out;
87  else if (strcmp(name, FILE_NAME) == 0)
88  e.ino = FILE_INO;
89  else
90  goto err_out;
91 
92  if (tfs_stat(e.ino, &e.attr) != 0)
93  goto err_out;
94  fuse_reply_entry(req, &e);
95  return;
96 
97 err_out:
98  fuse_reply_err(req, ENOENT);
99 }
100 
101 static void tfs_getattr(fuse_req_t req, fuse_ino_t ino,
102  struct fuse_file_info *fi) {
103  struct stat stbuf;
104 
105  (void) fi;
106 
107  memset(&stbuf, 0, sizeof(stbuf));
108  if (tfs_stat(ino, &stbuf) != 0)
109  fuse_reply_err(req, ENOENT);
110  else
111  fuse_reply_attr(req, &stbuf, 5);
112 }
113 
114 static void tfs_open(fuse_req_t req, fuse_ino_t ino,
115  struct fuse_file_info *fi) {
116  if (ino == FUSE_ROOT_ID)
117  fuse_reply_err(req, EISDIR);
118  else {
119  assert(ino == FILE_INO);
120  fuse_reply_open(req, fi);
121  }
122 }
123 
124 static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
125  size_t size, off_t off, struct fuse_file_info *fi) {
126  (void) fi; (void) buf; (void) off;
127  size_t expected;
128 
129  assert(ino == FILE_INO);
130  expected = options.data_size;
131  if(options.writeback)
132  expected *= 2;
133 
134  if(size != expected)
135  fprintf(stderr, "ERROR: Expected %zd bytes, got %zd\n!",
136  expected, size);
137  else
138  got_write = 1;
139  fuse_reply_write(req, size);
140 }
141 
142 static struct fuse_lowlevel_ops tfs_oper = {
143  .init = tfs_init,
144  .lookup = tfs_lookup,
145  .getattr = tfs_getattr,
146  .open = tfs_open,
147  .write = tfs_write,
148 };
149 
150 static void* run_fs(void *data) {
151  struct fuse_session *se = (struct fuse_session*) data;
152  assert(fuse_session_loop(se) == 0);
153  return NULL;
154 }
155 
156 static void test_fs(char *mountpoint) {
157  char fname[PATH_MAX];
158  char *buf;
159  size_t dsize = options.data_size;
160  int fd;
161 
162  buf = malloc(dsize);
163  assert(buf != NULL);
164  assert((fd = open("/dev/urandom", O_RDONLY)) != -1);
165  assert(read(fd, buf, dsize) == dsize);
166  close(fd);
167 
168  assert(snprintf(fname, PATH_MAX, "%s/" FILE_NAME,
169  mountpoint) > 0);
170  fd = open(fname, O_WRONLY);
171  if (fd == -1) {
172  perror(fname);
173  assert(0);
174  }
175 
176  assert(write(fd, buf, dsize) == dsize);
177  assert(write(fd, buf, dsize) == dsize);
178  free(buf);
179  close(fd);
180 }
181 
182 int main(int argc, char *argv[]) {
183  struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
184  struct fuse_session *se;
185  struct fuse_cmdline_opts fuse_opts;
186  pthread_t fs_thread;
187 
188  assert(fuse_opt_parse(&args, &options, option_spec, NULL) == 0);
189  assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
190 #ifndef __FreeBSD__
191  assert(fuse_opt_add_arg(&args, "-oauto_unmount") == 0);
192 #endif
193  se = fuse_session_new(&args, &tfs_oper,
194  sizeof(tfs_oper), NULL);
195  fuse_opt_free_args(&args);
196  assert (se != NULL);
197  assert(fuse_set_signal_handlers(se) == 0);
198  assert(fuse_session_mount(se, fuse_opts.mountpoint) == 0);
199 
200  /* Start file-system thread */
201  assert(pthread_create(&fs_thread, NULL, run_fs, (void *)se) == 0);
202 
203  /* Write test data */
204  test_fs(fuse_opts.mountpoint);
205  free(fuse_opts.mountpoint);
206 
207  /* Stop file system */
208  fuse_session_exit(se);
210  assert(pthread_join(fs_thread, NULL) == 0);
211 
212  assert(got_write == 1);
215 
216  printf("Test completed successfully.\n");
217  return 0;
218 }
219 
220 
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:197
unsigned capable
Definition: fuse_common.h:370
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
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:255
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.h:59
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)
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
int fuse_reply_err(fuse_req_t req, int err)
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:54
void fuse_session_destroy(struct fuse_session *se)
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
void fuse_session_exit(struct fuse_session *se)
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
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
#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
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43