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

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

Bugfix during new block allocation

  • Property mode set to 100644
File size: 13.0 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
97uint8_t ext4_directory_entry_ll_get_inode_type(
98 ext4_superblock_t *sb, ext4_directory_entry_ll_t *de)
99{
100 if (ext4_superblock_get_rev_level(sb) == 0 &&
101 ext4_superblock_get_minor_rev_level(sb) < 5) {
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{
113 if (ext4_superblock_get_rev_level(sb) == 0 &&
114 ext4_superblock_get_minor_rev_level(sb) < 5) {
115
116 de->inode_type = type;
117 }
118
119 // else do nothing
120
121}
122
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
152 uint64_t size = ext4_inode_get_size(it->fs->superblock, it->inode_ref->inode);
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
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;
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
187 uint32_t next_block_phys_idx;
188 rc = ext4_filesystem_get_inode_data_block_index(it->fs,
189 it->inode_ref->inode, next_block_idx, &next_block_phys_idx);
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;
203
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
213 uint32_t offset_in_block = it->current_offset % block_size;
214
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
262int ext4_directory_add_entry(ext4_filesystem_t *fs, ext4_inode_ref_t * inode_ref,
263 const char *entry_name, ext4_inode_ref_t *child)
264{
265 EXT4FS_DBG("adding dentry \%s to child inode \%u to directory \%u", entry_name, child->index, inode_ref->index);
266
267 int rc;
268
269 // USE index if allowed
270
271 uint16_t name_len = strlen(entry_name);
272 uint16_t required_len = 8 + name_len + (4 - name_len % 4);
273
274 ext4_directory_iterator_t it;
275 rc = ext4_directory_iterator_init(&it, fs, inode_ref, 0);
276 if (rc != EOK) {
277 return rc;
278 }
279
280 while (it.current != NULL) {
281 uint32_t entry_inode = ext4_directory_entry_ll_get_inode(it.current);
282 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(it.current);
283
284 EXT4FS_DBG("inode = \%u, rec_len == \%u, required_len = \%u", entry_inode, rec_len, required_len);
285
286 if ((entry_inode == 0) && (rec_len >= required_len)) {
287
288 // Don't touch entry length
289 ext4_directory_entry_ll_set_inode(it.current, child->index);
290 ext4_directory_entry_ll_set_name_length(fs->superblock, it.current, name_len);
291 it.current_block->dirty = true;
292 return ext4_directory_iterator_fini(&it);
293 }
294
295 if (entry_inode != 0) {
296 uint16_t used_name_len = ext4_directory_entry_ll_get_name_length(
297 fs->superblock, it.current);
298 uint16_t free_space = rec_len - 8 - (used_name_len + (4- used_name_len % 4));
299
300 if (free_space >= required_len) {
301 uint16_t used_len = rec_len - free_space;
302
303 // Cut tail of current entry
304 ext4_directory_entry_ll_set_entry_length(it.current, used_len);
305
306 // SEEK manually
307 it.current_offset += used_len;
308 ext4_directory_entry_ll_t *new_entry = it.current_block->data + it.current_offset;
309
310 // We are sure, that both entries are in the same data block
311 // dirtyness will be set now
312
313 ext4_directory_entry_ll_set_inode(new_entry, child->index);
314 ext4_directory_entry_ll_set_entry_length(new_entry, free_space);
315 ext4_directory_entry_ll_set_name_length(
316 fs->superblock, new_entry, name_len);
317
318 if (ext4_inode_is_type(fs->superblock, child->inode, EXT4_INODE_MODE_DIRECTORY)) {
319 ext4_directory_entry_ll_set_inode_type(
320 fs->superblock, new_entry, EXT4_DIRECTORY_FILETYPE_DIR);
321 } else {
322 ext4_directory_entry_ll_set_inode_type(
323 fs->superblock, new_entry, EXT4_DIRECTORY_FILETYPE_REG_FILE);
324 }
325
326 memcpy(new_entry->name, entry_name, name_len);
327 it.current_block->dirty = true;
328 return ext4_directory_iterator_fini(&it);
329 }
330
331 }
332
333 rc = ext4_directory_iterator_next(&it);
334 if (rc != EOK) {
335 return rc;
336 }
337 }
338
339 // Save position and destroy iterator
340 aoff64_t pos = it.current_offset;
341 ext4_directory_iterator_fini(&it);
342
343 // Compute next block index and allocate data block
344 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
345 uint32_t block_idx = pos / block_size;
346
347 uint32_t fblock;
348 rc = ext4_filesystem_get_inode_data_block_index(fs, inode_ref->inode, block_idx, &fblock);
349 if (rc != EOK) {
350 return rc;
351 }
352
353 if (fblock == 0) {
354 rc = ext4_balloc_alloc_block(fs, inode_ref, &fblock);
355 if (rc != EOK) {
356 return rc;
357 }
358
359 rc = ext4_filesystem_set_inode_data_block_index(fs, inode_ref, block_idx, fblock);
360 if (rc != EOK) {
361 ext4_balloc_free_block(fs, inode_ref, fblock);
362 return rc;
363 }
364
365 uint64_t inode_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
366 inode_size += block_size;
367 ext4_inode_set_size(inode_ref->inode, inode_size);
368
369 inode_ref->dirty = true;
370 }
371
372 // Load block
373 block_t *new_block;
374 rc = block_get(&new_block, fs->device, fblock, BLOCK_FLAGS_NOREAD);
375 if (rc != EOK) {
376 return rc;
377 }
378
379 ext4_directory_entry_ll_t *block_entry = new_block->data;
380
381 ext4_directory_entry_ll_set_entry_length(block_entry, block_size);
382 ext4_directory_entry_ll_set_inode(block_entry, child->index);
383 ext4_directory_entry_ll_set_name_length(fs->superblock, block_entry, name_len);
384 memcpy(block_entry->name, entry_name, name_len);
385
386 new_block->dirty = true;
387 rc = block_put(new_block);
388 if (rc != EOK) {
389 return rc;
390 }
391
392 return EOK;
393}
394
395int ext4_directory_find_entry(ext4_directory_iterator_t *it,
396 ext4_inode_ref_t *parent, const char *name)
397{
398 int rc;
399 uint32_t name_size = strlen(name);
400
401 // Index search
402 if (ext4_superblock_has_feature_compatible(it->fs->superblock, EXT4_FEATURE_COMPAT_DIR_INDEX) &&
403 ext4_inode_has_flag(parent->inode, EXT4_INODE_FLAG_INDEX)) {
404
405 rc = ext4_directory_dx_find_entry(it, it->fs, parent, name_size, name);
406
407 // Check if index is not corrupted
408 if (rc != EXT4_ERR_BAD_DX_DIR) {
409
410 if (rc != EOK) {
411 return rc;
412 }
413 return EOK;
414 }
415
416 EXT4FS_DBG("index is corrupted - doing linear search");
417 }
418
419 bool found = false;
420 // Linear search
421 while (it->current != NULL) {
422 uint32_t inode = ext4_directory_entry_ll_get_inode(it->current);
423
424 /* ignore empty directory entries */
425 if (inode != 0) {
426 uint16_t entry_name_size = ext4_directory_entry_ll_get_name_length(
427 it->fs->superblock, it->current);
428
429 if (entry_name_size == name_size && bcmp(name, it->current->name,
430 name_size) == 0) {
431 found = true;
432 break;
433 }
434 }
435
436 rc = ext4_directory_iterator_next(it);
437 if (rc != EOK) {
438 return rc;
439 }
440 }
441
442 if (!found) {
443 return ENOENT;
444 }
445
446 return EOK;
447}
448
449
450int ext4_directory_remove_entry(ext4_filesystem_t* fs,
451 ext4_inode_ref_t *parent, const char *name)
452{
453 int rc;
454
455 if (!ext4_inode_is_type(fs->superblock, parent->inode,
456 EXT4_INODE_MODE_DIRECTORY)) {
457 return ENOTDIR;
458 }
459
460 ext4_directory_iterator_t it;
461 rc = ext4_directory_iterator_init(&it, fs, parent, 0);
462 if (rc != EOK) {
463 return rc;
464 }
465
466 rc = ext4_directory_find_entry(&it, parent, name);
467 if (rc != EOK) {
468 ext4_directory_iterator_fini(&it);
469 return rc;
470 }
471
472 // TODO modify HTREE index if exists
473
474 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
475 uint32_t pos = it.current_offset % block_size;
476
477 ext4_directory_entry_ll_set_inode(it.current, 0);
478
479 if (pos != 0) {
480 uint32_t offset = 0;
481
482 ext4_directory_entry_ll_t *tmp_dentry = it.current_block->data;
483 uint16_t tmp_dentry_length =
484 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
485
486 while ((offset + tmp_dentry_length) < pos) {
487 offset += ext4_directory_entry_ll_get_entry_length(tmp_dentry);
488 tmp_dentry = it.current_block->data + offset;
489 tmp_dentry_length =
490 ext4_directory_entry_ll_get_entry_length(tmp_dentry);
491 }
492
493 assert(tmp_dentry_length + offset == pos);
494
495 uint16_t del_entry_length =
496 ext4_directory_entry_ll_get_entry_length(it.current);
497 ext4_directory_entry_ll_set_entry_length(tmp_dentry,
498 tmp_dentry_length + del_entry_length);
499
500 }
501
502
503 it.current_block->dirty = true;
504
505 ext4_directory_iterator_fini(&it);
506 return EOK;
507}
508
509
510/**
511 * @}
512 */
Note: See TracBrowser for help on using the repository browser.