source: mainline/uspace/lib/ext4/src/filesystem.c@ 6ff23ff

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

Fix block comment formatting (ccheck).

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