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

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

Fix block comment formatting (ccheck).

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