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

Last change on this file was 2175178, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Allow creating ext2 dynamic revision. Allow choosing ext filesystem version by mkext4 argument. Fix support for filesystems without filetype feature.

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