### Root Cause Analysis
#### 1. Functions, Structs, and Files Involved
- **`fs/ext4/inode.c`**:
- `mpage_map_one_extent()`: Maps delayed/unwritten extents during writeback. Hits `WARN_ON_ONCE(!err)` at line 2512 when `ext4_map_blocks()` returns `0`.
- `mpage_add_bh_to_extent()`: Inspects page buffer heads and queues logical blocks into `mpd->map` when `buffer_dirty(bh) && buffer_mapped(bh) && (buffer_delay(bh) || buffer_unwritten(bh))`.
- `ext4_map_blocks()`: Maps/allocates blocks. When invoked with `EXT4_GET_BLOCKS_IO_SUBMIT`, it explicitly avoids creating blocks if the extent status tree or disk query encounters a hole (`retval == 0` and `!map->m_flags`), returning `0`.
- `ext4_block_zero_eof()` / `ext4_block_do_zero_range()` / `ext4_load_tail_bh()`: Executed during truncation (`ext4_truncate()`) to zero the tail of the block straddling `i_size`.
#### 2. Trigger and Invariant Violation
During delayed allocation writeback (`ext4_do_writepages()` via `wb_workfn`):
1. `mpage_process_page_bufs()` iterates over page buffers within file limits (`lblk < blocks`) and invokes `mpage_add_bh_to_extent()`.
2. An invariant assumed by the writeback code is that any buffer head added to `mpd->map` (due to possessing `BH_Dirty`, `BH_Mapped`, and either `BH_Delay` or `BH_Unwritten`) corresponds to a valid delayed or unwritten extent in the extent tree or disk inode. Consequently, `ext4_map_blocks()` called with `EXT4_GET_BLOCKS_CREATE | EXT4_GET_BLOCKS_IO_SUBMIT` is expected to return `> 0` (the number of blocks mapped/allocated) or `< 0` (an error like `-ENOSPC`).
3. In the reproducer sequence (`open(O_DIRECT)` -> `sendfile` -> `open` -> `ftruncate(7)`), a state discrepancy occurs between the page cache buffer head state and the extent status tree / on-disk block state. Specifically, the buffer head for block 0 is marked dirty, mapped, and delayed/unwritten (or re-dirtied during EOF zeroing by `ext4_block_zero_eof()` -> `ext4_block_do_zero_range()` -> `mark_buffer_dirty(bh)`), while the extent status tree or on-disk lookup reports that the block is a hole (`!map->m_flags` and `retval == 0`).
4. In `ext4_map_blocks()`:
```c
} else if (retval == 0) {
/*
* Do not allocate blocks for holes in the context of
* data submission path.
*/
if (!map->m_flags && (flags & EXT4_GET_BLOCKS_IO_SUBMIT))
goto out;
}
```
Because `flags` includes `EXT4_GET_BLOCKS_IO_SUBMIT`, `ext4_map_blocks()` returns `0`.
5. `mpage_map_one_extent()` treats `0` as an unexpected condition and triggers:
```c
if (WARN_ON_ONCE(!err))
return -EINVAL;
```
#### 3. Runtime Consequences in Production
- **Memory Corruption (`false`)**: No invalid pointers, use-after-free, double-free, or out-of-bounds writes occur.
- **Information Leak (`none`)**: No kernel data or pointers are exposed to userspace or the network.
- **Denial of Service (`false`)**: In production kernels without `CONFIG_PANIC_ON_WARN`, `WARN_ON_ONCE(!err)` logs a warning and returns `-EINVAL`. In `mpage_map_and_submit_extent()`, the error is caught, the failure is logged (`Delayed block allocation failed for inode...`), and dirty pages are invalidated via `invalidate_dirty_pages`. Normal error recovery completes without a hang, deadlock, resource leak, or system crash.
- **Virtualization / Network Candidates (`false`)**: The defect is purely in the ext4 filesystem writeback / page cache subsystem.