source: mainline/uspace/lib/ext4/src/filesystem.c@ 71fe4723

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 71fe4723 was fcb0d76, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Libext4 should try to include what it uses.

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