The branch stable/14 has been updated by emaste:

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

commit b991b25ed35a15ab03d61fabf4b7f2c3b1178982
Author:     Boris Lytochkin <[email protected]>
AuthorDate: 2025-08-27 18:49:12 +0000
Commit:     Ed Maste <[email protected]>
CommitDate: 2025-11-12 15:47:43 +0000

    makefs: Calculate indirect block count properly for large files on ffs
    
    When building an ffs image containing large file(s), space requirements
    were calculated incorrectly yielding a bigger image than necessary.
    The reason is that amount of indirect blocks estimation was done wrong:
    
    - single indirect block was treated as it can hold just 12 data blocks
    - nested indirect blocks were not taken into account at all
    
    Add support for indirect blocks and fix another tiny bug with
    underestimated space requirement for files with size between
    (UFS_NDADDR-1)*blksz+fragsz ... (UFS_NDADDR)*blksz requesting N>1
    fragments instead of a whole block.
    
    Reviewed by:    kib
    Differential Revision: https://reviews.freebsd.org/D52120
    
    (cherry picked from commit 9ce29249ec5d7d1d0a9f5f7655e1b37d54622665)
---
 usr.sbin/makefs/ffs.c | 94 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 76 insertions(+), 18 deletions(-)

diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
index 8c4b790f53b0..0e6d3075ed8d 100644
--- a/usr.sbin/makefs/ffs.c
+++ b/usr.sbin/makefs/ffs.c
@@ -593,6 +593,75 @@ ffs_create_image(const char *image, fsinfo_t *fsopts)
        return (fsopts->fd);
 }
 
+static void
+ffs_add_size(fsinfo_t *fsopts, size_t file_len)
+{
+       ffs_opt_t       *ffs_opts = fsopts->fs_specific;
+       size_t          blocks, fs_nindir, overhead;
+       int             indir_level;
+
+       blocks = howmany(file_len, ffs_opts->bsize);
+
+       if (blocks <= UFS_NDADDR) {
+               /* Count full blocks. */
+               fsopts->size += rounddown2(file_len, ffs_opts->bsize);
+               /* Calculate fragment size needed. */
+               overhead = howmany(file_len -
+                   rounddown2(file_len, ffs_opts->bsize), ffs_opts->fsize);
+
+               /*
+                * A file could have just 1 fragment with size 1/8, 1/4 or 1/2
+                * of bsize.
+                */
+               switch (overhead) {
+               case 0:
+                       break;
+               case 1:
+                       fsopts->size += ffs_opts->fsize;
+                       break;
+               case 2:
+                       fsopts->size += 2 * ffs_opts->fsize;
+                       break;
+               case 3:
+               case 4:
+                       fsopts->size += 4 * ffs_opts->fsize;
+                       break;
+               default:
+                       fsopts->size += ffs_opts->bsize;
+                       break;
+               }
+               return;
+       }
+
+       /* File does not fit into direct blocks, count indirect blocks. */
+       blocks = howmany(file_len - UFS_NDADDR * (size_t)ffs_opts->bsize,
+           ffs_opts->bsize);
+       fs_nindir = (size_t)ffs_opts->bsize / ((ffs_opts->version == 1) ?
+           sizeof(ufs1_daddr_t) : sizeof(ufs2_daddr_t));
+
+       indir_level = overhead = 0;
+       while (blocks > 0 && indir_level < 3) {
+               /* One indirect block is stored in di_ib[] */
+               blocks = howmany(blocks, fs_nindir) - 1;
+               fsopts->size += ffs_opts->bsize * blocks;
+               overhead += blocks + 1;
+               indir_level++;
+       }
+
+       assert(blocks == 0);
+
+       if ((debug & DEBUG_FS_SIZE_DIR_NODE) != 0) {
+               printf("ffs_size_dir: size %jd, using %d levels of indirect "
+                   "blocks, overhead %jd blocks\n", (uintmax_t)file_len,
+                   indir_level, (uintmax_t)overhead);
+       }
+
+       /*
+        * If the file is big enough to use indirect blocks,
+        * we allocate bsize block for trailing data.
+        */
+       fsopts->size += roundup2(file_len, ffs_opts->bsize);
+}
 
 static void
 ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
@@ -624,20 +693,6 @@ ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
                    e, tmpdir.d_namlen, this, curdirsize);              \
 } while (0);
 
-#define        ADDSIZE(x) do {                                                 
\
-       if ((size_t)(x) < UFS_NDADDR * (size_t)ffs_opts->bsize) {       \
-               fsopts->size += roundup((x), ffs_opts->fsize);          \
-       } else {                                                        \
-               /* Count space consumed by indirecttion blocks. */      \
-               fsopts->size += ffs_opts->bsize *                       \
-                   (howmany((x), UFS_NDADDR * ffs_opts->bsize) - 1);   \
-               /*                                                      \
-                * If the file is big enough to use indirect blocks,    \
-                * we allocate bsize block for trailing data.           \
-                */                                                     \
-               fsopts->size += roundup((x), ffs_opts->bsize);          \
-       }                                                               \
-} while (0);
 
        curdirsize = 0;
        for (node = root; node != NULL; node = node->next) {
@@ -648,13 +703,13 @@ ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
                } else if ((node->inode->flags & FI_SIZED) == 0) {
                                /* don't count duplicate names */
                        node->inode->flags |= FI_SIZED;
-                       if (debug & DEBUG_FS_SIZE_DIR_NODE)
+                       if ((debug & DEBUG_FS_SIZE_DIR_NODE) != 0)
                                printf("ffs_size_dir: `%s' size %lld\n",
                                    node->name,
                                    (long long)node->inode->st.st_size);
                        fsopts->inodes++;
                        if (node->type == S_IFREG)
-                               ADDSIZE(node->inode->st.st_size);
+                               ffs_add_size(fsopts, node->inode->st.st_size);
                        if (node->type == S_IFLNK) {
                                size_t slen;
 
@@ -662,13 +717,16 @@ ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
                                if (slen >= (ffs_opts->version == 1 ?
                                                UFS1_MAXSYMLINKLEN :
                                                UFS2_MAXSYMLINKLEN))
-                                       ADDSIZE(slen);
+                                       ffs_add_size(fsopts, slen);
                        }
                }
                if (node->type == S_IFDIR)
                        ffs_size_dir(node->child, fsopts);
        }
-       ADDSIZE(curdirsize);
+       ffs_add_size(fsopts, curdirsize);
+
+       /* Round up to full block to account fragment scattering. */
+       fsopts->size = roundup2(fsopts->size, ffs_opts->bsize);
 
        if (debug & DEBUG_FS_SIZE_DIR)
                printf("ffs_size_dir: exit: size %lld inodes %lld\n",

Reply via email to