From nobody Mon Apr 6 19:39:16 2026 Received: from sender4-of-o54.zoho.com (sender4-of-o54.zoho.com [136.143.188.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 787DD219A7A; Wed, 18 Mar 2026 07:39:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.54 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773819551; cv=pass; b=KjJ1g2GWufqrtGEc3JfmObwdy6XYHxytwSyixRCSfecH5hYcD/1+4WxlK/NeYY4ZNnujlLot3SterWWvOm3bspdY6VuodP2CzyhPUX5Mnj1b7JHC2N97uMCpjKnsddeQ8e4zlKwsO45giRVdFyPTVGFCyat70QMNKxPc5KFlG+w= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773819551; c=relaxed/simple; bh=APC7dhPS14RLMryDqNJVIeos7Did10qefUPSaTlKXn0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=J8tK4DXY3sKpoesANKTbmOqou9hEssD3Zx8hbfPuEFSFPO5bnnKZmAPKXvHmWbL6hJ8OrQWXG1Zbmk5/QDZeDYShFpac0heM4IdTNG/R+hd52X3BVci0KWzv9A4rJz+k7+hQNTSauUalthn3iA6cHOIUuD6mVRiEhP49LGM2u0Y= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com; spf=pass smtp.mailfrom=mpiricsoftware.com; dkim=pass (1024-bit key) header.d=mpiricsoftware.com header.i=shardul.b@mpiricsoftware.com header.b=XpiDJ2u5; arc=pass smtp.client-ip=136.143.188.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mpiricsoftware.com header.i=shardul.b@mpiricsoftware.com header.b="XpiDJ2u5" ARC-Seal: i=1; a=rsa-sha256; t=1773819517; cv=none; d=zohomail.com; s=zohoarc; b=U0/BphT8MMAS640SILB//S7NzjRhDDtTqwQqNPtsQXhZeCHcJLpkkzSl5mPTifdT4c/aJjmYYPHY2iqzhBdxhy+d3O8oQxRmBaOeBfqNkU4szq2E79UYGZcZJ+qQjZYUp5juNr0xKwamcIUMulZbZEpNpIDqKgsK4ABZSehdY30= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773819517; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=IZPtfNB2CTsZYPIJtir5IBqrmLbkftBTCzKm1cR0ilc=; b=Bs8svvkZizSa7Wpx5TDAXotSvHN4/DRtjDUNGahInQyIIQdQ07Zubag2IfcbyUlmQsxjPtbHH2A7AuT9m+RQmEXhFrlBW7FqpTbMcwIMd0QD2K3nQIUg/ORRoESCR1ZGiGFHOJGiz7xFlLR+911PXUr9NoXBN1GaN9zVQAeCIio= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=mpiricsoftware.com; spf=pass smtp.mailfrom=shardul.b@mpiricsoftware.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1773819517; s=mpiric; d=mpiricsoftware.com; i=shardul.b@mpiricsoftware.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=IZPtfNB2CTsZYPIJtir5IBqrmLbkftBTCzKm1cR0ilc=; b=XpiDJ2u55UvS8FhEV6Dw95wCC0nKWLgFKSfZ5w3cIqVLN4O9ks8hMLiiuxG2KkSA qj4zzncTeFXJpC0RNR+9clIED9vdg2GDxsqjV4Hzv2eGRbIXco+8J49iW07DVLJOf/Z HYl5o/yQU2V3LTGFH4GlZp1drKztnLvog+bhRSqk= Received: by mx.zohomail.com with SMTPS id 1773819516723728.7404562950832; Wed, 18 Mar 2026 00:38:36 -0700 (PDT) From: Shardul Bankar To: slava@dubeyko.com, glaubitz@physik.fu-berlin.de, frank.li@vivo.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: janak@mpiricsoftware.com, janak@mpiric.us, shardulsb08@gmail.com, Shardul Bankar Subject: [PATCH v7 1/2] hfsplus: refactor b-tree map page access and add node-type validation Date: Wed, 18 Mar 2026 13:08:22 +0530 Message-Id: <20260318073823.3933718-2-shardul.b@mpiricsoftware.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260318073823.3933718-1-shardul.b@mpiricsoftware.com> References: <20260318073823.3933718-1-shardul.b@mpiricsoftware.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" In HFS+ b-trees, the node allocation bitmap is stored across multiple records. The first chunk resides in the b-tree Header Node at record index 2, while all subsequent chunks are stored in dedicated Map Nodes at record index 0. This structural quirk forces callers like hfs_bmap_alloc() and hfs_bmap_free() to duplicate boilerplate code to validate offsets, correct lengths, and map the underlying pages via kmap_local_page(). There is also currently no strict node-type validation before reading these records, leaving the allocator vulnerable if a corrupted image points a map linkage to an Index or Leaf node. Introduce a unified bit-level API to encapsulate the map record access: 1. A new `struct hfs_bmap_ctx` to cleanly pass state and safely handle page math across all architectures. 2. `hfs_bmap_get_map_page()`: Automatically validates node types (HFS_NODE_HEADER vs HFS_NODE_MAP), infers the correct record index, handles page-boundary math, and returns the unmapped `struct page *` directly to the caller to avoid asymmetric mappings. 3. `hfs_bmap_clear_bit()`: A clean wrapper that internally handles page mapping/unmapping for single-bit operations. Refactor hfs_bmap_alloc() and hfs_bmap_free() to utilize this new API. This deduplicates the allocator logic, hardens the map traversal against fuzzed images, and provides the exact abstractions needed for upcoming mount-time validation checks. Signed-off-by: Shardul Bankar Reviewed-by: Viacheslav Dubeyko Tested-by: Viacheslav Dubeyko --- fs/hfsplus/btree.c | 169 ++++++++++++++++++++++++++----------- include/linux/hfs_common.h | 2 + 2 files changed, 124 insertions(+), 47 deletions(-) diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 1220a2f22737..64d168347b4b 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -129,6 +129,95 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 = node_size, return clump_size; } =20 +/* Context for iterating b-tree map pages + * @page_idx: The index of the page within the b-node's page array + * @off: The byte offset within the mapped page + * @len: The remaining length of the map record + */ +struct hfs_bmap_ctx { + unsigned int page_idx; + unsigned int off; + u16 len; +}; + +/* + * Finds the specific page containing the requested byte offset within the= map + * record. Automatically handles the difference between header and map nod= es. + * Returns the struct page pointer, or an ERR_PTR on failure. + * Note: The caller is responsible for mapping/unmapping the returned page. + */ +static struct page *hfs_bmap_get_map_page(struct hfs_bnode *node, struct h= fs_bmap_ctx *ctx, + u32 byte_offset) +{ + u16 rec_idx, off16; + unsigned int page_off; + + if (node->this =3D=3D HFSPLUS_TREE_HEAD) { + if (node->type !=3D HFS_NODE_HEADER) { + pr_err("hfsplus: invalid btree header node\n"); + return ERR_PTR(-EIO); + } + rec_idx =3D HFSPLUS_BTREE_HDR_MAP_REC_INDEX; + } else { + if (node->type !=3D HFS_NODE_MAP) { + pr_err("hfsplus: invalid btree map node\n"); + return ERR_PTR(-EIO); + } + rec_idx =3D HFSPLUS_BTREE_MAP_NODE_REC_INDEX; + } + + ctx->len =3D hfs_brec_lenoff(node, rec_idx, &off16); + if (!ctx->len) + return ERR_PTR(-ENOENT); + + if (!is_bnode_offset_valid(node, off16)) + return ERR_PTR(-EIO); + + ctx->len =3D check_and_correct_requested_length(node, off16, ctx->len); + + if (byte_offset >=3D ctx->len) + return ERR_PTR(-EINVAL); + + page_off =3D (u32)off16 + node->page_offset + byte_offset; + ctx->page_idx =3D page_off >> PAGE_SHIFT; + ctx->off =3D page_off & ~PAGE_MASK; + + return node->page[ctx->page_idx]; +} + +/** + * hfs_bmap_clear_bit - clear a bit in the b-tree map + * @node: the b-tree node containing the map record + * @node_bit_idx: the relative bit index within the node's map record + * + * Returns 0 on success, -EINVAL if already clear, or negative error code. + */ +static int hfs_bmap_clear_bit(struct hfs_bnode *node, u32 node_bit_idx) +{ + struct hfs_bmap_ctx ctx; + struct page *page; + u8 *bmap, mask; + + page =3D hfs_bmap_get_map_page(node, &ctx, node_bit_idx / BITS_PER_BYTE); + if (IS_ERR(page)) + return PTR_ERR(page); + + bmap =3D kmap_local_page(page); + + mask =3D 1 << (7 - (node_bit_idx % BITS_PER_BYTE)); + + if (!(bmap[ctx.off] & mask)) { + kunmap_local(bmap); + return -EINVAL; + } + + bmap[ctx.off] &=3D ~mask; + set_page_dirty(page); + kunmap_local(bmap); + + return 0; +} + /* Get a reference to a B*Tree and do some initial checks */ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) { @@ -374,11 +463,9 @@ int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_= nodes) struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) { struct hfs_bnode *node, *next_node; - struct page **pagep; + struct hfs_bmap_ctx ctx; + struct page *page; u32 nidx, idx; - unsigned off; - u16 off16; - u16 len; u8 *data, byte, m; int i, res; =20 @@ -390,30 +477,26 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tr= ee) node =3D hfs_bnode_find(tree, nidx); if (IS_ERR(node)) return node; - len =3D hfs_brec_lenoff(node, 2, &off16); - off =3D off16; =20 - if (!is_bnode_offset_valid(node, off)) { + page =3D hfs_bmap_get_map_page(node, &ctx, 0); + if (IS_ERR(page)) { + res =3D PTR_ERR(page); hfs_bnode_put(node); - return ERR_PTR(-EIO); + return ERR_PTR(res); } - len =3D check_and_correct_requested_length(node, off, len); =20 - off +=3D node->page_offset; - pagep =3D node->page + (off >> PAGE_SHIFT); - data =3D kmap_local_page(*pagep); - off &=3D ~PAGE_MASK; + data =3D kmap_local_page(page); idx =3D 0; =20 for (;;) { - while (len) { - byte =3D data[off]; + while (ctx.len) { + byte =3D data[ctx.off]; if (byte !=3D 0xff) { for (m =3D 0x80, i =3D 0; i < 8; m >>=3D 1, i++) { if (!(byte & m)) { idx +=3D i; - data[off] |=3D m; - set_page_dirty(*pagep); + data[ctx.off] |=3D m; + set_page_dirty(page); kunmap_local(data); tree->free_nodes--; mark_inode_dirty(tree->inode); @@ -423,13 +506,14 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tr= ee) } } } - if (++off >=3D PAGE_SIZE) { + if (++ctx.off >=3D PAGE_SIZE) { kunmap_local(data); - data =3D kmap_local_page(*++pagep); - off =3D 0; + page =3D node->page[++ctx.page_idx]; + data =3D kmap_local_page(page); + ctx.off =3D 0; } idx +=3D 8; - len--; + ctx.len--; } kunmap_local(data); nidx =3D node->next; @@ -443,22 +527,22 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tr= ee) return next_node; node =3D next_node; =20 - len =3D hfs_brec_lenoff(node, 0, &off16); - off =3D off16; - off +=3D node->page_offset; - pagep =3D node->page + (off >> PAGE_SHIFT); - data =3D kmap_local_page(*pagep); - off &=3D ~PAGE_MASK; + page =3D hfs_bmap_get_map_page(node, &ctx, 0); + if (IS_ERR(page)) { + res =3D PTR_ERR(page); + hfs_bnode_put(node); + return ERR_PTR(res); + } + data =3D kmap_local_page(page); } } =20 void hfs_bmap_free(struct hfs_bnode *node) { struct hfs_btree *tree; - struct page *page; u16 off, len; u32 nidx; - u8 *data, byte, m; + int res; =20 hfs_dbg("node %u\n", node->this); BUG_ON(!node->this); @@ -495,24 +579,15 @@ void hfs_bmap_free(struct hfs_bnode *node) } len =3D hfs_brec_lenoff(node, 0, &off); } - off +=3D node->page_offset + nidx / 8; - page =3D node->page[off >> PAGE_SHIFT]; - data =3D kmap_local_page(page); - off &=3D ~PAGE_MASK; - m =3D 1 << (~nidx & 7); - byte =3D data[off]; - if (!(byte & m)) { - pr_crit("trying to free free bnode " - "%u(%d)\n", - node->this, node->type); - kunmap_local(data); - hfs_bnode_put(node); - return; + + res =3D hfs_bmap_clear_bit(node, nidx); + if (res =3D=3D -EINVAL) { + pr_crit("trying to free free bnode %u(%d)\n", + node->this, node->type); + } else if (!res) { + tree->free_nodes++; + mark_inode_dirty(tree->inode); } - data[off] =3D byte & ~m; - set_page_dirty(page); - kunmap_local(data); + hfs_bnode_put(node); - tree->free_nodes++; - mark_inode_dirty(tree->inode); } diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h index dadb5e0aa8a3..be24c687858e 100644 --- a/include/linux/hfs_common.h +++ b/include/linux/hfs_common.h @@ -510,6 +510,8 @@ struct hfs_btree_header_rec { #define HFSPLUS_NODE_MXSZ 32768 #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 +#define HFSPLUS_BTREE_HDR_MAP_REC_INDEX 2 /* Map (bitmap) record in Heade= r node */ +#define HFSPLUS_BTREE_MAP_NODE_REC_INDEX 0 /* Map record in Map Node */ #define HFSPLUS_BTREE_HDR_USER_BYTES 128 =20 /* btree key type */ --=20 2.34.1 From nobody Mon Apr 6 19:39:16 2026 Received: from sender4-of-o54.zoho.com (sender4-of-o54.zoho.com [136.143.188.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B5D536BCD5; Wed, 18 Mar 2026 07:39:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.54 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773819573; cv=pass; b=O4KPA5nnM3RTXqBCjL4JOHgXALHgH4cpszU35oN0j6A2J/24hRVnFa+3MxWGvJ6KrTroHS6GQQp28SHWugmKWZ8XHtp76bFco3OKZLPXF4mnHVs7uCWmQrAFO8rm/NZpYEVspxNneLz7RXnNueI3scLi0WFmhk8gce56GM97Ii8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773819573; c=relaxed/simple; bh=h72dNGgqhk1G0IghvxUcYfOZ685gXKT8AD/e09cMrI0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OYxHB2NSBMJG23dsGYY0dzCKzd9buTn908yuUUlAldCc7eHIPMM7bw2hvIOrhtX8dIPej+eUz2RMiDFTlSIJ/VtXF2olWbesl4RidwHIt1MYPgKZ2z4NQsseGZMy3Jn0Z4dIl7qjoAXdVi0Yy4gnfDnMGMJx/+NqwtsN7SaFZGY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com; spf=pass smtp.mailfrom=mpiricsoftware.com; dkim=pass (1024-bit key) header.d=mpiricsoftware.com header.i=shardul.b@mpiricsoftware.com header.b=W8b95eXt; arc=pass smtp.client-ip=136.143.188.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=mpiricsoftware.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mpiricsoftware.com header.i=shardul.b@mpiricsoftware.com header.b="W8b95eXt" ARC-Seal: i=1; a=rsa-sha256; t=1773819523; cv=none; d=zohomail.com; s=zohoarc; b=WB2EqjCOmEF810jaboTY0i+/NAzKYa+Q7Oo81+apc2R7mgoZJ0M+2VjYt4eAkgQ8ouSryhQ9nEnTOwKvYGlTiKyipLkQEpx4AVc+ZiYJBTwxmvLs1WJciPpc8xGea27tnqPesoMHKrtUwKWqoVBJsHaWKGU7OChqKIA22sR64Uk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1773819523; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=LCnT2uWy60WrFcN45C+ke4n0VxQX6V+FIJZ20t59Xz4=; b=kiPrO5T8Q9dKlO5cR24rWMbG41Z2MRs5cfxcDS35ZI/6VQxHQ78RG9UTYZYDYoip8ZFYygZmWGBE7TBFhQV/EU1/RCrM5aeruSCw6W/WK9digThfPmFGd7N1qaJxU83Npmf+xxXWLQ1hXQbSaJ4mSEiguHvpxqlWp7htz7MzgVA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=mpiricsoftware.com; spf=pass smtp.mailfrom=shardul.b@mpiricsoftware.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1773819523; s=mpiric; d=mpiricsoftware.com; i=shardul.b@mpiricsoftware.com; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-Id:Message-Id:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Reply-To; bh=LCnT2uWy60WrFcN45C+ke4n0VxQX6V+FIJZ20t59Xz4=; b=W8b95eXtYyxidS8chEU35LABgeRctwlHgAGNweJA1i2EKTCUelUHTq9fvCD+CiMS KlMTn6y/3yUjO9LP1ko0Q94lBtnGwKVU/hyvVyPahJwb0l6UujM/ROks5HhqgaR/yje XflwUgSV93F2iGNRLd13++jC5nPFm8hCKIBi7Dkc= Received: by mx.zohomail.com with SMTPS id 1773819521713519.8621537982034; Wed, 18 Mar 2026 00:38:41 -0700 (PDT) From: Shardul Bankar To: slava@dubeyko.com, glaubitz@physik.fu-berlin.de, frank.li@vivo.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: janak@mpiricsoftware.com, janak@mpiric.us, shardulsb08@gmail.com, Shardul Bankar , syzbot+1c8ff72d0cd8a50dfeaa@syzkaller.appspotmail.com Subject: [PATCH v7 2/2] hfsplus: validate b-tree node 0 bitmap at mount time Date: Wed, 18 Mar 2026 13:08:23 +0530 Message-Id: <20260318073823.3933718-3-shardul.b@mpiricsoftware.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260318073823.3933718-1-shardul.b@mpiricsoftware.com> References: <20260318073823.3933718-1-shardul.b@mpiricsoftware.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Syzkaller reported an issue with corrupted HFS+ images where the b-tree allocation bitmap indicates that the header node (Node 0) is free. Node 0 must always be allocated as it contains the b-tree header record and the allocation bitmap itself. Violating this invariant leads to allocator corruption, which cascades into kernel panics or undefined behavior when the filesystem attempts to allocate blocks. Prevent trusting a corrupted allocator state by adding a validation check during hfs_btree_open(). Introduce the hfs_bmap_test_bit() helper (utilizing the newly added map-access API) to safely verify that the MSB of the first bitmap byte (representing Node 0) is marked as allocated. The helper returns a boolean, allowing the caller to safely catch both structural IO errors and illegally cleared bits in a single check. If corruption is detected, print a warning identifying the specific corrupted tree and force the filesystem to mount read-only (SB_RDONLY). This prevents kernel panics from corrupted images while enabling data recovery. As a minor cleanup to support the warning logs, replace the verbose CNID logic with cleaner macro definitions (using official structural names like "Extents Overflow File") and a dedicated string lookup helper. Reported-by: syzbot+1c8ff72d0cd8a50dfeaa@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=3D1c8ff72d0cd8a50dfeaa Link: https://lore.kernel.org/all/20260315172005.2066677-1-shardul.b@mpiric= software.com/ Signed-off-by: Shardul Bankar Reviewed-by: Viacheslav Dubeyko Tested-by: Viacheslav Dubeyko --- fs/hfsplus/btree.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 64d168347b4b..04304f952f6b 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -185,6 +185,32 @@ static struct page *hfs_bmap_get_map_page(struct hfs_b= node *node, struct hfs_bma return node->page[ctx->page_idx]; } =20 +/** + * hfs_bmap_test_bit - test a bit in the b-tree map + * @node: the b-tree node containing the map record + * @node_bit_idx: the relative bit index within the node's map record + * + * Returns true if set, false if clear or on failure. + */ +static bool hfs_bmap_test_bit(struct hfs_bnode *node, u32 node_bit_idx) +{ + struct hfs_bmap_ctx ctx; + struct page *page; + u8 *bmap, byte, mask; + + page =3D hfs_bmap_get_map_page(node, &ctx, node_bit_idx / BITS_PER_BYTE); + if (IS_ERR(page)) + return false; + + bmap =3D kmap_local_page(page); + byte =3D bmap[ctx.off]; + kunmap_local(bmap); + + mask =3D 1 << (7 - (node_bit_idx % BITS_PER_BYTE)); + return (byte & mask) !=3D 0; +} + + /** * hfs_bmap_clear_bit - clear a bit in the b-tree map * @node: the b-tree node containing the map record @@ -218,12 +244,32 @@ static int hfs_bmap_clear_bit(struct hfs_bnode *node,= u32 node_bit_idx) return 0; } =20 +#define HFS_EXTENT_TREE_NAME "Extents Overflow File" +#define HFS_CATALOG_TREE_NAME "Catalog File" +#define HFS_ATTR_TREE_NAME "Attributes File" +#define HFS_UNKNOWN_TREE_NAME "Unknown B-tree" + +static const char *hfs_btree_name(u32 cnid) +{ + switch (cnid) { + case HFSPLUS_EXT_CNID: + return HFS_EXTENT_TREE_NAME; + case HFSPLUS_CAT_CNID: + return HFS_CATALOG_TREE_NAME; + case HFSPLUS_ATTR_CNID: + return HFS_ATTR_TREE_NAME; + default: + return HFS_UNKNOWN_TREE_NAME; + } +} + /* Get a reference to a B*Tree and do some initial checks */ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) { struct hfs_btree *tree; struct hfs_btree_header_rec *head; struct address_space *mapping; + struct hfs_bnode *node; struct inode *inode; struct page *page; unsigned int size; @@ -331,6 +377,20 @@ struct hfs_btree *hfs_btree_open(struct super_block *s= b, u32 id) =20 kunmap_local(head); put_page(page); + + node =3D hfs_bnode_find(tree, HFSPLUS_TREE_HEAD); + if (IS_ERR(node)) + goto free_inode; + + if (!hfs_bmap_test_bit(node, 0)) { + pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption de= tected, forcing read-only.\n", + sb->s_id, hfs_btree_name(id), id); + pr_warn("Run fsck.hfsplus to repair.\n"); + sb->s_flags |=3D SB_RDONLY; + } + + hfs_bnode_put(node); + return tree; =20 fail_page: --=20 2.34.1