source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 840e227

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

added debug error messages and modified delete orphan function (will be deleted probably)

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