source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 052e82d

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

partially functional truncate operation

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