The branch main has been updated by mckusick:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=661ca921e8cd56b17fc6615bc7e596e56e0e7c31

commit 661ca921e8cd56b17fc6615bc7e596e56e0e7c31
Author:     Kirk McKusick <mckus...@freebsd.org>
AuthorDate: 2025-01-16 18:43:48 +0000
Commit:     Kirk McKusick <mckus...@freebsd.org>
CommitDate: 2025-01-16 18:44:35 +0000

    Fix backward compatibility with UFS1 filesystems created before June 2002
    
    The code to handle compatibility with old filesystems exists and does
    the right things. The loss of compatibility was introduced 2.5 years ago
    when the superblock integrity checks were added. The problem is that the
    compatibility code was being run after the new integrity checks rather
    than before them.
    
    The reason that it has taken so long to show up is because it only
    affected filesystems created before UFS2 got added in June 2002 and
    that had never been mounted read-write (as their superblocks would
    be updated by the compatibility code if they could be written).
    Hence the problem was not apparent until someone tried to mount a
    pre-2002 virgin UFS1 filesystem image.
    
    See the Pahabricator review for more details.
    
    Reviewed-by: kib
    Tested-by:   Peter Holm
    MFC-after:   1 week
    Differential-Revision: https://reviews.freebsd.org/D48472
---
 sbin/growfs/debug.c        |  2 -
 sys/ufs/ffs/ffs_extern.h   |  2 +-
 sys/ufs/ffs/ffs_snapshot.c |  2 +-
 sys/ufs/ffs/ffs_softdep.c  |  4 +-
 sys/ufs/ffs/ffs_subr.c     | 81 +++++++++++++++++++++++++++++++++++++-
 sys/ufs/ffs/ffs_vfsops.c   | 96 +---------------------------------------------
 sys/ufs/ffs/fs.h           |  5 ++-
 7 files changed, 88 insertions(+), 104 deletions(-)

diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c
index bfff2acad2b5..3fe7e26e3e50 100644
--- a/sbin/growfs/debug.c
+++ b/sbin/growfs/debug.c
@@ -305,8 +305,6 @@ dbg_dump_fs(struct fs *sb, const char *comment)
            sb->fs_avgfilesize);
        fprintf(dbg_log, "avgfpdir          int32_t          0x%08x\n",
            sb->fs_avgfpdir);
