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 GPLv2.
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),
47 FUSE_OPT_END
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);
57 conn->want |= 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);
209 fuse_session_unmount(se);
210 assert(pthread_join(fs_thread, NULL) == 0);
211
212 assert(got_write == 1);
213 fuse_remove_signal_handlers(se);
214 fuse_session_destroy(se);
215
216 printf("Test completed successfully.\n");
217 return 0;
218 }
219
220
fuse_opt_parse
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
fuse_session_mount
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
Definition: fuse_lowlevel.c:2981
fuse_conn_info::capable
unsigned capable
Definition: fuse_common.h:423
fuse_req_t
struct fuse_req * fuse_req_t
Definition: fuse_lowlevel.h:49
fuse_conn_info
Definition: fuse_common.h:385
fuse_set_signal_handlers
int fuse_set_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:62
fuse_reply_attr
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
Definition: fuse_lowlevel.c:433
FUSE_ARGS_INIT
#define FUSE_ARGS_INIT(argc, argv)
Definition: fuse_opt.h:123
fuse_entry_param
Definition: fuse_lowlevel.h:59
fuse_reply_write
int fuse_reply_write(fuse_req_t req, size_t count)
Definition: fuse_lowlevel.c:462
fuse_file_info
Definition: fuse_common.h:44
fuse_session_loop
int fuse_session_loop(struct fuse_session *se)
Definition: fuse_loop.c:19
FUSE_ROOT_ID
#define FUSE_ROOT_ID
Definition: fuse_lowlevel.h:43
fuse_parse_cmdline
int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts)
Definition: helper.c:202
fuse_session_new
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
Definition: fuse_lowlevel.c:2886
fuse_remove_signal_handlers
void fuse_remove_signal_handlers(struct fuse_session *se)
Definition: fuse_signals.c:79
FUSE_OPT_END
#define FUSE_OPT_END
Definition: fuse_opt.h:104
fuse_session_unmount
void fuse_session_unmount(struct fuse_session *se)
Definition: fuse_lowlevel.c:3036
fuse_reply_err
int fuse_reply_err(fuse_req_t req, int err)
Definition: fuse_lowlevel.c:312
FUSE_CAP_WRITEBACK_CACHE
#define FUSE_CAP_WRITEBACK_CACHE
Definition: fuse_common.h:296
fuse_opt_free_args
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:34
fuse_reply_entry
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
Definition: fuse_lowlevel.c:401
fuse_session_destroy
void fuse_session_destroy(struct fuse_session *se)
Definition: fuse_lowlevel.c:2700
fuse_lowlevel_ops
Definition: fuse_lowlevel.h:172
fuse_opt
Definition: fuse_opt.h:77
fuse_conn_info::want
unsigned want
Definition: fuse_common.h:431
fuse_args
Definition: fuse_opt.h:109
fuse_ino_t
uint64_t fuse_ino_t
Definition: fuse_lowlevel.h:46
fuse_opt_add_arg
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:55
fuse_lowlevel_ops::init
void(* init)(void *userdata, struct fuse_conn_info *conn)
Definition: fuse_lowlevel.h:189
fuse_reply_open
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi)
Definition: fuse_lowlevel.c:453
fuse_session_exit
void fuse_session_exit(struct fuse_session *se)
Definition: fuse_lowlevel.c:3115