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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e25d78c was fe61181, checked in by Frantisek Princ <frantisek.princ@…>, 13 years ago

Debug messages removed

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