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

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

checks by mount operation

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