/* 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_indirect(struct ext4_inode *inode, unsigned long len, unsigned long backing_len) { struct block_allocation *alloc; u32 block_len = DIV_ROUND_UP(len, info.block_size); u8 *data = NULL; alloc = do_inode_allocate_indirect(inode, block_len); if (alloc == NULL) { error("failed to allocate extents for %lu bytes", len); return NULL; } if (backing_len) { data = create_backing(alloc, backing_len); if (!data) error("failed to create backing for %lu bytes", backing_len); } rewind_alloc(alloc); if (do_inode_attach_indirect(inode, alloc, block_len)) error("failed to attach blocks to indirect inode"); free_alloc(alloc); return data; }
static int do_inode_attach_indirect(struct ext4_inode *inode, struct block_allocation *alloc, u32 block_len) { u32 count = block_len; if (inode_attach_direct_blocks(inode, alloc, &count)) { error("failed to attach direct blocks to inode"); return -1; } if (count > 0) { if (inode_attach_indirect_blocks(inode, alloc, &count)) { error("failed to attach indirect blocks to inode"); return -1; } } if (count > 0) { if (inode_attach_dindirect_blocks(inode, alloc, &count)) { error("failed to attach dindirect blocks to inode"); return -1; } } if (count > 0) { if (inode_attach_tindirect_blocks(inode, alloc, &count)) { error("failed to attach tindirect blocks to inode"); return -1; } } if (count) { error("blocks left after triply-indirect allocation"); return -1; } rewind_alloc(alloc); return 0; }
/* Allocates enough blocks to hold len bytes and connects them to an inode */ void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len) { struct block_allocation *alloc; u32 block_len = DIV_ROUND_UP(len, info.block_size); u32 indirect_len = indirect_blocks_needed(block_len); alloc = do_inode_allocate_indirect(inode, block_len); if (alloc == NULL) { error("failed to allocate extents for %lu bytes", len); return; } reserve_all_indirect_blocks(alloc, block_len); rewind_alloc(alloc); if (do_inode_attach_indirect(inode, alloc, block_len)) error("failed to attach blocks to indirect inode"); inode->i_flags = 0; inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512; inode->i_size_lo = len; free_alloc(alloc); }
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; }