Blame view

fs/hfsplus/extents.c 14.5 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);
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
85
86
  static void __hfsplus_ext_write_extent(struct inode *inode,
  		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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  	res = hfs_brec_find(fd);
b33b7921d   Christoph Hellwig   hfsplus: split up...
95
  	if (hip->extent_state & HFSPLUS_EXT_NEW) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
  		if (res != -ENOENT)
  			return;
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
103
  	} else {
  		if (res)
  			return;
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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
117
  static int hfsplus_ext_write_extent_locked(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  {
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
119
  	int res;
b33b7921d   Christoph Hellwig   hfsplus: split up...
120
  	if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  		struct hfs_find_data fd;
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
122
123
124
125
126
  		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
  		if (res)
  			return res;
  		__hfsplus_ext_write_extent(inode, &fd);
  		hfs_find_exit(&fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  	}
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
128
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
130
  int hfsplus_ext_write_extent(struct inode *inode)
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
131
  {
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
132
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
133
  	mutex_lock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
134
  	res = hfsplus_ext_write_extent_locked(inode);
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
135
  	mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
136
137
  
  	return res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
138
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  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;
  	res = hfs_brec_find(fd);
  	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 ...
155
156
  	hfs_bnode_read(fd->bnode, extent, fd->entryoffset,
  		sizeof(hfsplus_extent_rec));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
  	return 0;
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
159
160
  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
161
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
162
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
164
  	WARN_ON(!mutex_is_locked(&hip->extents_lock));
b33b7921d   Christoph Hellwig   hfsplus: split up...
165
  	if (hip->extent_state & HFSPLUS_EXT_DIRTY)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  		__hfsplus_ext_write_extent(inode, fd);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
167
168
169
170
  	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
171
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
172
  		hip->cached_start = be32_to_cpu(fd->key->ext.start_block);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
173
174
  		hip->cached_blocks =
  			hfsplus_ext_block_count(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
176
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
177
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
183
  	}
  	return res;
  }
  
  static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
184
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
  	struct hfs_find_data fd;
  	int res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
187
188
  	if (block >= hip->cached_start &&
  	    block < hip->cached_start + hip->cached_blocks)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
190
191
192
193
194
  	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
195
196
197
198
199
200
201
  	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...
202
203
  	struct super_block *sb = inode->i_sb;
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
204
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
  	int res = -EIO;
  	u32 ablock, dblock, mask;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
207
  	sector_t sector;
e34947056   Christoph Hellwig   hfsplus: optimize...
208
  	int was_dirty = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  	int shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
  	/* Convert inode block to disk allocation block */
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
211
212
  	shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits;
  	ablock = iblock >> sbi->fs_shift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
214
215
  	if (iblock >= hip->fs_blocks) {
  		if (iblock > hip->fs_blocks || !create)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  			return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
217
  		if (ablock >= hip->alloc_blocks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
  			res = hfsplus_file_extend(inode);
  			if (res)
  				return res;
  		}
  	} else
  		create = 0;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
224
225
  	if (ablock < hip->first_blocks) {
  		dblock = hfsplus_ext_find_block(hip->first_extents, ablock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
  		goto done;
  	}
248736c2a   Eric Sesterhenn   hfsplus: fix poss...
228
229
  	if (inode->i_ino == HFSPLUS_EXT_CNID)
  		return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
230
  	mutex_lock(&hip->extents_lock);
e34947056   Christoph Hellwig   hfsplus: optimize...
231
232
233
234
235
236
237
  
  	/*
  	 * 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
238
  	res = hfsplus_ext_read_extent(inode, ablock);
e34947056   Christoph Hellwig   hfsplus: optimize...
239
  	if (res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
240
  		mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
  		return -EIO;
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
243
244
  	dblock = hfsplus_ext_find_block(hip->cached_extents,
  					ablock - hip->cached_start);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
245
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
  
  done:
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
248
249
250
  	dprint(DBG_EXTENT, "get_block(%lu): %llu - %u
  ",
  		inode->i_ino, (long long)iblock, dblock);
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
251

dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
252
  	mask = (1 << sbi->fs_shift) - 1;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
253
254
255
  	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
256
257
  	if (create) {
  		set_buffer_new(bh_result);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
258
259
  		hip->phys_size += sb->s_blocksize;
  		hip->fs_blocks++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  		inode_add_bytes(inode, sb->s_blocksize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
262
263
  	if (create || was_dirty)
  		mark_inode_dirty(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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
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
  	return 0;
  }
  
  static void hfsplus_dump_extent(struct hfsplus_extent *extent)
  {
  	int i;
  
  	dprint(DBG_EXTENT, "   ");
  	for (i = 0; i < 8; i++)
  		dprint(DBG_EXTENT, " %u:%u", be32_to_cpu(extent[i].start_block),
  				 be32_to_cpu(extent[i].block_count));
  	dprint(DBG_EXTENT, "
  ");
  }
  
  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;
  
  	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) {
  			hfsplus_block_free(sb, start, count);
  			extent->block_count = 0;
  			extent->start_block = 0;
  			block_nr -= count;
  		} else {
  			count -= block_nr;
  			hfsplus_block_free(sb, start + count, block_nr);
  			extent->block_count = cpu_to_be32(count);
  			block_nr = 0;
  		}
  		if (!block_nr || !i)
  			return 0;
  		i--;
  		extent--;
  		count = be32_to_cpu(extent->block_count);
  	}
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
346
347
  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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  {
  	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...
367
368
369
  	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
  	if (res)
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  	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;
  }
  
  int hfsplus_file_extend(struct inode *inode)
  {
  	struct super_block *sb = inode->i_sb;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
390
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
391
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
  	u32 start, len, goal;
  	int res;
1065348d4   Christoph Hellwig   hfsplus: fix up a...
394
395
  	if (sbi->alloc_file->i_size * 8 <
  	    sbi->total_blocks - sbi->free_blocks + 8) {
21f2296a5   Anton Salikhmetov   hfsplus: C99 comm...
396
  		/* extend alloc file */
b2837fcf4   Anton Salikhmetov   hfsplus: %L-to-%l...
397
398
399
400
401
  		printk(KERN_ERR "hfs: 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
402
  		return -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
404
405
406
  	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
407
  	else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
408
  		res = hfsplus_ext_read_extent(inode, hip->alloc_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
410
  		if (res)
  			goto out;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
411
  		goal = hfsplus_ext_lastblock(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
413
  	len = hip->clump_blocks;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
414
415
  	start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len);
  	if (start >= sbi->total_blocks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
419
420
421
422
423
424
  		start = hfsplus_block_allocate(sb, goal, 0, &len);
  		if (start >= goal) {
  			res = -ENOSPC;
  			goto out;
  		}
  	}
  
  	dprint(DBG_EXTENT, "extend %lu: %u,%u
  ", inode->i_ino, start, len);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
425
426
427
  
  	if (hip->alloc_blocks <= hip->first_blocks) {
  		if (!hip->first_blocks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
  			dprint(DBG_EXTENT, "first extents
  ");
  			/* no extents yet */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
431
432
  			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
433
434
435
  			res = 0;
  		} else {
  			/* try to append to extents in inode */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
436
437
  			res = hfsplus_add_extent(hip->first_extents,
  						 hip->alloc_blocks,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
  						 start, len);
  			if (res == -ENOSPC)
  				goto insert_extent;
  		}
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
443
444
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
  		}
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
447
448
  		res = hfsplus_add_extent(hip->cached_extents,
  					 hip->alloc_blocks - hip->cached_start,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
  					 start, len);
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
451
  			hfsplus_dump_extent(hip->cached_extents);
b33b7921d   Christoph Hellwig   hfsplus: split up...
452
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
453
  			hip->cached_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
  		} else if (res == -ENOSPC)
  			goto insert_extent;
  	}
  out:
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
458
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
460
  		hip->alloc_blocks += len;
e34947056   Christoph Hellwig   hfsplus: optimize...
461
  		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
467
  	}
  	return res;
  
  insert_extent:
  	dprint(DBG_EXTENT, "insert new extent
  ");
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
468
469
470
  	res = hfsplus_ext_write_extent_locked(inode);
  	if (res)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
472
473
474
475
  	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...
476
  	hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
477
478
  	hip->cached_start = hip->alloc_blocks;
  	hip->cached_blocks = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
480
481
482
483
484
485
486
  
  	res = 0;
  	goto out;
  }
  
  void hfsplus_file_truncate(struct inode *inode)
  {
  	struct super_block *sb = inode->i_sb;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
487
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
490
  	struct hfs_find_data fd;
  	u32 alloc_cnt, blk_cnt, start;
  	int res;
b2837fcf4   Anton Salikhmetov   hfsplus: %L-to-%l...
491
492
493
494
  	dprint(DBG_INODE, "truncate: %lu, %llu -> %llu
  ",
  		inode->i_ino, (long long)hip->phys_size,
  		inode->i_size);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
495
496
  
  	if (inode->i_size > hip->phys_size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
  		struct address_space *mapping = inode->i_mapping;
  		struct page *page;
7c0efc627   Nick Piggin   hfsplus: convert ...
499
500
  		void *fsdata;
  		u32 size = inode->i_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501

7c0efc627   Nick Piggin   hfsplus: convert ...
502
503
504
  		res = pagecache_write_begin(NULL, mapping, size, 0,
  						AOP_FLAG_UNINTERRUPTIBLE,
  						&page, &fsdata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  		if (res)
7c0efc627   Nick Piggin   hfsplus: convert ...
506
  			return;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
507
508
  		res = pagecache_write_end(NULL, mapping, size,
  			0, 0, page, fsdata);
7c0efc627   Nick Piggin   hfsplus: convert ...
509
510
  		if (res < 0)
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
  		mark_inode_dirty(inode);
  		return;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
513
  	} else if (inode->i_size == hip->phys_size)
f76d28d23   Roman Zippel   [PATCH] hfs: don'...
514
  		return;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
515
516
  	blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
  			HFSPLUS_SB(sb)->alloc_blksz_shift;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
517
  	alloc_cnt = hip->alloc_blocks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
  	if (blk_cnt == alloc_cnt)
  		goto out;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
520
  	mutex_lock(&hip->extents_lock);
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
521
522
523
524
525
526
  	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
527
  	while (1) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
528
529
  		if (alloc_cnt == hip->first_blocks) {
  			hfsplus_free_extents(sb, hip->first_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  					     alloc_cnt, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
531
532
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks = blk_cnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
537
  			break;
  		}
  		res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
  		if (res)
  			break;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
538
539
  		start = hip->cached_start;
  		hfsplus_free_extents(sb, hip->cached_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  				     alloc_cnt - start, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
541
  		hfsplus_dump_extent(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
  		if (blk_cnt > start) {
b33b7921d   Christoph Hellwig   hfsplus: split up...
543
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
546
  			break;
  		}
  		alloc_cnt = start;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
547
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
548
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
  		hfs_brec_remove(&fd);
  	}
  	hfs_find_exit(&fd);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
552
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
554
  	hip->alloc_blocks = blk_cnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  out:
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
556
  	hip->phys_size = inode->i_size;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
557
558
  	hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
  		sb->s_blocksize_bits;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
559
  	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
e34947056   Christoph Hellwig   hfsplus: optimize...
560
  	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  }