This example implements a file system with a single file whose contents change dynamically: it always contains the current time.
Observe that the output never changes, even though the file system updates it once per second. This is because the contents are cached in the kernel:
#define FUSE_USE_VERSION 34
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
#define NO_TIMEOUT 500000
#define MAX_STR_LEN 128
#define FILE_INO 2
#define FILE_NAME "current_time"
static char file_contents[MAX_STR_LEN];
static int lookup_cnt = 0;
static size_t file_size;
static int retrieve_status = 0;
struct options {
int no_notify;
int update_interval;
};
static struct options options = {
.no_notify = 0,
.update_interval = 1,
};
#define OPTION(t, p) \
{ t, offsetof(struct options, p), 1 }
static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
};
static int tfs_stat(
fuse_ino_t ino,
struct stat *stbuf) {
stbuf->st_ino = ino;
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1;
}
else if (ino == FILE_INO) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
else
return -1;
return 0;
}
const char *name) {
memset(&e, 0, sizeof(e));
goto err_out;
else if (strcmp(name, FILE_NAME) == 0) {
e.ino = FILE_INO;
lookup_cnt++;
} else
goto err_out;
e.attr_timeout = NO_TIMEOUT;
e.entry_timeout = NO_TIMEOUT;
if (tfs_stat(e.ino, &e.attr) != 0)
goto err_out;
return;
err_out:
}
uint64_t nlookup) {
(void) req;
lookup_cnt -= nlookup;
else
}
struct stat stbuf;
(void) fi;
memset(&stbuf, 0, sizeof(stbuf));
if (tfs_stat(ino, &stbuf) != 0)
else
}
struct dirbuf {
char *p;
size_t size;
};
static void dirbuf_add(
fuse_req_t req,
struct dirbuf *b,
const char *name,
struct stat stbuf;
size_t oldsize = b->size;
b->p = (char *) realloc(b->p, b->size);
memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_ino = ino;
b->size);
}
#define min(x, y) ((x) < (y) ? (x) : (y))
static int reply_buf_limited(
fuse_req_t req,
const char *buf,
size_t bufsize,
off_t off, size_t maxsize) {
if (off < bufsize)
min(bufsize - off, maxsize));
else
}
(void) fi;
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, FILE_NAME, FILE_INO);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
else if ((fi->
flags & O_ACCMODE) != O_RDONLY)
else if (ino == FILE_INO)
else {
fprintf(stderr, "Got open for non-existing inode!\n");
}
}
(void) fi;
assert(ino == FILE_INO);
reply_buf_limited(req, file_contents, file_size, off, size);
}
char *expected;
ssize_t ret;
assert(ino == FILE_INO);
assert(offset == 0);
expected = (char*) cookie;
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = MAX_STR_LEN;
assert(ret > 0);
assert(strncmp(
buf, expected, ret) == 0);
free(expected);
retrieve_status = 2;
}
.getattr = tfs_getattr,
.readdir = tfs_readdir,
.open = tfs_open,
.read = tfs_read,
.forget = tfs_forget,
.retrieve_reply = tfs_retrieve_reply,
};
static void update_fs(void) {
struct tm *now;
time_t t;
t = time(NULL);
now = localtime(&t);
assert(now != NULL);
file_size = strftime(file_contents, MAX_STR_LEN,
"The current time is %H:%M:%S\n", now);
assert(file_size != 0);
}
static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
int ret;
while(1) {
update_fs();
if (!options.no_notify && lookup_cnt) {
bufv.count = 1;
bufv.idx = 0;
bufv.off = 0;
bufv.buf[0].size = file_size;
bufv.buf[0].mem = file_contents;
bufv.buf[0].flags = 0;
if (-ret == ENODEV) {
break;
}
else if (ret != 0) {
fprintf(stderr, "ERROR: fuse_lowlevel_notify_store() failed with %s (%d)\n",
strerror(-ret), -ret);
abort();
}
0, (void*) strdup(file_contents));
if (-ret == ENODEV) {
break;
}
assert(ret == 0);
if(retrieve_status == 0)
retrieve_status = 1;
}
sleep(options.update_interval);
}
return NULL;
}
static void show_help(const char *progname)
{
printf("usage: %s [options] <mountpoint>\n\n", progname);
printf("File-system specific options:\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
"\n");
}
int main(int argc, char *argv[]) {
struct fuse_session *se;
pthread_t updater;
int ret = -1;
return 1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
show_help(argv[0]);
ret = 0;
goto err_out1;
} else if (opts.show_version) {
ret = 0;
goto err_out1;
}
update_fs();
sizeof(tfs_oper), NULL);
if (se == NULL)
goto err_out1;
goto err_out2;
goto err_out3;
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
fprintf(stderr, "pthread_create failed with %s\n",
strerror(ret));
goto err_out3;
}
if (opts.singlethread)
else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
ret = fuse_session_loop_mt(se, &config);
}
assert(retrieve_status != 1);
err_out3:
err_out2:
err_out1:
free(opts.mountpoint);
return ret ? 1 : 0;
}
int fuse_set_signal_handlers(struct fuse_session *se)
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, enum fuse_buf_copy_flags flags)
const char * fuse_pkgversion(void)
void fuse_remove_signal_handlers(struct fuse_session *se)
int fuse_daemonize(int foreground)
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_err(fuse_req_t req, int err)
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
struct fuse_req * fuse_req_t
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, size_t size, off_t offset, void *cookie)
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)
void fuse_cmdline_help(void)
struct fuse_session * fuse_session_new(struct fuse_args *args, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata)
void fuse_reply_none(fuse_req_t req)
void fuse_lowlevel_help(void)
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
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)
int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
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)
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)
enum fuse_buf_flags flags
void(* lookup)(fuse_req_t req, fuse_ino_t parent, const char *name)