-       fprintf(dbg_log, "save_cgsize       int32_t          0x%08x\n",
-           sb->fs_save_cgsize);
        fprintf(dbg_log, "flags             int32_t          0x%08x\n",
            sb->fs_flags);
        fprintf(dbg_log, "contigsumsize     int32_t          0x%08x\n",
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
index 112ab1107e8b..119de616003d 100644
--- a/sys/ufs/ffs/ffs_extern.h
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -82,7 +82,7 @@ int   ffs_inotovp(struct mount *, ino_t, uint64_t, int, 
struct vnode **,
            int);
 int    ffs_isblock(struct fs *, uint8_t *, ufs1_daddr_t);
 int    ffs_isfreeblock(struct fs *, uint8_t *, ufs1_daddr_t);
-void   ffs_oldfscompat_write(struct fs *, struct ufsmount *);
+void   ffs_oldfscompat_write(struct fs *);
 int    ffs_own_mount(const struct mount *mp);
 int    ffs_sbsearch(void *, struct fs **, int, struct malloc_type *,
            int (*)(void *, off_t, void **, int));
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index f4eed782f7bc..31823458ed8a 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -838,7 +838,7 @@ resumefs:
                copy_fs->fs_fmod = 0;
                bpfs = (struct fs *)&nbp->b_data[loc];
                bcopy((caddr_t)copy_fs, (caddr_t)bpfs, (uint64_t)fs->fs_sbsize);
-               ffs_oldfscompat_write(bpfs, ump);
+               ffs_oldfscompat_write(bpfs);
                bpfs->fs_ckhash = ffs_calc_sbhash(bpfs);
                bawrite(nbp);
        }
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
index 98ad4269b5f2..751b6e42a347 100644
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -9937,7 +9937,7 @@ clear_unlinked_inodedep( struct inodedep *inodedep)
                if (pino == 0) {
                        bcopy((caddr_t)fs, bp->b_data, (uint64_t)fs->fs_sbsize);
                        bpfs = (struct fs *)bp->b_data;
-                       ffs_oldfscompat_write(bpfs, ump);
+                       ffs_oldfscompat_write(bpfs);
                        softdep_setup_sbupdate(ump, bpfs, bp);
                        /*
                         * Because we may have made changes to the superblock,
@@ -9969,7 +9969,7 @@ clear_unlinked_inodedep( struct inodedep *inodedep)
                            (int)fs->fs_sbsize, 0, 0, 0);
                        bcopy((caddr_t)fs, bp->b_data, (uint64_t)fs->fs_sbsize);
                        bpfs = (struct fs *)bp->b_data;
-                       ffs_oldfscompat_write(bpfs, ump);
+                       ffs_oldfscompat_write(bpfs);
                        softdep_setup_sbupdate(ump, bpfs, bp);
                        /*
                         * Because we may have made changes to the superblock,
diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c
index bcb4d25c8ad9..7c2971d885ea 100644
--- a/sys/ufs/ffs/ffs_subr.c
+++ b/sys/ufs/ffs/ffs_subr.c
@@ -123,6 +123,7 @@ ffs_update_dinode_ckhash(struct fs *fs, struct ufs2_dinode 
*dip)
 static off_t sblock_try[] = SBLOCKSEARCH;
 static int readsuper(void *, struct fs **, off_t, int,
        int (*)(void *, off_t, void **, int));
+static void ffs_oldfscompat_read(struct fs *, ufs2_daddr_t);
 static int validate_sblock(struct fs *, int);
 
 /*
@@ -268,6 +269,7 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, 
int flags,
        if (fs->fs_magic == FS_UFS1_MAGIC && (flags & UFS_ALTSBLK) == 0 &&
            fs->fs_bsize == SBLOCK_UFS2 && sblockloc == SBLOCK_UFS2)
                return (ENOENT);
+       ffs_oldfscompat_read(fs, sblockloc);
        if ((error = validate_sblock(fs, flags)) > 0)
                return (error);
        /*
@@ -316,6 +318,83 @@ readsuper(void *devfd, struct fs **fsp, off_t sblockloc, 
int flags,
        return (0);
 }
 
+/*
+ * Sanity checks for loading old filesystem superblocks.
+ * See ffs_oldfscompat_write below for unwound actions.
+ *
+ * XXX - Parts get retired eventually.
+ * Unfortunately new bits get added.
+ */
+static void
+ffs_oldfscompat_read(struct fs *fs, ufs2_daddr_t sblockloc)
+{
+       uint64_t maxfilesize;
+
+       /*
+        * If not yet done, update fs_flags location and value of fs_sblockloc.
+        */
+       if ((fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
+               fs->fs_flags = fs->fs_old_flags;
+               fs->fs_old_flags |= FS_FLAGS_UPDATED;
+               fs->fs_sblockloc = sblockloc;
+       }
+       /*
+        * If not yet done, update UFS1 superblock with new wider fields.
+        */
+       if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_maxbsize != fs->fs_bsize) {
+               fs->fs_maxbsize = fs->fs_bsize;
+               fs->fs_time = fs->fs_old_time;
+               fs->fs_size = fs->fs_old_size;
+               fs->fs_dsize = fs->fs_old_dsize;
+               fs->fs_csaddr = fs->fs_old_csaddr;
+               fs->fs_cstotal.cs_ndir = fs->fs_old_cstotal.cs_ndir;
+               fs->fs_cstotal.cs_nbfree = fs->fs_old_cstotal.cs_nbfree;
+               fs->fs_cstotal.cs_nifree = fs->fs_old_cstotal.cs_nifree;
+               fs->fs_cstotal.cs_nffree = fs->fs_old_cstotal.cs_nffree;
+       }
+       if (fs->fs_magic == FS_UFS1_MAGIC &&
+           fs->fs_old_inodefmt < FS_44INODEFMT) {
+               fs->fs_maxfilesize = ((uint64_t)1 << 31) - 1;
+               fs->fs_qbmask = ~fs->fs_bmask;
+               fs->fs_qfmask = ~fs->fs_fmask;
+       }
+       if (fs->fs_magic == FS_UFS1_MAGIC) {
+               fs->fs_save_maxfilesize = fs->fs_maxfilesize;
+               maxfilesize = (uint64_t)0x80000000 * fs->fs_bsize - 1;
+               if (fs->fs_maxfilesize > maxfilesize)
+                       fs->fs_maxfilesize = maxfilesize;
+       }
+       /* Compatibility for old filesystems */
+       if (fs->fs_avgfilesize <= 0)
+               fs->fs_avgfilesize = AVFILESIZ;
+       if (fs->fs_avgfpdir <= 0)
+               fs->fs_avgfpdir = AFPDIR;
+}
+
+/*
+ * Unwinding superblock updates for old filesystems.
+ * See ffs_oldfscompat_read above for details.
+ *
+ * XXX - Parts get retired eventually.
+ * Unfortunately new bits get added.
+ */
+void
+ffs_oldfscompat_write(struct fs *fs)
+{
+
+       /*
+        * Copy back UFS2 updated fields that UFS1 inspects.
+        */
+       if (fs->fs_magic == FS_UFS1_MAGIC) {
+               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;
+       }
+}
+
 /*
  * Verify the filesystem values.
  */
@@ -561,7 +640,7 @@ validate_sblock(struct fs *fs, int flags)
                sizepb *= NINDIR(fs);
                maxfilesize += sizepb;
        }
