source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 418f21d

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

libext4: fix ext4_filesystem_blockaddr2group() to support filesystems with block size = 1 Kb

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