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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b3cf946 was d1538a1, checked in by Martin Sucha <sucha14@…>, 13 years ago

Add missing copyright headers to ext4

Those files are based on ext2 filesystem driver code.

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