source: mainline/uspace/lib/ext2/libext2_filesystem.c@ 66bea243

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

Check filesystem feature flags when mounting

  • Property mode set to 100644
File size: 9.8 KB
Line 
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
36#include "libext2_filesystem.h"
37#include "libext2_superblock.h"
38#include "libext2_block_group.h"
39#include "libext2_inode.h"
40#include <errno.h>
41#include <libblock.h>
42#include <malloc.h>
43#include <assert.h>
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.
49 *
50 * @param fs Pointer to ext2_filesystem_t to initialize
51 * @param devmap_handle Device handle of the block device
52 *
53 * @return EOK on success or negative error code on failure
54 */
55int ext2_filesystem_init(ext2_filesystem_t *fs, devmap_handle_t devmap_handle)
56{
57 int rc;
58 ext2_superblock_t *temp_superblock;
59 size_t block_size;
60
61 fs->device = devmap_handle;
62
63 rc = block_init(fs->device, 2048);
64 if (rc != EOK) {
65 return rc;
66 }
67
68 rc = ext2_superblock_read_direct(fs->device, &temp_superblock);
69 if (rc != EOK) {
70 block_fini(fs->device);
71 return rc;
72 }
73
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;
88
89 return EOK;
90}
91
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
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{
119 // feature flags are present in rev 1 and later
120 if (ext2_superblock_get_rev_major(fs->superblock) == 0) {
121 *o_read_only = false;
122 return 0;
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
131 // unset any supported features
132 incompatible &= ~EXT2_SUPPORTED_INCOMPATIBLE_FEATURES;
133 read_only &= ~EXT2_SUPPORTED_READ_ONLY_FEATURES;
134
135 if (incompatible > 0) {
136 *o_read_only = true;
137 return ENOTSUP;
138 }
139
140 if (read_only > 0) {
141 *o_read_only = true;
142 }
143
144 return EOK;
145}
146
147/**
148 * Get a reference to block descriptor
149 *
150 * @param fs Pointer to filesystem information
151 * @param bgid Index of block group to find
152 * @param ref Pointer where to store pointer to block group reference
153 *
154 * @return EOK on success or negative error code on failure
155 */
156int ext2_filesystem_get_block_group_ref(ext2_filesystem_t *fs, uint32_t bgid,
157 ext2_block_group_ref_t **ref)
158{
159 int rc;
160 aoff64_t block_id;
161 uint32_t descriptors_per_block;
162 size_t offset;
163 ext2_block_group_ref_t *newref;
164
165 newref = malloc(sizeof(ext2_block_group_ref_t));
166 if (newref == NULL) {
167 return ENOMEM;
168 }
169
170 descriptors_per_block = ext2_superblock_get_block_size(fs->superblock)
171 / EXT2_BLOCK_GROUP_DESCRIPTOR_SIZE;
172
173 // Block group descriptor table starts at the next block after superblock
174 block_id = ext2_superblock_get_first_block(fs->superblock) + 1;
175
176 // Find the block containing the descriptor we are looking for
177 block_id += bgid / descriptors_per_block;
178 offset = (bgid % descriptors_per_block) * EXT2_BLOCK_GROUP_DESCRIPTOR_SIZE;
179
180 rc = block_get(&newref->block, fs->device, block_id, 0);
181 if (rc != EOK) {
182 free(newref);
183 return rc;
184 }
185
186 newref->block_group = newref->block->data + offset;
187
188 *ref = newref;
189
190 return EOK;
191}
192
193/**
194 * Free a reference to block group
195 *
196 * @param ref Pointer to block group reference to free
197 *
198 * @return EOK on success or negative error code on failure
199 */
200int ext2_filesystem_put_block_group_ref(ext2_block_group_ref_t *ref)
201{
202 int rc;
203
204 rc = block_put(ref->block);
205 free(ref);
206
207 return rc;
208}
209
210/**
211 * Get a reference to inode
212 *
213 * @param fs Pointer to filesystem information
214 * @param index The index number of the inode
215 * @param ref Pointer where to store pointer to inode reference
216 *
217 * @return EOK on success or negative error code on failure
218 */
219int ext2_filesystem_get_inode_ref(ext2_filesystem_t *fs, uint32_t index,
220 ext2_inode_ref_t **ref)
221{
222 int rc;
223 aoff64_t block_id;
224 uint32_t block_group;
225 uint32_t offset_in_group;
226 uint32_t byte_offset_in_group;
227 size_t offset_in_block;
228 uint32_t inodes_per_group;
229 uint32_t inode_table_start;
230 uint16_t inode_size;
231 uint32_t block_size;
232 ext2_block_group_ref_t *bg_ref;
233 ext2_inode_ref_t *newref;
234
235 newref = malloc(sizeof(ext2_inode_ref_t));
236 if (newref == NULL) {
237 return ENOMEM;
238 }
239
240 inodes_per_group = ext2_superblock_get_inodes_per_group(fs->superblock);
241
242 // inode numbers are 1-based
243 index -= 1;
244 block_group = index / inodes_per_group;
245 offset_in_group = index % inodes_per_group;
246
247 rc = ext2_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
248 if (rc != EOK) {
249 free(newref);
250 return rc;
251 }
252
253 inode_table_start = ext2_block_group_get_inode_table_first_block(
254 bg_ref->block_group);
255
256 inode_size = ext2_superblock_get_inode_size(fs->superblock);
257 block_size = ext2_superblock_get_block_size(fs->superblock);
258
259 byte_offset_in_group = offset_in_group * inode_size;
260
261 block_id = inode_table_start + (byte_offset_in_group / block_size);
262 offset_in_block = byte_offset_in_group % block_size;
263
264 rc = block_get(&newref->block, fs->device, block_id, 0);
265 if (rc != EOK) {
266 free(newref);
267 return rc;
268 }
269
270 newref->inode = newref->block->data + offset_in_block;
271 newref->index = index+1; // we decremented index above
272
273 *ref = newref;
274
275 return EOK;
276}
277
278/**
279 * Free a reference to inode
280 *
281 * @param ref Pointer to inode reference to free
282 *
283 * @return EOK on success or negative error code on failure
284 */
285int ext2_filesystem_put_inode_ref(ext2_inode_ref_t *ref)
286{
287 int rc;
288
289 rc = block_put(ref->block);
290 free(ref);
291
292 return rc;
293}
294
295/**
296 * Find a filesystem block number where iblock-th data block
297 * of the given inode is located.
298 *
299 * @return EOK on success or negative error code on failure
300 */
301int ext2_filesystem_get_inode_data_block_index(ext2_filesystem_t *fs, ext2_inode_t* inode,
302 aoff64_t iblock, uint32_t* fblock)
303{
304 int rc;
305 aoff64_t limits[4];
306 uint32_t block_ids_per_block;
307 aoff64_t blocks_per_level[4];
308 uint32_t offset_in_block;
309 uint32_t current_block;
310 aoff64_t block_offset_in_level;
311 int i;
312 int level;
313 block_t *block;
314
315 if (iblock < EXT2_INODE_DIRECT_BLOCKS) {
316 current_block = ext2_inode_get_direct_block(inode, (uint32_t)iblock);
317 if (current_block == 0) {
318 return EIO;
319 }
320 *fblock = current_block;
321 return EOK;
322 }
323
324 // Compute limits for indirect block levels
325 // TODO: compute this once when loading filesystem and store in ext2_filesystem_t
326 block_ids_per_block = ext2_superblock_get_block_size(fs->superblock) / sizeof(uint32_t);
327 limits[0] = EXT2_INODE_DIRECT_BLOCKS;
328 blocks_per_level[0] = 1;
329 for (i = 1; i < 4; i++) {
330 blocks_per_level[i] = blocks_per_level[i-1] *
331 block_ids_per_block;
332 limits[i] = limits[i-1] + blocks_per_level[i];
333 }
334
335 // Determine the indirection level needed to get the desired block
336 level = -1;
337 for (i = 1; i < 4; i++) {
338 if (iblock < limits[i]) {
339 level = i;
340 break;
341 }
342 }
343
344 if (level == -1) {
345 return EIO;
346 }
347
348 block_offset_in_level = iblock - limits[level-1];
349 current_block = ext2_inode_get_indirect_block(inode, level-1);
350 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
351
352 while (level > 0) {
353 rc = block_get(&block, fs->device, current_block, 0);
354 if (rc != EOK) {
355 return rc;
356 }
357
358 assert(offset_in_block < block_ids_per_block);
359 current_block = ((uint32_t*)block->data)[offset_in_block];
360
361 rc = block_put(block);
362 if (rc != EOK) {
363 return rc;
364 }
365
366 if (current_block == 0) {
367 return EIO;
368 }
369
370 level -= 1;
371
372 if (level == 0) {
373 break;
374 }
375
376 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
377 block_offset_in_level %= blocks_per_level[level-1];
378 }
379
380 *fblock = current_block;
381
382 return EOK;
383}
384
385/**
386 * Finalize an instance of filesystem
387 *
388 * @param fs Pointer to ext2_filesystem_t to finalize
389 */
390void ext2_filesystem_fini(ext2_filesystem_t *fs)
391{
392 free(fs->superblock);
393 block_fini(fs->device);
394}
395
396
397/** @}
398 */
Note: See TracBrowser for help on using the repository browser.