source: mainline/uspace/lib/ext4/libext4_directory.c@ 2d53cfc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d53cfc was 38542dc, checked in by Martin Decky <martin@…>, 13 years ago

ext4 code review and coding style cleanup

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