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 "ext4_utils.h" 18 #include "extent.h" 19 20 #include <sparse/sparse.h> 21 22 #include <inttypes.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 26 27 /* Creates data buffers for the first backing_len bytes of a block allocation 28 and queues them to be written */ 29 static 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 sparse_file_add_data(ext4_sparse_file, 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 */ 55 static void extent_create_backing_file(struct block_allocation *alloc, 56 u64 backing_len, const char *filename) 57 { 58 off64_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 sparse_file_add_file(ext4_sparse_file, filename, offset, len, 68 region_block); 69 offset += len; 70 backing_len -= len; 71 } 72 } 73 74 static struct block_allocation *do_inode_allocate_extents( 75 struct ext4_inode *inode, u64 len) 76 { 77 u32 block_len = DIV_ROUND_UP(len, info.block_size); 78 struct block_allocation *alloc = allocate_blocks(block_len + 1); 79 u32 extent_block = 0; 80 u32 file_block = 0; 81 struct ext4_extent *extent; 82 u64 blocks; 83 84 if (alloc == NULL) { 85 error("Failed to allocate %d blocks\n", block_len + 1); 86 return NULL; 87 } 88 89 int allocation_len = block_allocation_num_regions(alloc); 90 if (allocation_len <= 3) { 91 reduce_allocation(alloc, 1); 92 } else { 93 reserve_oob_blocks(alloc, 1); 94 extent_block = get_oob_block(alloc, 0); 95 } 96 97 if (!extent_block) { 98 struct ext4_extent_header *hdr = 99 (struct ext4_extent_header *)&inode->i_block[0]; 100 hdr->eh_magic = EXT4_EXT_MAGIC; 101 hdr->eh_entries = allocation_len; 102 hdr->eh_max = 3; 103 hdr->eh_generation = 0; 104 hdr->eh_depth = 0; 105 106 extent = (struct ext4_extent *)&inode->i_block[3]; 107 } else { 108 struct ext4_extent_header *hdr = 109 (struct ext4_extent_header *)&inode->i_block[0]; 110 hdr->eh_magic = EXT4_EXT_MAGIC; 111 hdr->eh_entries = 1; 112 hdr->eh_max = 3; 113 hdr->eh_generation = 0; 114 hdr->eh_depth = 1; 115 116 struct ext4_extent_idx *idx = 117 (struct ext4_extent_idx *)&inode->i_block[3]; 118 idx->ei_block = 0; 119 idx->ei_leaf_lo = extent_block; 120 idx->ei_leaf_hi = 0; 121 idx->ei_unused = 0; 122 123 u8 *data = calloc(info.block_size, 1); 124 if (!data) 125 critical_error_errno("calloc"); 126 127 sparse_file_add_data(ext4_sparse_file, data, info.block_size, 128 extent_block); 129 130 if (((int)(info.block_size - sizeof(struct ext4_extent_header) / 131 sizeof(struct ext4_extent))) < allocation_len) { 132 error("File size %"PRIu64" is too big to fit in a single extent block\n", 133 len); 134 return NULL; 135 } 136 137 hdr = (struct ext4_extent_header *)data; 138 hdr->eh_magic = EXT4_EXT_MAGIC; 139 hdr->eh_entries = allocation_len; 140 hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) / 141 sizeof(struct ext4_extent); 142 hdr->eh_generation = 0; 143 hdr->eh_depth = 0; 144 145 extent = (struct ext4_extent *)(data + 146 sizeof(struct ext4_extent_header)); 147 } 148 149 for (; !last_region(alloc); extent++, get_next_region(alloc)) { 150 u32 region_block; 151 u32 region_len; 152 153 get_region(alloc, ®ion_block, ®ion_len); 154 extent->ee_block = file_block; 155 extent->ee_len = region_len; 156 extent->ee_start_hi = 0; 157 extent->ee_start_lo = region_block; 158 file_block += region_len; 159 } 160 161 if (extent_block) 162 block_len += 1; 163 164 blocks = (u64)block_len * info.block_size / 512; 165 166 inode->i_flags |= EXT4_EXTENTS_FL; 167 inode->i_size_lo = len; 168 inode->i_size_high = len >> 32; 169 inode->i_blocks_lo = blocks; 170 inode->osd2.linux2.l_i_blocks_high = blocks >> 32; 171 172 rewind_alloc(alloc); 173 174 return alloc; 175 } 176 177 /* Allocates enough blocks to hold len bytes, with backing_len bytes in a data 178 buffer, and connects them to an inode. Returns a pointer to the data 179 buffer. */ 180 u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len, 181 u64 backing_len) 182 { 183 struct block_allocation *alloc; 184 u8 *data = NULL; 185 186 alloc = do_inode_allocate_extents(inode, len); 187 if (alloc == NULL) { 188 error("failed to allocate extents for %"PRIu64" bytes", len); 189 return NULL; 190 } 191 192 if (backing_len) { 193 data = extent_create_backing(alloc, backing_len); 194 if (!data) 195 error("failed to create backing for %"PRIu64" bytes", backing_len); 196 } 197 198 free_alloc(alloc); 199 200 return data; 201 } 202 203 /* Allocates enough blocks to hold len bytes, queues them to be written 204 from a file, and connects them to an inode. */ 205 struct block_allocation* inode_allocate_file_extents(struct ext4_inode *inode, u64 len, 206 const char *filename) 207 { 208 struct block_allocation *alloc; 209 210 alloc = do_inode_allocate_extents(inode, len); 211 if (alloc == NULL) { 212 error("failed to allocate extents for %"PRIu64" bytes", len); 213 return NULL; 214 } 215 216 extent_create_backing_file(alloc, len, filename); 217 return alloc; 218 } 219 220 /* Allocates enough blocks to hold len bytes and connects them to an inode */ 221 void inode_allocate_extents(struct ext4_inode *inode, u64 len) 222 { 223 struct block_allocation *alloc; 224 225 alloc = do_inode_allocate_extents(inode, len); 226 if (alloc == NULL) { 227 error("failed to allocate extents for %"PRIu64" bytes", len); 228 return; 229 } 230 231 free_alloc(alloc); 232 } 233