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