source: mainline/uspace/lib/ext4/libext4_directory.c@ d776329b

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

libext4: remove unnecessary check

  • Property mode set to 100644
File size: 19.8 KB
RevLine 
[eb91db7]1/*
[d1538a1]2 * Copyright (c) 2011 Martin Sucha
[f22d5ef0]3 * Copyright (c) 2012 Frantisek Princ
[eb91db7]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 * @{
[38542dc]32 */
[eb91db7]33/**
[38542dc]34 * @file libext4_directory.c
35 * @brief Ext4 directory structure operations.
[eb91db7]36 */
37
[9b9d37bb]38#include <byteorder.h>
39#include <errno.h>
[f49638e]40#include <malloc.h>
[3711e7e]41#include "libext4.h"
[eb91db7]42
[bae2a79e]43/** Get i-node number from directory entry.
44 *
[38542dc]45 * @param de Directory entry
46 *
47 * @return I-node number
48 *
[bae2a79e]49 */
[9b9d37bb]50uint32_t ext4_directory_entry_ll_get_inode(ext4_directory_entry_ll_t *de)
51{
52 return uint32_t_le2host(de->inode);
53}
54
[bae2a79e]55/** Set i-node number to directory entry.
56 *
[38542dc]57 * @param de Directory entry
58 * @param inode I-node number
59 *
[bae2a79e]60 */
[343ccfd]61void ext4_directory_entry_ll_set_inode(ext4_directory_entry_ll_t *de,
[38542dc]62 uint32_t inode)
[343ccfd]63{
64 de->inode = host2uint32_t_le(inode);
65}
66
[bae2a79e]67/** Get directory entry length.
68 *
[38542dc]69 * @param de Directory entry
70 *
71 * @return Entry length
72 *
[bae2a79e]73 */
[38542dc]74uint16_t ext4_directory_entry_ll_get_entry_length(ext4_directory_entry_ll_t *de)
[9b9d37bb]75{
76 return uint16_t_le2host(de->entry_length);
77}
78
[bae2a79e]79/** Set directory entry length.
80 *
[38542dc]81 * @param de Directory entry
82 * @param length Entry length
83 *
[bae2a79e]84 */
[343ccfd]85void ext4_directory_entry_ll_set_entry_length(ext4_directory_entry_ll_t *de,
[38542dc]86 uint16_t length)
[343ccfd]87{
88 de->entry_length = host2uint16_t_le(length);
89}
90
[bae2a79e]91/** Get directory entry name length.
92 *
[38542dc]93 * @param sb Superblock
94 * @param de Directory entry
95 *
96 * @return Entry name length
97 *
[bae2a79e]98 */
[38542dc]99uint16_t ext4_directory_entry_ll_get_name_length(ext4_superblock_t *sb,
100 ext4_directory_entry_ll_t *de)
[9b9d37bb]101{
[38542dc]102 if ((ext4_superblock_get_rev_level(sb) == 0) &&
103 (ext4_superblock_get_minor_rev_level(sb) < 5))
[8be96a0]104 return ((uint16_t)de->name_length_high) << 8 |
[38542dc]105 ((uint16_t)de->name_length);
106
[8be96a0]107 return de->name_length;
[7bc4508]108
109}
110
[bae2a79e]111/** Set directory entry name length.
112 *
[38542dc]113 * @param sb Superblock
114 * @param de Directory entry
115 * @param length Entry name length
116 *
[bae2a79e]117 */
[343ccfd]118void ext4_directory_entry_ll_set_name_length(ext4_superblock_t *sb,
[38542dc]119 ext4_directory_entry_ll_t *de, uint16_t length)
[7bc4508]120{
[343ccfd]121 de->name_length = (length << 8) >> 8;
[38542dc]122
123 if ((ext4_superblock_get_rev_level(sb) == 0) &&
124 (ext4_superblock_get_minor_rev_level(sb) < 5))
[343ccfd]125 de->name_length_high = length >> 8;
[38542dc]126
127 /* Else do nothing */
[343ccfd]128}
[7bc4508]129
[bae2a79e]130/** Get i-node type of directory entry.
131 *
[38542dc]132 * @param sb Superblock
133 * @param de Directory entry
134 *
135 * @return I-node type (file, dir, etc.)
136 *
[bae2a79e]137 */
[38542dc]138uint8_t ext4_directory_entry_ll_get_inode_type(ext4_superblock_t *sb,
139 ext4_directory_entry_ll_t *de)
[cd1cc4e6]140{
[38542dc]141 if ((ext4_superblock_get_rev_level(sb) > 0) ||
142 (ext4_superblock_get_minor_rev_level(sb) >= 5))
143 return de->inode_type;
144
[cd1cc4e6]145 return EXT4_DIRECTORY_FILETYPE_UNKNOWN;
146}
147
[bae2a79e]148/** Set i-node type of directory entry.
149 *
[38542dc]150 * @param sb Superblock
151 * @param de Directory entry
152 * @param type I-node type (file, dir, etc.)
153 *
[bae2a79e]154 */
[38542dc]155void ext4_directory_entry_ll_set_inode_type(ext4_superblock_t *sb,
156 ext4_directory_entry_ll_t *de, uint8_t type)
[cd1cc4e6]157{
[38542dc]158 if ((ext4_superblock_get_rev_level(sb) > 0) ||
159 (ext4_superblock_get_minor_rev_level(sb) >= 5))
[cd1cc4e6]160 de->inode_type = type;
[38542dc]161
162 /* Else do nothing */
[cd1cc4e6]163}
[7bc4508]164
[38542dc]165static int ext4_directory_iterator_seek(ext4_directory_iterator_t *, aoff64_t);
166static int ext4_directory_iterator_set(ext4_directory_iterator_t *, uint32_t);
[bae2a79e]167
168/** Initialize directory iterator.
169 *
170 * Set position to the first valid entry from the required position.
171 *
[38542dc]172 * @param it Pointer to iterator to be initialized
173 * @param inode_ref Directory i-node
174 * @param pos Position to start reading entries from
175 *
176 * @return Error code
177 *
[bae2a79e]178 */
[9b9d37bb]179int ext4_directory_iterator_init(ext4_directory_iterator_t *it,
[38542dc]180 ext4_inode_ref_t *inode_ref, aoff64_t pos)
[9b9d37bb]181{
182 it->inode_ref = inode_ref;
183 it->current = NULL;
184 it->current_offset = 0;
185 it->current_block = NULL;
[38542dc]186
[9b9d37bb]187 return ext4_directory_iterator_seek(it, pos);
188}
189
[bae2a79e]190/** Jump to the next valid entry
191 *
[38542dc]192 * @param it Initialized iterator
193 *
194 * @return Error code
195 *
[bae2a79e]196 */
[9b9d37bb]197int ext4_directory_iterator_next(ext4_directory_iterator_t *it)
198{
199 assert(it->current != NULL);
[38542dc]200
201 uint16_t skip = ext4_directory_entry_ll_get_entry_length(it->current);
202
[9b9d37bb]203 return ext4_directory_iterator_seek(it, it->current_offset + skip);
204}
205
[bae2a79e]206/** Seek to next valid directory entry.
207 *
208 * Here can be jumped to the next data block.
209 *
[38542dc]210 * @param it Initialized iterator
211 * @param pos Position of the next entry
212 *
213 * @return Error code
214 *
[bae2a79e]215 */
[9b9d37bb]216int ext4_directory_iterator_seek(ext4_directory_iterator_t *it, aoff64_t pos)
217{
[38542dc]218 uint64_t size = ext4_inode_get_size(it->inode_ref->fs->superblock,
219 it->inode_ref->inode);
220
[9b9d37bb]221 /* The iterator is not valid until we seek to the desired position */
222 it->current = NULL;
[38542dc]223
[9b9d37bb]224 /* Are we at the end? */
225 if (pos >= size) {
226 if (it->current_block) {
[38542dc]227 int rc = block_put(it->current_block);
[9b9d37bb]228 it->current_block = NULL;
[38542dc]229
230 if (rc != EOK)
[9b9d37bb]231 return rc;
232 }
[38542dc]233
[9b9d37bb]234 it->current_offset = pos;
235 return EOK;
236 }
[38542dc]237
[06d85e5]238 /* Compute next block address */
[38542dc]239 uint32_t block_size =
240 ext4_superblock_get_block_size(it->inode_ref->fs->superblock);
[d9bbe45]241 aoff64_t current_block_idx = it->current_offset / block_size;
242 aoff64_t next_block_idx = pos / block_size;
[38542dc]243
244 /*
245 * If we don't have a block or are moving accross block boundary,
[9b9d37bb]246 * we need to get another block
247 */
[38542dc]248 if ((it->current_block == NULL) ||
249 (current_block_idx != next_block_idx)) {
[9b9d37bb]250 if (it->current_block) {
[38542dc]251 int rc = block_put(it->current_block);
[9b9d37bb]252 it->current_block = NULL;
[38542dc]253
254 if (rc != EOK)
[9b9d37bb]255 return rc;
256 }
[38542dc]257
[d9bbe45]258 uint32_t next_block_phys_idx;
[38542dc]259 int rc = ext4_filesystem_get_inode_data_block_index(it->inode_ref,
260 next_block_idx, &next_block_phys_idx);
261 if (rc != EOK)
[9b9d37bb]262 return rc;
[38542dc]263
[bae2a79e]264 rc = block_get(&it->current_block, it->inode_ref->fs->device,
[38542dc]265 next_block_phys_idx, BLOCK_FLAGS_NONE);
[9b9d37bb]266 if (rc != EOK) {
267 it->current_block = NULL;
268 return rc;
269 }
270 }
[38542dc]271
[9b9d37bb]272 it->current_offset = pos;
[38542dc]273
[9b9d37bb]274 return ext4_directory_iterator_set(it, block_size);
275}
276
[bae2a79e]277/** Do some checks before returning iterator.
278 *
[38542dc]279 * @param it Iterator to be checked
280 * @param block_size Size of data block
281 *
282 * @return Error code
283 *
[bae2a79e]284 */
[9b9d37bb]285static int ext4_directory_iterator_set(ext4_directory_iterator_t *it,
286 uint32_t block_size)
287{
288 it->current = NULL;
[38542dc]289
[d9bbe45]290 uint32_t offset_in_block = it->current_offset % block_size;
[38542dc]291
[9b9d37bb]292 /* Ensure proper alignment */
[38542dc]293 if ((offset_in_block % 4) != 0)
[9b9d37bb]294 return EIO;
[38542dc]295
[9b9d37bb]296 /* Ensure that the core of the entry does not overflow the block */
[38542dc]297 if (offset_in_block > block_size - 8)
[9b9d37bb]298 return EIO;
[38542dc]299
300 ext4_directory_entry_ll_t *entry =
301 it->current_block->data + offset_in_block;
302
[9b9d37bb]303 /* Ensure that the whole entry does not overflow the block */
304 uint16_t length = ext4_directory_entry_ll_get_entry_length(entry);
[38542dc]305 if (offset_in_block + length > block_size)
[9b9d37bb]306 return EIO;
[38542dc]307
[9b9d37bb]308 /* Ensure the name length is not too large */
[38542dc]309 if (ext4_directory_entry_ll_get_name_length(
310 it->inode_ref->fs->superblock, entry) > length-8)
[9b9d37bb]311 return EIO;
[38542dc]312
[06d85e5]313 /* Everything OK - "publish" the entry */
[9b9d37bb]314 it->current = entry;
315 return EOK;
316}
317
[bae2a79e]318/** Uninitialize directory iterator.
319 *
320 * Release all allocated structures.
321 *
[38542dc]322 * @param it Iterator to be finished
323 *
324 * @return Error code
325 *
[bae2a79e]326 */
[9b9d37bb]327int ext4_directory_iterator_fini(ext4_directory_iterator_t *it)
328{
329 it->inode_ref = NULL;
330 it->current = NULL;
[38542dc]331
332 if (it->current_block)
333 return block_put(it->current_block);
334
[9b9d37bb]335 return EOK;
336}
337
[38542dc]338/** Write directory entry to concrete data block.
339 *
340 * @param sb Superblock
341 * @param entry Pointer to entry to be written
342 * @param entry_len Length of new entry
343 * @param child Child i-node to be written to new entry
344 * @param name Name of the new entry
345 * @param name_len Length of entry name
346 *
[bae2a79e]347 */
[5c83612b]348void ext4_directory_write_entry(ext4_superblock_t *sb,
[38542dc]349 ext4_directory_entry_ll_t *entry, uint16_t entry_len,
350 ext4_inode_ref_t *child, const char *name, size_t name_len)
[1d69c69]351{
[06d85e5]352 /* Check maximum entry length */
[bae2a79e]353 uint32_t block_size = ext4_superblock_get_block_size(sb);
354 assert(entry_len <= block_size);
[38542dc]355
[06d85e5]356 /* Set basic attributes */
[1d69c69]357 ext4_directory_entry_ll_set_inode(entry, child->index);
358 ext4_directory_entry_ll_set_entry_length(entry, entry_len);
359 ext4_directory_entry_ll_set_name_length(sb, entry, name_len);
[38542dc]360
[06d85e5]361 /* Write name */
[bae2a79e]362 memcpy(entry->name, name, name_len);
[38542dc]363
[06d85e5]364 /* Set type of entry */
[38542dc]365 if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY))
366 ext4_directory_entry_ll_set_inode_type(sb, entry,
367 EXT4_DIRECTORY_FILETYPE_DIR);
368 else
369 ext4_directory_entry_ll_set_inode_type(sb, entry,
370 EXT4_DIRECTORY_FILETYPE_REG_FILE);
[1d69c69]371}
372
[bae2a79e]373/** Add new entry to the directory.
374 *
[38542dc]375 * @param parent Directory i-node
376 * @param name Name of new entry
377 * @param child I-node to be referenced from new entry
378 *
379 * @return Error code
380 *
[bae2a79e]381 */
[38542dc]382int ext4_directory_add_entry(ext4_inode_ref_t *parent, const char *name,
383 ext4_inode_ref_t *child)
[73196d2]384{
[1ac1ab4]385 ext4_filesystem_t *fs = parent->fs;
[38542dc]386
[06d85e5]387 /* Index adding (if allowed) */
[38542dc]388 if ((ext4_superblock_has_feature_compatible(fs->superblock,
389 EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
390 (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
391 int rc = ext4_directory_dx_add_entry(parent, child, name);
[356e6882]392
[06d85e5]393 /* Check if index is not corrupted */
[356e6882]394 if (rc != EXT4_ERR_BAD_DX_DIR)
395 return rc;
396
[06d85e5]397 /* Needed to clear dir index flag if corrupted */
[565b6ff]398 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
399 parent->dirty = true;
400 }
[38542dc]401
[06d85e5]402 /* Linear algorithm */
[38542dc]403
404 uint32_t iblock = 0;
405 uint32_t fblock = 0;
[565b6ff]406 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[d0d7afb]407 uint32_t inode_size = ext4_inode_get_size(fs->superblock, parent->inode);
408 uint32_t total_blocks = inode_size / block_size;
[38542dc]409
410 uint32_t name_len = str_size(name);
411
[06d85e5]412 /* Find block, where is space for new entry and try to add */
[d0d7afb]413 bool success = false;
414 for (iblock = 0; iblock < total_blocks; ++iblock) {
[38542dc]415 int rc = ext4_filesystem_get_inode_data_block_index(parent,
416 iblock, &fblock);
417 if (rc != EOK)
[d0d7afb]418 return rc;
[38542dc]419
[d0d7afb]420 block_t *block;
421 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
[38542dc]422 if (rc != EOK)
[d0d7afb]423 return rc;
[38542dc]424
[06d85e5]425 /* If adding is successful, function can finish */
[38542dc]426 rc = ext4_directory_try_insert_entry(fs->superblock, block,
427 child, name, name_len);
428 if (rc == EOK)
[d0d7afb]429 success = true;
[38542dc]430
[d0d7afb]431 rc = block_put(block);
[38542dc]432 if (rc != EOK)
[73196d2]433 return rc;
[38542dc]434
435 if (success)
[d0d7afb]436 return EOK;
[73196d2]437 }
[38542dc]438
[06d85e5]439 /* No free block found - needed to allocate next data block */
[38542dc]440
[7eb033ce]441 iblock = 0;
442 fblock = 0;
[38542dc]443 int rc = ext4_filesystem_append_inode_block(parent, &fblock, &iblock);
444 if (rc != EOK)
[b7e0260]445 return rc;
[38542dc]446
[06d85e5]447 /* Load new block */
[ed6fdc7]448 block_t *new_block;
449 rc = block_get(&new_block, fs->device, fblock, BLOCK_FLAGS_NOREAD);
[38542dc]450 if (rc != EOK)
[b7e0260]451 return rc;
[38542dc]452
[06d85e5]453 /* Fill block with zeroes */
[ca3d77a]454 memset(new_block->data, 0, block_size);
[ed6fdc7]455 ext4_directory_entry_ll_t *block_entry = new_block->data;
[38542dc]456 ext4_directory_write_entry(fs->superblock, block_entry, block_size,
457 child, name, name_len);
458
[06d85e5]459 /* Save new block */
[ed6fdc7]460 new_block->dirty = true;
461 rc = block_put(new_block);
[38542dc]462
463 return rc;
[73196d2]464}
[246a5af]465
[bae2a79e]466/** Find directory entry with passed name.
467 *
[38542dc]468 * @param result Result structure to be returned if entry found
469 * @param parent Directory i-node
470 * @param name Name of entry to be found
471 *
472 * @return Error code
473 *
[bae2a79e]474 */
[1ac1ab4]475int ext4_directory_find_entry(ext4_directory_search_result_t *result,
[38542dc]476 ext4_inode_ref_t *parent, const char *name)
[f49638e]477{
[38542dc]478 uint32_t name_len = str_size(name);
479
[1ac1ab4]480 ext4_superblock_t *sb = parent->fs->superblock;
[38542dc]481
[06d85e5]482 /* Index search */
[38542dc]483 if ((ext4_superblock_has_feature_compatible(sb,
484 EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
485 (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
486 int rc = ext4_directory_dx_find_entry(result, parent, name_len,
487 name);
488
[06d85e5]489 /* Check if index is not corrupted */
[8be96a0]490 if (rc != EXT4_ERR_BAD_DX_DIR) {
[38542dc]491 if (rc != EOK)
[8be96a0]492 return rc;
[38542dc]493
[8be96a0]494 return EOK;
[f49638e]495 }
[38542dc]496
[06d85e5]497 /* Needed to clear dir index flag if corrupted */
[bae2a79e]498 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
499 parent->dirty = true;
[8be96a0]500 }
[38542dc]501
[06d85e5]502 /* Linear algorithm */
[38542dc]503
504 uint32_t iblock;
505 uint32_t fblock;
[1ac1ab4]506 uint32_t block_size = ext4_superblock_get_block_size(sb);
507 uint32_t inode_size = ext4_inode_get_size(sb, parent->inode);
[7689590]508 uint32_t total_blocks = inode_size / block_size;
[38542dc]509
[06d85e5]510 /* Walk through all data blocks */
[7689590]511 for (iblock = 0; iblock < total_blocks; ++iblock) {
[06d85e5]512 /* Load block address */
[38542dc]513 int rc = ext4_filesystem_get_inode_data_block_index(parent, iblock,
514 &fblock);
515 if (rc != EOK)
[7689590]516 return rc;
[38542dc]517
[06d85e5]518 /* Load data block */
[7689590]519 block_t *block;
[1ac1ab4]520 rc = block_get(&block, parent->fs->device, fblock, BLOCK_FLAGS_NONE);
[38542dc]521 if (rc != EOK)
[f49638e]522 return rc;
[38542dc]523
[06d85e5]524 /* Try to find entry in block */
[7689590]525 ext4_directory_entry_ll_t *res_entry;
[38542dc]526 rc = ext4_directory_find_in_block(block, sb, name_len, name,
527 &res_entry);
[7689590]528 if (rc == EOK) {
529 result->block = block;
530 result->dentry = res_entry;
531 return EOK;
532 }
[38542dc]533
[06d85e5]534 /* Entry not found - put block and continue to the next block */
[38542dc]535
[7689590]536 rc = block_put(block);
[38542dc]537 if (rc != EOK)
[7689590]538 return rc;
[f49638e]539 }
[38542dc]540
[06d85e5]541 /* Entry was not found */
[38542dc]542
[7689590]543 result->block = NULL;
544 result->dentry = NULL;
[38542dc]545
[7689590]546 return ENOENT;
[8be96a0]547}
548
[bae2a79e]549/** Remove directory entry.
550 *
[38542dc]551 * @param parent Directory i-node
552 * @param name Name of the entry to be removed
553 *
554 * @return Error code
555 *
[bae2a79e]556 */
[1ac1ab4]557int ext4_directory_remove_entry(ext4_inode_ref_t *parent, const char *name)
[8be96a0]558{
[06d85e5]559 /* Check if removing from directory */
[1ac1ab4]560 if (!ext4_inode_is_type(parent->fs->superblock, parent->inode,
[38542dc]561 EXT4_INODE_MODE_DIRECTORY))
[8be96a0]562 return ENOTDIR;
[38542dc]563
[06d85e5]564 /* Try to find entry */
[7689590]565 ext4_directory_search_result_t result;
[38542dc]566 int rc = ext4_directory_find_entry(&result, parent, name);
567 if (rc != EOK)
[8be96a0]568 return rc;
[38542dc]569
[06d85e5]570 /* Invalidate entry */
[7689590]571 ext4_directory_entry_ll_set_inode(result.dentry, 0);
[38542dc]572
[06d85e5]573 /* Store entry position in block */
[38542dc]574 uint32_t pos = (void *) result.dentry - result.block->data;
575
576 /*
577 * If entry is not the first in block, it must be merged
[06d85e5]578 * with previous entry
579 */
[f49638e]580 if (pos != 0) {
[bae2a79e]581 uint32_t offset = 0;
[38542dc]582
[06d85e5]583 /* Start from the first entry in block */
[7689590]584 ext4_directory_entry_ll_t *tmp_dentry = result.block->data;
[f49638e]585 uint16_t tmp_dentry_length =
[38542dc]586 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
587
[06d85e5]588 /* Find direct predecessor of removed entry */
[f49638e]589 while ((offset + tmp_dentry_length) < pos) {
[38542dc]590 offset +=
591 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
[7689590]592 tmp_dentry = result.block->data + offset;
[f49638e]593 tmp_dentry_length =
[38542dc]594 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
[f49638e]595 }
[38542dc]596
[f49638e]597 assert(tmp_dentry_length + offset == pos);
[38542dc]598
[06d85e5]599 /* Add to removed entry length to predecessor's length */
[f49638e]600 uint16_t del_entry_length =
[38542dc]601 ext4_directory_entry_ll_get_entry_length(result.dentry);
[f49638e]602 ext4_directory_entry_ll_set_entry_length(tmp_dentry,
[38542dc]603 tmp_dentry_length + del_entry_length);
[f49638e]604 }
[38542dc]605
[7689590]606 result.block->dirty = true;
[38542dc]607
[7689590]608 return ext4_directory_destroy_result(&result);
[f49638e]609}
[7bc4508]610
[bae2a79e]611/** Try to insert entry to concrete data block.
612 *
[38542dc]613 * @param sb Superblock
614 * @param target_block Block to try to insert entry to
615 * @param child Child i-node to be inserted by new entry
616 * @param name Name of the new entry
617 * @param name_len Length of the new entry name
618 *
619 * @return Error code
620 *
[bae2a79e]621 */
[d0d7afb]622int ext4_directory_try_insert_entry(ext4_superblock_t *sb,
[38542dc]623 block_t *target_block, ext4_inode_ref_t *child, const char *name,
624 uint32_t name_len)
[d0d7afb]625{
[06d85e5]626 /* Compute required length entry and align it to 4 bytes */
[38542dc]627 uint32_t block_size = ext4_superblock_get_block_size(sb);
628 uint16_t required_len = sizeof(ext4_fake_directory_entry_t) + name_len;
629
630 if ((required_len % 4) != 0)
631 required_len += 4 - (required_len % 4);
632
633 /* Initialize pointers, stop means to upper bound */
634 ext4_directory_entry_ll_t *dentry = target_block->data;
635 ext4_directory_entry_ll_t *stop = target_block->data + block_size;
636
637 /*
638 * Walk through the block and check for invalid entries
639 * or entries with free space for new entry
640 */
641 while (dentry < stop) {
642 uint32_t inode = ext4_directory_entry_ll_get_inode(dentry);
643 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(dentry);
644
645 /* If invalid and large enough entry, use it */
646 if ((inode == 0) && (rec_len >= required_len)) {
647 ext4_directory_write_entry(sb, dentry, rec_len, child,
648 name, name_len);
649 target_block->dirty = true;
650
651 return EOK;
652 }
653
654 /* Valid entry, try to split it */
655 if (inode != 0) {
656 uint16_t used_name_len =
657 ext4_directory_entry_ll_get_name_length(sb, dentry);
658
659 uint16_t used_space =
660 sizeof(ext4_fake_directory_entry_t) + used_name_len;
661
662 if ((used_name_len % 4) != 0)
663 used_space += 4 - (used_name_len % 4);
664
665 uint16_t free_space = rec_len - used_space;
666
667 /* There is free space for new entry */
668 if (free_space >= required_len) {
669 /* Cut tail of current entry */
670 ext4_directory_entry_ll_set_entry_length(dentry, used_space);
671 ext4_directory_entry_ll_t *new_entry =
672 (void *) dentry + used_space;
673 ext4_directory_write_entry(sb, new_entry,
674 free_space, child, name, name_len);
675
676 target_block->dirty = true;
677
[d0d7afb]678 return EOK;
[38542dc]679 }
680 }
681
682 /* Jump to the next entry */
683 dentry = (void *) dentry + rec_len;
684 }
685
686 /* No free space found for new entry */
687 return ENOSPC;
[d0d7afb]688}
689
[bae2a79e]690/** Try to find entry in block by name.
691 *
[38542dc]692 * @param block Block containing entries
693 * @param sb Superblock
694 * @param name_len Length of entry name
695 * @param name Name of entry to be found
696 * @param res_entry Output pointer to found entry, NULL if not found
697 *
698 * @return Error code
699 *
[bae2a79e]700 */
[38542dc]701int ext4_directory_find_in_block(block_t *block, ext4_superblock_t *sb,
702 size_t name_len, const char *name, ext4_directory_entry_ll_t **res_entry)
[7689590]703{
[06d85e5]704 /* Start from the first entry in block */
[38542dc]705 ext4_directory_entry_ll_t *dentry =
706 (ext4_directory_entry_ll_t *) block->data;
707
708 /* Set upper bound for cycling */
[7689590]709 uint8_t *addr_limit = block->data + ext4_superblock_get_block_size(sb);
[38542dc]710
[06d85e5]711 /* Walk through the block and check entries */
[38542dc]712 while ((uint8_t *) dentry < addr_limit) {
[06d85e5]713 /* Termination condition */
[38542dc]714 if ((uint8_t *) dentry + name_len > addr_limit)
[7689590]715 break;
[38542dc]716
[06d85e5]717 /* Valid entry - check it */
[7689590]718 if (dentry->inode != 0) {
[06d85e5]719 /* For more effectivity compare firstly only lengths */
[38542dc]720 if (ext4_directory_entry_ll_get_name_length(sb, dentry) ==
721 name_len) {
[06d85e5]722 /* Compare names */
[44ecf89]723 if (memcmp((uint8_t *) name, dentry->name, name_len) == 0) {
[7689590]724 *res_entry = dentry;
725 return EOK;
726 }
727 }
728 }
[38542dc]729
730 uint16_t dentry_len =
731 ext4_directory_entry_ll_get_entry_length(dentry);
732
[06d85e5]733 /* Corrupted entry */
[38542dc]734 if (dentry_len == 0)
[7689590]735 return EINVAL;
[38542dc]736
[06d85e5]737 /* Jump to next entry */
[38542dc]738 dentry = (ext4_directory_entry_ll_t *) ((uint8_t *) dentry + dentry_len);
[7689590]739 }
[38542dc]740
[06d85e5]741 /* Entry not found */
[7689590]742 return ENOENT;
743}
744
[bae2a79e]745/** Simple function to release allocated data from result.
746 *
[38542dc]747 * @param result Search result to destroy
748 *
749 * @return Error code
750 *
[bae2a79e]751 */
[7689590]752int ext4_directory_destroy_result(ext4_directory_search_result_t *result)
753{
[38542dc]754 if (result->block)
[7689590]755 return block_put(result->block);
[38542dc]756
[7689590]757 return EOK;
758}
[eb91db7]759
760/**
761 * @}
[7689590]762 */
Note: See TracBrowser for help on using the repository browser.