source: mainline/uspace/lib/ext4/libext4_directory.c@ 1fff583

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

not debugged version of dir index initialization

  • Property mode set to 100644
File size: 15.0 KB
RevLine 
[eb91db7]1/*
2 * Copyright (c) 2011 Frantisek Princ
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]44static int ext4_directory_iterator_set(ext4_directory_iterator_t *,
45 uint32_t);
46
47
48uint32_t ext4_directory_entry_ll_get_inode(ext4_directory_entry_ll_t *de)
49{
50 return uint32_t_le2host(de->inode);
51}
52
[343ccfd]53void ext4_directory_entry_ll_set_inode(ext4_directory_entry_ll_t *de,
54 uint32_t inode)
55{
56 de->inode = host2uint32_t_le(inode);
57}
58
[9b9d37bb]59uint16_t ext4_directory_entry_ll_get_entry_length(
[343ccfd]60 ext4_directory_entry_ll_t *de)
[9b9d37bb]61{
62 return uint16_t_le2host(de->entry_length);
63}
64
[343ccfd]65void ext4_directory_entry_ll_set_entry_length(ext4_directory_entry_ll_t *de,
66 uint16_t length)
67{
68 de->entry_length = host2uint16_t_le(length);
69}
70
[9b9d37bb]71uint16_t ext4_directory_entry_ll_get_name_length(
72 ext4_superblock_t *sb, ext4_directory_entry_ll_t *de)
73{
74 if (ext4_superblock_get_rev_level(sb) == 0 &&
75 ext4_superblock_get_minor_rev_level(sb) < 5) {
[343ccfd]76
[8be96a0]77 return ((uint16_t)de->name_length_high) << 8 |
78 ((uint16_t)de->name_length);
[7bc4508]79
[8be96a0]80 }
81 return de->name_length;
[7bc4508]82
83}
84
[343ccfd]85void ext4_directory_entry_ll_set_name_length(ext4_superblock_t *sb,
86 ext4_directory_entry_ll_t *de, uint16_t length)
[7bc4508]87{
[343ccfd]88 de->name_length = (length << 8) >> 8;
[7bc4508]89
[8be96a0]90 if (ext4_superblock_get_rev_level(sb) == 0 &&
91 ext4_superblock_get_minor_rev_level(sb) < 5) {
[7bc4508]92
[343ccfd]93 de->name_length_high = length >> 8;
94 }
95}
[7bc4508]96
[cd1cc4e6]97uint8_t ext4_directory_entry_ll_get_inode_type(
98 ext4_superblock_t *sb, ext4_directory_entry_ll_t *de)
99{
[1d69c69]100 if (ext4_superblock_get_rev_level(sb) > 0 ||
101 ext4_superblock_get_minor_rev_level(sb) >= 5) {
[cd1cc4e6]102
103 return de->inode_type;
104 }
105
106 return EXT4_DIRECTORY_FILETYPE_UNKNOWN;
107
108}
109
110void ext4_directory_entry_ll_set_inode_type(
111 ext4_superblock_t *sb, ext4_directory_entry_ll_t *de, uint8_t type)
112{
[1d69c69]113 if (ext4_superblock_get_rev_level(sb) > 0 ||
114 ext4_superblock_get_minor_rev_level(sb) >= 5) {
[cd1cc4e6]115
116 de->inode_type = type;
117 }
118
119 // else do nothing
120
121}
[7bc4508]122
[9b9d37bb]123int ext4_directory_iterator_init(ext4_directory_iterator_t *it,
124 ext4_filesystem_t *fs, ext4_inode_ref_t *inode_ref, aoff64_t pos)
125{
126 it->inode_ref = inode_ref;
127 it->fs = fs;
128 it->current = NULL;
129 it->current_offset = 0;
130 it->current_block = NULL;
131
132 return ext4_directory_iterator_seek(it, pos);
133}
134
135
136int ext4_directory_iterator_next(ext4_directory_iterator_t *it)
137{
138 uint16_t skip;
139
140 assert(it->current != NULL);
141
142 skip = ext4_directory_entry_ll_get_entry_length(it->current);
143
144 return ext4_directory_iterator_seek(it, it->current_offset + skip);
145}
146
147
148int ext4_directory_iterator_seek(ext4_directory_iterator_t *it, aoff64_t pos)
149{
150 int rc;
151
[d9bbe45]152 uint64_t size = ext4_inode_get_size(it->fs->superblock, it->inode_ref->inode);
[9b9d37bb]153
154 /* The iterator is not valid until we seek to the desired position */
155 it->current = NULL;
156
157 /* Are we at the end? */
158 if (pos >= size) {
159 if (it->current_block) {
160 rc = block_put(it->current_block);
161 it->current_block = NULL;
162 if (rc != EOK) {
163 return rc;
164 }
165 }
166
167 it->current_offset = pos;
168 return EOK;
169 }
170
[d9bbe45]171 uint32_t block_size = ext4_superblock_get_block_size(it->fs->superblock);
172 aoff64_t current_block_idx = it->current_offset / block_size;
173 aoff64_t next_block_idx = pos / block_size;
[9b9d37bb]174
175 /* If we don't have a block or are moving accross block boundary,
176 * we need to get another block
177 */
178 if (it->current_block == NULL || current_block_idx != next_block_idx) {
179 if (it->current_block) {
180 rc = block_put(it->current_block);
181 it->current_block = NULL;
182 if (rc != EOK) {
183 return rc;
184 }
185 }
186
[d9bbe45]187 uint32_t next_block_phys_idx;
[1ac1ab4]188 rc = ext4_filesystem_get_inode_data_block_index(it->inode_ref,
189 next_block_idx, &next_block_phys_idx);
[9b9d37bb]190 if (rc != EOK) {
191 return rc;
192 }
193
194 rc = block_get(&it->current_block, it->fs->device, next_block_phys_idx,
195 BLOCK_FLAGS_NONE);
196 if (rc != EOK) {
197 it->current_block = NULL;
198 return rc;
199 }
200 }
201
202 it->current_offset = pos;
[e68c834]203
[9b9d37bb]204 return ext4_directory_iterator_set(it, block_size);
205}
206
207static int ext4_directory_iterator_set(ext4_directory_iterator_t *it,
208 uint32_t block_size)
209{
210
211 it->current = NULL;
212
[d9bbe45]213 uint32_t offset_in_block = it->current_offset % block_size;
214
[9b9d37bb]215 /* Ensure proper alignment */
216 if ((offset_in_block % 4) != 0) {
217 return EIO;
218 }
219
220 /* Ensure that the core of the entry does not overflow the block */
221 if (offset_in_block > block_size - 8) {
222 return EIO;
223 }
224
225 ext4_directory_entry_ll_t *entry = it->current_block->data + offset_in_block;
226
227 /* Ensure that the whole entry does not overflow the block */
228 uint16_t length = ext4_directory_entry_ll_get_entry_length(entry);
229 if (offset_in_block + length > block_size) {
230 return EIO;
231 }
232
233 /* Ensure the name length is not too large */
234 if (ext4_directory_entry_ll_get_name_length(it->fs->superblock,
235 entry) > length-8) {
236 return EIO;
237 }
238
239 it->current = entry;
240 return EOK;
241}
242
243
244int ext4_directory_iterator_fini(ext4_directory_iterator_t *it)
245{
246 int rc;
247
248 it->fs = NULL;
249 it->inode_ref = NULL;
250 it->current = NULL;
251
252 if (it->current_block) {
253 rc = block_put(it->current_block);
254 if (rc != EOK) {
255 return rc;
256 }
257 }
258
259 return EOK;
260}
261
[5c83612b]262void ext4_directory_write_entry(ext4_superblock_t *sb,
[1d69c69]263 ext4_directory_entry_ll_t *entry, uint16_t entry_len,
264 ext4_inode_ref_t *child, const char *name, size_t name_len)
265{
[7eb033ce]266
267 EXT4FS_DBG("writing entry \%s, len \%u, addr = \%u", name, entry_len, (uint32_t)entry);
268
[1d69c69]269 ext4_directory_entry_ll_set_inode(entry, child->index);
270 ext4_directory_entry_ll_set_entry_length(entry, entry_len);
271 ext4_directory_entry_ll_set_name_length(sb, entry, name_len);
272
273 if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY)) {
274 ext4_directory_entry_ll_set_inode_type(
275 sb, entry, EXT4_DIRECTORY_FILETYPE_DIR);
276 } else {
277 ext4_directory_entry_ll_set_inode_type(
278 sb, entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
279 }
280 memcpy(entry->name, name, name_len);
281}
282
[1ac1ab4]283int ext4_directory_add_entry(ext4_inode_ref_t * parent,
[d0d7afb]284 const char *name, ext4_inode_ref_t *child)
[73196d2]285{
286 int rc;
287
[d0d7afb]288 EXT4FS_DBG("adding entry to directory \%u [ino = \%u, name = \%s]", parent->index, child->index, name);
[565b6ff]289
[1ac1ab4]290 ext4_filesystem_t *fs = parent->fs;
291
[565b6ff]292 // Index adding (if allowed)
293 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_COMPAT_DIR_INDEX) &&
294 ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX)) {
295
[7eb033ce]296 EXT4FS_DBG("index");
297
[1ac1ab4]298 rc = ext4_directory_dx_add_entry(parent, child, name);
[565b6ff]299
300 // Check if index is not corrupted
301 if (rc != EXT4_ERR_BAD_DX_DIR) {
302
303 if (rc != EOK) {
304 return rc;
305 }
306
307 return EOK;
308 }
309
310 // Needed to clear dir index flag
311 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX);
312 parent->dirty = true;
313
314 EXT4FS_DBG("index is corrupted - doing linear algorithm, index flag cleared");
315 }
316
317 // Linear algorithm
[73196d2]318
[7eb033ce]319 uint32_t iblock = 0, fblock = 0;
[565b6ff]320 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[d0d7afb]321 uint32_t inode_size = ext4_inode_get_size(fs->superblock, parent->inode);
322 uint32_t total_blocks = inode_size / block_size;
[565b6ff]323
[d0d7afb]324 uint32_t name_len = strlen(name);
[73196d2]325
[7689590]326 // Find block, where is space for new entry
[d0d7afb]327 bool success = false;
328 for (iblock = 0; iblock < total_blocks; ++iblock) {
[73196d2]329
[1ac1ab4]330 rc = ext4_filesystem_get_inode_data_block_index(parent, iblock, &fblock);
[d0d7afb]331 if (rc != EOK) {
332 return rc;
[73196d2]333 }
334
[d0d7afb]335 block_t *block;
336 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
337 if (rc != EOK) {
338 return rc;
339 }
[73196d2]340
[d0d7afb]341 rc = ext4_directory_try_insert_entry(fs->superblock, block, child, name, name_len);
342 if (rc == EOK) {
343 success = true;
[73196d2]344 }
345
[d0d7afb]346 rc = block_put(block);
[73196d2]347 if (rc != EOK) {
348 return rc;
349 }
[d0d7afb]350
351 if (success) {
352 return EOK;
353 }
[73196d2]354 }
355
[7689590]356 // No free block found - needed to allocate next block
[b7e0260]357
[7eb033ce]358 iblock = 0;
359 fblock = 0;
[5b16912]360 rc = ext4_filesystem_append_inode_block(parent, &fblock, &iblock);
[b7e0260]361 if (rc != EOK) {
362 return rc;
363 }
364
[7eb033ce]365 EXT4FS_DBG("using iblock \%u fblock \%u", iblock, fblock);
366
[7689590]367 // Load new block
[ed6fdc7]368 block_t *new_block;
369 rc = block_get(&new_block, fs->device, fblock, BLOCK_FLAGS_NOREAD);
[b7e0260]370 if (rc != EOK) {
371 return rc;
372 }
373
[7eb033ce]374 EXT4FS_DBG("loaded");
375
[ca3d77a]376 // Fill block with zeroes
377 memset(new_block->data, 0, block_size);
[ed6fdc7]378 ext4_directory_entry_ll_t *block_entry = new_block->data;
[d0d7afb]379 ext4_directory_write_entry(fs->superblock, block_entry, block_size, child, name, name_len);
[b7e0260]380
[7eb033ce]381 EXT4FS_DBG("written");
382
[7689590]383 // Save new block
[ed6fdc7]384 new_block->dirty = true;
385 rc = block_put(new_block);
386 if (rc != EOK) {
387 return rc;
388 }
[b7e0260]389
[7eb033ce]390 EXT4FS_DBG("returning");
391
[ed6fdc7]392 return EOK;
[73196d2]393}
[246a5af]394
[1ac1ab4]395int ext4_directory_find_entry(ext4_directory_search_result_t *result,
396 ext4_inode_ref_t *parent, const char *name)
[f49638e]397{
398 int rc;
[7689590]399 uint32_t name_len = strlen(name);
[f49638e]400
[1ac1ab4]401 ext4_superblock_t *sb = parent->fs->superblock;
402
[8be96a0]403 // Index search
[1ac1ab4]404 if (ext4_superblock_has_feature_compatible(sb, EXT4_FEATURE_COMPAT_DIR_INDEX) &&
[8be96a0]405 ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX)) {
[f49638e]406
[1ac1ab4]407 rc = ext4_directory_dx_find_entry(result, parent, name_len, name);
[f49638e]408
[8be96a0]409 // Check if index is not corrupted
410 if (rc != EXT4_ERR_BAD_DX_DIR) {
[f49638e]411
[8be96a0]412 if (rc != EOK) {
413 return rc;
414 }
415 return EOK;
[f49638e]416 }
417
[8be96a0]418 EXT4FS_DBG("index is corrupted - doing linear search");
419 }
[f49638e]420
[7689590]421 uint32_t iblock, fblock;
[1ac1ab4]422 uint32_t block_size = ext4_superblock_get_block_size(sb);
423 uint32_t inode_size = ext4_inode_get_size(sb, parent->inode);
[7689590]424 uint32_t total_blocks = inode_size / block_size;
[8be96a0]425
[7689590]426 for (iblock = 0; iblock < total_blocks; ++iblock) {
[8be96a0]427
[1ac1ab4]428 rc = ext4_filesystem_get_inode_data_block_index(parent, iblock, &fblock);
[7689590]429 if (rc != EOK) {
430 return rc;
[f49638e]431 }
432
[7689590]433 block_t *block;
[1ac1ab4]434 rc = block_get(&block, parent->fs->device, fblock, BLOCK_FLAGS_NONE);
[f49638e]435 if (rc != EOK) {
436 return rc;
437 }
438
[7689590]439 // find block entry
440 ext4_directory_entry_ll_t *res_entry;
[1ac1ab4]441 rc = ext4_directory_find_in_block(block, sb, name_len, name, &res_entry);
[7689590]442 if (rc == EOK) {
443 result->block = block;
444 result->dentry = res_entry;
445 return EOK;
446 }
447
448 rc = block_put(block);
449 if (rc != EOK) {
450 return rc;
451 }
[f49638e]452 }
453
[7689590]454 result->block = NULL;
455 result->dentry = NULL;
456
457 return ENOENT;
[8be96a0]458}
459
460
[1ac1ab4]461int ext4_directory_remove_entry(ext4_inode_ref_t *parent, const char *name)
[8be96a0]462{
463 int rc;
464
[1ac1ab4]465 if (!ext4_inode_is_type(parent->fs->superblock, parent->inode,
[8be96a0]466 EXT4_INODE_MODE_DIRECTORY)) {
467 return ENOTDIR;
468 }
469
[7689590]470 ext4_directory_search_result_t result;
[1ac1ab4]471 rc = ext4_directory_find_entry(&result, parent, name);
[8be96a0]472 if (rc != EOK) {
473 return rc;
474 }
475
[7689590]476 ext4_directory_entry_ll_set_inode(result.dentry, 0);
[f49638e]477
[7689590]478 uint32_t pos = (void *)result.dentry - result.block->data;
[f49638e]479
[7689590]480 uint32_t offset = 0;
[f49638e]481 if (pos != 0) {
482
[7689590]483 ext4_directory_entry_ll_t *tmp_dentry = result.block->data;
[f49638e]484 uint16_t tmp_dentry_length =
485 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
486
487 while ((offset + tmp_dentry_length) < pos) {
488 offset += ext4_directory_entry_ll_get_entry_length(tmp_dentry);
[7689590]489 tmp_dentry = result.block->data + offset;
[f49638e]490 tmp_dentry_length =
491 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
492 }
493
494 assert(tmp_dentry_length + offset == pos);
495
496 uint16_t del_entry_length =
[7689590]497 ext4_directory_entry_ll_get_entry_length(result.dentry);
[f49638e]498 ext4_directory_entry_ll_set_entry_length(tmp_dentry,
499 tmp_dentry_length + del_entry_length);
500
501 }
502
[7689590]503 result.block->dirty = true;
[f49638e]504
[7689590]505 return ext4_directory_destroy_result(&result);
[f49638e]506}
[7bc4508]507
[7689590]508
[d0d7afb]509int ext4_directory_try_insert_entry(ext4_superblock_t *sb,
510 block_t *target_block, ext4_inode_ref_t *child,
511 const char *name, uint32_t name_len)
512{
513 uint32_t block_size = ext4_superblock_get_block_size(sb);
514 uint16_t required_len = sizeof(ext4_fake_directory_entry_t) + name_len;
515 if ((required_len % 4) != 0) {
516 required_len += 4 - (required_len % 4);
517 }
518
519 ext4_directory_entry_ll_t *dentry = target_block->data;
520 ext4_directory_entry_ll_t *stop = target_block->data + block_size;
521
522 while (dentry < stop) {
523
524 uint32_t inode = ext4_directory_entry_ll_get_inode(dentry);
525 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(dentry);
526
527 if ((inode == 0) && (rec_len >= required_len)) {
528 ext4_directory_write_entry(sb, dentry, rec_len, child, name, name_len);
529 target_block->dirty = true;
530 return EOK;
531 }
532
533 if (inode != 0) {
534 uint16_t used_name_len =
535 ext4_directory_entry_ll_get_name_length(sb, dentry);
536
537 uint16_t used_space =
538 sizeof(ext4_fake_directory_entry_t) + used_name_len;
539 if ((used_name_len % 4) != 0) {
540 used_space += 4 - (used_name_len % 4);
541 }
542 uint16_t free_space = rec_len - used_space;
543
544 if (free_space >= required_len) {
545
546 // Cut tail of current entry
547 ext4_directory_entry_ll_set_entry_length(dentry, used_space);
548 ext4_directory_entry_ll_t *new_entry =
549 (void *)dentry + used_space;
550 ext4_directory_write_entry(sb, new_entry,
551 free_space, child, name, name_len);
552
553 target_block->dirty = true;
554 return EOK;
555 }
556 }
557
558 dentry = (void *)dentry + rec_len;
559 }
560
561 return ENOSPC;
562}
563
[7689590]564int ext4_directory_find_in_block(block_t *block,
565 ext4_superblock_t *sb, size_t name_len, const char *name,
566 ext4_directory_entry_ll_t **res_entry)
567{
568
569 ext4_directory_entry_ll_t *dentry = (ext4_directory_entry_ll_t *)block->data;
570 uint8_t *addr_limit = block->data + ext4_superblock_get_block_size(sb);
571
572 while ((uint8_t *)dentry < addr_limit) {
573
574 if ((uint8_t*) dentry + name_len > addr_limit) {
575 break;
576 }
577
578 if (dentry->inode != 0) {
579 if (name_len == ext4_directory_entry_ll_get_name_length(sb, dentry)) {
580 // Compare names
581 if (bcmp((uint8_t *)name, dentry->name, name_len) == 0) {
582 *res_entry = dentry;
583 return EOK;
584 }
585 }
586 }
587
588 // Goto next entry
589 uint16_t dentry_len = ext4_directory_entry_ll_get_entry_length(dentry);
590
591 if (dentry_len == 0) {
592 return EINVAL;
593 }
594
595 dentry = (ext4_directory_entry_ll_t *)((uint8_t *)dentry + dentry_len);
596 }
597
598 return ENOENT;
599}
600
601int ext4_directory_destroy_result(ext4_directory_search_result_t *result)
602{
603 if (result->block) {
604 return block_put(result->block);
605 }
606
607 return EOK;
608}
[eb91db7]609
610/**
611 * @}
[7689590]612 */
Note: See TracBrowser for help on using the repository browser.