-       WCHK(fs->fs_maxfilesize, !=, maxfilesize, %jd);
+       WCHK(fs->fs_maxfilesize, >, maxfilesize, %jd);
        /*
         * These values have a tight interaction with each other that
         * makes it hard to tightly bound them. So we can only check
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index a739e0220ec9..da079ce1efb6 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -83,8 +83,6 @@ static uma_zone_t uma_inode, uma_ufs1, uma_ufs2;
 VFS_SMR_DECLARE;
 
 static int     ffs_mountfs(struct vnode *, struct mount *, struct thread *);
-static void    ffs_oldfscompat_read(struct fs *, struct ufsmount *,
-                   ufs2_daddr_t);
 static void    ffs_ifree(struct ufsmount *ump, struct inode *ip);
 static int     ffs_sync_lazy(struct mount *mp);
 static int     ffs_use_bread(void *devfd, off_t loc, void **bufp, int size);
@@ -795,7 +793,6 @@ ffs_reload(struct mount *mp, int flags)
        fs = VFSTOUFS(mp)->um_fs = newfs;
        ump->um_bsize = fs->fs_bsize;
        ump->um_maxsymlinklen = fs->fs_maxsymlinklen;
-       ffs_oldfscompat_read(fs, VFSTOUFS(mp), fs->fs_sblockloc);
        UFS_LOCK(ump);
        if (fs->fs_pendingblocks != 0 || fs->fs_pendinginodes != 0) {
                printf("WARNING: %s: reload pending error: blocks %jd "
@@ -1010,7 +1007,6 @@ ffs_mountfs(struct vnode *odevvp, struct mount *mp, 
struct thread *td)
                ump->um_check_blkno = NULL;
        mtx_init(UFS_MTX(ump), "FFS", "FFS Lock", MTX_DEF);
        sx_init(&ump->um_checkpath_lock, "uchpth");
-       ffs_oldfscompat_read(fs, ump, fs->fs_sblockloc);
        fs->fs_ronly = ronly;
        fs->fs_active = NULL;
        mp->mnt_data = ump;
@@ -1219,96 +1215,6 @@ ffs_use_bread(void *devfd, off_t loc, void **bufp, int 
size)
        return (0);
 }
 
-static int bigcgs = 0;
-SYSCTL_INT(_debug, OID_AUTO, bigcgs, CTLFLAG_RW, &bigcgs, 0, "");
-
-/*
- * Sanity checks for loading old filesystem superblocks.
- * See ffs_oldfscompat_write below for unwound actions.
- *
- * XXX - Parts get retired eventually.
- * Unfortunately new bits get added.
- */
-static void
-ffs_oldfscompat_read(struct fs *fs,
-       struct ufsmount *ump,
-       ufs2_daddr_t sblockloc)
-{
-       off_t maxfilesize;
-
-       /*
-        * If not yet done, update fs_flags location and value of fs_sblockloc.
-        */
-       if ((fs->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
-               fs->fs_flags = fs->fs_old_flags;
-               fs->fs_old_flags |= FS_FLAGS_UPDATED;
-               fs->fs_sblockloc = sblockloc;
-       }
-       /*
-        * If not yet done, update UFS1 superblock with new wider fields.
-        */
-       if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_maxbsize != fs->fs_bsize) {
-               fs->fs_maxbsize = fs->fs_bsize;
-               fs->fs_time = fs->fs_old_time;
-               fs->fs_size = fs->fs_old_size;
-               fs->fs_dsize = fs->fs_old_dsize;
-               fs->fs_csaddr = fs->fs_old_csaddr;
-               fs->fs_cstotal.cs_ndir = fs->fs_old_cstotal.cs_ndir;
-               fs->fs_cstotal.cs_nbfree = fs->fs_old_cstotal.cs_nbfree;
-               fs->fs_cstotal.cs_nifree = fs->fs_old_cstotal.cs_nifree;
-               fs->fs_cstotal.cs_nffree = fs->fs_old_cstotal.cs_nffree;
-       }
-       if (fs->fs_magic == FS_UFS1_MAGIC &&
-           fs->fs_old_inodefmt < FS_44INODEFMT) {
-               fs->fs_maxfilesize = ((uint64_t)1 << 31) - 1;
-               fs->fs_qbmask = ~fs->fs_bmask;
-               fs->fs_qfmask = ~fs->fs_fmask;
-       }
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-               ump->um_savedmaxfilesize = fs->fs_maxfilesize;
-               maxfilesize = (uint64_t)0x80000000 * fs->fs_bsize - 1;
-               if (fs->fs_maxfilesize > maxfilesize)
-                       fs->fs_maxfilesize = maxfilesize;
-       }
-       /* Compatibility for old filesystems */
-       if (fs->fs_avgfilesize <= 0)
-               fs->fs_avgfilesize = AVFILESIZ;
-       if (fs->fs_avgfpdir <= 0)
-               fs->fs_avgfpdir = AFPDIR;
-       if (bigcgs) {
-               fs->fs_save_cgsize = fs->fs_cgsize;
-               fs->fs_cgsize = fs->fs_bsize;
-       }
-}
-
-/*
- * Unwinding superblock updates for old filesystems.
- * See ffs_oldfscompat_read above for details.
- *
- * XXX - Parts get retired eventually.
- * Unfortunately new bits get added.
- */
-void
-ffs_oldfscompat_write(struct fs *fs, struct ufsmount *ump)
-{
-
-       /*
-        * Copy back UFS2 updated fields that UFS1 inspects.
-        */
-       if (fs->fs_magic == FS_UFS1_MAGIC) {
-               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 = ump->um_savedmaxfilesize;
-       }
-       if (bigcgs) {
-               fs->fs_cgsize = fs->fs_save_cgsize;
-               fs->fs_save_cgsize = 0;
-       }
-}
-
 /*
  * unmount system call
  */
