Skip to content

Commit

Permalink
Add support for an idle timeout when mounting a squashfile. (#28)
Browse files Browse the repository at this point in the history
* Add support for an idle timeout when mounting a squashfile.

If specified via the timeout option (-o timeout=N), then squashfuse_ll
will track the time of all accesses as well as the count of open files
and directories.  If the open count is zero and more seconds than the
specified timeout have passed since the last activity on the
filesystem, then we exit.

The approach is based on fuse's own signal handling (from
lib/fuse_signals.c).

* use sig_atomic_t for refcount

* bump version
  • Loading branch information
chipturner authored Mar 1, 2018
1 parent 3f4a93f commit 3998713
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
squashfuse
squashfuse_ll
squashfuse_ls
squashfuse_extract
*.dSYM
*.o
*.lo
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AC_INIT([squashfuse], [0.1.101], [dave@vasilevsky.ca])
AC_INIT([squashfuse], [0.1.102], [dave@vasilevsky.ca])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
Expand Down
1 change: 1 addition & 0 deletions fuseprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ typedef struct {
const char *image;
int mountpoint;
size_t offset;
unsigned int idle_timeout_secs;
} sqfs_opts;
int sqfs_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs);
Expand Down
84 changes: 84 additions & 0 deletions ll.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

static const double SQFS_TIMEOUT = DBL_MAX;

/* See comment near alarm_tick for details of how idle timeouts are
managed. */

/* timeout, in seconds, after which we will automatically unmount */
static unsigned int idle_timeout_secs = 0;
/* last access timestamp */
static time_t last_access = 0;
/* count of files and directories currently open. drecement after
* last_access for correctness. */
static sig_atomic_t open_refcount = 0;
/* same as lib/fuse_signals.c */
static struct fuse_session *fuse_instance = NULL;

static void sqfs_ll_op_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
sqfs_ll_i lli;
struct stat st;
last_access = time(NULL);
if (sqfs_ll_iget(req, &lli, ino))
return;

