1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdlib.h> 18#include <stdio.h> 19 20#include "ext4_utils.h" 21#include "ext4.h" 22#include "ext4_extents.h" 23#include "backed_block.h" 24 25#include "extent.h" 26 27/* Creates data buffers for the first backing_len bytes of a block allocation 28 and queues them to be written */ 29static u8 *extent_create_backing(struct block_allocation *alloc, 30 u64 backing_len) 31{ 32 u8 *data = calloc(backing_len, 1); 33 if (!data) 34 critical_error_errno("calloc"); 35 36 u8 *ptr = data; 37 for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { 38 u32 region_block; 39 u32 region_len; 40 u32 len; 41 get_region(alloc, ®ion_block, ®ion_len); 42 43 len = min(region_len * info.block_size, backing_len); 44 45 queue_data_block(ptr, len, region_block); 46 ptr += len; 47 backing_len -= len; 48 } 49 50 return data; 51} 52 53/* Queues each chunk of a file to be written to contiguous data block 54 regions */ 55static void extent_create_backing_file(struct block_allocation *alloc, 56 u64 backing_len, const char *filename) 57{ 58 off_t offset = 0; 59 for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { 60 u32 region_block; 61 u32 region_len; 62 u32 len; 63 get_region(alloc, ®ion_block, ®ion_len); 64 65 len = min(region_len * info.block_size, backing_len); 66 67 queue_data_file(filename, offset, len, region_block); 68 offset += len; 69 backing_len -= len; 70 } 71} 72 73static struct block_allocation *do_inode_allocate_extents( 74 struct ext4_inode *inode, u64 len) 75{ 76 u32 block_len = DIV_ROUND_UP(len, info.block_size); 77 struct block_allocation *alloc = allocate_blocks(block_len + 1); 78 u32 extent_block = 0; 79 u32 file_block = 0; 80 struct ext4_extent *extent; 81 u64 blocks; 82 83 if (alloc == NULL) { 84 error("Failed to allocate %d blocks\n", block_len + 1); 85 return NULL; 86 } 87 88 int allocation_len = block_allocation_num_regions(alloc); 89 if (allocation_len <= 3) { 90 reduce_allocation(alloc, 1); 91 } else { 92 reserve_oob_blocks(alloc, 1); 93 extent_block = get_oob_block(alloc, 0); 94 } 95 96 if (!extent_block) { 97 struct ext4_extent_header *hdr = 98 (struct ext4_extent_header *)&inode->i_block[0]; 99 hdr->eh_magic = EXT4_EXT_MAGIC; 100 hdr->eh_entries = allocation_len; 101 hdr->eh_max = 3; 102 hdr->eh_generation = 0; 103 hdr->eh_depth = 0; 104 105 extent = (struct ext4_extent *)&inode->i_block[3]; 106 } else { 107 struct ext4_extent_header *hdr = 108 (struct ext4_extent_header *)&inode->i_block[0]; 109 hdr->eh_magic = EXT4_EXT_MAGIC; 110 hdr->eh_entries = 1; 111 hdr->eh_max = 3; 112 hdr->eh_generation = 0; 113 hdr->eh_depth = 1; 114 115 struct ext4_extent_idx *idx = 116 (struct ext4_extent_idx *)&inode->i_block[3]; 117 idx->ei_block = 0; 118 idx->ei_leaf_lo = extent_block; 119 idx->ei_leaf_hi = 0; 120 idx->ei_unused = 0; 121 122 u8 *data = calloc(info.block_size, 1); 123 if (!data) 124 critical_error_errno("calloc"); 125 126 queue_data_block(data, info.block_size, extent_block); 127 128 if (((int)(info.block_size - sizeof(struct ext4_extent_header) / 129 sizeof(struct ext4_extent))) < allocation_len) { 130 error("File size %llu is too big to fit in a single extent block\n", 131 len); 132 return NULL; 133 } 134 135 hdr = (struct ext4_extent_header *)data; 136 hdr->eh_magic = EXT4_EXT_MAGIC; 137 hdr->eh_entries = allocation_len; 138 hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) / 139 sizeof(struct ext4_extent); 140 hdr->eh_generation = 0; 141 hdr->eh_depth = 0; 142 143 extent = (struct ext4_extent *)(data + 144 sizeof(struct ext4_extent_header)); 145 } 146 147 for (; !last_region(alloc); extent++, get_next_region(alloc)) { 148 u32 region_block; 149 u32 region_len; 150 151 get_region(alloc, ®ion_block, ®ion_len); 152 extent->ee_block = file_block; 153 extent->ee_len = region_len; 154 extent->ee_start_hi = 0; 155 extent->ee_start_lo = region_block; 156 file_block += region_len; 157 } 158 159 if (extent_block) 160 block_len += 1; 161 162 blocks = (u64)block_len * info.block_size / 512; 163 164 inode->i_flags |= EXT4_EXTENTS_FL; 165 inode->i_size_lo = len; 166 inode->i_size_high = len >> 32; 167 inode->i_blocks_lo = blocks; 168 inode->osd2.linux2.l_i_blocks_high = blocks >> 32; 169 170 rewind_alloc(alloc); 171 172 return alloc; 173} 174 175/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data 176 buffer, and connects them to an inode. Returns a pointer to the data 177 buffer. */ 178u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len, 179 u64 backing_len) 180{ 181 struct block_allocation *alloc; 182 u8 *data = NULL; 183 184 alloc = do_inode_allocate_extents(inode, len); 185 if (alloc == NULL) { 186 error("failed to allocate extents for %llu bytes", len); 187 return NULL; 188 } 189 190 if (backing_len) { 191 data = extent_create_backing(alloc, backing_len); 192 if (!data) 193 error("failed to create backing for %llu bytes", backing_len); 194 } 195 196 free_alloc(alloc); 197 198 return data; 199} 200 201/* Allocates enough blocks to hold len bytes, queues them to be written 202 from a file, and connects them to an inode. */ 203void inode_allocate_file_extents(struct ext4_inode *inode, u64 len, 204 const char *filename) 205{ 206 struct block_allocation *alloc; 207 208 alloc = do_inode_allocate_extents(inode, len); 209 if (alloc == NULL) { 210 error("failed to allocate extents for %llu bytes", len); 211 return; 212 } 213 214 extent_create_backing_file(alloc, len, filename); 215 216 free_alloc(alloc); 217} 218 219/* Allocates enough blocks to hold len bytes and connects them to an inode */ 220void inode_allocate_extents(struct ext4_inode *inode, u64 len) 221{ 222 struct block_allocation *alloc; 223 224 alloc = do_inode_allocate_extents(inode, len); 225 if (alloc == NULL) { 226 error("failed to allocate extents for %llu bytes", len); 227 return; 228 } 229 230 free_alloc(alloc); 231} 232