Skip to content

Commit 5f63ac8

Browse files
committed
hfsplus: fix issue of direct writes beyond end-of-file
The xfstests' test-case generic/729 fails with error: sudo ./check generic/729 FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #36 SMP PREEMPT_DYNAMIC Fri Apr 17 12:40:51 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/729 23s ... [failed, exit status 1]- output mismatch mmap-rw-fault: /mnt/test/mmap-rw-fault.tmp: Input/output error The hfsplus_get_block() only allows creating the next sequential block. It returns -EIO for direct writes beyond EOF. This patch waits for any in-flight DIO on the inode to finish. Then, it extends the file by calling generic_cont_expand_simple() with the goal to guarantee that blockdev_direct_IO() finds all needed blocks already reachable sequentially. And, finally, it flushes and invalidates the DIO range again so the page cache is clean before the direct write begins. sudo ./check generic/729 FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #40 SMP PREEMPT_DYNAMIC Thu Apr 16 15:41:03 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/729 23s ... 32s Ran: generic/729 Passed all 1 tests Closes: hfs-linux-kernel#210 cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> cc: Yangtao Li <frank.li@vivo.com> cc: linux-fsdevel@vger.kernel.org Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Link: https://lore.kernel.org/r/20260417214940.2735557-2-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
1 parent ca724ed commit 5f63ac8

1 file changed

Lines changed: 36 additions & 2 deletions

File tree

fs/hfsplus/inode.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,52 @@ static ssize_t hfsplus_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
125125
struct file *file = iocb->ki_filp;
126126
struct address_space *mapping = file->f_mapping;
127127
struct inode *inode = mapping->host;
128+
loff_t isize;
128129
size_t count = iov_iter_count(iter);
130+
loff_t end = iocb->ki_pos + count;
129131
ssize_t ret;
130132

133+
/*
134+
* The hfsplus_get_block() only allows creating the next sequential block.
135+
* For direct writes beyond EOF, expand the file first.
136+
*/
137+
if (iov_iter_rw(iter) == WRITE && iocb->ki_pos > i_size_read(inode)) {
138+
loff_t start_off, end_off;
139+
loff_t start_page, end_page;
140+
141+
isize = i_size_read(inode);
142+
143+
/*
144+
* Wait for any in-flight DIO on this inode to finish before
145+
* calling generic_cont_expand_simple().
146+
*/
147+
inode_dio_wait(inode);
148+
149+
ret = generic_cont_expand_simple(inode, iocb->ki_pos);
150+
if (ret)
151+
return ret;
152+
153+
start_off = isize;
154+
end_off = (end > 0) ? end - 1 : end;
155+
156+
ret = filemap_write_and_wait_range(mapping, start_off, end_off);
157+
if (ret)
158+
return ret;
159+
160+
start_page = start_off >> PAGE_SHIFT;
161+
end_page = end_off >> PAGE_SHIFT;
162+
163+
invalidate_inode_pages2_range(mapping, start_page, end_page);
164+
}
165+
131166
ret = blockdev_direct_IO(iocb, inode, iter, hfsplus_get_block);
132167

133168
/*
134169
* In case of error extending write may have instantiated a few
135170
* blocks outside i_size. Trim these off again.
136171
*/
137172
if (unlikely(iov_iter_rw(iter) == WRITE && ret < 0)) {
138-
loff_t isize = i_size_read(inode);
139-
loff_t end = iocb->ki_pos + count;
173+
isize = i_size_read(inode);
140174

141175
if (end > isize)
142176
hfsplus_write_failed(mapping, end);

0 commit comments

Comments
 (0)