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

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

Refafctorization - principle of locality in variables declaration

  • Property mode set to 100644
File size: 12.2 KB
Line 
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
35 * @brief Ext4 directory structure operations.
36 */
37
38#include <byteorder.h>
39#include <errno.h>
40#include <malloc.h>
41#include <string.h>
42#include "libext4.h"
43
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
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
59uint16_t ext4_directory_entry_ll_get_entry_length(
60 ext4_directory_entry_ll_t *de)
61{
62 return uint16_t_le2host(de->entry_length);
63}
64
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
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) {
76
77 return ((uint16_t)de->name_length_high) << 8 |
78 ((uint16_t)de->name_length);
79
80 }
81 return de->name_length;
82
83}
84
85void ext4_directory_entry_ll_set_name_length(ext4_superblock_t *sb,
86 ext4_directory_entry_ll_t *de, uint16_t length)
87{
88 de->name_length = (length << 8) >> 8;
89
90 if (ext4_superblock_get_rev_level(sb) == 0 &&
91 ext4_superblock_get_minor_rev_level(sb) < 5) {
92
93 de->name_length_high = length >> 8;
94 }
95}
96
97
98int ext4_directory_iterator_init(ext4_directory_iterator_t *it,
99 ext4_filesystem_t *fs, ext4_inode_ref_t *inode_ref, aoff64_t pos)
100{
101 it->inode_ref = inode_ref;
102 it->fs = fs;
103 it->current = NULL;
104 it->current_offset = 0;
105 it->current_block = NULL;
106
107 return ext4_directory_iterator_seek(it, pos);
108}
109
110
111int ext4_directory_iterator_next(ext4_directory_iterator_t *it)
112{
113 uint16_t skip;
114
115 assert(it->current != NULL);
116
117 skip = ext4_directory_entry_ll_get_entry_length(it->current);
118
119 return ext4_directory_iterator_seek(it, it->current_offset + skip);
120}
121
122
123int ext4_directory_iterator_seek(ext4_directory_iterator_t *it, aoff64_t pos)
124{
125 int rc;
126
127 uint64_t size = ext4_inode_get_size(it->fs->superblock, it->inode_ref->inode);
128
129 /* The iterator is not valid until we seek to the desired position */
130 it->current = NULL;
131
132 /* Are we at the end? */
133 if (pos >= size) {
134 if (it->current_block) {
135 rc = block_put(it->current_block);
136 it->current_block = NULL;
137 if (rc != EOK) {
138 return rc;
139 }
140 }
141
142 it->current_offset = pos;
143 return EOK;
144 }
145
146 uint32_t block_size = ext4_superblock_get_block_size(it->fs->superblock);
147 aoff64_t current_block_idx = it->current_offset / block_size;
148 aoff64_t next_block_idx = pos / block_size;
149
150 /* If we don't have a block or are moving accross block boundary,
151 * we need to get another block
152 */
153 if (it->current_block == NULL || current_block_idx != next_block_idx) {
154 if (it->current_block) {
155 rc = block_put(it->current_block);
156 it->current_block = NULL;
157 if (rc != EOK) {
158 return rc;
159 }
160 }
161
162 uint32_t next_block_phys_idx;
163 rc = ext4_filesystem_get_inode_data_block_index(it->fs,
164 it->inode_ref->inode, next_block_idx, &next_block_phys_idx);
165 if (rc != EOK) {
166 return rc;
167 }
168
169 rc = block_get(&it->current_block, it->fs->device, next_block_phys_idx,
170 BLOCK_FLAGS_NONE);
171 if (rc != EOK) {
172 it->current_block = NULL;
173 return rc;
174 }
175 }
176
177 it->current_offset = pos;
178
179 return ext4_directory_iterator_set(it, block_size);
180}
181
182static int ext4_directory_iterator_set(ext4_directory_iterator_t *it,
183 uint32_t block_size)
184{
185
186 it->current = NULL;
187
188 uint32_t offset_in_block = it->current_offset % block_size;
189
190 /* Ensure proper alignment */
191 if ((offset_in_block % 4) != 0) {
192 return EIO;
193 }
194
195 /* Ensure that the core of the entry does not overflow the block */
196 if (offset_in_block > block_size - 8) {
197 return EIO;
198 }
199
200 ext4_directory_entry_ll_t *entry = it->current_block->data + offset_in_block;
201
202 /* Ensure that the whole entry does not overflow the block */
203 uint16_t length = ext4_directory_entry_ll_get_entry_length(entry);
204 if (offset_in_block + length > block_size) {
205 return EIO;
206 }
207
208 /* Ensure the name length is not too large */
209 if (ext4_directory_entry_ll_get_name_length(it->fs->superblock,
210 entry) > length-8) {
211 return EIO;
212 }
213
214 it->current = entry;
215 return EOK;
216}
217
218
219int ext4_directory_iterator_fini(ext4_directory_iterator_t *it)
220{
221 int rc;
222
223 it->fs = NULL;
224 it->inode_ref = NULL;
225 it->current = NULL;
226
227 if (it->current_block) {
228 rc = block_put(it->current_block);
229 if (rc != EOK) {
230 return rc;
231 }
232 }
233
234 return EOK;
235}
236
237/**
238 * Fill data block with invalid directory entry.
239 */
240static int ext4_directory_init_block(ext4_filesystem_t *fs, uint32_t fblock)
241{
242 int rc;
243
244 block_t *block;
245 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NOREAD);
246 if (rc != EOK) {
247 return rc;
248 }
249
250 ext4_directory_entry_ll_t *entry = block->data;
251 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
252
253 ext4_directory_entry_ll_set_entry_length(entry, block_size);
254 ext4_directory_entry_ll_set_inode(entry, 0);
255 ext4_directory_entry_ll_set_name_length(fs->superblock, entry, 0);
256 entry->name[0] = 0;
257
258 block->dirty = true;
259 return block_put(block);
260}
261
262int ext4_directory_add_entry(ext4_filesystem_t *fs, ext4_inode_ref_t * inode_ref,
263 const char *entry_name, uint32_t child_inode)
264{
265 int rc;
266
267 // USE index if allowed
268
269 uint16_t name_len = strlen(entry_name);
270 uint16_t required_len = 8 + name_len + (4 - name_len % 4);
271
272 ext4_directory_iterator_t it;
273 rc = ext4_directory_iterator_init(&it, fs, inode_ref, 0);
274 if (rc != EOK) {
275 return rc;
276 }
277
278 while (it.current != NULL) {
279 uint32_t entry_inode = ext4_directory_entry_ll_get_inode(it.current);
280 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(it.current);
281
282 if ((entry_inode == 0) && (rec_len >= required_len)) {
283
284 // Don't touch entry length
285 ext4_directory_entry_ll_set_inode(it.current, child_inode);
286 ext4_directory_entry_ll_set_name_length(fs->superblock, it.current, name_len);
287 it.current_block->dirty = true;
288 return ext4_directory_iterator_fini(&it);
289 }
290
291 if (entry_inode != 0) {
292
293 uint16_t used_name_len = ext4_directory_entry_ll_get_name_length(
294 fs->superblock, it.current);
295 uint16_t free_space = rec_len - 8 - (used_name_len + (4- used_name_len % 4));
296
297 if (free_space >= required_len) {
298 uint16_t used_len = rec_len - free_space;
299
300 // Cut tail of current entry
301 ext4_directory_entry_ll_set_entry_length(it.current, used_len);
302
303 // Jump to newly created
304 rc = ext4_directory_iterator_next(&it);
305 if (rc != EOK) {
306 return rc;
307 }
308
309 // We are sure, that both entries are in the same data block
310 // dirtyness will be set now
311
312 ext4_directory_entry_ll_set_inode(it.current, child_inode);
313 ext4_directory_entry_ll_set_entry_length(it.current, free_space);
314 ext4_directory_entry_ll_set_name_length(
315 fs->superblock, it.current, name_len);
316 memcpy(it.current->name, entry_name, name_len);
317 it.current_block->dirty = true;
318 return ext4_directory_iterator_fini(&it);
319 }
320
321 }
322
323 rc = ext4_directory_iterator_next(&it);
324 if (rc != EOK) {
325 return rc;
326 }
327 }
328
329 // Save position and destroy iterator
330 aoff64_t pos = it.current_offset;
331 ext4_directory_iterator_fini(&it);
332
333 // Compute next block index and allocate data block
334 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
335 uint32_t block_idx = pos / block_size;
336
337 uint32_t fblock;
338 rc = ext4_filesystem_get_inode_data_block_index(fs, inode_ref->inode, block_idx, &fblock);
339 if (rc != EOK) {
340 return rc;
341 }
342
343 if (fblock == 0) {
344 rc = ext4_balloc_alloc_block(fs, inode_ref, &fblock);
345 if (rc != EOK) {
346 return rc;
347 }
348
349 rc = ext4_filesystem_set_inode_data_block_index(fs, inode_ref, block_idx, fblock);
350 if (rc != EOK) {
351 ext4_balloc_free_block(fs, inode_ref, fblock);
352 return rc;
353 }
354
355 inode_ref->dirty = true;
356
357 rc = ext4_directory_init_block(fs, fblock);
358 if (rc != EOK) {
359 return rc;
360 }
361 }
362
363 rc = ext4_directory_iterator_init(&it, fs, inode_ref, pos);
364 if (rc != EOK) {
365 return rc;
366 }
367
368 // Entry length is not affected
369 ext4_directory_entry_ll_set_inode(it.current, child_inode);
370 ext4_directory_entry_ll_set_name_length(fs->superblock, it.current, name_len);
371 memcpy(it.current->name, entry_name, name_len);
372
373 it.current_block->dirty = true;
374
375 return ext4_directory_iterator_fini(&it);
376}
377
378int ext4_directory_find_entry(ext4_directory_iterator_t *it,
379 ext4_inode_ref_t *parent, const char *name)
380{
381 int rc;
382 uint32_t name_size = strlen(name);
383
384 // Index search
385 if (ext4_superblock_has_feature_compatible(it->fs->superblock, EXT4_FEATURE_COMPAT_DIR_INDEX) &&
386 ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX)) {
387
388 rc = ext4_directory_dx_find_entry(it, it->fs, parent, name_size, name);
389
390 // Check if index is not corrupted
391 if (rc != EXT4_ERR_BAD_DX_DIR) {
392
393 if (rc != EOK) {
394 return rc;
395 }
396 return EOK;
397 }
398
399 EXT4FS_DBG("index is corrupted - doing linear search");
400 }
401
402 bool found = false;
403 // Linear search
404 while (it->current != NULL) {
405 uint32_t inode = ext4_directory_entry_ll_get_inode(it->current);
406
407 /* ignore empty directory entries */
408 if (inode != 0) {
409 uint16_t entry_name_size = ext4_directory_entry_ll_get_name_length(
410 it->fs->superblock, it->current);
411
412 if (entry_name_size == name_size && bcmp(name, it->current->name,
413 name_size) == 0) {
414 found = true;
415 break;
416 }
417 }
418
419 rc = ext4_directory_iterator_next(it);
420 if (rc != EOK) {
421 return rc;
422 }
423 }
424
425 if (!found) {
426 return ENOENT;
427 }
428
429 return EOK;
430}
431
432
433int ext4_directory_remove_entry(ext4_filesystem_t* fs,
434 ext4_inode_ref_t *parent, const char *name)
435{
436 int rc;
437
438 if (!ext4_inode_is_type(fs->superblock, parent->inode,
439 EXT4_INODE_MODE_DIRECTORY)) {
440 return ENOTDIR;
441 }
442
443 ext4_directory_iterator_t it;
444 rc = ext4_directory_iterator_init(&it, fs, parent, 0);
445 if (rc != EOK) {
446 return rc;
447 }
448
449 rc = ext4_directory_find_entry(&it, parent, name);
450 if (rc != EOK) {
451 ext4_directory_iterator_fini(&it);
452 return rc;
453 }
454
455 // TODO modify HTREE index if exists
456
457 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
458 uint32_t pos = it.current_offset % block_size;
459
460 ext4_directory_entry_ll_set_inode(it.current, 0);
461
462 if (pos != 0) {
463 uint32_t offset = 0;
464
465 ext4_directory_entry_ll_t *tmp_dentry = it.current_block->data;
466 uint16_t tmp_dentry_length =
467 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
468
469 while ((offset + tmp_dentry_length) < pos) {
470 offset += ext4_directory_entry_ll_get_entry_length(tmp_dentry);
471 tmp_dentry = it.current_block->data + offset;
472 tmp_dentry_length =
473 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
474 }
475
476 assert(tmp_dentry_length + offset == pos);
477
478 uint16_t del_entry_length =
479 ext4_directory_entry_ll_get_entry_length(it.current);
480 ext4_directory_entry_ll_set_entry_length(tmp_dentry,
481 tmp_dentry_length + del_entry_length);
482
483 }
484
485
486 it.current_block->dirty = true;
487
488 ext4_directory_iterator_fini(&it);
489 return EOK;
490}
491
492
493/**
494 * @}
495 */
Note: See TracBrowser for help on using the repository browser.