Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package apfsprogs for openSUSE:Factory 
checked in at 2025-06-11 16:22:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/apfsprogs (Old)
 and      /work/SRC/openSUSE:Factory/.apfsprogs.new.19631 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "apfsprogs"

Wed Jun 11 16:22:40 2025 rev:8 rq:1284419 version:0.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/apfsprogs/apfsprogs.changes      2025-01-13 
17:53:36.583260239 +0100
+++ /work/SRC/openSUSE:Factory/.apfsprogs.new.19631/apfsprogs.changes   
2025-06-11 16:23:42.329411607 +0200
@@ -1,0 +2,12 @@
+Tue Jun 10 05:50:11 UTC 2025 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to version 0.2.1
+  * apfsck: Implement more checks for fusion drives
+  * refactor: Remove redundant MIN macro in spaceman.c
+  * apfsck: Use DIV_ROUND_UP macro
+  * apfs-label: Get rid of mmap to support 16K pages
+  * apfs-label: Fix memory leak in read_latest_super()
+  * mkapfs: Get rid of mmap to support 16K pages
+- Explicitly list files matched in %files section
+
+-------------------------------------------------------------------

Old:
----
  apfsprogs-0.2.0.tar.gz

New:
----
  apfsprogs-0.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ apfsprogs.spec ++++++
--- /var/tmp/diff_new_pack.lDOxvx/_old  2025-06-11 16:23:43.345454006 +0200
+++ /var/tmp/diff_new_pack.lDOxvx/_new  2025-06-11 16:23:43.349454173 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           apfsprogs
-Version:        0.2.0
+Version:        0.2.1
 Release:        0
 Summary:        Experimental APFS tools for Linux
 License:        GPL-2.0-only
@@ -53,6 +53,16 @@
 %files
 %license LICENSE
 %doc README
-%{_sbindir}/*
-%{_mandir}/*
+%{_mandir}/apfs-label.8
+%{_mandir}/apfs-snap.8
+%{_mandir}/apfsck.8
+%{_mandir}/fsck.apfs.8
+%{_mandir}/mkapfs.8
+%{_mandir}/mkfs.apfs.8
+%{_sbindir}/apfs-label
+%{_sbindir}/apfs-snap
+%{_sbindir}/apfsck
+%{_sbindir}/fsck.apfs
+%{_sbindir}/mkapfs
+%{_sbindir}/mkfs.apfs
 

++++++ apfsprogs-0.2.0.tar.gz -> apfsprogs-0.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfs-label/apfs-label.8 
new/apfsprogs-0.2.1/apfs-label/apfs-label.8
--- old/apfsprogs-0.2.0/apfs-label/apfs-label.8 2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/apfs-label/apfs-label.8 2025-06-02 21:50:22.000000000 
+0200
@@ -2,7 +2,7 @@
 .\"
 .\" Copyright (C) 2024 Ernesto A. Fernández <erne...@corellium.com>
 .\"
-.TH apfs-label 8 "November 2024" "apfsprogs 0.2.0"
+.TH apfs-label 8 "June 2025" "apfsprogs 0.2.1"
 .SH NAME
 apfs-label \- list the labels of all volumes in an APFS filesystem
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfs-label/apfs-label.c 
new/apfsprogs-0.2.1/apfs-label/apfs-label.c
--- old/apfsprogs-0.2.0/apfs-label/apfs-label.c 2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/apfs-label/apfs-label.c 2025-06-02 21:50:22.000000000 
+0200
@@ -5,7 +5,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -62,6 +61,27 @@
        exit(EXIT_FAILURE);
 }
 
+static void *readall(int fd, size_t count, off_t offset)
+{
+       void *buf = NULL;
+       size_t copied;
+       ssize_t ret;
+
+       buf = malloc(count);
+       if (!buf)
+               system_error();
+
+       copied = 0;
+       while (count > 0) {
+               ret = pread(fd, buf + copied, count, offset + copied);
+               if (ret < 0)
+                       system_error();
+               count -= ret;
+               copied += ret;
+       }
+       return buf;
+}
+
 /**
  * read_super_copy - Read the copy of the container superblock in block 0
  *
@@ -78,9 +98,7 @@
         */
        bsize_tmp = APFS_NX_DEFAULT_BLOCK_SIZE;
 
-       msb_raw = mmap(NULL, bsize_tmp, PROT_READ, MAP_PRIVATE, dev_fd, 
APFS_NX_BLOCK_NUM * bsize_tmp);
-       if (msb_raw == MAP_FAILED)
-               system_error();
+       msb_raw = readall(dev_fd, bsize_tmp, APFS_NX_BLOCK_NUM * bsize_tmp);
        if (le32_to_cpu(msb_raw->nx_magic) != APFS_NX_MAGIC)
                fatal("not an apfs container");
        s_blocksize = le32_to_cpu(msb_raw->nx_block_size);
@@ -88,11 +106,9 @@
                fatal("reported blocksize is too small");
 
        if (s_blocksize != bsize_tmp) {
-               munmap(msb_raw, bsize_tmp);
+               free(msb_raw);
 
-               msb_raw = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, 
dev_fd, APFS_NX_BLOCK_NUM * s_blocksize);
-               if (msb_raw == MAP_FAILED)
-                       system_error();
+               msb_raw = readall(dev_fd, s_blocksize, APFS_NX_BLOCK_NUM * 
s_blocksize);
        }
        return msb_raw;
 }
