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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 43.4 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Sucha
3 * Copyright (c) 2012 Frantisek Princ
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libext4
31 * @{
32 */
33/**
34 * @file 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 /* Very old fs were all block groups have
753 * superblock and block descriptors backups.
754 */
755 has_backups = true;
756 } else {
757 if ((idx & 1) && (is_power_of(idx, 3) ||
758 is_power_of(idx, 5) || is_power_of(idx, 7)))
759 has_backups = true;
760 }
761 }
762
763 if (has_backups) {
764 uint32_t bg_count;
765 uint32_t bg_desc_sz;
766 uint32_t gdt_table; /* Size of the GDT in blocks */
767 uint32_t block_size = ext4_superblock_get_block_size(sb);
768
769 /* Now we know that this block group has backups,
770 * we have to compute how many blocks are reserved
771 * for them
772 */
773
774 if (idx == 0 && block_size == 1024) {
775 /* Special case for first group were the boot block
776 * resides
777 */
778 r++;
779 }
780
781 /* This accounts for the superblock */
782 r++;
783
784 /* Add the number of blocks used for the GDT */
785 bg_count = ext4_superblock_get_block_group_count(sb);
786 bg_desc_sz = ext4_superblock_get_desc_size(sb);
787 gdt_table = ROUND_UP(bg_count * bg_desc_sz, block_size) /
788 block_size;
789
790 r += gdt_table;
791
792 /* And now the number of reserved GDT blocks */
793 r += ext4_superblock_get_reserved_gdt_blocks(sb);
794 }
795
796 return r;
797}
798
799/** Put reference to block group.
800 *
801 * @param ref Pointer for reference to be put back
802 *
803 * @return Error code
804 *
805 */
806errno_t ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
807{
808 /* Check if reference modified */
809 if (ref->dirty) {
810 /* Compute new checksum of block group */
811 uint16_t checksum =
812 ext4_filesystem_bg_checksum(ref->fs->superblock, ref->index,
813 ref->block_group);
814 ext4_block_group_set_checksum(ref->block_group, checksum);
815
816 /* Mark block dirty for writing changes to physical device */
817 ref->block->dirty = true;
818 }
819
820 /* Put back block, that contains block group descriptor */
821 errno_t rc = block_put(ref->block);
822 free(ref);
823
824 return rc;
825}
826
827/** Get reference to i-node specified by index.
828 *
829 * @param fs Filesystem to find i-node on
830 * @param index Index of i-node to load
831 * @oaram ref Output pointer for reference
832 *
833 * @return Error code
834 *
835 */
836errno_t ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
837 ext4_inode_ref_t **ref)
838{
839 /* Allocate memory for new structure */
840 ext4_inode_ref_t *newref =
841 malloc(sizeof(ext4_inode_ref_t));
842 if (newref == NULL)
843 return ENOMEM;
844
845 /* Compute number of i-nodes, that fits in one data block */
846 uint32_t inodes_per_group =
847 ext4_superblock_get_inodes_per_group(fs->superblock);
848
849 /*
850 * Inode numbers are 1-based, but it is simpler to work with 0-based
851 * when computing indices
852 */
853 index -= 1;
854 uint32_t block_group = index / inodes_per_group;
855 uint32_t offset_in_group = index % inodes_per_group;
856
857 /* Load block group, where i-node is located */
858 ext4_block_group_ref_t *bg_ref;
859 errno_t rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
860 if (rc != EOK) {
861 free(newref);
862 return rc;
863 }
864
865 /* Load block address, where i-node table is located */
866 uint32_t inode_table_start =
867 ext4_block_group_get_inode_table_first_block(bg_ref->block_group,
868 fs->superblock);
869
870 /* Put back block group reference (not needed more) */
871 rc = ext4_filesystem_put_block_group_ref(bg_ref);
872 if (rc != EOK) {
873 free(newref);
874 return rc;
875 }
876
877 /* Compute position of i-node in the block group */
878 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
879 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
880 uint32_t byte_offset_in_group = offset_in_group * inode_size;
881
882 /* Compute block address */
883 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
884 rc = block_get(&newref->block, fs->device, block_id, 0);
885 if (rc != EOK) {
886 free(newref);
887 return rc;
888 }
889
890 /* Compute position of i-node in the data block */
891 uint32_t offset_in_block = byte_offset_in_group % block_size;
892 newref->inode = newref->block->data + offset_in_block;
893
894 /* We need to store the original value of index in the reference */
895 newref->index = index + 1;
896 newref->fs = fs;
897 newref->dirty = false;
898
899 *ref = newref;
900
901 return EOK;
902}
903
904/** Put reference to i-node.
905 *
906 * @param ref Pointer for reference to be put back
907 *
908 * @return Error code
909 *
910 */
911errno_t ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
912{
913 /* Check if reference modified */
914 if (ref->dirty) {
915 /* Mark block dirty for writing changes to physical device */
916 ref->block->dirty = true;
917 }
918
919 /* Put back block, that contains i-node */
920 errno_t rc = block_put(ref->block);
921 free(ref);
922
923 return rc;
924}
925
926/** Allocate new i-node in the filesystem.
927 *
928 * @param fs Filesystem to allocated i-node on
929 * @param inode_ref Output pointer to return reference to allocated i-node
930 * @param flags Flags to be set for newly created i-node
931 *
932 * @return Error code
933 *
934 */
935errno_t ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
936 ext4_inode_ref_t **inode_ref, int flags)
937{
938 /* Check if newly allocated i-node will be a directory */
939 bool is_dir = false;
940 if (flags & L_DIRECTORY)
941 is_dir = true;
942
943 /* Allocate inode by allocation algorithm */
944 uint32_t index;
945 errno_t rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
946 if (rc != EOK)
947 return rc;
948
949 /* Load i-node from on-disk i-node table */
950 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
951 if (rc != EOK) {
952 ext4_ialloc_free_inode(fs, index, is_dir);
953 return rc;
954 }
955
956 /* Initialize i-node */
957 ext4_inode_t *inode = (*inode_ref)->inode;
958
959 uint16_t mode;
960 if (is_dir) {
961 /*
962 * Default directory permissions to be compatible with other systems
963 * 0777 (octal) == rwxrwxrwx
964 */
965
966 mode = 0777;
967 mode |= EXT4_INODE_MODE_DIRECTORY;
968 ext4_inode_set_mode(fs->superblock, inode, mode);
969 ext4_inode_set_links_count(inode, 1); /* '.' entry */
970 } else {
971 /*
972 * Default file permissions to be compatible with other systems
973 * 0666 (octal) == rw-rw-rw-
974 */
975
976 mode = 0666;
977 mode |= EXT4_INODE_MODE_FILE;
978 ext4_inode_set_mode(fs->superblock, inode, mode);
979 ext4_inode_set_links_count(inode, 0);
980 }
981
982 ext4_inode_set_uid(inode, 0);
983 ext4_inode_set_gid(inode, 0);
984 ext4_inode_set_size(inode, 0);
985 ext4_inode_set_access_time(inode, 0);
986 ext4_inode_set_change_inode_time(inode, 0);
987 ext4_inode_set_modification_time(inode, 0);
988 ext4_inode_set_deletion_time(inode, 0);
989 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
990 ext4_inode_set_flags(inode, 0);
991 ext4_inode_set_generation(inode, 0);
992
993 /* Reset blocks array */
994 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++)
995 inode->blocks[i] = 0;
996
997 /* Initialize extents if needed */
998 if (ext4_superblock_has_feature_incompatible(
999 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
1000 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
1001
1002 /* Initialize extent root header */
1003 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
1004 ext4_extent_header_set_depth(header, 0);
1005 ext4_extent_header_set_entries_count(header, 0);
1006 ext4_extent_header_set_generation(header, 0);
1007 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
1008
1009 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
1010 sizeof(ext4_extent_header_t)) / sizeof(ext4_extent_t);
1011
1012 ext4_extent_header_set_max_entries_count(header, max_entries);
1013 }
1014
1015 (*inode_ref)->dirty = true;
1016
1017 return EOK;
1018}
1019
1020/** Release i-node and mark it as free.
1021 *
1022 * @param inode_ref I-node to be released
1023 *
1024 * @return Error code
1025 *
1026 */
1027errno_t ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
1028{
1029 ext4_filesystem_t *fs = inode_ref->fs;
1030
1031 /* For extents must be data block destroyed by other way */
1032 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1033 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1034 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1035 /* Data structures are released during truncate operation... */
1036 goto finish;
1037 }
1038
1039 /* Release all indirect (no data) blocks */
1040
1041 /* 1) Single indirect */
1042 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
1043 if (fblock != 0) {
1044 errno_t rc = ext4_balloc_free_block(inode_ref, fblock);
1045 if (rc != EOK)
1046 return rc;
1047
1048 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
1049 }
1050
1051 block_t *block;
1052 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1053 uint32_t count = block_size / sizeof(uint32_t);
1054
1055 /* 2) Double indirect */
1056 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
1057 if (fblock != 0) {
1058 errno_t rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
1059 if (rc != EOK)
1060 return rc;
1061
1062 uint32_t ind_block;
1063 for (uint32_t offset = 0; offset < count; ++offset) {
1064 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
1065
1066 if (ind_block != 0) {
1067 rc = ext4_balloc_free_block(inode_ref, ind_block);
1068 if (rc != EOK) {
1069 block_put(block);
1070 return rc;
1071 }
1072 }
1073 }
1074
1075 rc = block_put(block);
1076 if (rc != EOK)
1077 return rc;
1078
1079 rc = ext4_balloc_free_block(inode_ref, fblock);
1080 if (rc != EOK)
1081 return rc;
1082
1083 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
1084 }
1085
1086 /* 3) Tripple indirect */
1087 block_t *subblock;
1088 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
1089 if (fblock != 0) {
1090 errno_t rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
1091 if (rc != EOK)
1092 return rc;
1093
1094 uint32_t ind_block;
1095 for (uint32_t offset = 0; offset < count; ++offset) {
1096 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
1097
1098 if (ind_block != 0) {
1099 rc = block_get(&subblock, fs->device, ind_block,
1100 BLOCK_FLAGS_NONE);
1101 if (rc != EOK) {
1102 block_put(block);
1103 return rc;
1104 }
1105
1106 uint32_t ind_subblock;
1107 for (uint32_t suboffset = 0; suboffset < count;
1108 ++suboffset) {
1109 ind_subblock = uint32_t_le2host(((uint32_t *)
1110 subblock->data)[suboffset]);
1111
1112 if (ind_subblock != 0) {
1113 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
1114 if (rc != EOK) {
1115 block_put(subblock);
1116 block_put(block);
1117 return rc;
1118 }
1119 }
1120 }
1121
1122 rc = block_put(subblock);
1123 if (rc != EOK) {
1124 block_put(block);
1125 return rc;
1126 }
1127 }
1128
1129 rc = ext4_balloc_free_block(inode_ref, ind_block);
1130 if (rc != EOK) {
1131 block_put(block);
1132 return rc;
1133 }
1134 }
1135
1136 rc = block_put(block);
1137 if (rc != EOK)
1138 return rc;
1139
1140 rc = ext4_balloc_free_block(inode_ref, fblock);
1141 if (rc != EOK)
1142 return rc;
1143
1144 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
1145 }
1146
1147finish:
1148 /* Mark inode dirty for writing to the physical device */
1149 inode_ref->dirty = true;
1150
1151 /* Free block with extended attributes if present */
1152 uint32_t xattr_block = ext4_inode_get_file_acl(
1153 inode_ref->inode, fs->superblock);
1154 if (xattr_block) {
1155 errno_t rc = ext4_balloc_free_block(inode_ref, xattr_block);
1156 if (rc != EOK)
1157 return rc;
1158
1159 ext4_inode_set_file_acl(inode_ref->inode, fs->superblock, 0);
1160 }
1161
1162 /* Free inode by allocator */
1163 errno_t rc;
1164 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
1165 EXT4_INODE_MODE_DIRECTORY))
1166 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
1167 else
1168 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
1169
1170 return rc;
1171}
1172
1173/** Truncate i-node data blocks.
1174 *
1175 * @param inode_ref I-node to be truncated
1176 * @param new_size New size of inode (must be < current size)
1177 *
1178 * @return Error code
1179 *
1180 */
1181errno_t ext4_filesystem_truncate_inode(ext4_inode_ref_t *inode_ref,
1182 aoff64_t new_size)
1183{
1184 ext4_superblock_t *sb = inode_ref->fs->superblock;
1185
1186 /* Check flags, if i-node can be truncated */
1187 if (!ext4_inode_can_truncate(sb, inode_ref->inode))
1188 return EINVAL;
1189
1190 /* If sizes are equal, nothing has to be done. */
1191 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
1192 if (old_size == new_size)
1193 return EOK;
1194
1195 /* It's not suppported to make the larger file by truncate operation */
1196 if (old_size < new_size)
1197 return EINVAL;
1198
1199 /* Compute how many blocks will be released */
1200 aoff64_t size_diff = old_size - new_size;
1201 uint32_t block_size = ext4_superblock_get_block_size(sb);
1202 uint32_t diff_blocks_count = size_diff / block_size;
1203 if (size_diff % block_size != 0)
1204 diff_blocks_count++;
1205
1206 uint32_t old_blocks_count = old_size / block_size;
1207 if (old_size % block_size != 0)
1208 old_blocks_count++;
1209
1210 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1211 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1212 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1213 /* Extents require special operation */
1214 errno_t rc = ext4_extent_release_blocks_from(inode_ref,
1215 old_blocks_count - diff_blocks_count);
1216 if (rc != EOK)
1217 return rc;
1218 } else {
1219 /* Release data blocks from the end of file */
1220
1221 /* Starting from 1 because of logical blocks are numbered from 0 */
1222 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
1223 errno_t rc = ext4_filesystem_release_inode_block(inode_ref,
1224 old_blocks_count - i);
1225 if (rc != EOK)
1226 return rc;
1227 }
1228 }
1229
1230 /* Update i-node */
1231 ext4_inode_set_size(inode_ref->inode, new_size);
1232 inode_ref->dirty = true;
1233
1234 return EOK;
1235}
1236
1237/** Get physical block address by logical index of the block.
1238 *
1239 * @param inode_ref I-node to read block address from
1240 * @param iblock Logical index of block
1241 * @param fblock Output pointer for return physical block address
1242 *
1243 * @return Error code
1244 *
1245 */
1246errno_t ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
1247 aoff64_t iblock, uint32_t *fblock)
1248{
1249 ext4_filesystem_t *fs = inode_ref->fs;
1250
1251 /* For empty file is situation simple */
1252 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
1253 *fblock = 0;
1254 return EOK;
1255 }
1256
1257 uint32_t current_block;
1258
1259 /* Handle i-node using extents */
1260 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1261 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1262 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1263 errno_t rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
1264 if (rc != EOK)
1265 return rc;
1266
1267 *fblock = current_block;
1268 return EOK;
1269 }
1270
1271 ext4_inode_t *inode = inode_ref->inode;
1272
1273 /* Direct block are read directly from array in i-node structure */
1274 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1275 current_block = ext4_inode_get_direct_block(inode, (uint32_t) iblock);
1276 *fblock = current_block;
1277 return EOK;
1278 }
1279
1280 /* Determine indirection level of the target block */
1281 unsigned int level = 0;
1282 for (unsigned int i = 1; i < 4; i++) {
1283 if (iblock < fs->inode_block_limits[i]) {
1284 level = i;
1285 break;
1286 }
1287 }
1288
1289 if (level == 0)
1290 return EIO;
1291
1292 /* Compute offsets for the topmost level */
1293 aoff64_t block_offset_in_level =
1294 iblock - fs->inode_block_limits[level - 1];
1295 current_block = ext4_inode_get_indirect_block(inode, level - 1);
1296 uint32_t offset_in_block =
1297 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1298
1299 /* Sparse file */
1300 if (current_block == 0) {
1301 *fblock = 0;
1302 return EOK;
1303 }
1304
1305 block_t *block;
1306
1307 /*
1308 * Navigate through other levels, until we find the block number
1309 * or find null reference meaning we are dealing with sparse file
1310 */
1311 while (level > 0) {
1312 /* Load indirect block */
1313 errno_t rc = block_get(&block, fs->device, current_block, 0);
1314 if (rc != EOK)
1315 return rc;
1316
1317 /* Read block address from indirect block */
1318 current_block =
1319 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1320
1321 /* Put back indirect block untouched */
1322 rc = block_put(block);
1323 if (rc != EOK)
1324 return rc;
1325
1326 /* Check for sparse file */
1327 if (current_block == 0) {
1328 *fblock = 0;
1329 return EOK;
1330 }
1331
1332 /* Jump to the next level */
1333 level--;
1334
1335 /* Termination condition - we have address of data block loaded */
1336 if (level == 0)
1337 break;
1338
1339 /* Visit the next level */
1340 block_offset_in_level %= fs->inode_blocks_per_level[level];
1341 offset_in_block =
1342 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1343 }
1344
1345 *fblock = current_block;
1346
1347 return EOK;
1348}
1349
1350/** Set physical block address for the block logical address into the i-node.
1351 *
1352 * @param inode_ref I-node to set block address to
1353 * @param iblock Logical index of block
1354 * @param fblock Physical block address
1355 *
1356 * @return Error code
1357 *
1358 */
1359errno_t ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
1360 aoff64_t iblock, uint32_t fblock)
1361{
1362 ext4_filesystem_t *fs = inode_ref->fs;
1363
1364 /* Handle inode using extents */
1365 if ((ext4_superblock_has_feature_compatible(fs->superblock,
1366 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1367 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1368 /* Not reachable */
1369 return ENOTSUP;
1370 }
1371
1372 /* Handle simple case when we are dealing with direct reference */
1373 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1374 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t) iblock, fblock);
1375 inode_ref->dirty = true;
1376
1377 return EOK;
1378 }
1379
1380 /* Determine the indirection level needed to get the desired block */
1381 unsigned int level = 0;
1382 for (unsigned int i = 1; i < 4; i++) {
1383 if (iblock < fs->inode_block_limits[i]) {
1384 level = i;
1385 break;
1386 }
1387 }
1388
1389 if (level == 0)
1390 return EIO;
1391
1392 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1393
1394 /* Compute offsets for the topmost level */
1395 aoff64_t block_offset_in_level =
1396 iblock - fs->inode_block_limits[level - 1];
1397 uint32_t current_block =
1398 ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1399 uint32_t offset_in_block =
1400 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1401
1402 uint32_t new_block_addr;
1403 block_t *block;
1404 block_t *new_block;
1405
1406 /* Is needed to allocate indirect block on the i-node level */
1407 if (current_block == 0) {
1408 /* Allocate new indirect block */
1409 errno_t rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1410 if (rc != EOK)
1411 return rc;
1412
1413 /* Update i-node */
1414 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1415 new_block_addr);
1416 inode_ref->dirty = true;
1417
1418 /* Load newly allocated block */
1419 rc = block_get(&new_block, fs->device, new_block_addr,
1420 BLOCK_FLAGS_NOREAD);
1421 if (rc != EOK) {
1422 ext4_balloc_free_block(inode_ref, new_block_addr);
1423 return rc;
1424 }
1425
1426 /* Initialize new block */
1427 memset(new_block->data, 0, block_size);
1428 new_block->dirty = true;
1429
1430 /* Put back the allocated block */
1431 rc = block_put(new_block);
1432 if (rc != EOK)
1433 return rc;
1434
1435 current_block = new_block_addr;
1436 }
1437
1438 /*
1439 * Navigate through other levels, until we find the block number
1440 * or find null reference meaning we are dealing with sparse file
1441 */
1442 while (level > 0) {
1443 errno_t rc = block_get(&block, fs->device, current_block, 0);
1444 if (rc != EOK)
1445 return rc;
1446
1447 current_block =
1448 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1449
1450 if ((level > 1) && (current_block == 0)) {
1451 /* Allocate new block */
1452 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1453 if (rc != EOK) {
1454 block_put(block);
1455 return rc;
1456 }
1457
1458 /* Load newly allocated block */
1459 rc = block_get(&new_block, fs->device, new_block_addr,
1460 BLOCK_FLAGS_NOREAD);
1461 if (rc != EOK) {
1462 block_put(block);
1463 return rc;
1464 }
1465
1466 /* Initialize allocated block */
1467 memset(new_block->data, 0, block_size);
1468 new_block->dirty = true;
1469
1470 rc = block_put(new_block);
1471 if (rc != EOK) {
1472 block_put(block);
1473 return rc;
1474 }
1475
1476 /* Write block address to the parent */
1477 ((uint32_t *) block->data)[offset_in_block] =
1478 host2uint32_t_le(new_block_addr);
1479 block->dirty = true;
1480 current_block = new_block_addr;
1481 }
1482
1483 /* Will be finished, write the fblock address */
1484 if (level == 1) {
1485 ((uint32_t *) block->data)[offset_in_block] =
1486 host2uint32_t_le(fblock);
1487 block->dirty = true;
1488 }
1489
1490 rc = block_put(block);
1491 if (rc != EOK)
1492 return rc;
1493
1494 level--;
1495
1496 /*
1497 * If we are on the last level, break here as
1498 * there is no next level to visit
1499 */
1500 if (level == 0)
1501 break;
1502
1503 /* Visit the next level */
1504 block_offset_in_level %= fs->inode_blocks_per_level[level];
1505 offset_in_block =
1506 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1507 }
1508
1509 return EOK;
1510}
1511
1512/** Release data block from i-node
1513 *
1514 * @param inode_ref I-node to release block from
1515 * @param iblock Logical block to be released
1516 *
1517 * @return Error code
1518 *
1519 */
1520errno_t ext4_filesystem_release_inode_block(ext4_inode_ref_t *inode_ref,
1521 uint32_t iblock)
1522{
1523 uint32_t fblock;
1524
1525 ext4_filesystem_t *fs = inode_ref->fs;
1526
1527 /* Extents are handled otherwise = there is not support in this function */
1528 assert(!(ext4_superblock_has_feature_incompatible(fs->superblock,
1529 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1530 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1531
1532 ext4_inode_t *inode = inode_ref->inode;
1533
1534 /* Handle simple case when we are dealing with direct reference */
1535 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1536 fblock = ext4_inode_get_direct_block(inode, iblock);
1537
1538 /* Sparse file */
1539 if (fblock == 0)
1540 return EOK;
1541
1542 ext4_inode_set_direct_block(inode, iblock, 0);
1543 return ext4_balloc_free_block(inode_ref, fblock);
1544 }
1545
1546 /* Determine the indirection level needed to get the desired block */
1547 unsigned int level = 0;
1548 for (unsigned int i = 1; i < 4; i++) {
1549 if (iblock < fs->inode_block_limits[i]) {
1550 level = i;
1551 break;
1552 }
1553 }
1554
1555 if (level == 0)
1556 return EIO;
1557
1558 /* Compute offsets for the topmost level */
1559 aoff64_t block_offset_in_level =
1560 iblock - fs->inode_block_limits[level - 1];
1561 uint32_t current_block =
1562 ext4_inode_get_indirect_block(inode, level - 1);
1563 uint32_t offset_in_block =
1564 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1565
1566 /*
1567 * Navigate through other levels, until we find the block number
1568 * or find null reference meaning we are dealing with sparse file
1569 */
1570 block_t *block;
1571 while (level > 0) {
1572
1573 /* Sparse check */
1574 if (current_block == 0)
1575 return EOK;
1576
1577 errno_t rc = block_get(&block, fs->device, current_block, 0);
1578 if (rc != EOK)
1579 return rc;
1580
1581 current_block =
1582 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1583
1584 /* Set zero if physical data block address found */
1585 if (level == 1) {
1586 ((uint32_t *) block->data)[offset_in_block] =
1587 host2uint32_t_le(0);
1588 block->dirty = true;
1589 }
1590
1591 rc = block_put(block);
1592 if (rc != EOK)
1593 return rc;
1594
1595 level--;
1596
1597 /*
1598 * If we are on the last level, break here as
1599 * there is no next level to visit
1600 */
1601 if (level == 0)
1602 break;
1603
1604 /* Visit the next level */
1605 block_offset_in_level %= fs->inode_blocks_per_level[level];
1606 offset_in_block =
1607 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1608 }
1609
1610 fblock = current_block;
1611 if (fblock == 0)
1612 return EOK;
1613
1614 /* Physical block is not referenced, it can be released */
1615 return ext4_balloc_free_block(inode_ref, fblock);
1616}
1617
1618/** Append following logical block to the i-node.
1619 *
1620 * @param inode_ref I-node to append block to
1621 * @param fblock Output physical block address of newly allocated block
1622 * @param iblock Output logical number of newly allocated block
1623 *
1624 * @return Error code
1625 *
1626 */
1627errno_t ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1628 uint32_t *fblock, uint32_t *iblock)
1629{
1630 /* Handle extents separately */
1631 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1632 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1633 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)))
1634 return ext4_extent_append_block(inode_ref, iblock, fblock, true);
1635
1636 ext4_superblock_t *sb = inode_ref->fs->superblock;
1637
1638 /* Compute next block index and allocate data block */
1639 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1640 uint32_t block_size = ext4_superblock_get_block_size(sb);
1641
1642 /* Align size i-node size */
1643 if ((inode_size % block_size) != 0)
1644 inode_size += block_size - (inode_size % block_size);
1645
1646 /* Logical blocks are numbered from 0 */
1647 uint32_t new_block_idx = inode_size / block_size;
1648
1649 /* Allocate new physical block */
1650 uint32_t phys_block;
1651 errno_t rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1652 if (rc != EOK)
1653 return rc;
1654
1655 /* Add physical block address to the i-node */
1656 rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
1657 new_block_idx, phys_block);
1658 if (rc != EOK) {
1659 ext4_balloc_free_block(inode_ref, phys_block);
1660 return rc;
1661 }
1662
1663 /* Update i-node */
1664 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1665 inode_ref->dirty = true;
1666
1667 *fblock = phys_block;
1668 *iblock = new_block_idx;
1669
1670 return EOK;
1671}
1672
1673/**
1674 * @}
1675 */
Note: See TracBrowser for help on using the repository browser.