[PATCH 16/19] fuse2fs: improve error handling behaviors

Darrick J. Wong
2025-11-06T22:35:20Z

Make the behavior of fuse2fs on filesystem errors consistent with what
the kernel driver does. Sort of. We can't panic the kernel, but we can
abort the server, which leaves a dead mount.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.1.in | 6 ++++++
misc/fuse2fs.c | 42 +++++++++++++++++++++++++++++++++++++++---
2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/misc/fuse2fs.1.in b/misc/fuse2fs.1.in
index b18b7f3464bc74..6acfa092851292 100644
--- a/misc/fuse2fs.1.in
+++ b/misc/fuse2fs.1.in
@@ -60,6 +60,12 @@ .SS "fuse2fs options:"
\fB-o\fR dirsync
Flush dirty metadata to disk after every directory update.
.TP
+\fB-o\fR errors=continue
+ignore errors
+.TP
+\fB-o\fR errors=remount-ro
+stop allowing writes after errors
+.TP
\fB-o\fR errors=panic
dump core on error
.TP
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index d890855df9c0f3..fd21f546db7fb1 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -224,7 +224,7 @@ struct fuse2fs {
int ro;
int debug;
int no_default_opts;
- int panic_on_error;
+ int errors_behavior; /* actually an enum */
int minixdf;
int fakeroot;
int alloc_all_blocks;
@@ -4771,6 +4771,7 @@ enum {
FUSE2FS_HELPFULL,
FUSE2FS_CACHE_SIZE,
FUSE2FS_DIRSYNC,
+ FUSE2FS_ERRORS_BEHAVIOR,
};

#define FUSE2FS_OPT(t, p, v) { t, offsetof(struct fuse2fs, p), v }
@@ -4778,7 +4779,6 @@ enum {
static struct fuse_opt fuse2fs_opts[] = {
FUSE2FS_OPT("ro", ro, 1),
FUSE2FS_OPT("rw", ro, 0),
- FUSE2FS_OPT("errors=panic", panic_on_error, 1),
FUSE2FS_OPT("minixdf", minixdf, 1),
FUSE2FS_OPT("bsddf", minixdf, 0),
FUSE2FS_OPT("fakeroot", fakeroot, 1),
@@ -4798,6 +4798,7 @@ static struct fuse_opt fuse2fs_opts[] = {
FUSE_OPT_KEY("nodelalloc", FUSE2FS_IGNORED),
FUSE_OPT_KEY("cache_size=%s", FUSE2FS_CACHE_SIZE),
FUSE_OPT_KEY("dirsync", FUSE2FS_DIRSYNC),
+ FUSE_OPT_KEY("errors=%s", FUSE2FS_ERRORS_BEHAVIOR),

FUSE_OPT_KEY("-V", FUSE2FS_VERSION),
FUSE_OPT_KEY("--version", FUSE2FS_VERSION),
@@ -4832,6 +4833,21 @@ static int fuse2fs_opt_proc(void *data, const char *arg,
return -1;
}

+ /* do not pass through to libfuse */
+ return 0;
+ case FUSE2FS_ERRORS_BEHAVIOR:
+ if (strcmp(arg + 7, "continue") == 0)
+ ff->errors_behavior = EXT2_ERRORS_CONTINUE;
+ else if (strcmp(arg + 7, "remount-ro") == 0)
+ ff->errors_behavior = EXT2_ERRORS_RO;
+ else if (strcmp(arg + 7, "panic") == 0)
+ ff->errors_behavior = EXT2_ERRORS_PANIC;
+ else {
+ fprintf(stderr, "%s: %s\n", arg,
+ _("unknown errors behavior."));
+ return -1;
+ }
+
/* do not pass through to libfuse */
return 0;
case FUSE2FS_IGNORED:
@@ -4859,6 +4875,8 @@ static int fuse2fs_opt_proc(void *data, const char *arg,
" allow_others,default_permissions,suid,dev\n"
" -o directio use O_DIRECT to read and write the disk\n"
" -o cache_size=N[KMG] use a disk cache of this size\n"
+ " -o errors= behavior when an error is encountered:\n"
+ " continue|remount-ro|panic\n"
"\n",
outargs->argv[0]);
if (key == FUSE2FS_HELPFULL) {
@@ -5226,6 +5244,9 @@ int main(int argc, char *argv[])
}
}

+ if (!fctx.errors_behavior)
+ fctx.errors_behavior = global_fs->super->s_errors;
+
/* Initialize generation counter */
get_random_bytes(&fctx.next_generation, sizeof(unsigned int));

@@ -5492,8 +5513,23 @@ static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
fs->super->s_error_count++;
ext2fs_mark_super_dirty(fs);
ext2fs_flush(fs);
- if (ff->panic_on_error)
+ switch (ff->errors_behavior) {
+ case EXT2_ERRORS_CONTINUE:
+ err_printf(ff, "%s\n",
+ _("Continuing after errors; is this a good idea?"));
+ break;
+ case EXT2_ERRORS_RO:
+ if (fs->flags & EXT2_FLAG_RW)
+ err_printf(ff, "%s\n",
+ _("Remounting read-only due to errors."));
+ fs->flags &= ~EXT2_FLAG_RW;
+ break;
+ case EXT2_ERRORS_PANIC:
+ err_printf(ff, "%s\n",
+ _("Aborting filesystem mount due to errors."));
abort();
+ break;
+ }

return ret;
}