@@ -114,22 +130,23 @@
        u64 bno;
 
        for (bno = base; bno < base + blocks; ++bno) {
-               if (current)
-                       munmap(current, s_blocksize);
-               current = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, 
dev_fd, bno * s_blocksize);
-               if (current == MAP_FAILED)
-                       system_error();
+               current = readall(dev_fd, s_blocksize, bno * s_blocksize);
 
                if (le32_to_cpu(current->nx_magic) != APFS_NX_MAGIC)
-                       continue; /* Not a superblock */
+                       goto next; /* Not a superblock */
                if (le64_to_cpu(current->nx_o.o_xid) <= xid)
-                       continue; /* Old */
+                       goto next; /* Old */
                if (!obj_verify_csum(&current->nx_o))
-                       continue; /* Corrupted */
+                       goto next; /* Corrupted */
 
                xid = le64_to_cpu(current->nx_o.o_xid);
+               if (latest)
+                       free(latest);
                latest = current;
                current = NULL;
+next:
+               if (current)
+                       free(current);
        }
 
        if (!latest)
@@ -153,7 +170,7 @@
        desc_blocks = le32_to_cpu(msb->nx_xp_desc_blocks);
        if (desc_blocks > 10000) /* Arbitrary loop limit, is it enough? */
                fatal("too many checkpoint descriptors?");
-       munmap(msb, s_blocksize);
+       free(msb);
        msb = NULL;
 
        return read_latest_super(desc_base, desc_blocks);
@@ -165,15 +182,11 @@
        struct apfs_btree_node_phys *root = NULL;
        u64 root_bno;
 
-       omap = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, omap_bno 
* s_blocksize);
-       if (omap == MAP_FAILED)
-               system_error();
+       omap = readall(dev_fd, s_blocksize, omap_bno * s_blocksize);
        root_bno = le64_to_cpu(omap->om_tree_oid);
-       munmap(omap, s_blocksize);
+       free(omap);
 
-       root = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, root_bno 
* s_blocksize);
-       if (root == MAP_FAILED)
-               system_error();
+       root = readall(dev_fd, s_blocksize, root_bno * s_blocksize);
 
        /* I don't think this can happen so I don't support it for now */
        if ((le16_to_cpu(root->btn_flags) & APFS_BTNODE_LEAF) == 0)
@@ -293,17 +306,15 @@
                if (vol_id == 0)
                        continue;
                vol_bno = omap_lookup(omap, vol_id);
-               vsb = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, 
vol_bno * s_blocksize);
-               if (vsb == MAP_FAILED)
-                       system_error();
+               vsb = readall(dev_fd, s_blocksize, vol_bno * s_blocksize);
                if (vsb->apfs_volname[APFS_VOLNAME_LEN - 1])
                        fatal("volume label is not properly null-terminated");
                printf("%d\t%s\n", i, vsb->apfs_volname);
-               munmap(vsb, s_blocksize);
+               free(vsb);
        }
 
-       munmap(omap, s_blocksize);
-       munmap(msb, s_blocksize);
+       free(omap);
+       free(msb);
 }
 
 int main(int argc, char *argv[])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfs-snap/apfs-snap.8 
new/apfsprogs-0.2.1/apfs-snap/apfs-snap.8
--- old/apfsprogs-0.2.0/apfs-snap/apfs-snap.8   2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/apfs-snap/apfs-snap.8   2025-06-02 21:50:22.000000000 
+0200
@@ -2,7 +2,7 @@
 .\"
 .\" Copyright (C) 2022 Ernesto A. Fernández <erne...@corellium.com>
 .\"
-.TH apfs-snap 8 "November 2024" "apfsprogs 0.2.0"
+.TH apfs-snap 8 "June 2025" "apfsprogs 0.2.1"
 .SH NAME
 apfs-snap \- take an snapshot of an APFS filesystem
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/apfsck.8 
new/apfsprogs-0.2.1/apfsck/apfsck.8
--- old/apfsprogs-0.2.0/apfsck/apfsck.8 2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/apfsck.8 2025-06-02 21:50:22.000000000 +0200
@@ -2,7 +2,7 @@
 .\"
 .\" Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernan...@gmail.com>
 .\"
-.TH apfsck 8 "November 2024" "apfsprogs 0.2.0"
+.TH apfsck 8 "June 2025" "apfsprogs 0.2.1"
 .SH NAME
 apfsck \- check an APFS filesystem
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/btree.c 
new/apfsprogs-0.2.1/apfsck/btree.c
--- old/apfsprogs-0.2.0/apfsck/btree.c  2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/btree.c  2025-06-02 21:50:22.000000000 +0200
@@ -132,10 +132,10 @@
        int off;
 
        /* Each bit represents a byte in the key area */
-       node->free_key_bmap = malloc((area_len + 7) / 8);
+       node->free_key_bmap = malloc(DIV_ROUND_UP(area_len, 8));
        if (!node->free_key_bmap)
                system_error();
