source: mainline/uspace/lib/ext4/libext4_filesystem.c@ d0c9b4b

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

filesystem code comments

  • Property mode set to 100644
File size: 31.5 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{
51 int rc;
52
53 fs->device = service_id;
54
55 // Initialize block library (4096 is size of communication channel)
56 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
57 if (rc != EOK) {
58 return rc;
59 }
60
61 // Read superblock from device to memory
62 ext4_superblock_t *temp_superblock;
63 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
64 if (rc != EOK) {
65 block_fini(fs->device);
66 return rc;
67 }
68
69 // Read block size from superblock and check
70 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
71 if (block_size > EXT4_MAX_BLOCK_SIZE) {
72 block_fini(fs->device);
73 return ENOTSUP;
74 }
75
76 // Initialize block caching by libblock
77 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
78 if (rc != EOK) {
79 block_fini(fs->device);
80 return rc;
81 }
82
83 // Compute limits for indirect block levels
84 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
85 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
86 fs->inode_blocks_per_level[0] = 1;
87 for (int i = 1; i < 4; i++) {
88 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
89 block_ids_per_block;
90 fs->inode_block_limits[i] = fs->inode_block_limits[i-1] +
91 fs->inode_blocks_per_level[i];
92 }
93
94 // Return loaded superblock
95 fs->superblock = temp_superblock;
96
97 return EOK;
98}
99
100/** Destroy filesystem instance (used by unmount operation).
101 *
102 * @param fs filesystem to be destroyed
103 * @param write_sb flag if superblock should be written to device
104 * @return error code
105 */
106int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
107{
108 int rc = EOK;
109
110 // If needed, write the superblock to the device
111 if (write_sb) {
112 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
113 }
114
115 // Release memory space for superblock
116 free(fs->superblock);
117
118 // Finish work with block library
119 block_fini(fs->device);
120
121 return rc;
122}
123
124/** Check sanity of the filesystem.
125 *
126 * Main is the check of the superblock structure.
127 *
128 * @param fs filesystem to be checked
129 * @return error code
130 */
131int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
132{
133 int rc;
134
135 // Check superblock
136 rc = ext4_superblock_check_sanity(fs->superblock);
137 if (rc != EOK) {
138 return rc;
139 }
140
141 return EOK;
142}
143
144/** Check filesystem's features, if supported by this driver
145 *
146 * Function can return EOK and set read_only flag. It mean's that
147 * there are some not-supported features, that can cause problems
148 * during some write operations.
149 *
150 * @param fs filesystem to be checked
151 * @param read_only flag if filesystem should be mounted only for reading
152 * @return error code
153 */
154int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *read_only)
155{
156 // Feature flags are present only in higher revisions
157 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
158 *read_only = false;
159 return EOK;
160 }
161
162 // Check incompatible features - if filesystem has some,
163 // volume can't be mounted
164 uint32_t incompatible_features;
165 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
166 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
167 if (incompatible_features > 0) {
168 return ENOTSUP;
169 }
170
171 // Check read-only features, if filesystem has some,
172 // volume can be mount only in read-only mode
173 uint32_t compatible_read_only;
174 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
175 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
176 if (compatible_read_only > 0) {
177 *read_only = true;
178 return EOK;
179 }
180
181 return EOK;
182}
183
184/** Get reference to block group specified by index.
185 *
186 * @param fs filesystem to find block group on
187 * @param bgid index of block group to load
188 * @param ref output pointer for reference
189 * @return error code
190 */
191int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
192 ext4_block_group_ref_t **ref)
193{
194 int rc;
195
196 // Allocate memory for new structure
197 ext4_block_group_ref_t *newref = malloc(sizeof(ext4_block_group_ref_t));
198 if (newref == NULL) {
199 return ENOMEM;
200 }
201
202 // Compute number of descriptors, that fits in one data block
203 uint32_t descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
204 / ext4_superblock_get_desc_size(fs->superblock);
205
206 // Block group descriptor table starts at the next block after superblock
207 aoff64_t block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
208
209 // Find the block containing the descriptor we are looking for
210 block_id += bgid / descriptors_per_block;
211 uint32_t offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
212
213 // Load block with descriptors
214 rc = block_get(&newref->block, fs->device, block_id, 0);
215 if (rc != EOK) {
216 free(newref);
217 return rc;
218 }
219
220 // Inititialize in-memory representation
221 newref->block_group = newref->block->data + offset;
222 newref->fs = fs;
223 newref->index = bgid;
224 newref->dirty = false;
225
226 *ref = newref;
227
228 return EOK;
229}
230
231/** Compute checksum of block group descriptor.
232 *
233 * It uses crc functions from Linux kernel implementation.
234 *
235 * @param sb superblock
236 * @param bgid index of block group in the filesystem
237 * @param bg block group to compute checksum for
238 * @return checksum value
239 */
240static uint16_t ext4_filesystem_bg_checksum(ext4_superblock_t *sb, uint32_t bgid,
241 ext4_block_group_t *bg)
242{
243 // If checksum not supported, 0 will be returned
244 uint16_t crc = 0;
245
246 // Compute the checksum only if the filesystem supports it
247 if (ext4_superblock_has_feature_read_only(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
248
249 void *base = bg;
250 void *checksum = &bg->checksum;
251
252 uint32_t offset = (uint32_t)(checksum - base);
253
254 // Convert block group index to little endian
255 uint32_t le_group = host2uint32_t_le(bgid);
256
257 // Initialization
258 crc = crc16(~0, sb->uuid, sizeof(sb->uuid));
259
260 // Include index of block group
261 crc = crc16(crc, (uint8_t *)&le_group, sizeof(le_group));
262
263 // Compute crc from the first part (stop before checksum field)
264 crc = crc16(crc, (uint8_t *)bg, offset);
265
266 // Skip checksum
267 offset += sizeof(bg->checksum);
268
269 // Checksum of the rest of block group descriptor
270 if ((ext4_superblock_has_feature_incompatible(sb, EXT4_FEATURE_INCOMPAT_64BIT)) &&
271 offset < ext4_superblock_get_desc_size(sb)) {
272
273 crc = crc16(crc, ((uint8_t *)bg) + offset, ext4_superblock_get_desc_size(sb) - offset);
274 }
275 }
276
277 return crc;
278
279}
280
281/** Put reference to block group.
282 *
283 * @oaram ref pointer for reference to be put back
284 * @return error code
285 */
286int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
287{
288 int rc;
289
290 // Check if reference modified
291 if (ref->dirty) {
292
293 // Compute new checksum of block group
294 uint16_t checksum = ext4_filesystem_bg_checksum(
295 ref->fs->superblock, ref->index, ref->block_group);
296 ext4_block_group_set_checksum(ref->block_group, checksum);
297
298 // Mark block dirty for writing changes to physical device
299 ref->block->dirty = true;
300 }
301
302 // Put back block, that contains block group descriptor
303 rc = block_put(ref->block);
304 free(ref);
305
306 return rc;
307}
308
309/** Get reference to i-node specified by index.
310 *
311 * @param fs filesystem to find i-node on
312 * @param index index of i-node to load
313 * @oaram ref output pointer for reference
314 * @return error code
315 */
316int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
317 ext4_inode_ref_t **ref)
318{
319 int rc;
320
321 // Allocate memory for new structure
322 ext4_inode_ref_t *newref = malloc(sizeof(ext4_inode_ref_t));
323 if (newref == NULL) {
324 return ENOMEM;
325 }
326
327 // Compute number of i-nodes, that fits in one data block
328 uint32_t inodes_per_group =
329 ext4_superblock_get_inodes_per_group(fs->superblock);
330
331 /*
332 * inode numbers are 1-based, but it is simpler to work with 0-based
333 * when computing indices
334 */
335 index -= 1;
336 uint32_t block_group = index / inodes_per_group;
337 uint32_t offset_in_group = index % inodes_per_group;
338
339 // Load block group, where i-node is located
340 ext4_block_group_ref_t *bg_ref;
341 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
342 if (rc != EOK) {
343 free(newref);
344 return rc;
345 }
346
347 // Load block address, where i-node table is located
348 uint32_t inode_table_start = ext4_block_group_get_inode_table_first_block(
349 bg_ref->block_group, fs->superblock);
350
351 // Put back block group reference (not needed more)
352 rc = ext4_filesystem_put_block_group_ref(bg_ref);
353 if (rc != EOK) {
354 free(newref);
355 return rc;
356 }
357
358 // Compute position of i-node in the block group
359 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
360 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
361 uint32_t byte_offset_in_group = offset_in_group * inode_size;
362
363 // Compute block address
364 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
365 rc = block_get(&newref->block, fs->device, block_id, 0);
366 if (rc != EOK) {
367 free(newref);
368 return rc;
369 }
370
371 // Compute position of i-node in the data block
372 uint32_t offset_in_block = byte_offset_in_group % block_size;
373 newref->inode = newref->block->data + offset_in_block;
374
375 // We need to store the original value of index in the reference
376 newref->index = index + 1;
377 newref->fs = fs;
378 newref->dirty = false;
379
380 *ref = newref;
381
382 return EOK;
383}
384
385/** Put reference to i-node.
386 *
387 * @param ref pointer for reference to be put back
388 * @return error code
389 */
390int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
391{
392 int rc;
393
394 // Check if reference modified
395 if (ref->dirty) {
396
397 // Mark block dirty for writing changes to physical device
398 ref->block->dirty = true;
399 }
400
401 // Put back block, that contains i-node
402 rc = block_put(ref->block);
403 free(ref);
404
405 return rc;
406}
407
408/** Allocate new i-node in the filesystem.
409 *
410 * @param fs filesystem to allocated i-node on
411 * @param inode_ref output pointer to return reference to allocated i-node
412 * @param flags flags to be set for newly created i-node
413 * @return error code
414 */
415int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
416 ext4_inode_ref_t **inode_ref, int flags)
417{
418 int rc;
419
420 // Check if newly allocated i-node will be a directory
421 bool is_dir = false;
422 if (flags & L_DIRECTORY) {
423 is_dir = true;
424 }
425
426 // Allocate inode by allocation algorithm
427 uint32_t index;
428 rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
429 if (rc != EOK) {
430 return rc;
431 }
432
433 // Load i-node from on-disk i-node table
434 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
435 if (rc != EOK) {
436 ext4_ialloc_free_inode(fs, index, is_dir);
437 return rc;
438 }
439
440 // Initialize i-node
441 ext4_inode_t *inode = (*inode_ref)->inode;
442
443 if (is_dir) {
444 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_DIRECTORY);
445 ext4_inode_set_links_count(inode, 1); // '.' entry
446 } else {
447 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_FILE);
448 ext4_inode_set_links_count(inode, 0);
449 }
450
451 ext4_inode_set_uid(inode, 0);
452 ext4_inode_set_gid(inode, 0);
453 ext4_inode_set_size(inode, 0);
454 ext4_inode_set_access_time(inode, 0);
455 ext4_inode_set_change_inode_time(inode, 0);
456 ext4_inode_set_modification_time(inode, 0);
457 ext4_inode_set_deletion_time(inode, 0);
458 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
459 ext4_inode_set_flags(inode, 0);
460 ext4_inode_set_generation(inode, 0);
461
462 // Reset blocks array
463 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++) {
464 inode->blocks[i] = 0;
465 }
466
467 // Initialize extents if needed
468 if (ext4_superblock_has_feature_incompatible(
469 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
470
471 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
472
473 // Initialize extent root header
474 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
475 ext4_extent_header_set_depth(header, 0);
476 ext4_extent_header_set_entries_count(header, 0);
477 ext4_extent_header_set_generation(header, 0);
478 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
479
480 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof (uint32_t) - sizeof(ext4_extent_header_t))
481 / sizeof(ext4_extent_t);
482
483 ext4_extent_header_set_max_entries_count(header, max_entries);
484 }
485
486 (*inode_ref)->dirty = true;
487
488 return EOK;
489}
490
491/** Release i-node and mark it as free.
492 *
493 * @param inode_ref i-node to be released
494 * @return error code
495 */
496int ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
497{
498 int rc;
499
500 ext4_filesystem_t *fs = inode_ref->fs;
501
502 // For extents must be data block destroyed by other way
503 if (ext4_superblock_has_feature_incompatible(
504 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
505 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
506
507 // Data structures are released during truncate operation...
508 goto finish;
509 }
510
511 // Release all indirect (no data) blocks
512
513 // 1) Single indirect
514 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
515 if (fblock != 0) {
516 rc = ext4_balloc_free_block(inode_ref, fblock);
517 if (rc != EOK) {
518 return rc;
519 }
520
521 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
522 }
523
524 block_t *block;
525 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
526 uint32_t count = block_size / sizeof(uint32_t);
527
528 // 2) Double indirect
529 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
530 if (fblock != 0) {
531 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
532 if (rc != EOK) {
533 return rc;
534 }
535
536 uint32_t ind_block;
537 for (uint32_t offset = 0; offset < count; ++offset) {
538 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
539
540 if (ind_block != 0) {
541 rc = ext4_balloc_free_block(inode_ref, ind_block);
542 if (rc != EOK) {
543 block_put(block);
544 return rc;
545 }
546 }
547 }
548
549 block_put(block);
550 rc = ext4_balloc_free_block(inode_ref, fblock);
551 if (rc != EOK) {
552 return rc;
553 }
554
555 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
556 }
557
558
559 // 3) Tripple indirect
560 block_t *subblock;
561 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
562 if (fblock != 0) {
563 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
564 if (rc != EOK) {
565 return rc;
566 }
567
568 uint32_t ind_block;
569 for (uint32_t offset = 0; offset < count; ++offset) {
570 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
571
572 if (ind_block != 0) {
573 rc = block_get(&subblock, fs->device, ind_block, BLOCK_FLAGS_NONE);
574 if (rc != EOK) {
575 block_put(block);
576 return rc;
577 }
578
579 uint32_t ind_subblock;
580 for (uint32_t suboffset = 0; suboffset < count; ++suboffset) {
581 ind_subblock = uint32_t_le2host(((uint32_t*)subblock->data)[suboffset]);
582
583 if (ind_subblock != 0) {
584 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
585 if (rc != EOK) {
586 block_put(subblock);
587 block_put(block);
588 return rc;
589 }
590 }
591
592 }
593 block_put(subblock);
594
595 }
596
597 rc = ext4_balloc_free_block(inode_ref, ind_block);
598 if (rc != EOK) {
599 block_put(block);
600 return rc;
601 }
602
603
604 }
605
606 block_put(block);
607 rc = ext4_balloc_free_block(inode_ref, fblock);
608 if (rc != EOK) {
609 return rc;
610 }
611
612 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
613 }
614
615finish:
616
617 // Mark inode dirty for writing to the physical device
618 inode_ref->dirty = true;
619
620 // Free inode by allocator
621 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
622 EXT4_INODE_MODE_DIRECTORY)) {
623 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
624 } else {
625 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
626 }
627 if (rc != EOK) {
628 return rc;
629 }
630
631 return EOK;
632}
633
634/** Truncate i-node data blocks.
635 *
636 * @param inode_ref i-node to be truncated
637 * @param new_size new size of inode (must be < current size)
638 * @return error code
639 */
640int ext4_filesystem_truncate_inode(
641 ext4_inode_ref_t *inode_ref, aoff64_t new_size)
642{
643 int rc;
644
645 ext4_superblock_t *sb = inode_ref->fs->superblock;
646
647 // Check flags, if i-node can be truncated
648 if (! ext4_inode_can_truncate(sb, inode_ref->inode)) {
649 return EINVAL;
650 }
651
652 // If sizes are equal, nothing has to be done.
653 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
654 if (old_size == new_size) {
655 return EOK;
656 }
657
658 // It's not suppported to make the larger file by truncate operation
659 if (old_size < new_size) {
660 return EINVAL;
661 }
662
663 // Compute how many blocks will be released
664 aoff64_t size_diff = old_size - new_size;
665 uint32_t block_size = ext4_superblock_get_block_size(sb);
666 uint32_t diff_blocks_count = size_diff / block_size;
667 if (size_diff % block_size != 0) {
668 diff_blocks_count++;
669 }
670
671 uint32_t old_blocks_count = old_size / block_size;
672 if (old_size % block_size != 0) {
673 old_blocks_count++;
674 }
675
676 if (ext4_superblock_has_feature_incompatible(
677 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
678 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
679
680 // Extents require special operation
681
682 rc = ext4_extent_release_blocks_from(inode_ref,
683 old_blocks_count - diff_blocks_count);
684 if (rc != EOK) {
685 return rc;
686 }
687 } else {
688
689 // Release data blocks from the end of file
690
691 // Starting from 1 because of logical blocks are numbered from 0
692 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
693 rc = ext4_filesystem_release_inode_block(inode_ref, old_blocks_count - i);
694 if (rc != EOK) {
695 return rc;
696 }
697 }
698 }
699
700 // Update i-node
701 ext4_inode_set_size(inode_ref->inode, new_size);
702 inode_ref->dirty = true;
703
704 return EOK;
705}
706
707/** Get physical block address by logical index of the block.
708 *
709 * @param inode_ref i-node to read block address from
710 * @param iblock logical index of block
711 * @param fblock output pointer for return physical block address
712 * @return error code
713 */
714int ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
715 aoff64_t iblock, uint32_t *fblock)
716{
717 int rc;
718
719 ext4_filesystem_t *fs = inode_ref->fs;
720
721 // For empty file is situation simple
722 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
723 *fblock = 0;
724 return EOK;
725 }
726
727 uint32_t current_block;
728
729 // Handle i-node using extents
730 if (ext4_superblock_has_feature_incompatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
731 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
732
733 rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
734
735 if (rc != EOK) {
736 return rc;
737 }
738
739 *fblock = current_block;
740 return EOK;
741
742 }
743
744 ext4_inode_t *inode = inode_ref->inode;
745
746 // Direct block are read directly from array in i-node structure
747 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
748 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
749 *fblock = current_block;
750 return EOK;
751 }
752
753 // Determine indirection level of the target block
754 int level = -1;
755 for (int i = 1; i < 4; i++) {
756 if (iblock < fs->inode_block_limits[i]) {
757 level = i;
758 break;
759 }
760 }
761
762 if (level == -1) {
763 return EIO;
764 }
765
766 // Compute offsets for the topmost level
767 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
768 current_block = ext4_inode_get_indirect_block(inode, level-1);
769 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
770
771 // Sparse file
772 if (current_block == 0) {
773 *fblock = 0;
774 return EOK;
775 }
776
777 block_t *block;
778
779 /* Navigate through other levels, until we find the block number
780 * or find null reference meaning we are dealing with sparse file
781 */
782 while (level > 0) {
783
784 // Load indirect block
785 rc = block_get(&block, fs->device, current_block, 0);
786 if (rc != EOK) {
787 return rc;
788 }
789
790 // Read block address from indirect block
791 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
792
793 // Put back indirect block untouched
794 rc = block_put(block);
795 if (rc != EOK) {
796 return rc;
797 }
798
799 // Check for sparse file
800 if (current_block == 0) {
801 *fblock = 0;
802 return EOK;
803 }
804
805 // Jump to the next level
806 level -= 1;
807
808 // Termination condition - we have address of data block loaded
809 if (level == 0) {
810 break;
811 }
812
813 // Visit the next level
814 block_offset_in_level %= fs->inode_blocks_per_level[level];
815 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
816 }
817
818 *fblock = current_block;
819
820 return EOK;
821}
822
823/** Set physical block address for the block logical address into the i-node.
824 *
825 * @param inode_ref i-node to set block address to
826 * @param iblock logical index of block
827 * @param fblock physical block address
828 * @return error code
829 */
830int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
831 aoff64_t iblock, uint32_t fblock)
832{
833 int rc;
834
835 ext4_filesystem_t *fs = inode_ref->fs;
836
837 // Handle inode using extents
838 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
839 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
840 // not reachable !!!
841 return ENOTSUP;
842 }
843
844 // Handle simple case when we are dealing with direct reference
845 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
846 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
847 inode_ref->dirty = true;
848 return EOK;
849 }
850
851 // Determine the indirection level needed to get the desired block
852 int level = -1;
853 for (int i = 1; i < 4; i++) {
854 if (iblock < fs->inode_block_limits[i]) {
855 level = i;
856 break;
857 }
858 }
859
860 if (level == -1) {
861 return EIO;
862 }
863
864 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
865
866 // Compute offsets for the topmost level
867 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
868 uint32_t current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
869 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
870
871 uint32_t new_block_addr;
872 block_t *block, *new_block;
873
874 // Is needed to allocate indirect block on the i-node level
875 if (current_block == 0) {
876
877 // Allocate new indirect block
878 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
879 if (rc != EOK) {
880 return rc;
881 }
882
883 // Update i-node
884 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
885 inode_ref->dirty = true;
886
887 // Load newly allocated block
888 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
889 if (rc != EOK) {
890 ext4_balloc_free_block(inode_ref, new_block_addr);
891 return rc;
892 }
893
894 // Initialize new block
895 memset(new_block->data, 0, block_size);
896 new_block->dirty = true;
897
898 // Put back the allocated block
899 rc = block_put(new_block);
900 if (rc != EOK) {
901 return rc;
902 }
903
904 current_block = new_block_addr;
905 }
906
907 /* Navigate through other levels, until we find the block number
908 * or find null reference meaning we are dealing with sparse file
909 */
910 while (level > 0) {
911
912 rc = block_get(&block, fs->device, current_block, 0);
913 if (rc != EOK) {
914 return rc;
915 }
916
917 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
918
919 if ((level > 1) && (current_block == 0)) {
920
921 // Allocate new block
922 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
923 if (rc != EOK) {
924 block_put(block);
925 return rc;
926 }
927
928 // Load newly allocated block
929 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
930 if (rc != EOK) {
931 block_put(block);
932 return rc;
933 }
934
935 // Initialize allocated block
936 memset(new_block->data, 0, block_size);
937 new_block->dirty = true;
938
939 rc = block_put(new_block);
940 if (rc != EOK) {
941 block_put(block);
942 return rc;
943 }
944
945 // Write block address to the parent
946 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
947 block->dirty = true;
948 current_block = new_block_addr;
949 }
950
951 // Will be finished, write the fblock address
952 if (level == 1) {
953 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
954 block->dirty = true;
955 }
956
957 rc = block_put(block);
958 if (rc != EOK) {
959 return rc;
960 }
961
962 level -= 1;
963
964 /* If we are on the last level, break here as
965 * there is no next level to visit
966 */
967 if (level == 0) {
968 break;
969 }
970
971 /* Visit the next level */
972 block_offset_in_level %= fs->inode_blocks_per_level[level];
973 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
974 }
975
976 return EOK;
977}
978
979/** Release data block from i-node
980 *
981 * @param inode_ref i-node to release block from
982 * @param iblock logical block to be released
983 * @return error code
984 */
985int ext4_filesystem_release_inode_block(
986 ext4_inode_ref_t *inode_ref, uint32_t iblock)
987{
988 int rc;
989
990 uint32_t fblock;
991
992 ext4_filesystem_t *fs = inode_ref->fs;
993
994 // EXTENTS are handled otherwise = there is not support in this function
995 assert(! (ext4_superblock_has_feature_incompatible(fs->superblock,
996 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
997 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)));
998
999 ext4_inode_t *inode = inode_ref->inode;
1000
1001 // Handle simple case when we are dealing with direct reference
1002 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1003 fblock = ext4_inode_get_direct_block(inode, iblock);
1004 // Sparse file
1005 if (fblock == 0) {
1006 return EOK;
1007 }
1008
1009 ext4_inode_set_direct_block(inode, iblock, 0);
1010 return ext4_balloc_free_block(inode_ref, fblock);
1011 }
1012
1013
1014 // Determine the indirection level needed to get the desired block
1015 int level = -1;
1016 for (int i = 1; i < 4; i++) {
1017 if (iblock < fs->inode_block_limits[i]) {
1018 level = i;
1019 break;
1020 }
1021 }
1022
1023 if (level == -1) {
1024 return EIO;
1025 }
1026
1027 // Compute offsets for the topmost level
1028 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
1029 uint32_t current_block = ext4_inode_get_indirect_block(inode, level-1);
1030 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
1031
1032 /* Navigate through other levels, until we find the block number
1033 * or find null reference meaning we are dealing with sparse file
1034 */
1035 block_t *block;
1036 while (level > 0) {
1037 rc = block_get(&block, fs->device, current_block, 0);
1038 if (rc != EOK) {
1039 return rc;
1040 }
1041
1042 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
1043
1044 // Set zero if physical data block address found
1045 if (level == 1) {
1046 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
1047 block->dirty = true;
1048 }
1049
1050 rc = block_put(block);
1051 if (rc != EOK) {
1052 return rc;
1053 }
1054
1055 level -= 1;
1056
1057 /* If we are on the last level, break here as
1058 * there is no next level to visit
1059 */
1060 if (level == 0) {
1061 break;
1062 }
1063
1064 /* Visit the next level */
1065 block_offset_in_level %= fs->inode_blocks_per_level[level];
1066 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
1067 }
1068
1069 fblock = current_block;
1070
1071 if (fblock == 0) {
1072 return EOK;
1073 }
1074
1075 // Physical block is not referenced, it can be released
1076
1077 return ext4_balloc_free_block(inode_ref, fblock);
1078
1079}
1080
1081/** Append following logical block to the i-node.
1082 *
1083 * @param inode_ref i-node to append block to
1084 * @param fblock output physical block address of newly allocated block
1085 * @param iblock output logical number of newly allocated block
1086 * @return error code
1087 */
1088int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1089 uint32_t *fblock, uint32_t *iblock)
1090{
1091 int rc;
1092
1093 EXT4FS_DBG("");
1094
1095 // Handle extents separately
1096 if (ext4_superblock_has_feature_incompatible(
1097 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1098 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
1099
1100 return ext4_extent_append_block(inode_ref, iblock, fblock);
1101
1102 }
1103
1104 ext4_superblock_t *sb = inode_ref->fs->superblock;
1105
1106 // Compute next block index and allocate data block
1107 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1108 uint32_t block_size = ext4_superblock_get_block_size(sb);
1109
1110 // Align size i-node size
1111 if ((inode_size % block_size) != 0) {
1112 inode_size += block_size - (inode_size % block_size);
1113 }
1114
1115 // Logical blocks are numbered from 0
1116 uint32_t new_block_idx = inode_size / block_size;
1117
1118 // Allocate new physical block
1119 uint32_t phys_block;
1120 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1121 if (rc != EOK) {
1122 return rc;
1123 }
1124
1125 // Add physical block address to the i-node
1126 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, new_block_idx, phys_block);
1127 if (rc != EOK) {
1128 ext4_balloc_free_block(inode_ref, phys_block);
1129 return rc;
1130 }
1131
1132 // Update i-node
1133 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1134 inode_ref->dirty = true;
1135
1136 *fblock = phys_block;
1137 *iblock = new_block_idx;
1138
1139 return EOK;
1140}
1141
1142/** Add orphaned i-node to the orphans linked list.
1143 *
1144 * @param inode_ref i-node to be added to orphans list
1145 * @return error code
1146 */
1147int ext4_filesystem_add_orphan(ext4_inode_ref_t *inode_ref)
1148{
1149 uint32_t next_orphan = ext4_superblock_get_last_orphan(
1150 inode_ref->fs->superblock);
1151
1152 // Deletion time is used for holding next item of the list
1153 ext4_inode_set_deletion_time(inode_ref->inode, next_orphan);
1154
1155 // Head of the list is in the superblock
1156 ext4_superblock_set_last_orphan(
1157 inode_ref->fs->superblock, inode_ref->index);
1158 inode_ref->dirty = true;
1159
1160 return EOK;
1161}
1162
1163/** Delete orphaned i-node from the orphans linked list.
1164 *
1165 * @param inode_ref i-node to be deleted from the orphans list
1166 * @return error code
1167 */
1168int ext4_filesystem_delete_orphan(ext4_inode_ref_t *inode_ref)
1169{
1170 int rc;
1171
1172 // Get head of the linked list
1173 uint32_t last_orphan = ext4_superblock_get_last_orphan(
1174 inode_ref->fs->superblock);
1175 assert(last_orphan > 0);
1176
1177 uint32_t next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1178
1179 // Check if the head is the target
1180 if (last_orphan == inode_ref->index) {
1181 ext4_superblock_set_last_orphan(inode_ref->fs->superblock, next_orphan);
1182 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1183 inode_ref->dirty = true;
1184 return EOK;
1185 }
1186
1187 ext4_inode_ref_t *current;
1188 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, last_orphan, &current);
1189 if (rc != EOK) {
1190 return rc;
1191 }
1192
1193 next_orphan = ext4_inode_get_deletion_time(current->inode);
1194
1195 bool found = false;
1196
1197 // Walk thourgh the linked list
1198 while (next_orphan != 0) {
1199
1200 // Found?
1201 if (next_orphan == inode_ref->index) {
1202 next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1203 ext4_inode_set_deletion_time(current->inode, next_orphan);
1204 current->dirty = true;
1205 found = true;
1206 break;
1207 }
1208
1209 ext4_filesystem_put_inode_ref(current);
1210
1211 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, next_orphan, &current);
1212 if (rc != EOK) {
1213 return rc;
1214 }
1215 next_orphan = ext4_inode_get_deletion_time(current->inode);
1216
1217 }
1218
1219 if (found) {
1220 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1221 }
1222
1223 rc = ext4_filesystem_put_inode_ref(current);
1224 if (rc != EOK) {
1225 return rc;
1226 }
1227
1228 if (!found) {
1229 return ENOENT;
1230 }
1231
1232 return EOK;
1233}
1234
1235/**
1236 * @}
1237 */
Note: See TracBrowser for help on using the repository browser.