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

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

Restructure ext4 filesystem opening/closing.

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