source: mainline/uspace/lib/ext4/libext4_filesystem.c@ b828907

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b828907 was 4cdac68, checked in by Frantisek Princ <frantisek.princ@…>, 13 years ago

debugged initialization of block group structures

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