DF-3083 / fix.diff
--- a/sys/vfs/ext2fs/ext2_lookup.c +++ b/sys/vfs/ext2fs/ext2_lookup.c @@ -774,13 +774,11 @@ struct mount *mp; mp = ITOV(ip)->v_mount; - if ((mp->mnt_flag & MNT_RDONLY) == 0) - panic("ext2_dirbad: %s: bad dir ino %ju at offset %ld: %s\n", - mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number, - (long)offset, how); - else - SDT_PROBE4(ext2fs, , trace, ext2_dirbad_error, - mp->mnt_stat.f_mntonname, ip->i_number, offset, how); + printf("ext2_dirbad: %s: bad dir ino %ju at offset %ld: %s\n", + mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number, + (long)offset, how); + SDT_PROBE4(ext2fs, , trace, ext2_dirbad_error, + mp->mnt_stat.f_mntonname, ip->i_number, offset, how); } /* @@ -948,8 +946,16 @@ if (dp->i_count == 0) return ext2_add_first_entry(dvp, &newdir, cnp); + /* + * Never compact-truncate an indexed directory: when the free slot + * was found by ext2_htree_lookup(), i_endoff only covers the leaf + * blocks on the hash path, not the whole directory, so truncating + * to it deletes every later directory block still referenced by + * the index (silent mass data loss + later ext2_dirbad panics). + */ error = ext2_add_entry(dvp, &newdir); - if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) + if (!error && !ext2_htree_has_idx(dp) && + dp->i_endoff && dp->i_endoff < dp->i_size) error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC, cnp->cn_cred); return (error); |