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

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

Fixed bug with different sizes in writing file on extents

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