DF-3152 / fix.diff
--- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -119,6 +119,7 @@ int ronly; int setmp = 0; int i; + struct partinfo dpart; mp = hmp->mp; ronly = ((mp->mnt_flag & MNT_RDONLY) ? 1 : 0); @@ -174,6 +175,31 @@ setmp = 1; /* + * When formatting a new volume (hammer volume-add) the caller + * supplies the intended volume header. vol_buf_end derives from + * the user-supplied vol_size and must not exceed the real size of + * the device, otherwise freemap formatting walks past the device + * end (panic on INVARIANTS kernels, half-formatted volume plus a + * swallowed error on production kernels). HAMMER2 already does + * the equivalent check in its own ondisk.c. + */ + if (data) { + if (VOP_IOCTL(volume->devvp, DIOCGPART, (void *)&dpart, + 0, FSCRED, NULL) == 0 && + ((hammer_volume_ondisk_t)data)->vol_buf_end > + (int64_t)dpart.media_size) { + hkprintf("volume %s: vol_buf_end %jd exceeds device " + "size %ju\n", + volname, + (intmax_t)((hammer_volume_ondisk_t)data)-> + vol_buf_end, + (uintmax_t)dpart.media_size); + error = EFTYPE; + goto late_failure; + } + } + + /* * Extract the volume number from the volume header and do various * sanity checks. */ --- a/sys/vfs/hammer/hammer_volume.c +++ b/sys/vfs/hammer/hammer_volume.c @@ -115,12 +115,17 @@ KKASSERT(volume != NULL && error == 0); error = hammer_format_freemap(trans, volume); - KKASSERT(error == 0); + if (error) + goto add_failed; error = hammer_count_bigblocks(hmp, volume, &total_bigblocks, &empty_bigblocks); - KKASSERT(error == 0); - KKASSERT(total_bigblocks == empty_bigblocks); + if (error) + goto add_failed; + if (total_bigblocks != empty_bigblocks) { + error = EFTYPE; + goto add_failed; + } hammer_rel_volume(volume, 0); @@ -138,6 +143,20 @@ if (error) hmkprintf(hmp, "An error occurred: %d\n", error); return (error); + +add_failed: + /* + * Freemap formatting or counting failed (device I/O error, or a + * claimed vol_size larger than the real device). Uninstall the + * volume we just installed so the filesystem is not left with a + * half-formatted member, and return the error. Never report + * success on this path. + */ + hammer_rel_volume(volume, 0); + hammer_unload_volume(volume, NULL); + hammer_unlock(&hmp->blkmap_lock); + hammer_sync_unlock(trans); + goto end; } |