source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 60c409c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 60c409c was 60c409c, checked in by Maurizio Lombardi <m.lombardi85@…>, 10 years ago

libext4: fix ext4_filesystem_blockaddr2group() to support filesystems with block size = 1 Kb

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