-       memset(node->free_key_bmap, 0xFF, (area_len + 7) / 8);
+       memset(node->free_key_bmap, 0xFF, DIV_ROUND_UP(area_len, 8));
 
        off = le16_to_cpu(free->off);
        while (total > 0) {
@@ -195,10 +195,10 @@
        end_raw = (void *)node->raw + node->data + area_len;
 
        /* Each bit represents a byte in the value area */
-       node->free_val_bmap = malloc((area_len + 7) / 8);
+       node->free_val_bmap = malloc(DIV_ROUND_UP(area_len, 8));
        if (!node->free_val_bmap)
                system_error();
-       memset(node->free_val_bmap, 0xFF, (area_len + 7) / 8);
+       memset(node->free_val_bmap, 0xFF, DIV_ROUND_UP(area_len, 8));
 
        off = le16_to_cpu(free->off);
        while (total > 0) {
@@ -261,7 +261,7 @@
        keys_len = node->free - node->key;
 
        /* Each bit represents a byte in the key area */
-       node->used_key_bmap = calloc(1, (keys_len + 7) / 8);
+       node->used_key_bmap = calloc(1, DIV_ROUND_UP(keys_len, 8));
        if (!node->used_key_bmap)
                system_error();
 
@@ -270,7 +270,7 @@
                     (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0);
 
        /* Each bit represents a byte in the value area */
-       node->used_val_bmap = calloc(1, (values_len + 7) / 8);
+       node->used_val_bmap = calloc(1, DIV_ROUND_UP(values_len, 8));
        if (!node->used_val_bmap)
                system_error();
 
@@ -435,7 +435,7 @@
                struct apfs_kvoff *entry;
 
                entry = (struct apfs_kvoff *)raw->btn_data + index;
-               len = btree_is_snapshots(node->btree) ? 8 : 16;
+               len = btree_is_snapshots(node->btree) || 
btree_is_fusion_mt(node->btree) ? 8 : 16;
                off_in_area = le16_to_cpu(entry->k);
        } else {
                /* These node types have variable length keys and data */
@@ -496,6 +496,8 @@
                        len = node_is_leaf(node) ? sizeof(struct 
apfs_omap_snapshot) : 8;
                if (btree_is_fext(btree))
                        len = node_is_leaf(node) ? sizeof(struct 
apfs_fext_tree_val) : 8;
+               if (btree_is_fusion_mt(btree))
+                       len = node_is_leaf(node) ? sizeof(struct 
apfs_fusion_mt_val) : 8;
 
                /* Value offsets are backwards from the end of the value area */
                off_in_area = area_len - le16_to_cpu(entry->v);
@@ -670,6 +672,74 @@
 }
 
 /**
+ * parse_fusion_mt_record - Parse a fusion mt value and check for corruption
+ * @key:       pointer to the raw key
+ * @val:       pointer to the raw value
+ * @len:       length of the raw value
+ *
+ * Internal consistency of @key must be checked before calling this function.
+ */
+static void parse_fusion_mt_record(struct apfs_fusion_mt_key *key, struct 
apfs_fusion_mt_val *val, int len)
+{
+       char *data_tier2 = NULL, *data_main = NULL;
+       u64 paddr_tier2, paddr_main;
+       u64 offset_tier2, offset_main;
+       u32 blkcnt;
+       u64 length;
+       u32 flags;
+
+       if (len != sizeof(*val))
+               report("Fusion middle-tree record", "wrong value size.");
+
+       paddr_tier2 = le64_to_cpu(key->fmk_paddr);
+       offset_tier2 = paddr_tier2 << sb->s_blocksize_bits;
+       if (offset_tier2 < APFS_FUSION_TIER2_DEVICE_BYTE_ADDR)
+               report("Fusion middle-tree record", "key address is in main.");
+
+       paddr_main = le64_to_cpu(val->fmv_lba);
+       offset_main = paddr_main << sb->s_blocksize_bits;
+       if (offset_main >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR)
+               report("Fusion middle-tree record", "value address is in tier 
2.");
+
+       blkcnt = le32_to_cpu(val->fmv_length);
+
+       /*
+        * The tier 2 blocks get counted when they are actually used; blocks in
+        * the writeback cache were already counted by check_fusion_wbc().
+        */
+       if (!range_in_wbc(paddr_main, blkcnt))
+               container_bmap_mark_as_used(paddr_main, blkcnt);
+
+       flags = le32_to_cpu(val->fmv_flags);
+       if ((flags & APFS_FUSION_MT_ALLFLAGS) != flags)
+               report("Fusion middle-tree record", "invalid flags in use.");
+       if ((flags & APFS_FUSION_MT_TENANT) && (flags & APFS_FUSION_MT_DIRTY))
+               report("Fusion middle-tree record", "can't be both dirty and 
tenant.");
+       if (flags & APFS_FUSION_MT_TENANT)
+               report_unknown("Tenant mt record");
+
+       if (!(flags & APFS_FUSION_MT_DIRTY)) {
+               length = (u64)blkcnt * sb->s_blocksize;
+               /*
+                * This may actually not check the whole range if the fsck runs
+                * in a 32-bit computer. I guess it doesn't matter.
+                */
+               data_tier2 = apfs_mmap(NULL, length, PROT_READ, MAP_PRIVATE, 
offset_tier2);
+               if (data_tier2 == MAP_FAILED)
+                       system_error();
+               data_main = apfs_mmap(NULL, length, PROT_READ, MAP_PRIVATE, 
offset_main);
+               if (data_main == MAP_FAILED)
+                       system_error();
+               if (memcmp(data_tier2, data_main, length) != 0)
+                       report("Fusion middle-tree record", "missing dirty 
flag.");
+               munmap(data_tier2, length);
+               data_tier2 = NULL;
+               munmap(data_main, length);
+               data_main = NULL;
+       }
+}
+
+/**
  * free_omap_record - Free an object map record list after a final check
  * @entry: the entry to free
  */
@@ -935,6 +1005,8 @@
                        read_omap_snap_key(raw_key, len, &curr_key);
                if (btree_is_fext(btree))
                        read_fext_key(raw_key, len, &curr_key);
+               if (btree_is_fusion_mt(btree))
+                       read_fusion_mt_key(raw_key, len, &curr_key);
 
                if (keycmp(last_key, &curr_key) > 0)
                        report("B-tree", "keys are out of order.");
@@ -970,6 +1042,8 @@
                                parse_omap_snap_record(raw_key, raw_val, len);
                        if (btree_is_fext(btree))
                                parse_fext_record(raw_key, raw_val, len);
+                       if (btree_is_fusion_mt(btree))
+                               parse_fusion_mt_record(raw_key, raw_val, len);
                        continue;
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/compress.c 
new/apfsprogs-0.2.1/apfsck/compress.c
--- old/apfsprogs-0.2.0/apfsck/compress.c       2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/apfsck/compress.c       2025-06-02 21:50:22.000000000 
+0200
@@ -332,7 +332,7 @@
     if(compress->size != size)
         report("Resource compressed file", "wrong reported length.");
 
-    block_num = (compress->size + APFS_COMPRESS_BLOCK - 1) / 
APFS_COMPRESS_BLOCK;
+    block_num = DIV_ROUND_UP(compress->size, APFS_COMPRESS_BLOCK);
     if(block_num != compress->block_num)
         report("Resource compressed file", "inconsistent block count.");
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/key.c 
new/apfsprogs-0.2.1/apfsck/key.c
--- old/apfsprogs-0.2.0/apfsck/key.c    2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/key.c    2025-06-02 21:50:22.000000000 +0200
@@ -439,3 +439,17 @@
        key->number = 0;
        key->name = NULL;
 }
+
+void read_fusion_mt_key(void *raw, int size, struct key *key)
+{
+       struct apfs_fusion_mt_key *raw_key = NULL;
+
+       if (size != sizeof(*raw_key))
+               report("Fusion middle-tree", "wrong size of key.");
+       raw_key = raw;
+
+       key->id = le64_to_cpu(raw_key->fmk_paddr);
+       key->type = 0;
+       key->number = 0;
+       key->name = NULL;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/key.h 
new/apfsprogs-0.2.1/apfsck/key.h
--- old/apfsprogs-0.2.0/apfsck/key.h    2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/key.h    2025-06-02 21:50:22.000000000 +0200
@@ -134,5 +134,6 @@
 extern void read_snap_key(void *raw, int size, struct key *key);
 extern void read_omap_snap_key(void *raw, int size, struct key *key);
 extern void read_fext_key(void *raw, int size, struct key *key);
+extern void read_fusion_mt_key(void *raw, int size, struct key *key);
 
 #endif /* _KEY_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/super.c 
new/apfsprogs-0.2.1/apfsck/super.c
--- old/apfsprogs-0.2.0/apfsck/super.c  2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/super.c  2025-06-02 21:50:22.000000000 +0200
@@ -1197,9 +1197,9 @@
        sb->s_reaper = parse_reaper(le64_to_cpu(sb->s_raw->nx_reaper_oid));
 
        /* Check the fusion structures now */
+       check_fusion_wbc(le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_start_paddr), 
le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_block_count));
        sb->s_fusion_mt = 
parse_fusion_middle_tree(le64_to_cpu(sb->s_raw->nx_fusion_mt_oid));
        sb->s_fusion_wbc = 
parse_fusion_wbc_state(le64_to_cpu(sb->s_raw->nx_fusion_wbc_oid));
-       check_fusion_wbc(le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_start_paddr), 
le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_block_count));
 
        for (vol = 0; vol < APFS_NX_MAX_FILE_SYSTEMS; ++vol) {
                struct apfs_superblock *vsb_raw;
@@ -1752,5 +1752,39 @@
        }
        if (!bno || !blkcnt)
                report("Fusion wb cache", "should exist.");
