diff options
author | Kirk McKusick <mckusick@FreeBSD.org> | 2022-02-20 21:18:05 +0000 |
---|---|---|
committer | Kirk McKusick <mckusick@FreeBSD.org> | 2022-02-20 21:21:12 +0000 |
commit | 7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff (patch) | |
tree | e21108b9531234dd7689c46d21a3dbad467b3abc /sbin | |
parent | a52d8d4a6c62f65ccec5734e0b0e779ae47b37ed (diff) | |
download | src-7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff.tar.gz src-7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff.zip |
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
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/fsck_ffs/setup.c | 30 |
1 files 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); } |