source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 0b293a6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0b293a6 was 0b293a6, checked in by Frantisek Princ <frantisek.princ@…>, 13 years ago

added variable cache mode + missing cache fini

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