+
+       if (bno >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR)
+               report("Fusion wb cache", "is in tier 2.");
        container_bmap_mark_as_used(bno, blkcnt);
+
+       sb->s_wbc_bno = bno;
+       sb->s_wbc_blkcnt = blkcnt;
+}
+
+/**
+ * block_in_wbc - Does this block belong to the writeback cache?
+ * @bno: block number to check
+ */
+static inline bool block_in_wbc(u64 bno)
+{
+       u64 start = sb->s_wbc_bno;
+       u64 end = start + sb->s_wbc_blkcnt;
+
+       return bno >= start && bno < end;
+}
+
+/**
+ * range_in_wbc - Is this range included in the writeback cache?
+ * @paddr:     first block of the range
+ * @length:    length of the range
+ */
+bool range_in_wbc(u64 paddr, u64 length)
+{
+       u64 last = paddr + length - 1;
+       bool first_in_wbc = block_in_wbc(paddr);
+       bool last_in_wbc = block_in_wbc(last);
+
+       if ((first_in_wbc && !last_in_wbc) || (!first_in_wbc && last_in_wbc))
+               report("Writeback cache", "is overrun.");
+       return first_in_wbc;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/apfsck/super.h 
new/apfsprogs-0.2.1/apfsck/super.h
--- old/apfsprogs-0.2.0/apfsck/super.h  2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/apfsck/super.h  2025-06-02 21:50:22.000000000 +0200
@@ -97,6 +97,8 @@
        u32 s_data_index; /* Index of first valid block in checkpoint data */
        u32 s_data_len; /* Number of valid blocks in checkpoint data area */
        u64 s_reaper_fs_id; /* Volume id reported by the reaper */
+       u64 s_wbc_bno; /* First block of the writeback cache */
+       u64 s_wbc_blkcnt; /* Number of blocks in the writeback cache */
 
        /* Hash table of ephemeral object mappings for the checkpoint */
        struct htable_entry **s_cpoint_map_table;
@@ -214,5 +216,6 @@
 extern struct volume_superblock *alloc_volume_super(bool snap);
 extern void read_volume_super(int vol, struct volume_superblock *vsb, struct 
object *obj);
 extern void check_volume_super(void);
+extern bool range_in_wbc(u64 paddr, u64 length);
 
 #endif /* _SUPER_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/include/apfs/raw.h 
new/apfsprogs-0.2.1/include/apfs/raw.h
--- old/apfsprogs-0.2.0/include/apfs/raw.h      2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/include/apfs/raw.h      2025-06-02 21:50:22.000000000 
+0200
@@ -1548,7 +1548,7 @@
        __le32 fwp_listBlocksCount;
        __le32 fwp_reserved;
        __le64 fwp_usedByRC;
-       struct apfs_prange fwp_rcStash;;
+       struct apfs_prange fwp_rcStash;
 } __packed;
 
 struct apfs_fusion_wbc_list_entry {
@@ -1589,6 +1589,6 @@
 /* Flags for the fusion middle-tree */
 #define APFS_FUSION_MT_DIRTY   (1 << 0)
 #define APFS_FUSION_MT_TENANT  (1 << 1)
-#define APFS_FUSION_MT_ALLFLAGS        (FUSION_MT_DIRTY | FUSION_MT_TENANT)
+#define APFS_FUSION_MT_ALLFLAGS        (APFS_FUSION_MT_DIRTY | 
APFS_FUSION_MT_TENANT)
 
 #endif /* _RAW_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/mkapfs/btree.c 
