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(¤t->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); }