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

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

Libext4 should try to include what it uses.

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