• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/make_ext4fs/indirect.c

  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 "indirect.h"
 19 #include "allocate.h"
 20 
 21 #include <sparse/sparse.h>
 22 
 23 #include <stdlib.h>
 24 #include <stdio.h>
 25 
 26 /* Creates data buffers for the first backing_len bytes of a block allocation
 27    and queues them to be written */
 28 static u8 *create_backing(struct block_allocation *alloc,
 29                 unsigned long backing_len)
 30 {
 31         if (DIV_ROUND_UP(backing_len, info.block_size) > EXT4_NDIR_BLOCKS)
 32                 critical_error("indirect backing larger than %d blocks", EXT4_NDIR_BLOCKS);
 33 
 34         u8 *data = calloc(backing_len, 1);
 35         if (!data)
 36                 critical_error_errno("calloc");
 37 
 38         u8 *ptr = data;
 39         for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
 40                 u32 region_block;
 41                 u32 region_len;
 42                 u32 len;
 43                 get_region(alloc, &region_block, &region_len);
 44 
 45                 len = min(region_len * info.block_size, backing_len);
 46 
 47                 sparse_file_add_data(ext4_sparse_file, ptr, len, region_block);
 48                 ptr += len;
 49                 backing_len -= len;
 50         }
 51 
 52         return data;
 53 }
 54 
 55 static void reserve_indirect_block(struct block_allocation *alloc, int len)
 56 {
 57         if (reserve_oob_blocks(alloc, 1)) {
 58                 error("failed to reserve oob block");
 59                 return;
 60         }
 61 
 62         if (advance_blocks(alloc, len)) {
 63                 error("failed to advance %d blocks", len);
 64                 return;
 65         }
 66 }
 67 
 68 static void reserve_dindirect_block(struct block_allocation *alloc, int len)
 69 {
 70         if (reserve_oob_blocks(alloc, 1)) {
 71                 error("failed to reserve oob block");
 72                 return;
 73         }
 74 
 75         while (len > 0) {
 76                 int ind_block_len = min((int)aux_info.blocks_per_ind, len);
 77 
 78                 reserve_indirect_block(alloc, ind_block_len);
 79 
 80                 len -= ind_block_len;
 81         }
 82 
 83 }
 84 
 85 static void reserve_tindirect_block(struct block_allocation *alloc, int len)
 86 {
 87         if (reserve_oob_blocks(alloc, 1)) {
 88                 error("failed to reserve oob block");
 89                 return;
 90         }
 91 
 92         while (len > 0) {
 93                 int dind_block_len = min((int)aux_info.blocks_per_dind, len);
 94 
 95                 reserve_dindirect_block(alloc, dind_block_len);
 96 
 97                 len -= dind_block_len;
 98         }
 99 }
