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

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

more comments, now missing very small number of comments

  • Property mode set to 100644
File size: 30.6 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/** TODO comment
824 *
825 */
826int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
827 aoff64_t iblock, uint32_t fblock)
828{
829 int rc;
830
831 ext4_filesystem_t *fs = inode_ref->fs;
832
833 /* Handle inode using extents */
834 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
835 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
836 // not reachable !!!
837 return ENOTSUP;
838 }
839
840 /* Handle simple case when we are dealing with direct reference */
841 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
842 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
843 inode_ref->dirty = true;
844 return EOK;
845 }
846
847 /* Determine the indirection level needed to get the desired block */
848 int level = -1;
849 for (int i = 1; i < 4; i++) {
850 if (iblock < fs->inode_block_limits[i]) {
851 level = i;
852 break;
853 }
854 }
855
856 if (level == -1) {
857 return EIO;
858 }
859
860 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
861
862 /* Compute offsets for the topmost level */
863 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
864 uint32_t current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
865 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
866
867 uint32_t new_block_addr;
868 block_t *block, *new_block;
869
870 if (current_block == 0) {
871 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
872 if (rc != EOK) {
873 return rc;
874 }
875
876 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
877
878 inode_ref->dirty = true;
879
880 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
881 if (rc != EOK) {
882 ext4_balloc_free_block(inode_ref, new_block_addr);
883 return rc;
884 }
885
886 memset(new_block->data, 0, block_size);
887 new_block->dirty = true;
888
889 rc = block_put(new_block);
890 if (rc != EOK) {
891 return rc;
892 }
893
894 current_block = new_block_addr;
895 }
896
897 /* Navigate through other levels, until we find the block number
898 * or find null reference meaning we are dealing with sparse file
899 */
900 while (level > 0) {
901
902 rc = block_get(&block, fs->device, current_block, 0);
903 if (rc != EOK) {
904 return rc;
905 }
906
907 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
908
909 if ((level > 1) && (current_block == 0)) {
910 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
911 if (rc != EOK) {
912 block_put(block);
913 return rc;
914 }
915
916 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
917 if (rc != EOK) {
918 block_put(block);
919 return rc;
920 }
921
922 memset(new_block->data, 0, block_size);
923 new_block->dirty = true;
924
925 rc = block_put(new_block);
926 if (rc != EOK) {
927 block_put(block);
928 return rc;
929 }
930
931 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
932 block->dirty = true;
933 current_block = new_block_addr;
934 }
935
936 if (level == 1) {
937 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
938 block->dirty = true;
939 }
940
941 rc = block_put(block);
942 if (rc != EOK) {
943 return rc;
944 }
945
946 level -= 1;
947
948 /* If we are on the last level, break here as
949 * there is no next level to visit
950 */
951 if (level == 0) {
952 break;
953 }
954
955 /* Visit the next level */
956 block_offset_in_level %= fs->inode_blocks_per_level[level];
957 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
958 }
959
960 return EOK;
961}
962
963/** TODO comment
964 *
965 */
966int ext4_filesystem_release_inode_block(
967 ext4_inode_ref_t *inode_ref, uint32_t iblock)
968{
969 int rc;
970
971 uint32_t fblock;
972
973 ext4_filesystem_t *fs = inode_ref->fs;
974
975 // EXTENTS are handled otherwise
976 assert(! (ext4_superblock_has_feature_incompatible(fs->superblock,
977 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
978 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)));
979
980 ext4_inode_t *inode = inode_ref->inode;
981
982 /* Handle simple case when we are dealing with direct reference */
983 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
984 fblock = ext4_inode_get_direct_block(inode, iblock);
985 // Sparse file
986 if (fblock == 0) {
987 return EOK;
988 }
989
990 ext4_inode_set_direct_block(inode, iblock, 0);
991 return ext4_balloc_free_block(inode_ref, fblock);
992 }
993
994
995 /* Determine the indirection level needed to get the desired block */
996 int level = -1;
997 for (int i = 1; i < 4; i++) {
998 if (iblock < fs->inode_block_limits[i]) {
999 level = i;
1000 break;
1001 }
1002 }
1003
1004 if (level == -1) {
1005 return EIO;
1006 }
1007
1008 /* Compute offsets for the topmost level */
1009 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
1010 uint32_t current_block = ext4_inode_get_indirect_block(inode, level-1);
1011 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
1012
1013 /* Navigate through other levels, until we find the block number
1014 * or find null reference meaning we are dealing with sparse file
1015 */
1016 block_t *block;
1017 while (level > 0) {
1018 rc = block_get(&block, fs->device, current_block, 0);
1019 if (rc != EOK) {
1020 return rc;
1021 }
1022
1023 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
1024
1025 // Set zero
1026 if (level == 1) {
1027 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
1028 block->dirty = true;
1029 }
1030
1031 rc = block_put(block);
1032 if (rc != EOK) {
1033 return rc;
1034 }
1035
1036 level -= 1;
1037
1038 /* If we are on the last level, break here as
1039 * there is no next level to visit
1040 */
1041 if (level == 0) {
1042 break;
1043 }
1044
1045 /* Visit the next level */
1046 block_offset_in_level %= fs->inode_blocks_per_level[level];
1047 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
1048 }
1049
1050 fblock = current_block;
1051
1052 if (fblock == 0) {
1053 return EOK;
1054 }
1055
1056 return ext4_balloc_free_block(inode_ref, fblock);
1057
1058}
1059
1060/** Append following logical block to the i-node.
1061 *
1062 * @param inode_ref i-node to append block to
1063 * @param fblock output physical block address of newly allocated block
1064 * @param iblock output logical number of newly allocated block
1065 * @return error code
1066 */
1067int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1068 uint32_t *fblock, uint32_t *iblock)
1069{
1070 int rc;
1071
1072 EXT4FS_DBG("");
1073
1074 // Handle extents separately
1075 if (ext4_superblock_has_feature_incompatible(
1076 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1077 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
1078
1079 return ext4_extent_append_block(inode_ref, iblock, fblock);
1080
1081 }
1082
1083 ext4_superblock_t *sb = inode_ref->fs->superblock;
1084
1085 // Compute next block index and allocate data block
1086 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1087 uint32_t block_size = ext4_superblock_get_block_size(sb);
1088
1089 // Align size i-node size
1090 if ((inode_size % block_size) != 0) {
1091 inode_size += block_size - (inode_size % block_size);
1092 }
1093
1094 // Logical blocks are numbered from 0
1095 uint32_t new_block_idx = inode_size / block_size;
1096
1097 // Allocate new physical block
1098 uint32_t phys_block;
1099 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1100 if (rc != EOK) {
1101 return rc;
1102 }
1103
1104 // Add physical block address to the i-node
1105 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, new_block_idx, phys_block);
1106 if (rc != EOK) {
1107 ext4_balloc_free_block(inode_ref, phys_block);
1108 return rc;
1109 }
1110
1111 // Update i-node
1112 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1113 inode_ref->dirty = true;
1114
1115 *fblock = phys_block;
1116 *iblock = new_block_idx;
1117
1118 return EOK;
1119}
1120
1121/** Add orphaned i-node to the orphans linked list.
1122 *
1123 * @param inode_ref i-node to be added to orphans list
1124 * @return error code
1125 */
1126int ext4_filesystem_add_orphan(ext4_inode_ref_t *inode_ref)
1127{
1128 uint32_t next_orphan = ext4_superblock_get_last_orphan(
1129 inode_ref->fs->superblock);
1130
1131 // Deletion time is used for holding next item of the list
1132 ext4_inode_set_deletion_time(inode_ref->inode, next_orphan);
1133
1134 // Head of the list is in the superblock
1135 ext4_superblock_set_last_orphan(
1136 inode_ref->fs->superblock, inode_ref->index);
1137 inode_ref->dirty = true;
1138
1139 return EOK;
1140}
1141
1142/** Delete orphaned i-node from the orphans linked list.
1143 *
1144 * @param inode_ref i-node to be deleted from the orphans list
1145 * @return error code
1146 */
1147int ext4_filesystem_delete_orphan(ext4_inode_ref_t *inode_ref)
1148{
1149 int rc;
1150
1151 // Get head of the linked list
1152 uint32_t last_orphan = ext4_superblock_get_last_orphan(
1153 inode_ref->fs->superblock);
1154 assert(last_orphan > 0);
1155
1156 uint32_t next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1157
1158 // Check if the head is the target
1159 if (last_orphan == inode_ref->index) {
1160 ext4_superblock_set_last_orphan(inode_ref->fs->superblock, next_orphan);
1161 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1162 inode_ref->dirty = true;
1163 return EOK;
1164 }
1165
1166 ext4_inode_ref_t *current;
1167 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, last_orphan, &current);
1168 if (rc != EOK) {
1169 return rc;
1170 }
1171
1172 next_orphan = ext4_inode_get_deletion_time(current->inode);
1173
1174 bool found = false;
1175
1176 // Walk thourgh the linked list
1177 while (next_orphan != 0) {
1178
1179 // Found?
1180 if (next_orphan == inode_ref->index) {
1181 next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1182 ext4_inode_set_deletion_time(current->inode, next_orphan);
1183 current->dirty = true;
1184 found = true;
1185 break;
1186 }
1187
1188 ext4_filesystem_put_inode_ref(current);
1189
1190 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, next_orphan, &current);
1191 if (rc != EOK) {
1192 return rc;
1193 }
1194 next_orphan = ext4_inode_get_deletion_time(current->inode);
1195
1196 }
1197
1198 if (found) {
1199 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1200 }
1201
1202 rc = ext4_filesystem_put_inode_ref(current);
1203 if (rc != EOK) {
1204 return rc;
1205 }
1206
1207 if (!found) {
1208 return ENOENT;
1209 }
1210
1211 return EOK;
1212}
1213
1214/**
1215 * @}
1216 */
Note: See TracBrowser for help on using the repository browser.