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
RevLine 
[6c501f8]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
[9b9d37bb]38#include <byteorder.h>
[6c501f8]39#include <errno.h>
[01ab41b]40#include <malloc.h>
[3711e7e]41#include "libext4.h"
[6c501f8]42
43int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
44{
[01ab41b]45 int rc;
46 ext4_superblock_t *temp_superblock;
47 size_t block_size;
[a9a0982]48 uint32_t block_ids_per_block;
49 int i;
[01ab41b]50
51 fs->device = service_id;
52
[9c0c0e1]53 // TODO what does constant 2048 mean?
[01ab41b]54 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 2048);
55 if (rc != EOK) {
56 return rc;
57 }
58
[9c0c0e1]59 /* Read superblock from device */
[01ab41b]60 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
61 if (rc != EOK) {
62 block_fini(fs->device);
63 return rc;
64 }
65
[9c0c0e1]66 /* Read block size from superblock and check */
[01ab41b]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
[9c0c0e1]73 /* Initialize block caching */
[b12ca16]74 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
[01ab41b]75 if (rc != EOK) {
76 block_fini(fs->device);
77 return rc;
78 }
79
[a9a0982]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++) {
[6088193]84 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
[a9a0982]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
[9c0c0e1]90 /* Return loaded superblock */
[01ab41b]91 fs->superblock = temp_superblock;
92
[6c501f8]93 return EOK;
94}
95
[ae3d4f8]96int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
[3711e7e]97{
[ae3d4f8]98 int rc = EOK;
99 if (write_sb) {
100 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
101 }
102
[3711e7e]103 free(fs->superblock);
104 block_fini(fs->device);
[ae3d4f8]105
106 return rc;
[3711e7e]107}
108
[6c501f8]109int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
110{
[01ab41b]111 int rc;
112
113 rc = ext4_superblock_check_sanity(fs->superblock);
114 if (rc != EOK) {
115 return rc;
116 }
117
[6c501f8]118 return EOK;
119}
120
[9c0c0e1]121int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *o_read_only)
[6c501f8]122{
[9c0c0e1]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
[6c501f8]144 return EOK;
145}
146
[3711e7e]147int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
148 ext4_block_group_ref_t **ref)
[6c501f8]149{
[3711e7e]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)
[c25e39b]162 / ext4_superblock_get_desc_size(fs->superblock);
[3711e7e]163
164 /* Block group descriptor table starts at the next block after superblock */
[3712434]165 block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
[3711e7e]166
167 /* Find the block containing the descriptor we are looking for */
168 block_id += bgid / descriptors_per_block;
[c25e39b]169 offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
[3711e7e]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;
[12b4a7f]178 newref->dirty = false;
[3711e7e]179
180 *ref = newref;
181
182 return EOK;
[6c501f8]183}
184
[829d238]185int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
186{
187 int rc;
188
[12b4a7f]189 if (ref->dirty) {
190 ref->block->dirty = true;
191 }
192
[829d238]193 rc = block_put(ref->block);
194 free(ref);
195
196 return rc;
197}
198
[3711e7e]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(
[fe27eb4]236 bg_ref->block_group, fs->superblock);
[3711e7e]237
[829d238]238 rc = ext4_filesystem_put_block_group_ref(bg_ref);
239 if (rc != EOK) {
240 free(newref);
241 return rc;
242 }
243
[3711e7e]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;
[052e82d]263 newref->dirty = false;
[3711e7e]264
265 *ref = newref;
266
[9b9d37bb]267 return EOK;
268}
269
[e68c834]270
[9b9d37bb]271int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
272{
273 int rc;
274
[052e82d]275 if (ref->dirty) {
276 ref->block->dirty = true;
277 }
278
[9b9d37bb]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
[8958a26]296 /* Handle inode using extents */
[c25e39b]297 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
298 ext4_inode_has_flag(inode, EXT4_INODE_FLAG_EXTENTS)) {
[1a7756a]299 current_block = ext4_inode_get_extent_block(inode, iblock, fs->device);
[acd869e]300 *fblock = current_block;
301 return EOK;
302
303 }
304
[9b9d37bb]305 /* Handle simple case when we are dealing with direct reference */
[e68c834]306 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[9b9d37bb]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++) {
[a9a0982]315 if (iblock < fs->inode_block_limits[i]) {
[9b9d37bb]316 level = i;
317 break;
318 }
319 }
320
321 if (level == -1) {
322 return EIO;
323 }
324
325 /* Compute offsets for the topmost level */
[a9a0982]326 block_offset_in_level = iblock - fs->inode_block_limits[level-1];
[9b9d37bb]327 current_block = ext4_inode_get_indirect_block(inode, level-1);
[a9a0982]328 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
[9b9d37bb]329
[6088193]330 if (current_block == 0) {
331 *fblock = 0;
332 return EOK;
333 }
334
[9b9d37bb]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 */
[a9a0982]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];
[9b9d37bb]369 }
370
371 *fblock = current_block;
372
[3711e7e]373 return EOK;
374}
[6c501f8]375
[d5a78e28]376
[35f48f2]377int ext4_filesystem_set_inode_data_block_index(ext4_filesystem_t *fs,
[1e65444]378 ext4_inode_ref_t *inode_ref, aoff64_t iblock, uint32_t fblock)
[35f48f2]379{
380
[1e65444]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;
[35f48f2]389
390 /* Handle inode using extents */
391 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
[1e65444]392 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
[35f48f2]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) {
[1e65444]399 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
400 inode_ref->dirty = true;
[35f48f2]401 return EOK;
402 }
403
[1e65444]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) {
[b12ca16]425 rc = ext4_balloc_alloc_block(fs, inode_ref, &new_block_addr);
[1e65444]426 if (rc != EOK) {
427 // TODO error
[6088193]428 EXT4FS_DBG("error in allocation");
[1e65444]429 }
[6088193]430// EXT4FS_DBG("AAA: new addr \%u, level = \%u", new_block_addr, level);
[1e65444]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
[6088193]450// EXT4FS_DBG("allocated indirect block for level \%u, during setting iblock \%u", level, (uint32_t)iblock);
451
[1e65444]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
[b12ca16]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 }
[ae3d4f8]473// EXT4FS_DBG("BBB: new addr \%u, offset = \%u, level = \%u", new_block_addr, offset_in_block, level);
[1e65444]474
[b12ca16]475 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
476 if (rc != EOK) {
477 // TODO error
[1e65444]478
[b12ca16]479 EXT4FS_DBG("BBB: error block loading");
[6088193]480
[b12ca16]481 }
482 memset(new_block->data, 0, block_size);
483 new_block->dirty = true;
[6088193]484
[b12ca16]485 rc = block_put(new_block);
486 if (rc != EOK) {
487 EXT4FS_DBG("BBB: error indirect block saving");
488 }
[1e65444]489
[b12ca16]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 }
[1e65444]494
[b12ca16]495 if (level == 1) {
496 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
497 block->dirty = true;
[1e65444]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 }
[35f48f2]518
519 return EOK;
520}
521
[052e82d]522int ext4_filesystem_release_inode_block(ext4_filesystem_t *fs,
523 ext4_inode_ref_t *inode_ref, uint32_t iblock)
[d5a78e28]524{
525 int rc;
526 uint32_t fblock;
[052e82d]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;
[d5a78e28]534
[052e82d]535 /* TODO handle extents */
[d5a78e28]536
[12b4a7f]537
[052e82d]538 /* Handle simple case when we are dealing with direct reference */
539 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[12b4a7f]540 fblock = ext4_inode_get_direct_block(inode, iblock);
[052e82d]541 // Sparse file
542 if (fblock == 0) {
543 return EOK;
544 }
[d5a78e28]545
[052e82d]546 ext4_inode_set_direct_block(inode, iblock, 0);
[b12ca16]547 return ext4_balloc_free_block(fs, inode_ref, fblock);
[12b4a7f]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;
[052e82d]557 }
[12b4a7f]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];
[d5a78e28]568
[12b4a7f]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;
[052e82d]576 }
[d5a78e28]577
[12b4a7f]578 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
[d5a78e28]579
[12b4a7f]580 // Set zero
581 if (level == 1) {
582 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
583 block->dirty = true;
[052e82d]584 }
[d5a78e28]585
[12b4a7f]586 rc = block_put(block);
587 if (rc != EOK) {
588 return rc;
589 }
[d5a78e28]590
[12b4a7f]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;
[052e82d]598 }
[12b4a7f]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;
[052e82d]609 }
[d5a78e28]610
[b12ca16]611 return ext4_balloc_free_block(fs, inode_ref, fblock);
[d5a78e28]612
613}
614
[6c501f8]615/**
616 * @}
617 */
Note: See TracBrowser for help on using the repository browser.