* [PATCHSET 6/6] fuse2fs: better tracking of writable state
@ 2025-09-15 23:59 Darrick J. Wong
2025-09-16 0:05 ` [PATCH 1/3] fuse2fs: pass a struct fuse2fs to fs_writeable Darrick J. Wong
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-09-15 23:59 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
Hi all,
There are multiple mutability variables in play in fuse2fs -- first,
EXT2_FLAG_RW tracks whether or not we can write anything to the
filesystem. However, there's a second state, which is whether or not
we actually want to write to the filesystem, regardless of the library
state. This can happen if we open libext2fs for writing, but then
discover something about the filesystem that makes us not want to write
to it after all.
Split out this second variable into an explicit variable in fuse2fs.
If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.
Comments and questions are, as always, welcome.
e2fsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/e2fsprogs.git/log/?h=fuse2fs-writability
---
Commits in this patchset:
* fuse2fs: pass a struct fuse2fs to fs_writeable
* fuse2fs: track our own writable state
* fuse2fs: enable the shutdown ioctl
---
misc/fuse2fs.c | 97 ++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 69 insertions(+), 28 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] fuse2fs: pass a struct fuse2fs to fs_writeable
2025-09-15 23:59 [PATCHSET 6/6] fuse2fs: better tracking of writable state Darrick J. Wong
@ 2025-09-16 0:05 ` Darrick J. Wong
2025-09-16 0:05 ` [PATCH 2/3] fuse2fs: track our own writable state Darrick J. Wong
2025-09-16 0:05 ` [PATCH 3/3] fuse2fs: enable the shutdown ioctl Darrick J. Wong
2 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-09-16 0:05 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
From: Darrick J. Wong <djwong@kernel.org>
Pass the outer fuse2fs context to fs_writable in preparation for the
next patch.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 6173d6f51892a9..dd731d84c4535f 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -726,8 +726,10 @@ static int fs_can_allocate(struct fuse2fs *ff, blk64_t num)
return ext2fs_free_blocks_count(fs->super) > reserved + num;
}
-static int fs_writeable(ext2_filsys fs)
+static int fuse2fs_is_writeable(struct fuse2fs *ff)
{
+ ext2_filsys fs = ff->fs;
+
return (fs->flags & EXT2_FLAG_RW) && (fs->super->s_error_count == 0);
}
@@ -756,12 +758,10 @@ static inline int want_check_owner(struct fuse2fs *ff,
static int check_iflags_access(struct fuse2fs *ff, ext2_ino_t ino,
const struct ext2_inode *inode, int mask)
{
- ext2_filsys fs = ff->fs;
-
EXT2FS_BUILD_BUG_ON((A_OK & (R_OK | W_OK | X_OK | F_OK)) != 0);
/* no writing or metadata changes to read-only or broken fs */
- if ((mask & (W_OK | A_OK)) && !fs_writeable(fs))
+ if ((mask & (W_OK | A_OK)) && !fuse2fs_is_writeable(ff))
return -EROFS;
dbg_printf(ff, "access ino=%d mask=e%s%s%s%s iflags=0x%x\n",
@@ -794,7 +794,7 @@ static int check_inum_access(struct fuse2fs *ff, ext2_ino_t ino, int mask)
int ret;
/* no writing to read-only or broken fs */
- if ((mask & (W_OK | A_OK)) && !fs_writeable(fs))
+ if ((mask & (W_OK | A_OK)) && !fuse2fs_is_writeable(ff))
return -EROFS;
err = ext2fs_read_inode(fs, ino, &inode);
@@ -1517,7 +1517,7 @@ static int op_readlink(const char *path, char *buf, size_t len)
}
buf[len] = 0;
- if (fs_writeable(fs)) {
+ if (fuse2fs_is_writeable(ff)) {
ret = update_atime(fs, ino);
if (ret)
goto out;
@@ -3252,7 +3252,7 @@ static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
goto out;
}
- if (fh->check_flags != X_OK && fs_writeable(fs)) {
+ if (fh->check_flags != X_OK && fuse2fs_is_writeable(ff)) {
ret = update_atime(fs, fh->ino);
if (ret)
goto out;
@@ -3279,7 +3279,7 @@ static int op_write(const char *path EXT2FS_ATTR((unused)),
dbg_printf(ff, "%s: ino=%d off=0x%llx len=0x%zx\n", __func__, fh->ino,
(unsigned long long) offset, len);
fs = fuse2fs_start(ff);
- if (!fs_writeable(fs)) {
+ if (!fuse2fs_is_writeable(ff)) {
ret = -EROFS;
goto out;
}
@@ -3347,7 +3347,7 @@ static int op_release(const char *path EXT2FS_ATTR((unused)),
fs = fuse2fs_start(ff);
if ((fp->flags & O_SYNC) &&
- fs_writeable(fs) &&
+ fuse2fs_is_writeable(ff) &&
(fh->open_flags & EXT2_FILE_WRITE)) {
err = ext2fs_flush2(fs, EXT2_FLAG_FLUSH_NO_SYNC);
if (err)
@@ -3377,7 +3377,7 @@ static int op_fsync(const char *path EXT2FS_ATTR((unused)),
dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
fs = fuse2fs_start(ff);
/* For now, flush everything, even if it's slow */
- if (fs_writeable(fs) && fh->open_flags & EXT2_FILE_WRITE) {
+ if (fuse2fs_is_writeable(ff) && fh->open_flags & EXT2_FILE_WRITE) {
err = ext2fs_flush2(fs, 0);
if (err)
ret = translate_error(fs, fh->ino, err);
@@ -3915,7 +3915,7 @@ static int op_readdir(const char *path EXT2FS_ATTR((unused)),
goto out;
}
- if (fs_writeable(i.fs)) {
+ if (fuse2fs_is_writeable(ff)) {
ret = update_atime(i.fs, fh->ino);
if (ret)
goto out;
@@ -4097,7 +4097,7 @@ static int op_ftruncate(const char *path EXT2FS_ATTR((unused)),
dbg_printf(ff, "%s: ino=%d len=%jd\n", __func__, fh->ino,
(intmax_t) len);
fs = fuse2fs_start(ff);
- if (!fs_writeable(fs)) {
+ if (!fuse2fs_is_writeable(ff)) {
ret = -EROFS;
goto out;
}
@@ -4475,7 +4475,7 @@ static int ioctl_fitrim(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
blk64_t max_blks = ext2fs_blocks_count(fs->super);
errcode_t err = 0;
- if (!fs_writeable(fs))
+ if (!fuse2fs_is_writeable(ff))
return -EROFS;
start = FUSE2FS_B_TO_FSBT(ff, fr->start);
@@ -4893,7 +4893,6 @@ static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
{
struct fuse2fs *ff = fuse2fs_get();
struct fuse2fs_file_handle *fh = fuse2fs_get_handle(fp);
- ext2_filsys fs;
int ret;
/* Catch unknown flags */
@@ -4902,8 +4901,8 @@ static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
FUSE2FS_CHECK_CONTEXT(ff);
FUSE2FS_CHECK_HANDLE(ff, fh);
- fs = fuse2fs_start(ff);
- if (!fs_writeable(fs)) {
+ fuse2fs_start(ff);
+ if (!fuse2fs_is_writeable(ff)) {
ret = -EROFS;
goto out;
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] fuse2fs: track our own writable state
2025-09-15 23:59 [PATCHSET 6/6] fuse2fs: better tracking of writable state Darrick J. Wong
2025-09-16 0:05 ` [PATCH 1/3] fuse2fs: pass a struct fuse2fs to fs_writeable Darrick J. Wong
@ 2025-09-16 0:05 ` Darrick J. Wong
2025-09-16 0:05 ` [PATCH 3/3] fuse2fs: enable the shutdown ioctl Darrick J. Wong
2 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-09-16 0:05 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
From: Darrick J. Wong <djwong@kernel.org>
Track our own willingness to write to the filesystem in a separate
variable from that of libext2fs. There's a small window between opening
the filesystem and mounting it where the library is rw but we might fail
a mount task and therefore don't want to write anything more.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index dd731d84c4535f..80d1c79b5cce1c 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -218,6 +218,11 @@ struct fuse2fs_file_handle {
int check_flags;
};
+enum fuse2fs_opstate {
+ F2OP_READONLY,
+ F2OP_WRITABLE,
+};
+
/* Main program context */
#define FUSE2FS_MAGIC (0xEF53DEADUL)
struct fuse2fs {
@@ -243,6 +248,7 @@ struct fuse2fs {
int unmount_in_destroy;
int noblkdev;
+ enum fuse2fs_opstate opstate;
int logfd;
int blocklog;
unsigned int blockmask;
@@ -611,8 +617,6 @@ static int update_atime(ext2_filsys fs, ext2_ino_t ino)
struct timespec atime, mtime, now;
double datime, dmtime, dnow;
- if (!(fs->flags & EXT2_FLAG_RW))
- return 0;
err = fuse2fs_read_inode(fs, ino, &inode);
if (err)
return translate_error(fs, ino, err);
@@ -728,9 +732,8 @@ static int fs_can_allocate(struct fuse2fs *ff, blk64_t num)
static int fuse2fs_is_writeable(struct fuse2fs *ff)
{
- ext2_filsys fs = ff->fs;
-
- return (fs->flags & EXT2_FLAG_RW) && (fs->super->s_error_count == 0);
+ return ff->opstate == F2OP_WRITABLE &&
+ (ff->fs->super->s_error_count == 0);
}
static inline int is_superuser(struct fuse2fs *ff, struct fuse_context *ctxt)
@@ -946,6 +949,7 @@ static errcode_t fuse2fs_open(struct fuse2fs *ff, int libext2_flags)
}
snprintf(options, sizeof(options) - 1, "offset=%lu", ff->offset);
+ ff->opstate = F2OP_READONLY;
if (ff->directio)
flags |= EXT2_FLAG_DIRECT_IO;
@@ -1083,6 +1087,7 @@ static int fuse2fs_mount(struct fuse2fs *ff)
_("Warning: fuse2fs does not support using the journal.\n"
"There may be file system corruption or data loss if\n"
"the file system is not gracefully unmounted.\n"));
+ ff->opstate = F2OP_WRITABLE;
}
if (!(fs->super->s_state & EXT2_VALID_FS))
@@ -1105,7 +1110,7 @@ static int fuse2fs_mount(struct fuse2fs *ff)
ff->errors_behavior = fs->super->s_errors;
/* Clear the valid flag so that an unclean shutdown forces a fsck */
- if (fs->flags & EXT2_FLAG_RW) {
+ if (ff->opstate == F2OP_WRITABLE) {
fs->super->s_mnt_count++;
ext2fs_set_tstamp(fs->super, s_mtime, time(NULL));
fs->super->s_state &= ~EXT2_VALID_FS;
@@ -1129,7 +1134,7 @@ static void op_destroy(void *p EXT2FS_ATTR((unused)))
fs = fuse2fs_start(ff);
dbg_printf(ff, "%s: dev=%s\n", __func__, fs->device_name);
- if (fs->flags & EXT2_FLAG_RW) {
+ if (ff->opstate == F2OP_WRITABLE) {
fs->super->s_state |= EXT2_VALID_FS;
if (fs->super->s_error_count)
fs->super->s_state |= EXT2_ERROR_FS;
@@ -1337,7 +1342,7 @@ static void *op_init(struct fuse_conn_info *conn
log_printf(ff, "%s %s.\n", _("mounted filesystem"), uuid);
}
- if (ff->fs->flags & EXT2_FLAG_RW)
+ if (ff->opstate == F2OP_WRITABLE)
fuse2fs_read_bitmaps(ff);
return ff;
@@ -3427,7 +3432,7 @@ static int op_statfs(const char *path EXT2FS_ATTR((unused)),
fsid ^= *f;
buf->f_fsid = fsid;
buf->f_flag = 0;
- if (!(fs->flags & EXT2_FLAG_RW))
+ if (ff->opstate != F2OP_WRITABLE)
buf->f_flag |= ST_RDONLY;
buf->f_namemax = EXT2_NAME_LEN;
fuse2fs_finish(ff, 0);
@@ -5218,6 +5223,7 @@ int main(int argc, char *argv[])
memset(&fctx, 0, sizeof(fctx));
fctx.magic = FUSE2FS_MAGIC;
fctx.logfd = -1;
+ fctx.opstate = F2OP_WRITABLE;
ret = fuse_opt_parse(&args, &fctx, fuse2fs_opts, fuse2fs_opt_proc);
if (ret)
@@ -5600,9 +5606,11 @@ static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
_("Continuing after errors; is this a good idea?"));
break;
case EXT2_ERRORS_RO:
- if (fs->flags & EXT2_FLAG_RW)
+ if (ff->opstate == F2OP_WRITABLE) {
err_printf(ff, "%s\n",
_("Remounting read-only due to errors."));
+ ff->opstate = F2OP_READONLY;
+ }
fs->flags &= ~EXT2_FLAG_RW;
break;
case EXT2_ERRORS_PANIC:
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] fuse2fs: enable the shutdown ioctl
2025-09-15 23:59 [PATCHSET 6/6] fuse2fs: better tracking of writable state Darrick J. Wong
2025-09-16 0:05 ` [PATCH 1/3] fuse2fs: pass a struct fuse2fs to fs_writeable Darrick J. Wong
2025-09-16 0:05 ` [PATCH 2/3] fuse2fs: track our own writable state Darrick J. Wong
@ 2025-09-16 0:05 ` Darrick J. Wong
2025-10-08 22:04 ` Darrick J. Wong
2 siblings, 1 reply; 6+ messages in thread
From: Darrick J. Wong @ 2025-09-16 0:05 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
From: Darrick J. Wong <djwong@kernel.org>
Implement a bastardized version of EXT4_IOC_SHUTDOWN, because the people
who invented the ioctl got the direction wrong, so we can't actually
read the flags.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.c | 42 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 80d1c79b5cce1c..101f0fa03c397d 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -221,6 +221,7 @@ struct fuse2fs_file_handle {
enum fuse2fs_opstate {
F2OP_READONLY,
F2OP_WRITABLE,
+ F2OP_SHUTDOWN,
};
/* Main program context */
@@ -276,7 +277,7 @@ struct fuse2fs {
} \
} while (0)
-#define __FUSE2FS_CHECK_CONTEXT(ff, retcode) \
+#define __FUSE2FS_CHECK_CONTEXT(ff, retcode, shutcode) \
do { \
if ((ff) == NULL || (ff)->magic != FUSE2FS_MAGIC) { \
fprintf(stderr, \
@@ -285,14 +286,17 @@ struct fuse2fs {
fflush(stderr); \
retcode; \
} \
+ if ((ff)->opstate == F2OP_SHUTDOWN) { \
+ shutcode; \
+ } \
} while (0)
#define FUSE2FS_CHECK_CONTEXT(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN)
+ __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN, return -EIO)
#define FUSE2FS_CHECK_CONTEXT_RETURN(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), return)
+ __FUSE2FS_CHECK_CONTEXT((ff), return, return)
#define FUSE2FS_CHECK_CONTEXT_ABORT(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), abort())
+ __FUSE2FS_CHECK_CONTEXT((ff), abort(), abort())
static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
const char *func, int line);
@@ -4566,6 +4570,33 @@ static int ioctl_fitrim(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
}
#endif /* FITRIM */
+#ifndef EXT4_IOC_SHUTDOWN
+# define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32)
+#endif
+
+static int ioctl_shutdown(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
+ void *data)
+{
+ struct fuse_context *ctxt = fuse_get_context();
+ ext2_filsys fs = ff->fs;
+
+ if (!is_superuser(ff, ctxt))
+ return -EPERM;
+
+ err_printf(ff, "%s.\n", _("shut down requested"));
+
+ /*
+ * EXT4_IOC_SHUTDOWN inherited the inverted polarity on the ioctl
+ * direction from XFS. Unfortunately, that means we can't implement
+ * any of the flags. Flush whatever is dirty and shut down.
+ */
+ if (ff->opstate == F2OP_WRITABLE)
+ ext2fs_flush2(fs, 0);
+ ff->opstate = F2OP_SHUTDOWN;
+
+ return 0;
+}
+
#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
@@ -4612,6 +4643,9 @@ static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
ret = ioctl_fitrim(ff, fh, data);
break;
#endif
+ case EXT4_IOC_SHUTDOWN:
+ ret = ioctl_shutdown(ff, fh, data);
+ break;
default:
dbg_printf(ff, "%s: Unknown ioctl %d\n", __func__, cmd);
ret = -ENOTTY;
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] fuse2fs: enable the shutdown ioctl
2025-09-16 0:05 ` [PATCH 3/3] fuse2fs: enable the shutdown ioctl Darrick J. Wong
@ 2025-10-08 22:04 ` Darrick J. Wong
0 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-10-08 22:04 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
On Mon, Sep 15, 2025 at 05:05:35PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Implement a bastardized version of EXT4_IOC_SHUTDOWN, because the people
> who invented the ioctl got the direction wrong, so we can't actually
> read the flags.
>
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
> misc/fuse2fs.c | 42 ++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 38 insertions(+), 4 deletions(-)
>
>
> diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
> index 80d1c79b5cce1c..101f0fa03c397d 100644
> --- a/misc/fuse2fs.c
> +++ b/misc/fuse2fs.c
> @@ -221,6 +221,7 @@ struct fuse2fs_file_handle {
> enum fuse2fs_opstate {
> F2OP_READONLY,
> F2OP_WRITABLE,
> + F2OP_SHUTDOWN,
> };
>
> /* Main program context */
> @@ -276,7 +277,7 @@ struct fuse2fs {
> } \
> } while (0)
>
> -#define __FUSE2FS_CHECK_CONTEXT(ff, retcode) \
> +#define __FUSE2FS_CHECK_CONTEXT(ff, retcode, shutcode) \
> do { \
> if ((ff) == NULL || (ff)->magic != FUSE2FS_MAGIC) { \
> fprintf(stderr, \
> @@ -285,14 +286,17 @@ struct fuse2fs {
> fflush(stderr); \
> retcode; \
> } \
> + if ((ff)->opstate == F2OP_SHUTDOWN) { \
> + shutcode; \
> + } \
> } while (0)
>
> #define FUSE2FS_CHECK_CONTEXT(ff) \
> - __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN)
> + __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN, return -EIO)
> #define FUSE2FS_CHECK_CONTEXT_RETURN(ff) \
> - __FUSE2FS_CHECK_CONTEXT((ff), return)
> + __FUSE2FS_CHECK_CONTEXT((ff), return, return)
This change means that we return early from op_destroy on a shut down
filesystem, which means that on iomap filesystems we don't actually
uphold the requirement that we've closed the block device before
replying to the FUSE_DESTROY message that the kernel gives us during
unmount. This causes odd regressions on generic/730 and generic/635,
both of which are due to fstests not being able to format a new
filesystem because fuse4fs hasn't quite exited yet.
> #define FUSE2FS_CHECK_CONTEXT_ABORT(ff) \
> - __FUSE2FS_CHECK_CONTEXT((ff), abort())
> + __FUSE2FS_CHECK_CONTEXT((ff), abort(), abort())
>
> static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
> const char *func, int line);
> @@ -4566,6 +4570,33 @@ static int ioctl_fitrim(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
> }
> #endif /* FITRIM */
>
> +#ifndef EXT4_IOC_SHUTDOWN
> +# define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32)
> +#endif
> +
> +static int ioctl_shutdown(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
> + void *data)
> +{
> + struct fuse_context *ctxt = fuse_get_context();
> + ext2_filsys fs = ff->fs;
> +
> + if (!is_superuser(ff, ctxt))
> + return -EPERM;
> +
> + err_printf(ff, "%s.\n", _("shut down requested"));
> +
> + /*
> + * EXT4_IOC_SHUTDOWN inherited the inverted polarity on the ioctl
> + * direction from XFS. Unfortunately, that means we can't implement
> + * any of the flags. Flush whatever is dirty and shut down.
> + */
> + if (ff->opstate == F2OP_WRITABLE)
> + ext2fs_flush2(fs, 0);
> + ff->opstate = F2OP_SHUTDOWN;
This needs to clear EXT2_FLAG_RW or else ext2fs_close2() will try to
write the group descriptors/superblock even though the filesystem was
supposedly shut down.
--D
> +
> + return 0;
> +}
> +
> #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
> static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
> #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
> @@ -4612,6 +4643,9 @@ static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
> ret = ioctl_fitrim(ff, fh, data);
> break;
> #endif
> + case EXT4_IOC_SHUTDOWN:
> + ret = ioctl_shutdown(ff, fh, data);
> + break;
> default:
> dbg_printf(ff, "%s: Unknown ioctl %d\n", __func__, cmd);
> ret = -ENOTTY;
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] fuse2fs: enable the shutdown ioctl
2025-11-06 22:29 [PATCHSET 7/9] fuse2fs: better tracking of writable state Darrick J. Wong
@ 2025-11-06 22:41 ` Darrick J. Wong
0 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-11-06 22:41 UTC (permalink / raw)
To: tytso; +Cc: linux-ext4
From: Darrick J. Wong <djwong@kernel.org>
Implement a bastardized version of EXT4_IOC_SHUTDOWN, because the people
who invented the ioctl got the direction wrong, so we can't actually
read the flags. So all we do is flush the filesystem, clear the
writable flags, and hope that's what the user wanted.
Since we change __FUSE2FS_CHECK_CONTEXT to return an error code on a
shut down filesystem, remove FUSE2FS_CHECK_CONTEXT_RETURN so that
op_destroy always tears everything down and logs the unmount message.
Later on in the iomap patchset this will become critical because we need
to try to close the block device before unmount completes to avoid
problems with fstests.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.c | 45 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 41 insertions(+), 4 deletions(-)
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 8d5b705280b72f..51e6b3b1969d62 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -222,6 +222,7 @@ struct fuse2fs_file_handle {
enum fuse2fs_opstate {
F2OP_READONLY,
F2OP_WRITABLE,
+ F2OP_SHUTDOWN,
};
/* Main program context */
@@ -280,7 +281,7 @@ struct fuse2fs {
} \
} while (0)
-#define __FUSE2FS_CHECK_CONTEXT(ff, retcode) \
+#define __FUSE2FS_CHECK_CONTEXT(ff, retcode, shutcode) \
do { \
if ((ff) == NULL || (ff)->magic != FUSE2FS_MAGIC) { \
fprintf(stderr, \
@@ -289,14 +290,17 @@ struct fuse2fs {
fflush(stderr); \
retcode; \
} \
+ if ((ff)->opstate == F2OP_SHUTDOWN) { \
+ shutcode; \
+ } \
} while (0)
#define FUSE2FS_CHECK_CONTEXT(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN)
+ __FUSE2FS_CHECK_CONTEXT((ff), return -EUCLEAN, return -EIO)
#define FUSE2FS_CHECK_CONTEXT_DESTROY(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), return)
+ __FUSE2FS_CHECK_CONTEXT((ff), return, /* do not return */)
#define FUSE2FS_CHECK_CONTEXT_INIT(ff) \
- __FUSE2FS_CHECK_CONTEXT((ff), abort())
+ __FUSE2FS_CHECK_CONTEXT((ff), abort(), abort())
static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
const char *func, int line);
@@ -4863,6 +4867,36 @@ static int ioctl_fitrim(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
}
#endif /* FITRIM */
+#ifndef EXT4_IOC_SHUTDOWN
+# define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32)
+#endif
+
+static int ioctl_shutdown(struct fuse2fs *ff, struct fuse2fs_file_handle *fh,
+ void *data)
+{
+ struct fuse_context *ctxt = fuse_get_context();
+ ext2_filsys fs = ff->fs;
+
+ if (!is_superuser(ff, ctxt))
+ return -EPERM;
+
+ err_printf(ff, "%s.\n", _("shut down requested"));
+
+ fuse2fs_mmp_cancel(ff);
+
+ /*
+ * EXT4_IOC_SHUTDOWN inherited the inverted polarity on the ioctl
+ * direction from XFS. Unfortunately, that means we can't implement
+ * any of the flags. Flush whatever is dirty and shut down.
+ */
+ if (ff->opstate == F2OP_WRITABLE)
+ ext2fs_flush2(fs, 0);
+ ff->opstate = F2OP_SHUTDOWN;
+ fs->flags &= ~EXT2_FLAG_RW;
+
+ return 0;
+}
+
#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
@@ -4909,6 +4943,9 @@ static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
ret = ioctl_fitrim(ff, fh, data);
break;
#endif
+ case EXT4_IOC_SHUTDOWN:
+ ret = ioctl_shutdown(ff, fh, data);
+ break;
default:
dbg_printf(ff, "%s: Unknown ioctl %d\n", __func__, cmd);
ret = -ENOTTY;
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-11-06 22:42 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-15 23:59 [PATCHSET 6/6] fuse2fs: better tracking of writable state Darrick J. Wong
2025-09-16 0:05 ` [PATCH 1/3] fuse2fs: pass a struct fuse2fs to fs_writeable Darrick J. Wong
2025-09-16 0:05 ` [PATCH 2/3] fuse2fs: track our own writable state Darrick J. Wong
2025-09-16 0:05 ` [PATCH 3/3] fuse2fs: enable the shutdown ioctl Darrick J. Wong
2025-10-08 22:04 ` Darrick J. Wong
-- strict thread matches above, loose matches on Subject: below --
2025-11-06 22:29 [PATCHSET 7/9] fuse2fs: better tracking of writable state Darrick J. Wong
2025-11-06 22:41 ` [PATCH 3/3] fuse2fs: enable the shutdown ioctl Darrick J. Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox