source: mainline/uspace/lib/ext4/libext4_filesystem.c@ fc22069

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fc22069 was fc22069, checked in by Martin Decky <martin@…>, 10 years ago

block devices use the same interface, therefore the API of libblock should not expose the implementation details

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