aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example/notify_inval_entry.c41
-rw-r--r--include/fuse_common.h2
-rw-r--r--include/fuse_lowlevel.h28
-rw-r--r--lib/fuse_lowlevel.c41
-rwxr-xr-xtest/test_examples.py2
5 files changed, 86 insertions, 28 deletions
diff --git a/example/notify_inval_entry.c b/example/notify_inval_entry.c
index 8af31bc..ea3d43f 100644
--- a/example/notify_inval_entry.c
+++ b/example/notify_inval_entry.c
@@ -85,6 +85,7 @@
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
+#include <signal.h>
#include <stddef.h>
#include <unistd.h>
#include <pthread.h>
@@ -93,6 +94,7 @@
static char file_name[MAX_STR_LEN];
static fuse_ino_t file_ino = 2;
static int lookup_cnt = 0;
+static pthread_t main_thread;
/* Command line parsing */
struct options {
@@ -253,14 +255,32 @@ static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
char *old_name;
- while(1) {
+
+ while(!fuse_session_exited(se)) {
old_name = strdup(file_name);
update_fs();
+
if (!options.no_notify && lookup_cnt) {
- if(options.only_expire) {
- assert(fuse_lowlevel_notify_expire_entry
- (se, FUSE_ROOT_ID, old_name, strlen(old_name), FUSE_LL_EXPIRE_ONLY) == 0);
- } else {
+ if(options.only_expire) { // expire entry
+ int ret = fuse_lowlevel_notify_expire_entry
+ (se, FUSE_ROOT_ID, old_name, strlen(old_name));
+
+ // no kernel support
+ if (ret == -ENOSYS) {
+ printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
+ printf("Exiting...\n");
+
+ fuse_session_exit(se);
+ // Make sure to exit now, rather than on next request from userspace
+ pthread_kill(main_thread, SIGPIPE);
+
+ break;
+ }
+ // 1) ret == 0: successful expire of an existing entry
+ // 2) ret == -ENOENT: kernel has already expired the entry /
+ // entry does not exist anymore in the kernel
+ assert(ret == 0 || ret == -ENOENT);
+ } else { // invalidate entry
assert(fuse_lowlevel_notify_inval_entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
}
@@ -312,7 +332,7 @@ int main(int argc, char *argv[]) {
update_fs();
se = fuse_session_new(&args, &tfs_oper,
- sizeof(tfs_oper), NULL);
+ sizeof(tfs_oper), &se);
if (se == NULL)
goto err_out1;
@@ -324,6 +344,11 @@ int main(int argc, char *argv[]) {
fuse_daemonize(opts.foreground);
+ // Needed to ensure that the main thread continues/restarts processing as soon
+ // as the fuse session ends (immediately after calling fuse_session_exit() )
+ // and not only on the next request from userspace
+ main_thread = pthread_self();
+
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
@@ -333,9 +358,9 @@ int main(int argc, char *argv[]) {
}
/* Block until ctrl+c or fusermount -u */
- if (opts.singlethread)
+ if (opts.singlethread) {
ret = fuse_session_loop(se);
- else {
+ } else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
ret = fuse_session_loop_mt(se, &config);
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 3a1e1f8..837df5a 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -418,7 +418,7 @@ struct fuse_loop_config_v1 {
#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
/**
- * Indicates support that dentries can be expired or invalidated.
+ * Indicates support that dentries can be expired.
*
* Expiring dentries, instead of invalidating them, makes a difference for
* overmounted dentries, where plain invalidation would detach all submounts
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index b42447e..3ac97eb 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -139,11 +139,12 @@ struct fuse_custom_io {
};
/**
- * Flags for fuse_lowlevel_notify_expire_entry()
+ * Flags for fuse_lowlevel_notify_entry()
* 0 = invalidate entry
* FUSE_LL_EXPIRE_ONLY = expire entry
*/
-enum fuse_expire_flags {
+enum fuse_notify_entry_flags {
+ FUSE_LL_INVALIDATE = 0,
FUSE_LL_EXPIRE_ONLY = (1 << 0),
};
@@ -1682,8 +1683,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
off_t off, off_t len);
/**
- * Notify to invalidate parent attributes and the dentry matching
- * parent/name
+ * Notify to invalidate parent attributes and the dentry matching parent/name
*
* To avoid a deadlock this function must not be called in the
* execution path of a related filesystem operation or within any code
@@ -1710,14 +1710,13 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen);
/**
- * Notify to expire or invalidate parent attributes and the dentry
- * matching parent/name
+ * Notify to expire parent attributes and the dentry matching parent/name
*
- * Underlying function for fuse_lowlevel_notify_inval_entry().
+ * Same restrictions apply as for fuse_lowlevel_notify_inval_entry()
*
- * In addition to invalidating an entry, it also allows to expire an entry.
- * In that case, the entry is not forcefully removed from kernel cache
- * but instead the next access to it forces a lookup from the filesystem.
+ * Compared to invalidating an entry, expiring the entry results not in a
+ * forceful removal of that entry from kernel cache but instead the next access
+ * to it forces a lookup from the filesystem.
*
* This makes a difference for overmounted dentries, where plain invalidation
* would detach all submounts before dropping the dentry from the cache.
@@ -1728,17 +1727,18 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
* so invalidation will only be triggered for the non-overmounted case.
* The dentry could also be mounted in a different mount instance, in which case
* any submounts will still be detached.
+ *
+ * Added in FUSE protocol version 7.38. If the kernel does not support
+ * this (or a newer) version, the function will return -ENOSYS and do nothing.
*
* @param se the session object
* @param parent inode number
* @param name file name
* @param namelen strlen() of file name
- * @param flags flags to control if the entry should be expired or invalidated
- * @return zero for success, -errno for failure
+ * @return zero for success, -errno for failure, -enosys if no kernel support
*/
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
- const char *name, size_t namelen,
- enum fuse_expire_flags flags);
+ const char *name, size_t namelen);
/**
* This function behaves like fuse_lowlevel_notify_inval_entry() with
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 1ce6083..4b9ee89 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -2305,9 +2305,28 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
}
-int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
- const char *name, size_t namelen,
- enum fuse_expire_flags flags)
+/**
+ * Notify parent attributes and the dentry matching parent/name
+ *
+ * Underlying base function for fuse_lowlevel_notify_inval_entry() and
+ * fuse_lowlevel_notify_expire_entry().
+ *
+ * @warning
+ * Only checks if fuse_lowlevel_notify_inval_entry() is supported by
+ * the kernel. All other flags will fall back to
+ * fuse_lowlevel_notify_inval_entry() if not supported!
+ * DO THE PROPER CHECKS IN THE DERIVED FUNCTION!
+ *
+ * @param se the session object
+ * @param parent inode number
+ * @param name file name
+ * @param namelen strlen() of file name
+ * @param flags flags to control if the entry should be expired or invalidated
+ * @return zero for success, -errno for failure
+*/
+static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
+ const char *name, size_t namelen,
+ enum fuse_notify_entry_flags flags)
{
struct fuse_notify_inval_entry_out outarg;
struct iovec iov[3];
@@ -2333,9 +2352,21 @@ int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent
}
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
- const char *name, size_t namelen)
+ const char *name, size_t namelen)
+{
+ return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
+}
+
+int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
+ const char *name, size_t namelen)
{
- return fuse_lowlevel_notify_expire_entry(se, parent, name, namelen, 0);
+ if (!se)
+ return -EINVAL;
+
+ if (!(se->conn.capable & FUSE_CAP_EXPIRE_ONLY))
+ return -ENOSYS;
+
+ return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
}
diff --git a/test/test_examples.py b/test/test_examples.py
index 958e633..96e4108 100755
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -347,6 +347,8 @@ def test_notify_inval_entry(tmpdir, only_expire, notify, output_checker):
cmdline.append('--no-notify')
if only_expire == "expire_entries":
cmdline.append('--only-expire')
+ if fuse_proto < (7,38):
+ pytest.skip('only-expire not supported by running kernel')
mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
stderr=output_checker.fd)
try: