source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 6efd162

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6efd162 was f5c03a8, checked in by Maurizio Lombardi <m.lombardi85@…>, 10 years ago

libext4: remove block leak in the error code path of ext4_filesystem_free_inode()

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