xref: /openbmc/linux/fs/hfs/bnode.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/hfs/bnode.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001
61da177e4SLinus Torvalds  * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds  * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Handle basic btree node operations
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/pagemap.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/swap.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include "btree.h"
171da177e4SLinus Torvalds 
hfs_bnode_read(struct hfs_bnode * node,void * buf,int off,int len)1854a5ead6SDesmond Cheong Zhi Xi void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
191da177e4SLinus Torvalds {
201da177e4SLinus Torvalds 	struct page *page;
2154a5ead6SDesmond Cheong Zhi Xi 	int pagenum;
2254a5ead6SDesmond Cheong Zhi Xi 	int bytes_read;
2354a5ead6SDesmond Cheong Zhi Xi 	int bytes_to_read;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	off += node->page_offset;
2654a5ead6SDesmond Cheong Zhi Xi 	pagenum = off >> PAGE_SHIFT;
2754a5ead6SDesmond Cheong Zhi Xi 	off &= ~PAGE_MASK; /* compute page offset for the first page */
281da177e4SLinus Torvalds 
2954a5ead6SDesmond Cheong Zhi Xi 	for (bytes_read = 0; bytes_read < len; bytes_read += bytes_to_read) {
3054a5ead6SDesmond Cheong Zhi Xi 		if (pagenum >= node->tree->pages_per_bnode)
3154a5ead6SDesmond Cheong Zhi Xi 			break;
3254a5ead6SDesmond Cheong Zhi Xi 		page = node->page[pagenum];
3354a5ead6SDesmond Cheong Zhi Xi 		bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
3454a5ead6SDesmond Cheong Zhi Xi 
35ca0ac8dfSFabio M. De Francesco 		memcpy_from_page(buf + bytes_read, page, off, bytes_to_read);
3654a5ead6SDesmond Cheong Zhi Xi 
3754a5ead6SDesmond Cheong Zhi Xi 		pagenum++;
3854a5ead6SDesmond Cheong Zhi Xi 		off = 0; /* page offset only applies to the first page */
3954a5ead6SDesmond Cheong Zhi Xi 	}
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds 
hfs_bnode_read_u16(struct hfs_bnode * node,int off)421da177e4SLinus Torvalds u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
431da177e4SLinus Torvalds {
441da177e4SLinus Torvalds 	__be16 data;
451da177e4SLinus Torvalds 	// optimize later...
461da177e4SLinus Torvalds 	hfs_bnode_read(node, &data, off, 2);
471da177e4SLinus Torvalds 	return be16_to_cpu(data);
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds 
hfs_bnode_read_u8(struct hfs_bnode * node,int off)501da177e4SLinus Torvalds u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds 	u8 data;
531da177e4SLinus Torvalds 	// optimize later...
541da177e4SLinus Torvalds 	hfs_bnode_read(node, &data, off, 1);
551da177e4SLinus Torvalds 	return data;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
hfs_bnode_read_key(struct hfs_bnode * node,void * key,int off)581da177e4SLinus Torvalds void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds 	struct hfs_btree *tree;
611da177e4SLinus Torvalds 	int key_len;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	tree = node->tree;
641da177e4SLinus Torvalds 	if (node->type == HFS_NODE_LEAF ||
651da177e4SLinus Torvalds 	    tree->attributes & HFS_TREE_VARIDXKEYS)
661da177e4SLinus Torvalds 		key_len = hfs_bnode_read_u8(node, off) + 1;
671da177e4SLinus Torvalds 	else
681da177e4SLinus Torvalds 		key_len = tree->max_key_len + 1;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	hfs_bnode_read(node, key, off, key_len);
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
hfs_bnode_write(struct hfs_bnode * node,void * buf,int off,int len)731da177e4SLinus Torvalds void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds 	struct page *page;
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	off += node->page_offset;
781da177e4SLinus Torvalds 	page = node->page[0];
791da177e4SLinus Torvalds 
80ca0ac8dfSFabio M. De Francesco 	memcpy_to_page(page, off, buf, len);
811da177e4SLinus Torvalds 	set_page_dirty(page);
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
hfs_bnode_write_u16(struct hfs_bnode * node,int off,u16 data)841da177e4SLinus Torvalds void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	__be16 v = cpu_to_be16(data);
871da177e4SLinus Torvalds 	// optimize later...
881da177e4SLinus Torvalds 	hfs_bnode_write(node, &v, off, 2);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
hfs_bnode_write_u8(struct hfs_bnode * node,int off,u8 data)911da177e4SLinus Torvalds void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	// optimize later...
941da177e4SLinus Torvalds 	hfs_bnode_write(node, &data, off, 1);
951da177e4SLinus Torvalds }
961da177e4SLinus Torvalds 
hfs_bnode_clear(struct hfs_bnode * node,int off,int len)971da177e4SLinus Torvalds void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	struct page *page;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	off += node->page_offset;
1021da177e4SLinus Torvalds 	page = node->page[0];
1031da177e4SLinus Torvalds 
104ca0ac8dfSFabio M. De Francesco 	memzero_page(page, off, len);
1051da177e4SLinus Torvalds 	set_page_dirty(page);
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
hfs_bnode_copy(struct hfs_bnode * dst_node,int dst,struct hfs_bnode * src_node,int src,int len)1081da177e4SLinus Torvalds void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
1091da177e4SLinus Torvalds 		struct hfs_bnode *src_node, int src, int len)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	struct page *src_page, *dst_page;
1121da177e4SLinus Torvalds 
113c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
1141da177e4SLinus Torvalds 	if (!len)
1151da177e4SLinus Torvalds 		return;
1161da177e4SLinus Torvalds 	src += src_node->page_offset;
1171da177e4SLinus Torvalds 	dst += dst_node->page_offset;
1181da177e4SLinus Torvalds 	src_page = src_node->page[0];
1191da177e4SLinus Torvalds 	dst_page = dst_node->page[0];
1201da177e4SLinus Torvalds 
121ca0ac8dfSFabio M. De Francesco 	memcpy_page(dst_page, dst, src_page, src, len);
1221da177e4SLinus Torvalds 	set_page_dirty(dst_page);
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
hfs_bnode_move(struct hfs_bnode * node,int dst,int src,int len)1251da177e4SLinus Torvalds void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	struct page *page;
1281da177e4SLinus Torvalds 	void *ptr;
1291da177e4SLinus Torvalds 
130c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
1311da177e4SLinus Torvalds 	if (!len)
1321da177e4SLinus Torvalds 		return;
1331da177e4SLinus Torvalds 	src += node->page_offset;
1341da177e4SLinus Torvalds 	dst += node->page_offset;
1351da177e4SLinus Torvalds 	page = node->page[0];
136ca0ac8dfSFabio M. De Francesco 	ptr = kmap_local_page(page);
1371da177e4SLinus Torvalds 	memmove(ptr + dst, ptr + src, len);
138ca0ac8dfSFabio M. De Francesco 	kunmap_local(ptr);
1391da177e4SLinus Torvalds 	set_page_dirty(page);
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds 
hfs_bnode_dump(struct hfs_bnode * node)1421da177e4SLinus Torvalds void hfs_bnode_dump(struct hfs_bnode *node)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds 	struct hfs_bnode_desc desc;
1451da177e4SLinus Torvalds 	__be32 cnid;
1461da177e4SLinus Torvalds 	int i, off, key_off;
1471da177e4SLinus Torvalds 
148c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "bnode: %d\n", node->this);
1491da177e4SLinus Torvalds 	hfs_bnode_read(node, &desc, 0, sizeof(desc));
150c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "%d, %d, %d, %d, %d\n",
1511da177e4SLinus Torvalds 		be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
1521da177e4SLinus Torvalds 		desc.type, desc.height, be16_to_cpu(desc.num_recs));
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	off = node->tree->node_size - 2;
1551da177e4SLinus Torvalds 	for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) {
1561da177e4SLinus Torvalds 		key_off = hfs_bnode_read_u16(node, off);
157c2b3e1f7SJoe Perches 		hfs_dbg_cont(BNODE_MOD, " %d", key_off);
1581da177e4SLinus Torvalds 		if (i && node->type == HFS_NODE_INDEX) {
1591da177e4SLinus Torvalds 			int tmp;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 			if (node->tree->attributes & HFS_TREE_VARIDXKEYS)
1621da177e4SLinus Torvalds 				tmp = (hfs_bnode_read_u8(node, key_off) | 1) + 1;
1631da177e4SLinus Torvalds 			else
1641da177e4SLinus Torvalds 				tmp = node->tree->max_key_len + 1;
165c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, " (%d,%d",
166c2b3e1f7SJoe Perches 				     tmp, hfs_bnode_read_u8(node, key_off));
1671da177e4SLinus Torvalds 			hfs_bnode_read(node, &cnid, key_off + tmp, 4);
168c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, ",%d)", be32_to_cpu(cnid));
1691da177e4SLinus Torvalds 		} else if (i && node->type == HFS_NODE_LEAF) {
1701da177e4SLinus Torvalds 			int tmp;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 			tmp = hfs_bnode_read_u8(node, key_off);
173c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, " (%d)", tmp);
1741da177e4SLinus Torvalds 		}
1751da177e4SLinus Torvalds 	}
176c2b3e1f7SJoe Perches 	hfs_dbg_cont(BNODE_MOD, "\n");
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
hfs_bnode_unlink(struct hfs_bnode * node)1791da177e4SLinus Torvalds void hfs_bnode_unlink(struct hfs_bnode *node)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	struct hfs_btree *tree;
1821da177e4SLinus Torvalds 	struct hfs_bnode *tmp;
1831da177e4SLinus Torvalds 	__be32 cnid;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	tree = node->tree;
1861da177e4SLinus Torvalds 	if (node->prev) {
1871da177e4SLinus Torvalds 		tmp = hfs_bnode_find(tree, node->prev);
1881da177e4SLinus Torvalds 		if (IS_ERR(tmp))
1891da177e4SLinus Torvalds 			return;
1901da177e4SLinus Torvalds 		tmp->next = node->next;
1911da177e4SLinus Torvalds 		cnid = cpu_to_be32(tmp->next);
1921da177e4SLinus Torvalds 		hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
1931da177e4SLinus Torvalds 		hfs_bnode_put(tmp);
1941da177e4SLinus Torvalds 	} else if (node->type == HFS_NODE_LEAF)
1951da177e4SLinus Torvalds 		tree->leaf_head = node->next;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	if (node->next) {
1981da177e4SLinus Torvalds 		tmp = hfs_bnode_find(tree, node->next);
1991da177e4SLinus Torvalds 		if (IS_ERR(tmp))
2001da177e4SLinus Torvalds 			return;
2011da177e4SLinus Torvalds 		tmp->prev = node->prev;
2021da177e4SLinus Torvalds 		cnid = cpu_to_be32(tmp->prev);
2031da177e4SLinus Torvalds 		hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4);
2041da177e4SLinus Torvalds 		hfs_bnode_put(tmp);
2051da177e4SLinus Torvalds 	} else if (node->type == HFS_NODE_LEAF)
2061da177e4SLinus Torvalds 		tree->leaf_tail = node->prev;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	// move down?
2091da177e4SLinus Torvalds 	if (!node->prev && !node->next) {
2107cf3cc30SRoman Zippel 		printk(KERN_DEBUG "hfs_btree_del_level\n");
2111da177e4SLinus Torvalds 	}
2121da177e4SLinus Torvalds 	if (!node->parent) {
2131da177e4SLinus Torvalds 		tree->root = 0;
2141da177e4SLinus Torvalds 		tree->depth = 0;
2151da177e4SLinus Torvalds 	}
2161da177e4SLinus Torvalds 	set_bit(HFS_BNODE_DELETED, &node->flags);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
hfs_bnode_hash(u32 num)2191da177e4SLinus Torvalds static inline int hfs_bnode_hash(u32 num)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds 	num = (num >> 16) + num;
2221da177e4SLinus Torvalds 	num += num >> 8;
2231da177e4SLinus Torvalds 	return num & (NODE_HASH_SIZE - 1);
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds 
hfs_bnode_findhash(struct hfs_btree * tree,u32 cnid)2261da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	struct hfs_bnode *node;
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	if (cnid >= tree->node_count) {
231d6142673SJoe Perches 		pr_err("request for non-existent node %d in B*Tree\n", cnid);
2321da177e4SLinus Torvalds 		return NULL;
2331da177e4SLinus Torvalds 	}
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	for (node = tree->node_hash[hfs_bnode_hash(cnid)];
2361da177e4SLinus Torvalds 	     node; node = node->next_hash) {
2371da177e4SLinus Torvalds 		if (node->this == cnid) {
2381da177e4SLinus Torvalds 			return node;
2391da177e4SLinus Torvalds 		}
2401da177e4SLinus Torvalds 	}
2411da177e4SLinus Torvalds 	return NULL;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
__hfs_bnode_create(struct hfs_btree * tree,u32 cnid)2441da177e4SLinus Torvalds static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
2451da177e4SLinus Torvalds {
2461da177e4SLinus Torvalds 	struct hfs_bnode *node, *node2;
2471da177e4SLinus Torvalds 	struct address_space *mapping;
2481da177e4SLinus Torvalds 	struct page *page;
2491da177e4SLinus Torvalds 	int size, block, i, hash;
2501da177e4SLinus Torvalds 	loff_t off;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	if (cnid >= tree->node_count) {
253d6142673SJoe Perches 		pr_err("request for non-existent node %d in B*Tree\n", cnid);
2541da177e4SLinus Torvalds 		return NULL;
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 	size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
2581da177e4SLinus Torvalds 		sizeof(struct page *);
259f8314dc6SPanagiotis Issaris 	node = kzalloc(size, GFP_KERNEL);
2601da177e4SLinus Torvalds 	if (!node)
2611da177e4SLinus Torvalds 		return NULL;
2621da177e4SLinus Torvalds 	node->tree = tree;
2631da177e4SLinus Torvalds 	node->this = cnid;
2641da177e4SLinus Torvalds 	set_bit(HFS_BNODE_NEW, &node->flags);
2651da177e4SLinus Torvalds 	atomic_set(&node->refcnt, 1);
266c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_REFS, "new_node(%d:%d): 1\n",
2671da177e4SLinus Torvalds 		node->tree->cnid, node->this);
2681da177e4SLinus Torvalds 	init_waitqueue_head(&node->lock_wq);
2691da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
2701da177e4SLinus Torvalds 	node2 = hfs_bnode_findhash(tree, cnid);
2711da177e4SLinus Torvalds 	if (!node2) {
2721da177e4SLinus Torvalds 		hash = hfs_bnode_hash(cnid);
2731da177e4SLinus Torvalds 		node->next_hash = tree->node_hash[hash];
2741da177e4SLinus Torvalds 		tree->node_hash[hash] = node;
2751da177e4SLinus Torvalds 		tree->node_hash_cnt++;
2761da177e4SLinus Torvalds 	} else {
277*a9dc087fSLiu Shixin 		hfs_bnode_get(node2);
2781da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
2791da177e4SLinus Torvalds 		kfree(node);
2801da177e4SLinus Torvalds 		wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags));
2811da177e4SLinus Torvalds 		return node2;
2821da177e4SLinus Torvalds 	}
2831da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	mapping = tree->inode->i_mapping;
2861da177e4SLinus Torvalds 	off = (loff_t)cnid * tree->node_size;
28709cbfeafSKirill A. Shutemov 	block = off >> PAGE_SHIFT;
28809cbfeafSKirill A. Shutemov 	node->page_offset = off & ~PAGE_MASK;
2891da177e4SLinus Torvalds 	for (i = 0; i < tree->pages_per_bnode; i++) {
290090d2b18SPekka Enberg 		page = read_mapping_page(mapping, block++, NULL);
2911da177e4SLinus Torvalds 		if (IS_ERR(page))
2921da177e4SLinus Torvalds 			goto fail;
2931da177e4SLinus Torvalds 		node->page[i] = page;
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	return node;
2971da177e4SLinus Torvalds fail:
2981da177e4SLinus Torvalds 	set_bit(HFS_BNODE_ERROR, &node->flags);
2991da177e4SLinus Torvalds 	return node;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
hfs_bnode_unhash(struct hfs_bnode * node)3021da177e4SLinus Torvalds void hfs_bnode_unhash(struct hfs_bnode *node)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct hfs_bnode **p;
3051da177e4SLinus Torvalds 
306c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_REFS, "remove_node(%d:%d): %d\n",
3071da177e4SLinus Torvalds 		node->tree->cnid, node->this, atomic_read(&node->refcnt));
3081da177e4SLinus Torvalds 	for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)];
3091da177e4SLinus Torvalds 	     *p && *p != node; p = &(*p)->next_hash)
3101da177e4SLinus Torvalds 		;
3114d4ef9abSEric Sesterhenn 	BUG_ON(!*p);
3121da177e4SLinus Torvalds 	*p = node->next_hash;
3131da177e4SLinus Torvalds 	node->tree->node_hash_cnt--;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds /* Load a particular node out of a tree */
hfs_bnode_find(struct hfs_btree * tree,u32 num)3171da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num)
3181da177e4SLinus Torvalds {
3191da177e4SLinus Torvalds 	struct hfs_bnode *node;
3201da177e4SLinus Torvalds 	struct hfs_bnode_desc *desc;
3211da177e4SLinus Torvalds 	int i, rec_off, off, next_off;
3221da177e4SLinus Torvalds 	int entry_size, key_size;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
3251da177e4SLinus Torvalds 	node = hfs_bnode_findhash(tree, num);
3261da177e4SLinus Torvalds 	if (node) {
3271da177e4SLinus Torvalds 		hfs_bnode_get(node);
3281da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
3291da177e4SLinus Torvalds 		wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags));
3301da177e4SLinus Torvalds 		if (test_bit(HFS_BNODE_ERROR, &node->flags))
3311da177e4SLinus Torvalds 			goto node_error;
3321da177e4SLinus Torvalds 		return node;
3331da177e4SLinus Torvalds 	}
3341da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
3351da177e4SLinus Torvalds 	node = __hfs_bnode_create(tree, num);
3361da177e4SLinus Torvalds 	if (!node)
3371da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
3381da177e4SLinus Torvalds 	if (test_bit(HFS_BNODE_ERROR, &node->flags))
3391da177e4SLinus Torvalds 		goto node_error;
3401da177e4SLinus Torvalds 	if (!test_bit(HFS_BNODE_NEW, &node->flags))
3411da177e4SLinus Torvalds 		return node;
3421da177e4SLinus Torvalds 
343ca0ac8dfSFabio M. De Francesco 	desc = (struct hfs_bnode_desc *)(kmap_local_page(node->page[0]) +
344ca0ac8dfSFabio M. De Francesco 					 node->page_offset);
3451da177e4SLinus Torvalds 	node->prev = be32_to_cpu(desc->prev);
3461da177e4SLinus Torvalds 	node->next = be32_to_cpu(desc->next);
3471da177e4SLinus Torvalds 	node->num_recs = be16_to_cpu(desc->num_recs);
3481da177e4SLinus Torvalds 	node->type = desc->type;
3491da177e4SLinus Torvalds 	node->height = desc->height;
350ca0ac8dfSFabio M. De Francesco 	kunmap_local(desc);
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	switch (node->type) {
3531da177e4SLinus Torvalds 	case HFS_NODE_HEADER:
3541da177e4SLinus Torvalds 	case HFS_NODE_MAP:
3551da177e4SLinus Torvalds 		if (node->height != 0)
3561da177e4SLinus Torvalds 			goto node_error;
3571da177e4SLinus Torvalds 		break;
3581da177e4SLinus Torvalds 	case HFS_NODE_LEAF:
3591da177e4SLinus Torvalds 		if (node->height != 1)
3601da177e4SLinus Torvalds 			goto node_error;
3611da177e4SLinus Torvalds 		break;
3621da177e4SLinus Torvalds 	case HFS_NODE_INDEX:
3631da177e4SLinus Torvalds 		if (node->height <= 1 || node->height > tree->depth)
3641da177e4SLinus Torvalds 			goto node_error;
3651da177e4SLinus Torvalds 		break;
3661da177e4SLinus Torvalds 	default:
3671da177e4SLinus Torvalds 		goto node_error;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	rec_off = tree->node_size - 2;
3711da177e4SLinus Torvalds 	off = hfs_bnode_read_u16(node, rec_off);
3721da177e4SLinus Torvalds 	if (off != sizeof(struct hfs_bnode_desc))
3731da177e4SLinus Torvalds 		goto node_error;
3741da177e4SLinus Torvalds 	for (i = 1; i <= node->num_recs; off = next_off, i++) {
3751da177e4SLinus Torvalds 		rec_off -= 2;
3761da177e4SLinus Torvalds 		next_off = hfs_bnode_read_u16(node, rec_off);
3771da177e4SLinus Torvalds 		if (next_off <= off ||
3781da177e4SLinus Torvalds 		    next_off > tree->node_size ||
3791da177e4SLinus Torvalds 		    next_off & 1)
3801da177e4SLinus Torvalds 			goto node_error;
3811da177e4SLinus Torvalds 		entry_size = next_off - off;
3821da177e4SLinus Torvalds 		if (node->type != HFS_NODE_INDEX &&
3831da177e4SLinus Torvalds 		    node->type != HFS_NODE_LEAF)
3841da177e4SLinus Torvalds 			continue;
3851da177e4SLinus Torvalds 		key_size = hfs_bnode_read_u8(node, off) + 1;
3861da177e4SLinus Torvalds 		if (key_size >= entry_size /*|| key_size & 1*/)
3871da177e4SLinus Torvalds 			goto node_error;
3881da177e4SLinus Torvalds 	}
3891da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
3901da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
3911da177e4SLinus Torvalds 	return node;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds node_error:
3941da177e4SLinus Torvalds 	set_bit(HFS_BNODE_ERROR, &node->flags);
3951da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
3961da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
3971da177e4SLinus Torvalds 	hfs_bnode_put(node);
3981da177e4SLinus Torvalds 	return ERR_PTR(-EIO);
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
hfs_bnode_free(struct hfs_bnode * node)4011da177e4SLinus Torvalds void hfs_bnode_free(struct hfs_bnode *node)
4021da177e4SLinus Torvalds {
4037cb74be6SHin-Tak Leung 	int i;
4041da177e4SLinus Torvalds 
4057cb74be6SHin-Tak Leung 	for (i = 0; i < node->tree->pages_per_bnode; i++)
4067cb74be6SHin-Tak Leung 		if (node->page[i])
40709cbfeafSKirill A. Shutemov 			put_page(node->page[i]);
4081da177e4SLinus Torvalds 	kfree(node);
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
hfs_bnode_create(struct hfs_btree * tree,u32 num)4111da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	struct hfs_bnode *node;
4141da177e4SLinus Torvalds 	struct page **pagep;
4151da177e4SLinus Torvalds 	int i;
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
4181da177e4SLinus Torvalds 	node = hfs_bnode_findhash(tree, num);
4191da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
420fb09c373SJeff Mahoney 	if (node) {
421fb09c373SJeff Mahoney 		pr_crit("new node %u already hashed?\n", num);
422fb09c373SJeff Mahoney 		WARN_ON(1);
423fb09c373SJeff Mahoney 		return node;
424fb09c373SJeff Mahoney 	}
4251da177e4SLinus Torvalds 	node = __hfs_bnode_create(tree, num);
4261da177e4SLinus Torvalds 	if (!node)
4271da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
4281da177e4SLinus Torvalds 	if (test_bit(HFS_BNODE_ERROR, &node->flags)) {
4291da177e4SLinus Torvalds 		hfs_bnode_put(node);
4301da177e4SLinus Torvalds 		return ERR_PTR(-EIO);
4311da177e4SLinus Torvalds 	}
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	pagep = node->page;
434ca0ac8dfSFabio M. De Francesco 	memzero_page(*pagep, node->page_offset,
43509cbfeafSKirill A. Shutemov 		     min((int)PAGE_SIZE, (int)tree->node_size));
4361da177e4SLinus Torvalds 	set_page_dirty(*pagep);
4371da177e4SLinus Torvalds 	for (i = 1; i < tree->pages_per_bnode; i++) {
438ca0ac8dfSFabio M. De Francesco 		memzero_page(*++pagep, 0, PAGE_SIZE);
4391da177e4SLinus Torvalds 		set_page_dirty(*pagep);
4401da177e4SLinus Torvalds 	}
4411da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
4421da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	return node;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
hfs_bnode_get(struct hfs_bnode * node)4471da177e4SLinus Torvalds void hfs_bnode_get(struct hfs_bnode *node)
4481da177e4SLinus Torvalds {
4491da177e4SLinus Torvalds 	if (node) {
4501da177e4SLinus Torvalds 		atomic_inc(&node->refcnt);
451c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_REFS, "get_node(%d:%d): %d\n",
452c2b3e1f7SJoe Perches 			node->tree->cnid, node->this,
453c2b3e1f7SJoe Perches 			atomic_read(&node->refcnt));
4541da177e4SLinus Torvalds 	}
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds /* Dispose of resources used by a node */
hfs_bnode_put(struct hfs_bnode * node)4581da177e4SLinus Torvalds void hfs_bnode_put(struct hfs_bnode *node)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	if (node) {
4611da177e4SLinus Torvalds 		struct hfs_btree *tree = node->tree;
4621da177e4SLinus Torvalds 		int i;
4631da177e4SLinus Torvalds 
464c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_REFS, "put_node(%d:%d): %d\n",
465c2b3e1f7SJoe Perches 			node->tree->cnid, node->this,
466c2b3e1f7SJoe Perches 			atomic_read(&node->refcnt));
4674d4ef9abSEric Sesterhenn 		BUG_ON(!atomic_read(&node->refcnt));
468a5e3985fSRoman Zippel 		if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock))
4691da177e4SLinus Torvalds 			return;
4701da177e4SLinus Torvalds 		for (i = 0; i < tree->pages_per_bnode; i++) {
47174f9c9c2SRoman Zippel 			if (!node->page[i])
47274f9c9c2SRoman Zippel 				continue;
4731da177e4SLinus Torvalds 			mark_page_accessed(node->page[i]);
4741da177e4SLinus Torvalds 		}
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 		if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
4771da177e4SLinus Torvalds 			hfs_bnode_unhash(node);
4781da177e4SLinus Torvalds 			spin_unlock(&tree->hash_lock);
4791da177e4SLinus Torvalds 			hfs_bmap_free(node);
4801da177e4SLinus Torvalds 			hfs_bnode_free(node);
4811da177e4SLinus Torvalds 			return;
4821da177e4SLinus Torvalds 		}
4831da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
4841da177e4SLinus Torvalds 	}
4851da177e4SLinus Torvalds }
486