source: mainline/uspace/lib/ext2/libext2_filesystem.c@ 260c052

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 260c052 was 260c052, checked in by Martin Sucha <sucha14@…>, 14 years ago

coding style

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