source: mainline/uspace/lib/ext2/libext2_filesystem.c@ d3ce33fa

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d3ce33fa was 79ae36dd, checked in by Martin Decky <martin@…>, 14 years ago

new async framework with integrated exchange tracking

  • strict isolation between low-level IPC and high-level async framework with integrated exchange tracking
    • each IPC connection is represented by an async_sess_t structure
    • each IPC exchange is represented by an async_exch_t structure
    • exchange management is either based on atomic messages (EXCHANGE_ATOMIC), locking (EXCHANGE_SERIALIZE) or connection cloning (EXCHANGE_CLONE)
  • async_obsolete: temporary compatibility layer to keep old async clients working (several pieces of code are currently broken, but only non-essential functionality)
  • IPC_M_PHONE_HANGUP is now method no. 0 (for elegant boolean evaluation)
  • IPC_M_DEBUG_ALL has been renamed to IPC_M_DEBUG
  • IPC_M_PING has been removed (VFS protocol now has VFS_IN_PING)
  • console routines in libc have been rewritten for better abstraction
  • additional use for libc-private header files (FILE structure opaque to the client)
  • various cstyle changes (typos, indentation, missing externs in header files, improved comments, etc.)
  • Property mode set to 100644
File size: 13.3 KB
RevLine 
[36bca8eb]1/*
2 * Copyright (c) 2011 Martin Sucha
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 libext2
30 * @{
31 */
32/**
33 * @file
34 */
35
[d241aae]36#include "libext2_filesystem.h"
37#include "libext2_superblock.h"
38#include "libext2_block_group.h"
[ce13577]39#include "libext2_inode.h"
[36bca8eb]40#include <errno.h>
41#include <libblock.h>
42#include <malloc.h>
[ad34feb]43#include <assert.h>
[d7c3367]44#include <byteorder.h>
[36bca8eb]45
46/**
47 * Initialize an instance of filesystem on the device.
48 * This function reads superblock from the device and
49 * initializes libblock cache with appropriate logical block size.
[566c401]50 *
[8bd5dad]51 * @param fs Pointer to ext2_filesystem_t to initialize
52 * @param devmap_handle Device handle of the block device
[1d6f507]53 *
54 * @return EOK on success or negative error code on failure
[36bca8eb]55 */
[566c401]56int ext2_filesystem_init(ext2_filesystem_t *fs, devmap_handle_t devmap_handle)
57{
[36bca8eb]58 int rc;
59 ext2_superblock_t *temp_superblock;
[8bd5dad]60 size_t block_size;
[36bca8eb]61
[566c401]62 fs->device = devmap_handle;
[36bca8eb]63
[79ae36dd]64 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 2048);
[36bca8eb]65 if (rc != EOK) {
66 return rc;
67 }
68
[566c401]69 rc = ext2_superblock_read_direct(fs->device, &temp_superblock);
[36bca8eb]70 if (rc != EOK) {
[566c401]71 block_fini(fs->device);
[36bca8eb]72 return rc;
73 }
74
[8bd5dad]75 block_size = ext2_superblock_get_block_size(temp_superblock);
76
77 if (block_size > EXT2_MAX_BLOCK_SIZE) {
78 block_fini(fs->device);
79 return ENOTSUP;
80 }
81
82 rc = block_cache_init(devmap_handle, block_size, 0, CACHE_MODE_WT);
83 if (rc != EOK) {
84 block_fini(fs->device);
85 return rc;
86 }
87
88 fs->superblock = temp_superblock;
[36bca8eb]89
90 return EOK;
91}
92
[1d6f507]93/**
94 * Check filesystem for sanity
95 *
96 * @param fs Pointer to ext2_filesystem_t to check
97 * @return EOK on success or negative error code on failure
98 */
99int ext2_filesystem_check_sanity(ext2_filesystem_t *fs)
100{
101 int rc;
102
103 rc = ext2_superblock_check_sanity(fs->superblock);
104 if (rc != EOK) {
105 return rc;
106 }
107
108 return EOK;
109}
110
[eef306c]111/**
112 * Check feature flags
113 *
114 * @param fs Pointer to ext2_filesystem_t to check
115 * @param read_only bool to set to true if the fs needs to be read-only
116 * @return EOK on success or negative error code on failure
117 */
118int ext2_filesystem_check_flags(ext2_filesystem_t *fs, bool *o_read_only)
119{
[260c052]120 /* feature flags are present in rev 1 and later */
[eef306c]121 if (ext2_superblock_get_rev_major(fs->superblock) == 0) {
122 *o_read_only = false;
[260c052]123 return EOK;
[eef306c]124 }
125
126 uint32_t incompatible;
127 uint32_t read_only;
128
129 incompatible = ext2_superblock_get_features_incompatible(fs->superblock);
130 read_only = ext2_superblock_get_features_read_only(fs->superblock);
131
[260c052]132 /* check whether we support all features
133 * first unset any supported feature flags
134 * and see whether any unspported feature remains */
[eef306c]135 incompatible &= ~EXT2_SUPPORTED_INCOMPATIBLE_FEATURES;
136 read_only &= ~EXT2_SUPPORTED_READ_ONLY_FEATURES;
137
138 if (incompatible > 0) {
139 *o_read_only = true;
140 return ENOTSUP;
141 }
142
143 if (read_only > 0) {
144 *o_read_only = true;
145 }
146
147 return EOK;
148}
149
[d241aae]150/**
151 * Get a reference to block descriptor
152 *
153 * @param fs Pointer to filesystem information
154 * @param bgid Index of block group to find
155 * @param ref Pointer where to store pointer to block group reference
156 *
157 * @return EOK on success or negative error code on failure
158 */
159int ext2_filesystem_get_block_group_ref(ext2_filesystem_t *fs, uint32_t bgid,
160 ext2_block_group_ref_t **ref)
161{
162 int rc;
163 aoff64_t block_id;
164 uint32_t descriptors_per_block;
165 size_t offset;
166 ext2_block_group_ref_t *newref;
167
168 newref = malloc(sizeof(ext2_block_group_ref_t));
169 if (newref == NULL) {
170 return ENOMEM;
171 }
172
173 descriptors_per_block = ext2_superblock_get_block_size(fs->superblock)
174 / EXT2_BLOCK_GROUP_DESCRIPTOR_SIZE;
175
[260c052]176 /* Block group descriptor table starts at the next block after superblock */
[d241aae]177 block_id = ext2_superblock_get_first_block(fs->superblock) + 1;
178
[260c052]179 /* Find the block containing the descriptor we are looking for */
[d241aae]180 block_id += bgid / descriptors_per_block;
181 offset = (bgid % descriptors_per_block) * EXT2_BLOCK_GROUP_DESCRIPTOR_SIZE;
182
183 rc = block_get(&newref->block, fs->device, block_id, 0);
184 if (rc != EOK) {
185 free(newref);
186 return rc;
187 }
188
189 newref->block_group = newref->block->data + offset;
190
191 *ref = newref;
192
193 return EOK;
194}
195
196/**
197 * Free a reference to block group
198 *
199 * @param ref Pointer to block group reference to free
200 *
201 * @return EOK on success or negative error code on failure
202 */
203int ext2_filesystem_put_block_group_ref(ext2_block_group_ref_t *ref)
204{
205 int rc;
206
207 rc = block_put(ref->block);
208 free(ref);
209
210 return rc;
211}
212
[ce13577]213/**
214 * Get a reference to inode
215 *
216 * @param fs Pointer to filesystem information
217 * @param index The index number of the inode
218 * @param ref Pointer where to store pointer to inode reference
219 *
220 * @return EOK on success or negative error code on failure
221 */
222int ext2_filesystem_get_inode_ref(ext2_filesystem_t *fs, uint32_t index,
223 ext2_inode_ref_t **ref)
224{
225 int rc;
226 aoff64_t block_id;
227 uint32_t block_group;
228 uint32_t offset_in_group;
[a2a1792]229 uint32_t byte_offset_in_group;
230 size_t offset_in_block;
[ce13577]231 uint32_t inodes_per_group;
232 uint32_t inode_table_start;
233 uint16_t inode_size;
234 uint32_t block_size;
235 ext2_block_group_ref_t *bg_ref;
236 ext2_inode_ref_t *newref;
237
238 newref = malloc(sizeof(ext2_inode_ref_t));
239 if (newref == NULL) {
240 return ENOMEM;
241 }
242
243 inodes_per_group = ext2_superblock_get_inodes_per_group(fs->superblock);
244
[260c052]245 /* inode numbers are 1-based, but it is simpler to work with 0-based
246 * when computing indices
247 */
[ce13577]248 index -= 1;
249 block_group = index / inodes_per_group;
250 offset_in_group = index % inodes_per_group;
251
252 rc = ext2_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
253 if (rc != EOK) {
254 free(newref);
255 return rc;
256 }
257
258 inode_table_start = ext2_block_group_get_inode_table_first_block(
259 bg_ref->block_group);
260
261 inode_size = ext2_superblock_get_inode_size(fs->superblock);
262 block_size = ext2_superblock_get_block_size(fs->superblock);
263
[a2a1792]264 byte_offset_in_group = offset_in_group * inode_size;
265
266 block_id = inode_table_start + (byte_offset_in_group / block_size);
267 offset_in_block = byte_offset_in_group % block_size;
[ce13577]268
269 rc = block_get(&newref->block, fs->device, block_id, 0);
270 if (rc != EOK) {
271 free(newref);
272 return rc;
273 }
274
[a2a1792]275 newref->inode = newref->block->data + offset_in_block;
[260c052]276 /* we decremented index above, but need to store the original value
277 * in the reference
278 */
279 newref->index = index+1;
[ce13577]280
281 *ref = newref;
282
283 return EOK;
284}
285
286/**
287 * Free a reference to inode
288 *
289 * @param ref Pointer to inode reference to free
290 *
291 * @return EOK on success or negative error code on failure
292 */
293int ext2_filesystem_put_inode_ref(ext2_inode_ref_t *ref)
294{
295 int rc;
296
297 rc = block_put(ref->block);
298 free(ref);
299
300 return rc;
301}
302
[ad34feb]303/**
304 * Find a filesystem block number where iblock-th data block
305 * of the given inode is located.
306 *
[7ea69a6]307 * @param fblock the number of filesystem block, or 0 if no such block is allocated yet
308 *
[ad34feb]309 * @return EOK on success or negative error code on failure
310 */
311int ext2_filesystem_get_inode_data_block_index(ext2_filesystem_t *fs, ext2_inode_t* inode,
312 aoff64_t iblock, uint32_t* fblock)
313{
314 int rc;
315 aoff64_t limits[4];
316 uint32_t block_ids_per_block;
317 aoff64_t blocks_per_level[4];
318 uint32_t offset_in_block;
319 uint32_t current_block;
320 aoff64_t block_offset_in_level;
321 int i;
322 int level;
323 block_t *block;
324
[260c052]325 /* Handle simple case when we are dealing with direct reference */
[ad34feb]326 if (iblock < EXT2_INODE_DIRECT_BLOCKS) {
327 current_block = ext2_inode_get_direct_block(inode, (uint32_t)iblock);
328 *fblock = current_block;
329 return EOK;
330 }
331
[260c052]332 /* Compute limits for indirect block levels
333 * TODO: compute this once when loading filesystem and store in ext2_filesystem_t
334 */
[ad34feb]335 block_ids_per_block = ext2_superblock_get_block_size(fs->superblock) / sizeof(uint32_t);
336 limits[0] = EXT2_INODE_DIRECT_BLOCKS;
337 blocks_per_level[0] = 1;
338 for (i = 1; i < 4; i++) {
339 blocks_per_level[i] = blocks_per_level[i-1] *
340 block_ids_per_block;
341 limits[i] = limits[i-1] + blocks_per_level[i];
342 }
343
[260c052]344 /* Determine the indirection level needed to get the desired block */
[ad34feb]345 level = -1;
346 for (i = 1; i < 4; i++) {
347 if (iblock < limits[i]) {
348 level = i;
349 break;
350 }
351 }
352
353 if (level == -1) {
354 return EIO;
355 }
356
[260c052]357 /* Compute offsets for the topmost level */
[ad34feb]358 block_offset_in_level = iblock - limits[level-1];
359 current_block = ext2_inode_get_indirect_block(inode, level-1);
360 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
361
[260c052]362 /* Navigate through other levels, until we find the block number
363 * or find null reference meaning we are dealing with sparse file
364 */
[ad34feb]365 while (level > 0) {
366 rc = block_get(&block, fs->device, current_block, 0);
367 if (rc != EOK) {
368 return rc;
369 }
370
371 assert(offset_in_block < block_ids_per_block);
[d7c3367]372 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
[ad34feb]373
374 rc = block_put(block);
375 if (rc != EOK) {
376 return rc;
377 }
378
379 if (current_block == 0) {
[260c052]380 /* This is a sparse file */
[7ea69a6]381 *fblock = 0;
382 return EOK;
[ad34feb]383 }
384
385 level -= 1;
386
[260c052]387 /* If we are on the last level, break here as
388 * there is no next level to visit
389 */
[ad34feb]390 if (level == 0) {
391 break;
392 }
393
[260c052]394 /* Visit the next level */
[c7faade]395 block_offset_in_level %= blocks_per_level[level];
[ad34feb]396 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
397 }
398
399 *fblock = current_block;
400
401 return EOK;
402}
403
[f8c60f5]404/**
405 * Allocate a given number of blocks and store their ids in blocks
406 *
[260c052]407 * @todo TODO: This function is not finished and really has never been
408 * used (and tested) yet
409 *
[f8c60f5]410 * @param fs pointer to filesystem
411 * @param blocks array of count uint32_t values where store block ids
412 * @param count number of blocks to allocate and elements in blocks array
413 * @param preferred_bg preferred block group number
414 *
415 * @return EOK on success or negative error code on failure
416 */
417int ext2_filesystem_allocate_blocks(ext2_filesystem_t *fs, uint32_t *blocks,
418 size_t count, uint32_t preferred_bg)
419{
420 uint32_t bg_count = ext2_superblock_get_block_group_count(fs->superblock);
421 uint32_t bpg = ext2_superblock_get_blocks_per_group(fs->superblock);
422 uint32_t block_size = ext2_superblock_get_block_size(fs->superblock);
423 uint32_t block_group = preferred_bg;
424 uint32_t free_blocks_sb;
425 uint32_t block_groups_left;
426 size_t idx;
427 ext2_block_group_ref_t *bg;
428 int rc;
429 uint32_t bb_block;
430 block_t *block;
431 size_t bb_idx;
432 size_t bb_bit;
433
434 free_blocks_sb = ext2_superblock_get_free_block_count(fs->superblock);
435
436 if (count > free_blocks_sb) {
437 return EIO;
438 }
439
440 block_groups_left = bg_count;
441
442 idx = 0;
443
[260c052]444 /* Read the block group descriptor */
[f8c60f5]445 rc = ext2_filesystem_get_block_group_ref(fs, block_group, &bg);
446 if (rc != EOK) {
447 goto failed;
448 }
449
450 while (idx < count && block_groups_left > 0) {
451 uint16_t fb = ext2_block_group_get_free_block_count(bg->block_group);
452 if (fb == 0) {
453 block_group = (block_group + 1) % bg_count;
454 block_groups_left -= 1;
455
456 rc = ext2_filesystem_put_block_group_ref(bg);
457 if (rc != EOK) {
458 goto failed;
459 }
460
461 rc = ext2_filesystem_get_block_group_ref(fs, block_group, &bg);
462 if (rc != EOK) {
463 goto failed;
464 }
465 continue;
466 }
467
[260c052]468 /* We found a block group with free block, let's look at the block bitmap */
[f8c60f5]469 bb_block = ext2_block_group_get_block_bitmap_block(bg->block_group);
470
471 rc = block_get(&block, fs->device, bb_block, BLOCK_FLAGS_NONE);
472 if (rc != EOK) {
473 goto failed;
474 }
475
[260c052]476 /* Use all blocks from this block group */
[f8c60f5]477 for (bb_idx = 0; bb_idx < block_size && idx < count; bb_idx++) {
478 uint8_t *data = (uint8_t *) block->data;
479 if (data[bb_idx] == 0xff) {
480 continue;
481 }
[260c052]482 /* find an empty bit */
[f8c60f5]483 uint8_t mask;
484 for (mask = 1, bb_bit = 0;
485 bb_bit < 8 && idx < count;
486 bb_bit++, mask = mask << 1) {
487 if ((data[bb_idx] & mask) == 0) {
488 // free block found
489 blocks[idx] = block_group * bpg + bb_idx*8 + bb_bit;
490 data[bb_idx] |= mask;
491 idx += 1;
492 fb -= 1;
493 ext2_block_group_set_free_block_count(bg->block_group, fb);
494 }
495 }
496 }
497 }
498
499 rc = ext2_filesystem_put_block_group_ref(bg);
500 if (rc != EOK) {
501 goto failed;
502 }
503
504 // TODO update superblock
505
506 return EOK;
507failed:
508 // TODO deallocate already allocated blocks, if possible
509
510 return rc;
511}
512
[36bca8eb]513/**
514 * Finalize an instance of filesystem
[566c401]515 *
516 * @param fs Pointer to ext2_filesystem_t to finalize
[36bca8eb]517 */
[566c401]518void ext2_filesystem_fini(ext2_filesystem_t *fs)
519{
[8bd5dad]520 free(fs->superblock);
[36bca8eb]521 block_fini(fs->device);
522}
523
524
525/** @}
526 */
Note: See TracBrowser for help on using the repository browser.