From 51bc827df873d9ff4069b83796cd32fcb6bd659e Mon Sep 17 00:00:00 2001 From: HereThereBeDragons Date: Fri, 30 Jun 2023 14:57:06 +0200 Subject: [PATCH] Make expire only function fail if no kernel support (#789) --- include/fuse_common.h | 2 +- include/fuse_lowlevel.h | 28 ++++++++++++++-------------- lib/fuse_lowlevel.c | 41 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 20 deletions(-) --- a/include/fuse_common.h +++ b/include/fuse_common.h @@ -395,7 +395,7 @@ struct fuse_loop_config { #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 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -128,11 +128,12 @@ struct fuse_forget_data { }; /** - * 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), }; @@ -1657,8 +1658,7 @@ int fuse_lowlevel_notify_inval_inode(str 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 filesytem operation or within any code @@ -1685,14 +1685,13 @@ int fuse_lowlevel_notify_inval_entry(str 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. @@ -1703,17 +1702,18 @@ int fuse_lowlevel_notify_inval_entry(str * 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 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -2257,9 +2257,28 @@ int fuse_lowlevel_notify_inval_inode(str 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]; @@ -2285,9 +2304,21 @@ int fuse_lowlevel_notify_expire_entry(st } 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); }