git: 7a1c1f6a0332 - main - Avoid unaligned writes by fsck_ffs(8).

From: Kirk McKusick <mckusick_at_FreeBSD.org>
Date: Sun, 20 Feb 2022 21:21:32 UTC
The branch main has been updated by mckusick:

URL: https://cgit.FreeBSD.org/src/commit/?id=7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff

commit 7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff
Author:     Kirk McKusick <mckusick@FreeBSD.org>
AuthorDate: 2022-02-20 21:18:05 +0000
Commit:     Kirk McKusick <mckusick@FreeBSD.org>
CommitDate: 2022-02-20 21:21:12 +0000

    Avoid unaligned writes by fsck_ffs(8).
    
    Normally fsck_ffs never does reads or writes that are not aligned
    to the size of one of the checked filesystems fragments. The one
    exception is when it finds that it needs to write the superblock
    recovery information. Here it will write with the alignment reported
    by the underlying disk as its sector size as reported by an
    ioctl(diskfd, DIOCGSECTORSIZE, &secsize).
    
    Modern disks have a sector size of 4096, but for backward compatibility
    with older disks will report that they have a sector size of 512.
    When presented with a 512 byte write, they have to read the associated
    4096 byte sector, replace the 512 bytes to be written, and write
    the updated 4096 byte sector back to the disk. Unfortunately, some
    disks report that they have 512 sectors, but fail writes that are not
    aligned to 4096 boundaries and are a multiple of 4096 bytes in size.
    
    This commit updates fsck_ffs(8) so that it uses the filesystem fragment
    size as the smallest size and alignment for doing writes rather than
    the disk's reported sector size.
    
    Reported by:  Andriy Gapon
    MFC after:    1 week
---
 sbin/fsck_ffs/setup.c | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index 506a027f40ac..bbb8a854e999 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -399,17 +399,19 @@ chkrecovery(int devfd)
 {
 	struct fsrecovery *fsr;
 	char *fsrbuf;
-	u_int secsize;
+	u_int secsize, rdsize;
 
 	/*
 	 * Could not determine if backup material exists, so do not
 	 * offer to create it.
 	 */
 	fsrbuf = NULL;
+	rdsize = sblock.fs_fsize;
 	if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 ||
-	    (fsrbuf = Malloc(secsize)) == NULL ||
-	    blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
-	      secsize) != 0) {
+	    rdsize % secsize != 0 ||
+	    (fsrbuf = Malloc(rdsize)) == NULL ||
+	    blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
+	      rdsize) != 0) {
 		free(fsrbuf);
 		return (1);
 	}
@@ -417,7 +419,7 @@ chkrecovery(int devfd)
 	 * Recovery material has already been created, so do not
 	 * need to create it again.
 	 */
-	fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
+	fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
 	if (fsr->fsr_magic == FS_UFS2_MAGIC) {
 		free(fsrbuf);
 		return (1);
@@ -430,8 +432,8 @@ chkrecovery(int devfd)
 }
 
 /*
- * Read the last sector of the boot block, replace the last
- * 20 bytes with the recovery information, then write it back.
+ * Read the last filesystem-size piece of the boot block, replace the
+ * last 20 bytes with the recovery information, then write it back.
  * The recovery information only works for UFS2 filesystems.
  */
 static void
@@ -439,24 +441,26 @@ saverecovery(int readfd, int writefd)
 {
 	struct fsrecovery *fsr;
 	char *fsrbuf;
-	u_int secsize;
+	u_int secsize, rdsize;
 
 	fsrbuf = NULL;
+	rdsize = sblock.fs_fsize;
 	if (sblock.fs_magic != FS_UFS2_MAGIC ||
 	    ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 ||
-	    (fsrbuf = Malloc(secsize)) == NULL ||
-	    blread(readfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
-	      secsize) != 0) {
+	    rdsize % secsize != 0 ||
+	    (fsrbuf = Malloc(rdsize)) == NULL ||
+	    blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
+	      rdsize) != 0) {
 		printf("RECOVERY DATA COULD NOT BE CREATED\n");
 		free(fsrbuf);
 		return;
 	}
-	fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
+	fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
 	fsr->fsr_magic = sblock.fs_magic;
 	fsr->fsr_fpg = sblock.fs_fpg;
 	fsr->fsr_fsbtodb = sblock.fs_fsbtodb;
 	fsr->fsr_sblkno = sblock.fs_sblkno;
 	fsr->fsr_ncg = sblock.fs_ncg;
-	blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - secsize) / secsize, secsize);
+	blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize);
 	free(fsrbuf);
 }