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

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

Restructure ext4 filesystem opening/closing.

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