Blame view

fs/hfsplus/extents.c 15.5 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   *  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
15
16
17
18
19
  
  #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 ...
20
21
  int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1,
  			const hfsplus_btree_key *k2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  {
  	__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...
86
  static int __hfsplus_ext_write_extent(struct inode *inode,
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
87
  		struct hfs_find_data *fd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
89
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
91
  	WARN_ON(!mutex_is_locked(&hip->extents_lock));
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
92
93
94
  	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...
95
  	res = hfs_brec_find(fd, hfs_find_rec_by_key);
b33b7921d   Christoph Hellwig   hfsplus: split up...
96
  	if (hip->extent_state & HFSPLUS_EXT_NEW) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  		if (res != -ENOENT)
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
98
  			return res;
d92915c35   Ernesto A. Fernández   hfsplus: prevent ...
99
100
101
102
  		/* Fail early and avoid ENOSPC during the btree operation */
  		res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
  		if (res)
  			return res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
103
104
  		hfs_brec_insert(fd, hip->cached_extents,
  				sizeof(hfsplus_extent_rec));
b33b7921d   Christoph Hellwig   hfsplus: split up...
105
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
  	} else {
  		if (res)
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
108
  			return res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
109
110
  		hfs_bnode_write(fd->bnode, hip->cached_extents,
  				fd->entryoffset, fd->entrylength);
b33b7921d   Christoph Hellwig   hfsplus: split up...
111
  		hip->extent_state &= ~HFSPLUS_EXT_DIRTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
113
114
115
116
117
118
119
120
  
  	/*
  	 * 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...
121
122
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
124
  static int hfsplus_ext_write_extent_locked(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  {
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
126
  	int res = 0;
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
127

b33b7921d   Christoph Hellwig   hfsplus: split up...
128
  	if (HFSPLUS_I(inode)->extent_state & HFSPLUS_EXT_DIRTY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  		struct hfs_find_data fd;
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
130
131
132
  		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd);
  		if (res)
  			return res;
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
133
  		res = __hfsplus_ext_write_extent(inode, &fd);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
134
  		hfs_find_exit(&fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  	}
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
136
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  }
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
138
  int hfsplus_ext_write_extent(struct inode *inode)
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
139
  {
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
140
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
141
  	mutex_lock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
142
  	res = hfsplus_ext_write_extent_locked(inode);
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
143
  	mutex_unlock(&HFSPLUS_I(inode)->extents_lock);
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
144
145
  
  	return res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
146
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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;
324ef39a8   Vyacheslav Dubeyko   hfsplus: add supp...
155
  	res = hfs_brec_find(fd, hfs_find_rec_by_key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
  	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 ...
163
164
  	hfs_bnode_read(fd->bnode, extent, fd->entryoffset,
  		sizeof(hfsplus_extent_rec));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
  	return 0;
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
167
168
  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
169
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
170
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	int res;
7fcc99f4f   Christoph Hellwig   hfsplus: add miss...
172
  	WARN_ON(!mutex_is_locked(&hip->extents_lock));
d7a475d0c   Alexey Khoroshilov   hfsplus: add erro...
173
174
175
176
177
  	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
178

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
179
180
181
182
  	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
183
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
184
  		hip->cached_start = be32_to_cpu(fd->key->ext.start_block);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
185
186
  		hip->cached_blocks =
  			hfsplus_ext_block_count(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
188
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
189
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
  	}
  	return res;
  }
  
  static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
  {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
196
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
  	struct hfs_find_data fd;
  	int res;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
199
200
  	if (block >= hip->cached_start &&
  	    block < hip->cached_start + hip->cached_blocks)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  		return 0;
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
202
203
204
205
206
  	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
207
208
209
210
211
212
213
  	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...
214
215
  	struct super_block *sb = inode->i_sb;
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
216
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
  	int res = -EIO;
  	u32 ablock, dblock, mask;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
219
  	sector_t sector;
e34947056   Christoph Hellwig   hfsplus: optimize...
220
  	int was_dirty = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221

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

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
225
  	if (iblock >= hip->fs_blocks) {
839c3a6a5   Ernesto A. Fernández   hfsplus: fix retu...
226
227
228
  		if (!create)
  			return 0;
  		if (iblock > hip->fs_blocks)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  			return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
230
  		if (ablock >= hip->alloc_blocks) {
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
231
  			res = hfsplus_file_extend(inode, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
  			if (res)
  				return res;
  		}
  	} else
  		create = 0;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
237
238
  	if (ablock < hip->first_blocks) {
  		dblock = hfsplus_ext_find_block(hip->first_extents, ablock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
  		goto done;
  	}
248736c2a   Eric Sesterhenn   hfsplus: fix poss...
241
242
  	if (inode->i_ino == HFSPLUS_EXT_CNID)
  		return -EIO;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
243
  	mutex_lock(&hip->extents_lock);
e34947056   Christoph Hellwig   hfsplus: optimize...
244
245
246
247
248
249
250
  
  	/*
  	 * 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
251
  	res = hfsplus_ext_read_extent(inode, ablock);
e34947056   Christoph Hellwig   hfsplus: optimize...
252
  	if (res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
253
  		mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
  		return -EIO;
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
256
257
  	dblock = hfsplus_ext_find_block(hip->cached_extents,
  					ablock - hip->cached_start);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
258
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
  
  done:
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
261
262
  	hfs_dbg(EXTENT, "get_block(%lu): %llu - %u
  ",
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
263
  		inode->i_ino, (long long)iblock, dblock);
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
264

dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
265
  	mask = (1 << sbi->fs_shift) - 1;
bf1a1b31f   Christoph Hellwig   hfsplus: fix over...
266
267
268
  	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
269
270
  	if (create) {
  		set_buffer_new(bh_result);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
271
272
  		hip->phys_size += sb->s_blocksize;
  		hip->fs_blocks++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  		inode_add_bytes(inode, sb->s_blocksize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  	}
e34947056   Christoph Hellwig   hfsplus: optimize...
275
276
  	if (create || was_dirty)
  		mark_inode_dirty(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
  	return 0;
  }
  
  static void hfsplus_dump_extent(struct hfsplus_extent *extent)
  {
  	int i;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
283
  	hfs_dbg(EXTENT, "   ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  	for (i = 0; i < 8; i++)
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
285
286
287
288
289
  		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
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
  }
  
  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...
326
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327

31651c607   Ernesto A. Fernández   hfsplus: avoid de...
328
329
  	/* Mapping the allocation file may lock the extent tree */
  	WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  	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...
345
346
  			err = hfsplus_block_free(sb, start, count);
  			if (err) {
d61426732   Joe Perches   hfs/hfsplus: conv...
347
348
  				pr_err("can't free extent
  ");
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
349
350
  				hfs_dbg(EXTENT, " start: %u count: %u
  ",
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
351
352
  					start, count);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
  			extent->block_count = 0;
  			extent->start_block = 0;
  			block_nr -= count;
  		} else {
  			count -= block_nr;
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
358
359
  			err = hfsplus_block_free(sb, start + count, block_nr);
  			if (err) {
d61426732   Joe Perches   hfs/hfsplus: conv...
360
361
  				pr_err("can't free extent
  ");
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
362
363
  				hfs_dbg(EXTENT, " start: %u count: %u
  ",
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
364
365
  					start, count);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
  			extent->block_count = cpu_to_be32(count);
  			block_nr = 0;
  		}
1b243fd39   Vyacheslav Dubeyko   hfsplus: rework p...
369
370
371
372
373
374
375
  		if (!block_nr || !i) {
  			/*
  			 * Try to free all extents and
  			 * return only last error
  			 */
  			return err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
379
380
  		i--;
  		extent--;
  		count = be32_to_cpu(extent->block_count);
  	}
  }
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
381
382
  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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  {
  	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...
402
403
404
  	res = hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd);
  	if (res)
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
  	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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  		hfs_brec_remove(&fd);
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
412
413
414
415
  
  		mutex_unlock(&fd.tree->tree_lock);
  		hfsplus_free_extents(sb, ext_entry, total_blocks - start,
  				     total_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  		total_blocks = start;
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
417
  		mutex_lock(&fd.tree->tree_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
  	} while (total_blocks > blocks);
  	hfs_find_exit(&fd);
  
  	return res;
  }
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
423
  int hfsplus_file_extend(struct inode *inode, bool zeroout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
  {
  	struct super_block *sb = inode->i_sb;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
426
  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
427
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
  	u32 start, len, goal;
  	int res;
1065348d4   Christoph Hellwig   hfsplus: fix up a...
430
431
  	if (sbi->alloc_file->i_size * 8 <
  	    sbi->total_blocks - sbi->free_blocks + 8) {
21f2296a5   Anton Salikhmetov   hfsplus: C99 comm...
432
  		/* extend alloc file */
b73f3d0e7   Fabian Frederick   fs/hfsplus: fix p...
433
434
435
436
  		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
437
  		return -ENOSPC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
439
440
441
  	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
442
  	else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
443
  		res = hfsplus_ext_read_extent(inode, hip->alloc_blocks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
  		if (res)
  			goto out;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
446
  		goal = hfsplus_ext_lastblock(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
  	}
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
448
  	len = hip->clump_blocks;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
449
450
  	start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len);
  	if (start >= sbi->total_blocks) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
  		start = hfsplus_block_allocate(sb, goal, 0, &len);
  		if (start >= goal) {
  			res = -ENOSPC;
  			goto out;
  		}
  	}
2cd282a1b   Sergei Antonov   hfsplus: fix "unu...
457
458
459
460
461
  	if (zeroout) {
  		res = sb_issue_zeroout(sb, start, len, GFP_NOFS);
  		if (res)
  			goto out;
  	}
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
462
463
  	hfs_dbg(EXTENT, "extend %lu: %u,%u
  ", inode->i_ino, start, len);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
464
465
466
  
  	if (hip->alloc_blocks <= hip->first_blocks) {
  		if (!hip->first_blocks) {
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
467
468
  			hfs_dbg(EXTENT, "first extents
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
  			/* no extents yet */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
470
471
  			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
472
473
474
  			res = 0;
  		} else {
  			/* try to append to extents in inode */
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
475
476
  			res = hfsplus_add_extent(hip->first_extents,
  						 hip->alloc_blocks,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
478
479
480
481
  						 start, len);
  			if (res == -ENOSPC)
  				goto insert_extent;
  		}
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
482
483
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
  		}
  	} else {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
486
487
  		res = hfsplus_add_extent(hip->cached_extents,
  					 hip->alloc_blocks - hip->cached_start,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
489
  					 start, len);
  		if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
490
  			hfsplus_dump_extent(hip->cached_extents);
b33b7921d   Christoph Hellwig   hfsplus: split up...
491
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
492
  			hip->cached_blocks += len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
496
  		} else if (res == -ENOSPC)
  			goto insert_extent;
  	}
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  	if (!res) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
498
  		hip->alloc_blocks += len;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
499
  		mutex_unlock(&hip->extents_lock);
e34947056   Christoph Hellwig   hfsplus: optimize...
500
  		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
501
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
  	}
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
503
  	mutex_unlock(&hip->extents_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
506
  	return res;
  
  insert_extent:
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
507
508
  	hfs_dbg(EXTENT, "insert new extent
  ");
dd7f3d545   Alexey Khoroshilov   hfsplus: Add erro...
509
510
511
  	res = hfsplus_ext_write_extent_locked(inode);
  	if (res)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
513
514
515
516
  	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...
517
  	hip->extent_state |= HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
518
519
  	hip->cached_start = hip->alloc_blocks;
  	hip->cached_blocks = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
522
523
524
525
526
527
  
  	res = 0;
  	goto out;
  }
  
  void hfsplus_file_truncate(struct inode *inode)
  {
  	struct super_block *sb = inode->i_sb;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
528
  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
  	struct hfs_find_data fd;
  	u32 alloc_cnt, blk_cnt, start;
  	int res;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
532
533
534
  	hfs_dbg(INODE, "truncate: %lu, %llu -> %llu
  ",
  		inode->i_ino, (long long)hip->phys_size, inode->i_size);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
535
536
  
  	if (inode->i_size > hip->phys_size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
  		struct address_space *mapping = inode->i_mapping;
  		struct page *page;
7c0efc627   Nick Piggin   hfsplus: convert ...
539
  		void *fsdata;
12f267a20   Vyacheslav Dubeyko   hfsplus: fix pote...
540
  		loff_t size = inode->i_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

c718a9751   Tetsuo Handa   fs: semove set bu...
542
543
  		res = pagecache_write_begin(NULL, mapping, size, 0, 0,
  					    &page, &fsdata);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
  		if (res)
7c0efc627   Nick Piggin   hfsplus: convert ...
545
  			return;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
546
547
  		res = pagecache_write_end(NULL, mapping, size,
  			0, 0, page, fsdata);
7c0efc627   Nick Piggin   hfsplus: convert ...
548
549
  		if (res < 0)
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
  		mark_inode_dirty(inode);
  		return;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
552
  	} else if (inode->i_size == hip->phys_size)
f76d28d23   Roman Zippel   [PATCH] hfs: don'...
553
  		return;
dd73a01a3   Christoph Hellwig   hfsplus: fix HFSP...
554
555
  	blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >>
  			HFSPLUS_SB(sb)->alloc_blksz_shift;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
556
557
  
  	mutex_lock(&hip->extents_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
558
  	alloc_cnt = hip->alloc_blocks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
  	if (blk_cnt == alloc_cnt)
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
560
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561

5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
562
563
564
565
566
567
  	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
568
  	while (1) {
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
569
  		if (alloc_cnt == hip->first_blocks) {
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
570
  			mutex_unlock(&fd.tree->tree_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
571
  			hfsplus_free_extents(sb, hip->first_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  					     alloc_cnt, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
573
574
  			hfsplus_dump_extent(hip->first_extents);
  			hip->first_blocks = blk_cnt;
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
575
  			mutex_lock(&fd.tree->tree_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
  			break;
  		}
  		res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
  		if (res)
  			break;
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
581
582
583
  		hfs_brec_remove(&fd);
  
  		mutex_unlock(&fd.tree->tree_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
584
585
  		start = hip->cached_start;
  		hfsplus_free_extents(sb, hip->cached_extents,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  				     alloc_cnt - start, alloc_cnt - blk_cnt);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
587
  		hfsplus_dump_extent(hip->cached_extents);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  		if (blk_cnt > start) {
b33b7921d   Christoph Hellwig   hfsplus: split up...
589
  			hip->extent_state |= HFSPLUS_EXT_DIRTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
  			break;
  		}
  		alloc_cnt = start;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
593
  		hip->cached_start = hip->cached_blocks = 0;
b33b7921d   Christoph Hellwig   hfsplus: split up...
594
  		hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
31651c607   Ernesto A. Fernández   hfsplus: avoid de...
595
  		mutex_lock(&fd.tree->tree_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
  	}
  	hfs_find_exit(&fd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598

6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
599
  	hip->alloc_blocks = blk_cnt;
d7bdb996a   Sougata Santra   fs/hfsplus/extent...
600
601
  out_unlock:
  	mutex_unlock(&hip->extents_lock);
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
602
  	hip->phys_size = inode->i_size;
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
603
604
  	hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >>
  		sb->s_blocksize_bits;
6af502de2   Christoph Hellwig   hfsplus: fix HFSP...
605
  	inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits);
e34947056   Christoph Hellwig   hfsplus: optimize...
606
  	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ALLOC_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
  }