source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 447201e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 447201e was 447201e, checked in by Maurizio Lombardi <m.lombardi85@…>, 10 years ago

libext4: adds a function that, given a block group ref, calculates the number of blocks reserved to GDT+superblock backups.

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