DragonFlyBSD Kernel Audit
DF-3083 / fix.diff
← back to finding ↓ download raw
--- 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);