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

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

Libext4 went the wrong direction with filename prefixing.

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