/* * shrink_fork() * * Try to remove enough allocation blocks from 'fork' * so that it is 'ablocks' allocation blocks long. */ static void shrink_fork(struct hfs_fork *fork, int ablocks) { struct hfs_mdb *mdb = fork->entry->mdb; struct hfs_extent *ext; int i, error, next, count; hfs_u32 ablksz = mdb->alloc_blksz; next = (fork->psize / ablksz) - 1; ext = find_ext(fork, next); while (ext && ext->start && (ext->start >= ablocks)) { next = ext->start - 1; delete_extent(fork, ext); ext = find_ext(fork, next); } if (!ext) { fork->psize = (next + 1) * ablksz; return; } if ((count = next + 1 - ablocks) > 0) { for (i=2; (i>=0) && !ext->length[i]; --i) {}; lock_bitmap(mdb); while (count && (ext->length[i] <= count)) { ext->end -= ext->length[i]; count -= ext->length[i]; error = hfs_clear_vbm_bits(mdb, ext->block[i], ext->length[i]); if (error) { hfs_warn("hfs_truncate: error %d freeing " "blocks.\n", error); } ext->block[i] = ext->length[i] = 0; --i; } if (count) { ext->end -= count; ext->length[i] -= count; error = hfs_clear_vbm_bits(mdb, ext->block[i] + ext->length[i], count); if (error) { hfs_warn("hfs_truncate: error %d freeing " "blocks.\n", error); } } unlock_bitmap(mdb); update_ext(fork, ext); } fork->psize = ablocks * ablksz; }
static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent, u16 offset, u16 block_nr) { u16 count, start; int i; hfs_dump_extent(extent); for (i = 0; i < 3; extent++, i++) { count = be16_to_cpu(extent->count); if (offset == count) goto found; else if (offset < count) break; offset -= count; } /* panic? */ return -EIO; found: for (;;) { start = be16_to_cpu(extent->block); if (count <= block_nr) { hfs_clear_vbm_bits(sb, start, count); extent->block = 0; extent->count = 0; block_nr -= count; } else { count -= block_nr; hfs_clear_vbm_bits(sb, start + count, block_nr); extent->count = cpu_to_be16(count); block_nr = 0; } if (!block_nr || !i) return 0; i--; extent--; count = be16_to_cpu(extent->count); } }
/* * delete_extent() * * Description: * Deletes an extent record from a fork, reducing its physical length. * Input Variable(s): * struct hfs_fork *fork: the fork * struct hfs_extent *ext: the current last extent for 'fork' * Output Variable(s): * NONE * Returns: * void * Preconditions: * 'fork' points to a valid (struct hfs_fork) * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork' * and which is not also the first extent in 'fork'. * Postconditions: * The extent record has been removed if possible, and a warning has been * printed otherwise. */ static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext) { struct hfs_mdb *mdb = fork->entry->mdb; struct hfs_ext_key key; int error; if (fork->cache == ext) { set_cache(fork, ext->prev); } ext->prev->next = NULL; if (ext->count != 1) { hfs_warn("hfs_truncate: extent has count %d.\n", ext->count); } lock_bitmap(mdb); error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]); if (error) { hfs_warn("hfs_truncate: error %d freeing blocks.\n", error); } unlock_bitmap(mdb); build_key(&key, fork, ext->start); error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key)); if (error) { hfs_warn("hfs_truncate: error %d deleting an extent.\n", error); } HFS_DELETE(ext); }
/* * grow_fork() * * Try to add enough allocation blocks to 'fork' * so that it is 'ablock' allocation blocks long. */ static int grow_fork(struct hfs_fork *fork, int ablocks) { struct hfs_cat_entry *entry = fork->entry; struct hfs_mdb *mdb = entry->mdb; struct hfs_extent *ext; int i, start, err; hfs_u16 need, len=0; hfs_u32 ablksz = mdb->alloc_blksz; hfs_u32 blocks, clumpablks; blocks = fork->psize; need = ablocks - blocks/ablksz; if (need < 1) { /* no need to grow the fork */ return 0; } /* round up to clumpsize */ if (entry->u.file.clumpablks) { clumpablks = entry->u.file.clumpablks; } else { clumpablks = mdb->clumpablks; } need = ((need + clumpablks - 1) / clumpablks) * clumpablks; /* find last extent record and try to extend it */ if (!(ext = find_ext(fork, blocks/ablksz - 1))) { /* somehow we couldn't find the end of the file! */ return -1; } /* determine which is the last used extent in the record */ /* then try to allocate the blocks immediately following it */ for (i=2; (i>=0) && !ext->length[i]; --i) {}; if (i>=0) { /* try to extend the last extent */ start = ext->block[i] + ext->length[i]; err = 0; lock_bitmap(mdb); len = hfs_vbm_count_free(mdb, start); if (!len) { unlock_bitmap(mdb); goto more_extents; } if (need < len) { len = need; } err = hfs_set_vbm_bits(mdb, start, len); unlock_bitmap(mdb); if (err) { relse_ext(ext); return -1; } zero_blocks(mdb, start, len); ext->length[i] += len; ext->end += len; blocks = (fork->psize += len * ablksz); need -= len; update_ext(fork, ext); } more_extents: /* add some more extents */ while (need) { len = need; err = 0; lock_bitmap(mdb); start = hfs_vbm_search_free(mdb, &len); if (need < len) { len = need; } err = hfs_set_vbm_bits(mdb, start, len); unlock_bitmap(mdb); if (!len || err) { relse_ext(ext); return -1; } zero_blocks(mdb, start, len); /* determine which is the first free extent in the record */ for (i=0; (i<3) && ext->length[i]; ++i) {}; if (i < 3) { ext->block[i] = start; ext->length[i] = len; ext->end += len; update_ext(fork, ext); } else { if (!(ext = new_extent(fork, ext, blocks/ablksz, start, len, ablksz))) { lock_bitmap(mdb); hfs_clear_vbm_bits(mdb, start, len); unlock_bitmap(mdb); return -1; } } blocks = (fork->psize += len * ablksz); need -= len; } set_cache(fork, ext); relse_ext(ext); return 0; }