new/apfsprogs-0.2.1/mkapfs/btree.c
--- old/apfsprogs-0.2.0/mkapfs/btree.c  2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/mkapfs/btree.c  2025-06-02 21:50:22.000000000 +0200
@@ -99,7 +99,7 @@
  */
 void make_empty_btree_root(u64 bno, u64 oid, u32 subtype)
 {
-       struct apfs_btree_node_phys *root = get_zeroed_block(bno);
+       struct apfs_btree_node_phys *root = get_zeroed_block();
        u32 type;
        u16 flags;
        int toc_len, free_len;
@@ -137,7 +137,7 @@
                type |= APFS_OBJ_PHYSICAL;
        set_object_header(&root->btn_o, param->blocksize, oid, type, subtype);
 
-       munmap(root, param->blocksize);
+       apfs_writeall(root, 1, bno);
 }
 
 /**
@@ -164,7 +164,7 @@
  */
 static void make_omap_root(u64 bno, bool is_vol)
 {
-       struct apfs_btree_node_phys *root = get_zeroed_block(bno);
+       struct apfs_btree_node_phys *root = get_zeroed_block();
        struct apfs_omap_key *key;
        struct apfs_omap_val *val;
        struct apfs_kvoff *kvoff;
@@ -219,7 +219,7 @@
        set_object_header(&root->btn_o, param->blocksize, bno,
                          APFS_OBJECT_TYPE_BTREE | APFS_OBJ_PHYSICAL,
                          APFS_OBJECT_TYPE_OMAP);
-       munmap(root, param->blocksize);
+       apfs_writeall(root, 1, bno);
 }
 
 /**
@@ -229,7 +229,7 @@
  */
 void make_omap_btree(u64 bno, bool is_vol)
 {
-       struct apfs_omap_phys *omap = get_zeroed_block(bno);
+       struct apfs_omap_phys *omap = get_zeroed_block();
 
        if (!is_vol)
                omap->om_flags = cpu_to_le32(APFS_OMAP_MANUALLY_MANAGED);
@@ -248,7 +248,7 @@
        set_object_header(&omap->om_o, param->blocksize, bno,
                          APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_OMAP,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(omap, param->blocksize);
+       apfs_writeall(omap, 1, bno);
 }
 
 /**
@@ -282,7 +282,7 @@
  */
 void make_cat_root(u64 bno, u64 oid)
 {
-       struct apfs_btree_node_phys *root = get_zeroed_block(bno);
+       struct apfs_btree_node_phys *root = get_zeroed_block();
        struct apfs_kvloc *kvloc;
        void *key, *key_area, *val_end, *val_area_end;
        int toc_len, key_len, free_len, val_len;
@@ -327,5 +327,5 @@
        set_object_header(&root->btn_o, param->blocksize, oid,
                          APFS_OBJECT_TYPE_BTREE | APFS_OBJ_VIRTUAL,
                          APFS_OBJECT_TYPE_FSTREE);
-       munmap(root, param->blocksize);
+       apfs_writeall(root, 1, bno);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/mkapfs/mkapfs.8 
new/apfsprogs-0.2.1/mkapfs/mkapfs.8
--- old/apfsprogs-0.2.0/mkapfs/mkapfs.8 2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/mkapfs/mkapfs.8 2025-06-02 21:50:22.000000000 +0200
@@ -2,7 +2,7 @@
 .\"
 .\" Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernan...@gmail.com>
 .\"
-.TH mkapfs 8 "November 2024" "apfsprogs 0.2.0"
+.TH mkapfs 8 "June 2025" "apfsprogs 0.2.1"
 .SH NAME
 mkapfs \- create an APFS filesystem
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/mkapfs/mkapfs.h 
new/apfsprogs-0.2.1/mkapfs/mkapfs.h
--- old/apfsprogs-0.2.0/mkapfs/mkapfs.h 2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/mkapfs/mkapfs.h 2025-06-02 21:50:22.000000000 +0200
@@ -5,9 +5,10 @@
 #ifndef _MKAPFS_H
 #define _MKAPFS_H
 
+#include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <time.h>
+#include <unistd.h>
 #include <apfs/raw.h>
 
 /* Filesystem parameters */
@@ -157,49 +158,59 @@
 extern __attribute__((noreturn)) void system_error(void);
 extern __attribute__((noreturn)) void fatal(const char *message);
 
-/* Forwards the mmap() call to the proper device of a fusion drive */
-static inline void *apfs_mmap(void *addr, size_t length, int prot, int flags, 
u64 offset)
+/* Forwards the pwrite() call to the proper device of a fusion drive */
+static inline void apfs_writeall(void *buf, u64 blkcnt, u64 bno)
 {
+       int proper_fd;
+       off_t offset;
+       size_t count, copied;
+       ssize_t ret;
+
+       offset = bno * param->blocksize;
+       count = blkcnt * param->blocksize;
+
+       proper_fd = fd_main;
        if (offset >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR) {
                if (fd_tier2 == -1)
                        fatal("allocation attempted in missing tier 2 device.");
                offset -= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR;
-               return mmap(addr, length, prot, flags, fd_tier2, (off_t)offset);
+               proper_fd = fd_tier2;
+       }
+
+       copied = 0;
+       while (count > 0) {
+               ret = pwrite(proper_fd, buf + copied, count, offset + copied);
+               if (ret < 0)
+                       system_error();
+               count -= ret;
+               copied += ret;
        }
-       return mmap(addr, length, prot, flags, fd_main, (off_t)offset);
+
+       free(buf);
 }
 
 /**
- * get_zeroed_blocks - Map and zero contiguous filesystem blocks
+ * get_zeroed_blocks - Return a number of zeroed blocks
  * @bno:       first block number
  * @count:     number of blocks
- *
- * Returns a pointer to the mapped area; the caller must unmap it after use.
  */
-static inline void *get_zeroed_blocks(u64 bno, u64 count)
+static inline void *get_zeroed_blocks(u64 count)
 {
        void *blocks;
-       size_t size = param->blocksize * count;
 
-       if (size / count != param->blocksize)
-               fatal("overflow detected on disk area mapping");
-
-       blocks = apfs_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, bno 
* param->blocksize);
-       if (blocks == MAP_FAILED)
+       blocks = calloc(count, param->blocksize);
+       if (!blocks)
                system_error();
-       memset(blocks, 0, size);
        return blocks;
 }
 
 /**
- * get_zeroed_block - Map and zero a filesystem block
+ * get_zeroed_block - Return a zeroed block
  * @bno: block number
- *
- * Returns a pointer to the mapped block; the caller must unmap it after use.
  */
-static inline void *get_zeroed_block(u64 bno)
+static inline void *get_zeroed_block(void)
 {
-       return get_zeroed_blocks(bno, 1);
+       return get_zeroed_blocks(1);
 }
 
 /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/mkapfs/spaceman.c 
new/apfsprogs-0.2.1/mkapfs/spaceman.c
--- old/apfsprogs-0.2.0/mkapfs/spaceman.c       2024-11-06 23:51:38.000000000 
+0100
+++ new/apfsprogs-0.2.1/mkapfs/spaceman.c       2025-06-02 21:50:22.000000000 
+0200
@@ -102,8 +102,6 @@
        return DIV_ROUND_UP(entry_count * sizeof(__le64) + 
main_dev->cib_addr_base_off, param->blocksize) * param->blocksize;
 }
 
