diff options
author | Colin Cross <ccross@android.com> | 2010-06-11 14:21:37 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2010-06-16 13:12:01 -0700 |
commit | ec0a2e83dc66d67addeb90e83144187691852a3e (patch) | |
tree | 6702cdbbbd9b5d6d939b6d3abb3c2b8514146550 /ext4_utils/extent.c | |
parent | 545e6ca13f744650e4a62630f1b901c0840dcd43 (diff) | |
download | extras-ec0a2e83dc66d67addeb90e83144187691852a3e.tar.gz |
Initial commit of ext4_utils
Change-Id: I911d5b7fd7170ec81d544850717d8e69976e272b
Diffstat (limited to 'ext4_utils/extent.c')
-rw-r--r-- | ext4_utils/extent.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/ext4_utils/extent.c b/ext4_utils/extent.c new file mode 100644 index 00000000..b1e7749f --- /dev/null +++ b/ext4_utils/extent.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> + +#include "ext4_utils.h" +#include "ext4.h" +#include "ext4_extents.h" +#include "backed_block.h" + +#include "extent.h" + +/* Creates data buffers for the first backing_len bytes of a block allocation + and queues them to be written */ +static u8 *extent_create_backing(struct block_allocation *alloc, + u64 backing_len) +{ + u8 *data = calloc(backing_len, 1); + if (!data) + critical_error_errno("calloc"); + + u8 *ptr = data; + for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { + u32 region_block; + u32 region_len; + u32 len; + get_region(alloc, ®ion_block, ®ion_len); + + len = min(region_len * info.block_size, backing_len); + + queue_data_block(ptr, len, region_block); + ptr += len; + backing_len -= len; + } + + return data; +} + +/* Queues each chunk of a file to be written to contiguous data block + regions */ +static void extent_create_backing_file(struct block_allocation *alloc, + u64 backing_len, const char *filename) +{ + off_t offset = 0; + for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { + u32 region_block; + u32 region_len; + u32 len; + get_region(alloc, ®ion_block, ®ion_len); + + len = min(region_len * info.block_size, backing_len); + + queue_data_file(filename, offset, len, region_block); + offset += len; + backing_len -= len; + } +} + +static struct block_allocation *do_inode_allocate_extents( + struct ext4_inode *inode, u64 len) +{ + u32 block_len = DIV_ROUND_UP(len, info.block_size); + struct block_allocation *alloc = allocate_blocks(block_len + 1); + u32 extent_block = 0; + u32 file_block = 0; + struct ext4_extent *extent; + u64 blocks; + + if (alloc == NULL) { + error("Failed to allocate %d blocks\n", block_len + 1); + return NULL; + } + + int allocation_len = block_allocation_num_regions(alloc); + if (allocation_len <= 3) { + reduce_allocation(alloc, 1); + } else { + reserve_oob_blocks(alloc, 1); + extent_block = get_oob_block(alloc, 0); + } + + if (!extent_block) { + struct ext4_extent_header *hdr = + (struct ext4_extent_header *)&inode->i_block[0]; + hdr->eh_magic = EXT4_EXT_MAGIC; + hdr->eh_entries = allocation_len; + hdr->eh_max = 3; + hdr->eh_generation = 0; + hdr->eh_depth = 0; + + extent = (struct ext4_extent *)&inode->i_block[3]; + } else { + struct ext4_extent_header *hdr = + (struct ext4_extent_header *)&inode->i_block[0]; + hdr->eh_magic = EXT4_EXT_MAGIC; + hdr->eh_entries = 1; + hdr->eh_max = 3; + hdr->eh_generation = 0; + hdr->eh_depth = 1; + + struct ext4_extent_idx *idx = + (struct ext4_extent_idx *)&inode->i_block[3]; + idx->ei_block = 0; + idx->ei_leaf_lo = extent_block; + idx->ei_leaf_hi = 0; + idx->ei_unused = 0; + + u8 *data = calloc(info.block_size, 1); + if (!data) + critical_error_errno("calloc"); + + queue_data_block(data, info.block_size, extent_block); + + if (((int)(info.block_size - sizeof(struct ext4_extent_header) / + sizeof(struct ext4_extent))) < allocation_len) { + error("File size %llu is too big to fit in a single extent block\n", + len); + return NULL; + } + + hdr = (struct ext4_extent_header *)data; + hdr->eh_magic = EXT4_EXT_MAGIC; + hdr->eh_entries = allocation_len; + hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) + / sizeof(struct ext4_extent); + hdr->eh_generation = 0; + hdr->eh_depth = 0; + + extent = (struct ext4_extent *)(data + + sizeof(struct ext4_extent_header)); + } + + for (; !last_region(alloc); extent++, get_next_region(alloc)) { + u32 region_block; + u32 region_len; + + get_region(alloc, ®ion_block, ®ion_len); + extent->ee_block = file_block; + extent->ee_len = region_len; + extent->ee_start_hi = 0; + extent->ee_start_lo = region_block; + file_block += region_len; + } + + if (extent_block) + block_len += 1; + + blocks = (u64)block_len * info.block_size / 512; + + inode->i_flags |= EXT4_EXTENTS_FL; + inode->i_size_lo = len; + inode->i_size_high = len >> 32; + inode->i_blocks_lo = blocks; + inode->osd2.linux2.l_i_blocks_high = blocks >> 32; + + rewind_alloc(alloc); + + return alloc; +} + +/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data + buffer, and connects them to an inode. Returns a pointer to the data + buffer. */ +u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len, + u64 backing_len) +{ + struct block_allocation *alloc; + u8 *data = NULL; + + alloc = do_inode_allocate_extents(inode, len); + if (alloc == NULL) { + error("failed to allocate extents for %llu bytes", len); + return NULL; + } + + if (backing_len) { + data = extent_create_backing(alloc, backing_len); + if (!data) + error("failed to create backing for %llu bytes", backing_len); + } + + free_alloc(alloc); + + return data; +} + +/* Allocates enough blocks to hold len bytes, queues them to be written + from a file, and connects them to an inode. */ +void inode_allocate_file_extents(struct ext4_inode *inode, u64 len, + const char *filename) +{ + struct block_allocation *alloc; + + alloc = do_inode_allocate_extents(inode, len); + if (alloc == NULL) { + error("failed to allocate extents for %llu bytes", len); + return; + } + + extent_create_backing_file(alloc, len, filename); + + free_alloc(alloc); +} + +/* Allocates enough blocks to hold len bytes and connects them to an inode */ +void inode_allocate_extents(struct ext4_inode *inode, u64 len) +{ + struct block_allocation *alloc; + + alloc = do_inode_allocate_extents(inode, len); + if (alloc == NULL) { + error("failed to allocate extents for %llu bytes", len); + return; + } + + free_alloc(alloc); +} |