xref: /openbmc/linux/fs/hfsplus/bnode.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/hfsplus/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/string.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/pagemap.h>
151da177e4SLinus Torvalds #include <linux/fs.h>
161da177e4SLinus Torvalds #include <linux/swap.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include "hfsplus_fs.h"
191da177e4SLinus Torvalds #include "hfsplus_raw.h"
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds /* Copy a specified range of bytes from the raw data of a node */
hfs_bnode_read(struct hfs_bnode * node,void * buf,int off,int len)221da177e4SLinus Torvalds void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
231da177e4SLinus Torvalds {
241da177e4SLinus Torvalds 	struct page **pagep;
251da177e4SLinus Torvalds 	int l;
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds 	off += node->page_offset;
2809cbfeafSKirill A. Shutemov 	pagep = node->page + (off >> PAGE_SHIFT);
2909cbfeafSKirill A. Shutemov 	off &= ~PAGE_MASK;
301da177e4SLinus Torvalds 
3109cbfeafSKirill A. Shutemov 	l = min_t(int, len, PAGE_SIZE - off);
32*6c3014a6SFabio M. De Francesco 	memcpy_from_page(buf, *pagep, off, l);
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds 	while ((len -= l) != 0) {
351da177e4SLinus Torvalds 		buf += l;
3609cbfeafSKirill A. Shutemov 		l = min_t(int, len, PAGE_SIZE);
37*6c3014a6SFabio M. De Francesco 		memcpy_from_page(buf, *++pagep, 0, l);
381da177e4SLinus Torvalds 	}
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
hfs_bnode_read_u16(struct hfs_bnode * node,int off)411da177e4SLinus Torvalds u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	__be16 data;
4421f2296aSAnton Salikhmetov 	/* TODO: optimize later... */
451da177e4SLinus Torvalds 	hfs_bnode_read(node, &data, off, 2);
461da177e4SLinus Torvalds 	return be16_to_cpu(data);
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
hfs_bnode_read_u8(struct hfs_bnode * node,int off)491da177e4SLinus Torvalds u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	u8 data;
5221f2296aSAnton Salikhmetov 	/* TODO: optimize later... */
531da177e4SLinus Torvalds 	hfs_bnode_read(node, &data, off, 1);
541da177e4SLinus Torvalds 	return data;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
hfs_bnode_read_key(struct hfs_bnode * node,void * key,int off)571da177e4SLinus Torvalds void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	struct hfs_btree *tree;
601da177e4SLinus Torvalds 	int key_len;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	tree = node->tree;
631da177e4SLinus Torvalds 	if (node->type == HFS_NODE_LEAF ||
64324ef39aSVyacheslav Dubeyko 	    tree->attributes & HFS_TREE_VARIDXKEYS ||
65324ef39aSVyacheslav Dubeyko 	    node->tree->cnid == HFSPLUS_ATTR_CNID)
661da177e4SLinus Torvalds 		key_len = hfs_bnode_read_u16(node, off) + 2;
671da177e4SLinus Torvalds 	else
681da177e4SLinus Torvalds 		key_len = tree->max_key_len + 2;
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 **pagep;
761da177e4SLinus Torvalds 	int l;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	off += node->page_offset;
7909cbfeafSKirill A. Shutemov 	pagep = node->page + (off >> PAGE_SHIFT);
8009cbfeafSKirill A. Shutemov 	off &= ~PAGE_MASK;
811da177e4SLinus Torvalds 
8209cbfeafSKirill A. Shutemov 	l = min_t(int, len, PAGE_SIZE - off);
83*6c3014a6SFabio M. De Francesco 	memcpy_to_page(*pagep, off, buf, l);
841da177e4SLinus Torvalds 	set_page_dirty(*pagep);
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	while ((len -= l) != 0) {
871da177e4SLinus Torvalds 		buf += l;
8809cbfeafSKirill A. Shutemov 		l = min_t(int, len, PAGE_SIZE);
89*6c3014a6SFabio M. De Francesco 		memcpy_to_page(*++pagep, 0, buf, l);
901da177e4SLinus Torvalds 		set_page_dirty(*pagep);
911da177e4SLinus Torvalds 	}
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
hfs_bnode_write_u16(struct hfs_bnode * node,int off,u16 data)941da177e4SLinus Torvalds void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds 	__be16 v = cpu_to_be16(data);
9721f2296aSAnton Salikhmetov 	/* TODO: optimize later... */
981da177e4SLinus Torvalds 	hfs_bnode_write(node, &v, off, 2);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds 
hfs_bnode_clear(struct hfs_bnode * node,int off,int len)1011da177e4SLinus Torvalds void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	struct page **pagep;
1041da177e4SLinus Torvalds 	int l;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	off += node->page_offset;
10709cbfeafSKirill A. Shutemov 	pagep = node->page + (off >> PAGE_SHIFT);
10809cbfeafSKirill A. Shutemov 	off &= ~PAGE_MASK;
1091da177e4SLinus Torvalds 
11009cbfeafSKirill A. Shutemov 	l = min_t(int, len, PAGE_SIZE - off);
111*6c3014a6SFabio M. De Francesco 	memzero_page(*pagep, off, l);
1121da177e4SLinus Torvalds 	set_page_dirty(*pagep);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	while ((len -= l) != 0) {
11509cbfeafSKirill A. Shutemov 		l = min_t(int, len, PAGE_SIZE);
116*6c3014a6SFabio M. De Francesco 		memzero_page(*++pagep, 0, l);
1171da177e4SLinus Torvalds 		set_page_dirty(*pagep);
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds 
hfs_bnode_copy(struct hfs_bnode * dst_node,int dst,struct hfs_bnode * src_node,int src,int len)1211da177e4SLinus Torvalds void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
1221da177e4SLinus Torvalds 		    struct hfs_bnode *src_node, int src, int len)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	struct page **src_page, **dst_page;
1251da177e4SLinus Torvalds 	int l;
1261da177e4SLinus Torvalds 
127c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
1281da177e4SLinus Torvalds 	if (!len)
1291da177e4SLinus Torvalds 		return;
1301da177e4SLinus Torvalds 	src += src_node->page_offset;
1311da177e4SLinus Torvalds 	dst += dst_node->page_offset;
13209cbfeafSKirill A. Shutemov 	src_page = src_node->page + (src >> PAGE_SHIFT);
13309cbfeafSKirill A. Shutemov 	src &= ~PAGE_MASK;
13409cbfeafSKirill A. Shutemov 	dst_page = dst_node->page + (dst >> PAGE_SHIFT);
13509cbfeafSKirill A. Shutemov 	dst &= ~PAGE_MASK;
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	if (src == dst) {
13809cbfeafSKirill A. Shutemov 		l = min_t(int, len, PAGE_SIZE - src);
139*6c3014a6SFabio M. De Francesco 		memcpy_page(*dst_page, src, *src_page, src, l);
1401da177e4SLinus Torvalds 		set_page_dirty(*dst_page);
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 		while ((len -= l) != 0) {
14309cbfeafSKirill A. Shutemov 			l = min_t(int, len, PAGE_SIZE);
144*6c3014a6SFabio M. De Francesco 			memcpy_page(*++dst_page, 0, *++src_page, 0, l);
1451da177e4SLinus Torvalds 			set_page_dirty(*dst_page);
1461da177e4SLinus Torvalds 		}
1471da177e4SLinus Torvalds 	} else {
1481da177e4SLinus Torvalds 		void *src_ptr, *dst_ptr;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 		do {
151*6c3014a6SFabio M. De Francesco 			dst_ptr = kmap_local_page(*dst_page) + dst;
152*6c3014a6SFabio M. De Francesco 			src_ptr = kmap_local_page(*src_page) + src;
15309cbfeafSKirill A. Shutemov 			if (PAGE_SIZE - src < PAGE_SIZE - dst) {
15409cbfeafSKirill A. Shutemov 				l = PAGE_SIZE - src;
1551da177e4SLinus Torvalds 				src = 0;
1561da177e4SLinus Torvalds 				dst += l;
1571da177e4SLinus Torvalds 			} else {
15809cbfeafSKirill A. Shutemov 				l = PAGE_SIZE - dst;
1591da177e4SLinus Torvalds 				src += l;
1601da177e4SLinus Torvalds 				dst = 0;
1611da177e4SLinus Torvalds 			}
1621da177e4SLinus Torvalds 			l = min(len, l);
1631da177e4SLinus Torvalds 			memcpy(dst_ptr, src_ptr, l);
164*6c3014a6SFabio M. De Francesco 			kunmap_local(src_ptr);
1651da177e4SLinus Torvalds 			set_page_dirty(*dst_page);
166*6c3014a6SFabio M. De Francesco 			kunmap_local(dst_ptr);
1671da177e4SLinus Torvalds 			if (!dst)
1681da177e4SLinus Torvalds 				dst_page++;
1691da177e4SLinus Torvalds 			else
1701da177e4SLinus Torvalds 				src_page++;
1711da177e4SLinus Torvalds 		} while ((len -= l));
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
hfs_bnode_move(struct hfs_bnode * node,int dst,int src,int len)1751da177e4SLinus Torvalds void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	struct page **src_page, **dst_page;
178*6c3014a6SFabio M. De Francesco 	void *src_ptr, *dst_ptr;
1791da177e4SLinus Torvalds 	int l;
1801da177e4SLinus Torvalds 
181c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
1821da177e4SLinus Torvalds 	if (!len)
1831da177e4SLinus Torvalds 		return;
1841da177e4SLinus Torvalds 	src += node->page_offset;
1851da177e4SLinus Torvalds 	dst += node->page_offset;
1861da177e4SLinus Torvalds 	if (dst > src) {
1871da177e4SLinus Torvalds 		src += len - 1;
18809cbfeafSKirill A. Shutemov 		src_page = node->page + (src >> PAGE_SHIFT);
18909cbfeafSKirill A. Shutemov 		src = (src & ~PAGE_MASK) + 1;
1901da177e4SLinus Torvalds 		dst += len - 1;
19109cbfeafSKirill A. Shutemov 		dst_page = node->page + (dst >> PAGE_SHIFT);
19209cbfeafSKirill A. Shutemov 		dst = (dst & ~PAGE_MASK) + 1;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 		if (src == dst) {
1951da177e4SLinus Torvalds 			while (src < len) {
196*6c3014a6SFabio M. De Francesco 				dst_ptr = kmap_local_page(*dst_page);
197*6c3014a6SFabio M. De Francesco 				src_ptr = kmap_local_page(*src_page);
198*6c3014a6SFabio M. De Francesco 				memmove(dst_ptr, src_ptr, src);
199*6c3014a6SFabio M. De Francesco 				kunmap_local(src_ptr);
2001da177e4SLinus Torvalds 				set_page_dirty(*dst_page);
201*6c3014a6SFabio M. De Francesco 				kunmap_local(dst_ptr);
2021da177e4SLinus Torvalds 				len -= src;
20309cbfeafSKirill A. Shutemov 				src = PAGE_SIZE;
2041da177e4SLinus Torvalds 				src_page--;
2051da177e4SLinus Torvalds 				dst_page--;
2061da177e4SLinus Torvalds 			}
2071da177e4SLinus Torvalds 			src -= len;
208*6c3014a6SFabio M. De Francesco 			dst_ptr = kmap_local_page(*dst_page);
209*6c3014a6SFabio M. De Francesco 			src_ptr = kmap_local_page(*src_page);
210*6c3014a6SFabio M. De Francesco 			memmove(dst_ptr + src, src_ptr + src, len);
211*6c3014a6SFabio M. De Francesco 			kunmap_local(src_ptr);
2121da177e4SLinus Torvalds 			set_page_dirty(*dst_page);
213*6c3014a6SFabio M. De Francesco 			kunmap_local(dst_ptr);
2141da177e4SLinus Torvalds 		} else {
2151da177e4SLinus Torvalds 			do {
216*6c3014a6SFabio M. De Francesco 				dst_ptr = kmap_local_page(*dst_page) + dst;
217*6c3014a6SFabio M. De Francesco 				src_ptr = kmap_local_page(*src_page) + src;
2181da177e4SLinus Torvalds 				if (src < dst) {
2191da177e4SLinus Torvalds 					l = src;
22009cbfeafSKirill A. Shutemov 					src = PAGE_SIZE;
2211da177e4SLinus Torvalds 					dst -= l;
2221da177e4SLinus Torvalds 				} else {
2231da177e4SLinus Torvalds 					l = dst;
2241da177e4SLinus Torvalds 					src -= l;
22509cbfeafSKirill A. Shutemov 					dst = PAGE_SIZE;
2261da177e4SLinus Torvalds 				}
2271da177e4SLinus Torvalds 				l = min(len, l);
2281da177e4SLinus Torvalds 				memmove(dst_ptr - l, src_ptr - l, l);
229*6c3014a6SFabio M. De Francesco 				kunmap_local(src_ptr);
2301da177e4SLinus Torvalds 				set_page_dirty(*dst_page);
231*6c3014a6SFabio M. De Francesco 				kunmap_local(dst_ptr);
23209cbfeafSKirill A. Shutemov 				if (dst == PAGE_SIZE)
2331da177e4SLinus Torvalds 					dst_page--;
2341da177e4SLinus Torvalds 				else
2351da177e4SLinus Torvalds 					src_page--;
2361da177e4SLinus Torvalds 			} while ((len -= l));
2371da177e4SLinus Torvalds 		}
2381da177e4SLinus Torvalds 	} else {
23909cbfeafSKirill A. Shutemov 		src_page = node->page + (src >> PAGE_SHIFT);
24009cbfeafSKirill A. Shutemov 		src &= ~PAGE_MASK;
24109cbfeafSKirill A. Shutemov 		dst_page = node->page + (dst >> PAGE_SHIFT);
24209cbfeafSKirill A. Shutemov 		dst &= ~PAGE_MASK;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 		if (src == dst) {
24509cbfeafSKirill A. Shutemov 			l = min_t(int, len, PAGE_SIZE - src);
246*6c3014a6SFabio M. De Francesco 
247*6c3014a6SFabio M. De Francesco 			dst_ptr = kmap_local_page(*dst_page) + src;
248*6c3014a6SFabio M. De Francesco 			src_ptr = kmap_local_page(*src_page) + src;
249*6c3014a6SFabio M. De Francesco 			memmove(dst_ptr, src_ptr, l);
250*6c3014a6SFabio M. De Francesco 			kunmap_local(src_ptr);
2511da177e4SLinus Torvalds 			set_page_dirty(*dst_page);
252*6c3014a6SFabio M. De Francesco 			kunmap_local(dst_ptr);
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 			while ((len -= l) != 0) {
25509cbfeafSKirill A. Shutemov 				l = min_t(int, len, PAGE_SIZE);
256*6c3014a6SFabio M. De Francesco 				dst_ptr = kmap_local_page(*++dst_page);
257*6c3014a6SFabio M. De Francesco 				src_ptr = kmap_local_page(*++src_page);
258*6c3014a6SFabio M. De Francesco 				memmove(dst_ptr, src_ptr, l);
259*6c3014a6SFabio M. De Francesco 				kunmap_local(src_ptr);
2601da177e4SLinus Torvalds 				set_page_dirty(*dst_page);
261*6c3014a6SFabio M. De Francesco 				kunmap_local(dst_ptr);
2621da177e4SLinus Torvalds 			}
2631da177e4SLinus Torvalds 		} else {
2641da177e4SLinus Torvalds 			do {
265*6c3014a6SFabio M. De Francesco 				dst_ptr = kmap_local_page(*dst_page) + dst;
266*6c3014a6SFabio M. De Francesco 				src_ptr = kmap_local_page(*src_page) + src;
26709cbfeafSKirill A. Shutemov 				if (PAGE_SIZE - src <
26809cbfeafSKirill A. Shutemov 						PAGE_SIZE - dst) {
26909cbfeafSKirill A. Shutemov 					l = PAGE_SIZE - src;
2701da177e4SLinus Torvalds 					src = 0;
2711da177e4SLinus Torvalds 					dst += l;
2721da177e4SLinus Torvalds 				} else {
27309cbfeafSKirill A. Shutemov 					l = PAGE_SIZE - dst;
2741da177e4SLinus Torvalds 					src += l;
2751da177e4SLinus Torvalds 					dst = 0;
2761da177e4SLinus Torvalds 				}
2771da177e4SLinus Torvalds 				l = min(len, l);
2781da177e4SLinus Torvalds 				memmove(dst_ptr, src_ptr, l);
279*6c3014a6SFabio M. De Francesco 				kunmap_local(src_ptr);
2801da177e4SLinus Torvalds 				set_page_dirty(*dst_page);
281*6c3014a6SFabio M. De Francesco 				kunmap_local(dst_ptr);
2821da177e4SLinus Torvalds 				if (!dst)
2831da177e4SLinus Torvalds 					dst_page++;
2841da177e4SLinus Torvalds 				else
2851da177e4SLinus Torvalds 					src_page++;
2861da177e4SLinus Torvalds 			} while ((len -= l));
2871da177e4SLinus Torvalds 		}
2881da177e4SLinus Torvalds 	}
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
hfs_bnode_dump(struct hfs_bnode * node)2911da177e4SLinus Torvalds void hfs_bnode_dump(struct hfs_bnode *node)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	struct hfs_bnode_desc desc;
2941da177e4SLinus Torvalds 	__be32 cnid;
2951da177e4SLinus Torvalds 	int i, off, key_off;
2961da177e4SLinus Torvalds 
297c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "bnode: %d\n", node->this);
2981da177e4SLinus Torvalds 	hfs_bnode_read(node, &desc, 0, sizeof(desc));
299c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_MOD, "%d, %d, %d, %d, %d\n",
3001da177e4SLinus Torvalds 		be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
3011da177e4SLinus Torvalds 		desc.type, desc.height, be16_to_cpu(desc.num_recs));
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	off = node->tree->node_size - 2;
3041da177e4SLinus Torvalds 	for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) {
3051da177e4SLinus Torvalds 		key_off = hfs_bnode_read_u16(node, off);
306c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_MOD, " %d", key_off);
3071da177e4SLinus Torvalds 		if (i && node->type == HFS_NODE_INDEX) {
3081da177e4SLinus Torvalds 			int tmp;
3091da177e4SLinus Torvalds 
310324ef39aSVyacheslav Dubeyko 			if (node->tree->attributes & HFS_TREE_VARIDXKEYS ||
311324ef39aSVyacheslav Dubeyko 					node->tree->cnid == HFSPLUS_ATTR_CNID)
3121da177e4SLinus Torvalds 				tmp = hfs_bnode_read_u16(node, key_off) + 2;
3131da177e4SLinus Torvalds 			else
3141da177e4SLinus Torvalds 				tmp = node->tree->max_key_len + 2;
315c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, " (%d", tmp);
3161da177e4SLinus Torvalds 			hfs_bnode_read(node, &cnid, key_off + tmp, 4);
317c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, ",%d)", be32_to_cpu(cnid));
3181da177e4SLinus Torvalds 		} else if (i && node->type == HFS_NODE_LEAF) {
3191da177e4SLinus Torvalds 			int tmp;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 			tmp = hfs_bnode_read_u16(node, key_off);
322c2b3e1f7SJoe Perches 			hfs_dbg_cont(BNODE_MOD, " (%d)", tmp);
3231da177e4SLinus Torvalds 		}
3241da177e4SLinus Torvalds 	}
325c2b3e1f7SJoe Perches 	hfs_dbg_cont(BNODE_MOD, "\n");
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
hfs_bnode_unlink(struct hfs_bnode * node)3281da177e4SLinus Torvalds void hfs_bnode_unlink(struct hfs_bnode *node)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	struct hfs_btree *tree;
3311da177e4SLinus Torvalds 	struct hfs_bnode *tmp;
3321da177e4SLinus Torvalds 	__be32 cnid;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	tree = node->tree;
3351da177e4SLinus Torvalds 	if (node->prev) {
3361da177e4SLinus Torvalds 		tmp = hfs_bnode_find(tree, node->prev);
3371da177e4SLinus Torvalds 		if (IS_ERR(tmp))
3381da177e4SLinus Torvalds 			return;
3391da177e4SLinus Torvalds 		tmp->next = node->next;
3401da177e4SLinus Torvalds 		cnid = cpu_to_be32(tmp->next);
3412753cc28SAnton Salikhmetov 		hfs_bnode_write(tmp, &cnid,
3422753cc28SAnton Salikhmetov 			offsetof(struct hfs_bnode_desc, next), 4);
3431da177e4SLinus Torvalds 		hfs_bnode_put(tmp);
3441da177e4SLinus Torvalds 	} else if (node->type == HFS_NODE_LEAF)
3451da177e4SLinus Torvalds 		tree->leaf_head = node->next;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	if (node->next) {
3481da177e4SLinus Torvalds 		tmp = hfs_bnode_find(tree, node->next);
3491da177e4SLinus Torvalds 		if (IS_ERR(tmp))
3501da177e4SLinus Torvalds 			return;
3511da177e4SLinus Torvalds 		tmp->prev = node->prev;
3521da177e4SLinus Torvalds 		cnid = cpu_to_be32(tmp->prev);
3532753cc28SAnton Salikhmetov 		hfs_bnode_write(tmp, &cnid,
3542753cc28SAnton Salikhmetov 			offsetof(struct hfs_bnode_desc, prev), 4);
3551da177e4SLinus Torvalds 		hfs_bnode_put(tmp);
3561da177e4SLinus Torvalds 	} else if (node->type == HFS_NODE_LEAF)
3571da177e4SLinus Torvalds 		tree->leaf_tail = node->prev;
3581da177e4SLinus Torvalds 
35921f2296aSAnton Salikhmetov 	/* move down? */
360b2837fcfSAnton Salikhmetov 	if (!node->prev && !node->next)
361c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_MOD, "hfs_btree_del_level\n");
3621da177e4SLinus Torvalds 	if (!node->parent) {
3631da177e4SLinus Torvalds 		tree->root = 0;
3641da177e4SLinus Torvalds 		tree->depth = 0;
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds 	set_bit(HFS_BNODE_DELETED, &node->flags);
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
hfs_bnode_hash(u32 num)3691da177e4SLinus Torvalds static inline int hfs_bnode_hash(u32 num)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds 	num = (num >> 16) + num;
3721da177e4SLinus Torvalds 	num += num >> 8;
3731da177e4SLinus Torvalds 	return num & (NODE_HASH_SIZE - 1);
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
hfs_bnode_findhash(struct hfs_btree * tree,u32 cnid)3761da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid)
3771da177e4SLinus Torvalds {
3781da177e4SLinus Torvalds 	struct hfs_bnode *node;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	if (cnid >= tree->node_count) {
381e46707d1SFabian Frederick 		pr_err("request for non-existent node %d in B*Tree\n",
3822753cc28SAnton Salikhmetov 		       cnid);
3831da177e4SLinus Torvalds 		return NULL;
3841da177e4SLinus Torvalds 	}
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	for (node = tree->node_hash[hfs_bnode_hash(cnid)];
387b2837fcfSAnton Salikhmetov 			node; node = node->next_hash)
388b2837fcfSAnton Salikhmetov 		if (node->this == cnid)
3891da177e4SLinus Torvalds 			return node;
3901da177e4SLinus Torvalds 	return NULL;
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds 
__hfs_bnode_create(struct hfs_btree * tree,u32 cnid)3931da177e4SLinus Torvalds static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
3941da177e4SLinus Torvalds {
3951da177e4SLinus Torvalds 	struct hfs_bnode *node, *node2;
3961da177e4SLinus Torvalds 	struct address_space *mapping;
3971da177e4SLinus Torvalds 	struct page *page;
3981da177e4SLinus Torvalds 	int size, block, i, hash;
3991da177e4SLinus Torvalds 	loff_t off;
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	if (cnid >= tree->node_count) {
402e46707d1SFabian Frederick 		pr_err("request for non-existent node %d in B*Tree\n",
4032753cc28SAnton Salikhmetov 		       cnid);
4041da177e4SLinus Torvalds 		return NULL;
4051da177e4SLinus Torvalds 	}
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
4081da177e4SLinus Torvalds 		sizeof(struct page *);
409f8314dc6SPanagiotis Issaris 	node = kzalloc(size, GFP_KERNEL);
4101da177e4SLinus Torvalds 	if (!node)
4111da177e4SLinus Torvalds 		return NULL;
4121da177e4SLinus Torvalds 	node->tree = tree;
4131da177e4SLinus Torvalds 	node->this = cnid;
4141da177e4SLinus Torvalds 	set_bit(HFS_BNODE_NEW, &node->flags);
4151da177e4SLinus Torvalds 	atomic_set(&node->refcnt, 1);
416c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_REFS, "new_node(%d:%d): 1\n",
4171da177e4SLinus Torvalds 		node->tree->cnid, node->this);
4181da177e4SLinus Torvalds 	init_waitqueue_head(&node->lock_wq);
4191da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
4201da177e4SLinus Torvalds 	node2 = hfs_bnode_findhash(tree, cnid);
4211da177e4SLinus Torvalds 	if (!node2) {
4221da177e4SLinus Torvalds 		hash = hfs_bnode_hash(cnid);
4231da177e4SLinus Torvalds 		node->next_hash = tree->node_hash[hash];
4241da177e4SLinus Torvalds 		tree->node_hash[hash] = node;
4251da177e4SLinus Torvalds 		tree->node_hash_cnt++;
4261da177e4SLinus Torvalds 	} else {
4271da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
4281da177e4SLinus Torvalds 		kfree(node);
4292753cc28SAnton Salikhmetov 		wait_event(node2->lock_wq,
4302753cc28SAnton Salikhmetov 			!test_bit(HFS_BNODE_NEW, &node2->flags));
4311da177e4SLinus Torvalds 		return node2;
4321da177e4SLinus Torvalds 	}
4331da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	mapping = tree->inode->i_mapping;
4361da177e4SLinus Torvalds 	off = (loff_t)cnid << tree->node_size_shift;
43709cbfeafSKirill A. Shutemov 	block = off >> PAGE_SHIFT;
43809cbfeafSKirill A. Shutemov 	node->page_offset = off & ~PAGE_MASK;
4391da177e4SLinus Torvalds 	for (i = 0; i < tree->pages_per_bnode; block++, i++) {
440090d2b18SPekka Enberg 		page = read_mapping_page(mapping, block, NULL);
4411da177e4SLinus Torvalds 		if (IS_ERR(page))
4421da177e4SLinus Torvalds 			goto fail;
4431da177e4SLinus Torvalds 		node->page[i] = page;
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	return node;
4471da177e4SLinus Torvalds fail:
4481da177e4SLinus Torvalds 	set_bit(HFS_BNODE_ERROR, &node->flags);
4491da177e4SLinus Torvalds 	return node;
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
hfs_bnode_unhash(struct hfs_bnode * node)4521da177e4SLinus Torvalds void hfs_bnode_unhash(struct hfs_bnode *node)
4531da177e4SLinus Torvalds {
4541da177e4SLinus Torvalds 	struct hfs_bnode **p;
4551da177e4SLinus Torvalds 
456c2b3e1f7SJoe Perches 	hfs_dbg(BNODE_REFS, "remove_node(%d:%d): %d\n",
4571da177e4SLinus Torvalds 		node->tree->cnid, node->this, atomic_read(&node->refcnt));
4581da177e4SLinus Torvalds 	for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)];
4591da177e4SLinus Torvalds 	     *p && *p != node; p = &(*p)->next_hash)
4601da177e4SLinus Torvalds 		;
4610bf3ba53SEric Sesterhenn 	BUG_ON(!*p);
4621da177e4SLinus Torvalds 	*p = node->next_hash;
4631da177e4SLinus Torvalds 	node->tree->node_hash_cnt--;
4641da177e4SLinus Torvalds }
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds /* Load a particular node out of a tree */
hfs_bnode_find(struct hfs_btree * tree,u32 num)4671da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num)
4681da177e4SLinus Torvalds {
4691da177e4SLinus Torvalds 	struct hfs_bnode *node;
4701da177e4SLinus Torvalds 	struct hfs_bnode_desc *desc;
4711da177e4SLinus Torvalds 	int i, rec_off, off, next_off;
4721da177e4SLinus Torvalds 	int entry_size, key_size;
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
4751da177e4SLinus Torvalds 	node = hfs_bnode_findhash(tree, num);
4761da177e4SLinus Torvalds 	if (node) {
4771da177e4SLinus Torvalds 		hfs_bnode_get(node);
4781da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
4792753cc28SAnton Salikhmetov 		wait_event(node->lock_wq,
4802753cc28SAnton Salikhmetov 			!test_bit(HFS_BNODE_NEW, &node->flags));
4811da177e4SLinus Torvalds 		if (test_bit(HFS_BNODE_ERROR, &node->flags))
4821da177e4SLinus Torvalds 			goto node_error;
4831da177e4SLinus Torvalds 		return node;
4841da177e4SLinus Torvalds 	}
4851da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
4861da177e4SLinus Torvalds 	node = __hfs_bnode_create(tree, num);
4871da177e4SLinus Torvalds 	if (!node)
4881da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
4891da177e4SLinus Torvalds 	if (test_bit(HFS_BNODE_ERROR, &node->flags))
4901da177e4SLinus Torvalds 		goto node_error;
4911da177e4SLinus Torvalds 	if (!test_bit(HFS_BNODE_NEW, &node->flags))
4921da177e4SLinus Torvalds 		return node;
4931da177e4SLinus Torvalds 
494*6c3014a6SFabio M. De Francesco 	desc = (struct hfs_bnode_desc *)(kmap_local_page(node->page[0]) +
4952753cc28SAnton Salikhmetov 							 node->page_offset);
4961da177e4SLinus Torvalds 	node->prev = be32_to_cpu(desc->prev);
4971da177e4SLinus Torvalds 	node->next = be32_to_cpu(desc->next);
4981da177e4SLinus Torvalds 	node->num_recs = be16_to_cpu(desc->num_recs);
4991da177e4SLinus Torvalds 	node->type = desc->type;
5001da177e4SLinus Torvalds 	node->height = desc->height;
501*6c3014a6SFabio M. De Francesco 	kunmap_local(desc);
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	switch (node->type) {
5041da177e4SLinus Torvalds 	case HFS_NODE_HEADER:
5051da177e4SLinus Torvalds 	case HFS_NODE_MAP:
5061da177e4SLinus Torvalds 		if (node->height != 0)
5071da177e4SLinus Torvalds 			goto node_error;
5081da177e4SLinus Torvalds 		break;
5091da177e4SLinus Torvalds 	case HFS_NODE_LEAF:
5101da177e4SLinus Torvalds 		if (node->height != 1)
5111da177e4SLinus Torvalds 			goto node_error;
5121da177e4SLinus Torvalds 		break;
5131da177e4SLinus Torvalds 	case HFS_NODE_INDEX:
5141da177e4SLinus Torvalds 		if (node->height <= 1 || node->height > tree->depth)
5151da177e4SLinus Torvalds 			goto node_error;
5161da177e4SLinus Torvalds 		break;
5171da177e4SLinus Torvalds 	default:
5181da177e4SLinus Torvalds 		goto node_error;
5191da177e4SLinus Torvalds 	}
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	rec_off = tree->node_size - 2;
5221da177e4SLinus Torvalds 	off = hfs_bnode_read_u16(node, rec_off);
5231da177e4SLinus Torvalds 	if (off != sizeof(struct hfs_bnode_desc))
5241da177e4SLinus Torvalds 		goto node_error;
5251da177e4SLinus Torvalds 	for (i = 1; i <= node->num_recs; off = next_off, i++) {
5261da177e4SLinus Torvalds 		rec_off -= 2;
5271da177e4SLinus Torvalds 		next_off = hfs_bnode_read_u16(node, rec_off);
5281da177e4SLinus Torvalds 		if (next_off <= off ||
5291da177e4SLinus Torvalds 		    next_off > tree->node_size ||
5301da177e4SLinus Torvalds 		    next_off & 1)
5311da177e4SLinus Torvalds 			goto node_error;
5321da177e4SLinus Torvalds 		entry_size = next_off - off;
5331da177e4SLinus Torvalds 		if (node->type != HFS_NODE_INDEX &&
5341da177e4SLinus Torvalds 		    node->type != HFS_NODE_LEAF)
5351da177e4SLinus Torvalds 			continue;
5361da177e4SLinus Torvalds 		key_size = hfs_bnode_read_u16(node, off) + 2;
5371da177e4SLinus Torvalds 		if (key_size >= entry_size || key_size & 1)
5381da177e4SLinus Torvalds 			goto node_error;
5391da177e4SLinus Torvalds 	}
5401da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
5411da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
5421da177e4SLinus Torvalds 	return node;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds node_error:
5451da177e4SLinus Torvalds 	set_bit(HFS_BNODE_ERROR, &node->flags);
5461da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
5471da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
5481da177e4SLinus Torvalds 	hfs_bnode_put(node);
5491da177e4SLinus Torvalds 	return ERR_PTR(-EIO);
5501da177e4SLinus Torvalds }
5511da177e4SLinus Torvalds 
hfs_bnode_free(struct hfs_bnode * node)5521da177e4SLinus Torvalds void hfs_bnode_free(struct hfs_bnode *node)
5531da177e4SLinus Torvalds {
55421f2296aSAnton Salikhmetov 	int i;
5551da177e4SLinus Torvalds 
55621f2296aSAnton Salikhmetov 	for (i = 0; i < node->tree->pages_per_bnode; i++)
55721f2296aSAnton Salikhmetov 		if (node->page[i])
55809cbfeafSKirill A. Shutemov 			put_page(node->page[i]);
5591da177e4SLinus Torvalds 	kfree(node);
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
hfs_bnode_create(struct hfs_btree * tree,u32 num)5621da177e4SLinus Torvalds struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)
5631da177e4SLinus Torvalds {
5641da177e4SLinus Torvalds 	struct hfs_bnode *node;
5651da177e4SLinus Torvalds 	struct page **pagep;
5661da177e4SLinus Torvalds 	int i;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	spin_lock(&tree->hash_lock);
5691da177e4SLinus Torvalds 	node = hfs_bnode_findhash(tree, num);
5701da177e4SLinus Torvalds 	spin_unlock(&tree->hash_lock);
5711da177e4SLinus Torvalds 	if (node) {
572d6142673SJoe Perches 		pr_crit("new node %u already hashed?\n", num);
573634725a9SRoman Zippel 		WARN_ON(1);
574634725a9SRoman Zippel 		return node;
5751da177e4SLinus Torvalds 	}
5761da177e4SLinus Torvalds 	node = __hfs_bnode_create(tree, num);
5771da177e4SLinus Torvalds 	if (!node)
5781da177e4SLinus Torvalds 		return ERR_PTR(-ENOMEM);
5791da177e4SLinus Torvalds 	if (test_bit(HFS_BNODE_ERROR, &node->flags)) {
5801da177e4SLinus Torvalds 		hfs_bnode_put(node);
5811da177e4SLinus Torvalds 		return ERR_PTR(-EIO);
5821da177e4SLinus Torvalds 	}
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	pagep = node->page;
585*6c3014a6SFabio M. De Francesco 	memzero_page(*pagep, node->page_offset,
58609cbfeafSKirill A. Shutemov 		     min_t(int, PAGE_SIZE, tree->node_size));
5871da177e4SLinus Torvalds 	set_page_dirty(*pagep);
5881da177e4SLinus Torvalds 	for (i = 1; i < tree->pages_per_bnode; i++) {
589*6c3014a6SFabio M. De Francesco 		memzero_page(*++pagep, 0, PAGE_SIZE);
5901da177e4SLinus Torvalds 		set_page_dirty(*pagep);
5911da177e4SLinus Torvalds 	}
5921da177e4SLinus Torvalds 	clear_bit(HFS_BNODE_NEW, &node->flags);
5931da177e4SLinus Torvalds 	wake_up(&node->lock_wq);
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 	return node;
5961da177e4SLinus Torvalds }
5971da177e4SLinus Torvalds 
hfs_bnode_get(struct hfs_bnode * node)5981da177e4SLinus Torvalds void hfs_bnode_get(struct hfs_bnode *node)
5991da177e4SLinus Torvalds {
6001da177e4SLinus Torvalds 	if (node) {
6011da177e4SLinus Torvalds 		atomic_inc(&node->refcnt);
602c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_REFS, "get_node(%d:%d): %d\n",
6032753cc28SAnton Salikhmetov 			node->tree->cnid, node->this,
6042753cc28SAnton Salikhmetov 			atomic_read(&node->refcnt));
6051da177e4SLinus Torvalds 	}
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds /* Dispose of resources used by a node */
hfs_bnode_put(struct hfs_bnode * node)6091da177e4SLinus Torvalds void hfs_bnode_put(struct hfs_bnode *node)
6101da177e4SLinus Torvalds {
6111da177e4SLinus Torvalds 	if (node) {
6121da177e4SLinus Torvalds 		struct hfs_btree *tree = node->tree;
6131da177e4SLinus Torvalds 		int i;
6141da177e4SLinus Torvalds 
615c2b3e1f7SJoe Perches 		hfs_dbg(BNODE_REFS, "put_node(%d:%d): %d\n",
6162753cc28SAnton Salikhmetov 			node->tree->cnid, node->this,
6172753cc28SAnton Salikhmetov 			atomic_read(&node->refcnt));
6180bf3ba53SEric Sesterhenn 		BUG_ON(!atomic_read(&node->refcnt));
619a5e3985fSRoman Zippel 		if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock))
6201da177e4SLinus Torvalds 			return;
6211da177e4SLinus Torvalds 		for (i = 0; i < tree->pages_per_bnode; i++) {
62274f9c9c2SRoman Zippel 			if (!node->page[i])
62374f9c9c2SRoman Zippel 				continue;
6241da177e4SLinus Torvalds 			mark_page_accessed(node->page[i]);
6251da177e4SLinus Torvalds 		}
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 		if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
6281da177e4SLinus Torvalds 			hfs_bnode_unhash(node);
6291da177e4SLinus Torvalds 			spin_unlock(&tree->hash_lock);
6302cd282a1SSergei Antonov 			if (hfs_bnode_need_zeroout(tree))
6312cd282a1SSergei Antonov 				hfs_bnode_clear(node, 0, tree->node_size);
6321da177e4SLinus Torvalds 			hfs_bmap_free(node);
6331da177e4SLinus Torvalds 			hfs_bnode_free(node);
6341da177e4SLinus Torvalds 			return;
6351da177e4SLinus Torvalds 		}
6361da177e4SLinus Torvalds 		spin_unlock(&tree->hash_lock);
6371da177e4SLinus Torvalds 	}
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds 
6402cd282a1SSergei Antonov /*
6412cd282a1SSergei Antonov  * Unused nodes have to be zeroed if this is the catalog tree and
6422cd282a1SSergei Antonov  * a corresponding flag in the volume header is set.
6432cd282a1SSergei Antonov  */
hfs_bnode_need_zeroout(struct hfs_btree * tree)6442cd282a1SSergei Antonov bool hfs_bnode_need_zeroout(struct hfs_btree *tree)
6452cd282a1SSergei Antonov {
6462cd282a1SSergei Antonov 	struct super_block *sb = tree->inode->i_sb;
6472cd282a1SSergei Antonov 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6482cd282a1SSergei Antonov 	const u32 volume_attr = be32_to_cpu(sbi->s_vhdr->attributes);
6492cd282a1SSergei Antonov 
6502cd282a1SSergei Antonov 	return tree->cnid == HFSPLUS_CAT_CNID &&
6512cd282a1SSergei Antonov 		volume_attr & HFSPLUS_VOL_UNUSED_NODE_FIX;
6522cd282a1SSergei Antonov }
653