source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 2d53cfc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d53cfc was 0d8322da, checked in by Jakub Jermar <jakub@…>, 13 years ago

Remove GPL licensed code from libext4.

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