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

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

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