Blame view

fs/hfsplus/btree.c 11.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   *  linux/fs/hfsplus/btree.c
   *
   * Copyright (C) 2001
   * Brad Boyer (flar@allandria.com)
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   *
   * Handle opening/closing btree
   */
  
  #include <linux/slab.h>
  #include <linux/pagemap.h>
e1b5c1d3d   Vignesh Babu BM   is_power_of_2 in ...
13
  #include <linux/log2.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
  
  #include "hfsplus_fs.h"
  #include "hfsplus_raw.h"
b3b5b0f03   Vyacheslav Dubeyko   hfsplus: add meta...
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  /*
   * Initial source code of clump size calculation is gotten
   * from http://opensource.apple.com/tarballs/diskdev_cmds/
   */
  #define CLUMP_ENTRIES	15
  
  static short clumptbl[CLUMP_ENTRIES * 3] = {
  /*
   *	    Volume	Attributes	 Catalog	 Extents
   *	     Size	Clump (MB)	Clump (MB)	Clump (MB)
   */
  	/*   1GB */	  4,		  4,		 4,
  	/*   2GB */	  6,		  6,		 4,
  	/*   4GB */	  8,		  8,		 4,
  	/*   8GB */	 11,		 11,		 5,
  	/*
  	 * For volumes 16GB and larger, we want to make sure that a full OS
  	 * install won't require fragmentation of the Catalog or Attributes
  	 * B-trees.  We do this by making the clump sizes sufficiently large,
  	 * and by leaving a gap after the B-trees for them to grow into.
  	 *
  	 * For SnowLeopard 10A298, a FullNetInstall with all packages selected
  	 * results in:
  	 * Catalog B-tree Header
  	 *	nodeSize:          8192
  	 *	totalNodes:       31616
  	 *	freeNodes:         1978
  	 * (used = 231.55 MB)
  	 * Attributes B-tree Header
  	 *	nodeSize:          8192
  	 *	totalNodes:       63232
  	 *	freeNodes:          958
  	 * (used = 486.52 MB)
  	 *
  	 * We also want Time Machine backup volumes to have a sufficiently
  	 * large clump size to reduce fragmentation.
  	 *
  	 * The series of numbers for Catalog and Attribute form a geometric
  	 * series. For Catalog (16GB to 512GB), each term is 8**(1/5) times
  	 * the previous term.  For Attributes (16GB to 512GB), each term is
  	 * 4**(1/5) times the previous term.  For 1TB to 16TB, each term is
  	 * 2**(1/5) times the previous term.
  	 */
  	/*  16GB */	 64,		 32,		 5,
  	/*  32GB */	 84,		 49,		 6,
  	/*  64GB */	111,		 74,		 7,
  	/* 128GB */	147,		111,		 8,
  	/* 256GB */	194,		169,		 9,
  	/* 512GB */	256,		256,		11,
  	/*   1TB */	294,		294,		14,
  	/*   2TB */	338,		338,		16,
  	/*   4TB */	388,		388,		20,
  	/*   8TB */	446,		446,		25,
  	/*  16TB */	512,		512,		32
  };
  
  u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size,
  					u64 sectors, int file_id)
  {
  	u32 mod = max(node_size, block_size);
  	u32 clump_size;
  	int column;
  	int i;
  
  	/* Figure out which column of the above table to use for this file. */
  	switch (file_id) {
  	case HFSPLUS_ATTR_CNID:
  		column = 0;
  		break;
  	case HFSPLUS_CAT_CNID:
  		column = 1;
  		break;
  	default:
  		column = 2;
  		break;
  	}
  
  	/*
  	 * The default clump size is 0.8% of the volume size. And
  	 * it must also be a multiple of the node and block size.
  	 */
  	if (sectors < 0x200000) {
  		clump_size = sectors << 2;	/*  0.8 %  */
  		if (clump_size < (8 * node_size))
  			clump_size = 8 * node_size;
  	} else {
  		/* turn exponent into table index... */
  		for (i = 0, sectors = sectors >> 22;
  		     sectors && (i < CLUMP_ENTRIES - 1);
  		     ++i, sectors = sectors >> 1) {
  			/* empty body */
  		}
  
  		clump_size = clumptbl[column + (i) * 3] * 1024 * 1024;
  	}
  
  	/*
  	 * Round the clump size to a multiple of node and block size.
  	 * NOTE: This rounds down.
  	 */
  	clump_size /= mod;
  	clump_size *= mod;
  
  	/*
  	 * Rounding down could have rounded down to 0 if the block size was
  	 * greater than the clump size.  If so, just use one block or node.
  	 */
  	if (clump_size == 0)
  		clump_size = mod;
  
  	return clump_size;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
135
  
  /* 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;
635253915   David Howells   iget: stop HFSPLU...
136
  	struct inode *inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  	struct page *page;
  	unsigned int size;
f8314dc60   Panagiotis Issaris   [PATCH] fs: Conve...
139
  	tree = kzalloc(sizeof(*tree), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  	if (!tree)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142

467c3d9cd   Thomas Gleixner   hfsplus: convert ...
143
  	mutex_init(&tree->tree_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	spin_lock_init(&tree->hash_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
  	tree->sb = sb;
  	tree->cnid = id;
635253915   David Howells   iget: stop HFSPLU...
147
148
  	inode = hfsplus_iget(sb, id);
  	if (IS_ERR(inode))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  		goto free_tree;
635253915   David Howells   iget: stop HFSPLU...
150
  	tree->inode = inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151

ee5271624   Jeff Mahoney   hfsplus: fix oops...
152
  	if (!HFSPLUS_I(tree->inode)->first_blocks) {
d61426732   Joe Perches   hfs/hfsplus: conv...
153
154
  		pr_err("invalid btree extent records (0 size)
  ");
ee5271624   Jeff Mahoney   hfsplus: fix oops...
155
156
  		goto free_inode;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  	mapping = tree->inode->i_mapping;
090d2b185   Pekka Enberg   [PATCH] read_mapp...
158
  	page = read_mapping_page(mapping, 0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
  	if (IS_ERR(page))
ee5271624   Jeff Mahoney   hfsplus: fix oops...
160
  		goto free_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  
  	/* Load the header */
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
163
164
  	head = (struct hfs_btree_header_rec *)(kmap(page) +
  		sizeof(struct hfs_bnode_desc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
170
171
172
173
174
  	tree->root = be32_to_cpu(head->root);
  	tree->leaf_count = be32_to_cpu(head->leaf_count);
  	tree->leaf_head = be32_to_cpu(head->leaf_head);
  	tree->leaf_tail = be32_to_cpu(head->leaf_tail);
  	tree->node_count = be32_to_cpu(head->node_count);
  	tree->free_nodes = be32_to_cpu(head->free_nodes);
  	tree->attributes = be32_to_cpu(head->attributes);
  	tree->node_size = be16_to_cpu(head->node_size);
  	tree->max_key_len = be16_to_cpu(head->max_key_len);
  	tree->depth = be16_to_cpu(head->depth);
9250f9259   Eric Sandeen   hfsplus: handle m...
175
176
177
178
  	/* Verify the tree and set the correct compare function */
  	switch (id) {
  	case HFSPLUS_EXT_CNID:
  		if (tree->max_key_len != HFSPLUS_EXT_KEYLEN - sizeof(u16)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
179
180
  			pr_err("invalid extent max_key_len %d
  ",
9250f9259   Eric Sandeen   hfsplus: handle m...
181
182
183
  				tree->max_key_len);
  			goto fail_page;
  		}
13571a697   Christoph Hellwig   hfsplus: validate...
184
  		if (tree->attributes & HFS_TREE_VARIDXKEYS) {
d61426732   Joe Perches   hfs/hfsplus: conv...
185
186
  			pr_err("invalid extent btree flag
  ");
13571a697   Christoph Hellwig   hfsplus: validate...
187
188
  			goto fail_page;
  		}
2179d372d   David Elliott   [PATCH] hfs: add ...
189
  		tree->keycmp = hfsplus_ext_cmp_key;
9250f9259   Eric Sandeen   hfsplus: handle m...
190
191
192
  		break;
  	case HFSPLUS_CAT_CNID:
  		if (tree->max_key_len != HFSPLUS_CAT_KEYLEN - sizeof(u16)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
193
194
  			pr_err("invalid catalog max_key_len %d
  ",
9250f9259   Eric Sandeen   hfsplus: handle m...
195
196
197
  				tree->max_key_len);
  			goto fail_page;
  		}
13571a697   Christoph Hellwig   hfsplus: validate...
198
  		if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
199
200
  			pr_err("invalid catalog btree flag
  ");
13571a697   Christoph Hellwig   hfsplus: validate...
201
202
  			goto fail_page;
  		}
9250f9259   Eric Sandeen   hfsplus: handle m...
203

84adede31   Christoph Hellwig   hfsplus: use atom...
204
  		if (test_bit(HFSPLUS_SB_HFSX, &HFSPLUS_SB(sb)->flags) &&
2179d372d   David Elliott   [PATCH] hfs: add ...
205
206
  		    (head->key_type == HFSPLUS_KEY_BINARY))
  			tree->keycmp = hfsplus_cat_bin_cmp_key;
d45bce8fa   Duane Griffin   HFS+: add custom ...
207
  		else {
2179d372d   David Elliott   [PATCH] hfs: add ...
208
  			tree->keycmp = hfsplus_cat_case_cmp_key;
84adede31   Christoph Hellwig   hfsplus: use atom...
209
  			set_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
d45bce8fa   Duane Griffin   HFS+: add custom ...
210
  		}
9250f9259   Eric Sandeen   hfsplus: handle m...
211
  		break;
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
212
213
  	case HFSPLUS_ATTR_CNID:
  		if (tree->max_key_len != HFSPLUS_ATTR_KEYLEN - sizeof(u16)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
214
215
  			pr_err("invalid attributes max_key_len %d
  ",
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
216
217
218
219
220
  				tree->max_key_len);
  			goto fail_page;
  		}
  		tree->keycmp = hfsplus_attr_bin_cmp_key;
  		break;
9250f9259   Eric Sandeen   hfsplus: handle m...
221
  	default:
d61426732   Joe Perches   hfs/hfsplus: conv...
222
223
  		pr_err("unknown B*Tree requested
  ");
2179d372d   David Elliott   [PATCH] hfs: add ...
224
225
  		goto fail_page;
  	}
13571a697   Christoph Hellwig   hfsplus: validate...
226
  	if (!(tree->attributes & HFS_TREE_BIGKEYS)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
227
228
  		pr_err("invalid btree flag
  ");
13571a697   Christoph Hellwig   hfsplus: validate...
229
230
  		goto fail_page;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
  	size = tree->node_size;
e1b5c1d3d   Vignesh Babu BM   is_power_of_2 in ...
232
  	if (!is_power_of_2(size))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
  		goto fail_page;
  	if (!tree->node_count)
  		goto fail_page;
9250f9259   Eric Sandeen   hfsplus: handle m...
236

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  	tree->node_size_shift = ffs(size) - 1;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
238
239
240
  	tree->pages_per_bnode =
  		(tree->node_size + PAGE_CACHE_SIZE - 1) >>
  		PAGE_CACHE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
246
  
  	kunmap(page);
  	page_cache_release(page);
  	return tree;
  
   fail_page:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  	page_cache_release(page);
ee5271624   Jeff Mahoney   hfsplus: fix oops...
248
   free_inode:
9250f9259   Eric Sandeen   hfsplus: handle m...
249
  	tree->inode->i_mapping->a_ops = &hfsplus_aops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  	iput(tree->inode);
ee5271624   Jeff Mahoney   hfsplus: fix oops...
251
   free_tree:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  	kfree(tree);
  	return NULL;
  }
  
  /* Release resources used by a btree */
  void hfs_btree_close(struct hfs_btree *tree)
  {
  	struct hfs_bnode *node;
  	int i;
  
  	if (!tree)
  		return;
  
  	for (i = 0; i < NODE_HASH_SIZE; i++) {
  		while ((node = tree->node_hash[i])) {
  			tree->node_hash[i] = node->next_hash;
  			if (atomic_read(&node->refcnt))
d61426732   Joe Perches   hfs/hfsplus: conv...
269
  				pr_crit("node %d:%d "
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
270
271
272
273
  						"still has %d user(s)!
  ",
  					node->tree->cnid, node->this,
  					atomic_read(&node->refcnt));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
280
  			hfs_bnode_free(node);
  			tree->node_hash_cnt--;
  		}
  	}
  	iput(tree->inode);
  	kfree(tree);
  }
81cc7fad5   Vyacheslav Dubeyko   hfsplus: rework p...
281
  int hfs_btree_write(struct hfs_btree *tree)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
  {
  	struct hfs_btree_header_rec *head;
  	struct hfs_bnode *node;
  	struct page *page;
  
  	node = hfs_bnode_find(tree, 0);
  	if (IS_ERR(node))
  		/* panic? */
81cc7fad5   Vyacheslav Dubeyko   hfsplus: rework p...
290
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
  	/* Load the header */
  	page = node->page[0];
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
293
294
  	head = (struct hfs_btree_header_rec *)(kmap(page) +
  		sizeof(struct hfs_bnode_desc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
303
304
305
306
307
  
  	head->root = cpu_to_be32(tree->root);
  	head->leaf_count = cpu_to_be32(tree->leaf_count);
  	head->leaf_head = cpu_to_be32(tree->leaf_head);
  	head->leaf_tail = cpu_to_be32(tree->leaf_tail);
  	head->node_count = cpu_to_be32(tree->node_count);
  	head->free_nodes = cpu_to_be32(tree->free_nodes);
  	head->attributes = cpu_to_be32(tree->attributes);
  	head->depth = cpu_to_be16(tree->depth);
  
  	kunmap(page);
  	set_page_dirty(page);
  	hfs_bnode_put(node);
81cc7fad5   Vyacheslav Dubeyko   hfsplus: rework p...
308
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  }
  
  static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
  {
  	struct hfs_btree *tree = prev->tree;
  	struct hfs_bnode *node;
  	struct hfs_bnode_desc desc;
  	__be32 cnid;
  
  	node = hfs_bnode_create(tree, idx);
  	if (IS_ERR(node))
  		return node;
  
  	tree->free_nodes--;
  	prev->next = idx;
  	cnid = cpu_to_be32(idx);
  	hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
  
  	node->type = HFS_NODE_MAP;
  	node->num_recs = 1;
  	hfs_bnode_clear(node, 0, tree->node_size);
  	desc.next = 0;
  	desc.prev = 0;
  	desc.type = HFS_NODE_MAP;
  	desc.height = 0;
  	desc.num_recs = cpu_to_be16(1);
  	desc.reserved = 0;
  	hfs_bnode_write(node, &desc, 0, sizeof(desc));
  	hfs_bnode_write_u16(node, 14, 0x8000);
  	hfs_bnode_write_u16(node, tree->node_size - 2, 14);
  	hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6);
  
  	return node;
  }
  
  struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
  {
  	struct hfs_bnode *node, *next_node;
  	struct page **pagep;
  	u32 nidx, idx;
487798df6   Andrew Morton   hfsplus: fix warn...
349
350
351
  	unsigned off;
  	u16 off16;
  	u16 len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
  	u8 *data, byte, m;
  	int i;
  
  	while (!tree->free_nodes) {
  		struct inode *inode = tree->inode;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
357
  		struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
  		u32 count;
  		int res;
  
  		res = hfsplus_file_extend(inode);
  		if (res)
  			return ERR_PTR(res);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
364
365
  		hip->phys_size = inode->i_size =
  			(loff_t)hip->alloc_blocks <<
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
366
  				HFSPLUS_SB(tree->sb)->alloc_blksz_shift;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
367
368
  		hip->fs_blocks =
  			hip->alloc_blocks << HFSPLUS_SB(tree->sb)->fs_shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
374
375
376
377
378
  		inode_set_bytes(inode, inode->i_size);
  		count = inode->i_size >> tree->node_size_shift;
  		tree->free_nodes = count - tree->node_count;
  		tree->node_count = count;
  	}
  
  	nidx = 0;
  	node = hfs_bnode_find(tree, nidx);
  	if (IS_ERR(node))
  		return node;
487798df6   Andrew Morton   hfsplus: fix warn...
379
380
  	len = hfs_brec_lenoff(node, 2, &off16);
  	off = off16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  
  	off += node->page_offset;
  	pagep = node->page + (off >> PAGE_CACHE_SHIFT);
  	data = kmap(*pagep);
  	off &= ~PAGE_CACHE_MASK;
  	idx = 0;
  
  	for (;;) {
  		while (len) {
  			byte = data[off];
  			if (byte != 0xff) {
  				for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
  					if (!(byte & m)) {
  						idx += i;
  						data[off] |= m;
  						set_page_dirty(*pagep);
  						kunmap(*pagep);
  						tree->free_nodes--;
  						mark_inode_dirty(tree->inode);
  						hfs_bnode_put(node);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
401
402
  						return hfs_bnode_create(tree,
  							idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
411
412
413
414
415
416
  					}
  				}
  			}
  			if (++off >= PAGE_CACHE_SIZE) {
  				kunmap(*pagep);
  				data = kmap(*++pagep);
  				off = 0;
  			}
  			idx += 8;
  			len--;
  		}
  		kunmap(*pagep);
  		nidx = node->next;
  		if (!nidx) {
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
417
418
  			hfs_dbg(BNODE_MOD, "create new bmap node
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
423
424
425
  			next_node = hfs_bmap_new_bmap(node, idx);
  		} else
  			next_node = hfs_bnode_find(tree, nidx);
  		hfs_bnode_put(node);
  		if (IS_ERR(next_node))
  			return next_node;
  		node = next_node;
487798df6   Andrew Morton   hfsplus: fix warn...
426
427
  		len = hfs_brec_lenoff(node, 0, &off16);
  		off = off16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
431
432
433
434
435
436
437
438
439
440
441
  		off += node->page_offset;
  		pagep = node->page + (off >> PAGE_CACHE_SHIFT);
  		data = kmap(*pagep);
  		off &= ~PAGE_CACHE_MASK;
  	}
  }
  
  void hfs_bmap_free(struct hfs_bnode *node)
  {
  	struct hfs_btree *tree;
  	struct page *page;
  	u16 off, len;
  	u32 nidx;
  	u8 *data, byte, m;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
442
443
  	hfs_dbg(BNODE_MOD, "btree_free_node: %u
  ", node->this);
0bf3ba538   Eric Sesterhenn   BUG_ON() Conversi...
444
  	BUG_ON(!node->this);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
448
449
450
451
452
453
454
455
456
457
458
  	tree = node->tree;
  	nidx = node->this;
  	node = hfs_bnode_find(tree, 0);
  	if (IS_ERR(node))
  		return;
  	len = hfs_brec_lenoff(node, 2, &off);
  	while (nidx >= len * 8) {
  		u32 i;
  
  		nidx -= len * 8;
  		i = node->next;
  		hfs_bnode_put(node);
  		if (!i) {
  			/* panic */;
d61426732   Joe Perches   hfs/hfsplus: conv...
459
  			pr_crit("unable to free bnode %u. "
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
460
461
462
  					"bmap not found!
  ",
  				node->this);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
465
466
467
468
469
  			return;
  		}
  		node = hfs_bnode_find(tree, i);
  		if (IS_ERR(node))
  			return;
  		if (node->type != HFS_NODE_MAP) {
  			/* panic */;
d61426732   Joe Perches   hfs/hfsplus: conv...
470
  			pr_crit("invalid bmap found! "
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
471
472
473
  					"(%u,%d)
  ",
  				node->this, node->type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
477
478
479
480
481
482
483
484
485
  			hfs_bnode_put(node);
  			return;
  		}
  		len = hfs_brec_lenoff(node, 0, &off);
  	}
  	off += node->page_offset + nidx / 8;
  	page = node->page[off >> PAGE_CACHE_SHIFT];
  	data = kmap(page);
  	off &= ~PAGE_CACHE_MASK;
  	m = 1 << (~nidx & 7);
  	byte = data[off];
  	if (!(byte & m)) {
d61426732   Joe Perches   hfs/hfsplus: conv...
486
  		pr_crit("trying to free free bnode "
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
487
488
489
  				"%u(%d)
  ",
  			node->this, node->type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
494
495
496
497
498
499
500
  		kunmap(page);
  		hfs_bnode_put(node);
  		return;
  	}
  	data[off] = byte & ~m;
  	set_page_dirty(page);
  	kunmap(page);
  	hfs_bnode_put(node);
  	tree->free_nodes++;
  	mark_inode_dirty(tree->inode);
  }