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

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

Libext4 went the wrong direction with filename prefixing.

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