blob: cd826ac1b29a8c595da733aeeb40acf38e703aa8 [file] [log] [blame]
Colin Crossec0a2e82010-06-11 14:21:37 -07001/*
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
Colin Crossec0a2e82010-06-11 14:21:37 -070017#include "ext4_utils.h"
18#include "ext4.h"
19#include "ext4_extents.h"
Colin Crossec0a2e82010-06-11 14:21:37 -070020#include "indirect.h"
21#include "allocate.h"
22
Colin Crossdc5abee2012-04-23 23:20:48 -070023#include <sparse/sparse.h>
24
Colin Cross33f96c62010-12-22 18:40:14 -080025#include <stdlib.h>
26#include <stdio.h>
27
Colin Crossec0a2e82010-06-11 14:21:37 -070028/* Creates data buffers for the first backing_len bytes of a block allocation
29 and queues them to be written */
30static u8 *create_backing(struct block_allocation *alloc,
31 unsigned long backing_len)
32{
33 if (DIV_ROUND_UP(backing_len, info.block_size) > EXT4_NDIR_BLOCKS)
34 critical_error("indirect backing larger than %d blocks", EXT4_NDIR_BLOCKS);
35
36 u8 *data = calloc(backing_len, 1);
37 if (!data)
38 critical_error_errno("calloc");
39
40 u8 *ptr = data;
41 for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
42 u32 region_block;
43 u32 region_len;
44 u32 len;
45 get_region(alloc, &region_block, &region_len);
46
47 len = min(region_len * info.block_size, backing_len);
48
Colin Crossf0ee37f2012-04-24 17:48:43 -070049 sparse_file_add_data(info.sparse_file, ptr, len, region_block);
Colin Crossec0a2e82010-06-11 14:21:37 -070050 ptr += len;
51 backing_len -= len;
52 }
53
54 return data;
55}
56
57static void reserve_indirect_block(struct block_allocation *alloc, int len)
58{
59 if (reserve_oob_blocks(alloc, 1)) {
Colin Cross8aef66d2010-06-20 23:22:12 -070060 error("failed to reserve oob block");
61 return;
62 }
Colin Crossec0a2e82010-06-11 14:21:37 -070063
64 if (advance_blocks(alloc, len)) {
65 error("failed to advance %d blocks", len);
66 return;
67 }
68}
69
70static void reserve_dindirect_block(struct block_allocation *alloc, int len)
71{
72 if (reserve_oob_blocks(alloc, 1)) {
Colin Cross8aef66d2010-06-20 23:22:12 -070073 error("failed to reserve oob block");
74 return;
75 }
Colin Crossec0a2e82010-06-11 14:21:37 -070076
77 while (len > 0) {
78 int ind_block_len = min((int)aux_info.blocks_per_ind, len);
79
80 reserve_indirect_block(alloc, ind_block_len);
81
82 len -= ind_block_len;
83 }
84
85}
86
87static void reserve_tindirect_block(struct block_allocation *alloc, int len)
88{
89 if (reserve_oob_blocks(alloc, 1)) {
Colin Cross8aef66d2010-06-20 23:22:12 -070090 error("failed to reserve oob block");
91 return;
92 }
Colin Crossec0a2e82010-06-11 14:21:37 -070093
94 while (len > 0) {
95 int dind_block_len = min((int)aux_info.blocks_per_dind, len);
96
97 reserve_dindirect_block(alloc, dind_block_len);
98
99 len -= dind_block_len;
100 }
101}
102
103static void fill_indirect_block(u32 *ind_block, int len, struct block_allocation *alloc)
104{
105 int i;
106 for (i = 0; i < len; i++) {
107 ind_block[i] = get_block(alloc, i);
108 }
109}
110
111static void fill_dindirect_block(u32 *dind_block, int len, struct block_allocation *alloc)
112{
113 int i;
114 u32 ind_block;
115
116 for (i = 0; len > 0; i++) {
117 ind_block = get_oob_block(alloc, 0);
118 if (advance_oob_blocks(alloc, 1)) {
Colin Cross8aef66d2010-06-20 23:22:12 -0700119 error("failed to reserve oob block");
120 return;
121 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700122
123 dind_block[i] = ind_block;
124
125 u32 *ind_block_data = calloc(info.block_size, 1);
Colin Crossf0ee37f2012-04-24 17:48:43 -0700126 sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size,
127 ind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700128 int ind_block_len = min((int)aux_info.blocks_per_ind, len);
129
130 fill_indirect_block(ind_block_data, ind_block_len, alloc);
131
132 if (advance_blocks(alloc, ind_block_len)) {
133 error("failed to advance %d blocks", ind_block_len);
134 return;
135 }
136
137 len -= ind_block_len;
138 }
139}
140
141static void fill_tindirect_block(u32 *tind_block, int len, struct block_allocation *alloc)
142{
143 int i;
144 u32 dind_block;
145
146 for (i = 0; len > 0; i++) {
147 dind_block = get_oob_block(alloc, 0);
148 if (advance_oob_blocks(alloc, 1)) {
Colin Cross8aef66d2010-06-20 23:22:12 -0700149 error("failed to reserve oob block");
150 return;
151 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700152
Colin Cross8aef66d2010-06-20 23:22:12 -0700153 tind_block[i] = dind_block;
Colin Crossec0a2e82010-06-11 14:21:37 -0700154
155 u32 *dind_block_data = calloc(info.block_size, 1);
Colin Crossf0ee37f2012-04-24 17:48:43 -0700156 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
157 dind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700158 int dind_block_len = min((int)aux_info.blocks_per_dind, len);
159
160 fill_dindirect_block(dind_block_data, dind_block_len, alloc);
161
Colin Cross8aef66d2010-06-20 23:22:12 -0700162 len -= dind_block_len;
Colin Crossec0a2e82010-06-11 14:21:37 -0700163 }
164}
165
166/* Given an allocation, attach as many blocks as possible to direct inode
167 blocks, and return the rest */
168static int inode_attach_direct_blocks(struct ext4_inode *inode,
169 struct block_allocation *alloc, u32 *block_len)
170{
171 int len = min(*block_len, EXT4_NDIR_BLOCKS);
172 int i;
173
174 for (i = 0; i < len; i++) {
175 inode->i_block[i] = get_block(alloc, i);
176 }
177
Colin Cross8aef66d2010-06-20 23:22:12 -0700178 if (advance_blocks(alloc, len)) {
179 error("failed to advance %d blocks", len);
180 return -1;
181 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700182
Colin Cross8aef66d2010-06-20 23:22:12 -0700183 *block_len -= len;
184 return 0;
Colin Crossec0a2e82010-06-11 14:21:37 -0700185}
186
187/* Given an allocation, attach as many blocks as possible to indirect blocks,
188 and return the rest
189 Assumes that the blocks necessary to hold the indirect blocks were included
190 as part of the allocation */
191static int inode_attach_indirect_blocks(struct ext4_inode *inode,
192 struct block_allocation *alloc, u32 *block_len)
193{
194 int len = min(*block_len, aux_info.blocks_per_ind);
195
196 int ind_block = get_oob_block(alloc, 0);
197 inode->i_block[EXT4_IND_BLOCK] = ind_block;
198
Colin Cross8aef66d2010-06-20 23:22:12 -0700199 if (advance_oob_blocks(alloc, 1)) {
200 error("failed to advance oob block");
201 return -1;
202 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700203
204 u32 *ind_block_data = calloc(info.block_size, 1);
Colin Crossf0ee37f2012-04-24 17:48:43 -0700205 sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size,
206 ind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700207
208 fill_indirect_block(ind_block_data, len, alloc);
209
Colin Cross8aef66d2010-06-20 23:22:12 -0700210 if (advance_blocks(alloc, len)) {
211 error("failed to advance %d blocks", len);
212 return -1;
213 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700214
Colin Cross8aef66d2010-06-20 23:22:12 -0700215 *block_len -= len;
216 return 0;
Colin Crossec0a2e82010-06-11 14:21:37 -0700217}
218
219/* Given an allocation, attach as many blocks as possible to doubly indirect
220 blocks, and return the rest.
221 Assumes that the blocks necessary to hold the indirect and doubly indirect
222 blocks were included as part of the allocation */
223static int inode_attach_dindirect_blocks(struct ext4_inode *inode,
224 struct block_allocation *alloc, u32 *block_len)
225{
226 int len = min(*block_len, aux_info.blocks_per_dind);
227
228 int dind_block = get_oob_block(alloc, 0);
229 inode->i_block[EXT4_DIND_BLOCK] = dind_block;
230
Colin Cross8aef66d2010-06-20 23:22:12 -0700231 if (advance_oob_blocks(alloc, 1)) {
232 error("failed to advance oob block");
233 return -1;
234 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700235
236 u32 *dind_block_data = calloc(info.block_size, 1);
Colin Crossf0ee37f2012-04-24 17:48:43 -0700237 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
238 dind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700239
240 fill_dindirect_block(dind_block_data, len, alloc);
241
Colin Cross8aef66d2010-06-20 23:22:12 -0700242 if (advance_blocks(alloc, len)) {
243 error("failed to advance %d blocks", len);
244 return -1;
245 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700246
Colin Cross8aef66d2010-06-20 23:22:12 -0700247 *block_len -= len;
Colin Crossec0a2e82010-06-11 14:21:37 -0700248 return 0;
249}
250
251/* Given an allocation, attach as many blocks as possible to triply indirect
252 blocks, and return the rest.
253 Assumes that the blocks necessary to hold the indirect, doubly indirect and
254 triply indirect blocks were included as part of the allocation */
255static int inode_attach_tindirect_blocks(struct ext4_inode *inode,
256 struct block_allocation *alloc, u32 *block_len)
257{
258 int len = min(*block_len, aux_info.blocks_per_tind);
259
260 int tind_block = get_oob_block(alloc, 0);
261 inode->i_block[EXT4_TIND_BLOCK] = tind_block;
262
Colin Cross8aef66d2010-06-20 23:22:12 -0700263 if (advance_oob_blocks(alloc, 1)) {
264 error("failed to advance oob block");
265 return -1;
266 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700267
268 u32 *tind_block_data = calloc(info.block_size, 1);
Colin Crossf0ee37f2012-04-24 17:48:43 -0700269 sparse_file_add_data(info.sparse_file, tind_block_data, info.block_size,
270 tind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700271
272 fill_tindirect_block(tind_block_data, len, alloc);
273
Colin Cross8aef66d2010-06-20 23:22:12 -0700274 if (advance_blocks(alloc, len)) {
275 error("failed to advance %d blocks", len);
276 return -1;
277 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700278
Colin Cross8aef66d2010-06-20 23:22:12 -0700279 *block_len -= len;
280 return 0;
Colin Crossec0a2e82010-06-11 14:21:37 -0700281}
282
283static void reserve_all_indirect_blocks(struct block_allocation *alloc, u32 len)
284{
285 if (len <= EXT4_NDIR_BLOCKS)
286 return;
287
288 len -= EXT4_NDIR_BLOCKS;
289 advance_blocks(alloc, EXT4_NDIR_BLOCKS);
290
291 u32 ind_block_len = min(aux_info.blocks_per_ind, len);
292 reserve_indirect_block(alloc, ind_block_len);
293
294 len -= ind_block_len;
295 if (len == 0)
296 return;
297
298 u32 dind_block_len = min(aux_info.blocks_per_dind, len);
299 reserve_dindirect_block(alloc, dind_block_len);
300
301 len -= dind_block_len;
302 if (len == 0)
303 return;
304
305 u32 tind_block_len = min(aux_info.blocks_per_tind, len);
306 reserve_tindirect_block(alloc, tind_block_len);
307
308 len -= tind_block_len;
309 if (len == 0)
310 return;
311
312 error("%d blocks remaining", len);
313}
314
315static u32 indirect_blocks_needed(u32 len)
316{
317 u32 ind = 0;
318
319 if (len <= EXT4_NDIR_BLOCKS)
320 return ind;
321
322 len -= EXT4_NDIR_BLOCKS;
323
324 /* We will need an indirect block for the rest of the blocks */
325 ind += DIV_ROUND_UP(len, aux_info.blocks_per_ind);
326
327 if (len <= aux_info.blocks_per_ind)
328 return ind;
329
330 len -= aux_info.blocks_per_ind;
331
332 ind += DIV_ROUND_UP(len, aux_info.blocks_per_dind);
333
334 if (len <= aux_info.blocks_per_dind)
335 return ind;
336
337 len -= aux_info.blocks_per_dind;
338
339 ind += DIV_ROUND_UP(len, aux_info.blocks_per_tind);
340
341 if (len <= aux_info.blocks_per_tind)
342 return ind;
343
344 critical_error("request too large");
345 return 0;
346}
347
348static int do_inode_attach_indirect(struct ext4_inode *inode,
349 struct block_allocation *alloc, u32 block_len)
350{
351 u32 count = block_len;
352
353 if (inode_attach_direct_blocks(inode, alloc, &count)) {
354 error("failed to attach direct blocks to inode");
355 return -1;
356 }
357
358 if (count > 0) {
Colin Cross8aef66d2010-06-20 23:22:12 -0700359 if (inode_attach_indirect_blocks(inode, alloc, &count)) {
360 error("failed to attach indirect blocks to inode");
361 return -1;
362 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700363 }
364
Colin Cross8aef66d2010-06-20 23:22:12 -0700365 if (count > 0) {
366 if (inode_attach_dindirect_blocks(inode, alloc, &count)) {
367 error("failed to attach dindirect blocks to inode");
368 return -1;
369 }
370 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700371
Colin Cross8aef66d2010-06-20 23:22:12 -0700372 if (count > 0) {
373 if (inode_attach_tindirect_blocks(inode, alloc, &count)) {
374 error("failed to attach tindirect blocks to inode");
375 return -1;
376 }
377 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700378
Colin Cross8aef66d2010-06-20 23:22:12 -0700379 if (count) {
Colin Crossec0a2e82010-06-11 14:21:37 -0700380 error("blocks left after triply-indirect allocation");
381 return -1;
Colin Cross8aef66d2010-06-20 23:22:12 -0700382 }
Colin Crossec0a2e82010-06-11 14:21:37 -0700383
384 rewind_alloc(alloc);
385
386 return 0;
387}
388
389static struct block_allocation *do_inode_allocate_indirect(
Nick Kralevich5446bde2013-02-19 19:05:47 -0800390 u32 block_len)
Colin Crossec0a2e82010-06-11 14:21:37 -0700391{
392 u32 indirect_len = indirect_blocks_needed(block_len);
393
394 struct block_allocation *alloc = allocate_blocks(block_len + indirect_len);
395
396 if (alloc == NULL) {
397 error("Failed to allocate %d blocks", block_len + indirect_len);
398 return NULL;
399 }
400
401 return alloc;
402}
403
404/* Allocates enough blocks to hold len bytes and connects them to an inode */
405void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len)
406{
407 struct block_allocation *alloc;
408 u32 block_len = DIV_ROUND_UP(len, info.block_size);
409 u32 indirect_len = indirect_blocks_needed(block_len);
410
Nick Kralevich5446bde2013-02-19 19:05:47 -0800411 alloc = do_inode_allocate_indirect(block_len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700412 if (alloc == NULL) {
413 error("failed to allocate extents for %lu bytes", len);
414 return;
415 }
416
417 reserve_all_indirect_blocks(alloc, block_len);
418 rewind_alloc(alloc);
419
420 if (do_inode_attach_indirect(inode, alloc, block_len))
421 error("failed to attach blocks to indirect inode");
422
423 inode->i_flags = 0;
424 inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512;
425 inode->i_size_lo = len;
426
427 free_alloc(alloc);
428}
429
430void inode_attach_resize(struct ext4_inode *inode,
431 struct block_allocation *alloc)
432{
433 u32 block_len = block_allocation_len(alloc);
Colin Cross22742ce2010-12-22 16:01:52 -0800434 u32 superblocks = block_len / info.bg_desc_reserve_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700435 u32 i, j;
436 u64 blocks;
437 u64 size;
438
Colin Cross22742ce2010-12-22 16:01:52 -0800439 if (block_len % info.bg_desc_reserve_blocks)
Colin Crossec0a2e82010-06-11 14:21:37 -0700440 critical_error("reserved blocks not a multiple of %d",
Colin Cross22742ce2010-12-22 16:01:52 -0800441 info.bg_desc_reserve_blocks);
Colin Crossec0a2e82010-06-11 14:21:37 -0700442
443 append_oob_allocation(alloc, 1);
444 u32 dind_block = get_oob_block(alloc, 0);
445
446 u32 *dind_block_data = calloc(info.block_size, 1);
447 if (!dind_block_data)
448 critical_error_errno("calloc");
Colin Crossf0ee37f2012-04-24 17:48:43 -0700449 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
450 dind_block);
Colin Crossec0a2e82010-06-11 14:21:37 -0700451
Colin Cross22742ce2010-12-22 16:01:52 -0800452 u32 *ind_block_data = calloc(info.block_size, info.bg_desc_reserve_blocks);
Colin Crossec0a2e82010-06-11 14:21:37 -0700453 if (!ind_block_data)
454 critical_error_errno("calloc");
Colin Crossf0ee37f2012-04-24 17:48:43 -0700455 sparse_file_add_data(info.sparse_file, ind_block_data,
Colin Cross22742ce2010-12-22 16:01:52 -0800456 info.block_size * info.bg_desc_reserve_blocks,
Colin Crossec0a2e82010-06-11 14:21:37 -0700457 get_block(alloc, 0));
458
Colin Cross22742ce2010-12-22 16:01:52 -0800459 for (i = 0; i < info.bg_desc_reserve_blocks; i++) {
460 int r = (i - aux_info.bg_desc_blocks) % info.bg_desc_reserve_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700461 if (r < 0)
Colin Cross22742ce2010-12-22 16:01:52 -0800462 r += info.bg_desc_reserve_blocks;
Colin Crossec0a2e82010-06-11 14:21:37 -0700463
464 dind_block_data[i] = get_block(alloc, r);
465
466 for (j = 1; j < superblocks; j++) {
Colin Cross22742ce2010-12-22 16:01:52 -0800467 u32 b = j * info.bg_desc_reserve_blocks + r;
Colin Crossec0a2e82010-06-11 14:21:37 -0700468 ind_block_data[r * aux_info.blocks_per_ind + j - 1] = get_block(alloc, b);
469 }
470 }
471
472 u32 last_block = EXT4_NDIR_BLOCKS + aux_info.blocks_per_ind +
Colin Cross22742ce2010-12-22 16:01:52 -0800473 aux_info.blocks_per_ind * (info.bg_desc_reserve_blocks - 1) +
Colin Crossec0a2e82010-06-11 14:21:37 -0700474 superblocks - 2;
475
476 blocks = ((u64)block_len + 1) * info.block_size / 512;
477 size = (u64)last_block * info.block_size;
478
479 inode->i_block[EXT4_DIND_BLOCK] = dind_block;
480 inode->i_flags = 0;
481 inode->i_blocks_lo = blocks;
482 inode->osd2.linux2.l_i_blocks_high = blocks >> 32;
483 inode->i_size_lo = size;
484 inode->i_size_high = size >> 32;
485}
486
487/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
488 buffer, and connects them to an inode. Returns a pointer to the data
489 buffer. */
490u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
491 unsigned long backing_len)
492{
493 struct block_allocation *alloc;
Johan Rudholm82c18e02012-02-06 15:17:04 +0100494 u32 block_len = DIV_ROUND_UP(len, info.block_size);
Colin Crossec0a2e82010-06-11 14:21:37 -0700495 u8 *data = NULL;
496
Nick Kralevich5446bde2013-02-19 19:05:47 -0800497 alloc = do_inode_allocate_indirect(block_len);
Colin Crossec0a2e82010-06-11 14:21:37 -0700498 if (alloc == NULL) {
499 error("failed to allocate extents for %lu bytes", len);
500 return NULL;
501 }
502
503 if (backing_len) {
504 data = create_backing(alloc, backing_len);
505 if (!data)
506 error("failed to create backing for %lu bytes", backing_len);
507 }
508
Johan Rudholm82c18e02012-02-06 15:17:04 +0100509 rewind_alloc(alloc);
510 if (do_inode_attach_indirect(inode, alloc, block_len))
511 error("failed to attach blocks to indirect inode");
512
Colin Crossec0a2e82010-06-11 14:21:37 -0700513 free_alloc(alloc);
514
515 return data;
516}