18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/hfs/bnode.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2001
68c2ecf20Sopenharmony_ci * Brad Boyer (flar@allandria.com)
78c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Handle basic btree node operations
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/swap.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "btree.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_civoid hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct page *page;
218c2ecf20Sopenharmony_ci	int pagenum;
228c2ecf20Sopenharmony_ci	int bytes_read;
238c2ecf20Sopenharmony_ci	int bytes_to_read;
248c2ecf20Sopenharmony_ci	void *vaddr;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	off += node->page_offset;
278c2ecf20Sopenharmony_ci	pagenum = off >> PAGE_SHIFT;
288c2ecf20Sopenharmony_ci	off &= ~PAGE_MASK; /* compute page offset for the first page */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	for (bytes_read = 0; bytes_read < len; bytes_read += bytes_to_read) {
318c2ecf20Sopenharmony_ci		if (pagenum >= node->tree->pages_per_bnode)
328c2ecf20Sopenharmony_ci			break;
338c2ecf20Sopenharmony_ci		page = node->page[pagenum];
348c2ecf20Sopenharmony_ci		bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci		vaddr = kmap_atomic(page);
378c2ecf20Sopenharmony_ci		memcpy(buf + bytes_read, vaddr + off, bytes_to_read);
388c2ecf20Sopenharmony_ci		kunmap_atomic(vaddr);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		pagenum++;
418c2ecf20Sopenharmony_ci		off = 0; /* page offset only applies to the first page */
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciu16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	__be16 data;
488c2ecf20Sopenharmony_ci	// optimize later...
498c2ecf20Sopenharmony_ci	hfs_bnode_read(node, &data, off, 2);
508c2ecf20Sopenharmony_ci	return be16_to_cpu(data);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciu8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	u8 data;
568c2ecf20Sopenharmony_ci	// optimize later...
578c2ecf20Sopenharmony_ci	hfs_bnode_read(node, &data, off, 1);
588c2ecf20Sopenharmony_ci	return data;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct hfs_btree *tree;
648c2ecf20Sopenharmony_ci	int key_len;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	tree = node->tree;
678c2ecf20Sopenharmony_ci	if (node->type == HFS_NODE_LEAF ||
688c2ecf20Sopenharmony_ci	    tree->attributes & HFS_TREE_VARIDXKEYS)
698c2ecf20Sopenharmony_ci		key_len = hfs_bnode_read_u8(node, off) + 1;
708c2ecf20Sopenharmony_ci	else
718c2ecf20Sopenharmony_ci		key_len = tree->max_key_len + 1;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	hfs_bnode_read(node, key, off, key_len);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_civoid hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct page *page;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	off += node->page_offset;
818c2ecf20Sopenharmony_ci	page = node->page[0];
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	memcpy(kmap(page) + off, buf, len);
848c2ecf20Sopenharmony_ci	kunmap(page);
858c2ecf20Sopenharmony_ci	set_page_dirty(page);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	__be16 v = cpu_to_be16(data);
918c2ecf20Sopenharmony_ci	// optimize later...
928c2ecf20Sopenharmony_ci	hfs_bnode_write(node, &v, off, 2);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_civoid hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	// optimize later...
988c2ecf20Sopenharmony_ci	hfs_bnode_write(node, &data, off, 1);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_civoid hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct page *page;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	off += node->page_offset;
1068c2ecf20Sopenharmony_ci	page = node->page[0];
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	memset(kmap(page) + off, 0, len);
1098c2ecf20Sopenharmony_ci	kunmap(page);
1108c2ecf20Sopenharmony_ci	set_page_dirty(page);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
1148c2ecf20Sopenharmony_ci		struct hfs_bnode *src_node, int src, int len)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct page *src_page, *dst_page;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
1198c2ecf20Sopenharmony_ci	if (!len)
1208c2ecf20Sopenharmony_ci		return;
1218c2ecf20Sopenharmony_ci	src += src_node->page_offset;
1228c2ecf20Sopenharmony_ci	dst += dst_node->page_offset;
1238c2ecf20Sopenharmony_ci	src_page = src_node->page[0];
1248c2ecf20Sopenharmony_ci	dst_page = dst_node->page[0];
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	memcpy(kmap(dst_page) + dst, kmap(src_page) + src, len);
1278c2ecf20Sopenharmony_ci	kunmap(src_page);
1288c2ecf20Sopenharmony_ci	kunmap(dst_page);
1298c2ecf20Sopenharmony_ci	set_page_dirty(dst_page);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_civoid hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct page *page;
1358c2ecf20Sopenharmony_ci	void *ptr;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
1388c2ecf20Sopenharmony_ci	if (!len)
1398c2ecf20Sopenharmony_ci		return;
1408c2ecf20Sopenharmony_ci	src += node->page_offset;
1418c2ecf20Sopenharmony_ci	dst += node->page_offset;
1428c2ecf20Sopenharmony_ci	page = node->page[0];
1438c2ecf20Sopenharmony_ci	ptr = kmap(page);
1448c2ecf20Sopenharmony_ci	memmove(ptr + dst, ptr + src, len);
1458c2ecf20Sopenharmony_ci	kunmap(page);
1468c2ecf20Sopenharmony_ci	set_page_dirty(page);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid hfs_bnode_dump(struct hfs_bnode *node)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct hfs_bnode_desc desc;
1528c2ecf20Sopenharmony_ci	__be32 cnid;
1538c2ecf20Sopenharmony_ci	int i, off, key_off;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_MOD, "bnode: %d\n", node->this);
1568c2ecf20Sopenharmony_ci	hfs_bnode_read(node, &desc, 0, sizeof(desc));
1578c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_MOD, "%d, %d, %d, %d, %d\n",
1588c2ecf20Sopenharmony_ci		be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
1598c2ecf20Sopenharmony_ci		desc.type, desc.height, be16_to_cpu(desc.num_recs));
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	off = node->tree->node_size - 2;
1628c2ecf20Sopenharmony_ci	for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) {
1638c2ecf20Sopenharmony_ci		key_off = hfs_bnode_read_u16(node, off);
1648c2ecf20Sopenharmony_ci		hfs_dbg_cont(BNODE_MOD, " %d", key_off);
1658c2ecf20Sopenharmony_ci		if (i && node->type == HFS_NODE_INDEX) {
1668c2ecf20Sopenharmony_ci			int tmp;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci			if (node->tree->attributes & HFS_TREE_VARIDXKEYS)
1698c2ecf20Sopenharmony_ci				tmp = (hfs_bnode_read_u8(node, key_off) | 1) + 1;
1708c2ecf20Sopenharmony_ci			else
1718c2ecf20Sopenharmony_ci				tmp = node->tree->max_key_len + 1;
1728c2ecf20Sopenharmony_ci			hfs_dbg_cont(BNODE_MOD, " (%d,%d",
1738c2ecf20Sopenharmony_ci				     tmp, hfs_bnode_read_u8(node, key_off));
1748c2ecf20Sopenharmony_ci			hfs_bnode_read(node, &cnid, key_off + tmp, 4);
1758c2ecf20Sopenharmony_ci			hfs_dbg_cont(BNODE_MOD, ",%d)", be32_to_cpu(cnid));
1768c2ecf20Sopenharmony_ci		} else if (i && node->type == HFS_NODE_LEAF) {
1778c2ecf20Sopenharmony_ci			int tmp;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci			tmp = hfs_bnode_read_u8(node, key_off);
1808c2ecf20Sopenharmony_ci			hfs_dbg_cont(BNODE_MOD, " (%d)", tmp);
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	hfs_dbg_cont(BNODE_MOD, "\n");
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_civoid hfs_bnode_unlink(struct hfs_bnode *node)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct hfs_btree *tree;
1898c2ecf20Sopenharmony_ci	struct hfs_bnode *tmp;
1908c2ecf20Sopenharmony_ci	__be32 cnid;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	tree = node->tree;
1938c2ecf20Sopenharmony_ci	if (node->prev) {
1948c2ecf20Sopenharmony_ci		tmp = hfs_bnode_find(tree, node->prev);
1958c2ecf20Sopenharmony_ci		if (IS_ERR(tmp))
1968c2ecf20Sopenharmony_ci			return;
1978c2ecf20Sopenharmony_ci		tmp->next = node->next;
1988c2ecf20Sopenharmony_ci		cnid = cpu_to_be32(tmp->next);
1998c2ecf20Sopenharmony_ci		hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
2008c2ecf20Sopenharmony_ci		hfs_bnode_put(tmp);
2018c2ecf20Sopenharmony_ci	} else if (node->type == HFS_NODE_LEAF)
2028c2ecf20Sopenharmony_ci		tree->leaf_head = node->next;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (node->next) {
2058c2ecf20Sopenharmony_ci		tmp = hfs_bnode_find(tree, node->next);
2068c2ecf20Sopenharmony_ci		if (IS_ERR(tmp))
2078c2ecf20Sopenharmony_ci			return;
2088c2ecf20Sopenharmony_ci		tmp->prev = node->prev;
2098c2ecf20Sopenharmony_ci		cnid = cpu_to_be32(tmp->prev);
2108c2ecf20Sopenharmony_ci		hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4);
2118c2ecf20Sopenharmony_ci		hfs_bnode_put(tmp);
2128c2ecf20Sopenharmony_ci	} else if (node->type == HFS_NODE_LEAF)
2138c2ecf20Sopenharmony_ci		tree->leaf_tail = node->prev;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	// move down?
2168c2ecf20Sopenharmony_ci	if (!node->prev && !node->next) {
2178c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "hfs_btree_del_level\n");
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	if (!node->parent) {
2208c2ecf20Sopenharmony_ci		tree->root = 0;
2218c2ecf20Sopenharmony_ci		tree->depth = 0;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	set_bit(HFS_BNODE_DELETED, &node->flags);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic inline int hfs_bnode_hash(u32 num)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	num = (num >> 16) + num;
2298c2ecf20Sopenharmony_ci	num += num >> 8;
2308c2ecf20Sopenharmony_ci	return num & (NODE_HASH_SIZE - 1);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistruct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct hfs_bnode *node;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (cnid >= tree->node_count) {
2388c2ecf20Sopenharmony_ci		pr_err("request for non-existent node %d in B*Tree\n", cnid);
2398c2ecf20Sopenharmony_ci		return NULL;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	for (node = tree->node_hash[hfs_bnode_hash(cnid)];
2438c2ecf20Sopenharmony_ci	     node; node = node->next_hash) {
2448c2ecf20Sopenharmony_ci		if (node->this == cnid) {
2458c2ecf20Sopenharmony_ci			return node;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci	return NULL;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct hfs_bnode *node, *node2;
2548c2ecf20Sopenharmony_ci	struct address_space *mapping;
2558c2ecf20Sopenharmony_ci	struct page *page;
2568c2ecf20Sopenharmony_ci	int size, block, i, hash;
2578c2ecf20Sopenharmony_ci	loff_t off;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (cnid >= tree->node_count) {
2608c2ecf20Sopenharmony_ci		pr_err("request for non-existent node %d in B*Tree\n", cnid);
2618c2ecf20Sopenharmony_ci		return NULL;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
2658c2ecf20Sopenharmony_ci		sizeof(struct page *);
2668c2ecf20Sopenharmony_ci	node = kzalloc(size, GFP_KERNEL);
2678c2ecf20Sopenharmony_ci	if (!node)
2688c2ecf20Sopenharmony_ci		return NULL;
2698c2ecf20Sopenharmony_ci	node->tree = tree;
2708c2ecf20Sopenharmony_ci	node->this = cnid;
2718c2ecf20Sopenharmony_ci	set_bit(HFS_BNODE_NEW, &node->flags);
2728c2ecf20Sopenharmony_ci	atomic_set(&node->refcnt, 1);
2738c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_REFS, "new_node(%d:%d): 1\n",
2748c2ecf20Sopenharmony_ci		node->tree->cnid, node->this);
2758c2ecf20Sopenharmony_ci	init_waitqueue_head(&node->lock_wq);
2768c2ecf20Sopenharmony_ci	spin_lock(&tree->hash_lock);
2778c2ecf20Sopenharmony_ci	node2 = hfs_bnode_findhash(tree, cnid);
2788c2ecf20Sopenharmony_ci	if (!node2) {
2798c2ecf20Sopenharmony_ci		hash = hfs_bnode_hash(cnid);
2808c2ecf20Sopenharmony_ci		node->next_hash = tree->node_hash[hash];
2818c2ecf20Sopenharmony_ci		tree->node_hash[hash] = node;
2828c2ecf20Sopenharmony_ci		tree->node_hash_cnt++;
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		hfs_bnode_get(node2);
2858c2ecf20Sopenharmony_ci		spin_unlock(&tree->hash_lock);
2868c2ecf20Sopenharmony_ci		kfree(node);
2878c2ecf20Sopenharmony_ci		wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags));
2888c2ecf20Sopenharmony_ci		return node2;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	spin_unlock(&tree->hash_lock);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	mapping = tree->inode->i_mapping;
2938c2ecf20Sopenharmony_ci	off = (loff_t)cnid * tree->node_size;
2948c2ecf20Sopenharmony_ci	block = off >> PAGE_SHIFT;
2958c2ecf20Sopenharmony_ci	node->page_offset = off & ~PAGE_MASK;
2968c2ecf20Sopenharmony_ci	for (i = 0; i < tree->pages_per_bnode; i++) {
2978c2ecf20Sopenharmony_ci		page = read_mapping_page(mapping, block++, NULL);
2988c2ecf20Sopenharmony_ci		if (IS_ERR(page))
2998c2ecf20Sopenharmony_ci			goto fail;
3008c2ecf20Sopenharmony_ci		if (PageError(page)) {
3018c2ecf20Sopenharmony_ci			put_page(page);
3028c2ecf20Sopenharmony_ci			goto fail;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci		node->page[i] = page;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return node;
3088c2ecf20Sopenharmony_cifail:
3098c2ecf20Sopenharmony_ci	set_bit(HFS_BNODE_ERROR, &node->flags);
3108c2ecf20Sopenharmony_ci	return node;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_civoid hfs_bnode_unhash(struct hfs_bnode *node)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct hfs_bnode **p;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	hfs_dbg(BNODE_REFS, "remove_node(%d:%d): %d\n",
3188c2ecf20Sopenharmony_ci		node->tree->cnid, node->this, atomic_read(&node->refcnt));
3198c2ecf20Sopenharmony_ci	for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)];
3208c2ecf20Sopenharmony_ci	     *p && *p != node; p = &(*p)->next_hash)
3218c2ecf20Sopenharmony_ci		;
3228c2ecf20Sopenharmony_ci	BUG_ON(!*p);
3238c2ecf20Sopenharmony_ci	*p = node->next_hash;
3248c2ecf20Sopenharmony_ci	node->tree->node_hash_cnt--;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/* Load a particular node out of a tree */
3288c2ecf20Sopenharmony_cistruct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct hfs_bnode *node;
3318c2ecf20Sopenharmony_ci	struct hfs_bnode_desc *desc;
3328c2ecf20Sopenharmony_ci	int i, rec_off, off, next_off;
3338c2ecf20Sopenharmony_ci	int entry_size, key_size;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	spin_lock(&tree->hash_lock);
3368c2ecf20Sopenharmony_ci	node = hfs_bnode_findhash(tree, num);
3378c2ecf20Sopenharmony_ci	if (node) {
3388c2ecf20Sopenharmony_ci		hfs_bnode_get(node);
3398c2ecf20Sopenharmony_ci		spin_unlock(&tree->hash_lock);
3408c2ecf20Sopenharmony_ci		wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags));
3418c2ecf20Sopenharmony_ci		if (test_bit(HFS_BNODE_ERROR, &node->flags))
3428c2ecf20Sopenharmony_ci			goto node_error;
3438c2ecf20Sopenharmony_ci		return node;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	spin_unlock(&tree->hash_lock);
3468c2ecf20Sopenharmony_ci	node = __hfs_bnode_create(tree, num);
3478c2ecf20Sopenharmony_ci	if (!node)
3488c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3498c2ecf20Sopenharmony_ci	if (test_bit(HFS_BNODE_ERROR, &node->flags))
3508c2ecf20Sopenharmony_ci		goto node_error;
3518c2ecf20Sopenharmony_ci	if (!test_bit(HFS_BNODE_NEW, &node->flags))
3528c2ecf20Sopenharmony_ci		return node;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset);
3558c2ecf20Sopenharmony_ci	node->prev = be32_to_cpu(desc->prev);
3568c2ecf20Sopenharmony_ci	node->next = be32_to_cpu(desc->next);
3578c2ecf20Sopenharmony_ci	node->num_recs = be16_to_cpu(desc->num_recs);
3588c2ecf20Sopenharmony_ci	node->type = desc->type;
3598c2ecf20Sopenharmony_ci	node->height = desc->height;
3608c2ecf20Sopenharmony_ci	kunmap(node->page[0]);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	switch (node->type) {
3638c2ecf20Sopenharmony_ci	case HFS_NODE_HEADER:
3648c2ecf20Sopenharmony_ci	case HFS_NODE_MAP:
3658c2ecf20Sopenharmony_ci		if (node->height != 0)
3668c2ecf20Sopenharmony_ci			goto node_error;
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case HFS_NODE_LEAF:
3698c2ecf20Sopenharmony_ci		if (node->height != 1)
3708c2ecf20Sopenharmony_ci			goto node_error;
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	case HFS_NODE_INDEX:
3738c2ecf20Sopenharmony_ci		if (node->height <= 1 || node->height > tree->depth)
3748c2ecf20Sopenharmony_ci			goto node_error;
3758c2ecf20Sopenharmony_ci		break;
3768c2ecf20Sopenharmony_ci	default:
3778c2ecf20Sopenharmony_ci		goto node_error;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	rec_off = tree->node_size - 2;
3818c2ecf20Sopenharmony_ci	off = hfs_bnode_read_u16(node, rec_off);
3828c2ecf20Sopenharmony_ci	if (off != sizeof(struct hfs_bnode_desc))
3838c2ecf20Sopenharmony_ci		goto node_error;
3848c2ecf20Sopenharmony_ci	for (i = 1; i <= node->num_recs; off = next_off, i++) {
3858c2ecf20Sopenharmony_ci		rec_off -= 2;
3868c2ecf20Sopenharmony_ci		next_off = hfs_bnode_read_u16(node, rec_off);
3878c2ecf20Sopenharmony_ci		if (next_off <= off ||
3888c2ecf20Sopenharmony_ci		    next_off > tree->node_size ||
3898c2ecf20Sopenharmony_ci		    next_off & 1)
3908c2ecf20Sopenharmony_ci			goto node_error;
3918c2ecf20Sopenharmony_ci		entry_size = next_off - off;
3928c2ecf20Sopenharmony_ci		if (node->type != HFS_NODE_INDEX &&
3938c2ecf20Sopenharmony_ci		    node->type != HFS_NODE_LEAF)
3948c2ecf20Sopenharmony_ci			continue;
3958c2ecf20Sopenharmony_ci		key_size = hfs_bnode_read_u8(node, off) + 1;
3968c2ecf20Sopenharmony_ci		if (key_size >= entry_size /*|| key_size & 1*/)
3978c2ecf20Sopenharmony_ci			goto node_error;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci	clear_bit(HFS_BNODE_NEW, &node->flags);
4008c2ecf20Sopenharmony_ci	wake_up(&node->lock_wq);
4018c2ecf20Sopenharmony_ci	return node;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cinode_error:
4048c2ecf20Sopenharmony_ci	set_bit(HFS_BNODE_ERROR, &node->flags);
4058c2ecf20Sopenharmony_ci	clear_bit(HFS_BNODE_NEW, &node->flags);
4068c2ecf20Sopenharmony_ci	wake_up(&node->lock_wq);
4078c2ecf20Sopenharmony_ci	hfs_bnode_put(node);
4088c2ecf20Sopenharmony_ci	return ERR_PTR(-EIO);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_civoid hfs_bnode_free(struct hfs_bnode *node)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	int i;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	for (i = 0; i < node->tree->pages_per_bnode; i++)
4168c2ecf20Sopenharmony_ci		if (node->page[i])
4178c2ecf20Sopenharmony_ci			put_page(node->page[i]);
4188c2ecf20Sopenharmony_ci	kfree(node);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistruct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct hfs_bnode *node;
4248c2ecf20Sopenharmony_ci	struct page **pagep;
4258c2ecf20Sopenharmony_ci	int i;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	spin_lock(&tree->hash_lock);
4288c2ecf20Sopenharmony_ci	node = hfs_bnode_findhash(tree, num);
4298c2ecf20Sopenharmony_ci	spin_unlock(&tree->hash_lock);
4308c2ecf20Sopenharmony_ci	if (node) {
4318c2ecf20Sopenharmony_ci		pr_crit("new node %u already hashed?\n", num);
4328c2ecf20Sopenharmony_ci		WARN_ON(1);
4338c2ecf20Sopenharmony_ci		return node;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	node = __hfs_bnode_create(tree, num);
4368c2ecf20Sopenharmony_ci	if (!node)
4378c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4388c2ecf20Sopenharmony_ci	if (test_bit(HFS_BNODE_ERROR, &node->flags)) {
4398c2ecf20Sopenharmony_ci		hfs_bnode_put(node);
4408c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	pagep = node->page;
4448c2ecf20Sopenharmony_ci	memset(kmap(*pagep) + node->page_offset, 0,
4458c2ecf20Sopenharmony_ci	       min((int)PAGE_SIZE, (int)tree->node_size));
4468c2ecf20Sopenharmony_ci	set_page_dirty(*pagep);
4478c2ecf20Sopenharmony_ci	kunmap(*pagep);
4488c2ecf20Sopenharmony_ci	for (i = 1; i < tree->pages_per_bnode; i++) {
4498c2ecf20Sopenharmony_ci		memset(kmap(*++pagep), 0, PAGE_SIZE);
4508c2ecf20Sopenharmony_ci		set_page_dirty(*pagep);
4518c2ecf20Sopenharmony_ci		kunmap(*pagep);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci	clear_bit(HFS_BNODE_NEW, &node->flags);
4548c2ecf20Sopenharmony_ci	wake_up(&node->lock_wq);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return node;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_civoid hfs_bnode_get(struct hfs_bnode *node)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	if (node) {
4628c2ecf20Sopenharmony_ci		atomic_inc(&node->refcnt);
4638c2ecf20Sopenharmony_ci		hfs_dbg(BNODE_REFS, "get_node(%d:%d): %d\n",
4648c2ecf20Sopenharmony_ci			node->tree->cnid, node->this,
4658c2ecf20Sopenharmony_ci			atomic_read(&node->refcnt));
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci/* Dispose of resources used by a node */
4708c2ecf20Sopenharmony_civoid hfs_bnode_put(struct hfs_bnode *node)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	if (node) {
4738c2ecf20Sopenharmony_ci		struct hfs_btree *tree = node->tree;
4748c2ecf20Sopenharmony_ci		int i;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		hfs_dbg(BNODE_REFS, "put_node(%d:%d): %d\n",
4778c2ecf20Sopenharmony_ci			node->tree->cnid, node->this,
4788c2ecf20Sopenharmony_ci			atomic_read(&node->refcnt));
4798c2ecf20Sopenharmony_ci		BUG_ON(!atomic_read(&node->refcnt));
4808c2ecf20Sopenharmony_ci		if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock))
4818c2ecf20Sopenharmony_ci			return;
4828c2ecf20Sopenharmony_ci		for (i = 0; i < tree->pages_per_bnode; i++) {
4838c2ecf20Sopenharmony_ci			if (!node->page[i])
4848c2ecf20Sopenharmony_ci				continue;
4858c2ecf20Sopenharmony_ci			mark_page_accessed(node->page[i]);
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
4898c2ecf20Sopenharmony_ci			hfs_bnode_unhash(node);
4908c2ecf20Sopenharmony_ci			spin_unlock(&tree->hash_lock);
4918c2ecf20Sopenharmony_ci			hfs_bmap_free(node);
4928c2ecf20Sopenharmony_ci			hfs_bnode_free(node);
4938c2ecf20Sopenharmony_ci			return;
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci		spin_unlock(&tree->hash_lock);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci}
498