Blame view

fs/hfsplus/extents.c 15 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   *  linux/fs/hfsplus/extents.c
   *
   * Copyright (C) 2001
   * Brad Boyer (flar@allandria.com)
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   *
   * Handling of Extents both in catalog and extents overflow trees
   */
  
  #include <linux/errno.h>
  #include <linux/fs.h>
  #include <linux/pagemap.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
  
  #include "hfsplus_fs.h"
  #include "hfsplus_raw.h"
  
  /* Compare two extents keys, returns 0 on same, pos/neg for difference */
2179d372d   David Elliott   [PATCH] hfs: add ...
19
20
  int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1,
  			const hfsplus_btree_key *k2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  {
  	__be32 k1id, k2id;
  	__be32 k1s, k2s;
  
  	k1id = k1->ext.cnid;
  	k2id = k2->ext.cnid;
  	if (k1id != k2id)
  		return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1;
  
  	if (k1->ext.fork_type != k2->ext.fork_type)
  		return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1;
  
  	k1s = k1->ext.start_block;
  	k2s = k2->ext.start_block;
  	if (k1s == k2s)
  		return 0;
  	return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1;
  }
  
  static void hfsplus_ext_build_key(hfsplus_btree_key *key, u32 cnid,
  				  u32 block, u8 type)
  {
  	key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2);
  	key->ext.cnid = cpu_to_be32(cnid);
  	key->ext.start_block = cpu_to_be32(block);
  	key->ext.fork_type = type;
  	key->ext.pad = 0;
  }
  
  static u32 hfsplus_ext_find_block(struct hfsplus_extent *ext, u32 off)
  {
  	int i;
  	u32 count;
  
  	for (i = 0; i < 8; ext++, i++) {
  		count = be32_to_cpu(ext->block_count);
  		if (off < count)
  			return be32_to_cpu(ext->start_block) + off;
  		off -= count;
  	}
  	/* panic? */
  	return 0;
  }
  
  static int hfsplus_ext_block_count(struct hfsplus_extent *ext)
  {
  	int i;
  	u32 count = 0;
  
  	for (i = 0; i < 8; ext++, i++)
  		count += be32_to_cpu(ext->block_count);
  	return count;
  }
  
  static u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext)
  {
  	int i;
  
  	ext += 7;
  	for (i = 0; i < 7; ext--, i++)
  		if (ext->block_count)
  			break;
  	return be32_to_cpu(ext->start_block) + be32_to_cpu(ext->block_count);
  }
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
85
  static int __hfsplus_ext_write_extent(struct inode *inode,
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
86
  		struct hfs_find_data *fd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
88
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
90
  	WARN_ON(!mutex_is_locked(&hip->extents_lock));
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
91
92
93
  	hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start,
  			      HFSPLUS_IS_RSRC(inode) ?
  				HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
94
  	res = hfs_brec_find(fd, hfs_find_rec_by_key);
b33b7921d   Christoph Hellwig   hfsplus: split up...
95
  	if (hip->extent_state & HFSPLUS_EXT_NEW) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  		if (res != -ENOENT)
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
97
  			return res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
98
99
  		hfs_brec_insert(fd, hip->cached_extents,
  				sizeof(hfsplus_extent_rec));
b33b7921d   Christoph Hellwig   hfsplus: split up...
100
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
  	} else {
  		if (res)
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
103
  			return res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
104
105
  		hfs_bnode_write(fd->bnode, hip->cached_extents,
  				fd->entryoffset, fd->entrylength);
b33b7921d   Christoph Hellwig   hfsplus: split up...
106
  		hip->extent_state &= ~HFSPLUS_EXT_DIRTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
108
109
110
111
112
113
114
115
  
  	/*
  	 * We can't just use hfsplus_mark_inode_dirty here, because we
  	 * also get called from hfsplus_write_inode, which should not
  	 * redirty the inode.  Instead the callers have to be careful
  	 * to explicily mark the inode dirty, too.
  	 */
  	set_bit(HFSPLUS_I_EXT_DIRTY, &hip->flags);
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
116
117
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
119
  static int hfsplus_ext_write_extent_locked(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  {
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
121
  	int res = 0;
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
122

b33b7921d   Christoph Hellwig   hfsplus: split up...
123
  	if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  		struct hfs_find_data fd;
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
125
126
127
  		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
  		if (res)
  			return res;
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
128
  		res = __hfsplus_ext_write_extent(inode, &fd);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
129
  		hfs_find_exit(&fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  	}
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
131
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
133
  int hfsplus_ext_write_extent(struct inode *inode)
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
134
  {
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
135
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
136
  	mutex_lock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
137
  	res = hfsplus_ext_write_extent_locked(inode);
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
138
  	mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
139
140
  
  	return res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
141
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
148
149
  static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
  					    struct hfsplus_extent *extent,
  					    u32 cnid, u32 block, u8 type)
  {
  	int res;
  
  	hfsplus_ext_build_key(fd->search_key, cnid, block, type);
  	fd->key->ext.cnid = 0;
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
150
  	res = hfs_brec_find(fd, hfs_find_rec_by_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
155
156
157
  	if (res && res != -ENOENT)
  		return res;
  	if (fd->key->ext.cnid != fd->search_key->ext.cnid ||
  	    fd->key->ext.fork_type != fd->search_key->ext.fork_type)
  		return -ENOENT;
  	if (fd->entrylength != sizeof(hfsplus_extent_rec))
  		return -EIO;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
158
159
  	hfs_bnode_read(fd->bnode, extent, fd->entryoffset,
  		sizeof(hfsplus_extent_rec));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  	return 0;
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
162
163
  static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd,
  		struct inode *inode, u32 block)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
165
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
167
  	WARN_ON(!mutex_is_locked(&hip->extents_lock));
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
168
169
170
171
172
  	if (hip->extent_state & HFSPLUS_EXT_DIRTY) {
  		res = __hfsplus_ext_write_extent(inode, fd);
  		if (res)
  			return res;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
174
175
176
177
  	res = __hfsplus_ext_read_extent(fd, hip->cached_extents, inode->i_ino,
  					block, HFSPLUS_IS_RSRC(inode) ?
  						HFSPLUS_TYPE_RSRC :
  						HFSPLUS_TYPE_DATA);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
179
  		hip->cached_start = be32_to_cpu(fd->key->ext.start_block);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
180
181
  		hip->cached_blocks =
  			hfsplus_ext_block_count(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
183
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
184
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
187
188
189
190
  	}
  	return res;
  }
  
  static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
191
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	struct hfs_find_data fd;
  	int res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
194
195
  	if (block >= hip->cached_start &&
  	    block < hip->cached_start + hip->cached_blocks)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
197
198
199
200
201
  	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
  	if (!res) {
  		res = __hfsplus_ext_cache_extent(&fd, inode, block);
  		hfs_find_exit(&fd);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
  	return res;
  }
  
  /* Get a block at iblock for inode, possibly allocating if create */
  int hfsplus_get_block(struct inode *inode, sector_t iblock,
  		      struct buffer_head *bh_result, int create)
  {
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
209
210
  	struct super_block *sb = inode->i_sb;
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
211
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
  	int res = -EIO;
  	u32 ablock, dblock, mask;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
214
  	sector_t sector;
e34947056   Christoph Hellwig   hfsplus: optimize...
215
  	int was_dirty = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  	/* Convert inode block to disk allocation block */
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
218
  	ablock = iblock >> sbi->fs_shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
220
221
  	if (iblock >= hip->fs_blocks) {
  		if (iblock > hip->fs_blocks || !create)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  			return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
223
  		if (ablock >= hip->alloc_blocks) {
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
224
  			res = hfsplus_file_extend(inode, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
  			if (res)
  				return res;
  		}
  	} else
  		create = 0;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
230
231
  	if (ablock < hip->first_blocks) {
  		dblock = hfsplus_ext_find_block(hip->first_extents, ablock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  		goto done;
  	}
248736c2a   Eric Sesterhenn   hfsplus: fix poss...
234
235
  	if (inode->i_ino == HFSPLUS_EXT_CNID)
  		return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
236
  	mutex_lock(&hip->extents_lock);
e34947056   Christoph Hellwig   hfsplus: optimize...
237
238
239
240
241
242
243
  
  	/*
  	 * hfsplus_ext_read_extent will write out a cached extent into
  	 * the extents btree.  In that case we may have to mark the inode
  	 * dirty even for a pure read of an extent here.
  	 */
  	was_dirty = (hip->extent_state & HFSPLUS_EXT_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  	res = hfsplus_ext_read_extent(inode, ablock);
e34947056   Christoph Hellwig   hfsplus: optimize...
245
  	if (res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
246
  		mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
  		return -EIO;
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
249
250
  	dblock = hfsplus_ext_find_block(hip->cached_extents,
  					ablock - hip->cached_start);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
251
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  
  done:
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
254
255
  	hfs_dbg(EXTENT, "get_block(%lu): %llu - %u
  ",
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
256
  		inode->i_ino, (long long)iblock, dblock);
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
257

dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
258
  	mask = (1 << sbi->fs_shift) - 1;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
259
260
261
  	sector = ((sector_t)dblock << sbi->fs_shift) +
  		  sbi->blockoffset + (iblock & mask);
  	map_bh(bh_result, sb, sector);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
  	if (create) {
  		set_buffer_new(bh_result);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
264
265
  		hip->phys_size += sb->s_blocksize;
  		hip->fs_blocks++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
  		inode_add_bytes(inode, sb->s_blocksize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
268
269
  	if (create || was_dirty)
  		mark_inode_dirty(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
274
275
  	return 0;
  }
  
  static void hfsplus_dump_extent(struct hfsplus_extent *extent)
  {
  	int i;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
276
  	hfs_dbg(EXTENT, "   ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  	for (i = 0; i < 8; i++)
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
278
279
280
281
282
  		hfs_dbg_cont(EXTENT, " %u:%u",
  			     be32_to_cpu(extent[i].start_block),
  			     be32_to_cpu(extent[i].block_count));
  	hfs_dbg_cont(EXTENT, "
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  }
  
  static int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset,
  			      u32 alloc_block, u32 block_count)
  {
  	u32 count, start;
  	int i;
  
  	hfsplus_dump_extent(extent);
  	for (i = 0; i < 8; extent++, i++) {
  		count = be32_to_cpu(extent->block_count);
  		if (offset == count) {
  			start = be32_to_cpu(extent->start_block);
  			if (alloc_block != start + count) {
  				if (++i >= 8)
  					return -ENOSPC;
  				extent++;
  				extent->start_block = cpu_to_be32(alloc_block);
  			} else
  				block_count += count;
  			extent->block_count = cpu_to_be32(block_count);
  			return 0;
  		} else if (offset < count)
  			break;
  		offset -= count;
  	}
  	/* panic? */
  	return -EIO;
  }
  
  static int hfsplus_free_extents(struct super_block *sb,
  				struct hfsplus_extent *extent,
  				u32 offset, u32 block_nr)
  {
  	u32 count, start;
  	int i;
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
319
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  
  	hfsplus_dump_extent(extent);
  	for (i = 0; i < 8; extent++, i++) {
  		count = be32_to_cpu(extent->block_count);
  		if (offset == count)
  			goto found;
  		else if (offset < count)
  			break;
  		offset -= count;
  	}
  	/* panic? */
  	return -EIO;
  found:
  	for (;;) {
  		start = be32_to_cpu(extent->start_block);
  		if (count <= block_nr) {
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
336
337
  			err = hfsplus_block_free(sb, start, count);
  			if (err) {
d61426732   Joe Perches   hfs/hfsplus: conv...
338
339
  				pr_err("can't free extent
  ");
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
340
341
  				hfs_dbg(EXTENT, " start: %u count: %u
  ",
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
342
343
  					start, count);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
348
  			extent->block_count = 0;
  			extent->start_block = 0;
  			block_nr -= count;
  		} else {
  			count -= block_nr;
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
349
350
  			err = hfsplus_block_free(sb, start + count, block_nr);
  			if (err) {
d61426732   Joe Perches   hfs/hfsplus: conv...
351
352
  				pr_err("can't free extent
  ");
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
353
354
  				hfs_dbg(EXTENT, " start: %u count: %u
  ",
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
355
356
  					start, count);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
  			extent->block_count = cpu_to_be32(count);
  			block_nr = 0;
  		}
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
360
361
362
363
364
365
366
  		if (!block_nr || !i) {
  			/*
  			 * Try to free all extents and
  			 * return only last error
  			 */
  			return err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
  		i--;
  		extent--;
  		count = be32_to_cpu(extent->block_count);
  	}
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
372
373
  int hfsplus_free_fork(struct super_block *sb, u32 cnid,
  		struct hfsplus_fork_raw *fork, int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  {
  	struct hfs_find_data fd;
  	hfsplus_extent_rec ext_entry;
  	u32 total_blocks, blocks, start;
  	int res, i;
  
  	total_blocks = be32_to_cpu(fork->total_blocks);
  	if (!total_blocks)
  		return 0;
  
  	blocks = 0;
  	for (i = 0; i < 8; i++)
  		blocks += be32_to_cpu(fork->extents[i].block_count);
  
  	res = hfsplus_free_extents(sb, fork->extents, blocks, blocks);
  	if (res)
  		return res;
  	if (total_blocks == blocks)
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
393
394
395
  	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
  	if (res)
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  	do {
  		res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid,
  						total_blocks, type);
  		if (res)
  			break;
  		start = be32_to_cpu(fd.key->ext.start_block);
  		hfsplus_free_extents(sb, ext_entry,
  				     total_blocks - start,
  				     total_blocks);
  		hfs_brec_remove(&fd);
  		total_blocks = start;
  	} while (total_blocks > blocks);
  	hfs_find_exit(&fd);
  
  	return res;
  }
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
412
  int hfsplus_file_extend(struct inode *inode, bool zeroout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
  {
  	struct super_block *sb = inode->i_sb;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
415
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
416
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
  	u32 start, len, goal;
  	int res;
1065348d4   Christoph Hellwig   hfsplus: fix up a...
419
420
  	if (sbi->alloc_file->i_size * 8 <
  	    sbi->total_blocks - sbi->free_blocks + 8) {
21f2296a5   Anton Salikhmetov   hfsplus: C99 comm...
421
  		/* extend alloc file */
b73f3d0e7   Fabian Frederick   fs/hfsplus: fix p...
422
423
424
425
  		pr_err("extend alloc file! (%llu,%u,%u)
  ",
  		       sbi->alloc_file->i_size * 8,
  		       sbi->total_blocks, sbi->free_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
  		return -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
428
429
430
  	mutex_lock(&hip->extents_lock);
  	if (hip->alloc_blocks == hip->first_blocks)
  		goal = hfsplus_ext_lastblock(hip->first_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
432
  		res = hfsplus_ext_read_extent(inode, hip->alloc_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
  		if (res)
  			goto out;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
435
  		goal = hfsplus_ext_lastblock(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
437
  	len = hip->clump_blocks;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
438
439
  	start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len);
  	if (start >= sbi->total_blocks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
  		start = hfsplus_block_allocate(sb, goal, 0, &len);
  		if (start >= goal) {
  			res = -ENOSPC;
  			goto out;
  		}
  	}
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
446
447
448
449
450
  	if (zeroout) {
  		res = sb_issue_zeroout(sb, start, len, GFP_NOFS);
  		if (res)
  			goto out;
  	}
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
451
452
  	hfs_dbg(EXTENT, "extend %lu: %u,%u
  ", inode->i_ino, start, len);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
453
454
455
  
  	if (hip->alloc_blocks <= hip->first_blocks) {
  		if (!hip->first_blocks) {
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
456
457
  			hfs_dbg(EXTENT, "first extents
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  			/* no extents yet */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
459
460
  			hip->first_extents[0].start_block = cpu_to_be32(start);
  			hip->first_extents[0].block_count = cpu_to_be32(len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
463
  			res = 0;
  		} else {
  			/* try to append to extents in inode */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
464
465
  			res = hfsplus_add_extent(hip->first_extents,
  						 hip->alloc_blocks,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
  						 start, len);
  			if (res == -ENOSPC)
  				goto insert_extent;
  		}
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
471
472
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
  		}
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
475
476
  		res = hfsplus_add_extent(hip->cached_extents,
  					 hip->alloc_blocks - hip->cached_start,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
  					 start, len);
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
479
  			hfsplus_dump_extent(hip->cached_extents);
b33b7921d   Christoph Hellwig   hfsplus: split up...
480
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
481
  			hip->cached_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
  		} else if (res == -ENOSPC)
  			goto insert_extent;
  	}
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
487
  		hip->alloc_blocks += len;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
488
  		mutex_unlock(&hip->extents_lock);
e34947056   Christoph Hellwig   hfsplus: optimize...
489
  		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
490
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
  	}
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
492
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
  	return res;
  
  insert_extent:
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
496
497
  	hfs_dbg(EXTENT, "insert new extent
  ");
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
498
499
500
  	res = hfsplus_ext_write_extent_locked(inode);
  	if (res)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
502
503
504
505
  	memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec));
  	hip->cached_extents[0].start_block = cpu_to_be32(start);
  	hip->cached_extents[0].block_count = cpu_to_be32(len);
  	hfsplus_dump_extent(hip->cached_extents);
b33b7921d   Christoph Hellwig   hfsplus: split up...
506
  	hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
507
508
  	hip->cached_start = hip->alloc_blocks;
  	hip->cached_blocks = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
511
512
513
514
515
516
  
  	res = 0;
  	goto out;
  }
  
  void hfsplus_file_truncate(struct inode *inode)
  {
  	struct super_block *sb = inode->i_sb;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
517
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
  	struct hfs_find_data fd;
  	u32 alloc_cnt, blk_cnt, start;
  	int res;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
521
522
523
  	hfs_dbg(INODE, "truncate: %lu, %llu -> %llu
  ",
  		inode->i_ino, (long long)hip->phys_size, inode->i_size);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
524
525
  
  	if (inode->i_size > hip->phys_size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
  		struct address_space *mapping = inode->i_mapping;
  		struct page *page;
7c0efc627   Nick Piggin   hfsplus: convert ...
528
  		void *fsdata;
12f267a20   Vyacheslav Dubeyko   hfsplus: fix pote...
529
  		loff_t size = inode->i_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530

7c0efc627   Nick Piggin   hfsplus: convert ...
531
532
533
  		res = pagecache_write_begin(NULL, mapping, size, 0,
  						AOP_FLAG_UNINTERRUPTIBLE,
  						&page, &fsdata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
  		if (res)
7c0efc627   Nick Piggin   hfsplus: convert ...
535
  			return;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
536
537
  		res = pagecache_write_end(NULL, mapping, size,
  			0, 0, page, fsdata);
7c0efc627   Nick Piggin   hfsplus: convert ...
538
539
  		if (res < 0)
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
  		mark_inode_dirty(inode);
  		return;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
542
  	} else if (inode->i_size == hip->phys_size)
f76d28d23   Roman Zippel   [PATCH] hfs: don'...
543
  		return;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
544
545
  	blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
  			HFSPLUS_SB(sb)->alloc_blksz_shift;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
546
547
  
  	mutex_lock(&hip->extents_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
548
  	alloc_cnt = hip->alloc_blocks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
  	if (blk_cnt == alloc_cnt)
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
550
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551

5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
552
553
554
555
556
557
  	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
  	if (res) {
  		mutex_unlock(&hip->extents_lock);
  		/* XXX: We lack error handling of hfsplus_file_truncate() */
  		return;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  	while (1) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
559
560
  		if (alloc_cnt == hip->first_blocks) {
  			hfsplus_free_extents(sb, hip->first_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  					     alloc_cnt, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
562
563
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks = blk_cnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
  			break;
  		}
  		res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
  		if (res)
  			break;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
569
570
  		start = hip->cached_start;
  		hfsplus_free_extents(sb, hip->cached_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  				     alloc_cnt - start, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
572
  		hfsplus_dump_extent(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  		if (blk_cnt > start) {
b33b7921d   Christoph Hellwig   hfsplus: split up...
574
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
  			break;
  		}
  		alloc_cnt = start;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
578
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
579
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
  		hfs_brec_remove(&fd);
  	}
  	hfs_find_exit(&fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
584
  	hip->alloc_blocks = blk_cnt;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
585
586
  out_unlock:
  	mutex_unlock(&hip->extents_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
587
  	hip->phys_size = inode->i_size;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
588
589
  	hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
  		sb->s_blocksize_bits;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
590
  	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
e34947056   Christoph Hellwig   hfsplus: optimize...
591
  	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
  }