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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since da4e695 was 44ecf89, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Replace deprecated function bcmp() with memcmp().

  • Property mode set to 100644
File size: 19.9 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);
392
[06d85e5]393 /* Check if index is not corrupted */
[565b6ff]394 if (rc != EXT4_ERR_BAD_DX_DIR) {
[38542dc]395 if (rc != EOK)
[565b6ff]396 return rc;
[38542dc]397
[565b6ff]398 return EOK;
399 }
[38542dc]400
[06d85e5]401 /* Needed to clear dir index flag if corrupted */
[565b6ff]402 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
403 parent->dirty = true;
404 }
[38542dc]405
[06d85e5]406 /* Linear algorithm */
[38542dc]407
408 uint32_t iblock = 0;
409 uint32_t fblock = 0;
[565b6ff]410 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[d0d7afb]411 uint32_t inode_size = ext4_inode_get_size(fs->superblock, parent->inode);
412 uint32_t total_blocks = inode_size / block_size;
[38542dc]413
414 uint32_t name_len = str_size(name);
415
[06d85e5]416 /* Find block, where is space for new entry and try to add */
[d0d7afb]417 bool success = false;
418 for (iblock = 0; iblock < total_blocks; ++iblock) {
[38542dc]419 int rc = ext4_filesystem_get_inode_data_block_index(parent,
420 iblock, &fblock);
421 if (rc != EOK)
[d0d7afb]422 return rc;
[38542dc]423
[d0d7afb]424 block_t *block;
425 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
[38542dc]426 if (rc != EOK)
[d0d7afb]427 return rc;
[38542dc]428
[06d85e5]429 /* If adding is successful, function can finish */
[38542dc]430 rc = ext4_directory_try_insert_entry(fs->superblock, block,
431 child, name, name_len);
432 if (rc == EOK)
[d0d7afb]433 success = true;
[38542dc]434
[d0d7afb]435 rc = block_put(block);
[38542dc]436 if (rc != EOK)
[73196d2]437 return rc;
[38542dc]438
439 if (success)
[d0d7afb]440 return EOK;
[73196d2]441 }
[38542dc]442
[06d85e5]443 /* No free block found - needed to allocate next data block */
[38542dc]444
[7eb033ce]445 iblock = 0;
446 fblock = 0;
[38542dc]447 int rc = ext4_filesystem_append_inode_block(parent, &fblock, &iblock);
448 if (rc != EOK)
[b7e0260]449 return rc;
[38542dc]450
[06d85e5]451 /* Load new block */
[ed6fdc7]452 block_t *new_block;
453 rc = block_get(&new_block, fs->device, fblock, BLOCK_FLAGS_NOREAD);
[38542dc]454 if (rc != EOK)
[b7e0260]455 return rc;
[38542dc]456
[06d85e5]457 /* Fill block with zeroes */
[ca3d77a]458 memset(new_block->data, 0, block_size);
[ed6fdc7]459 ext4_directory_entry_ll_t *block_entry = new_block->data;
[38542dc]460 ext4_directory_write_entry(fs->superblock, block_entry, block_size,
461 child, name, name_len);
462
[06d85e5]463 /* Save new block */
[ed6fdc7]464 new_block->dirty = true;
465 rc = block_put(new_block);
[38542dc]466
467 return rc;
[73196d2]468}
[246a5af]469
[bae2a79e]470/** Find directory entry with passed name.
471 *
[38542dc]472 * @param result Result structure to be returned if entry found
473 * @param parent Directory i-node
474 * @param name Name of entry to be found
475 *
476 * @return Error code
477 *
[bae2a79e]478 */
[1ac1ab4]479int ext4_directory_find_entry(ext4_directory_search_result_t *result,
[38542dc]480 ext4_inode_ref_t *parent, const char *name)
[f49638e]481{
[38542dc]482 uint32_t name_len = str_size(name);
483
[1ac1ab4]484 ext4_superblock_t *sb = parent->fs->superblock;
[38542dc]485
[06d85e5]486 /* Index search */
[38542dc]487 if ((ext4_superblock_has_feature_compatible(sb,
488 EXT4_FEATURE_COMPAT_DIR_INDEX)) &&
489 (ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX))) {
490 int rc = ext4_directory_dx_find_entry(result, parent, name_len,
491 name);
492
[06d85e5]493 /* Check if index is not corrupted */
[8be96a0]494 if (rc != EXT4_ERR_BAD_DX_DIR) {
[38542dc]495 if (rc != EOK)
[8be96a0]496 return rc;
[38542dc]497
[8be96a0]498 return EOK;
[f49638e]499 }
[38542dc]500
[06d85e5]501 /* Needed to clear dir index flag if corrupted */
[bae2a79e]502 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
503 parent->dirty = true;
[8be96a0]504 }
[38542dc]505
[06d85e5]506 /* Linear algorithm */
[38542dc]507
508 uint32_t iblock;
509 uint32_t fblock;
[1ac1ab4]510 uint32_t block_size = ext4_superblock_get_block_size(sb);
511 uint32_t inode_size = ext4_inode_get_size(sb, parent->inode);
[7689590]512 uint32_t total_blocks = inode_size / block_size;
[38542dc]513
[06d85e5]514 /* Walk through all data blocks */
[7689590]515 for (iblock = 0; iblock < total_blocks; ++iblock) {
[06d85e5]516 /* Load block address */
[38542dc]517 int rc = ext4_filesystem_get_inode_data_block_index(parent, iblock,
518 &fblock);
519 if (rc != EOK)
[7689590]520 return rc;
[38542dc]521
[06d85e5]522 /* Load data block */
[7689590]523 block_t *block;
[1ac1ab4]524 rc = block_get(&block, parent->fs->device, fblock, BLOCK_FLAGS_NONE);
[38542dc]525 if (rc != EOK)
[f49638e]526 return rc;
[38542dc]527
[06d85e5]528 /* Try to find entry in block */
[7689590]529 ext4_directory_entry_ll_t *res_entry;
[38542dc]530 rc = ext4_directory_find_in_block(block, sb, name_len, name,
531 &res_entry);
[7689590]532 if (rc == EOK) {
533 result->block = block;
534 result->dentry = res_entry;
535 return EOK;
536 }
[38542dc]537
[06d85e5]538 /* Entry not found - put block and continue to the next block */
[38542dc]539
[7689590]540 rc = block_put(block);
[38542dc]541 if (rc != EOK)
[7689590]542 return rc;
[f49638e]543 }
[38542dc]544
[06d85e5]545 /* Entry was not found */
[38542dc]546
[7689590]547 result->block = NULL;
548 result->dentry = NULL;
[38542dc]549
[7689590]550 return ENOENT;
[8be96a0]551}
552
[bae2a79e]553/** Remove directory entry.
554 *
[38542dc]555 * @param parent Directory i-node
556 * @param name Name of the entry to be removed
557 *
558 * @return Error code
559 *
[bae2a79e]560 */
[1ac1ab4]561int ext4_directory_remove_entry(ext4_inode_ref_t *parent, const char *name)
[8be96a0]562{
[06d85e5]563 /* Check if removing from directory */
[1ac1ab4]564 if (!ext4_inode_is_type(parent->fs->superblock, parent->inode,
[38542dc]565 EXT4_INODE_MODE_DIRECTORY))
[8be96a0]566 return ENOTDIR;
[38542dc]567
[06d85e5]568 /* Try to find entry */
[7689590]569 ext4_directory_search_result_t result;
[38542dc]570 int rc = ext4_directory_find_entry(&result, parent, name);
571 if (rc != EOK)
[8be96a0]572 return rc;
[38542dc]573
[06d85e5]574 /* Invalidate entry */
[7689590]575 ext4_directory_entry_ll_set_inode(result.dentry, 0);
[38542dc]576
[06d85e5]577 /* Store entry position in block */
[38542dc]578 uint32_t pos = (void *) result.dentry - result.block->data;
579
580 /*
581 * If entry is not the first in block, it must be merged
[06d85e5]582 * with previous entry
583 */
[f49638e]584 if (pos != 0) {
[bae2a79e]585 uint32_t offset = 0;
[38542dc]586
[06d85e5]587 /* Start from the first entry in block */
[7689590]588 ext4_directory_entry_ll_t *tmp_dentry = result.block->data;
[f49638e]589 uint16_t tmp_dentry_length =
[38542dc]590 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
591
[06d85e5]592 /* Find direct predecessor of removed entry */
[f49638e]593 while ((offset + tmp_dentry_length) < pos) {
[38542dc]594 offset +=
595 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
[7689590]596 tmp_dentry = result.block->data + offset;
[f49638e]597 tmp_dentry_length =
[38542dc]598 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
[f49638e]599 }
[38542dc]600
[f49638e]601 assert(tmp_dentry_length + offset == pos);
[38542dc]602
[06d85e5]603 /* Add to removed entry length to predecessor's length */
[f49638e]604 uint16_t del_entry_length =
[38542dc]605 ext4_directory_entry_ll_get_entry_length(result.dentry);
[f49638e]606 ext4_directory_entry_ll_set_entry_length(tmp_dentry,
[38542dc]607 tmp_dentry_length + del_entry_length);
[f49638e]608 }
[38542dc]609
[7689590]610 result.block->dirty = true;
[38542dc]611
[7689590]612 return ext4_directory_destroy_result(&result);
[f49638e]613}
[7bc4508]614
[bae2a79e]615/** Try to insert entry to concrete data block.
616 *
[38542dc]617 * @param sb Superblock
618 * @param target_block Block to try to insert entry to
619 * @param child Child i-node to be inserted by new entry
620 * @param name Name of the new entry
621 * @param name_len Length of the new entry name
622 *
623 * @return Error code
624 *
[bae2a79e]625 */
[d0d7afb]626int ext4_directory_try_insert_entry(ext4_superblock_t *sb,
[38542dc]627 block_t *target_block, ext4_inode_ref_t *child, const char *name,
628 uint32_t name_len)
[d0d7afb]629{
[06d85e5]630 /* Compute required length entry and align it to 4 bytes */
[38542dc]631 uint32_t block_size = ext4_superblock_get_block_size(sb);
632 uint16_t required_len = sizeof(ext4_fake_directory_entry_t) + name_len;
633
634 if ((required_len % 4) != 0)
635 required_len += 4 - (required_len % 4);
636
637 /* Initialize pointers, stop means to upper bound */
638 ext4_directory_entry_ll_t *dentry = target_block->data;
639 ext4_directory_entry_ll_t *stop = target_block->data + block_size;
640
641 /*
642 * Walk through the block and check for invalid entries
643 * or entries with free space for new entry
644 */
645 while (dentry < stop) {
646 uint32_t inode = ext4_directory_entry_ll_get_inode(dentry);
647 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(dentry);
648
649 /* If invalid and large enough entry, use it */
650 if ((inode == 0) && (rec_len >= required_len)) {
651 ext4_directory_write_entry(sb, dentry, rec_len, child,
652 name, name_len);
653 target_block->dirty = true;
654
655 return EOK;
656 }
657
658 /* Valid entry, try to split it */
659 if (inode != 0) {
660 uint16_t used_name_len =
661 ext4_directory_entry_ll_get_name_length(sb, dentry);
662
663 uint16_t used_space =
664 sizeof(ext4_fake_directory_entry_t) + used_name_len;
665
666 if ((used_name_len % 4) != 0)
667 used_space += 4 - (used_name_len % 4);
668
669 uint16_t free_space = rec_len - used_space;
670
671 /* There is free space for new entry */
672 if (free_space >= required_len) {
673 /* Cut tail of current entry */
674 ext4_directory_entry_ll_set_entry_length(dentry, used_space);
675 ext4_directory_entry_ll_t *new_entry =
676 (void *) dentry + used_space;
677 ext4_directory_write_entry(sb, new_entry,
678 free_space, child, name, name_len);
679
680 target_block->dirty = true;
681
[d0d7afb]682 return EOK;
[38542dc]683 }
684 }
685
686 /* Jump to the next entry */
687 dentry = (void *) dentry + rec_len;
688 }
689
690 /* No free space found for new entry */
691 return ENOSPC;
[d0d7afb]692}
693
[bae2a79e]694/** Try to find entry in block by name.
695 *
[38542dc]696 * @param block Block containing entries
697 * @param sb Superblock
698 * @param name_len Length of entry name
699 * @param name Name of entry to be found
700 * @param res_entry Output pointer to found entry, NULL if not found
701 *
702 * @return Error code
703 *
[bae2a79e]704 */
[38542dc]705int ext4_directory_find_in_block(block_t *block, ext4_superblock_t *sb,
706 size_t name_len, const char *name, ext4_directory_entry_ll_t **res_entry)
[7689590]707{
[06d85e5]708 /* Start from the first entry in block */
[38542dc]709 ext4_directory_entry_ll_t *dentry =
710 (ext4_directory_entry_ll_t *) block->data;
711
712 /* Set upper bound for cycling */
[7689590]713 uint8_t *addr_limit = block->data + ext4_superblock_get_block_size(sb);
[38542dc]714
[06d85e5]715 /* Walk through the block and check entries */
[38542dc]716 while ((uint8_t *) dentry < addr_limit) {
[06d85e5]717 /* Termination condition */
[38542dc]718 if ((uint8_t *) dentry + name_len > addr_limit)
[7689590]719 break;
[38542dc]720
[06d85e5]721 /* Valid entry - check it */
[7689590]722 if (dentry->inode != 0) {
[06d85e5]723 /* For more effectivity compare firstly only lengths */
[38542dc]724 if (ext4_directory_entry_ll_get_name_length(sb, dentry) ==
725 name_len) {
[06d85e5]726 /* Compare names */
[44ecf89]727 if (memcmp((uint8_t *) name, dentry->name, name_len) == 0) {
[7689590]728 *res_entry = dentry;
729 return EOK;
730 }
731 }
732 }
[38542dc]733
734 uint16_t dentry_len =
735 ext4_directory_entry_ll_get_entry_length(dentry);
736
[06d85e5]737 /* Corrupted entry */
[38542dc]738 if (dentry_len == 0)
[7689590]739 return EINVAL;
[38542dc]740
[06d85e5]741 /* Jump to next entry */
[38542dc]742 dentry = (ext4_directory_entry_ll_t *) ((uint8_t *) dentry + dentry_len);
[7689590]743 }
[38542dc]744
[06d85e5]745 /* Entry not found */
[7689590]746 return ENOENT;
747}
748
[bae2a79e]749/** Simple function to release allocated data from result.
750 *
[38542dc]751 * @param result Search result to destroy
752 *
753 * @return Error code
754 *
[bae2a79e]755 */
[7689590]756int ext4_directory_destroy_result(ext4_directory_search_result_t *result)
757{
[38542dc]758 if (result->block)
[7689590]759 return block_put(result->block);
[38542dc]760
[7689590]761 return EOK;
762}
[eb91db7]763
764/**
765 * @}
[7689590]766 */
Note: See TracBrowser for help on using the repository browser.