Milos Nikic, le ven. 09 oct. 2026 22:36:05 -0700, a ecrit:
> trunc_indirect took END - OFFSET as the index of the first entry to
> free at every level of indirection. That is right for a single
> indirect block only; an entry of a double (triple) indirect block
> covers addr_per_block (addr_per_block^2) blocks. A truncation to a
> size inside those ranges thus started at the wrong entry or freed
> nothing, leaving the blocks past the new end allocated. A file that
> later grows over them gets their old contents back instead of zeros.
>
> Steps to reproduce (with or without the journal):
>
> sudo settrans -ac /mnt/stress /hurd/ext2fs.static /dev/sd0
>
> sudo dd if=/dev/urandom of=/mnt/stress/f bs=1M count=7
> sudo truncate -s 6000000 /mnt/stress/f
> sudo settrans -ga /mnt/stress && sudo /sbin/fsck.ext2 -fn /dev/sd0
> Inode 16996, i_size is 6000000, should be 7340032. Fix? no
Ah, thanks! I indeed had already seen such messages from times to times,
without gathering the time to investigate :)
Samuel
> ---
> ext2fs/truncate.c | 22 +++++++++++++---------
> 1 file changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c
> index 837fd02c3..082947d32 100644
> --- a/ext2fs/truncate.c
> +++ b/ext2fs/truncate.c
> @@ -111,13 +111,14 @@ trunc_direct (struct node *node, block_t end, struct
> free_block_run *fbr)
>
> /* Free any blocks in NODE greater than or equal to END that are rooted in
> the indirect block *P; OFFSET should be the block position that *P
> - corresponds to. For each block pointer in *P that should be freed,
> - FREE_BLOCK is called with a pointer to the entry for that block, and the
> - index of the entry within *P. If every block in *P is freed, then *P is
> - set to 0, otherwise it is left alone. */
> + corresponds to, and SPAN the number of file blocks each entry of *P
> + covers (1 for a single indirect block). For each block pointer in *P
> + that should be freed, FREE_BLOCK is called with a pointer to the entry
> + for that block, and the index of the entry within *P. If every block in
> + *P is freed, then *P is set to 0, otherwise it is left alone. */
> static void
> trunc_indirect (struct node *node, block_t end,
> - block_t *p, block_t offset,
> + block_t *p, block_t offset, block_t span,
> void (*free_block)(block_t *p, unsigned index),
> struct free_block_run *fbr)
> {
> @@ -128,7 +129,9 @@ trunc_indirect (struct node *node, block_t end,
> unsigned index;
> int modified = 0, all_freed = 1;
> block_t *ind_bh = (block_t *) disk_cache_block_ref (*p);
> - unsigned first = end < offset ? 0 : end - offset;
> + /* The entry that holds END: it is truncated in part, and the ones
> + after it are freed. */
> + unsigned first = end < offset ? 0 : (end - offset) / span;
>
> for (index = first; index < addr_per_block; index++)
> if (ind_bh[index])
> @@ -177,7 +180,7 @@ trunc_single_indirect (struct node *node, block_t end,
> {
> free_block_run_free_ptr (fbr, p);
> }
> - trunc_indirect (node, end, p, offset, free_block, fbr);
> + trunc_indirect (node, end, p, offset, 1, free_block, fbr);
> }
>
> static void
> @@ -190,7 +193,7 @@ trunc_double_indirect (struct node *node, block_t end,
> block_t entry_offs = offset + (index * addr_per_block);
> trunc_single_indirect (node, end, p, entry_offs, fbr);
> }
> - trunc_indirect (node, end, p, offset, free_block, fbr);
> + trunc_indirect (node, end, p, offset, addr_per_block, free_block, fbr);
> }
>
> static void
> @@ -203,7 +206,8 @@ trunc_triple_indirect (struct node *node, block_t end,
> block_t entry_offs = offset + (index * addr_per_block *
> addr_per_block);
> trunc_double_indirect (node, end, p, entry_offs, fbr);
> }
> - trunc_indirect (node, end, p, offset, free_block, fbr);
> + trunc_indirect (node, end, p, offset, addr_per_block * addr_per_block,
> + free_block, fbr);
> }
>
> /* ---------------------------------------------------------------- */
> --
> 2.56.0
>
--
Samuel
<c> tiens, je suis déçu
<c> quand on clique sur le bouton random de http://xkcd.com/221/ on ne tombe
pas (toujours) sur http://xkcd.com/4/
<c> bon, j'envoie un bug-report à l'auteur