aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorKirk McKusick <mckusick@FreeBSD.org>2022-02-20 21:18:05 +0000
committerKirk McKusick <mckusick@FreeBSD.org>2022-02-20 21:21:12 +0000
commit7a1c1f6a0332c5b60349a5df0e3ce64e5005b2ff (patch)
treee21108b9531234dd7689c46d21a3dbad467b3abc /sbin
parenta52d8d4a6c62f65ccec5734e0b0e779ae47b37ed (diff)
downloadsrc-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.c30
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);
}