linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] fuse: fix fuseblk i_blkbits for iomap partial writes
@ 2025-08-12  1:46 Joanne Koong
  2025-08-12 22:47 ` Joanne Koong
  0 siblings, 1 reply; 2+ messages in thread
From: Joanne Koong @ 2025-08-12  1:46 UTC (permalink / raw)
  To: brauner; +Cc: miklos, djwong, linux-fsdevel, kernel-team

On regular fuse filesystems, i_blkbits is set to PAGE_SHIFT which means
any iomap partial writes will mark the entire folio as uptodate. However
fuseblk filesystems work differently and allow the blocksize to be less
than the page size. As such, this may lead to data corruption if fuseblk
sets its blocksize to less than the page size, uses the writeback cache,
and does a partial write, then a read and the read happens before the
write has undergone writeback, since the folio will not be marked
uptodate from the partial write so the read will read in the entire
folio from disk, which will overwrite the partial write.

The long-term solution for this, which will also be needed for fuse to
enable large folios with the writeback cache on, is to have fuse also
use iomap for folio reads, but until that is done, the cleanest
workaround is to use the page size for fuseblk's internal kernel
blksize/blkbits values while maintaining current behavior for stat().

This was verified using ntfs-3g:
$ sudo mkfs.ntfs -f -c 512 /dev/vdd1
$ sudo ntfs-3g /dev/vdd1 ~/fuseblk
$ stat ~/fuseblk/hi.txt
IO Block: 512

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Fixes: a4c9ab1d4975 ("fuse: use iomap for buffered writes")
---
 fs/fuse/dir.c    | 2 +-
 fs/fuse/fuse_i.h | 3 +++
 fs/fuse/inode.c  | 9 +++++++++
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 2d817d7cab26..18900fa6d5da 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1199,7 +1199,7 @@ static void fuse_fillattr(struct mnt_idmap *idmap, struct inode *inode,
 	if (attr->blksize != 0)
 		blkbits = ilog2(attr->blksize);
 	else
-		blkbits = inode->i_sb->s_blocksize_bits;
+		blkbits = fc->inode_blkbits;
 
 	stat->blksize = 1 << blkbits;
 }
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index ec248d13c8bf..3be86056f4ff 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -969,6 +969,9 @@ struct fuse_conn {
 		/* Request timeout (in jiffies). 0 = no timeout */
 		unsigned int req_timeout;
 	} timeout;
+
+	/** This is a workaround until fuse uses iomap for reads */
+	unsigned inode_blkbits;
 };
 
 /*
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 67c2318bfc42..681167117edf 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1805,10 +1805,19 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 		err = -EINVAL;
 		if (!sb_set_blocksize(sb, ctx->blksize))
 			goto err;
+		/*
+		 * This is a workaround until fuse hooks into iomap for reads.
+		 * Else if ctx->blksize < PAGE_SIZE and the writeback cache is
+		 * enabled, a read may overwrite partially written data.
+		 */
+		fc->inode_blkbits = sb->s_blocksize_bits;
+		if (!sb_set_blocksize(sb, PAGE_SIZE))
+			goto err;
 #endif
 	} else {
 		sb->s_blocksize = PAGE_SIZE;
 		sb->s_blocksize_bits = PAGE_SHIFT;
+		fc->inode_blkbits = sb->s_blocksize_bits;
 	}
 
 	sb->s_subtype = ctx->subtype;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] fuse: fix fuseblk i_blkbits for iomap partial writes
  2025-08-12  1:46 [PATCH] fuse: fix fuseblk i_blkbits for iomap partial writes Joanne Koong
@ 2025-08-12 22:47 ` Joanne Koong
  0 siblings, 0 replies; 2+ messages in thread
From: Joanne Koong @ 2025-08-12 22:47 UTC (permalink / raw)
  To: brauner; +Cc: miklos, djwong, linux-fsdevel, kernel-team

On Mon, Aug 11, 2025 at 6:49 PM Joanne Koong <joannelkoong@gmail.com> wrote:
>
> On regular fuse filesystems, i_blkbits is set to PAGE_SHIFT which means
> any iomap partial writes will mark the entire folio as uptodate. However
> fuseblk filesystems work differently and allow the blocksize to be less
> than the page size. As such, this may lead to data corruption if fuseblk
> sets its blocksize to less than the page size, uses the writeback cache,
> and does a partial write, then a read and the read happens before the
> write has undergone writeback, since the folio will not be marked
> uptodate from the partial write so the read will read in the entire
> folio from disk, which will overwrite the partial write.
>
> The long-term solution for this, which will also be needed for fuse to
> enable large folios with the writeback cache on, is to have fuse also
> use iomap for folio reads, but until that is done, the cleanest
> workaround is to use the page size for fuseblk's internal kernel
> blksize/blkbits values while maintaining current behavior for stat().
>
> This was verified using ntfs-3g:
> $ sudo mkfs.ntfs -f -c 512 /dev/vdd1
> $ sudo ntfs-3g /dev/vdd1 ~/fuseblk
> $ stat ~/fuseblk/hi.txt
> IO Block: 512
>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> Fixes: a4c9ab1d4975 ("fuse: use iomap for buffered writes")

Please ignore this version of the patch. This is superseded by the
newer version here:
https://lore.kernel.org/linux-fsdevel/20250812214614.2674485-1-joannelkoong@gmail.com/T/#t

Thanks,
Joanne

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-08-12 22:47 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-12  1:46 [PATCH] fuse: fix fuseblk i_blkbits for iomap partial writes Joanne Koong
2025-08-12 22:47 ` Joanne Koong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).