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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • 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/**
[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);
[a35b458]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;
[a35b458]128
[38542dc]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;
[a35b458]132
[38542dc]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;
[a35b458]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;
[a35b458]167
[38542dc]168 /* Else do nothing */
[cd1cc4e6]169}
[7bc4508]170
[b7fd2a0]171static errno_t ext4_directory_iterator_seek(ext4_directory_iterator_t *, aoff64_t);
172static errno_t 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 */
[b7fd2a0]185errno_t 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;
[a35b458]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 */
[b7fd2a0]203errno_t ext4_directory_iterator_next(ext4_directory_iterator_t *it)
[9b9d37bb]204{
205 assert(it->current != NULL);
[a35b458]206
[38542dc]207 uint16_t skip = ext4_directory_entry_ll_get_entry_length(it->current);
[a35b458]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 */
[b7fd2a0]222errno_t ext4_directory_iterator_seek(ext4_directory_iterator_t *it, aoff64_t pos)
[9b9d37bb]223{
[38542dc]224 uint64_t size = ext4_inode_get_size(it->inode_ref->fs->superblock,
225 it->inode_ref->inode);
[a35b458]226
[9b9d37bb]227 /* The iterator is not valid until we seek to the desired position */
228 it->current = NULL;
[a35b458]229
[9b9d37bb]230 /* Are we at the end? */
231 if (pos >= size) {
232 if (it->current_block) {
[b7fd2a0]233 errno_t rc = block_put(it->current_block);
[9b9d37bb]234 it->current_block = NULL;
[a35b458]235
[38542dc]236 if (rc != EOK)
[9b9d37bb]237 return rc;
238 }
[a35b458]239
[9b9d37bb]240 it->current_offset = pos;
241 return EOK;
242 }
[a35b458]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;
[a35b458]249
[38542dc]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) {
[b7fd2a0]257 errno_t rc = block_put(it->current_block);
[9b9d37bb]258 it->current_block = NULL;
[a35b458]259
[38542dc]260 if (rc != EOK)
[9b9d37bb]261 return rc;
262 }
[a35b458]263
[d9bbe45]264 uint32_t next_block_phys_idx;
[b7fd2a0]265 errno_t rc = ext4_filesystem_get_inode_data_block_index(it->inode_ref,
[38542dc]266 next_block_idx, &next_block_phys_idx);
267 if (rc != EOK)
[9b9d37bb]268 return rc;
[a35b458]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 }
[a35b458]277
[9b9d37bb]278 it->current_offset = pos;
[a35b458]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 */
[b7fd2a0]291static errno_t ext4_directory_iterator_set(ext4_directory_iterator_t *it,
[9b9d37bb]292 uint32_t block_size)
293{
294 it->current = NULL;
[a35b458]295
[d9bbe45]296 uint32_t offset_in_block = it->current_offset % block_size;
[a35b458]297
[9b9d37bb]298 /* Ensure proper alignment */
[38542dc]299 if ((offset_in_block % 4) != 0)
[9b9d37bb]300 return EIO;
[a35b458]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;
[a35b458]305
[38542dc]306 ext4_directory_entry_ll_t *entry =
307 it->current_block->data + offset_in_block;
[a35b458]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;
[a35b458]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;
[a35b458]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 */
[b7fd2a0]333errno_t ext4_directory_iterator_fini(ext4_directory_iterator_t *it)
[9b9d37bb]334{
335 it->inode_ref = NULL;
336 it->current = NULL;
[a35b458]337
[38542dc]338 if (it->current_block)
339 return block_put(it->current_block);
[a35b458]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);
[a35b458]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);
[a35b458]366
[06d85e5]367 /* Write name */
[bae2a79e]368 memcpy(entry->name, name, name_len);
[a35b458]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 */
[b7fd2a0]388errno_t ext4_directory_add_entry(ext4_inode_ref_t *parent, const char *name,
[38542dc]389 ext4_inode_ref_t *child)
[73196d2]390{
[1ac1ab4]391 ext4_filesystem_t *fs = parent->fs;
[a35b458]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))) {
[b7fd2a0]397 errno_t 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 }
[a35b458]407
[06d85e5]408 /* Linear algorithm */
[a35b458]409
[38542dc]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;
[a35b458]415
[38542dc]416 uint32_t name_len = str_size(name);
[a35b458]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) {
[b7fd2a0]421 errno_t rc = ext4_filesystem_get_inode_data_block_index(parent,
[38542dc]422 iblock, &fblock);
423 if (rc != EOK)
[d0d7afb]424 return rc;
[a35b458]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;
[a35b458]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;
[a35b458]436
[d0d7afb]437 rc = block_put(block);
[38542dc]438 if (rc != EOK)
[73196d2]439 return rc;
[a35b458]440
[38542dc]441 if (success)
[d0d7afb]442 return EOK;
[73196d2]443 }
[a35b458]444
[06d85e5]445 /* No free block found - needed to allocate next data block */
[a35b458]446
[7eb033ce]447 iblock = 0;
448 fblock = 0;
[b7fd2a0]449 errno_t rc = ext4_filesystem_append_inode_block(parent, &fblock, &iblock);
[38542dc]450 if (rc != EOK)
[b7e0260]451 return rc;
[a35b458]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;
[a35b458]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);
[a35b458]464
[06d85e5]465 /* Save new block */
[ed6fdc7]466 new_block->dirty = true;
467 rc = block_put(new_block);
[a35b458]468
[38542dc]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 */
[b7fd2a0]481errno_t 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);
[a35b458]485
[1ac1ab4]486 ext4_superblock_t *sb = parent->fs->superblock;
[a35b458]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))) {
[b7fd2a0]492 errno_t rc = ext4_directory_dx_find_entry(result, parent, name_len,
[38542dc]493 name);
[a35b458]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;
[a35b458]499
[8be96a0]500 return EOK;
[f49638e]501 }
[a35b458]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 }
[a35b458]507
[06d85e5]508 /* Linear algorithm */
[a35b458]509
[38542dc]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;
[a35b458]515
[06d85e5]516 /* Walk through all data blocks */
[7689590]517 for (iblock = 0; iblock < total_blocks; ++iblock) {
[06d85e5]518 /* Load block address */
[b7fd2a0]519 errno_t rc = ext4_filesystem_get_inode_data_block_index(parent, iblock,
[38542dc]520 &fblock);
521 if (rc != EOK)
[7689590]522 return rc;
[a35b458]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;
[a35b458]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 }
[a35b458]539
[06d85e5]540 /* Entry not found - put block and continue to the next block */
[a35b458]541
[7689590]542 rc = block_put(block);
[38542dc]543 if (rc != EOK)
[7689590]544 return rc;
[f49638e]545 }
[a35b458]546
[06d85e5]547 /* Entry was not found */
[a35b458]548
[7689590]549 result->block = NULL;
550 result->dentry = NULL;
[a35b458]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 */
[b7fd2a0]563errno_t 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;
[a35b458]569
[06d85e5]570 /* Try to find entry */
[7689590]571 ext4_directory_search_result_t result;
[b7fd2a0]572 errno_t rc = ext4_directory_find_entry(&result, parent, name);
[38542dc]573 if (rc != EOK)
[8be96a0]574 return rc;
[a35b458]575
[06d85e5]576 /* Invalidate entry */
[7689590]577 ext4_directory_entry_ll_set_inode(result.dentry, 0);
[a35b458]578
[06d85e5]579 /* Store entry position in block */
[38542dc]580 uint32_t pos = (void *) result.dentry - result.block->data;
[a35b458]581
[38542dc]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;
[a35b458]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);
[a35b458]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 }
[a35b458]602
[f49638e]603 assert(tmp_dentry_length + offset == pos);
[a35b458]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 }
[a35b458]611
[7689590]612 result.block->dirty = true;
[a35b458]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 */
[b7fd2a0]628errno_t 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;
[a35b458]635
[38542dc]636 if ((required_len % 4) != 0)
637 required_len += 4 - (required_len % 4);
[a35b458]638
[38542dc]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;
[a35b458]642
[38542dc]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);
[a35b458]650
[38542dc]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;
[a35b458]656
[38542dc]657 return EOK;
658 }
[a35b458]659
[38542dc]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);
[a35b458]664
[38542dc]665 uint16_t used_space =
666 sizeof(ext4_fake_directory_entry_t) + used_name_len;
[a35b458]667
[38542dc]668 if ((used_name_len % 4) != 0)
669 used_space += 4 - (used_name_len % 4);
[a35b458]670
[38542dc]671 uint16_t free_space = rec_len - used_space;
[a35b458]672
[38542dc]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);
[a35b458]681
[38542dc]682 target_block->dirty = true;
[a35b458]683
[d0d7afb]684 return EOK;
[38542dc]685 }
686 }
[a35b458]687
[38542dc]688 /* Jump to the next entry */
689 dentry = (void *) dentry + rec_len;
690 }
[a35b458]691
[38542dc]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 */
[b7fd2a0]707errno_t ext4_directory_find_in_block(block_t *block, ext4_superblock_t *sb,
[38542dc]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;
[a35b458]713
[38542dc]714 /* Set upper bound for cycling */
[7689590]715 uint8_t *addr_limit = block->data + ext4_superblock_get_block_size(sb);
[a35b458]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;
[a35b458]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 }
[a35b458]735
[38542dc]736 uint16_t dentry_len =
737 ext4_directory_entry_ll_get_entry_length(dentry);
[a35b458]738
[06d85e5]739 /* Corrupted entry */
[38542dc]740 if (dentry_len == 0)
[7689590]741 return EINVAL;
[a35b458]742
[06d85e5]743 /* Jump to next entry */
[38542dc]744 dentry = (ext4_directory_entry_ll_t *) ((uint8_t *) dentry + dentry_len);
[7689590]745 }
[a35b458]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 */
[b7fd2a0]758errno_t ext4_directory_destroy_result(ext4_directory_search_result_t *result)
[7689590]759{
[38542dc]760 if (result->block)
[7689590]761 return block_put(result->block);
[a35b458]762
[7689590]763 return EOK;
764}
[eb91db7]765
766/**
767 * @}
[7689590]768 */
Note: See TracBrowser for help on using the repository browser.