-#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
-
 /**
  * count_used_blocks_in_chunk - Calculate number of allocated blocks in a chunk
  * @dev:       device for the chunk
@@ -190,7 +188,7 @@
        void *bmap = NULL;
 
        dev = &sm_info.dev_info[APFS_SD_MAIN];
-       bmap = get_zeroed_blocks(dev->first_chunk_bmap, dev->used_chunks_end);
+       bmap = get_zeroed_blocks(dev->used_chunks_end);
 
        /* Block zero */
        bmap_mark_as_used(bmap, 0, 1);
@@ -212,7 +210,7 @@
                bmap_mark_as_used(bmap, FUSION_WBC_FIRST_BNO, 1);
        }
 
-       munmap(bmap, dev->used_chunks_end * param->blocksize);
+       apfs_writeall(bmap, dev->used_chunks_end, dev->first_chunk_bmap);
 }
 
 /**
@@ -224,12 +222,12 @@
        void *bmap = NULL;
 
        dev = &sm_info.dev_info[APFS_SD_TIER2];
-       bmap = get_zeroed_blocks(dev->first_chunk_bmap, dev->used_chunks_end);
+       bmap = get_zeroed_blocks(dev->used_chunks_end);
 
        /* Block zero */
        bmap_mark_as_used(bmap, 0, 1);
 
