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

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

Probing for ExFAT, Ext4 and CDFS.

  • Property mode set to 100644
File size: 43.6 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 int 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 int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id,
70 enum cache_mode cmode)
71{
72 int 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 negative error code.
164 *
165 */
166int ext4_filesystem_probe(service_id_t service_id)
167{
168 ext4_filesystem_t *fs = NULL;
169 int 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 */
197int 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 int 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 negative error code. On error the state of the file
255 * system is unchanged.
256 *
257 */
258int 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 int 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 int 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 int 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 int 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 int 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 int 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 int 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 int 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 */
546int 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 int 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 */
806int 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 int 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 */
836int 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 int 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 */
911int 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 int 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 */
935int 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 int 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 */
1027int 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 int 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 int 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 int 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 int 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 int 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 */
1181int 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 int 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 int 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 */
1246int 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 int 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 int 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 */
1359int 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 int 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 int 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 */
1520int 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 int 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 */
1627int 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 int 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.