9#define FUSE_USE_VERSION 30
14#include <fuse_config.h>
15#include <fuse_lowlevel.h>
31#include <linux/limits.h>
35#define FILE_NAME "write_me"
48#define WRITE_SYSCALLS 64
50#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
51static const struct fuse_opt option_spec[] = {
52 OPTION(
"writeback_cache", writeback),
53 OPTION(
"--data-size=%d", data_size), OPTION(
"--delay_ms=%d", delay_ms),
57static atomic_int write_cnt;
59pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
60pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
61static int write_start, write_done;
67 if (options.writeback) {
73static int tfs_stat(
fuse_ino_t ino,
struct stat *stbuf)
76 if (ino == FUSE_ROOT_ID) {
77 stbuf->st_mode = S_IFDIR | 0755;
81 else if (ino == FILE_INO) {
82 stbuf->st_mode = S_IFREG | 0222;
97 memset(&e, 0,
sizeof(e));
101 else if (strcmp(name, FILE_NAME) == 0)
106 if (tfs_stat(e.ino, &e.attr) != 0)
122 memset(&stbuf, 0,
sizeof(stbuf));
123 if (tfs_stat(ino, &stbuf) != 0)
131 if (ino == FUSE_ROOT_ID)
134 assert(ino == FILE_INO);
136 fi->
noflush = !options.writeback && options.delay_ms &&
137 (fi->
flags & O_ACCMODE) == O_RDONLY;
150 assert(ino == FILE_INO);
151 expected = options.data_size;
152 if (options.writeback)
157 if (size != expected && !options.writeback)
158 fprintf(stderr,
"ERROR: Expected %zd bytes, got %zd\n!",
164 if (options.delay_ms) {
165 pthread_mutex_lock(&lock);
167 pthread_cond_signal(&cond);
168 pthread_mutex_unlock(&lock);
170 usleep(options.delay_ms * 1000);
172 pthread_mutex_lock(&lock);
174 pthread_cond_signal(&cond);
175 pthread_mutex_unlock(&lock);
183 .lookup = tfs_lookup,
184 .getattr = tfs_getattr,
189static void *close_rofd(
void *data)
191 int rofd = (int)(
long)data;
194 pthread_mutex_lock(&lock);
195 while (!write_start && !write_done)
196 pthread_cond_wait(&cond, &lock);
197 pthread_mutex_unlock(&lock);
200 printf(
"rofd closed. write_start: %d write_done: %d\n", write_start,
205 fprintf(stderr,
"ERROR: close(rofd) blocked on write!\n");
210static void *run_fs(
void *data)
212 struct fuse_session *se = (
struct fuse_session *)data;
218static void test_fs(
char *mountpoint)
220 char fname[PATH_MAX];
222 const size_t iosize = options.data_size;
223 const size_t dsize = options.data_size * WRITE_SYSCALLS;
225 pthread_t rofd_thread;
230 assert((fd = open(
"/dev/urandom", O_RDONLY)) != -1);
231 assert(read(fd, buf, dsize) == dsize);
234 assert(snprintf(fname, PATH_MAX,
"%s/" FILE_NAME, mountpoint) > 0);
235 fd = open(fname, O_WRONLY);
241 if (options.delay_ms) {
243 rofd = open(fname, O_RDONLY);
244 assert(pthread_create(&rofd_thread, NULL, close_rofd,
245 (
void *)(
long)rofd) == 0);
247 usleep(options.delay_ms * 1000);
250 for (
int cnt = 0; cnt < WRITE_SYSCALLS; cnt++) {
251 assert(pwrite(fd, buf + off, iosize, off) == iosize);
253 assert(off <= dsize);
258 if (options.delay_ms) {
259 printf(
"rwfd closed. write_start: %d write_done: %d\n",
260 write_start, write_done);
261 assert(pthread_join(rofd_thread, NULL) == 0);
265int main(
int argc,
char *argv[])
268 struct fuse_session *se;
273 assert(fuse_parse_cmdline(&args, &fuse_opts) == 0);
277 se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
284 assert(pthread_create(&fs_thread, NULL, run_fs, (
void *)se) == 0);
287 test_fs(fuse_opts.mountpoint);
288 free(fuse_opts.mountpoint);
293 assert(pthread_join(fs_thread, NULL) == 0);
295 assert(got_write == 1);
305 if (options.writeback)
306 assert(write_cnt < WRITE_SYSCALLS);
308 assert(write_cnt == WRITE_SYSCALLS);
313 printf(
"Test completed successfully.\n");
bool fuse_set_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
int fuse_set_signal_handlers(struct fuse_session *se)
#define FUSE_CAP_WRITEBACK_CACHE
bool fuse_get_feature_flag(struct fuse_conn_info *conn, uint64_t flag)
void fuse_remove_signal_handlers(struct fuse_session *se)
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_err(fuse_req_t req, int err)
struct fuse_req * fuse_req_t
int fuse_session_loop(struct fuse_session *se)
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
void fuse_session_unmount(struct fuse_session *se)
int fuse_reply_write(fuse_req_t req, size_t count)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
void fuse_opt_free_args(struct fuse_args *args)
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
#define FUSE_ARGS_INIT(argc, argv)
void(* init)(void *userdata, struct fuse_conn_info *conn)