@@ -2192,7 +2098,7 @@ ffs_use_bwrite(void *devfd, off_t loc, void *buf, int 
size)
        UFS_UNLOCK(ump);
        fs = (struct fs *)bp->b_data;
        fs->fs_fmod = 0;
-       ffs_oldfscompat_write(fs, ump);
+       ffs_oldfscompat_write(fs);
        fs->fs_si = NULL;
        /* Recalculate the superblock hash */
        fs->fs_ckhash = ffs_calc_sbhash(fs);
diff --git a/sys/ufs/ffs/fs.h b/sys/ufs/ffs/fs.h
index 8a1b5be18bc2..c092688c475b 100644
--- a/sys/ufs/ffs/fs.h
+++ b/sys/ufs/ffs/fs.h
@@ -411,7 +411,8 @@ struct fs {
        int64_t  fs_unrefs;             /* number of unreferenced inodes */
        int64_t  fs_providersize;       /* size of underlying GEOM provider */
        int64_t  fs_metaspace;          /* size of area reserved for metadata */
-       int64_t  fs_sparecon64[13];     /* old rotation block list head */
+       uint64_t fs_save_maxfilesize;   /* save old UFS1 maxfilesize */
+       int64_t  fs_sparecon64[12];     /* old rotation block list head */
        int64_t  fs_sblockactualloc;    /* byte offset of this superblock */
        int64_t  fs_sblockloc;          /* byte offset of standard superblock */
        struct  csum_total fs_cstotal;  /* (u) cylinder summary information */
@@ -424,7 +425,7 @@ struct fs {
        uint32_t fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */
        uint32_t fs_avgfilesize;        /* expected average file size */
        uint32_t fs_avgfpdir;           /* expected # of files per directory */
-       int32_t  fs_save_cgsize;        /* save real cg size to use fs_bsize */
+       uint32_t fs_available_spare;    /* old scratch space */
        ufs_time_t fs_mtime;            /* Last mount or fsck time. */
        int32_t  fs_sujfree;            /* SUJ free list */
        int32_t  fs_sparecon32[21];     /* reserved for future constants */

Reply via email to