Get consistent updates for UFS superblocks.

Superblocks are written by Freebsd clients using the ffs_sbput()
routine (exported from the kernel in sys/ufs/ffs/ffs_subr.c). These
clients include the kernel, some geom modules, and system utilities
using libufs. Currently backward compatible updates of UFS superblocks
are done only when the kernel writes them. These updates should be
done whenever any call to ffs_sbput() is done. This commit moves
these UFS superblock compatibility updates from the kernel-specific
superblock update to ffs_sbput() so that all clients make these
compatibility updates when they write superblocks.

Reviewed-by:  kib
Tested-by:    Peter Holm
MFC-after:    1 week
Differential  Revision: https://reviews.freebsd.org/D49276
Sponsored-by: Netflix
This commit is contained in:
Kirk McKusick 2025-03-19 16:32:39 -07:00
parent fe73eb441f
commit c2cd605e8c
2 changed files with 59 additions and 45 deletions

View file

@ -384,16 +384,35 @@ void
ffs_oldfscompat_write(struct fs *fs)
{
/*
* Copy back UFS2 updated fields that UFS1 inspects.
*/
if (fs->fs_magic == FS_UFS1_MAGIC) {
switch (fs->fs_magic) {
case FS_UFS1_MAGIC:
if (fs->fs_sblockloc != SBLOCK_UFS1 &&
(fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
printf(
"WARNING: %s: correcting fs_sblockloc from %jd to %d\n",
fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS1);
fs->fs_sblockloc = SBLOCK_UFS1;
}
/*
* Copy back UFS2 updated fields that UFS1 inspects.
*/
fs->fs_old_time = fs->fs_time;
fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir;
fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree;
fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree;
fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree;
fs->fs_maxfilesize = fs->fs_save_maxfilesize;
if (fs->fs_save_maxfilesize != 0)
fs->fs_maxfilesize = fs->fs_save_maxfilesize;
break;
case FS_UFS2_MAGIC:
if (fs->fs_sblockloc != SBLOCK_UFS2 &&
(fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
printf(
"WARNING: %s: correcting fs_sblockloc from %jd to %d\n",
fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS2);
fs->fs_sblockloc = SBLOCK_UFS2;
}
break;
}
}
@ -931,6 +950,7 @@ int
ffs_sbput(void *devfd, struct fs *fs, off_t loc,
int (*writefunc)(void *devfd, off_t loc, void *buf, int size))
{
struct fs_summary_info *fs_si;
int i, error, blks, size;
uint8_t *space;
@ -953,23 +973,27 @@ ffs_sbput(void *devfd, struct fs *fs, off_t loc,
}
}
fs->fs_fmod = 0;
#ifndef _KERNEL
{
struct fs_summary_info *fs_si;
fs->fs_time = time(NULL);
/* Clear the pointers for the duration of writing. */
fs_si = fs->fs_si;
fs->fs_si = NULL;
fs->fs_ckhash = ffs_calc_sbhash(fs);
error = (*writefunc)(devfd, loc, fs, fs->fs_sbsize);
fs->fs_si = fs_si;
}
#else /* _KERNEL */
ffs_oldfscompat_write(fs);
#ifdef _KERNEL
fs->fs_time = time_second;
#else /* User Code */
fs->fs_time = time(NULL);
#endif
/* Clear the pointers for the duration of writing. */
fs_si = fs->fs_si;
fs->fs_si = NULL;
fs->fs_ckhash = ffs_calc_sbhash(fs);
error = (*writefunc)(devfd, loc, fs, fs->fs_sbsize);
#endif /* _KERNEL */
/*
* A negative error code is returned when a copy of the
* superblock has been made which is discarded when the I/O
* is done. So the fs_si field does not and indeed cannot be
* restored after the write is done. Convert the error code
* back to its usual positive value when returning it.
*/
if (error < 0)
return (-error - 1);
fs->fs_si = fs_si;
return (error);
}

View file

@ -2032,9 +2032,15 @@ ffs_sbupdate(struct ufsmount *ump, int waitfor, int suspended)
panic("ffs_sbupdate: write read-only filesystem");
/*
* We use the superblock's buf to serialize calls to ffs_sbupdate().
* Copy superblock to this buffer and have it written out.
*/
sbbp = getblk(ump->um_devvp, btodb(fs->fs_sblockloc),
(int)fs->fs_sbsize, 0, 0, 0);
UFS_LOCK(ump);
fs->fs_fmod = 0;
bcopy((caddr_t)fs, sbbp->b_data, (uint64_t)fs->fs_sbsize);
UFS_UNLOCK(ump);
fs = (struct fs *)sbbp->b_data;
/*
* Initialize info needed for write function.
*/
@ -2060,7 +2066,8 @@ ffs_use_bwrite(void *devfd, off_t loc, void *buf, int size)
devfdp = devfd;
ump = devfdp->ump;
fs = ump->um_fs;
bp = devfdp->sbbp;
fs = (struct fs *)bp->b_data;
/*
* Writing the superblock summary information.
*/
@ -2077,44 +2084,27 @@ ffs_use_bwrite(void *devfd, off_t loc, void *buf, int size)
}
/*
* Writing the superblock itself. We need to do special checks for it.
* A negative error code is returned to indicate that a copy of the
* superblock has been made and that the copy is discarded when the
* I/O is done. So the the caller should not attempt to restore the
* fs_si field after the write is done. The caller will convert the
* error code back to its usual positive value when returning it.
*/
bp = devfdp->sbbp;
if (ffs_fsfail_cleanup(ump, devfdp->error))
devfdp->error = 0;
if (devfdp->error != 0) {
brelse(bp);
return (devfdp->error);
}
if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_sblockloc != SBLOCK_UFS1 &&
(fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
printf("WARNING: %s: correcting fs_sblockloc from %jd to %d\n",
fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS1);
fs->fs_sblockloc = SBLOCK_UFS1;
}
if (fs->fs_magic == FS_UFS2_MAGIC && fs->fs_sblockloc != SBLOCK_UFS2 &&
(fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
printf("WARNING: %s: correcting fs_sblockloc from %jd to %d\n",
fs->fs_fsmnt, fs->fs_sblockloc, SBLOCK_UFS2);
fs->fs_sblockloc = SBLOCK_UFS2;
return (-devfdp->error - 1);
}
if (MOUNTEDSOFTDEP(ump->um_mountp))
softdep_setup_sbupdate(ump, (struct fs *)bp->b_data, bp);
UFS_LOCK(ump);
bcopy((caddr_t)fs, bp->b_data, (uint64_t)fs->fs_sbsize);
UFS_UNLOCK(ump);
fs = (struct fs *)bp->b_data;
fs->fs_fmod = 0;
ffs_oldfscompat_write(fs);
fs->fs_si = NULL;
/* Recalculate the superblock hash */
fs->fs_ckhash = ffs_calc_sbhash(fs);
softdep_setup_sbupdate(ump, fs, bp);
if (devfdp->suspended)
bp->b_flags |= B_VALIDSUSPWRT;
if (devfdp->waitfor != MNT_WAIT)
bawrite(bp);
else if ((error = bwrite(bp)) != 0)
devfdp->error = error;
return (devfdp->error);
return (-devfdp->error - 1);
}
static int