aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--example/notify_inval_entry.c42
-rw-r--r--include/fuse_common.h2
-rw-r--r--include/fuse_lowlevel.h37
-rw-r--r--lib/fuse_lowlevel.c41
-rw-r--r--meson.build10
-rwxr-xr-xtest/test_examples.py2
-rw-r--r--test/test_write_cache.c3
-rw-r--r--util/fuse.conf16
8 files changed, 109 insertions, 44 deletions
diff --git a/example/notify_inval_entry.c b/example/notify_inval_entry.c
index 8af31bc..0d085df 100644
--- a/example/notify_inval_entry.c
+++ b/example/notify_inval_entry.c
@@ -85,7 +85,9 @@
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
+#include <signal.h>
#include <stddef.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
@@ -93,6 +95,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 +256,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 +333,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 +345,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 +359,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 9142b1c..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
@@ -2112,9 +2112,12 @@ int fuse_session_loop(struct fuse_session *se);
/**
* Flag a session as terminated.
*
- * This function is invoked by the POSIX signal handlers, when
- * registered using fuse_set_signal_handlers(). It will cause any
- * running event loops to terminate on the next opportunity.
+ * This will cause any running event loops to terminate on the next opportunity. If this function is
+ * called by a thread that is not a FUSE worker thread, the next
+ * opportunity will be when FUSE a request is received (which may be far in the future if the
+ * filesystem is not currently being used by any clients). One way to avoid this delay is to
+ * afterwards sent a signal to the main thread (if fuse_set_signal_handlers() is used, SIGPIPE
+ * will cause the main thread to wake-up but otherwise be ignored).
*
* @param se the session
*/
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/meson.build b/meson.build
index 489a815..c42d591 100644
--- a/meson.build
+++ b/meson.build
@@ -177,10 +177,12 @@ int main()
}
'''
-result = cc.run(detect_getmntent_needs_unescape)
-if result.compiled() and result.returncode() == 0 and result.stdout().strip() == 'needs escaping'
- message('getmntent does not unescape')
- add_project_arguments('-DGETMNTENT_NEEDS_UNESCAPING', language: 'c')
+if not meson.is_cross_build()
+ result = cc.run(detect_getmntent_needs_unescape)
+ if result.compiled() and result.returncode() == 0 and result.stdout().strip() == 'needs escaping'
+ message('getmntent does not unescape')
+ add_project_arguments('-DGETMNTENT_NEEDS_UNESCAPING', language: 'c')
+ endif
endif
# Write private test results into fuse_config.h (stored in build directory)
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:
diff --git a/test/test_write_cache.c b/test/test_write_cache.c
index bc62ac1..e0bdeb9 100644
--- a/test/test_write_cache.c
+++ b/test/test_write_cache.c
@@ -19,6 +19,7 @@
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
+#include <sys/stat.h>
#include <pthread.h>
#ifndef __linux__
@@ -37,7 +38,7 @@ struct options {
int delay_ms;
} options = {
.writeback = 0,
- .data_size = 4096,
+ .data_size = 2048,
.delay_ms = 0,
};
diff --git a/util/fuse.conf b/util/fuse.conf
index 3c9b6ac..ab048e0 100644
--- a/util/fuse.conf
+++ b/util/fuse.conf
@@ -1,17 +1,17 @@
# The file /etc/fuse.conf allows for the following parameters:
#
-# user_allow_other - Using the allow_other mount option works fine as root, in
-# order to have it work as user you need user_allow_other in /etc/fuse.conf as
-# well. (This option allows users to use the allow_other option.) You need
-# allow_other if you want users other than the owner to access a mounted fuse.
-# This option must appear on a line by itself. There is no value, just the
-# presence of the option.
+# user_allow_other - Using the allow_other mount option works fine as root, but
+# in order to have it work as a regular user, you need to set user_allow_other
+# in /etc/fuse.conf as well. This option allows non-root users to use the
+# allow_other option. You need allow_other if you want users other than the
+# owner of a mounted fuse to access it. This option must appear on a line by
+# itself. There is no value; just the presence of the option activates it.
#user_allow_other
# mount_max = n - this option sets the maximum number of mounts.
-# Currently (2014) it must be typed exactly as shown
-# (with a single space before and after the equals sign).
+# It must be typed exactly as shown (with a single space before and after the
+# equals sign).
#mount_max = 1000