source: mainline/uspace/lib/ext4/src/filesystem.c@ 4bfad34

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4bfad34 was 4bfad34, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Move parts of ext4 filesystem initialization.

  • Property mode set to 100644
File size: 42.2 KB
RevLine 
[6c501f8]1/*
[d1538a1]2 * Copyright (c) 2011 Martin Sucha
[f22d5ef0]3 * Copyright (c) 2012 Frantisek Princ
[6c501f8]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libext4
31 * @{
[38542dc]32 */
[6c501f8]33/**
[4bfad34]34 * @file filesystem.c
[38542dc]35 * @brief More complex filesystem operations.
[6c501f8]36 */
37
[9b9d37bb]38#include <byteorder.h>
[6c501f8]39#include <errno.h>
[fcb0d76]40#include <mem.h>
[447201e]41#include <align.h>
[2b5d966]42#include <crypto.h>
[fcb0d76]43#include <ipc/vfs.h>
[4bfad34]44#include <libfs.h>
[fcb0d76]45#include <stdlib.h>
46#include "ext4/balloc.h"
47#include "ext4/bitmap.h"
48#include "ext4/block_group.h"
49#include "ext4/extent.h"
50#include "ext4/filesystem.h"
51#include "ext4/ialloc.h"
52#include "ext4/inode.h"
[4bfad34]53#include "ext4/ops.h"
[fcb0d76]54#include "ext4/superblock.h"
[6c501f8]55
[4bfad34]56static int ext4_filesystem_check_features(ext4_filesystem_t *, bool *);
57
[5b26747]58/** Initialize filesystem and read all needed data.
[9fc72fb3]59 *
[38542dc]60 * @param fs Filesystem instance to be initialized
61 * @param service_id Identifier if device with the filesystem
[4bfad34]62 * @param cmode Cache mode
63 * @param read_only Place to store read only flag
64 * @param index Output value - index of root node
65 * @param size Output value - size of root node
66 * @param lnkcnt Output value - link count of root node
[38542dc]67 *
68 * @return Error code
69 *
[9fc72fb3]70 */
[4bfad34]71int ext4_filesystem_init(ext4_filesystem_t *fs, ext4fs_instance_t *inst,
72 service_id_t service_id, enum cache_mode cmode, aoff64_t *size)
[6c501f8]73{
[2b5d966]74 int rc;
[eb94d84]75 ext4_superblock_t *temp_superblock = NULL;
76
[01ab41b]77 fs->device = service_id;
[eb94d84]78
[06d85e5]79 /* Initialize block library (4096 is size of communication channel) */
[fc22069]80 rc = block_init(fs->device, 4096);
[38542dc]81 if (rc != EOK)
[eb94d84]82 goto err;
83
[06d85e5]84 /* Read superblock from device to memory */
[01ab41b]85 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
[eb94d84]86 if (rc != EOK)
87 goto err_1;
88
[06d85e5]89 /* Read block size from superblock and check */
[d9bbe45]90 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
[01ab41b]91 if (block_size > EXT4_MAX_BLOCK_SIZE) {
[eb94d84]92 rc = ENOTSUP;
93 goto err_1;
[01ab41b]94 }
[eb94d84]95
[06d85e5]96 /* Initialize block caching by libblock */
[0b293a6]97 rc = block_cache_init(service_id, block_size, 0, cmode);
[eb94d84]98 if (rc != EOK)
99 goto err_1;
100
[06d85e5]101 /* Compute limits for indirect block levels */
[d9bbe45]102 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
[a9a0982]103 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
104 fs->inode_blocks_per_level[0] = 1;
[38542dc]105 for (unsigned int i = 1; i < 4; i++) {
106 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i - 1] *
[a9a0982]107 block_ids_per_block;
[38542dc]108 fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
109 fs->inode_blocks_per_level[i];
[a9a0982]110 }
[eb94d84]111
[06d85e5]112 /* Return loaded superblock */
[01ab41b]113 fs->superblock = temp_superblock;
[eb94d84]114
[fb04cd90]115 uint16_t state = ext4_superblock_get_state(fs->superblock);
[eb94d84]116
[c3fe001]117 if (((state & EXT4_SUPERBLOCK_STATE_VALID_FS) !=
118 EXT4_SUPERBLOCK_STATE_VALID_FS) ||
119 ((state & EXT4_SUPERBLOCK_STATE_ERROR_FS) ==
120 EXT4_SUPERBLOCK_STATE_ERROR_FS)) {
[eb94d84]121 rc = ENOTSUP;
122 goto err_2;
[fb04cd90]123 }
[f066a87]124
125 rc = ext4_superblock_check_sanity(fs->superblock);
126 if (rc != EOK)
127 goto err_2;
[eb94d84]128
[4bfad34]129 /* Check flags */
130 bool read_only;
131 rc = ext4_filesystem_check_features(fs, &read_only);
132 if (rc != EOK)
133 goto err_2;
134
135 /* Read root node */
136 fs_node_t *root_node;
137 rc = ext4fs_node_get_core(&root_node, inst, EXT4_INODE_ROOT_INDEX);
138 if (rc != EOK)
139 goto err_2;
140
[06d85e5]141 /* Mark system as mounted */
[fb04cd90]142 ext4_superblock_set_state(fs->superblock, EXT4_SUPERBLOCK_STATE_ERROR_FS);
143 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
[eb94d84]144 if (rc != EOK)
[4bfad34]145 goto err_3;
[eb94d84]146
[4cdac68]147 uint16_t mnt_count = ext4_superblock_get_mount_count(fs->superblock);
148 ext4_superblock_set_mount_count(fs->superblock, mnt_count + 1);
[eb94d84]149
[4bfad34]150 ext4fs_node_t *enode = EXT4FS_NODE(root_node);
151
152 *size = ext4_inode_get_size(fs->superblock, enode->inode_ref->inode);
[eb94d84]153
[4bfad34]154 ext4fs_node_put(root_node);
155 return EOK;
156err_3:
157 ext4fs_node_put(root_node);
[eb94d84]158err_2:
159 block_cache_fini(fs->device);
160err_1:
161 block_fini(fs->device);
162err:
163 if (temp_superblock)
164 ext4_superblock_release(temp_superblock);
165 return rc;
[6c501f8]166}
167
[5b26747]168/** Destroy filesystem instance (used by unmount operation).
[9fc72fb3]169 *
[38542dc]170 * @param fs Filesystem to be destroyed
171 *
172 * @return Error code
173 *
[9fc72fb3]174 */
[fb04cd90]175int ext4_filesystem_fini(ext4_filesystem_t *fs)
[3711e7e]176{
[06d85e5]177 /* Write the superblock to the device */
[fb04cd90]178 ext4_superblock_set_state(fs->superblock, EXT4_SUPERBLOCK_STATE_VALID_FS);
[38542dc]179 int rc = ext4_superblock_write_direct(fs->device, fs->superblock);
180
[06d85e5]181 /* Release memory space for superblock */
[3711e7e]182 free(fs->superblock);
[2b5d966]183
[06d85e5]184 /* Finish work with block library */
[0b293a6]185 block_cache_fini(fs->device);
[3711e7e]186 block_fini(fs->device);
[38542dc]187
[ae3d4f8]188 return rc;
[3711e7e]189}
190
[5b26747]191/** Check filesystem's features, if supported by this driver
[9fc72fb3]192 *
[5b26747]193 * Function can return EOK and set read_only flag. It mean's that
194 * there are some not-supported features, that can cause problems
195 * during some write operations.
196 *
[38542dc]197 * @param fs Filesystem to be checked
[4bfad34]198 * @param read_only Place to write flag saying whether filesystem
199 * should be mounted only for reading
[38542dc]200 *
201 * @return Error code
202 *
[9fc72fb3]203 */
[4bfad34]204static int ext4_filesystem_check_features(ext4_filesystem_t *fs,
205 bool *read_only)
[6c501f8]206{
[06d85e5]207 /* Feature flags are present only in higher revisions */
[9c0c0e1]208 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
[5b26747]209 *read_only = false;
[9c0c0e1]210 return EOK;
211 }
[38542dc]212
213 /*
214 * Check incompatible features - if filesystem has some,
[06d85e5]215 * volume can't be mounted
216 */
[9c0c0e1]217 uint32_t incompatible_features;
[38542dc]218 incompatible_features =
219 ext4_superblock_get_features_incompatible(fs->superblock);
[9c0c0e1]220 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
[38542dc]221 if (incompatible_features > 0)
[9c0c0e1]222 return ENOTSUP;
[38542dc]223
224 /*
225 * Check read-only features, if filesystem has some,
[06d85e5]226 * volume can be mount only in read-only mode
227 */
[9c0c0e1]228 uint32_t compatible_read_only;
[38542dc]229 compatible_read_only =
230 ext4_superblock_get_features_read_only(fs->superblock);
[9c0c0e1]231 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
232 if (compatible_read_only > 0) {
[5b26747]233 *read_only = true;
234 return EOK;
[9c0c0e1]235 }
[38542dc]236
[6c501f8]237 return EOK;
238}
239
[4cdac68]240
241/** Convert block address to relative index in block group.
242 *
[38542dc]243 * @param sb Superblock pointer
244 * @param block_addr Block number to convert
245 *
246 * @return Relative number of block
247 *
[4cdac68]248 */
249uint32_t ext4_filesystem_blockaddr2_index_in_group(ext4_superblock_t *sb,
[38542dc]250 uint32_t block_addr)
[4cdac68]251{
252 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb);
253 uint32_t first_block = ext4_superblock_get_first_data_block(sb);
[38542dc]254
[4cdac68]255 /* First block == 0 or 1 */
[38542dc]256 if (first_block == 0)
[4cdac68]257 return block_addr % blocks_per_group;
[38542dc]258 else
[4cdac68]259 return (block_addr - 1) % blocks_per_group;
260}
261
262
263/** Convert relative block address in group to absolute address.
264 *
[38542dc]265 * @param sb Superblock pointer
266 *
267 * @return Absolute block address
268 *
[4cdac68]269 */
270uint32_t ext4_filesystem_index_in_group2blockaddr(ext4_superblock_t *sb,
[38542dc]271 uint32_t index, uint32_t bgid)
[4cdac68]272{
273 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb);
[38542dc]274
275 if (ext4_superblock_get_first_data_block(sb) == 0)
[4cdac68]276 return bgid * blocks_per_group + index;
[38542dc]277 else
[4cdac68]278 return bgid * blocks_per_group + index + 1;
279}
280
[d76973c]281/** Convert the absolute block number to group number
282 *
283 * @param sb Pointer to the superblock
284 * @param b Absolute block number
285 *
286 * @return Group number
287 */
288uint32_t ext4_filesystem_blockaddr2group(ext4_superblock_t *sb, uint64_t b)
289{
290 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb);
[60c409c]291 uint32_t first_block = ext4_superblock_get_first_data_block(sb);
[d76973c]292
[60c409c]293 return (b - first_block) / blocks_per_group;
[d76973c]294}
295
[4cdac68]296/** Initialize block bitmap in block group.
[38542dc]297 *
298 * @param bg_ref Reference to block group
299 *
300 * @return Error code
301 *
[4cdac68]302 */
303static int ext4_filesystem_init_block_bitmap(ext4_block_group_ref_t *bg_ref)
304{
[d76973c]305 uint64_t itb;
306 uint32_t sz;
307 uint32_t i;
308
[4cdac68]309 /* Load bitmap */
[d76973c]310 ext4_superblock_t *sb = bg_ref->fs->superblock;
311 uint64_t bitmap_block_addr = ext4_block_group_get_block_bitmap(
312 bg_ref->block_group, bg_ref->fs->superblock);
313 uint64_t bitmap_inode_addr = ext4_block_group_get_inode_bitmap(
[38542dc]314 bg_ref->block_group, bg_ref->fs->superblock);
315
[4cdac68]316 block_t *bitmap_block;
[38542dc]317 int rc = block_get(&bitmap_block, bg_ref->fs->device,
318 bitmap_block_addr, BLOCK_FLAGS_NOREAD);
319 if (rc != EOK)
[4cdac68]320 return rc;
[38542dc]321
[4cdac68]322 uint8_t *bitmap = bitmap_block->data;
[38542dc]323
[4cdac68]324 /* Initialize all bitmap bits to zero */
[d76973c]325 uint32_t block_size = ext4_superblock_get_block_size(sb);
[4cdac68]326 memset(bitmap, 0, block_size);
[38542dc]327
[d76973c]328 /* Determine the number of reserved blocks in the group */
329 uint32_t reserved_cnt = ext4_filesystem_bg_get_backup_blocks(bg_ref);
330
[4cdac68]331 /* Set bits from to first block to first data block - 1 to one (allocated) */
[d76973c]332 for (uint32_t block = 0; block < reserved_cnt; ++block)
[4cdac68]333 ext4_bitmap_set_bit(bitmap, block);
[d76973c]334
335 uint32_t bitmap_block_gid = ext4_filesystem_blockaddr2group(sb,
336 bitmap_block_addr);
337 if (bitmap_block_gid == bg_ref->index) {
338 ext4_bitmap_set_bit(bitmap,
339 ext4_filesystem_blockaddr2_index_in_group(sb, bitmap_block_addr));
340 }
341
342 uint32_t bitmap_inode_gid = ext4_filesystem_blockaddr2group(sb,
343 bitmap_inode_addr);
344 if (bitmap_inode_gid == bg_ref->index) {
345 ext4_bitmap_set_bit(bitmap,
346 ext4_filesystem_blockaddr2_index_in_group(sb, bitmap_inode_addr));
347 }
348
349 itb = ext4_block_group_get_inode_table_first_block(bg_ref->block_group,
350 sb);
351 sz = ext4_filesystem_bg_get_itable_size(sb, bg_ref);
352
353 for (i = 0; i < sz; ++i, ++itb) {
354 uint32_t gid = ext4_filesystem_blockaddr2group(sb, itb);
355 if (gid == bg_ref->index) {
356 ext4_bitmap_set_bit(bitmap,
357 ext4_filesystem_blockaddr2_index_in_group(sb, itb));
358 }
359 }
360
[4cdac68]361 bitmap_block->dirty = true;
[38542dc]362
[4cdac68]363 /* Save bitmap */
[38542dc]364 return block_put(bitmap_block);
[4cdac68]365}
366
367/** Initialize i-node bitmap in block group.
[38542dc]368 *
369 * @param bg_ref Reference to block group
370 *
371 * @return Error code
372 *
[4cdac68]373 */
374static int ext4_filesystem_init_inode_bitmap(ext4_block_group_ref_t *bg_ref)
375{
376 /* Load bitmap */
377 uint32_t bitmap_block_addr = ext4_block_group_get_inode_bitmap(
[38542dc]378 bg_ref->block_group, bg_ref->fs->superblock);
[4cdac68]379 block_t *bitmap_block;
[38542dc]380
381 int rc = block_get(&bitmap_block, bg_ref->fs->device,
382 bitmap_block_addr, BLOCK_FLAGS_NOREAD);
383 if (rc != EOK)
[4cdac68]384 return rc;
[38542dc]385
[4cdac68]386 uint8_t *bitmap = bitmap_block->data;
[38542dc]387
[4cdac68]388 /* Initialize all bitmap bits to zero */
389 uint32_t block_size = ext4_superblock_get_block_size(bg_ref->fs->superblock);
390 uint32_t inodes_per_group =
[38542dc]391 ext4_superblock_get_inodes_per_group(bg_ref->fs->superblock);
[4cdac68]392 memset(bitmap, 0, (inodes_per_group + 7) / 8);
[38542dc]393
[4cdac68]394 uint32_t start_bit = inodes_per_group;
395 uint32_t end_bit = block_size * 8;
[38542dc]396
[4cdac68]397 uint32_t i;
[38542dc]398 for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
[4cdac68]399 ext4_bitmap_set_bit(bitmap, i);
[38542dc]400
401 if (i < end_bit)
[4cdac68]402 memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
[38542dc]403
[4cdac68]404 bitmap_block->dirty = true;
[38542dc]405
[4cdac68]406 /* Save bitmap */
[38542dc]407 return block_put(bitmap_block);
[4cdac68]408}
409
410/** Initialize i-node table in block group.
[38542dc]411 *
412 * @param bg_ref Reference to block group
413 *
414 * @return Error code
415 *
[fe61181]416 */
417static int ext4_filesystem_init_inode_table(ext4_block_group_ref_t *bg_ref)
[4cdac68]418{
419 ext4_superblock_t *sb = bg_ref->fs->superblock;
[38542dc]420
[4cdac68]421 uint32_t inode_size = ext4_superblock_get_inode_size(sb);
422 uint32_t block_size = ext4_superblock_get_block_size(sb);
423 uint32_t inodes_per_block = block_size / inode_size;
[38542dc]424
[4cdac68]425 uint32_t inodes_in_group =
[38542dc]426 ext4_superblock_get_inodes_in_group(sb, bg_ref->index);
427
[4cdac68]428 uint32_t table_blocks = inodes_in_group / inodes_per_block;
[38542dc]429
430 if (inodes_in_group % inodes_per_block)
[4cdac68]431 table_blocks++;
[38542dc]432
[4cdac68]433 /* Compute initialization bounds */
434 uint32_t first_block = ext4_block_group_get_inode_table_first_block(
[38542dc]435 bg_ref->block_group, sb);
436
[4cdac68]437 uint32_t last_block = first_block + table_blocks - 1;
[38542dc]438
[4cdac68]439 /* Initialization of all itable blocks */
440 for (uint32_t fblock = first_block; fblock <= last_block; ++fblock) {
441 block_t *block;
[38542dc]442 int rc = block_get(&block, bg_ref->fs->device, fblock,
443 BLOCK_FLAGS_NOREAD);
444 if (rc != EOK)
[4cdac68]445 return rc;
[38542dc]446
[4cdac68]447 memset(block->data, 0, block_size);
448 block->dirty = true;
[38542dc]449
[4cdac68]450 rc = block_put(block);
[38542dc]451 if (rc != EOK)
[4cdac68]452 return rc;
453 }
[38542dc]454
[4cdac68]455 return EOK;
456}
457
[5b26747]458/** Get reference to block group specified by index.
[9fc72fb3]459 *
[38542dc]460 * @param fs Filesystem to find block group on
461 * @param bgid Index of block group to load
462 * @param ref Output pointer for reference
463 *
464 * @return Error code
465 *
[9fc72fb3]466 */
[3711e7e]467int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
468 ext4_block_group_ref_t **ref)
[6c501f8]469{
[06d85e5]470 /* Allocate memory for new structure */
[38542dc]471 ext4_block_group_ref_t *newref =
472 malloc(sizeof(ext4_block_group_ref_t));
473 if (newref == NULL)
[3711e7e]474 return ENOMEM;
[38542dc]475
[06d85e5]476 /* Compute number of descriptors, that fits in one data block */
[38542dc]477 uint32_t descriptors_per_block =
478 ext4_superblock_get_block_size(fs->superblock) /
479 ext4_superblock_get_desc_size(fs->superblock);
480
[06d85e5]481 /* Block group descriptor table starts at the next block after superblock */
[38542dc]482 aoff64_t block_id =
483 ext4_superblock_get_first_data_block(fs->superblock) + 1;
484
[06d85e5]485 /* Find the block containing the descriptor we are looking for */
[3711e7e]486 block_id += bgid / descriptors_per_block;
[38542dc]487 uint32_t offset = (bgid % descriptors_per_block) *
488 ext4_superblock_get_desc_size(fs->superblock);
489
[06d85e5]490 /* Load block with descriptors */
[38542dc]491 int rc = block_get(&newref->block, fs->device, block_id, 0);
[3711e7e]492 if (rc != EOK) {
493 free(newref);
494 return rc;
495 }
[38542dc]496
[7f29575]497 /* Initialize in-memory representation */
[3711e7e]498 newref->block_group = newref->block->data + offset;
[1ac1ab4]499 newref->fs = fs;
500 newref->index = bgid;
[12b4a7f]501 newref->dirty = false;
[38542dc]502
[3711e7e]503 *ref = newref;
[38542dc]504
[4cdac68]505 if (ext4_block_group_has_flag(newref->block_group,
[38542dc]506 EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
[4cdac68]507 rc = ext4_filesystem_init_block_bitmap(newref);
508 if (rc != EOK) {
509 block_put(newref->block);
510 free(newref);
511 return rc;
512 }
[38542dc]513
[4cdac68]514 ext4_block_group_clear_flag(newref->block_group,
[38542dc]515 EXT4_BLOCK_GROUP_BLOCK_UNINIT);
516
[4cdac68]517 newref->dirty = true;
518 }
[38542dc]519
[4cdac68]520 if (ext4_block_group_has_flag(newref->block_group,
[38542dc]521 EXT4_BLOCK_GROUP_INODE_UNINIT)) {
[4cdac68]522 rc = ext4_filesystem_init_inode_bitmap(newref);
523 if (rc != EOK) {
524 block_put(newref->block);
525 free(newref);
526 return rc;
527 }
[38542dc]528
[4cdac68]529 ext4_block_group_clear_flag(newref->block_group,
[38542dc]530 EXT4_BLOCK_GROUP_INODE_UNINIT);
531
532 if (!ext4_block_group_has_flag(newref->block_group,
533 EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
[4cdac68]534 rc = ext4_filesystem_init_inode_table(newref);
[38542dc]535 if (rc != EOK)
[4cdac68]536 return rc;
[38542dc]537
[4cdac68]538 ext4_block_group_set_flag(newref->block_group,
[38542dc]539 EXT4_BLOCK_GROUP_ITABLE_ZEROED);
[4cdac68]540 }
[38542dc]541
[4cdac68]542 newref->dirty = true;
543 }
[38542dc]544
[3711e7e]545 return EOK;
[6c501f8]546}
547
[81a7858]548/** Compute checksum of block group descriptor.
[9fc72fb3]549 *
[38542dc]550 * @param sb Superblock
551 * @param bgid Index of block group in the filesystem
552 * @param bg Block group to compute checksum for
553 *
554 * @return Checksum value
555 *
[9fc72fb3]556 */
[1ac1ab4]557static uint16_t ext4_filesystem_bg_checksum(ext4_superblock_t *sb, uint32_t bgid,
[38542dc]558 ext4_block_group_t *bg)
[1ac1ab4]559{
[06d85e5]560 /* If checksum not supported, 0 will be returned */
[1ac1ab4]561 uint16_t crc = 0;
[2b5d966]562
[06d85e5]563 /* Compute the checksum only if the filesystem supports it */
[38542dc]564 if (ext4_superblock_has_feature_read_only(sb,
565 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
[1ac1ab4]566 void *base = bg;
567 void *checksum = &bg->checksum;
[38542dc]568
569 uint32_t offset = (uint32_t) (checksum - base);
570
[06d85e5]571 /* Convert block group index to little endian */
[1ac1ab4]572 uint32_t le_group = host2uint32_t_le(bgid);
[38542dc]573
[06d85e5]574 /* Initialization */
[2b5d966]575 crc = crc16_ibm(~0, sb->uuid, sizeof(sb->uuid));
[38542dc]576
[06d85e5]577 /* Include index of block group */
[2b5d966]578 crc = crc16_ibm(crc, (uint8_t *) &le_group, sizeof(le_group));
[38542dc]579
[06d85e5]580 /* Compute crc from the first part (stop before checksum field) */
[2b5d966]581 crc = crc16_ibm(crc, (uint8_t *) bg, offset);
[38542dc]582
[06d85e5]583 /* Skip checksum */
[81a7858]584 offset += sizeof(bg->checksum);
[38542dc]585
[06d85e5]586 /* Checksum of the rest of block group descriptor */
[38542dc]587 if ((ext4_superblock_has_feature_incompatible(sb,
588 EXT4_FEATURE_INCOMPAT_64BIT)) &&
589 (offset < ext4_superblock_get_desc_size(sb)))
[2b5d966]590 crc = crc16_ibm(crc, ((uint8_t *) bg) + offset,
[38542dc]591 ext4_superblock_get_desc_size(sb) - offset);
[1ac1ab4]592 }
[38542dc]593
[1ac1ab4]594 return crc;
595}
596
[d76973c]597/** Get the size of the block group's inode table
598 *
599 * @param sb Pointer to the superblock
600 * @param bg_ref Pointer to the block group reference
601 *
602 * @return Size of the inode table in blocks.
603 */
604uint32_t ext4_filesystem_bg_get_itable_size(ext4_superblock_t *sb,
605 ext4_block_group_ref_t *bg_ref)
606{
607 uint32_t itable_size;
608 uint32_t block_group_count = ext4_superblock_get_block_group_count(sb);
609 uint16_t inode_table_item_size = ext4_superblock_get_inode_size(sb);
610 uint32_t inodes_per_group = ext4_superblock_get_inodes_per_group(sb);
611 uint32_t block_size = ext4_superblock_get_block_size(sb);
612
613 if (bg_ref->index < block_group_count - 1) {
614 itable_size = inodes_per_group * inode_table_item_size;
615 } else {
616 /* Last block group could be smaller */
617 uint32_t inodes_count_total = ext4_superblock_get_inodes_count(sb);
618 itable_size =
619 (inodes_count_total - ((block_group_count - 1) * inodes_per_group)) *
620 inode_table_item_size;
621 }
622
623 return ROUND_UP(itable_size, block_size) / block_size;
624}
625
[447201e]626/* Check if n is a power of p */
627static bool is_power_of(uint32_t n, unsigned p)
628{
629 if (p == 1 && n != p)
630 return false;
631
632 while (n != p) {
633 if (n < p)
634 return false;
635 else if ((n % p) != 0)
636 return false;
637
638 n /= p;
639 }
640
641 return true;
642}
643
644/** Get the number of blocks used by superblock + gdt + reserved gdt backups
645 *
646 * @param bg Pointer to block group
647 *
648 * @return Number of blocks
649 */
[d76973c]650uint32_t ext4_filesystem_bg_get_backup_blocks(ext4_block_group_ref_t *bg)
[447201e]651{
652 uint32_t const idx = bg->index;
653 uint32_t r = 0;
654 bool has_backups = false;
655 ext4_superblock_t *sb = bg->fs->superblock;
656
657 /* First step: determine if the block group contains the backups */
658
659 if (idx <= 1)
660 has_backups = true;
661 else {
662 if (ext4_superblock_has_feature_compatible(sb,
663 EXT4_FEATURE_COMPAT_SPARSE_SUPER2)) {
664 uint32_t g1, g2;
665
666 ext4_superblock_get_backup_groups_sparse2(sb,
667 &g1, &g2);
668
669 if (idx == g1 || idx == g2)
670 has_backups = true;
671 } else if (!ext4_superblock_has_feature_read_only(sb,
672 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
673 /* Very old fs were all block groups have
674 * superblock and block descriptors backups.
675 */
676 has_backups = true;
677 } else {
678 if ((idx & 1) && (is_power_of(idx, 3) ||
679 is_power_of(idx, 5) || is_power_of(idx, 7)))
680 has_backups = true;
681 }
682 }
683
684 if (has_backups) {
685 uint32_t bg_count;
686 uint32_t bg_desc_sz;
687 uint32_t gdt_table; /* Size of the GDT in blocks */
688 uint32_t block_size = ext4_superblock_get_block_size(sb);
689
690 /* Now we know that this block group has backups,
691 * we have to compute how many blocks are reserved
692 * for them
693 */
694
695 if (idx == 0 && block_size == 1024) {
696 /* Special case for first group were the boot block
697 * resides
698 */
699 r++;
700 }
701
702 /* This accounts for the superblock */
703 r++;
704
705 /* Add the number of blocks used for the GDT */
706 bg_count = ext4_superblock_get_block_group_count(sb);
707 bg_desc_sz = ext4_superblock_get_desc_size(sb);
708 gdt_table = ROUND_UP(bg_count * bg_desc_sz, block_size) /
709 block_size;
710
711 r += gdt_table;
712
713 /* And now the number of reserved GDT blocks */
714 r += ext4_superblock_get_reserved_gdt_blocks(sb);
715 }
716
717 return r;
718}
719
[5b26747]720/** Put reference to block group.
[9fc72fb3]721 *
[c1fd281]722 * @param ref Pointer for reference to be put back
[38542dc]723 *
724 * @return Error code
725 *
[9fc72fb3]726 */
[829d238]727int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
728{
[06d85e5]729 /* Check if reference modified */
[12b4a7f]730 if (ref->dirty) {
[06d85e5]731 /* Compute new checksum of block group */
[38542dc]732 uint16_t checksum =
733 ext4_filesystem_bg_checksum(ref->fs->superblock, ref->index,
734 ref->block_group);
[5b0a3946]735 ext4_block_group_set_checksum(ref->block_group, checksum);
[38542dc]736
[06d85e5]737 /* Mark block dirty for writing changes to physical device */
[12b4a7f]738 ref->block->dirty = true;
739 }
[38542dc]740
[06d85e5]741 /* Put back block, that contains block group descriptor */
[38542dc]742 int rc = block_put(ref->block);
[829d238]743 free(ref);
[38542dc]744
[829d238]745 return rc;
746}
747
[5b26747]748/** Get reference to i-node specified by index.
[9fc72fb3]749 *
[38542dc]750 * @param fs Filesystem to find i-node on
751 * @param index Index of i-node to load
752 * @oaram ref Output pointer for reference
753 *
754 * @return Error code
755 *
[9fc72fb3]756 */
[3711e7e]757int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
758 ext4_inode_ref_t **ref)
759{
[06d85e5]760 /* Allocate memory for new structure */
[38542dc]761 ext4_inode_ref_t *newref =
762 malloc(sizeof(ext4_inode_ref_t));
763 if (newref == NULL)
[3711e7e]764 return ENOMEM;
[38542dc]765
[06d85e5]766 /* Compute number of i-nodes, that fits in one data block */
[d9bbe45]767 uint32_t inodes_per_group =
[38542dc]768 ext4_superblock_get_inodes_per_group(fs->superblock);
769
770 /*
771 * Inode numbers are 1-based, but it is simpler to work with 0-based
[3711e7e]772 * when computing indices
773 */
774 index -= 1;
[d9bbe45]775 uint32_t block_group = index / inodes_per_group;
776 uint32_t offset_in_group = index % inodes_per_group;
[38542dc]777
[06d85e5]778 /* Load block group, where i-node is located */
[d9bbe45]779 ext4_block_group_ref_t *bg_ref;
[38542dc]780 int rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
[3711e7e]781 if (rc != EOK) {
782 free(newref);
783 return rc;
784 }
[38542dc]785
[06d85e5]786 /* Load block address, where i-node table is located */
[38542dc]787 uint32_t inode_table_start =
788 ext4_block_group_get_inode_table_first_block(bg_ref->block_group,
789 fs->superblock);
790
[06d85e5]791 /* Put back block group reference (not needed more) */
[829d238]792 rc = ext4_filesystem_put_block_group_ref(bg_ref);
793 if (rc != EOK) {
794 free(newref);
795 return rc;
796 }
[38542dc]797
[06d85e5]798 /* Compute position of i-node in the block group */
[d9bbe45]799 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
800 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
801 uint32_t byte_offset_in_group = offset_in_group * inode_size;
[38542dc]802
[06d85e5]803 /* Compute block address */
[d9bbe45]804 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
[3711e7e]805 rc = block_get(&newref->block, fs->device, block_id, 0);
806 if (rc != EOK) {
807 free(newref);
808 return rc;
809 }
[38542dc]810
[06d85e5]811 /* Compute position of i-node in the data block */
[d9bbe45]812 uint32_t offset_in_block = byte_offset_in_group % block_size;
[3711e7e]813 newref->inode = newref->block->data + offset_in_block;
[38542dc]814
[06d85e5]815 /* We need to store the original value of index in the reference */
[1ac1ab4]816 newref->index = index + 1;
817 newref->fs = fs;
[052e82d]818 newref->dirty = false;
[38542dc]819
[3711e7e]820 *ref = newref;
[38542dc]821
[9b9d37bb]822 return EOK;
823}
824
[81a7858]825/** Put reference to i-node.
[9fc72fb3]826 *
[38542dc]827 * @param ref Pointer for reference to be put back
828 *
829 * @return Error code
830 *
[9fc72fb3]831 */
[9b9d37bb]832int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
833{
[06d85e5]834 /* Check if reference modified */
[052e82d]835 if (ref->dirty) {
[06d85e5]836 /* Mark block dirty for writing changes to physical device */
[052e82d]837 ref->block->dirty = true;
838 }
[38542dc]839
[06d85e5]840 /* Put back block, that contains i-node */
[38542dc]841 int rc = block_put(ref->block);
[9b9d37bb]842 free(ref);
[38542dc]843
[9b9d37bb]844 return rc;
845}
846
[81a7858]847/** Allocate new i-node in the filesystem.
[9fc72fb3]848 *
[38542dc]849 * @param fs Filesystem to allocated i-node on
850 * @param inode_ref Output pointer to return reference to allocated i-node
851 * @param flags Flags to be set for newly created i-node
852 *
853 * @return Error code
854 *
[9fc72fb3]855 */
[304faab]856int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
[38542dc]857 ext4_inode_ref_t **inode_ref, int flags)
[2d2c6ce]858{
[06d85e5]859 /* Check if newly allocated i-node will be a directory */
[304faab]860 bool is_dir = false;
[38542dc]861 if (flags & L_DIRECTORY)
[304faab]862 is_dir = true;
[38542dc]863
[06d85e5]864 /* Allocate inode by allocation algorithm */
[304faab]865 uint32_t index;
[38542dc]866 int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
867 if (rc != EOK)
[304faab]868 return rc;
[38542dc]869
[06d85e5]870 /* Load i-node from on-disk i-node table */
[304faab]871 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
872 if (rc != EOK) {
873 ext4_ialloc_free_inode(fs, index, is_dir);
874 return rc;
875 }
[38542dc]876
[06d85e5]877 /* Initialize i-node */
[304faab]878 ext4_inode_t *inode = (*inode_ref)->inode;
[38542dc]879
[8847142]880 uint16_t mode;
[304faab]881 if (is_dir) {
[8847142]882 /*
883 * Default directory permissions to be compatible with other systems
884 * 0777 (octal) == rwxrwxrwx
885 */
[38542dc]886
[8847142]887 mode = 0777;
888 mode |= EXT4_INODE_MODE_DIRECTORY;
889 ext4_inode_set_mode(fs->superblock, inode, mode);
[38542dc]890 ext4_inode_set_links_count(inode, 1); /* '.' entry */
[2d2c6ce]891 } else {
[8847142]892 /*
893 * Default file permissions to be compatible with other systems
894 * 0666 (octal) == rw-rw-rw-
895 */
[38542dc]896
[8847142]897 mode = 0666;
898 mode |= EXT4_INODE_MODE_FILE;
899 ext4_inode_set_mode(fs->superblock, inode, mode);
[2d2c6ce]900 ext4_inode_set_links_count(inode, 0);
901 }
[38542dc]902
[2d2c6ce]903 ext4_inode_set_uid(inode, 0);
904 ext4_inode_set_gid(inode, 0);
905 ext4_inode_set_size(inode, 0);
906 ext4_inode_set_access_time(inode, 0);
907 ext4_inode_set_change_inode_time(inode, 0);
908 ext4_inode_set_modification_time(inode, 0);
909 ext4_inode_set_deletion_time(inode, 0);
910 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
911 ext4_inode_set_flags(inode, 0);
912 ext4_inode_set_generation(inode, 0);
[38542dc]913
[06d85e5]914 /* Reset blocks array */
[38542dc]915 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++)
[2d2c6ce]916 inode->blocks[i] = 0;
[38542dc]917
[06d85e5]918 /* Initialize extents if needed */
[936132f]919 if (ext4_superblock_has_feature_incompatible(
[38542dc]920 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
[936132f]921 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
[38542dc]922
[06d85e5]923 /* Initialize extent root header */
[936132f]924 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
925 ext4_extent_header_set_depth(header, 0);
926 ext4_extent_header_set_entries_count(header, 0);
927 ext4_extent_header_set_generation(header, 0);
928 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
[38542dc]929
930 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
931 sizeof(ext4_extent_header_t)) / sizeof(ext4_extent_t);
932
[936132f]933 ext4_extent_header_set_max_entries_count(header, max_entries);
934 }
[38542dc]935
[304faab]936 (*inode_ref)->dirty = true;
[38542dc]937
[2d2c6ce]938 return EOK;
939}
940
[81a7858]941/** Release i-node and mark it as free.
[9fc72fb3]942 *
[38542dc]943 * @param inode_ref I-node to be released
944 *
945 * @return Error code
946 *
[9fc72fb3]947 */
[1ac1ab4]948int ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
[3d4fd2c]949{
[3e2952b]950 ext4_filesystem_t *fs = inode_ref->fs;
[38542dc]951
[06d85e5]952 /* For extents must be data block destroyed by other way */
[38542dc]953 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
954 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
955 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
[06d85e5]956 /* Data structures are released during truncate operation... */
[3e2952b]957 goto finish;
958 }
[38542dc]959
[06d85e5]960 /* Release all indirect (no data) blocks */
[38542dc]961
[06d85e5]962 /* 1) Single indirect */
[d9bbe45]963 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
[3d4fd2c]964 if (fblock != 0) {
[38542dc]965 int rc = ext4_balloc_free_block(inode_ref, fblock);
966 if (rc != EOK)
[ca3d77a]967 return rc;
[38542dc]968
[3d4fd2c]969 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
970 }
[38542dc]971
[3d4fd2c]972 block_t *block;
973 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
974 uint32_t count = block_size / sizeof(uint32_t);
[38542dc]975
[06d85e5]976 /* 2) Double indirect */
[3d4fd2c]977 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
978 if (fblock != 0) {
[38542dc]979 int rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
980 if (rc != EOK)
[e63ce679]981 return rc;
[38542dc]982
[3d4fd2c]983 uint32_t ind_block;
984 for (uint32_t offset = 0; offset < count; ++offset) {
[38542dc]985 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
986
[3d4fd2c]987 if (ind_block != 0) {
[1ac1ab4]988 rc = ext4_balloc_free_block(inode_ref, ind_block);
[3d4fd2c]989 if (rc != EOK) {
[e63ce679]990 block_put(block);
991 return rc;
[3d4fd2c]992 }
993 }
994 }
[38542dc]995
[532f53d]996 rc = block_put(block);
997 if (rc != EOK)
998 return rc;
999
[1ac1ab4]1000 rc = ext4_balloc_free_block(inode_ref, fblock);
[38542dc]1001 if (rc != EOK)
[e63ce679]1002 return rc;
[38542dc]1003
[3d4fd2c]1004 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
1005 }
[38542dc]1006
[06d85e5]1007 /* 3) Tripple indirect */
[3d4fd2c]1008 block_t *subblock;
1009 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
1010 if (fblock != 0) {
[38542dc]1011 int rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
1012 if (rc != EOK)
[e63ce679]1013 return rc;
[38542dc]1014
[3d4fd2c]1015 uint32_t ind_block;
1016 for (uint32_t offset = 0; offset < count; ++offset) {
[38542dc]1017 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
1018
[3d4fd2c]1019 if (ind_block != 0) {
[38542dc]1020 rc = block_get(&subblock, fs->device, ind_block,
1021 BLOCK_FLAGS_NONE);
[3d4fd2c]1022 if (rc != EOK) {
[e63ce679]1023 block_put(block);
1024 return rc;
[3d4fd2c]1025 }
[38542dc]1026
[3d4fd2c]1027 uint32_t ind_subblock;
[38542dc]1028 for (uint32_t suboffset = 0; suboffset < count;
1029 ++suboffset) {
1030 ind_subblock = uint32_t_le2host(((uint32_t *)
1031 subblock->data)[suboffset]);
1032
[3d4fd2c]1033 if (ind_subblock != 0) {
[1ac1ab4]1034 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
[3d4fd2c]1035 if (rc != EOK) {
[e63ce679]1036 block_put(subblock);
1037 block_put(block);
1038 return rc;
[3d4fd2c]1039 }
1040 }
1041 }
[38542dc]1042
[532f53d]1043 rc = block_put(subblock);
[f5c03a8]1044 if (rc != EOK) {
1045 block_put(block);
[532f53d]1046 return rc;
[f5c03a8]1047 }
[3d4fd2c]1048 }
[38542dc]1049
[1ac1ab4]1050 rc = ext4_balloc_free_block(inode_ref, ind_block);
[3d4fd2c]1051 if (rc != EOK) {
[e63ce679]1052 block_put(block);
1053 return rc;
[3d4fd2c]1054 }
1055 }
[38542dc]1056
[532f53d]1057 rc = block_put(block);
1058 if (rc != EOK)
1059 return rc;
1060
[1ac1ab4]1061 rc = ext4_balloc_free_block(inode_ref, fblock);
[38542dc]1062 if (rc != EOK)
[e63ce679]1063 return rc;
[38542dc]1064
[3d4fd2c]1065 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
1066 }
[38542dc]1067
[3e2952b]1068finish:
[06d85e5]1069 /* Mark inode dirty for writing to the physical device */
[e5f8762]1070 inode_ref->dirty = true;
[38542dc]1071
[0b293a6]1072 /* Free block with extended attributes if present */
1073 uint32_t xattr_block = ext4_inode_get_file_acl(
[38542dc]1074 inode_ref->inode, fs->superblock);
[0b293a6]1075 if (xattr_block) {
[38542dc]1076 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
1077 if (rc != EOK)
[0b293a6]1078 return rc;
[38542dc]1079
[0b293a6]1080 ext4_inode_set_file_acl(inode_ref->inode, fs->superblock, 0);
1081 }
[38542dc]1082
[06d85e5]1083 /* Free inode by allocator */
[38542dc]1084 int rc;
[304faab]1085 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
[38542dc]1086 EXT4_INODE_MODE_DIRECTORY))
[304faab]1087 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
[38542dc]1088 else
[304faab]1089 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
[38542dc]1090
1091 return rc;
[3d4fd2c]1092}
1093
[81a7858]1094/** Truncate i-node data blocks.
[9fc72fb3]1095 *
[38542dc]1096 * @param inode_ref I-node to be truncated
1097 * @param new_size New size of inode (must be < current size)
1098 *
1099 * @return Error code
1100 *
[9fc72fb3]1101 */
[38542dc]1102int ext4_filesystem_truncate_inode(ext4_inode_ref_t *inode_ref,
1103 aoff64_t new_size)
[3d4fd2c]1104{
[1ac1ab4]1105 ext4_superblock_t *sb = inode_ref->fs->superblock;
[38542dc]1106
[06d85e5]1107 /* Check flags, if i-node can be truncated */
[38542dc]1108 if (!ext4_inode_can_truncate(sb, inode_ref->inode))
[3d4fd2c]1109 return EINVAL;
[38542dc]1110
[06d85e5]1111 /* If sizes are equal, nothing has to be done. */
[1ac1ab4]1112 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
[38542dc]1113 if (old_size == new_size)
[3d4fd2c]1114 return EOK;
[38542dc]1115
[06d85e5]1116 /* It's not suppported to make the larger file by truncate operation */
[38542dc]1117 if (old_size < new_size)
[3d4fd2c]1118 return EINVAL;
[38542dc]1119
[06d85e5]1120 /* Compute how many blocks will be released */
[d9bbe45]1121 aoff64_t size_diff = old_size - new_size;
[1ac1ab4]1122 uint32_t block_size = ext4_superblock_get_block_size(sb);
[ca3d77a]1123 uint32_t diff_blocks_count = size_diff / block_size;
[38542dc]1124 if (size_diff % block_size != 0)
[ca3d77a]1125 diff_blocks_count++;
[38542dc]1126
[ca3d77a]1127 uint32_t old_blocks_count = old_size / block_size;
[38542dc]1128 if (old_size % block_size != 0)
[ca3d77a]1129 old_blocks_count++;
[38542dc]1130
1131 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1132 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1133 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
[06d85e5]1134 /* Extents require special operation */
[38542dc]1135 int rc = ext4_extent_release_blocks_from(inode_ref,
1136 old_blocks_count - diff_blocks_count);
1137 if (rc != EOK)
[ca3d77a]1138 return rc;
[5b0a3946]1139 } else {
[06d85e5]1140 /* Release data blocks from the end of file */
[38542dc]1141
[06d85e5]1142 /* Starting from 1 because of logical blocks are numbered from 0 */
[5b0a3946]1143 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
[38542dc]1144 int rc = ext4_filesystem_release_inode_block(inode_ref,
1145 old_blocks_count - i);
1146 if (rc != EOK)
[5b0a3946]1147 return rc;
1148 }
[3d4fd2c]1149 }
[38542dc]1150
[06d85e5]1151 /* Update i-node */
[3d4fd2c]1152 ext4_inode_set_size(inode_ref->inode, new_size);
1153 inode_ref->dirty = true;
[38542dc]1154
[3d4fd2c]1155 return EOK;
1156}
1157
[81a7858]1158/** Get physical block address by logical index of the block.
[9fc72fb3]1159 *
[38542dc]1160 * @param inode_ref I-node to read block address from
1161 * @param iblock Logical index of block
1162 * @param fblock Output pointer for return physical block address
1163 *
1164 * @return Error code
1165 *
[9fc72fb3]1166 */
[1ac1ab4]1167int ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
[38542dc]1168 aoff64_t iblock, uint32_t *fblock)
[9b9d37bb]1169{
[1ac1ab4]1170 ext4_filesystem_t *fs = inode_ref->fs;
[38542dc]1171
[06d85e5]1172 /* For empty file is situation simple */
[b73530a]1173 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
1174 *fblock = 0;
1175 return EOK;
1176 }
[38542dc]1177
[9b9d37bb]1178 uint32_t current_block;
[38542dc]1179
[06d85e5]1180 /* Handle i-node using extents */
[38542dc]1181 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1182 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1183 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1184 int rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
1185 if (rc != EOK)
[9104bb5]1186 return rc;
[38542dc]1187
[acd869e]1188 *fblock = current_block;
1189 return EOK;
1190 }
[38542dc]1191
[9104bb5]1192 ext4_inode_t *inode = inode_ref->inode;
[38542dc]1193
[06d85e5]1194 /* Direct block are read directly from array in i-node structure */
[e68c834]1195 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[38542dc]1196 current_block = ext4_inode_get_direct_block(inode, (uint32_t) iblock);
[9b9d37bb]1197 *fblock = current_block;
1198 return EOK;
1199 }
[38542dc]1200
[06d85e5]1201 /* Determine indirection level of the target block */
[38542dc]1202 unsigned int level = 0;
1203 for (unsigned int i = 1; i < 4; i++) {
[a9a0982]1204 if (iblock < fs->inode_block_limits[i]) {
[9b9d37bb]1205 level = i;
1206 break;
1207 }
1208 }
[38542dc]1209
1210 if (level == 0)
[9b9d37bb]1211 return EIO;
[38542dc]1212
[06d85e5]1213 /* Compute offsets for the topmost level */
[38542dc]1214 aoff64_t block_offset_in_level =
1215 iblock - fs->inode_block_limits[level - 1];
1216 current_block = ext4_inode_get_indirect_block(inode, level - 1);
1217 uint32_t offset_in_block =
1218 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1219
[06d85e5]1220 /* Sparse file */
[6088193]1221 if (current_block == 0) {
1222 *fblock = 0;
1223 return EOK;
1224 }
[38542dc]1225
[d9bbe45]1226 block_t *block;
[38542dc]1227
1228 /*
1229 * Navigate through other levels, until we find the block number
[9b9d37bb]1230 * or find null reference meaning we are dealing with sparse file
1231 */
1232 while (level > 0) {
[06d85e5]1233 /* Load indirect block */
[38542dc]1234 int rc = block_get(&block, fs->device, current_block, 0);
1235 if (rc != EOK)
[9b9d37bb]1236 return rc;
[38542dc]1237
[06d85e5]1238 /* Read block address from indirect block */
[38542dc]1239 current_block =
1240 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1241
[06d85e5]1242 /* Put back indirect block untouched */
[9b9d37bb]1243 rc = block_put(block);
[38542dc]1244 if (rc != EOK)
[9b9d37bb]1245 return rc;
[38542dc]1246
[06d85e5]1247 /* Check for sparse file */
[9b9d37bb]1248 if (current_block == 0) {
1249 *fblock = 0;
1250 return EOK;
1251 }
[38542dc]1252
[06d85e5]1253 /* Jump to the next level */
[38542dc]1254 level--;
1255
[06d85e5]1256 /* Termination condition - we have address of data block loaded */
[38542dc]1257 if (level == 0)
[9b9d37bb]1258 break;
[38542dc]1259
[06d85e5]1260 /* Visit the next level */
[a9a0982]1261 block_offset_in_level %= fs->inode_blocks_per_level[level];
[38542dc]1262 offset_in_block =
1263 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
[9b9d37bb]1264 }
[38542dc]1265
[9b9d37bb]1266 *fblock = current_block;
[38542dc]1267
[3711e7e]1268 return EOK;
1269}
[6c501f8]1270
[8060341a]1271/** Set physical block address for the block logical address into the i-node.
[9fc72fb3]1272 *
[38542dc]1273 * @param inode_ref I-node to set block address to
1274 * @param iblock Logical index of block
1275 * @param fblock Physical block address
1276 *
1277 * @return Error code
1278 *
[9fc72fb3]1279 */
[1ac1ab4]1280int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
[38542dc]1281 aoff64_t iblock, uint32_t fblock)
[35f48f2]1282{
[1ac1ab4]1283 ext4_filesystem_t *fs = inode_ref->fs;
[38542dc]1284
[06d85e5]1285 /* Handle inode using extents */
[38542dc]1286 if ((ext4_superblock_has_feature_compatible(fs->superblock,
1287 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1288 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1289 /* Not reachable */
[35f48f2]1290 return ENOTSUP;
1291 }
[38542dc]1292
[06d85e5]1293 /* Handle simple case when we are dealing with direct reference */
[35f48f2]1294 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[38542dc]1295 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t) iblock, fblock);
[1e65444]1296 inode_ref->dirty = true;
[38542dc]1297
[35f48f2]1298 return EOK;
1299 }
[38542dc]1300
[06d85e5]1301 /* Determine the indirection level needed to get the desired block */
[38542dc]1302 unsigned int level = 0;
1303 for (unsigned int i = 1; i < 4; i++) {
[1e65444]1304 if (iblock < fs->inode_block_limits[i]) {
1305 level = i;
1306 break;
1307 }
1308 }
[38542dc]1309
1310 if (level == 0)
[1e65444]1311 return EIO;
[38542dc]1312
[d9bbe45]1313 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[38542dc]1314
[06d85e5]1315 /* Compute offsets for the topmost level */
[38542dc]1316 aoff64_t block_offset_in_level =
1317 iblock - fs->inode_block_limits[level - 1];
1318 uint32_t current_block =
1319 ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1320 uint32_t offset_in_block =
1321 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1322
[d9bbe45]1323 uint32_t new_block_addr;
[38542dc]1324 block_t *block;
1325 block_t *new_block;
1326
[06d85e5]1327 /* Is needed to allocate indirect block on the i-node level */
[1e65444]1328 if (current_block == 0) {
[06d85e5]1329 /* Allocate new indirect block */
[38542dc]1330 int rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1331 if (rc != EOK)
[e63ce679]1332 return rc;
[38542dc]1333
[06d85e5]1334 /* Update i-node */
[38542dc]1335 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1336 new_block_addr);
[1e65444]1337 inode_ref->dirty = true;
[38542dc]1338
[06d85e5]1339 /* Load newly allocated block */
[38542dc]1340 rc = block_get(&new_block, fs->device, new_block_addr,
1341 BLOCK_FLAGS_NOREAD);
[1e65444]1342 if (rc != EOK) {
[1ac1ab4]1343 ext4_balloc_free_block(inode_ref, new_block_addr);
[e63ce679]1344 return rc;
[1e65444]1345 }
[38542dc]1346
[06d85e5]1347 /* Initialize new block */
[1e65444]1348 memset(new_block->data, 0, block_size);
1349 new_block->dirty = true;
[38542dc]1350
[06d85e5]1351 /* Put back the allocated block */
[1e65444]1352 rc = block_put(new_block);
[38542dc]1353 if (rc != EOK)
[e63ce679]1354 return rc;
[38542dc]1355
[1e65444]1356 current_block = new_block_addr;
1357 }
[38542dc]1358
1359 /*
1360 * Navigate through other levels, until we find the block number
[1e65444]1361 * or find null reference meaning we are dealing with sparse file
1362 */
1363 while (level > 0) {
[38542dc]1364 int rc = block_get(&block, fs->device, current_block, 0);
1365 if (rc != EOK)
[1e65444]1366 return rc;
[38542dc]1367
1368 current_block =
1369 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1370
[b12ca16]1371 if ((level > 1) && (current_block == 0)) {
[06d85e5]1372 /* Allocate new block */
[1ac1ab4]1373 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
[b12ca16]1374 if (rc != EOK) {
[e63ce679]1375 block_put(block);
1376 return rc;
[b12ca16]1377 }
[38542dc]1378
[06d85e5]1379 /* Load newly allocated block */
[38542dc]1380 rc = block_get(&new_block, fs->device, new_block_addr,
1381 BLOCK_FLAGS_NOREAD);
[b12ca16]1382 if (rc != EOK) {
[e63ce679]1383 block_put(block);
1384 return rc;
[b12ca16]1385 }
[38542dc]1386
[06d85e5]1387 /* Initialize allocated block */
[b12ca16]1388 memset(new_block->data, 0, block_size);
1389 new_block->dirty = true;
[38542dc]1390
[b12ca16]1391 rc = block_put(new_block);
1392 if (rc != EOK) {
[e63ce679]1393 block_put(block);
1394 return rc;
[b12ca16]1395 }
[38542dc]1396
[06d85e5]1397 /* Write block address to the parent */
[38542dc]1398 ((uint32_t *) block->data)[offset_in_block] =
1399 host2uint32_t_le(new_block_addr);
[b12ca16]1400 block->dirty = true;
1401 current_block = new_block_addr;
1402 }
[38542dc]1403
[06d85e5]1404 /* Will be finished, write the fblock address */
[b12ca16]1405 if (level == 1) {
[38542dc]1406 ((uint32_t *) block->data)[offset_in_block] =
1407 host2uint32_t_le(fblock);
[b12ca16]1408 block->dirty = true;
[1e65444]1409 }
[38542dc]1410
[1e65444]1411 rc = block_put(block);
[38542dc]1412 if (rc != EOK)
[1e65444]1413 return rc;
[38542dc]1414
1415 level--;
1416
1417 /*
1418 * If we are on the last level, break here as
[1e65444]1419 * there is no next level to visit
1420 */
[38542dc]1421 if (level == 0)
[1e65444]1422 break;
[38542dc]1423
[1e65444]1424 /* Visit the next level */
1425 block_offset_in_level %= fs->inode_blocks_per_level[level];
[38542dc]1426 offset_in_block =
1427 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
[1e65444]1428 }
[38542dc]1429
[35f48f2]1430 return EOK;
1431}
1432
[8060341a]1433/** Release data block from i-node
[9fc72fb3]1434 *
[38542dc]1435 * @param inode_ref I-node to release block from
1436 * @param iblock Logical block to be released
1437 *
1438 * @return Error code
1439 *
[9fc72fb3]1440 */
[38542dc]1441int ext4_filesystem_release_inode_block(ext4_inode_ref_t *inode_ref,
1442 uint32_t iblock)
[d5a78e28]1443{
[fffb061]1444 uint32_t fblock;
[38542dc]1445
[1ac1ab4]1446 ext4_filesystem_t *fs = inode_ref->fs;
[38542dc]1447
1448 /* Extents are handled otherwise = there is not support in this function */
1449 assert(!(ext4_superblock_has_feature_incompatible(fs->superblock,
1450 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1451 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1452
[d9bbe45]1453 ext4_inode_t *inode = inode_ref->inode;
[38542dc]1454
[06d85e5]1455 /* Handle simple case when we are dealing with direct reference */
[052e82d]1456 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[12b4a7f]1457 fblock = ext4_inode_get_direct_block(inode, iblock);
[38542dc]1458
[06d85e5]1459 /* Sparse file */
[38542dc]1460 if (fblock == 0)
[052e82d]1461 return EOK;
[38542dc]1462
[052e82d]1463 ext4_inode_set_direct_block(inode, iblock, 0);
[1ac1ab4]1464 return ext4_balloc_free_block(inode_ref, fblock);
[12b4a7f]1465 }
[38542dc]1466
[06d85e5]1467 /* Determine the indirection level needed to get the desired block */
[38542dc]1468 unsigned int level = 0;
1469 for (unsigned int i = 1; i < 4; i++) {
[12b4a7f]1470 if (iblock < fs->inode_block_limits[i]) {
1471 level = i;
1472 break;
[052e82d]1473 }
[12b4a7f]1474 }
[38542dc]1475
1476 if (level == 0)
[12b4a7f]1477 return EIO;
[38542dc]1478
[06d85e5]1479 /* Compute offsets for the topmost level */
[38542dc]1480 aoff64_t block_offset_in_level =
1481 iblock - fs->inode_block_limits[level - 1];
1482 uint32_t current_block =
1483 ext4_inode_get_indirect_block(inode, level - 1);
1484 uint32_t offset_in_block =
1485 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1486
1487 /*
1488 * Navigate through other levels, until we find the block number
[12b4a7f]1489 * or find null reference meaning we are dealing with sparse file
1490 */
[d9bbe45]1491 block_t *block;
[12b4a7f]1492 while (level > 0) {
[041ab64]1493
1494 /* Sparse check */
1495 if (current_block == 0)
1496 return EOK;
1497
[38542dc]1498 int rc = block_get(&block, fs->device, current_block, 0);
1499 if (rc != EOK)
[12b4a7f]1500 return rc;
[38542dc]1501
1502 current_block =
1503 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1504
[06d85e5]1505 /* Set zero if physical data block address found */
[12b4a7f]1506 if (level == 1) {
[38542dc]1507 ((uint32_t *) block->data)[offset_in_block] =
1508 host2uint32_t_le(0);
[12b4a7f]1509 block->dirty = true;
[052e82d]1510 }
[38542dc]1511
[12b4a7f]1512 rc = block_put(block);
[38542dc]1513 if (rc != EOK)
[12b4a7f]1514 return rc;
[38542dc]1515
1516 level--;
1517
1518 /*
1519 * If we are on the last level, break here as
[12b4a7f]1520 * there is no next level to visit
1521 */
[38542dc]1522 if (level == 0)
[12b4a7f]1523 break;
[38542dc]1524
[12b4a7f]1525 /* Visit the next level */
1526 block_offset_in_level %= fs->inode_blocks_per_level[level];
[38542dc]1527 offset_in_block =
1528 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
[12b4a7f]1529 }
[38542dc]1530
[12b4a7f]1531 fblock = current_block;
[38542dc]1532 if (fblock == 0)
[12b4a7f]1533 return EOK;
[38542dc]1534
[06d85e5]1535 /* Physical block is not referenced, it can be released */
[1ac1ab4]1536 return ext4_balloc_free_block(inode_ref, fblock);
[d5a78e28]1537}
1538
[81a7858]1539/** Append following logical block to the i-node.
[9fc72fb3]1540 *
[38542dc]1541 * @param inode_ref I-node to append block to
1542 * @param fblock Output physical block address of newly allocated block
1543 * @param iblock Output logical number of newly allocated block
1544 *
1545 * @return Error code
1546 *
[9fc72fb3]1547 */
[5b16912]1548int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
[38542dc]1549 uint32_t *fblock, uint32_t *iblock)
[5b16912]1550{
[06d85e5]1551 /* Handle extents separately */
[38542dc]1552 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1553 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1554 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)))
[d510ac01]1555 return ext4_extent_append_block(inode_ref, iblock, fblock, true);
[38542dc]1556
[5b16912]1557 ext4_superblock_t *sb = inode_ref->fs->superblock;
[38542dc]1558
[06d85e5]1559 /* Compute next block index and allocate data block */
[5b16912]1560 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1561 uint32_t block_size = ext4_superblock_get_block_size(sb);
[38542dc]1562
[06d85e5]1563 /* Align size i-node size */
[38542dc]1564 if ((inode_size % block_size) != 0)
[81a7858]1565 inode_size += block_size - (inode_size % block_size);
[38542dc]1566
[06d85e5]1567 /* Logical blocks are numbered from 0 */
[5b16912]1568 uint32_t new_block_idx = inode_size / block_size;
[38542dc]1569
[06d85e5]1570 /* Allocate new physical block */
[5b16912]1571 uint32_t phys_block;
[38542dc]1572 int rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1573 if (rc != EOK)
[5b16912]1574 return rc;
[38542dc]1575
[06d85e5]1576 /* Add physical block address to the i-node */
[38542dc]1577 rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
1578 new_block_idx, phys_block);
[5b16912]1579 if (rc != EOK) {
1580 ext4_balloc_free_block(inode_ref, phys_block);
1581 return rc;
1582 }
[38542dc]1583
[06d85e5]1584 /* Update i-node */
[5b16912]1585 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1586 inode_ref->dirty = true;
[38542dc]1587
[5b16912]1588 *fblock = phys_block;
1589 *iblock = new_block_idx;
[38542dc]1590
[5b16912]1591 return EOK;
1592}
1593
[6c501f8]1594/**
1595 * @}
[38542dc]1596 */
Note: See TracBrowser for help on using the repository browser.