[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <4fafc9c42962e1fdc8ce44b21fa977df5de0679e.1742800203.git.ojaswin@linux.ibm.com>
Date: Mon, 24 Mar 2025 13:07:09 +0530
From: Ojaswin Mujoo <ojaswin@...ux.ibm.com>
To: linux-ext4@...r.kernel.org, "Theodore Ts'o" <tytso@....edu>
Cc: John Garry <john.g.garry@...cle.com>, dchinner@...hat.com,
"Darrick J . Wong" <djwong@...nel.org>,
Ritesh Harjani <ritesh.list@...il.com>, linux-kernel@...r.kernel.org
Subject: [RFC v3 11/11] ext4: disallow unaligned deallocations on forcealign inodes
When forcealign is set, an unaligned deallocation can disturb the
alignment of the mappings of the file by introducing unaligned holes/unwritten
extents to it. This could then lead to increased allocations failures on the
file in future.
To avoid this, disallow deallocations or extent shifting operations (like insert
range) unless they are aligned to extsize as well.
Note that this is a relatively strict approach which can be relaxed a bit more
in the future by using partial zeroing tricks etc.
Signed-off-by: Ojaswin Mujoo <ojaswin@...ux.ibm.com>
---
fs/ext4/extents.c | 36 ++++++++++++++++++++++++++++++++++++
fs/ext4/inode.c | 12 ++++++++++++
2 files changed, 48 insertions(+)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 1835e18f0eef..1ac5bb8cbbde 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4754,6 +4754,18 @@ static long ext4_zero_range(struct file *file, loff_t offset,
return ret;
}
+ if (ext4_should_use_forcealign(inode)) {
+ u32 extsize_bytes = ext4_inode_get_extsize(EXT4_I(inode))
+ << inode->i_blkbits;
+
+ if (!IS_ALIGNED(offset | end, extsize_bytes)) {
+ ext4_warning(
+ inode->i_sb,
+ "tried unaligned operation on forcealign inode");
+ return -EINVAL;
+ }
+ }
+
flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
/* Preallocate the range including the unaligned edges */
if (!IS_ALIGNED(offset | end, blocksize)) {
@@ -5473,6 +5485,18 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len)
if (end >= inode->i_size)
return -EINVAL;
+ if (ext4_should_use_forcealign(inode)) {
+ u32 extsize_bytes = ext4_inode_get_extsize(EXT4_I(inode))
+ << inode->i_blkbits;
+
+ if (!IS_ALIGNED(offset | end, extsize_bytes)) {
+ ext4_warning(
+ inode->i_sb,
+ "tried unaligned operation on forcealign inode");
+ return -EINVAL;
+ }
+ }
+
/*
* Write tail of the last page before removed range and data that
* will be shifted since they will get removed from the page cache
@@ -5573,6 +5597,18 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
if (len > inode->i_sb->s_maxbytes - inode->i_size)
return -EFBIG;
+ if (ext4_should_use_forcealign(inode)) {
+ u32 extsize_bytes = ext4_inode_get_extsize(EXT4_I(inode))
+ << inode->i_blkbits;
+
+ if (!IS_ALIGNED(offset | (offset + len), extsize_bytes)) {
+ ext4_warning(
+ sb,
+ "tried unaligned operation on forcealign inode");
+ return -EINVAL;
+ }
+ }
+
/*
* Write out all dirty pages. Need to round down to align start offset
* to page size boundary for page size > block size.
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 5b36e62872d6..4c974e461061 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4397,6 +4397,18 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
end = max_end;
length = end - offset;
+ if (ext4_should_use_forcealign(inode)) {
+ u32 extsize_bytes = ext4_inode_get_extsize(EXT4_I(inode))
+ << inode->i_blkbits;
+
+ if (!IS_ALIGNED(offset | end, extsize_bytes)) {
+ ext4_warning(
+ sb,
+ "tried unaligned operation on forcealign inode");
+ return -EINVAL;
+ }
+ }
+
/*
* Attach jinode to inode for jbd2 if we do any zeroing of partial
* block.
--
2.48.1
Powered by blists - more mailing lists