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

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

Move parts of ext4 filesystem initialization.

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