100 
101 static void fill_indirect_block(u32 *ind_block, int len, struct block_allocation *alloc)
102 {
103         int i;
104         for (i = 0; i < len; i++) {
105                 ind_block[i] = get_block(alloc, i);
106         }
107 }
108 
109 static void fill_dindirect_block(u32 *dind_block, int len, struct block_allocation *alloc)
110 {
111         int i;
112         u32 ind_block;
113 
114         for (i = 0; len >  0; i++) {
115                 ind_block = get_oob_block(alloc, 0);
116                 if (advance_oob_blocks(alloc, 1)) {
117                         error("failed to reserve oob block");
118                         return;
119                 }
120 
121                 dind_block[i] = ind_block;
122 
123                 u32 *ind_block_data = calloc(info.block_size, 1);
124                 sparse_file_add_data(ext4_sparse_file, ind_block_data, info.block_size,
125                                 ind_block);
126                 int ind_block_len = min((int)aux_info.blocks_per_ind, len);
127 
128                 fill_indirect_block(ind_block_data, ind_block_len, alloc);
129 
130                 if (advance_blocks(alloc, ind_block_len)) {
131                         error("failed to advance %d blocks", ind_block_len);
132                         return;
133                 }
134 
135                 len -= ind_block_len;
136         }
137 }
138 
139 static void fill_tindirect_block(u32 *tind_block, int len, struct block_allocation *alloc)
140 {
141         int i;
142         u32 dind_block;
143 
144         for (i = 0; len > 0; i++) {
145                 dind_block = get_oob_block(alloc, 0);
146                 if (advance_oob_blocks(alloc, 1)) {
147                         error("failed to reserve oob block");
148                         return;
149                 }
150 
151                 tind_block[i] = dind_block;
152 
153                 u32 *dind_block_data = calloc(info.block_size, 1);
154                 sparse_file_add_data(ext4_sparse_file, dind_block_data, info.block_size,
155                                 dind_block);
156                 int dind_block_len = min((int)aux_info.blocks_per_dind, len);
157 
158                 fill_dindirect_block(dind_block_data, dind_block_len, alloc);
159 
160                 len -= dind_block_len;
161         }
162 }
163 
164 /* Given an allocation, attach as many blocks as possible to direct inode
165    blocks, and return the rest */
166 static int inode_attach_direct_blocks(struct ext4_inode *inode,
167                 struct block_allocation *alloc, u32 *block_len)
168 {
169         int len = min(*block_len, EXT4_NDIR_BLOCKS);
170         int i;
171 
172         for (i = 0; i < len; i++) {
173                 inode->i_block[i] = get_block(alloc, i);
174         }
175 
176         if (advance_blocks(alloc, len)) {
177                 error("failed to advance %d blocks", len);
178                 return -1;
179         }
180 
181         *block_len -= len;
182         return 0;
183 }
184 
185 /* Given an allocation, attach as many blocks as possible to indirect blocks,
186    and return the rest
187    Assumes that the blocks necessary to hold the indirect blocks were included
188    as part of the allocation */
189 static int inode_attach_indirect_blocks(struct ext4_inode *inode,
190                 struct block_allocation *alloc, u32 *block_len)
191 {
192         int len = min(*block_len, aux_info.blocks_per_ind);
193 
194         int ind_block = get_oob_block(alloc, 0);
195         inode->i_block[EXT4_IND_BLOCK] = ind_block;
196 
197         if (advance_oob_blocks(alloc, 1)) {
198                 error("failed to advance oob block");
199                 return -1;
200         }
201 
202         u32 *ind_block_data = calloc(info.block_size, 1);
203         sparse_file_add_data(ext4_sparse_file, ind_block_data, info.block_size,
204                         ind_block);
205 
206         fill_indirect_block(ind_block_data, len, alloc);
207 
208         if (advance_blocks(alloc, len)) {
209                 error("failed to advance %d blocks", len);
210                 return -1;
211         }
212 
213         *block_len -= len;
214         return 0;
215 }
216 
217 /* Given an allocation, attach as many blocks as possible to doubly indirect
218    blocks, and return the rest.
219    Assumes that the blocks necessary to hold the indirect and doubly indirect
220    blocks were included as part of the allocation */
221 static int inode_attach_dindirect_blocks(struct ext4_inode *inode,
222                 struct block_allocation *alloc, u32 *block_len)
223 {
224         int len = min(*block_len, aux_info.blocks_per_dind);
225 
226         int dind_block = get_oob_block(alloc, 0);
227         inode->i_block[EXT4_DIND_BLOCK] = dind_block;
228 
229         if (advance_oob_blocks(alloc, 1)) {
230                 error("failed to advance oob block");
231                 return -1;
232         }
233 
234         u32 *dind_block_data = calloc(info.block_size, 1);
235         sparse_file_add_data(ext4_sparse_file, dind_block_data, info.block_size,
236                         dind_block);
237 
238         fill_dindirect_block(dind_block_data, len, alloc);
239 
240         if (advance_blocks(alloc, len)) {
241                 error("failed to advance %d blocks", len);
242                 return -1;
243         }
244 
245         *block_len -= len;
246         return 0;
247 }
248 
249 /* Given an allocation, attach as many blocks as possible to triply indirect
250    blocks, and return the rest.
251    Assumes that the blocks necessary to hold the indirect, doubly indirect and
252    triply indirect blocks were included as part of the allocation */
253 static int inode_attach_tindirect_blocks(struct ext4_inode *inode,
254                 struct block_allocation *alloc, u32 *block_len)
255 {
256         int len = min(*block_len, aux_info.blocks_per_tind);
257 
258         int tind_block = get_oob_block(alloc, 0);
259         inode->i_block[EXT4_TIND_BLOCK] = tind_block;
260 
261         if (advance_oob_blocks(alloc, 1)) {
262                 error("failed to advance oob block");
263                 return -1;
264         }
265 
266         u32 *tind_block_data = calloc(info.block_size, 1);
267         sparse_file_add_data(ext4_sparse_file, tind_block_data, info.block_size,
268                         tind_block);
269 
270         fill_tindirect_block(tind_block_data, len, alloc);
271 
272         if (advance_blocks(alloc, len)) {
273                 error("failed to advance %d blocks", len);
274                 return -1;
275         }
276 
277         *block_len -= len;
278         return 0;
279 }
280 
281 static void reserve_all_indirect_blocks(struct block_allocation *alloc, u32 len)
282 {
283         if (len <= EXT4_NDIR_BLOCKS)
284                 return;
285 
286         len -= EXT4_NDIR_BLOCKS;
287         advance_blocks(alloc, EXT4_NDIR_BLOCKS);
288 
289         u32 ind_block_len = min(aux_info.blocks_per_ind, len);
290         reserve_indirect_block(alloc, ind_block_len);
291 
292         len -= ind_block_len;
293         if (len == 0)
294                 return;
295 
296         u32 dind_block_len = min(aux_info.blocks_per_dind, len);
297         reserve_dindirect_block(alloc, dind_block_len);
298 
299         len -= dind_block_len;
300         if (len == 0)
301                 return;
302 
303         u32 tind_block_len = min(aux_info.blocks_per_tind, len);
304         reserve_tindirect_block(alloc, tind_block_len);
305 
306         len -= tind_block_len;
307         if (len == 0)
308                 return;
309 
310         error("%d blocks remaining", len);
311 }
312 
313 static u32 indirect_blocks_needed(u32 len)
314 {
315         u32 ind = 0;
316 
317         if (len <= EXT4_NDIR_BLOCKS)
318                 return ind;
319 
320         len -= EXT4_NDIR_BLOCKS;
321 
322         /* We will need an indirect block for the rest of the blocks */
323         ind += DIV_ROUND_UP(len, aux_info.blocks_per_ind);
324 
325         if (len <= aux_info.blocks_per_ind)
326                 return ind;
327 
328         len -= aux_info.blocks_per_ind;
329 
330         ind += DIV_ROUND_UP(len, aux_info.blocks_per_dind);
331 
332         if (len <= aux_info.blocks_per_dind)
333                 return ind;
334 
335         len -= aux_info.blocks_per_dind;
336 
337         ind += DIV_ROUND_UP(len, aux_info.blocks_per_tind);
338 
339         if (len <= aux_info.blocks_per_tind)
340                 return ind;
341 
342         critical_error("request too large");
343         return 0;
344 }
345 
346 static int do_inode_attach_indirect(struct ext4_inode *inode,
347                 struct block_allocation *alloc, u32 block_len)
348 {
349         u32 count = block_len;
350 
351         if (inode_attach_direct_blocks(inode, alloc, &count)) {
352                 error("failed to attach direct blocks to inode");
353                 return -1;
354         }
355 
356         if (count > 0) {
357                 if (inode_attach_indirect_blocks(inode, alloc, &count)) {
358                         error("failed to attach indirect blocks to inode");
359                         return -1;
360                 }
361         }
362 
363         if (count > 0) {
364                 if (inode_attach_dindirect_blocks(inode, alloc, &count)) {
365                         error("failed to attach dindirect blocks to inode");
366                         return -1;
367                 }
368         }
369 
370         if (count > 0) {
371                 if (inode_attach_tindirect_blocks(inode, alloc, &count)) {
372                         error("failed to attach tindirect blocks to inode");
373                         return -1;
374                 }
375         }
376 
377         if (count) {
378                 error("blocks left after triply-indirect allocation");
379                 return -1;
380         }
381 
382         rewind_alloc(alloc);
383 
384         return 0;
385 }
386 
387 static struct block_allocation *do_inode_allocate_indirect(
388                 u32 block_len)
389 {
390         u32 indirect_len = indirect_blocks_needed(block_len);
391 
392         struct block_allocation *alloc = allocate_blocks(block_len + indirect_len);
393 
394         if (alloc == NULL) {
395                 error("Failed to allocate %d blocks", block_len + indirect_len);
396                 return NULL;
397         }
398 
399         return alloc;
400 }
401 
402 /* Allocates enough blocks to hold len bytes and connects them to an inode */
403 void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len)
404 {
405         struct block_allocation *alloc;
406         u32 block_len = DIV_ROUND_UP(len, info.block_size);
407         u32 indirect_len = indirect_blocks_needed(block_len);
408 
409         alloc = do_inode_allocate_indirect(block_len);
410         if (alloc == NULL) {
411                 error("failed to allocate extents for %lu bytes", len);
412                 return;
413         }
414 
415         reserve_all_indirect_blocks(alloc, block_len);
416         rewind_alloc(alloc);
417 
418         if (do_inode_attach_indirect(inode, alloc, block_len))
419                 error("failed to attach blocks to indirect inode");
420 
421         inode->i_flags = 0;
422         inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512;
423         inode->i_size_lo = len;
424 
425         free_alloc(alloc);
426 }
427 
428 void inode_attach_resize(struct ext4_inode *inode,
429                 struct block_allocation *alloc)
430 {
431         u32 block_len = block_allocation_len(alloc);
432         u32 superblocks = block_len / info.bg_desc_reserve_blocks;
433         u32 i, j;
434         u64 blocks;
435         u64 size;
436 
437         if (block_len % info.bg_desc_reserve_blocks)
438                 critical_error("reserved blocks not a multiple of %d",
439                                 info.bg_desc_reserve_blocks);
440 
441         append_oob_allocation(alloc, 1);
442         u32 dind_block = get_oob_block(alloc, 0);
443 
444         u32 *dind_block_data = calloc(info.block_size, 1);
445         if (!dind_block_data)
446                 critical_error_errno("calloc");
447         sparse_file_add_data(ext4_sparse_file, dind_block_data, info.block_size,
448                         dind_block);
449 
450         u32 *ind_block_data = calloc(info.block_size, info.bg_desc_reserve_blocks);
451         if (!ind_block_data)
452                 critical_error_errno("calloc");
453         sparse_file_add_data(ext4_sparse_file, ind_block_data,
454                         info.block_size * info.bg_desc_reserve_blocks,
455                         get_block(alloc, 0));
456 
457         for (i = 0; i < info.bg_desc_reserve_blocks; i++) {
458                 int r = (i - aux_info.bg_desc_blocks) % info.bg_desc_reserve_blocks;
459                 if (r < 0)
460                         r += info.bg_desc_reserve_blocks;
461 
462                 dind_block_data[i] = get_block(alloc, r);
463 
464                 for (j = 1; j < superblocks; j++) {
465                         u32 b = j * info.bg_desc_reserve_blocks + r;
466                         ind_block_data[r * aux_info.blocks_per_ind + j - 1] = get_block(alloc, b);
467                 }
468         }
469 
470         u32 last_block = EXT4_NDIR_BLOCKS + aux_info.blocks_per_ind +
471                         aux_info.blocks_per_ind * (info.bg_desc_reserve_blocks - 1) +
472                         superblocks - 2;
473 
474         blocks = ((u64)block_len + 1) * info.block_size / 512;
475         size = (u64)last_block * info.block_size;
476 
477         inode->i_block[EXT4_DIND_BLOCK] = dind_block;
478         inode->i_flags = 0;
479         inode->i_blocks_lo = blocks;
480         inode->osd2.linux2.l_i_blocks_high = blocks >> 32;
481         inode->i_size_lo = size;
482         inode->i_size_high = size >> 32;
483 }
484 
485 /* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
486    buffer, and connects them to an inode.  Returns a pointer to the data
487    buffer. */
488 u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
489                 unsigned long backing_len)
490 {
491         struct block_allocation *alloc;
492         u32 block_len = DIV_ROUND_UP(len, info.block_size);
493         u8 *data = NULL;
494 
495         alloc = do_inode_allocate_indirect(block_len);
496         if (alloc == NULL) {
497                 error("failed to allocate extents for %lu bytes", len);
498                 return NULL;
499         }
500 
501         if (backing_len) {
502                 data = create_backing(alloc, backing_len);
503                 if (!data)
504                         error("failed to create backing for %lu bytes", backing_len);
505         }
506 
507         rewind_alloc(alloc);
508         if (do_inode_attach_indirect(inode, alloc, block_len))
509                 error("failed to attach blocks to indirect inode");
510 
511         free_alloc(alloc);
512 
513         return data;
514 }
515 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt