source: mainline/uspace/lib/ext4/src/directory.c@ 36f0738

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 36f0738 was 38d150e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Prefer to get memory allocation functions through the standard stdlib header.

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