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

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

Added default file/dir permissions due to compatibility reasons

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