2 * linux/fs/hfsplus/extents.c
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handling of Extents both in catalog and extents overflow trees
11 #include <linux/errno.h>
13 #include <linux/pagemap.h>
14 #include <linux/version.h>
16 #include "hfsplus_fs.h"
17 #include "hfsplus_raw.h"
19 /* Compare two extents keys, returns 0 on same, pos/neg for difference */
20 int hfsplus_ext_cmp_key(hfsplus_btree_key
*k1
, hfsplus_btree_key
*k2
)
28 return be32_to_cpu(k1id
) < be32_to_cpu(k2id
) ? -1 : 1;
30 if (k1
->ext
.fork_type
!= k2
->ext
.fork_type
)
31 return k1
->ext
.fork_type
< k2
->ext
.fork_type
? -1 : 1;
33 k1s
= k1
->ext
.start_block
;
34 k2s
= k2
->ext
.start_block
;
37 return be32_to_cpu(k1s
) < be32_to_cpu(k2s
) ? -1 : 1;
40 static void hfsplus_ext_build_key(hfsplus_btree_key
*key
, u32 cnid
,
43 key
->key_len
= cpu_to_be16(HFSPLUS_EXT_KEYLEN
- 2);
44 key
->ext
.cnid
= cpu_to_be32(cnid
);
45 key
->ext
.start_block
= cpu_to_be32(block
);
46 key
->ext
.fork_type
= type
;
50 static u32
hfsplus_ext_find_block(struct hfsplus_extent
*ext
, u32 off
)
55 for (i
= 0; i
< 8; ext
++, i
++) {
56 count
= be32_to_cpu(ext
->block_count
);
58 return be32_to_cpu(ext
->start_block
) + off
;
65 static int hfsplus_ext_block_count(struct hfsplus_extent
*ext
)
70 for (i
= 0; i
< 8; ext
++, i
++)
71 count
+= be32_to_cpu(ext
->block_count
);
75 static u32
hfsplus_ext_lastblock(struct hfsplus_extent
*ext
)
80 for (i
= 0; i
< 7; ext
--, i
++)
83 return be32_to_cpu(ext
->start_block
) + be32_to_cpu(ext
->block_count
);
86 static void __hfsplus_ext_write_extent(struct inode
*inode
, struct hfs_find_data
*fd
)
90 hfsplus_ext_build_key(fd
->search_key
, inode
->i_ino
, HFSPLUS_I(inode
).cached_start
,
91 HFSPLUS_IS_RSRC(inode
) ? HFSPLUS_TYPE_RSRC
: HFSPLUS_TYPE_DATA
);
92 res
= hfs_brec_find(fd
);
93 if (HFSPLUS_I(inode
).flags
& HFSPLUS_FLG_EXT_NEW
) {
96 hfs_brec_insert(fd
, HFSPLUS_I(inode
).cached_extents
, sizeof(hfsplus_extent_rec
));
97 HFSPLUS_I(inode
).flags
&= ~(HFSPLUS_FLG_EXT_DIRTY
| HFSPLUS_FLG_EXT_NEW
);
101 hfs_bnode_write(fd
->bnode
, HFSPLUS_I(inode
).cached_extents
, fd
->entryoffset
, fd
->entrylength
);
102 HFSPLUS_I(inode
).flags
&= ~HFSPLUS_FLG_EXT_DIRTY
;
106 void hfsplus_ext_write_extent(struct inode
*inode
)
108 if (HFSPLUS_I(inode
).flags
& HFSPLUS_FLG_EXT_DIRTY
) {
109 struct hfs_find_data fd
;
111 hfs_find_init(HFSPLUS_SB(inode
->i_sb
).ext_tree
, &fd
);
112 __hfsplus_ext_write_extent(inode
, &fd
);
117 static inline int __hfsplus_ext_read_extent(struct hfs_find_data
*fd
,
118 struct hfsplus_extent
*extent
,
119 u32 cnid
, u32 block
, u8 type
)
123 hfsplus_ext_build_key(fd
->search_key
, cnid
, block
, type
);
124 fd
->key
->ext
.cnid
= 0;
125 res
= hfs_brec_find(fd
);
126 if (res
&& res
!= -ENOENT
)
128 if (fd
->key
->ext
.cnid
!= fd
->search_key
->ext
.cnid
||
129 fd
->key
->ext
.fork_type
!= fd
->search_key
->ext
.fork_type
)
131 if (fd
->entrylength
!= sizeof(hfsplus_extent_rec
))
133 hfs_bnode_read(fd
->bnode
, extent
, fd
->entryoffset
, sizeof(hfsplus_extent_rec
));
137 static inline int __hfsplus_ext_cache_extent(struct hfs_find_data
*fd
, struct inode
*inode
, u32 block
)
141 if (HFSPLUS_I(inode
).flags
& HFSPLUS_FLG_EXT_DIRTY
)
142 __hfsplus_ext_write_extent(inode
, fd
);
144 res
= __hfsplus_ext_read_extent(fd
, HFSPLUS_I(inode
).cached_extents
, inode
->i_ino
,
145 block
, HFSPLUS_IS_RSRC(inode
) ? HFSPLUS_TYPE_RSRC
: HFSPLUS_TYPE_DATA
);
147 HFSPLUS_I(inode
).cached_start
= be32_to_cpu(fd
->key
->ext
.start_block
);
148 HFSPLUS_I(inode
).cached_blocks
= hfsplus_ext_block_count(HFSPLUS_I(inode
).cached_extents
);
150 HFSPLUS_I(inode
).cached_start
= HFSPLUS_I(inode
).cached_blocks
= 0;
151 HFSPLUS_I(inode
).flags
&= ~(HFSPLUS_FLG_EXT_DIRTY
| HFSPLUS_FLG_EXT_NEW
);
156 static int hfsplus_ext_read_extent(struct inode
*inode
, u32 block
)
158 struct hfs_find_data fd
;
161 if (block
>= HFSPLUS_I(inode
).cached_start
&&
162 block
< HFSPLUS_I(inode
).cached_start
+ HFSPLUS_I(inode
).cached_blocks
)
165 hfs_find_init(HFSPLUS_SB(inode
->i_sb
).ext_tree
, &fd
);
166 res
= __hfsplus_ext_cache_extent(&fd
, inode
, block
);
171 /* Get a block at iblock for inode, possibly allocating if create */
172 int hfsplus_get_block(struct inode
*inode
, sector_t iblock
,
173 struct buffer_head
*bh_result
, int create
)
175 struct super_block
*sb
;
177 u32 ablock
, dblock
, mask
;
182 /* Convert inode block to disk allocation block */
183 shift
= HFSPLUS_SB(sb
).alloc_blksz_shift
- sb
->s_blocksize_bits
;
184 ablock
= iblock
>> HFSPLUS_SB(sb
).fs_shift
;
186 if (iblock
>= HFSPLUS_I(inode
).fs_blocks
) {
187 if (iblock
> HFSPLUS_I(inode
).fs_blocks
|| !create
)
189 if (ablock
>= HFSPLUS_I(inode
).alloc_blocks
) {
190 res
= hfsplus_file_extend(inode
);
197 if (ablock
< HFSPLUS_I(inode
).first_blocks
) {
198 dblock
= hfsplus_ext_find_block(HFSPLUS_I(inode
).first_extents
, ablock
);
202 down(&HFSPLUS_I(inode
).extents_lock
);
203 res
= hfsplus_ext_read_extent(inode
, ablock
);
205 dblock
= hfsplus_ext_find_block(HFSPLUS_I(inode
).cached_extents
, ablock
-
206 HFSPLUS_I(inode
).cached_start
);
208 up(&HFSPLUS_I(inode
).extents_lock
);
211 up(&HFSPLUS_I(inode
).extents_lock
);
214 dprint(DBG_EXTENT
, "get_block(%lu): %llu - %u\n", inode
->i_ino
, (long long)iblock
, dblock
);
215 mask
= (1 << HFSPLUS_SB(sb
).fs_shift
) - 1;
216 map_bh(bh_result
, sb
, (dblock
<< HFSPLUS_SB(sb
).fs_shift
) + HFSPLUS_SB(sb
).blockoffset
+ (iblock
& mask
));
218 set_buffer_new(bh_result
);
219 HFSPLUS_I(inode
).phys_size
+= sb
->s_blocksize
;
220 HFSPLUS_I(inode
).fs_blocks
++;
221 inode_add_bytes(inode
, sb
->s_blocksize
);
222 mark_inode_dirty(inode
);
227 static void hfsplus_dump_extent(struct hfsplus_extent
*extent
)
231 dprint(DBG_EXTENT
, " ");
232 for (i
= 0; i
< 8; i
++)
233 dprint(DBG_EXTENT
, " %u:%u", be32_to_cpu(extent
[i
].start_block
),
234 be32_to_cpu(extent
[i
].block_count
));
235 dprint(DBG_EXTENT
, "\n");
238 static int hfsplus_add_extent(struct hfsplus_extent
*extent
, u32 offset
,
239 u32 alloc_block
, u32 block_count
)
244 hfsplus_dump_extent(extent
);
245 for (i
= 0; i
< 8; extent
++, i
++) {
246 count
= be32_to_cpu(extent
->block_count
);
247 if (offset
== count
) {
248 start
= be32_to_cpu(extent
->start_block
);
249 if (alloc_block
!= start
+ count
) {
253 extent
->start_block
= cpu_to_be32(alloc_block
);
255 block_count
+= count
;
256 extent
->block_count
= cpu_to_be32(block_count
);
258 } else if (offset
< count
)
266 static int hfsplus_free_extents(struct super_block
*sb
,
267 struct hfsplus_extent
*extent
,
268 u32 offset
, u32 block_nr
)
273 hfsplus_dump_extent(extent
);
274 for (i
= 0; i
< 8; extent
++, i
++) {
275 count
= be32_to_cpu(extent
->block_count
);
278 else if (offset
< count
)
286 start
= be32_to_cpu(extent
->start_block
);
287 if (count
<= block_nr
) {
288 hfsplus_block_free(sb
, start
, count
);
289 extent
->block_count
= 0;
290 extent
->start_block
= 0;
294 hfsplus_block_free(sb
, start
+ count
, block_nr
);
295 extent
->block_count
= cpu_to_be32(count
);
302 count
= be32_to_cpu(extent
->block_count
);
306 int hfsplus_free_fork(struct super_block
*sb
, u32 cnid
, struct hfsplus_fork_raw
*fork
, int type
)
308 struct hfs_find_data fd
;
309 hfsplus_extent_rec ext_entry
;
310 u32 total_blocks
, blocks
, start
;
313 total_blocks
= be32_to_cpu(fork
->total_blocks
);
318 for (i
= 0; i
< 8; i
++)
319 blocks
+= be32_to_cpu(fork
->extents
[i
].block_count
);
321 res
= hfsplus_free_extents(sb
, fork
->extents
, blocks
, blocks
);
324 if (total_blocks
== blocks
)
327 hfs_find_init(HFSPLUS_SB(sb
).ext_tree
, &fd
);
329 res
= __hfsplus_ext_read_extent(&fd
, ext_entry
, cnid
,
333 start
= be32_to_cpu(fd
.key
->ext
.start_block
);
334 hfsplus_free_extents(sb
, ext_entry
,
335 total_blocks
- start
,
337 hfs_brec_remove(&fd
);
338 total_blocks
= start
;
339 } while (total_blocks
> blocks
);
345 int hfsplus_file_extend(struct inode
*inode
)
347 struct super_block
*sb
= inode
->i_sb
;
348 u32 start
, len
, goal
;
351 if (HFSPLUS_SB(sb
).alloc_file
->i_size
* 8 < HFSPLUS_SB(sb
).total_blocks
- HFSPLUS_SB(sb
).free_blocks
+ 8) {
353 printk("extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb
).alloc_file
->i_size
* 8,
354 HFSPLUS_SB(sb
).total_blocks
, HFSPLUS_SB(sb
).free_blocks
);
359 down(&HFSPLUS_I(inode
).extents_lock
);
360 if (HFSPLUS_I(inode
).alloc_blocks
== HFSPLUS_I(inode
).first_blocks
)
361 goal
= hfsplus_ext_lastblock(HFSPLUS_I(inode
).first_extents
);
363 res
= hfsplus_ext_read_extent(inode
, HFSPLUS_I(inode
).alloc_blocks
);
366 goal
= hfsplus_ext_lastblock(HFSPLUS_I(inode
).cached_extents
);
369 len
= HFSPLUS_I(inode
).clump_blocks
;
370 start
= hfsplus_block_allocate(sb
, HFSPLUS_SB(sb
).total_blocks
, goal
, &len
);
371 if (start
>= HFSPLUS_SB(sb
).total_blocks
) {
372 start
= hfsplus_block_allocate(sb
, goal
, 0, &len
);
379 dprint(DBG_EXTENT
, "extend %lu: %u,%u\n", inode
->i_ino
, start
, len
);
380 if (HFSPLUS_I(inode
).alloc_blocks
<= HFSPLUS_I(inode
).first_blocks
) {
381 if (!HFSPLUS_I(inode
).first_blocks
) {
382 dprint(DBG_EXTENT
, "first extents\n");
384 HFSPLUS_I(inode
).first_extents
[0].start_block
= cpu_to_be32(start
);
385 HFSPLUS_I(inode
).first_extents
[0].block_count
= cpu_to_be32(len
);
388 /* try to append to extents in inode */
389 res
= hfsplus_add_extent(HFSPLUS_I(inode
).first_extents
,
390 HFSPLUS_I(inode
).alloc_blocks
,
396 hfsplus_dump_extent(HFSPLUS_I(inode
).first_extents
);
397 HFSPLUS_I(inode
).first_blocks
+= len
;
400 res
= hfsplus_add_extent(HFSPLUS_I(inode
).cached_extents
,
401 HFSPLUS_I(inode
).alloc_blocks
-
402 HFSPLUS_I(inode
).cached_start
,
405 hfsplus_dump_extent(HFSPLUS_I(inode
).cached_extents
);
406 HFSPLUS_I(inode
).flags
|= HFSPLUS_FLG_EXT_DIRTY
;
407 HFSPLUS_I(inode
).cached_blocks
+= len
;
408 } else if (res
== -ENOSPC
)
412 up(&HFSPLUS_I(inode
).extents_lock
);
414 HFSPLUS_I(inode
).alloc_blocks
+= len
;
415 mark_inode_dirty(inode
);
420 dprint(DBG_EXTENT
, "insert new extent\n");
421 hfsplus_ext_write_extent(inode
);
423 memset(HFSPLUS_I(inode
).cached_extents
, 0, sizeof(hfsplus_extent_rec
));
424 HFSPLUS_I(inode
).cached_extents
[0].start_block
= cpu_to_be32(start
);
425 HFSPLUS_I(inode
).cached_extents
[0].block_count
= cpu_to_be32(len
);
426 hfsplus_dump_extent(HFSPLUS_I(inode
).cached_extents
);
427 HFSPLUS_I(inode
).flags
|= HFSPLUS_FLG_EXT_DIRTY
| HFSPLUS_FLG_EXT_NEW
;
428 HFSPLUS_I(inode
).cached_start
= HFSPLUS_I(inode
).alloc_blocks
;
429 HFSPLUS_I(inode
).cached_blocks
= len
;
435 void hfsplus_file_truncate(struct inode
*inode
)
437 struct super_block
*sb
= inode
->i_sb
;
438 struct hfs_find_data fd
;
439 u32 alloc_cnt
, blk_cnt
, start
;
442 dprint(DBG_INODE
, "truncate: %lu, %Lu -> %Lu\n", inode
->i_ino
,
443 (long long)HFSPLUS_I(inode
).phys_size
, inode
->i_size
);
444 if (inode
->i_size
> HFSPLUS_I(inode
).phys_size
) {
445 struct address_space
*mapping
= inode
->i_mapping
;
447 u32 size
= inode
->i_size
- 1;
450 page
= grab_cache_page(mapping
, size
>> PAGE_CACHE_SHIFT
);
453 size
&= PAGE_CACHE_SIZE
- 1;
455 res
= mapping
->a_ops
->prepare_write(NULL
, page
, size
, size
);
457 res
= mapping
->a_ops
->commit_write(NULL
, page
, size
, size
);
459 inode
->i_size
= HFSPLUS_I(inode
).phys_size
;
461 page_cache_release(page
);
462 mark_inode_dirty(inode
);
465 blk_cnt
= (inode
->i_size
+ HFSPLUS_SB(sb
).alloc_blksz
- 1) >> HFSPLUS_SB(sb
).alloc_blksz_shift
;
466 alloc_cnt
= HFSPLUS_I(inode
).alloc_blocks
;
467 if (blk_cnt
== alloc_cnt
)
470 down(&HFSPLUS_I(inode
).extents_lock
);
471 hfs_find_init(HFSPLUS_SB(sb
).ext_tree
, &fd
);
473 if (alloc_cnt
== HFSPLUS_I(inode
).first_blocks
) {
474 hfsplus_free_extents(sb
, HFSPLUS_I(inode
).first_extents
,
475 alloc_cnt
, alloc_cnt
- blk_cnt
);
476 hfsplus_dump_extent(HFSPLUS_I(inode
).first_extents
);
477 HFSPLUS_I(inode
).first_blocks
= blk_cnt
;
480 res
= __hfsplus_ext_cache_extent(&fd
, inode
, alloc_cnt
);
483 start
= HFSPLUS_I(inode
).cached_start
;
484 hfsplus_free_extents(sb
, HFSPLUS_I(inode
).cached_extents
,
485 alloc_cnt
- start
, alloc_cnt
- blk_cnt
);
486 hfsplus_dump_extent(HFSPLUS_I(inode
).cached_extents
);
487 if (blk_cnt
> start
) {
488 HFSPLUS_I(inode
).flags
|= HFSPLUS_FLG_EXT_DIRTY
;
492 HFSPLUS_I(inode
).cached_start
= HFSPLUS_I(inode
).cached_blocks
= 0;
493 HFSPLUS_I(inode
).flags
&= ~(HFSPLUS_FLG_EXT_DIRTY
| HFSPLUS_FLG_EXT_NEW
);
494 hfs_brec_remove(&fd
);
497 up(&HFSPLUS_I(inode
).extents_lock
);
499 HFSPLUS_I(inode
).alloc_blocks
= blk_cnt
;
501 HFSPLUS_I(inode
).phys_size
= inode
->i_size
;
502 HFSPLUS_I(inode
).fs_blocks
= (inode
->i_size
+ sb
->s_blocksize
- 1) >> sb
->s_blocksize_bits
;
503 inode_set_bytes(inode
, HFSPLUS_I(inode
).fs_blocks
<< sb
->s_blocksize_bits
);
504 mark_inode_dirty(inode
);