source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 9b9d37bb

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

mounting + list of mounted directory (ported from ext2) - many TODO remaining

  • Property mode set to 100644
File size: 9.3 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
43/**
44 * TODO doxy
45 */
46int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
47{
48
49 int rc;
50 ext4_superblock_t *temp_superblock;
51 size_t block_size;
52
53 fs->device = service_id;
54
55 // TODO what does constant 2048 mean?
56 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 2048);
57 if (rc != EOK) {
58 return rc;
59 }
60
61 /* Read superblock from device */
62 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
63 if (rc != EOK) {
64 block_fini(fs->device);
65 return rc;
66 }
67
68 /* Read block size from superblock and check */
69 block_size = ext4_superblock_get_block_size(temp_superblock);
70 if (block_size > EXT4_MAX_BLOCK_SIZE) {
71 block_fini(fs->device);
72 return ENOTSUP;
73 }
74
75 /* Initialize block caching */
76 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
77 if (rc != EOK) {
78 block_fini(fs->device);
79 return rc;
80 }
81
82 /* Return loaded superblock */
83 fs->superblock = temp_superblock;
84
85 return EOK;
86}
87
88/**
89 * TODO doxy
90 */
91void ext4_filesystem_fini(ext4_filesystem_t *fs)
92{
93 free(fs->superblock);
94 block_fini(fs->device);
95}
96
97/**
98 * TODO doxy
99 */
100int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
101{
102 int rc;
103
104 rc = ext4_superblock_check_sanity(fs->superblock);
105 if (rc != EOK) {
106 return rc;
107 }
108
109 return EOK;
110}
111
112/**
113 * TODO doxy
114 */
115int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *o_read_only)
116{
117 /* Feature flags are present in rev 1 and later */
118 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
119 *o_read_only = false;
120 return EOK;
121 }
122
123 uint32_t incompatible_features;
124 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
125 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
126 if (incompatible_features > 0) {
127 *o_read_only = true;
128 return ENOTSUP;
129 }
130
131 uint32_t compatible_read_only;
132 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
133 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
134 if (compatible_read_only > 0) {
135 *o_read_only = true;
136 }
137
138 return EOK;
139}
140
141/**
142 * TODO doxy
143 */
144int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
145 ext4_block_group_ref_t **ref)
146{
147 EXT4FS_DBG("");
148
149 int rc;
150 aoff64_t block_id;
151 uint32_t descriptors_per_block;
152 size_t offset;
153 ext4_block_group_ref_t *newref;
154
155 newref = malloc(sizeof(ext4_block_group_ref_t));
156 if (newref == NULL) {
157 return ENOMEM;
158 }
159
160 descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
161 / EXT4_BLOCK_GROUP_DESCRIPTOR_SIZE;
162
163 /* Block group descriptor table starts at the next block after superblock */
164 block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
165
166 EXT4FS_DBG("block_size = \%d", ext4_superblock_get_block_size(fs->superblock));
167 EXT4FS_DBG("descriptors_per_block = \%d", descriptors_per_block);
168 EXT4FS_DBG("bgid = \%d", bgid);
169 EXT4FS_DBG("first_data_block: \%d", (uint32_t)block_id);
170
171 /* Find the block containing the descriptor we are looking for */
172 block_id += bgid / descriptors_per_block;
173 offset = (bgid % descriptors_per_block) * EXT4_BLOCK_GROUP_DESCRIPTOR_SIZE;
174
175 EXT4FS_DBG("updated block_id: \%d", (uint32_t)block_id);
176
177 rc = block_get(&newref->block, fs->device, block_id, 0);
178 if (rc != EOK) {
179
180 EXT4FS_DBG("block_get error: \%d", rc);
181
182 free(newref);
183 return rc;
184 }
185
186 EXT4FS_DBG("block read");
187
188 newref->block_group = newref->block->data + offset;
189
190 *ref = newref;
191
192 EXT4FS_DBG("finished");
193
194 return EOK;
195}
196
197/**
198 * TODO doxy
199 */
200int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
201 ext4_inode_ref_t **ref)
202{
203 EXT4FS_DBG("");
204
205 int rc;
206 aoff64_t block_id;
207 uint32_t block_group;
208 uint32_t offset_in_group;
209 uint32_t byte_offset_in_group;
210 size_t offset_in_block;
211 uint32_t inodes_per_group;
212 uint32_t inode_table_start;
213 uint16_t inode_size;
214 uint32_t block_size;
215 ext4_block_group_ref_t *bg_ref;
216 ext4_inode_ref_t *newref;
217
218 newref = malloc(sizeof(ext4_inode_ref_t));
219 if (newref == NULL) {
220 return ENOMEM;
221 }
222
223 EXT4FS_DBG("allocated");
224
225 inodes_per_group = ext4_superblock_get_inodes_per_group(fs->superblock);
226
227 EXT4FS_DBG("inodes_per_group_loaded");
228
229 /* inode numbers are 1-based, but it is simpler to work with 0-based
230 * when computing indices
231 */
232 index -= 1;
233 block_group = index / inodes_per_group;
234 offset_in_group = index % inodes_per_group;
235
236 EXT4FS_DBG("index: \%d", index);
237 EXT4FS_DBG("inodes_per_group: \%d", inodes_per_group);
238 EXT4FS_DBG("bg_id: \%d", block_group);
239
240 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
241 if (rc != EOK) {
242 free(newref);
243 return rc;
244 }
245
246 EXT4FS_DBG("block_group_ref loaded");
247
248 inode_table_start = ext4_block_group_get_inode_table_first_block(
249 bg_ref->block_group);
250
251 EXT4FS_DBG("inode_table block loaded");
252
253 inode_size = ext4_superblock_get_inode_size(fs->superblock);
254 block_size = ext4_superblock_get_block_size(fs->superblock);
255
256 byte_offset_in_group = offset_in_group * inode_size;
257
258 block_id = inode_table_start + (byte_offset_in_group / block_size);
259 offset_in_block = byte_offset_in_group % block_size;
260
261 EXT4FS_DBG("superblock info loaded");
262
263 rc = block_get(&newref->block, fs->device, block_id, 0);
264 if (rc != EOK) {
265 free(newref);
266 return rc;
267 }
268
269 EXT4FS_DBG("block got");
270
271 newref->inode = newref->block->data + offset_in_block;
272 /* we decremented index above, but need to store the original value
273 * in the reference
274 */
275 newref->index = index+1;
276
277 *ref = newref;
278
279 EXT4FS_DBG("finished");
280
281 return EOK;
282}
283
284int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
285{
286 int rc;
287
288 rc = block_put(ref->block);
289 free(ref);
290
291 return rc;
292}
293
294int ext4_filesystem_get_inode_data_block_index(ext4_filesystem_t *fs, ext4_inode_t* inode,
295 aoff64_t iblock, uint32_t* fblock)
296{
297 int rc;
298 aoff64_t limits[4];
299 uint32_t block_ids_per_block;
300 aoff64_t blocks_per_level[4];
301 uint32_t offset_in_block;
302 uint32_t current_block;
303 aoff64_t block_offset_in_level;
304 int i;
305 int level;
306 block_t *block;
307
308 /* Handle simple case when we are dealing with direct reference */
309 if (iblock < EXT4_INODE_DIRECT_BLOCKS) {
310 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
311 *fblock = current_block;
312 return EOK;
313 }
314
315 /* Compute limits for indirect block levels
316 * TODO: compute this once when loading filesystem and store in ext2_filesystem_t
317 */
318 block_ids_per_block = ext4_superblock_get_block_size(fs->superblock) / sizeof(uint32_t);
319 limits[0] = EXT4_INODE_DIRECT_BLOCKS;
320 blocks_per_level[0] = 1;
321 for (i = 1; i < 4; i++) {
322 blocks_per_level[i] = blocks_per_level[i-1] *
323 block_ids_per_block;
324 limits[i] = limits[i-1] + blocks_per_level[i];
325 }
326
327 /* Determine the indirection level needed to get the desired block */
328 level = -1;
329 for (i = 1; i < 4; i++) {
330 if (iblock < limits[i]) {
331 level = i;
332 break;
333 }
334 }
335
336 if (level == -1) {
337 return EIO;
338 }
339
340 /* Compute offsets for the topmost level */
341 block_offset_in_level = iblock - limits[level-1];
342 current_block = ext4_inode_get_indirect_block(inode, level-1);
343 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
344
345 /* Navigate through other levels, until we find the block number
346 * or find null reference meaning we are dealing with sparse file
347 */
348 while (level > 0) {
349 rc = block_get(&block, fs->device, current_block, 0);
350 if (rc != EOK) {
351 return rc;
352 }
353
354 assert(offset_in_block < block_ids_per_block);
355 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
356
357 rc = block_put(block);
358 if (rc != EOK) {
359 return rc;
360 }
361
362 if (current_block == 0) {
363 /* This is a sparse file */
364 *fblock = 0;
365 return EOK;
366 }
367
368 level -= 1;
369
370 /* If we are on the last level, break here as
371 * there is no next level to visit
372 */
373 if (level == 0) {
374 break;
375 }
376
377 /* Visit the next level */
378 block_offset_in_level %= blocks_per_level[level];
379 offset_in_block = block_offset_in_level / blocks_per_level[level-1];
380 }
381
382 *fblock = current_block;
383
384 return EOK;
385}
386
387/**
388 * @}
389 */
Note: See TracBrowser for help on using the repository browser.