* [Devel] [PATCH VZ10] fs/fuse: strictly segregate vstorage features of generic fuse
@ 2026-09-15 16:00 Alexey Kuznetsov
2026-09-23 16:13 ` [Devel] [PATCH hci-8.1 v2] " Konstantin Khorenko
0 siblings, 1 reply; 3+ messages in thread
From: Alexey Kuznetsov @ 2026-09-15 16:00 UTC (permalink / raw)
To: devel, khorenko, kui.liu, azaitsev
1. Makes explicit use of option "close_wait" as specific to vstorage.
The option requires some time to be fully engaged, the support
should appear in kernel first then we use it in userspace,
then we can get rid of original ugly way relying on special
string name [pv]storage://
2. Option "close_wait" is privileged. It cannot be used inside
containers and ordinary users of fusermount are not allowed.
3. Our enhancements like multiple queues, zero-copy transfers,
lease invalidate FUSE_NOTIFY_INVAL_FILES etc. are explicitly
prohibited w/o close_wait.
4. When close_wait is enabled, we explicitly disallow to use
creepy bug prone fuse features like fuse interrupts, resends,
we reject all obscure notifications except for supported ones,
which is now only FUSE_NOTIFY_INVAL_FILES and value 4, which
will be dropped after old user space is washed out
Affects: #VSTOR-144529
Signed-off-by: Alexey Kuznetsov <kuznet@virtuozzo.com>
---
fs/fuse/dev.c | 32 ++++++++++++++++++--------
fs/fuse/fuse_i.h | 5 -----
fs/fuse/inode.c | 58 ++++++++++++++++++++++++++++++++++++++----------
3 files changed, 69 insertions(+), 26 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a9de0f815774..6a22fa73330b 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2019,6 +2019,8 @@ static int fuse_notify_inval_files(struct fuse_dev *fud, unsigned int size,
struct fuse_notify_inval_files_out outarg;
int err = -EINVAL;
+ if (!fc->close_wait)
+ goto err;
if (size != sizeof(outarg))
goto err;
@@ -2048,8 +2050,19 @@ static int fuse_notify(struct fuse_dev *fud, enum fuse_notify_code code,
cs->move_pages = 0;
/* ASSUMPTION: pstorage fused doesn't use FUSE_NOTIFY_STORE */
- if (fc->compat_inval_files && code == 4)
- code = FUSE_NOTIFY_INVAL_FILES;
+ if (fc->close_wait) {
+ if (code == 4)
+ code = FUSE_NOTIFY_INVAL_FILES;
+ if (code != FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ } else {
+ if (code == FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ }
switch (code) {
case FUSE_NOTIFY_POLL:
@@ -2412,10 +2425,11 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
}
err = -EINVAL;
- if (oh.error != FUSE_OUT_SPLICES &&
- oh.error != FUSE_OUT_KRPCZC &&
- (oh.error <= -512 || oh.error > 0))
- goto copy_finish;
+ if (oh.error <= -512 || oh.error > 0) {
+ if (!fc->close_wait ||
+ (oh.error != FUSE_OUT_SPLICES && oh.error != FUSE_OUT_KRPCZC))
+ goto copy_finish;
+ }
spin_lock(&fpq->lock);
req = NULL;
@@ -2438,7 +2452,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
err = -EINVAL;
else if (oh.error == -ENOSYS)
fc->no_interrupt = 1;
- else if (oh.error == -EAGAIN)
+ else if (oh.error == -EAGAIN && !fc->no_interrupt)
err = queue_interrupt(req->args->fiq, req);
fuse_put_request(req);
@@ -2455,10 +2469,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (!req->args->page_replace)
cs->move_pages = 0;
- if (oh.error == FUSE_OUT_SPLICES) {
+ if (oh.error == FUSE_OUT_SPLICES && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_splices(cs, req->args, nbytes);
- } else if (oh.error == FUSE_OUT_KRPCZC) {
+ } else if (oh.error == FUSE_OUT_KRPCZC && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_krpczc(cs, req->args, nbytes);
} else if (oh.error)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8d90f43f01c9..21f873337627 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -669,8 +669,6 @@ struct fuse_fs_context {
bool umount_wait:1;
bool close_wait:1;
bool disable_close_wait:1;
- bool compat_inval_files:1;
- bool no_fiemap:1;
bool kdirect_io:1;
bool destroy:1;
bool no_control:1;
@@ -1056,9 +1054,6 @@ struct fuse_conn {
/** Does the disable synchronous close? */
unsigned disable_close_wait:1;
- /** Handle wrong FUSE_NOTIFY_INVAL_FILES from old fused */
- unsigned compat_inval_files:1;
-
/** No ioctl(FIEMAP) */
unsigned no_fiemap:1;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ea158367a1ef..e70e0eedbb26 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -912,6 +912,7 @@ enum {
OPT_ODIRECT,
OPT_UMOUNT_WAIT,
OPT_DISABLE_CLOSE_WAIT,
+ OPT_CLOSE_WAIT,
OPT_ERR,
OPT_KIO_NAME
};
@@ -930,11 +931,18 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = {
fsparam_flag ("direct_enable", OPT_ODIRECT),
fsparam_flag ("umount_wait", OPT_UMOUNT_WAIT),
fsparam_flag ("disable_close_wait", OPT_DISABLE_CLOSE_WAIT),
+ fsparam_flag ("close_wait", OPT_CLOSE_WAIT),
fsparam_string ("kdirect", OPT_KIO_NAME),
fsparam_string ("subtype", OPT_SUBTYPE),
{}
};
+static int is_close_wait_allowed(void)
+{
+ return ve_is_super(get_exec_env()) && capable(CAP_SYS_ADMIN) &&
+ uid_eq(current_uid(), GLOBAL_ROOT_UID);
+}
+
static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
{
struct fs_parse_result result;
@@ -966,19 +974,22 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
param->string = NULL;
/* Hack to distinguish pcs fuse service and to force
- * synchronous close for it.
- *
- * libfuse is not ready for fiemap autodetection, so disable
- * fiemap support check in advance for all FUSE fs except for
- * pStorage.
+ * synchronous close for it. It is on way out, we are going
+ * to switch to mount option "close_wait", but this requires
+ * full cycle: support mount option in kernel, switch
+ * user space, wait till old user space is gone.
*/
if (fsc->source &&
(strncmp(fsc->source, "pstorage://", 11) == 0 ||
strncmp(fsc->source, "vstorage://", 11) == 0)) {
- ctx->close_wait = 1;
- ctx->compat_inval_files = 1;
- } else
- ctx->no_fiemap = 1;
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ /* If not allowed, just ignore. We can use vstorage mount
+ * in restricted environment, but with disabled
+ * features like multiple queues, precise metadata
+ * tracking and kernel acceleration.
+ */
+ }
break;
@@ -1066,6 +1077,13 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
ctx->close_wait = 0;
break;
+ case OPT_CLOSE_WAIT:
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ else
+ return -EPERM;
+ break;
+
case OPT_KIO_NAME: {
if (!ve_is_super(get_exec_env()))
return -EPERM;
@@ -1118,6 +1136,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",umount_wait");
if (fc->disable_close_wait)
seq_puts(m, ",disable_close_wait");
+ if (fc->close_wait)
+ seq_puts(m, ",close_wait");
if (fc->max_read != ~0)
seq_printf(m, ",max_read=%u", fc->max_read);
if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE)
@@ -1241,6 +1261,9 @@ int fuse_install_iq_route(struct fuse_dev *fud, struct fuse_iq_routing *req)
&fud->fc->rrt;
struct fuse_rtable rtl;
+ if (!fud->fc->close_wait)
+ return -EINVAL;
+
if (rt->type != FUSE_ROUTING_NONE && rt->type != req->type)
return -EINVAL;
@@ -2182,17 +2205,28 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
fc->umount_wait = ctx->umount_wait;
fc->close_wait = ctx->close_wait;
fc->disable_close_wait = ctx->disable_close_wait;
- fc->compat_inval_files = ctx->compat_inval_files;
- fc->no_fiemap = ctx->no_fiemap;
+ fc->no_fiemap = 0;
fc->writeback_cache = ctx->writeback_cache;
fc->destroy = ctx->destroy || ctx->umount_wait;
fc->no_control = ctx->no_control;
fc->no_force_umount = ctx->no_force_umount;
fc->kdirect_io = ctx->kdirect_io;
+ if (fc->close_wait) {
+ fc->no_interrupt = 1;
+ } else {
+ /* libfuse is not ready for fiemap autodetection, so disable
+ * fiemap support check in advance for all FUSE fs except for
+ * pStorage.
+ * NOTE as of 260912. This might be not true now, yet this
+ * still makes sense.
+ */
+ fc->no_fiemap = 1;
+ }
+
if (fc->kdirect_io) {
err = -EINVAL;
- if (!fc->writeback_cache)
+ if (!fc->writeback_cache || !fc->close_wait)
goto err_dev_free;
fc->kio.cached_op = fuse_kio_get(fc, ctx->kio_name);
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 3+ messages in thread
* [Devel] [PATCH hci-8.1 v2] fs/fuse: strictly segregate vstorage features of generic fuse
2026-09-15 16:00 [Devel] [PATCH VZ10] fs/fuse: strictly segregate vstorage features of generic fuse Alexey Kuznetsov
@ 2026-09-23 16:13 ` Konstantin Khorenko
2026-09-23 16:18 ` Konstantin Khorenko
0 siblings, 1 reply; 3+ messages in thread
From: Konstantin Khorenko @ 2026-09-23 16:13 UTC (permalink / raw)
To: Alexey Kuznetsov, OpenVZ devel list; +Cc: Liu Kui
From: Alexey Kuznetsov <kuznet@virtuozzo.com>
1. Makes explicit use of option "close_wait" as specific to vstorage.
The option requires some time to be fully engaged, the support
should appear in kernel first then we use it in userspace,
then we can get rid of original ugly way relying on special
string name [pv]storage://
2. Option "close_wait" is privileged. It cannot be used inside
containers and ordinary users of fusermount are not allowed.
3. Our enhancements like multiple queues, zero-copy transfers,
lease invalidate FUSE_NOTIFY_INVAL_FILES etc. are explicitly
prohibited w/o close_wait.
4. When close_wait is enabled, we explicitly disallow to use
creepy bug prone fuse features like fuse interrupts, resends,
we reject all obscure notifications except for supported ones,
which is now only FUSE_NOTIFY_INVAL_FILES and value 4, which
will be dropped after old user space is washed out
https://virtuozzo.atlassian.net/browse/VSTOR-144529
Feature: vStorage
Signed-off-by: Alexey Kuznetsov <kuznet@virtuozzo.com>
Reviewed-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
v1 -> v2:
- is_close_wait_allowed() returns bool instead of int (khorenko@)
fs/fuse/dev.c | 32 ++++++++++++++++++--------
fs/fuse/fuse_i.h | 5 -----
fs/fuse/inode.c | 58 ++++++++++++++++++++++++++++++++++++++----------
3 files changed, 69 insertions(+), 26 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a9de0f815774a..6a22fa73330b7 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2019,6 +2019,8 @@ static int fuse_notify_inval_files(struct fuse_dev *fud, unsigned int size,
struct fuse_notify_inval_files_out outarg;
int err = -EINVAL;
+ if (!fc->close_wait)
+ goto err;
if (size != sizeof(outarg))
goto err;
@@ -2048,8 +2050,19 @@ static int fuse_notify(struct fuse_dev *fud, enum fuse_notify_code code,
cs->move_pages = 0;
/* ASSUMPTION: pstorage fused doesn't use FUSE_NOTIFY_STORE */
- if (fc->compat_inval_files && code == 4)
- code = FUSE_NOTIFY_INVAL_FILES;
+ if (fc->close_wait) {
+ if (code == 4)
+ code = FUSE_NOTIFY_INVAL_FILES;
+ if (code != FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ } else {
+ if (code == FUSE_NOTIFY_INVAL_FILES) {
+ fuse_copy_finish(cs);
+ return -EINVAL;
+ }
+ }
switch (code) {
case FUSE_NOTIFY_POLL:
@@ -2412,10 +2425,11 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
}
err = -EINVAL;
- if (oh.error != FUSE_OUT_SPLICES &&
- oh.error != FUSE_OUT_KRPCZC &&
- (oh.error <= -512 || oh.error > 0))
- goto copy_finish;
+ if (oh.error <= -512 || oh.error > 0) {
+ if (!fc->close_wait ||
+ (oh.error != FUSE_OUT_SPLICES && oh.error != FUSE_OUT_KRPCZC))
+ goto copy_finish;
+ }
spin_lock(&fpq->lock);
req = NULL;
@@ -2438,7 +2452,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
err = -EINVAL;
else if (oh.error == -ENOSYS)
fc->no_interrupt = 1;
- else if (oh.error == -EAGAIN)
+ else if (oh.error == -EAGAIN && !fc->no_interrupt)
err = queue_interrupt(req->args->fiq, req);
fuse_put_request(req);
@@ -2455,10 +2469,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (!req->args->page_replace)
cs->move_pages = 0;
- if (oh.error == FUSE_OUT_SPLICES) {
+ if (oh.error == FUSE_OUT_SPLICES && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_splices(cs, req->args, nbytes);
- } else if (oh.error == FUSE_OUT_KRPCZC) {
+ } else if (oh.error == FUSE_OUT_KRPCZC && fc->close_wait) {
req->out.h.error = 0;
err = copy_out_krpczc(cs, req->args, nbytes);
} else if (oh.error)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 8d90f43f01c92..21f8733376270 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -669,8 +669,6 @@ struct fuse_fs_context {
bool umount_wait:1;
bool close_wait:1;
bool disable_close_wait:1;
- bool compat_inval_files:1;
- bool no_fiemap:1;
bool kdirect_io:1;
bool destroy:1;
bool no_control:1;
@@ -1056,9 +1054,6 @@ struct fuse_conn {
/** Does the disable synchronous close? */
unsigned disable_close_wait:1;
- /** Handle wrong FUSE_NOTIFY_INVAL_FILES from old fused */
- unsigned compat_inval_files:1;
-
/** No ioctl(FIEMAP) */
unsigned no_fiemap:1;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ea158367a1efd..eb2e9a4f87564 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -912,6 +912,7 @@ enum {
OPT_ODIRECT,
OPT_UMOUNT_WAIT,
OPT_DISABLE_CLOSE_WAIT,
+ OPT_CLOSE_WAIT,
OPT_ERR,
OPT_KIO_NAME
};
@@ -930,11 +931,18 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = {
fsparam_flag ("direct_enable", OPT_ODIRECT),
fsparam_flag ("umount_wait", OPT_UMOUNT_WAIT),
fsparam_flag ("disable_close_wait", OPT_DISABLE_CLOSE_WAIT),
+ fsparam_flag ("close_wait", OPT_CLOSE_WAIT),
fsparam_string ("kdirect", OPT_KIO_NAME),
fsparam_string ("subtype", OPT_SUBTYPE),
{}
};
+static bool is_close_wait_allowed(void)
+{
+ return ve_is_super(get_exec_env()) && capable(CAP_SYS_ADMIN) &&
+ uid_eq(current_uid(), GLOBAL_ROOT_UID);
+}
+
static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
{
struct fs_parse_result result;
@@ -966,19 +974,22 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
param->string = NULL;
/* Hack to distinguish pcs fuse service and to force
- * synchronous close for it.
- *
- * libfuse is not ready for fiemap autodetection, so disable
- * fiemap support check in advance for all FUSE fs except for
- * pStorage.
+ * synchronous close for it. It is on way out, we are going
+ * to switch to mount option "close_wait", but this requires
+ * full cycle: support mount option in kernel, switch
+ * user space, wait till old user space is gone.
*/
if (fsc->source &&
(strncmp(fsc->source, "pstorage://", 11) == 0 ||
strncmp(fsc->source, "vstorage://", 11) == 0)) {
- ctx->close_wait = 1;
- ctx->compat_inval_files = 1;
- } else
- ctx->no_fiemap = 1;
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ /* If not allowed, just ignore. We can use vstorage mount
+ * in restricted environment, but with disabled
+ * features like multiple queues, precise metadata
+ * tracking and kernel acceleration.
+ */
+ }
break;
@@ -1066,6 +1077,13 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
ctx->close_wait = 0;
break;
+ case OPT_CLOSE_WAIT:
+ if (is_close_wait_allowed())
+ ctx->close_wait = 1;
+ else
+ return -EPERM;
+ break;
+
case OPT_KIO_NAME: {
if (!ve_is_super(get_exec_env()))
return -EPERM;
@@ -1118,6 +1136,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",umount_wait");
if (fc->disable_close_wait)
seq_puts(m, ",disable_close_wait");
+ if (fc->close_wait)
+ seq_puts(m, ",close_wait");
if (fc->max_read != ~0)
seq_printf(m, ",max_read=%u", fc->max_read);
if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE)
@@ -1241,6 +1261,9 @@ int fuse_install_iq_route(struct fuse_dev *fud, struct fuse_iq_routing *req)
&fud->fc->rrt;
struct fuse_rtable rtl;
+ if (!fud->fc->close_wait)
+ return -EINVAL;
+
if (rt->type != FUSE_ROUTING_NONE && rt->type != req->type)
return -EINVAL;
@@ -2182,17 +2205,28 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
fc->umount_wait = ctx->umount_wait;
fc->close_wait = ctx->close_wait;
fc->disable_close_wait = ctx->disable_close_wait;
- fc->compat_inval_files = ctx->compat_inval_files;
- fc->no_fiemap = ctx->no_fiemap;
+ fc->no_fiemap = 0;
fc->writeback_cache = ctx->writeback_cache;
fc->destroy = ctx->destroy || ctx->umount_wait;
fc->no_control = ctx->no_control;
fc->no_force_umount = ctx->no_force_umount;
fc->kdirect_io = ctx->kdirect_io;
+ if (fc->close_wait) {
+ fc->no_interrupt = 1;
+ } else {
+ /* libfuse is not ready for fiemap autodetection, so disable
+ * fiemap support check in advance for all FUSE fs except for
+ * pStorage.
+ * NOTE as of 260912. This might be not true now, yet this
+ * still makes sense.
+ */
+ fc->no_fiemap = 1;
+ }
+
if (fc->kdirect_io) {
err = -EINVAL;
- if (!fc->writeback_cache)
+ if (!fc->writeback_cache || !fc->close_wait)
goto err_dev_free;
fc->kio.cached_op = fuse_kio_get(fc, ctx->kio_name);
--
2.43.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2] fs/fuse: strictly segregate vstorage features of generic fuse
2026-09-23 16:13 ` [Devel] [PATCH hci-8.1 v2] " Konstantin Khorenko
@ 2026-09-23 16:18 ` Konstantin Khorenko
0 siblings, 0 replies; 3+ messages in thread
From: Konstantin Khorenko @ 2026-09-23 16:18 UTC (permalink / raw)
To: Alexey Kuznetsov, OpenVZ devel list, Konstantin Khorenko; +Cc: Liu Kui
On Wed, 23 Sep 2026 18:13:35 +0200, Konstantin Khorenko wrote:
> fs/fuse: strictly segregate vstorage features of generic fuse
Applied to branch-rh10-6.12.0-211.52.1.22.x.vz10-ovz, will be released in rh10-6.12.0-211.52.1.22.3.vz10, thanks!
[1/1] fs/fuse: strictly segregate vstorage features of generic fuse
https://bitbucket.org/virtuozzocore/vzkernel.vzs/commits/ae8662087938
--
Konstantin Khorenko <khorenko@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-23 16:19 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 16:00 [Devel] [PATCH VZ10] fs/fuse: strictly segregate vstorage features of generic fuse Alexey Kuznetsov
2026-09-23 16:13 ` [Devel] [PATCH hci-8.1 v2] " Konstantin Khorenko
2026-09-23 16:18 ` Konstantin Khorenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox