From 9cf62102891827c2271de8b760c611689b2f6a10 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 09:25:56 +0000 Subject: [PATCH] Update APFS driver to v0.3.12-1 --- 8001-Add-APFS-driver.patch | 540 ++++++++++++++++++++++++------------- apfs_ver | 4 +- 2 files changed, 358 insertions(+), 186 deletions(-) diff --git a/8001-Add-APFS-driver.patch b/8001-Add-APFS-driver.patch index 665f2f4..bb96208 100644 --- a/8001-Add-APFS-driver.patch +++ b/8001-Add-APFS-driver.patch @@ -1,19 +1,19 @@ -From a168912db08f2921566367b1adf394e4c4a5f8f9 Mon Sep 17 00:00:00 2001 +From 6a7ae98b3893fff335a5e0c0e4442af065a60a07 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> -Date: Sat, 5 Oct 2024 18:42:54 +0000 +Date: Sat, 2 Nov 2024 09:25:54 +0000 Subject: [PATCH] Add APFS driver --- fs/apfs/Makefile | 23 + - fs/apfs/apfs.h | 1185 ++++++++++ + fs/apfs/apfs.h | 1191 ++++++++++ fs/apfs/apfs_raw.h | 1562 +++++++++++++ fs/apfs/btree.c | 1174 ++++++++++ fs/apfs/compress.c | 474 ++++ fs/apfs/dir.c | 1544 +++++++++++++ fs/apfs/extents.c | 2390 ++++++++++++++++++++ fs/apfs/file.c | 220 ++ - fs/apfs/inode.c | 2569 ++++++++++++++++++++++ + fs/apfs/inode.c | 2597 ++++++++++++++++++++++ fs/apfs/key.c | 334 +++ fs/apfs/libzbitmap.c | 442 ++++ fs/apfs/libzbitmap.h | 31 + @@ -37,16 +37,16 @@ Subject: [PATCH] Add APFS driver fs/apfs/node.c | 2069 ++++++++++++++++++ fs/apfs/object.c | 315 +++ fs/apfs/snapshot.c | 681 ++++++ - fs/apfs/spaceman.c | 1305 +++++++++++ - fs/apfs/super.c | 1946 +++++++++++++++++ + fs/apfs/spaceman.c | 1433 ++++++++++++ + fs/apfs/super.c | 1955 +++++++++++++++++ fs/apfs/symlink.c | 80 + - fs/apfs/transaction.c | 987 +++++++++ + fs/apfs/transaction.c | 988 +++++++++ fs/apfs/unicode.c | 3156 +++++++++++++++++++++++++++ fs/apfs/unicode.h | 27 + fs/apfs/version.h | 1 + fs/apfs/xattr.c | 922 ++++++++ fs/apfs/xfield.c | 171 ++ - 41 files changed, 29177 insertions(+) + 41 files changed, 29349 insertions(+) create mode 100644 fs/apfs/Makefile create mode 100644 fs/apfs/apfs.h create mode 100644 fs/apfs/apfs_raw.h @@ -120,10 +120,10 @@ index 000000000..ab4c49d55 + make -C $(KERNEL_DIR) M=$(PWD) clean diff --git a/fs/apfs/apfs.h b/fs/apfs/apfs.h new file mode 100644 -index 000000000..9ffa9f11b +index 000000000..44d6905f9 --- /dev/null +++ b/fs/apfs/apfs.h -@@ -0,0 +1,1185 @@ +@@ -0,0 +1,1191 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2018 Ernesto A. Fernández @@ -243,6 +243,11 @@ index 000000000..9ffa9f11b + return (node->flags & APFS_BTNODE_FIXED_KV_SIZE) != 0; +} + ++struct apfs_ip_bitmap_block_info { ++ bool dirty; /* Do we need to commit this block to disk? */ ++ void *block; /* In-memory address of the bitmap block */ ++}; ++ +/* + * Space manager data in memory. + */ @@ -273,7 +278,7 @@ index 000000000..9ffa9f11b + /* Number of ip bitmaps */ + u32 sm_ip_bmaps_count; + /* List of ip bitmaps, in order */ -+ struct buffer_head *sm_ip_bmaps[]; ++ struct apfs_ip_bitmap_block_info sm_ip_bmaps[]; +}; + +#define APFS_TRANS_MAIN_QUEUE_MAX 10000 @@ -1192,6 +1197,7 @@ index 000000000..9ffa9f11b +extern int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count); +extern int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count); +extern int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backwards); ++extern int apfs_write_ip_bitmaps(struct super_block *sb); + +/* super.c */ +extern int apfs_map_volume_super_bno(struct super_block *sb, u64 bno, bool check); @@ -8711,10 +8717,10 @@ index 000000000..c37d59a02 +}; diff --git a/fs/apfs/inode.c b/fs/apfs/inode.c new file mode 100644 -index 000000000..71a1dca38 +index 000000000..96129a452 --- /dev/null +++ b/fs/apfs/inode.c -@@ -0,0 +1,2569 @@ +@@ -0,0 +1,2597 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 Ernesto A. Fernández @@ -9282,7 +9288,11 @@ index 000000000..71a1dca38 + } + } + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++ err = __block_write_begin(page_folio(page), pos, len, apfs_get_new_block); ++#else + err = __block_write_begin(page, pos, len, apfs_get_new_block); ++#endif + if (err) { + apfs_err(sb, "CoW failed in inode 0x%llx", apfs_ino(inode)); + goto out_put_page; @@ -9297,7 +9307,11 @@ index 000000000..71a1dca38 + return err; +} + -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++static int apfs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned int len, ++ struct folio **foliop, void **fsdata) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) +static int apfs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, + struct page **pagep, void **fsdata) @@ -9309,6 +9323,10 @@ index 000000000..71a1dca38 +{ + struct inode *inode = mapping->host; + struct super_block *sb = inode->i_sb; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++ struct page *page = NULL; ++ struct page **pagep = &page; ++#endif + int blkcount = (len + sb->s_blocksize - 1) >> inode->i_blkbits; + struct apfs_max_ops maxops; + int err; @@ -9332,6 +9350,9 @@ index 000000000..71a1dca38 + err = __apfs_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + if (err) + goto fail; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++ *foliop = page_folio(page); ++#endif + return 0; + +fail: @@ -9345,7 +9366,11 @@ index 000000000..71a1dca38 + struct apfs_dstream_info *dstream = &APFS_I(inode)->i_dstream; + int ret, err; + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++ ret = generic_write_end(file, mapping, pos, len, copied, page_folio(page), fsdata); ++#else + ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); ++#endif + dstream->ds_size = i_size_read(inode); + if (ret < len && pos + len > inode->i_size) { + truncate_pagecache(inode, inode->i_size); @@ -9358,13 +9383,22 @@ index 000000000..71a1dca38 + return ret; +} + ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++static int apfs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned int len, unsigned int copied, ++ struct folio *folio, void *fsdata) ++#else +static int apfs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct page *page, void *fsdata) ++#endif +{ + struct inode *inode = mapping->host; + struct super_block *sb = inode->i_sb; + struct apfs_nx_transaction *trans = &APFS_NXI(sb)->nx_transaction; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) ++ struct page *page = &folio->page; ++#endif + int ret, err; + + ret = __apfs_write_end(file, mapping, pos, len, copied, page, fsdata); @@ -9410,9 +9444,9 @@ index 000000000..71a1dca38 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) -+ .readahead = apfs_readahead, ++ .readahead = apfs_readahead, +#else -+ .readpages = apfs_readpages, ++ .readpages = apfs_readpages, +#endif + + .write_begin = apfs_write_begin, @@ -20865,10 +20899,10 @@ index 000000000..c9de8a155 +} diff --git a/fs/apfs/spaceman.c b/fs/apfs/spaceman.c new file mode 100644 -index 000000000..b8d4b05f5 +index 000000000..7667f8733 --- /dev/null +++ b/fs/apfs/spaceman.c -@@ -0,0 +1,1305 @@ +@@ -0,0 +1,1433 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 Ernesto A. Fernández @@ -21006,84 +21040,147 @@ index 000000000..b8d4b05f5 +} + +/** -+ * apfs_ip_bm_is_free - Check if a given ip bitmap is in the free range -+ * @sm: on-disk spaceman structure -+ * @index: offset in the ring buffer of the bitmap block to check ++ * apfs_allocate_ip_bitmap - Allocate a free ip bitmap block ++ * @sb: filesystem superblock ++ * @offset_p: on return, the offset from sm_ip_bm_base of the allocated block ++ * ++ * Returns 0 on success or a negative error code in case of failure. + */ -+static bool apfs_ip_bm_is_free(struct apfs_spaceman_phys *sm, u16 index) ++static int apfs_allocate_ip_bitmap(struct super_block *sb, u16 *offset_p) +{ -+ u16 free_head = le16_to_cpu(sm->sm_ip_bm_free_head); -+ u16 free_tail = le16_to_cpu(sm->sm_ip_bm_free_tail); -+ u16 free_len, index_in_free; -+ u16 bmap_count = le32_to_cpu(sm->sm_ip_bm_block_count); ++ struct apfs_spaceman *spaceman = NULL; ++ struct apfs_spaceman_phys *sm_raw = NULL; ++ u32 free_next_offset, old_head_off; ++ u16 free_head, blkcnt; ++ __le16 *old_head_p = NULL; ++ ++ spaceman = APFS_SM(sb); ++ sm_raw = spaceman->sm_raw; ++ free_next_offset = le32_to_cpu(sm_raw->sm_ip_bm_free_next_offset); ++ free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head); ++ blkcnt = (u16)le32_to_cpu(sm_raw->sm_ip_bm_block_count); + -+ free_len = (bmap_count + free_tail - free_head) % bmap_count; -+ index_in_free = (bmap_count + index - free_head) % bmap_count; ++ /* ++ * The "free_next" array is a linked list of free blocks that starts ++ * with the "free_head". Allocate this head then, and make the next ++ * block into the new head. ++ */ ++ old_head_off = free_next_offset + free_head * sizeof(*old_head_p); ++ old_head_p = apfs_spaceman_get_16(sb, old_head_off); ++ if (!old_head_p) { ++ apfs_err(sb, "free_next head offset out of bounds (%u)", old_head_off); ++ return -EFSCORRUPTED; ++ } ++ *offset_p = free_head; ++ free_head = le16_to_cpup(old_head_p); ++ sm_raw->sm_ip_bm_free_head = *old_head_p; ++ /* No longer free, no longer part of the linked list */ ++ *old_head_p = cpu_to_le16(APFS_SPACEMAN_IP_BM_INDEX_INVALID); ++ ++ /* Just a little sanity check because I've messed this up before */ ++ if (free_head >= blkcnt || *offset_p >= blkcnt) { ++ apfs_err(sb, "free next list seems empty or corrupt"); ++ return -EFSCORRUPTED; ++ } + -+ return index_in_free < free_len; ++ return 0; +} + +/** -+ * apfs_update_ip_bm_free_next - Update free_next for the internal pool -+ * @sb: superblock structure ++ * apfs_free_ip_bitmap - Free a used ip bitmap block ++ * @sb: filesystem superblock ++ * @offset: the offset from sm_ip_bm_base of the block to free + * -+ * Uses the head and tail reported by the on-disk spaceman structure. Returns 0 -+ * on success, or -EFSCORRUPTED if corruption is detected. ++ * Returns 0 on success or a negative error code in case of failure. + */ -+static int apfs_update_ip_bm_free_next(struct super_block *sb) ++static int apfs_free_ip_bitmap(struct super_block *sb, u16 offset) +{ -+ struct apfs_spaceman *spaceman = APFS_SM(sb); -+ struct apfs_spaceman_phys *raw = spaceman->sm_raw; -+ u32 free_next_off = le32_to_cpu(raw->sm_ip_bm_free_next_offset); -+ int bmap_count = le32_to_cpu(raw->sm_ip_bm_block_count); /* TODO: this can overflow! */ -+ __le16 *free_next; -+ int i; ++ struct apfs_spaceman *spaceman = NULL; ++ struct apfs_spaceman_phys *sm_raw = NULL; ++ u32 free_next_offset, old_tail_off; ++ u16 free_tail; ++ __le16 *old_tail_p = NULL; ++ ++ spaceman = APFS_SM(sb); ++ sm_raw = spaceman->sm_raw; ++ free_next_offset = le32_to_cpu(sm_raw->sm_ip_bm_free_next_offset); ++ free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail); + -+ if (free_next_off > spaceman->sm_size) { -+ apfs_err(sb, "offset out of bounds (%u)", free_next_off); -+ return -EFSCORRUPTED; -+ } -+ if (free_next_off + bmap_count * sizeof(*free_next) > spaceman->sm_size) { -+ apfs_err(sb, "free next out of bounds (%u-%u)", free_next_off, bmap_count * (u32)sizeof(*free_next)); ++ /* ++ * The "free_next" array is a linked list of free blocks that ends ++ * with the "free_tail". The block getting freed will become the new ++ * tail of the list. ++ */ ++ old_tail_off = free_next_offset + free_tail * sizeof(*old_tail_p); ++ old_tail_p = apfs_spaceman_get_16(sb, old_tail_off); ++ if (!old_tail_p) { ++ apfs_err(sb, "free_next tail offset out of bounds (%u)", old_tail_off); + return -EFSCORRUPTED; + } -+ free_next = (void *)raw + free_next_off; ++ *old_tail_p = cpu_to_le16(offset); ++ sm_raw->sm_ip_bm_free_tail = cpu_to_le16(offset); ++ free_tail = offset; + -+ for (i = 0; i < bmap_count; ++i) { -+ if (apfs_ip_bm_is_free(raw, i)) -+ free_next[i] = cpu_to_le16((1 + i) % bmap_count); -+ else -+ free_next[i] = cpu_to_le16(0xFFFF); ++ return 0; ++} ++ ++/** ++ * apfs_reallocate_ip_bitmap - Find a new block for an ip bitmap ++ * @sb: filesystem superblock ++ * @offset_p: the offset from sm_ip_bm_base of the block to free ++ * ++ * On success returns 0 and updates @offset_p to the new offset allocated for ++ * the ip bitmap. Since blocks are allocated at the head of the list and freed ++ * at the tail, there is no risk of reuse by future reallocations within the ++ * same transaction (under there is some serious corruption, of course). ++ * ++ * Returns a negative error code in case of failure. ++ */ ++static int apfs_reallocate_ip_bitmap(struct super_block *sb, __le16 *offset_p) ++{ ++ int err; ++ u16 offset; ++ ++ offset = le16_to_cpup(offset_p); ++ ++ err = apfs_free_ip_bitmap(sb, offset); ++ if (err) { ++ apfs_err(sb, "failed to free ip bitmap %u", offset); ++ return err; ++ } ++ err = apfs_allocate_ip_bitmap(sb, &offset); ++ if (err) { ++ apfs_err(sb, "failed to allocate a new ip bitmap block"); ++ return err; + } ++ ++ *offset_p = cpu_to_le16(offset); + return 0; +} + +/** -+ * apfs_rotate_single_ip_bitmap - Reallocate an ip bmap in the circular buffer ++ * apfs_write_single_ip_bitmap - Write a single ip bitmap to disk + * @sb: filesystem superblock -+ * @idx: index of the ip bitmap to reallocate ++ * @bitmap: bitmap to write ++ * @idx: index of the ip bitmap to write + * + * Returns 0 on success or a negative error code in case of failure. + */ -+static int apfs_rotate_single_ip_bitmap(struct super_block *sb, u32 idx) ++static int apfs_write_single_ip_bitmap(struct super_block *sb, char *bitmap, u32 idx) +{ + struct apfs_nxsb_info *nxi = APFS_NXI(sb); + struct apfs_spaceman *spaceman = APFS_SM(sb); + struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw; -+ struct buffer_head *old_bh = NULL, *new_bh = NULL; -+ u64 ring_base; -+ u32 ring_length; ++ struct buffer_head *bh = NULL; ++ u64 ip_bm_base, ip_bitmap_bno; + u32 xid_off, ip_bitmap_off; -+ u64 ip_bitmap_bno; -+ u16 free_head; + __le64 *xid_p = NULL; + __le16 *ip_bitmap_p = NULL; + int err; + -+ ring_base = le64_to_cpu(sm_raw->sm_ip_bm_base); -+ ring_length = le32_to_cpu(sm_raw->sm_ip_bm_block_count); -+ free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head); ++ ip_bm_base = le64_to_cpu(sm_raw->sm_ip_bm_base); + ++ /* First update the xid, which is kept in a separate array */ + xid_off = le32_to_cpu(sm_raw->sm_ip_bm_xid_offset) + idx * sizeof(*xid_p); + xid_p = apfs_spaceman_get_64(sb, xid_off); + if (!xid_p) { @@ -21092,99 +21189,144 @@ index 000000000..b8d4b05f5 + } + *xid_p = cpu_to_le64(nxi->nx_xid); + ++ /* Now get find new location for the ip bitmap (and free the old one) */ + ip_bitmap_off = le32_to_cpu(sm_raw->sm_ip_bitmap_offset) + idx * sizeof(*ip_bitmap_p); + ip_bitmap_p = apfs_spaceman_get_16(sb, ip_bitmap_off); + if (!ip_bitmap_p) { + apfs_err(sb, "bmap offset out of bounds (%u)", ip_bitmap_off); + return -EFSCORRUPTED; + } -+ -+ ip_bitmap_bno = ring_base + le16_to_cpup(ip_bitmap_p); -+ old_bh = apfs_sb_bread(sb, ip_bitmap_bno); -+ if (!old_bh) { -+ apfs_err(sb, "failed to read current ip bitmap (0x%llx)", ip_bitmap_bno); -+ return -EIO; ++ err = apfs_reallocate_ip_bitmap(sb, ip_bitmap_p); ++ if (err) { ++ apfs_err(sb, "failed to reallocate ip bitmap %u", le16_to_cpup(ip_bitmap_p)); ++ return err; + } + -+ *ip_bitmap_p = cpu_to_le16(free_head); -+ free_head = (free_head + 1) % ring_length; -+ sm_raw->sm_ip_bm_free_head = cpu_to_le16(free_head); -+ -+ ip_bitmap_bno = ring_base + le16_to_cpup(ip_bitmap_p); -+ new_bh = apfs_getblk(sb, ip_bitmap_bno); -+ if (!new_bh) { ++ /* Finally, write the dirty bitmap to the new location */ ++ ip_bitmap_bno = ip_bm_base + le16_to_cpup(ip_bitmap_p); ++ bh = apfs_getblk(sb, ip_bitmap_bno); ++ if (!bh) { + apfs_err(sb, "failed to map block for CoW (0x%llx)", ip_bitmap_bno); -+ err = -EIO; -+ goto out; ++ return -EIO; + } -+ memcpy(new_bh->b_data, old_bh->b_data, sb->s_blocksize); -+ err = apfs_transaction_join(sb, new_bh); ++ memcpy(bh->b_data, bitmap, sb->s_blocksize); ++ err = apfs_transaction_join(sb, bh); + if (err) -+ goto out; -+ spaceman->sm_ip_bmaps[idx] = new_bh; ++ goto fail; ++ bh = NULL; + -+out: -+ brelse(old_bh); -+ if (err) -+ brelse(new_bh); ++ spaceman->sm_ip_bmaps[idx].dirty = false; ++ return 0; ++ ++fail: ++ brelse(bh); ++ bh = NULL; + return err; +} + +/** -+ * apfs_rotate_ip_bitmaps - Allocate new ip bitmaps from the circular buffer ++ * apfs_write_ip_bitmaps - Write all dirty ip bitmaps to disk + * @sb: superblock structure + * -+ * Allocates bitmaps for the whole internal pool at once, meaning that each -+ * transaction is forced to allocate one bitmap for every ~1.32 TiB of container -+ * size, even if they won't be needed. This seems very reasonable to me, but the -+ * official implementation avoids it and they may have a good reason. -+ * + * Returns 0 on success or a negative error code in case of failure. + */ -+static int apfs_rotate_ip_bitmaps(struct super_block *sb) ++int apfs_write_ip_bitmaps(struct super_block *sb) +{ + struct apfs_spaceman *spaceman = APFS_SM(sb); + struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw; -+ u32 ring_length = le32_to_cpu(sm_raw->sm_ip_bm_block_count); ++ struct apfs_ip_bitmap_block_info *info = NULL; + u32 bmaps_count = spaceman->sm_ip_bmaps_count; -+ u16 free_head, free_tail, free_len; + int err; + u32 i; + + apfs_assert_in_transaction(sb, &sm_raw->sm_o); + -+ free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head); -+ free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail); -+ -+ /* -+ * Check that we have enough room before doing anything. If we run out -+ * I may need to compact the ring using the blocks marked as 0xFFFF in -+ * ip_bm_free_next (TODO). -+ */ -+ free_len = (ring_length + free_tail - free_head) % ring_length; -+ if (free_len < bmaps_count) { -+ apfs_alert(sb, "full ip bitmap ring (%u < %u)", free_len, bmaps_count); -+ return -ENOSPC; -+ } -+ + for (i = 0; i < bmaps_count; ++i) { -+ err = apfs_rotate_single_ip_bitmap(sb, i); ++ info = &spaceman->sm_ip_bmaps[i]; ++ if (!info->dirty) ++ continue; ++ err = apfs_write_single_ip_bitmap(sb, info->block, i); + if (err) { + apfs_err(sb, "failed to rotate ip bitmap %u", i); + return err; + } + } ++ return 0; ++} + -+ /* All bitmaps have been reallocated, so just free the same number */ -+ free_tail = (free_tail + bmaps_count) % ring_length; -+ sm_raw->sm_ip_bm_free_tail = cpu_to_le16(free_tail); ++/** ++* apfs_read_single_ip_bitmap - Read a single ip bitmap to memory ++* @sb: filesystem superblock ++* @idx: index of the ip bitmap to read ++* ++* Returns 0 on success or a negative error code in case of failure. ++*/ ++static int apfs_read_single_ip_bitmap(struct super_block *sb, u32 idx) ++{ ++ struct apfs_spaceman *spaceman = APFS_SM(sb); ++ struct apfs_spaceman_phys *sm_raw = spaceman->sm_raw; ++ struct buffer_head *bh = NULL; ++ char *bitmap = NULL; ++ u64 ip_bm_base, ip_bitmap_bno; ++ u32 ip_bitmap_off; ++ __le16 *ip_bitmap_p = NULL; ++ int err; + -+ err = apfs_update_ip_bm_free_next(sb); -+ if (err) { -+ apfs_err(sb, "failed to update bitmap ring"); -+ return err; ++ ip_bm_base = le64_to_cpu(sm_raw->sm_ip_bm_base); ++ ++ ip_bitmap_off = le32_to_cpu(sm_raw->sm_ip_bitmap_offset) + idx * sizeof(*ip_bitmap_p); ++ ip_bitmap_p = apfs_spaceman_get_16(sb, ip_bitmap_off); ++ if (!ip_bitmap_p) { ++ apfs_err(sb, "bmap offset out of bounds (%u)", ip_bitmap_off); ++ return -EFSCORRUPTED; + } + ++ bitmap = kmalloc(sb->s_blocksize, GFP_KERNEL); ++ if (!bitmap) ++ return -ENOMEM; ++ ++ ip_bitmap_bno = ip_bm_base + le16_to_cpup(ip_bitmap_p); ++ bh = apfs_sb_bread(sb, ip_bitmap_bno); ++ if (!bh) { ++ apfs_err(sb, "failed to read ip bitmap (0x%llx)", ip_bitmap_bno); ++ err = -EIO; ++ goto fail; ++ } ++ memcpy(bitmap, bh->b_data, sb->s_blocksize); ++ brelse(bh); ++ bh = NULL; ++ ++ spaceman->sm_ip_bmaps[idx].dirty = false; ++ spaceman->sm_ip_bmaps[idx].block = bitmap; ++ bitmap = NULL; ++ return 0; ++ ++fail: ++ kfree(bitmap); ++ bitmap = NULL; ++ return err; ++} ++ ++/** ++ * apfs_read_ip_bitmaps - Read all the ip bitmaps to memory ++ * @sb: superblock structure ++ * ++ * Returns 0 on success or a negative error code in case of failure. ++ */ ++static int apfs_read_ip_bitmaps(struct super_block *sb) ++{ ++ struct apfs_spaceman *spaceman = APFS_SM(sb); ++ u32 bmaps_count = spaceman->sm_ip_bmaps_count; ++ int err; ++ u32 i; ++ ++ for (i = 0; i < bmaps_count; ++i) { ++ err = apfs_read_single_ip_bitmap(sb, i); ++ if (err) { ++ apfs_err(sb, "failed to read ip bitmap %u", i); ++ return err; ++ } ++ } + return 0; +} + @@ -21254,11 +21396,12 @@ index 000000000..b8d4b05f5 +{ + struct apfs_spaceman *sm = APFS_SM(sb); + struct apfs_spaceman_phys *sm_raw = sm->sm_raw; -+ struct buffer_head *bmap_bh = NULL; ++ struct apfs_ip_bitmap_block_info *info = NULL; + + bno -= le64_to_cpu(sm_raw->sm_ip_base); -+ bmap_bh = sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift]; -+ __clear_bit_le(bno & sm->sm_ip_bmaps_mask, bmap_bh->b_data); ++ info = &sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift]; ++ __clear_bit_le(bno & sm->sm_ip_bmaps_mask, info->block); ++ info->dirty = true; + + return 0; +} @@ -21431,22 +21574,30 @@ index 000000000..b8d4b05f5 +/** + * apfs_allocate_spaceman - Allocate an in-memory spaceman struct, if needed + * @sb: superblock structure -+ * @bmap_cnt: internal pool bitmap count ++ * @raw: on-disk spaceman struct ++ * @size: size of the on-disk spaceman + * -+ * Returns the spaceman and sets it in the superblock info. Also sets the fixed -+ * information about the ip bitmap count. On failure, returns an error pointer. ++ * Returns the spaceman and sets it in the superblock info. Also performs all ++ * initializations for the internal pool, including reading all the ip bitmaps. ++ * This is a bit out of place here, but it's convenient because it has to ++ * happen only once. ++ * ++ * On failure, returns an error pointer. + */ -+static struct apfs_spaceman *apfs_allocate_spaceman(struct super_block *sb, u32 bmap_cnt) ++static struct apfs_spaceman *apfs_allocate_spaceman(struct super_block *sb, struct apfs_spaceman_phys *raw, u32 size) +{ + struct apfs_nxsb_info *nxi = APFS_NXI(sb); + struct apfs_spaceman *spaceman = NULL; + int blk_bitcnt = sb->s_blocksize * 8; + size_t sm_size; ++ u32 bmap_cnt; ++ int err; + + if (nxi->nx_spaceman) + return nxi->nx_spaceman; + + /* We don't expect filesystems this big, it would be like 260 TiB */ ++ bmap_cnt = le32_to_cpu(raw->sm_ip_bm_size_in_blocks); + if (bmap_cnt > 200) { + apfs_err(sb, "too many ip bitmap blocks (%u)", bmap_cnt); + return ERR_PTR(-EFSCORRUPTED); @@ -21457,11 +21608,28 @@ index 000000000..b8d4b05f5 + if (!spaceman) + return ERR_PTR(-ENOMEM); + spaceman->sm_nxi = nxi; ++ /* ++ * These two fields must be set before reading the ip bitmaps, since ++ * that stuff involves several variable-length arrays inside the ++ * spaceman object itself. ++ */ ++ spaceman->sm_raw = raw; ++ spaceman->sm_size = size; + + spaceman->sm_ip_bmaps_count = bmap_cnt; + spaceman->sm_ip_bmaps_mask = blk_bitcnt - 1; + spaceman->sm_ip_bmaps_shift = order_base_2(blk_bitcnt); -+ return spaceman; ++ ++ /* This must happen only once, so it's easier to just leave it here */ ++ err = apfs_read_ip_bitmaps(sb); ++ if (err) { ++ apfs_err(sb, "failed to read the ip bitmaps"); ++ kfree(spaceman); ++ nxi->nx_spaceman = spaceman = NULL; ++ return ERR_PTR(err); ++ } ++ ++ return nxi->nx_spaceman; +} + +/** @@ -21494,14 +21662,12 @@ index 000000000..b8d4b05f5 + sm_raw = (struct apfs_spaceman_phys *)sm_eph_info->object; + sm_raw->sm_o.o_xid = cpu_to_le64(nxi->nx_xid); + -+ spaceman = apfs_allocate_spaceman(sb, le32_to_cpu(sm_raw->sm_ip_bm_size_in_blocks)); ++ spaceman = apfs_allocate_spaceman(sb, sm_raw, sm_eph_info->size); + if (IS_ERR(spaceman)) { + apfs_err(sb, "failed to allocate spaceman"); + err = PTR_ERR(spaceman); + goto fail; + } -+ spaceman->sm_raw = sm_raw; -+ spaceman->sm_size = sm_eph_info->size; + + spaceman->sm_free_cache_base = spaceman->sm_free_cache_blkcnt = 0; + @@ -21526,11 +21692,6 @@ index 000000000..b8d4b05f5 + goto fail; + } + -+ err = apfs_rotate_ip_bitmaps(sb); -+ if (err) { -+ apfs_err(sb, "failed to rotate ip bitmaps"); -+ goto fail; -+ } + err = apfs_flush_free_queue(sb, APFS_SFQ_IP, false /* force */); + if (err) { + apfs_err(sb, "failed to flush ip fq"); @@ -21582,7 +21743,7 @@ index 000000000..b8d4b05f5 + u32 i; + + for (i = 0; i < sm->sm_ip_bmaps_count; ++i) { -+ char *bitmap = sm->sm_ip_bmaps[i]->b_data; ++ char *bitmap = sm->sm_ip_bmaps[i].block; + u64 off_in_bmap_blk, off_in_ip; + + off_in_bmap_blk = find_next_zero_bit_le(bitmap, blk_bitcnt, 0 /* offset */); @@ -21627,11 +21788,12 @@ index 000000000..b8d4b05f5 +{ + struct apfs_spaceman *sm = APFS_SM(sb); + struct apfs_spaceman_phys *sm_raw = sm->sm_raw; -+ struct buffer_head *bmap_bh = NULL; ++ struct apfs_ip_bitmap_block_info *info = NULL; + + bno -= le64_to_cpu(sm_raw->sm_ip_base); -+ bmap_bh = sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift]; -+ __set_bit_le(bno & sm->sm_ip_bmaps_mask, bmap_bh->b_data); ++ info = &sm->sm_ip_bmaps[bno >> sm->sm_ip_bmaps_shift]; ++ __set_bit_le(bno & sm->sm_ip_bmaps_mask, info->block); ++ info->dirty = true; +} + +/** @@ -22176,10 +22338,10 @@ index 000000000..b8d4b05f5 +} diff --git a/fs/apfs/super.c b/fs/apfs/super.c new file mode 100644 -index 000000000..5fc2b2bf9 +index 000000000..2d3a5df93 --- /dev/null +++ b/fs/apfs/super.c -@@ -0,0 +1,1946 @@ +@@ -0,0 +1,1955 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 Ernesto A. Fernández @@ -22505,6 +22667,8 @@ index 000000000..5fc2b2bf9 + fmode_t mode = FMODE_READ | FMODE_EXCL; +#endif + struct apfs_ephemeral_object_info *eph_list = NULL; ++ struct apfs_spaceman *sm = NULL; ++ u32 bmap_idx; + int i; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) @@ -22544,8 +22708,15 @@ index 000000000..5fc2b2bf9 +#endif + + list_del(&nxi->nx_list); -+ kfree(nxi->nx_spaceman); -+ nxi->nx_spaceman = NULL; ++ sm = nxi->nx_spaceman; ++ if (sm) { ++ for (bmap_idx = 0; bmap_idx < sm->sm_ip_bmaps_count; ++bmap_idx) { ++ kfree(sm->sm_ip_bmaps[bmap_idx].block); ++ sm->sm_ip_bmaps[bmap_idx].block = NULL; ++ } ++ kfree(sm); ++ nxi->nx_spaceman = sm = NULL; ++ } + kfree(nxi); +out: + sbi->s_nxi = NULL; @@ -24214,10 +24385,10 @@ index 000000000..be4f9df8f +}; diff --git a/fs/apfs/transaction.c b/fs/apfs/transaction.c new file mode 100644 -index 000000000..bdecfc9f4 +index 000000000..25f9643cc --- /dev/null +++ b/fs/apfs/transaction.c -@@ -0,0 +1,987 @@ +@@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 Ernesto A. Fernández @@ -24321,13 +24492,18 @@ index 000000000..bdecfc9f4 +static int apfs_read_single_ephemeral_object(struct super_block *sb, struct apfs_checkpoint_mapping *map) +{ + struct apfs_nxsb_info *nxi = APFS_NXI(sb); ++ struct apfs_nx_superblock *raw_sb = NULL; + struct apfs_ephemeral_object_info *list = NULL; + struct buffer_head *bh = NULL; + char *object = NULL; + int count; -+ u32 size; -+ u64 bno, oid; -+ int err, i; ++ u32 data_blks, size; ++ u64 data_base, bno, oid; ++ int err, i, data_idx; ++ ++ raw_sb = nxi->nx_raw; ++ data_base = le64_to_cpu(raw_sb->nx_xp_data_base); ++ data_blks = le32_to_cpu(raw_sb->nx_xp_data_blocks); + + list = nxi->nx_eph_list; + count = nxi->nx_eph_count; @@ -24355,8 +24531,9 @@ index 000000000..bdecfc9f4 + if (!object) + return -ENOMEM; + ++ data_idx = bno - data_base; + for (i = 0; i < size >> sb->s_blocksize_bits; ++i) { -+ bh = apfs_sb_bread(sb, bno + i); ++ bh = apfs_sb_bread(sb, data_base + data_idx); + if (!bh) { + apfs_err(sb, "failed to read ephemeral block"); + err = -EIO; @@ -24365,6 +24542,9 @@ index 000000000..bdecfc9f4 + memcpy(object + (i << sb->s_blocksize_bits), bh->b_data, sb->s_blocksize); + brelse(bh); + bh = NULL; ++ /* Somewhat surprisingly, objects can wrap around */ ++ if (++data_idx == data_blks) ++ data_idx = 0; + } + + /* @@ -24684,18 +24864,24 @@ index 000000000..bdecfc9f4 +static int apfs_write_single_ephemeral_object(struct super_block *sb, struct apfs_obj_phys *obj_raw, const struct apfs_checkpoint_mapping *map) +{ + struct apfs_nxsb_info *nxi = APFS_NXI(sb); ++ struct apfs_nx_superblock *raw_sb = NULL; + struct buffer_head *bh = NULL; -+ u64 bno; -+ u32 size; -+ int err, i; ++ u64 data_base, bno; ++ u32 data_blks, size; ++ int err, i, data_idx; ++ ++ raw_sb = nxi->nx_raw; ++ data_base = le64_to_cpu(raw_sb->nx_xp_data_base); ++ data_blks = le32_to_cpu(raw_sb->nx_xp_data_blocks); + + bno = le64_to_cpu(map->cpm_paddr); + size = le32_to_cpu(map->cpm_size); + obj_raw->o_xid = cpu_to_le64(nxi->nx_xid); + apfs_multiblock_set_csum((char *)obj_raw, size); + ++ data_idx = bno - data_base; + for (i = 0; i < size >> sb->s_blocksize_bits; ++i) { -+ bh = apfs_getblk(sb, bno + i); ++ bh = apfs_getblk(sb, data_base + data_idx); + if (!bh) { + apfs_err(sb, "failed to map ephemeral block"); + return -EIO; @@ -24709,6 +24895,9 @@ index 000000000..bdecfc9f4 + memcpy(bh->b_data, (char *)obj_raw + (i << sb->s_blocksize_bits), sb->s_blocksize); + brelse(bh); + bh = NULL; ++ /* Somewhat surprisingly, objects can wrap around */ ++ if (++data_idx == data_blks) ++ data_idx = 0; + } + return 0; +} @@ -24784,15 +24973,6 @@ index 000000000..bdecfc9f4 + eph_info = &nxi->nx_eph_list[i]; + data_next = (data_index + data_len) % data_blks; + obj_blkcnt = eph_info->size >> sb->s_blocksize_bits; -+ if (obj_blkcnt > data_blks - data_next) { -+ /* -+ * This multiblock object does not fit in what's left -+ * of the ring buffer, so move it to the beginning and -+ * leave some empty blocks. -+ */ -+ data_len += data_blks - data_next; -+ data_next = 0; -+ } + + err = apfs_create_cpoint_map(sb, cpm, eph_info->object, data_base + data_next, eph_info->size); + if (err) { @@ -24851,7 +25031,6 @@ index 000000000..bdecfc9f4 + struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction; + struct apfs_bh_info *bhi, *tmp; + int err = 0; -+ u32 bmap_idx; + + ASSERT(!(sb->s_flags & SB_RDONLY)); + @@ -24875,6 +25054,16 @@ index 000000000..bdecfc9f4 + } + sm->sm_free_cache_base = sm->sm_free_cache_blkcnt = 0; + } ++ /* ++ * Writing the ip bitmaps modifies the spaceman, so it must happen ++ * before we commit the ephemeral objects. It must also happen after we ++ * flush the free queue, in case the last freed range was in the ip. ++ */ ++ err = apfs_write_ip_bitmaps(sb); ++ if (err) { ++ apfs_err(sb, "failed to commit the ip bitmaps"); ++ return err; ++ } + err = apfs_write_ephemeral_objects(sb); + if (err) + return err; @@ -24964,12 +25153,6 @@ index 000000000..bdecfc9f4 + return err; + } + -+ for (bmap_idx = 0; bmap_idx < APFS_SM(sb)->sm_ip_bmaps_count; ++bmap_idx) { -+ brelse(APFS_SM(sb)->sm_ip_bmaps[bmap_idx]); -+ APFS_SM(sb)->sm_ip_bmaps[bmap_idx] = NULL; -+ } -+ APFS_SM(sb)->sm_raw = NULL; -+ + nx_trans->t_starts_count = 0; + nx_trans->t_buffers_count = 0; + return 0; @@ -25154,8 +25337,6 @@ index 000000000..bdecfc9f4 + struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction; + struct apfs_bh_info *bhi, *tmp; + struct apfs_inode_info *ai, *ai_tmp; -+ struct apfs_spaceman *sm = NULL; -+ u32 bmap_idx; + + if (sb->s_flags & SB_RDONLY) { + /* Transaction already aborted, do nothing */ @@ -25183,15 +25364,6 @@ index 000000000..bdecfc9f4 + kfree(bhi); + } + -+ sm = APFS_SM(sb); -+ if (sm) { -+ for (bmap_idx = 0; bmap_idx < sm->sm_ip_bmaps_count; ++bmap_idx) { -+ brelse(sm->sm_ip_bmaps[bmap_idx]); -+ sm->sm_ip_bmaps[bmap_idx] = NULL; -+ } -+ APFS_SM(sb)->sm_raw = NULL; -+ } -+ + /* + * It's not possible to undo in-memory changes from old operations in + * the aborted transaction. To avoid corruption, never write again. @@ -25207,7 +25379,7 @@ index 000000000..bdecfc9f4 +} diff --git a/fs/apfs/unicode.c b/fs/apfs/unicode.c new file mode 100644 -index 000000000..4a747895c +index 000000000..7baf4305b --- /dev/null +++ b/fs/apfs/unicode.c @@ -0,0 +1,3156 @@ @@ -25446,7 +25618,7 @@ index 000000000..4a747895c + while (1) { + if (!total_len || !*utf8str) + return norm_len; -+ utf8len = utf8_to_utf32(utf8str, min(total_len, 4), &utf32char); ++ utf8len = utf8_to_utf32(utf8str, min(total_len, 4U), &utf32char); + if (utf8len < 0) /* Invalid unicode; don't normalize anything */ + return 0; + @@ -25518,7 +25690,7 @@ index 000000000..4a747895c + unicode_t utf32char; + int utf8len, pos; + -+ utf8len = utf8_to_utf32(utf8str, min(total_len, 4), &utf32char); ++ utf8len = utf8_to_utf32(utf8str, min(total_len, 4U), &utf32char); + for (pos = 0;; pos++, str_pos++) { + unicode_t utf32norm; + u8 ccc; @@ -28402,11 +28574,11 @@ index 000000000..e3b7edc51 +#endif /* _APFS_UNICODE_H */ diff --git a/fs/apfs/version.h b/fs/apfs/version.h new file mode 100644 -index 000000000..e16b23c10 +index 000000000..68cc43af9 --- /dev/null +++ b/fs/apfs/version.h @@ -0,0 +1 @@ -+#define GIT_COMMIT "" ++#define GIT_COMMIT "v0.3.12" diff --git a/fs/apfs/xattr.c b/fs/apfs/xattr.c new file mode 100644 index 000000000..0de1db140 @@ -29513,5 +29685,5 @@ index 000000000..b8cbe17fd + return total_len; +} -- -2.46.1 +2.47.0 diff --git a/apfs_ver b/apfs_ver index d6a7680..e42cfe5 100644 --- a/apfs_ver +++ b/apfs_ver @@ -1,2 +1,2 @@ -CURRENT_HASH=2920180ed673a613a566cc10aaa1cfb91f456ccb -RELEASE_VER=0.3.11-2 +CURRENT_HASH=5a2db9bd684233f2513bfaa6eca725b274e285a8 +RELEASE_VER=0.3.12-1