-       munmap(bmap, dev->used_chunks_end * param->blocksize);
+       apfs_writeall(bmap, dev->used_chunks_end, dev->first_chunk_bmap);
 }
 
 /*
@@ -282,7 +280,7 @@
  */
 static u64 make_chunk_info_block(struct device_info *dev, u64 bno, int index, 
u64 start)
 {
-       struct apfs_chunk_info_block *cib = get_zeroed_block(bno);
+       struct apfs_chunk_info_block *cib = get_zeroed_block();
        int i;
 
        cib->cib_index = cpu_to_le32(index);
@@ -296,7 +294,7 @@
        set_object_header(&cib->cib_o, param->blocksize, bno,
                          APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_SPACEMAN_CIB,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(cib, param->blocksize);
+       apfs_writeall(cib, 1, bno);
 
        return start;
 }
@@ -312,7 +310,7 @@
  */
 static u64 make_cib_addr_block(struct device_info *dev, u64 bno, int index, 
u64 start)
 {
-       struct apfs_cib_addr_block *cab = get_zeroed_block(bno);
+       struct apfs_cib_addr_block *cab = get_zeroed_block();
        int i;
 
        cab->cab_index = cpu_to_le32(index);
@@ -333,7 +331,7 @@
        set_object_header(&cab->cab_o, param->blocksize, bno,
                          APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_SPACEMAN_CAB,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(cab, param->blocksize);
+       apfs_writeall(cab, 1, bno);
 
        return start;
 }
@@ -429,7 +427,7 @@
  */
 static void make_ip_bitmap(void)
 {
-       void *bmap = get_zeroed_blocks(IP_BMAP_BASE, sm_info.ip_bm_size);
+       void *bmap = get_zeroed_blocks(sm_info.ip_bm_size);
        struct device_info *main_dev = NULL, *tier2_dev = NULL;
 
        main_dev = &sm_info.dev_info[APFS_SD_MAIN];
@@ -445,7 +443,7 @@
        bmap_mark_as_used(bmap, main_dev->first_chunk_bmap - sm_info.ip_base, 
main_dev->used_chunks_end);
        bmap_mark_as_used(bmap, tier2_dev->first_chunk_bmap - sm_info.ip_base, 
tier2_dev->used_chunks_end);
 
-       munmap(bmap, param->blocksize);
+       apfs_writeall(bmap, sm_info.ip_bm_size, IP_BMAP_BASE);
 }
 
 /**
@@ -489,7 +487,7 @@
        sm->sm_ip_bm_block_count = cpu_to_le32(sm_info.ip_bmap_blocks);
        sm->sm_ip_bm_base = cpu_to_le64(IP_BMAP_BASE);
        for (i = 0; i < sm_info.ip_bmap_blocks; ++i)
-               munmap(get_zeroed_block(IP_BMAP_BASE + i), param->blocksize);
+               apfs_writeall(get_zeroed_block(), 1, IP_BMAP_BASE + i);
 
        /* The current bitmaps are the first in the ring */
        sm->sm_ip_bitmap_offset = cpu_to_le32(sm_info.bm_addr_off);
@@ -594,7 +592,7 @@
 {
        struct apfs_spaceman_phys *sm = NULL;
 
-       sm = get_zeroed_blocks(bno, spaceman_size() / param->blocksize);
+       sm = get_zeroed_blocks(spaceman_size() / param->blocksize);
 
        sm->sm_block_size = cpu_to_le32(param->blocksize);
        sm->sm_blocks_per_chunk = cpu_to_le32(blocks_per_chunk());
@@ -614,5 +612,5 @@
        set_object_header(&sm->sm_o, spaceman_size(), oid,
                          APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_SPACEMAN,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(sm, spaceman_size());
+       apfs_writeall(sm, spaceman_size() / param->blocksize, bno);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/apfsprogs-0.2.0/mkapfs/super.c 
new/apfsprogs-0.2.1/mkapfs/super.c
--- old/apfsprogs-0.2.0/mkapfs/super.c  2024-11-06 23:51:38.000000000 +0100
+++ new/apfsprogs-0.2.1/mkapfs/super.c  2025-06-02 21:50:22.000000000 +0200
@@ -54,8 +54,8 @@
        void *block;
 
        for (bno = start; bno < start + blocks; ++bno) {
-               block = get_zeroed_block(bno);
-               munmap(block, param->blocksize);
+               block = get_zeroed_block();
+               apfs_writeall(block, 1, bno);
        }
 }
 
@@ -143,7 +143,7 @@
  */
 static void make_volume(u64 bno, u64 oid)
 {
-       struct apfs_superblock *vsb = get_zeroed_block(bno);
+       struct apfs_superblock *vsb = get_zeroed_block();
 
        vsb->apfs_magic = cpu_to_le32(APFS_MAGIC);
 
@@ -201,7 +201,7 @@
        set_object_header(&vsb->apfs_o, param->blocksize, oid,
                          APFS_OBJ_VIRTUAL | APFS_OBJECT_TYPE_FS,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(vsb, param->blocksize);
+       apfs_writeall(vsb, 1, bno);
 }
 
 /**
@@ -210,7 +210,7 @@
  */
 static void make_cpoint_map_block(u64 bno)
 {
-       struct apfs_checkpoint_map_phys *block = get_zeroed_block(bno);
+       struct apfs_checkpoint_map_phys *block = get_zeroed_block();
        struct apfs_checkpoint_mapping *map;
        int idx = 0;
 
@@ -275,7 +275,7 @@
        set_object_header(&block->cpm_o, param->blocksize, bno,
                          APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_CHECKPOINT_MAP,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(block, param->blocksize);
+       apfs_writeall(block, 1, bno);
 }
 
 /**
@@ -288,10 +288,10 @@
  */
 static void make_cpoint_superblock(u64 bno, struct apfs_nx_superblock *sb_copy)
 {
-       struct apfs_nx_superblock *sb = get_zeroed_block(bno);
+       struct apfs_nx_superblock *sb = get_zeroed_block();
 
        memcpy(sb, sb_copy, sizeof(*sb));
-       munmap(sb, param->blocksize);
+       apfs_writeall(sb, 1, bno);
 }
 
 /**
@@ -302,7 +302,7 @@
 {
        struct apfs_nx_superblock *tier2_sb = NULL;
 
-       tier2_sb = get_zeroed_block(APFS_FUSION_TIER2_DEVICE_BYTE_ADDR / 
param->blocksize);
+       tier2_sb = get_zeroed_block();
        memcpy(tier2_sb, sb, sizeof(*sb));
 
        /* The top bit is used to tell apart the main and tier 2 devices */
@@ -312,7 +312,7 @@
        set_object_header(&tier2_sb->nx_o, param->blocksize, 
APFS_OID_NX_SUPERBLOCK,
                          APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_SUPERBLOCK,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(tier2_sb, param->blocksize);
+       apfs_writeall(tier2_sb, 1, APFS_FUSION_TIER2_DEVICE_BYTE_ADDR / 
param->blocksize);
 }
 
 /**
@@ -322,7 +322,7 @@
  */
 static void make_empty_reaper(u64 bno, u64 oid)
 {
-       struct apfs_nx_reaper_phys *reaper = get_zeroed_block(bno);
+       struct apfs_nx_reaper_phys *reaper = get_zeroed_block();
 
        reaper->nr_next_reap_id = cpu_to_le64(1);
        reaper->nr_flags = cpu_to_le32(APFS_NR_BHM_FLAG);
@@ -332,7 +332,7 @@
        set_object_header(&reaper->nr_o, param->blocksize, oid,
                          APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_REAPER,
                          APFS_OBJECT_TYPE_INVALID);
-       munmap(reaper, param->blocksize);
+       apfs_writeall(reaper, 1, bno);
 }
 
 /**
@@ -342,12 +342,12 @@
  */
 static void make_empty_fusion_wbc_state(u64 bno, u64 oid)
 {
-       struct apfs_fusion_wbc_phys *wbc = get_zeroed_block(bno);
+       struct apfs_fusion_wbc_phys *wbc = get_zeroed_block();
 
        wbc->fwp_version = cpu_to_le64(0x70);
 
        set_object_header(&wbc->fwp_objHdr, param->blocksize, oid, 
APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_FUSION_WBC, APFS_OBJECT_TYPE_INVALID);
-       munmap(wbc, param->blocksize);
+       apfs_writeall(wbc, 1, bno);
 }
 
 /**
@@ -381,7 +381,7 @@
        struct apfs_nx_superblock *sb_copy = NULL;
        u64 size = param->blocksize * param->block_count;
 
-       sb_copy = get_zeroed_block(APFS_NX_BLOCK_NUM);
+       sb_copy = get_zeroed_block();
 
        sb_copy->nx_magic = cpu_to_le32(APFS_NX_MAGIC);
        sb_copy->nx_block_size = cpu_to_le32(param->blocksize);
@@ -448,5 +448,5 @@
        if (fd_tier2 != -1)
                make_tier2_superblock(sb_copy);
 
-       munmap(sb_copy, param->blocksize);
+       apfs_writeall(sb_copy, 1, APFS_NX_BLOCK_NUM);
 }

Reply via email to