Expand All @@ -54,6 +70,7 @@ static void sqfs_ll_op_getattr(fuse_req_t req, fuse_ino_t ino,
static void sqfs_ll_op_opendir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
sqfs_ll_i *lli;
last_access = time(NULL);

fi->fh = (intptr_t)NULL;

Expand All @@ -68,6 +85,7 @@ static void sqfs_ll_op_opendir(fuse_req_t req, fuse_ino_t ino,
fuse_reply_err(req, ENOTDIR);
} else {
fi->fh = (intptr_t)lli;
++open_refcount;
fuse_reply_open(req, fi);
return;
}
Expand All @@ -77,11 +95,14 @@ static void sqfs_ll_op_opendir(fuse_req_t req, fuse_ino_t ino,

static void sqfs_ll_op_create(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi) {
last_access = time(NULL);
fuse_reply_err(req, EROFS);
}

static void sqfs_ll_op_releasedir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
last_access = time(NULL);
--open_refcount;
free((sqfs_ll_i*)(intptr_t)fi->fh);
fuse_reply_err(req, 0); /* yes, this is necessary */
}
Expand Down Expand Up @@ -110,6 +131,7 @@ static void sqfs_ll_op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
sqfs_ll_i *lli = (sqfs_ll_i*)(intptr_t)fi->fh;
int err = 0;

last_access = time(NULL);
if (sqfs_dir_open(&lli->ll->fs, &lli->inode, &dir, off))
err = EINVAL;
if (!err && !(bufpos = buf = malloc(size)))
Expand Down Expand Up @@ -150,6 +172,7 @@ static void sqfs_ll_op_lookup(fuse_req_t req, fuse_ino_t parent,
bool found;
sqfs_inode inode;

last_access = time(NULL);
if (sqfs_ll_iget(req, &lli, parent))
return;

Expand Down Expand Up @@ -191,6 +214,7 @@ static void sqfs_ll_op_open(fuse_req_t req, fuse_ino_t ino,
sqfs_inode *inode;
sqfs_ll *ll;

last_access = time(NULL);
if (fi->flags & (O_WRONLY | O_RDWR)) {
fuse_reply_err(req, EROFS);
return;
Expand All @@ -210,6 +234,7 @@ static void sqfs_ll_op_open(fuse_req_t req, fuse_ino_t ino,
} else {
fi->fh = (intptr_t)inode;
fi->keep_cache = 1;
++open_refcount;
fuse_reply_open(req, fi);
return;
}
Expand All @@ -220,6 +245,8 @@ static void sqfs_ll_op_release(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi) {
free((sqfs_inode*)(intptr_t)fi->fh);
fi->fh = 0;
last_access = time(NULL);
--open_refcount;
fuse_reply_err(req, 0);
}

Expand All @@ -236,6 +263,7 @@ static void sqfs_ll_op_read(fuse_req_t req, fuse_ino_t ino,
return;
}

last_access = time(NULL);
osize = size;
err = sqfs_read_range(&ll->fs, inode, off, &osize, buf);
if (err) {
Expand All @@ -252,6 +280,7 @@ static void sqfs_ll_op_readlink(fuse_req_t req, fuse_ino_t ino) {
char *dst;
size_t size;
sqfs_ll_i lli;
last_access = time(NULL);
if (sqfs_ll_iget(req, &lli, ino))
return;

Expand All @@ -275,6 +304,7 @@ static void sqfs_ll_op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
char *buf;
int ferr;

last_access = time(NULL);
if (sqfs_ll_iget(req, &lli, ino))
return;

Expand Down Expand Up @@ -312,6 +342,7 @@ static void sqfs_ll_op_getxattr(fuse_req_t req, fuse_ino_t ino,
}
#endif

last_access = time(NULL);
if (sqfs_ll_iget(req, &lli, ino))
return;

Expand All @@ -333,6 +364,7 @@ static void sqfs_ll_op_getxattr(fuse_req_t req, fuse_ino_t ino,
static void sqfs_ll_op_forget(fuse_req_t req, fuse_ino_t ino,
unsigned long nlookup) {
sqfs_ll_i lli;
last_access = time(NULL);
sqfs_ll_iget(req, &lli, SQFS_FUSE_INODE_NONE);
lli.ll->ino_forget(lli.ll, ino, nlookup);
fuse_reply_none(req);
Expand Down Expand Up @@ -368,6 +400,53 @@ static void sqfs_ll_unmount(sqfs_ll_chan *ch, const char *mountpoint) {
#endif
}

/* Idle unmount timeout management is based on signal handling from
fuse (see set_one_signal_handler and exit_handler in libfuse's
lib/fuse_signals.c.
When an idle timeout is set, we use a one second alarm to check if
no activity has taken place within the idle window, as tracked by
last_access. We also maintain an open/opendir refcount so that
directories and files can be held open and unaccessed without
triggering the idle timeout.
*/

static void alarm_tick(int sig) {
if (!fuse_instance || idle_timeout_secs == 0) {
return;
}

if (open_refcount == 0 && time(NULL) - last_access > idle_timeout_secs) {
fuse_session_exit(fuse_instance);
return;
}
alarm(1); /* always reset our alarm */
}

static void setup_idle_timeout(struct fuse_session *se, unsigned int timeout_secs) {
last_access = time(NULL);
idle_timeout_secs = timeout_secs;

struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = alarm_tick;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;

fuse_instance = se;
if (sigaction(SIGALRM, &sa, NULL) == -1) {
perror("fuse: cannot get old signal handler");
return;
}

alarm(1);
}

static void teardown_idle_timeout() {
alarm(0);
fuse_instance = NULL;
}

static sqfs_ll *sqfs_ll_open(const char *path, size_t offset) {
sqfs_ll *ll;

Expand Down Expand Up @@ -401,6 +480,7 @@ int main(int argc, char *argv[]) {
sqfs_ll *ll;
struct fuse_opt fuse_opts[] = {
{"offset=%u", offsetof(sqfs_opts, offset), 0},
{"timeout=%u", offsetof(sqfs_opts, idle_timeout_secs), 0},
FUSE_OPT_END
};

Expand Down Expand Up @@ -450,9 +530,13 @@ int main(int argc, char *argv[]) {
if (se != NULL) {
if (sqfs_ll_daemonize(fg) != -1) {
if (fuse_set_signal_handlers(se) != -1) {
if (opts.idle_timeout_secs) {
setup_idle_timeout(se, opts.idle_timeout_secs);
}
fuse_session_add_chan(se, ch.ch);
/* FIXME: multithreading */
err = fuse_session_loop(se);
teardown_idle_timeout();
fuse_remove_signal_handlers(se);
#if HAVE_DECL_FUSE_SESSION_REMOVE_CHAN
fuse_session_remove_chan(ch.ch);
Expand Down

0 comments on commit 3998713

Please sign in to comment.