source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 22fb7ab

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

libext4: fix memory leak, release the superblock structure if the mount operation fails

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