source: mainline/uspace/lib/ext4/libext4_filesystem.c@ fe27eb4

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

added many getters and setters

  • Property mode set to 100644
File size: 15.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_filesystem.c
35 * @brief TODO
36 */
37
38#include <byteorder.h>
39#include <errno.h>
40#include <malloc.h>
41#include "libext4.h"
42
43int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
44{
45 int rc;
46 ext4_superblock_t *temp_superblock;
47 size_t block_size;
48 uint32_t block_ids_per_block;
49 int i;
50
51 fs->device = service_id;
52
53 // TODO what does constant 2048 mean?
54 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 2048);
55 if (rc != EOK) {
56 return rc;
57 }
58
59 /* Read superblock from device */
60 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
61 if (rc != EOK) {
62 block_fini(fs->device);
63 return rc;
64 }
65
66 /* Read block size from superblock and check */
67 block_size = ext4_superblock_get_block_size(temp_superblock);
68 if (block_size > EXT4_MAX_BLOCK_SIZE) {
69 block_fini(fs->device);
70 return ENOTSUP;
71 }
72
73 /* Initialize block caching */
74 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
75 if (rc != EOK) {
76 block_fini(fs->device);
77 return rc;
78 }
79
80 block_ids_per_block = block_size / sizeof(uint32_t);
81 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
82 fs->inode_blocks_per_level[0] = 1;
83 for (i = 1; i < 4; i++) {
84 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
85 block_ids_per_block;
86 fs->inode_block_limits[i] = fs->inode_block_limits[i-1] +
87 fs->inode_blocks_per_level[i];
88 }
89
90 /* Return loaded superblock */
91 fs->superblock = temp_superblock;
92
93 return EOK;
94}
95
96int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
97{
98 int rc = EOK;
99 if (write_sb) {
100 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
101 }
102
103 free(fs->superblock);
104 block_fini(fs->device);
105
106 return rc;
107}
108
109int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
110{
111 int rc;
112
113 rc = ext4_superblock_check_sanity(fs->superblock);
114 if (rc != EOK) {
115 return rc;
116 }
117
118 return EOK;
119}
120
121int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *o_read_only)
122{
123 /* Feature flags are present in rev 1 and later */
124 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
125 *o_read_only = false;
126 return EOK;
127 }
128
129 uint32_t incompatible_features;
130 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
131 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
132 if (incompatible_features > 0) {
133 *o_read_only = true;
134 return ENOTSUP;
135 }
136
137 uint32_t compatible_read_only;
138 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
139 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
140 if (compatible_read_only > 0) {
141 *o_read_only = true;
142 }
143
144 return EOK;
145}
146
147int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
148 ext4_block_group_ref_t **ref)
149{
150 int rc;
151 aoff64_t block_id;
152 uint32_t descriptors_per_block;
153 size_t offset;
154 ext4_block_group_ref_t *newref;
155
156 newref = malloc(sizeof(ext4_block_group_ref_t));
157 if (newref == NULL) {
158 return ENOMEM;
159 }
160
161 descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
162 / ext4_superblock_get_desc_size(fs->superblock);
163
164 /* Block group descriptor table starts at the next block after superblock */
165 block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
166
167 /* Find the block containing the descriptor we are looking for */
168 block_id += bgid / descriptors_per_block;
169 offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
170
171 rc = block_get(&newref->block, fs->device, block_id, 0);
172 if (rc != EOK) {
173 free(newref);
174 return rc;
175 }
176
177 newref->block_group = newref->block->data + offset;
178 newref->dirty = false;
179
180 *ref = newref;
181
182 return EOK;
183}
184
185int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
186{
187 int rc;
188
189 if (ref->dirty) {
190 ref->block->dirty = true;
191 }
192
193 rc = block_put(ref->block);
194 free(ref);
195
196 return rc;
197}
198
199int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
200 ext4_inode_ref_t **ref)
201{
202 int rc;
203 aoff64_t block_id;
204 uint32_t block_group;
205 uint32_t offset_in_group;
206 uint32_t byte_offset_in_group;
207 size_t offset_in_block;
208 uint32_t inodes_per_group;
209 uint32_t inode_table_start;
210 uint16_t inode_size;
211 uint32_t block_size;
212 ext4_block_group_ref_t *bg_ref;
213 ext4_inode_ref_t *newref;
214
215 newref = malloc(sizeof(ext4_inode_ref_t));
216 if (newref == NULL) {
217 return ENOMEM;
218 }
219
220 inodes_per_group = ext4_superblock_get_inodes_per_group(fs->superblock);
221
222 /* inode numbers are 1-based, but it is simpler to work with 0-based
223 * when computing indices
224 */
225 index -= 1;
226 block_group = index / inodes_per_group;
227 offset_in_group = index % inodes_per_group;
228
229 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
230 if (rc != EOK) {
231 free(newref);
232 return rc;
233 }
234
235 inode_table_start = ext4_block_group_get_inode_table_first_block(
236 bg_ref->block_group, fs->superblock);
237
238 rc = ext4_filesystem_put_block_group_ref(bg_ref);
239 if (rc != EOK) {
240 free(newref);
241 return rc;
242 }
243
244 inode_size = ext4_superblock_get_inode_size(fs->superblock);
245 block_size = ext4_superblock_get_block_size(fs->superblock);
246
247 byte_offset_in_group = offset_in_group * inode_size;
248
249 block_id = inode_table_start + (byte_offset_in_group / block_size);
250 offset_in_block = byte_offset_in_group % block_size;
251
252 rc = block_get(&newref->block, fs->device, block_id, 0);
253 if (rc != EOK) {
254 free(newref);
255 return rc;
256 }
257
258 newref->inode = newref->block->data + offset_in_block;
259 /* we decremented index above, but need to store the original value
260 * in the reference
261 */
262 newref->index = index+1;
263 newref->dirty = false;
264
265 *ref = newref;
266
267 return EOK;
268}
269
270
271int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
272{
273 int rc;
274
275 if (ref->dirty) {
276 ref->block->dirty = true;
277 }
278
279 rc = block_put(ref->block);
280 free(ref);
281
282 return rc;
283}
284
285int ext4_filesystem_get_inode_data_block_index(ext4_filesystem_t *fs, ext4_inode_t* inode,
286 aoff64_t iblock, uint32_t* fblock)
287{
288 int rc;
289 uint32_t offset_in_block;
290 uint32_t current_block;
291 aoff64_t block_offset_in_level;
292 int i;
293 int level;
294 block_t *block;
295
296 /* Handle inode using extents */
297 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
298 ext4_inode_has_flag(inode, EXT4_INODE_FLAG_EXTENTS)) {
299 current_block = ext4_inode_get_extent_block(inode, iblock, fs->device);
300 *fblock = current_block;
301 return EOK;
302
303 }
304
305 /* Handle simple case when we are dealing with direct reference */
306 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
307 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
308 *fblock = current_block;
309 return EOK;
310 }
311
312 /* Determine the indirection level needed to get the desired block */
313 level = -1;
314 for (i = 1; i < 4; i++) {
315 if (iblock < fs->inode_block_limits[i]) {
316 level = i;
317 break;
318 }
319 }
320
321 if (level == -1) {
322 return EIO;
323 }
324
325 /* Compute offsets for the topmost level */
326 block_offset_in_level = iblock - fs->inode_block_limits[level-1];
327 current_block = ext4_inode_get_indirect_block(inode, level-1);
328 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
329
330 if (current_block == 0) {
331 *fblock = 0;
332 return EOK;
333 }
334
335 /* Navigate through other levels, until we find the block number
336 * or find null reference meaning we are dealing with sparse file
337 */
338 while (level > 0) {
339 rc = block_get(&block, fs->device, current_block, 0);
340 if (rc != EOK) {
341 return rc;
342 }
343
344 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
345
346 rc = block_put(block);
347 if (rc != EOK) {
348 return rc;
349 }
350
351 if (current_block == 0) {
352 /* This is a sparse file */
353 *fblock = 0;
354 return EOK;
355 }
356
357 level -= 1;
358
359 /* If we are on the last level, break here as
360 * there is no next level to visit
361 */
362 if (level == 0) {
363 break;
364 }
365
366 /* Visit the next level */
367 block_offset_in_level %= fs->inode_blocks_per_level[level];
368 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
369 }
370
371 *fblock = current_block;
372
373 return EOK;
374}
375
376
377int ext4_filesystem_set_inode_data_block_index(ext4_filesystem_t *fs,
378 ext4_inode_ref_t *inode_ref, aoff64_t iblock, uint32_t fblock)
379{
380
381 int rc;
382 uint32_t offset_in_block;
383 uint32_t current_block, new_block_addr;
384 uint32_t block_size;
385 aoff64_t block_offset_in_level;
386 int i;
387 int level;
388 block_t *block, *new_block;
389
390 /* Handle inode using extents */
391 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
392 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
393 // TODO
394 return ENOTSUP;
395 }
396
397 /* Handle simple case when we are dealing with direct reference */
398 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
399 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
400 inode_ref->dirty = true;
401 return EOK;
402 }
403
404 /* Determine the indirection level needed to get the desired block */
405 level = -1;
406 for (i = 1; i < 4; i++) {
407 if (iblock < fs->inode_block_limits[i]) {
408 level = i;
409 break;
410 }
411 }
412
413 if (level == -1) {
414 return EIO;
415 }
416
417 block_size = ext4_superblock_get_block_size(fs->superblock);
418
419 /* Compute offsets for the topmost level */
420 block_offset_in_level = iblock - fs->inode_block_limits[level-1];
421 current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
422 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
423
424 if (current_block == 0) {
425 rc = ext4_balloc_alloc_block(fs, inode_ref, &new_block_addr);
426 if (rc != EOK) {
427 // TODO error
428 EXT4FS_DBG("error in allocation");
429 }
430// EXT4FS_DBG("AAA: new addr \%u, level = \%u", new_block_addr, level);
431
432 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
433
434 inode_ref->dirty = true;
435
436 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
437 if (rc != EOK) {
438 EXT4FS_DBG("block load error");
439 // TODO error
440 }
441
442 memset(new_block->data, 0, block_size);
443 new_block->dirty = true;
444
445 rc = block_put(new_block);
446 if (rc != EOK) {
447 EXT4FS_DBG("block put error");
448 }
449
450// EXT4FS_DBG("allocated indirect block for level \%u, during setting iblock \%u", level, (uint32_t)iblock);
451
452 current_block = new_block_addr;
453 }
454
455 /* Navigate through other levels, until we find the block number
456 * or find null reference meaning we are dealing with sparse file
457 */
458 while (level > 0) {
459
460 rc = block_get(&block, fs->device, current_block, 0);
461 if (rc != EOK) {
462 return rc;
463 }
464
465 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
466
467 if ((level > 1) && (current_block == 0)) {
468 rc = ext4_balloc_alloc_block(fs, inode_ref, &new_block_addr);
469 if (rc != EOK) {
470 // TODO error
471 EXT4FS_DBG("allocation error");
472 }
473// EXT4FS_DBG("BBB: new addr \%u, offset = \%u, level = \%u", new_block_addr, offset_in_block, level);
474
475 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
476 if (rc != EOK) {
477 // TODO error
478
479 EXT4FS_DBG("BBB: error block loading");
480
481 }
482 memset(new_block->data, 0, block_size);
483 new_block->dirty = true;
484
485 rc = block_put(new_block);
486 if (rc != EOK) {
487 EXT4FS_DBG("BBB: error indirect block saving");
488 }
489
490 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
491 block->dirty = true;
492 current_block = new_block_addr;
493 }
494
495 if (level == 1) {
496 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
497 block->dirty = true;
498 }
499
500 rc = block_put(block);
501 if (rc != EOK) {
502 return rc;
503 }
504
505 level -= 1;
506
507 /* If we are on the last level, break here as
508 * there is no next level to visit
509 */
510 if (level == 0) {
511 break;
512 }
513
514 /* Visit the next level */
515 block_offset_in_level %= fs->inode_blocks_per_level[level];
516 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
517 }
518
519 return EOK;
520}
521
522int ext4_filesystem_release_inode_block(ext4_filesystem_t *fs,
523 ext4_inode_ref_t *inode_ref, uint32_t iblock)
524{
525 int rc;
526 uint32_t fblock;
527 int i;
528 int level;
529 aoff64_t block_offset_in_level;
530 uint32_t current_block;
531 uint32_t offset_in_block;
532 block_t *block;
533 ext4_inode_t *inode = inode_ref->inode;
534
535 /* TODO handle extents */
536
537
538 /* Handle simple case when we are dealing with direct reference */
539 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
540 fblock = ext4_inode_get_direct_block(inode, iblock);
541 // Sparse file
542 if (fblock == 0) {
543 return EOK;
544 }
545
546 ext4_inode_set_direct_block(inode, iblock, 0);
547 return ext4_balloc_free_block(fs, inode_ref, fblock);
548 }
549
550
551 /* Determine the indirection level needed to get the desired block */
552 level = -1;
553 for (i = 1; i < 4; i++) {
554 if (iblock < fs->inode_block_limits[i]) {
555 level = i;
556 break;
557 }
558 }
559
560 if (level == -1) {
561 return EIO;
562 }
563
564 /* Compute offsets for the topmost level */
565 block_offset_in_level = iblock - fs->inode_block_limits[level-1];
566 current_block = ext4_inode_get_indirect_block(inode, level-1);
567 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
568
569 /* Navigate through other levels, until we find the block number
570 * or find null reference meaning we are dealing with sparse file
571 */
572 while (level > 0) {
573 rc = block_get(&block, fs->device, current_block, 0);
574 if (rc != EOK) {
575 return rc;
576 }
577
578 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
579
580 // Set zero
581 if (level == 1) {
582 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
583 block->dirty = true;
584 }
585
586 rc = block_put(block);
587 if (rc != EOK) {
588 return rc;
589 }
590
591 level -= 1;
592
593 /* If we are on the last level, break here as
594 * there is no next level to visit
595 */
596 if (level == 0) {
597 break;
598 }
599
600 /* Visit the next level */
601 block_offset_in_level %= fs->inode_blocks_per_level[level];
602 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
603 }
604
605 fblock = current_block;
606
607 if (fblock == 0) {
608 return EOK;
609 }
610
611 return ext4_balloc_free_block(fs, inode_ref, fblock);
612
613}
614
615/**
616 * @}
617 */
Note: See TracBrowser for help on using the repository browser.