source: mainline/uspace/lib/ext4/libext4_filesystem.c@ bed78cb

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

More comments - mostly by filesystem operations

  • Property mode set to 100644
File size: 27.7 KB
RevLine 
[6c501f8]1/*
[f22d5ef0]2 * Copyright (c) 2012 Frantisek Princ
[6c501f8]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
[e63ce679]35 * @brief More complex filesystem operations.
[6c501f8]36 */
37
[9b9d37bb]38#include <byteorder.h>
[6c501f8]39#include <errno.h>
[01ab41b]40#include <malloc.h>
[3711e7e]41#include "libext4.h"
[6c501f8]42
[5b26747]43/** Initialize filesystem and read all needed data.
[9fc72fb3]44 *
[5b26747]45 * @param fs filesystem instance to be initialized
46 * @param service_id identifier if device with the filesystem
47 * @return error code
[9fc72fb3]48 */
[6c501f8]49int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
50{
[01ab41b]51 int rc;
52
53 fs->device = service_id;
54
[5b26747]55 // Initialize block library (4096 is size of communication channel)
[fffb061]56 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
[01ab41b]57 if (rc != EOK) {
58 return rc;
59 }
60
[5b26747]61 // Read superblock from device to memory
[d9bbe45]62 ext4_superblock_t *temp_superblock;
[01ab41b]63 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
64 if (rc != EOK) {
65 block_fini(fs->device);
66 return rc;
67 }
68
[5b26747]69 // Read block size from superblock and check
[d9bbe45]70 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
[01ab41b]71 if (block_size > EXT4_MAX_BLOCK_SIZE) {
72 block_fini(fs->device);
73 return ENOTSUP;
74 }
75
[5b26747]76 // Initialize block caching by libblock
[b12ca16]77 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
[01ab41b]78 if (rc != EOK) {
79 block_fini(fs->device);
80 return rc;
81 }
82
[5b26747]83 // Compute limits for indirect block levels
[d9bbe45]84 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
[a9a0982]85 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
86 fs->inode_blocks_per_level[0] = 1;
[d9bbe45]87 for (int i = 1; i < 4; i++) {
[6088193]88 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
[a9a0982]89 block_ids_per_block;
90 fs->inode_block_limits[i] = fs->inode_block_limits[i-1] +
91 fs->inode_blocks_per_level[i];
92 }
93
[5b26747]94 // Return loaded superblock
[01ab41b]95 fs->superblock = temp_superblock;
96
[6c501f8]97 return EOK;
98}
99
[5b26747]100/** Destroy filesystem instance (used by unmount operation).
[9fc72fb3]101 *
[5b26747]102 * @param fs filesystem to be destroyed
103 * @param write_sb flag if superblock should be written to device
104 * @return error code
[9fc72fb3]105 */
[ae3d4f8]106int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
[3711e7e]107{
[ae3d4f8]108 int rc = EOK;
[d9bbe45]109
[5b26747]110 // If needed, write the superblock to the device
[ae3d4f8]111 if (write_sb) {
112 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
113 }
114
[5b26747]115 // Release memory space for superblock
[3711e7e]116 free(fs->superblock);
[5b26747]117
118 // Finish work with block library
[3711e7e]119 block_fini(fs->device);
[ae3d4f8]120
121 return rc;
[3711e7e]122}
123
[5b26747]124/** Check sanity of the filesystem.
[9fc72fb3]125 *
[5b26747]126 * Main is the check of the superblock structure.
127 *
128 * @param fs filesystem to be checked
129 * @return error code
[9fc72fb3]130 */
[6c501f8]131int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
132{
[01ab41b]133 int rc;
134
[5b26747]135 // Check superblock
[01ab41b]136 rc = ext4_superblock_check_sanity(fs->superblock);
137 if (rc != EOK) {
138 return rc;
139 }
140
[6c501f8]141 return EOK;
142}
143
[5b26747]144/** Check filesystem's features, if supported by this driver
[9fc72fb3]145 *
[5b26747]146 * Function can return EOK and set read_only flag. It mean's that
147 * there are some not-supported features, that can cause problems
148 * during some write operations.
149 *
150 * @param fs filesystem to be checked
151 * @param read_only flag if filesystem should be mounted only for reading
152 * @return error code
[9fc72fb3]153 */
[5b26747]154int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *read_only)
[6c501f8]155{
[5b26747]156 // Feature flags are present only in higher revisions
[9c0c0e1]157 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
[5b26747]158 *read_only = false;
[9c0c0e1]159 return EOK;
160 }
161
[5b26747]162 // Check incompatible features - if filesystem has some,
163 // volume can't be mounted
[9c0c0e1]164 uint32_t incompatible_features;
165 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
166 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
167 if (incompatible_features > 0) {
168 return ENOTSUP;
169 }
170
[5b26747]171 // Check read-only features, if filesystem has some,
172 // volume can be mount only in read-only mode
[9c0c0e1]173 uint32_t compatible_read_only;
174 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
175 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
176 if (compatible_read_only > 0) {
[5b26747]177 *read_only = true;
178 return EOK;
[9c0c0e1]179 }
180
[6c501f8]181 return EOK;
182}
183
[5b26747]184/** Get reference to block group specified by index.
[9fc72fb3]185 *
[5b26747]186 * @param fs filesystem to find block group on
187 * @param bgid index of block group to load
188 * @oaram ref output pointer for reference
189 * @return error code
[9fc72fb3]190 */
[3711e7e]191int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
192 ext4_block_group_ref_t **ref)
[6c501f8]193{
[3711e7e]194 int rc;
195
[5b26747]196 // Allocate memory for new structure
[d9bbe45]197 ext4_block_group_ref_t *newref = malloc(sizeof(ext4_block_group_ref_t));
[3711e7e]198 if (newref == NULL) {
199 return ENOMEM;
200 }
201
[5b26747]202 // Compute number of descriptors, that fits in one data block
[d9bbe45]203 uint32_t descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
[c25e39b]204 / ext4_superblock_get_desc_size(fs->superblock);
[3711e7e]205
[5b26747]206 // Block group descriptor table starts at the next block after superblock
[d9bbe45]207 aoff64_t block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
[3711e7e]208
[5b26747]209 // Find the block containing the descriptor we are looking for
[3711e7e]210 block_id += bgid / descriptors_per_block;
[d9bbe45]211 uint32_t offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
[3711e7e]212
[5b26747]213 // Load block with descriptors
[3711e7e]214 rc = block_get(&newref->block, fs->device, block_id, 0);
215 if (rc != EOK) {
216 free(newref);
217 return rc;
218 }
219
[5b26747]220 // Inititialize in-memory representation
[3711e7e]221 newref->block_group = newref->block->data + offset;
[1ac1ab4]222 newref->fs = fs;
223 newref->index = bgid;
[12b4a7f]224 newref->dirty = false;
[3711e7e]225
226 *ref = newref;
227
228 return EOK;
[6c501f8]229}
230
[9fc72fb3]231/** TODO comment
232 *
233 */
[1ac1ab4]234static uint16_t ext4_filesystem_bg_checksum(ext4_superblock_t *sb, uint32_t bgid,
235 ext4_block_group_t *bg)
236{
237 uint16_t crc = 0;
238
239 if (ext4_superblock_has_feature_read_only(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
240
241 void *base = bg;
242 void *checksum = &bg->checksum;
243
244 uint32_t offset = (uint32_t)(checksum - base);
245
246 uint32_t le_group = host2uint32_t_le(bgid);
247
248 crc = crc16(~0, sb->uuid, sizeof(sb->uuid));
249 crc = crc16(crc, (uint8_t *)&le_group, sizeof(le_group));
250 crc = crc16(crc, (uint8_t *)bg, offset);
251
252 offset += sizeof(bg->checksum); /* skip checksum */
253
254 /* for checksum of struct ext4_group_desc do the rest...*/
255 if ((ext4_superblock_has_feature_incompatible(sb, EXT4_FEATURE_INCOMPAT_64BIT)) &&
256 offset < ext4_superblock_get_desc_size(sb)) {
257
258 crc = crc16(crc, ((uint8_t *)bg) + offset, ext4_superblock_get_desc_size(sb) - offset);
259 }
260 }
261
262 return crc;
263
264}
265
[5b26747]266/** Put reference to block group.
[9fc72fb3]267 *
[5b26747]268 * @oaram ref pointer for reference to be put back
269 * @return error code
[9fc72fb3]270 */
[829d238]271int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
272{
273 int rc;
274
[5b26747]275 // Check if reference modified
[12b4a7f]276 if (ref->dirty) {
[5b26747]277
278 // Compute new checksum of block group
[5b0a3946]279 uint16_t checksum = ext4_filesystem_bg_checksum(
[1ac1ab4]280 ref->fs->superblock, ref->index, ref->block_group);
[5b0a3946]281 ext4_block_group_set_checksum(ref->block_group, checksum);
[1ac1ab4]282
[5b26747]283 // Mark block dirty for writing changes to physical device
[12b4a7f]284 ref->block->dirty = true;
285 }
286
[5b26747]287 // Put back block, that contains block group descriptor
[829d238]288 rc = block_put(ref->block);
289 free(ref);
290
291 return rc;
292}
293
[5b26747]294/** Get reference to i-node specified by index.
[9fc72fb3]295 *
[5b26747]296 * @param fs filesystem to find i-node on
297 * @param index index of i-node to load
298 * @oaram ref output pointer for reference
299 * @return error code
[9fc72fb3]300 */
[3711e7e]301int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
302 ext4_inode_ref_t **ref)
303{
304 int rc;
305
[5b26747]306 // Allocate memory for new structure
[d9bbe45]307 ext4_inode_ref_t *newref = malloc(sizeof(ext4_inode_ref_t));
[3711e7e]308 if (newref == NULL) {
309 return ENOMEM;
310 }
311
[5b26747]312 // Compute number of i-nodes, that fits in one data block
[d9bbe45]313 uint32_t inodes_per_group =
314 ext4_superblock_get_inodes_per_group(fs->superblock);
[3711e7e]315
[5b26747]316 /*
317 * inode numbers are 1-based, but it is simpler to work with 0-based
[3711e7e]318 * when computing indices
319 */
320 index -= 1;
[d9bbe45]321 uint32_t block_group = index / inodes_per_group;
322 uint32_t offset_in_group = index % inodes_per_group;
[3711e7e]323
[5b26747]324 // Load block group, where i-node is located
[d9bbe45]325 ext4_block_group_ref_t *bg_ref;
[3711e7e]326 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
327 if (rc != EOK) {
328 free(newref);
329 return rc;
330 }
331
[5b26747]332 // Load block address, where i-node table is located
[d9bbe45]333 uint32_t inode_table_start = ext4_block_group_get_inode_table_first_block(
[fe27eb4]334 bg_ref->block_group, fs->superblock);
[3711e7e]335
[5b26747]336 // Put back block group reference (not needed more)
[829d238]337 rc = ext4_filesystem_put_block_group_ref(bg_ref);
338 if (rc != EOK) {
339 free(newref);
340 return rc;
341 }
342
[5b26747]343 // Compute position of i-node in the block group
[d9bbe45]344 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
345 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
346 uint32_t byte_offset_in_group = offset_in_group * inode_size;
[3711e7e]347
[5b26747]348 // Compute block address
[d9bbe45]349 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
[3711e7e]350 rc = block_get(&newref->block, fs->device, block_id, 0);
351 if (rc != EOK) {
352 free(newref);
353 return rc;
354 }
355
[5b26747]356 // Compute position of i-node in the data block
[d9bbe45]357 uint32_t offset_in_block = byte_offset_in_group % block_size;
[3711e7e]358 newref->inode = newref->block->data + offset_in_block;
[5b26747]359
360 // We need to store the original value of index in the reference
[1ac1ab4]361 newref->index = index + 1;
362 newref->fs = fs;
[052e82d]363 newref->dirty = false;
[3711e7e]364
365 *ref = newref;
366
[9b9d37bb]367 return EOK;
368}
369
[9fc72fb3]370/** TODO comment
371 *
372 */
[9b9d37bb]373int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
374{
375 int rc;
376
[052e82d]377 if (ref->dirty) {
378 ref->block->dirty = true;
379 }
380
[9b9d37bb]381 rc = block_put(ref->block);
382 free(ref);
383
384 return rc;
385}
386
[9fc72fb3]387/** TODO comment
388 *
389 */
[304faab]390int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
391 ext4_inode_ref_t **inode_ref, int flags)
[2d2c6ce]392{
[304faab]393 int rc;
[2d2c6ce]394
[304faab]395 bool is_dir = false;
[2d2c6ce]396 if (flags & L_DIRECTORY) {
[304faab]397 is_dir = true;
398 }
399
400 // allocate inode
401 uint32_t index;
402 rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
403 if (rc != EOK) {
404 return rc;
405 }
406
407 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
408 if (rc != EOK) {
409 ext4_ialloc_free_inode(fs, index, is_dir);
410 return rc;
411 }
412
413 // init inode
414 ext4_inode_t *inode = (*inode_ref)->inode;
415
416 if (is_dir) {
[2d2c6ce]417 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_DIRECTORY);
418 ext4_inode_set_links_count(inode, 1); // '.' entry
419 } else {
420 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_FILE);
421 ext4_inode_set_links_count(inode, 0);
422 }
423
424 ext4_inode_set_uid(inode, 0);
425 ext4_inode_set_gid(inode, 0);
426 ext4_inode_set_size(inode, 0);
427 ext4_inode_set_access_time(inode, 0);
428 ext4_inode_set_change_inode_time(inode, 0);
429 ext4_inode_set_modification_time(inode, 0);
430 ext4_inode_set_deletion_time(inode, 0);
431 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
432 ext4_inode_set_flags(inode, 0);
433 ext4_inode_set_generation(inode, 0);
434
[936132f]435 // Reset blocks array
[2d2c6ce]436 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++) {
437 inode->blocks[i] = 0;
438 }
439
[936132f]440 if (ext4_superblock_has_feature_incompatible(
441 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
442
443 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
444
445 // Initialize extent root header
446 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
447 ext4_extent_header_set_depth(header, 0);
448 ext4_extent_header_set_entries_count(header, 0);
449 ext4_extent_header_set_generation(header, 0);
450 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
451
452 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof (uint32_t) - sizeof(ext4_extent_header_t))
453 / sizeof(ext4_extent_t);
454
455 ext4_extent_header_set_max_entries_count(header, max_entries);
456 }
457
[304faab]458 (*inode_ref)->dirty = true;
459
[2d2c6ce]460 return EOK;
461}
462
[9fc72fb3]463/** TODO comment
464 *
465 */
[1ac1ab4]466int ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
[3d4fd2c]467{
468 int rc;
469
[3e2952b]470 ext4_filesystem_t *fs = inode_ref->fs;
471
472 if (ext4_superblock_has_feature_incompatible(
473 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
474 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
475
476 // Data structures are released during truncate operation...
477 goto finish;
478 }
479
[ca3d77a]480 // release all indirect (no data) blocks
[3d4fd2c]481
482 // 1) Single indirect
[d9bbe45]483 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
[3d4fd2c]484 if (fblock != 0) {
[1ac1ab4]485 rc = ext4_balloc_free_block(inode_ref, fblock);
[3d4fd2c]486 if (rc != EOK) {
[ca3d77a]487 return rc;
[3d4fd2c]488 }
489
490 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
491 }
492
493 block_t *block;
494 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
495 uint32_t count = block_size / sizeof(uint32_t);
496
497 // 2) Double indirect
498 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
499 if (fblock != 0) {
500 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
501 if (rc != EOK) {
[e63ce679]502 return rc;
[3d4fd2c]503 }
504
505 uint32_t ind_block;
506 for (uint32_t offset = 0; offset < count; ++offset) {
507 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
508
509 if (ind_block != 0) {
[1ac1ab4]510 rc = ext4_balloc_free_block(inode_ref, ind_block);
[3d4fd2c]511 if (rc != EOK) {
[e63ce679]512 block_put(block);
513 return rc;
[3d4fd2c]514 }
515 }
516 }
517
518 block_put(block);
[1ac1ab4]519 rc = ext4_balloc_free_block(inode_ref, fblock);
[3d4fd2c]520 if (rc != EOK) {
[e63ce679]521 return rc;
[3d4fd2c]522 }
523
524 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
525 }
526
527
528 // 3) Tripple indirect
529 block_t *subblock;
530 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
531 if (fblock != 0) {
532 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
533 if (rc != EOK) {
[e63ce679]534 return rc;
[3d4fd2c]535 }
536
537 uint32_t ind_block;
538 for (uint32_t offset = 0; offset < count; ++offset) {
539 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
540
541 if (ind_block != 0) {
542 rc = block_get(&subblock, fs->device, ind_block, BLOCK_FLAGS_NONE);
543 if (rc != EOK) {
[e63ce679]544 block_put(block);
545 return rc;
[3d4fd2c]546 }
547
548 uint32_t ind_subblock;
549 for (uint32_t suboffset = 0; suboffset < count; ++suboffset) {
550 ind_subblock = uint32_t_le2host(((uint32_t*)subblock->data)[suboffset]);
551
552 if (ind_subblock != 0) {
[1ac1ab4]553 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
[3d4fd2c]554 if (rc != EOK) {
[e63ce679]555 block_put(subblock);
556 block_put(block);
557 return rc;
[3d4fd2c]558 }
559 }
560
561 }
562 block_put(subblock);
563
564 }
565
[1ac1ab4]566 rc = ext4_balloc_free_block(inode_ref, ind_block);
[3d4fd2c]567 if (rc != EOK) {
[e63ce679]568 block_put(block);
569 return rc;
[3d4fd2c]570 }
571
572
573 }
574
575 block_put(block);
[1ac1ab4]576 rc = ext4_balloc_free_block(inode_ref, fblock);
[3d4fd2c]577 if (rc != EOK) {
[e63ce679]578 return rc;
[3d4fd2c]579 }
580
581 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
582 }
583
[3e2952b]584finish:
[e5f8762]585 inode_ref->dirty = true;
586
[3d4fd2c]587 // Free inode
[304faab]588 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
589 EXT4_INODE_MODE_DIRECTORY)) {
590 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
591 } else {
592 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
593 }
[3d4fd2c]594 if (rc != EOK) {
595 return rc;
596 }
597
598 return EOK;
599}
600
[9fc72fb3]601/** TODO comment
602 *
603 */
[1ac1ab4]604int ext4_filesystem_truncate_inode(
[3d4fd2c]605 ext4_inode_ref_t *inode_ref, aoff64_t new_size)
606{
[ca3d77a]607 int rc;
608
[1ac1ab4]609 ext4_superblock_t *sb = inode_ref->fs->superblock;
610
611 if (! ext4_inode_can_truncate(sb, inode_ref->inode)) {
[3d4fd2c]612 // Unable to truncate
613 return EINVAL;
614 }
615
[1ac1ab4]616 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
[3d4fd2c]617 if (old_size == new_size) {
618 // Nothing to do
619 return EOK;
620 }
621
[ca3d77a]622 // It's not suppported to make the larger file
[3d4fd2c]623 if (old_size < new_size) {
624 return EINVAL;
625 }
626
[d9bbe45]627 aoff64_t size_diff = old_size - new_size;
[1ac1ab4]628 uint32_t block_size = ext4_superblock_get_block_size(sb);
[ca3d77a]629 uint32_t diff_blocks_count = size_diff / block_size;
[3d4fd2c]630 if (size_diff % block_size != 0) {
[ca3d77a]631 diff_blocks_count++;
[3d4fd2c]632 }
633
[ca3d77a]634 uint32_t old_blocks_count = old_size / block_size;
[3d4fd2c]635 if (old_size % block_size != 0) {
[ca3d77a]636 old_blocks_count++;
[3d4fd2c]637 }
638
[5b0a3946]639 if (ext4_superblock_has_feature_incompatible(
640 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
641 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
642
[1196df6]643 rc = ext4_extent_release_blocks_from(inode_ref,
644 old_blocks_count - diff_blocks_count);
[ca3d77a]645 if (rc != EOK) {
646 return rc;
647 }
[5b0a3946]648 } else {
649 // starting from 1 because of logical blocks are numbered from 0
650 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
651 rc = ext4_filesystem_release_inode_block(inode_ref, old_blocks_count - i);
652 if (rc != EOK) {
653 return rc;
654 }
655 }
[3d4fd2c]656 }
657
658 ext4_inode_set_size(inode_ref->inode, new_size);
659
660 inode_ref->dirty = true;
661
662 return EOK;
663}
664
[9fc72fb3]665/** TODO comment
666 *
667 */
[1ac1ab4]668int ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
[b73530a]669 aoff64_t iblock, uint32_t *fblock)
[9b9d37bb]670{
671 int rc;
[d9bbe45]672
[1ac1ab4]673 ext4_filesystem_t *fs = inode_ref->fs;
[d9bbe45]674
[b73530a]675 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
676 *fblock = 0;
677 return EOK;
678 }
679
[9b9d37bb]680 uint32_t current_block;
681
[8958a26]682 /* Handle inode using extents */
[a872fc09]683 if (ext4_superblock_has_feature_incompatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
[9104bb5]684 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
[1ac1ab4]685 rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
[9104bb5]686
687 if (rc != EOK) {
688 return rc;
689 }
690
[acd869e]691 *fblock = current_block;
692 return EOK;
693
694 }
695
[9104bb5]696 ext4_inode_t *inode = inode_ref->inode;
697
[9b9d37bb]698 /* Handle simple case when we are dealing with direct reference */
[e68c834]699 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[9b9d37bb]700 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
701 *fblock = current_block;
702 return EOK;
703 }
704
705 /* Determine the indirection level needed to get the desired block */
[d9bbe45]706 int level = -1;
707 for (int i = 1; i < 4; i++) {
[a9a0982]708 if (iblock < fs->inode_block_limits[i]) {
[9b9d37bb]709 level = i;
710 break;
711 }
712 }
713
714 if (level == -1) {
715 return EIO;
716 }
717
718 /* Compute offsets for the topmost level */
[d9bbe45]719 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
[9b9d37bb]720 current_block = ext4_inode_get_indirect_block(inode, level-1);
[d9bbe45]721 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
[9b9d37bb]722
[6088193]723 if (current_block == 0) {
724 *fblock = 0;
725 return EOK;
726 }
727
[d9bbe45]728 block_t *block;
729
[9b9d37bb]730 /* Navigate through other levels, until we find the block number
731 * or find null reference meaning we are dealing with sparse file
732 */
733 while (level > 0) {
734 rc = block_get(&block, fs->device, current_block, 0);
735 if (rc != EOK) {
736 return rc;
737 }
738
739 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
740
741 rc = block_put(block);
742 if (rc != EOK) {
743 return rc;
744 }
745
746 if (current_block == 0) {
747 /* This is a sparse file */
748 *fblock = 0;
749 return EOK;
750 }
751
752 level -= 1;
753
754 /* If we are on the last level, break here as
755 * there is no next level to visit
756 */
757 if (level == 0) {
758 break;
759 }
760
761 /* Visit the next level */
[a9a0982]762 block_offset_in_level %= fs->inode_blocks_per_level[level];
763 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
[9b9d37bb]764 }
765
766 *fblock = current_block;
767
[3711e7e]768 return EOK;
769}
[6c501f8]770
[9fc72fb3]771/** TODO comment
772 *
773 */
[1ac1ab4]774int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
775 aoff64_t iblock, uint32_t fblock)
[35f48f2]776{
[1e65444]777 int rc;
[d9bbe45]778
[1ac1ab4]779 ext4_filesystem_t *fs = inode_ref->fs;
[35f48f2]780
781 /* Handle inode using extents */
782 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
[1e65444]783 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
[ce6de59]784 // not reachable !!!
[35f48f2]785 return ENOTSUP;
786 }
787
788 /* Handle simple case when we are dealing with direct reference */
789 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[1e65444]790 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
791 inode_ref->dirty = true;
[35f48f2]792 return EOK;
793 }
794
[1e65444]795 /* Determine the indirection level needed to get the desired block */
[d9bbe45]796 int level = -1;
797 for (int i = 1; i < 4; i++) {
[1e65444]798 if (iblock < fs->inode_block_limits[i]) {
799 level = i;
800 break;
801 }
802 }
803
804 if (level == -1) {
805 return EIO;
806 }
807
[d9bbe45]808 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
[1e65444]809
810 /* Compute offsets for the topmost level */
[d9bbe45]811 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
812 uint32_t current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
813 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
814
815 uint32_t new_block_addr;
816 block_t *block, *new_block;
[1e65444]817
818 if (current_block == 0) {
[1ac1ab4]819 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
[1e65444]820 if (rc != EOK) {
[e63ce679]821 return rc;
[1e65444]822 }
823
824 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
825
826 inode_ref->dirty = true;
827
828 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
829 if (rc != EOK) {
[1ac1ab4]830 ext4_balloc_free_block(inode_ref, new_block_addr);
[e63ce679]831 return rc;
[1e65444]832 }
833
834 memset(new_block->data, 0, block_size);
835 new_block->dirty = true;
836
837 rc = block_put(new_block);
838 if (rc != EOK) {
[e63ce679]839 return rc;
[1e65444]840 }
841
842 current_block = new_block_addr;
843 }
844
845 /* Navigate through other levels, until we find the block number
846 * or find null reference meaning we are dealing with sparse file
847 */
848 while (level > 0) {
849
850 rc = block_get(&block, fs->device, current_block, 0);
851 if (rc != EOK) {
852 return rc;
853 }
854
855 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
856
[b12ca16]857 if ((level > 1) && (current_block == 0)) {
[1ac1ab4]858 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
[b12ca16]859 if (rc != EOK) {
[e63ce679]860 block_put(block);
861 return rc;
[b12ca16]862 }
[1e65444]863
[b12ca16]864 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
865 if (rc != EOK) {
[e63ce679]866 block_put(block);
867 return rc;
[b12ca16]868 }
[e63ce679]869
[b12ca16]870 memset(new_block->data, 0, block_size);
871 new_block->dirty = true;
[6088193]872
[b12ca16]873 rc = block_put(new_block);
874 if (rc != EOK) {
[e63ce679]875 block_put(block);
876 return rc;
[b12ca16]877 }
[1e65444]878
[b12ca16]879 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
880 block->dirty = true;
881 current_block = new_block_addr;
882 }
[1e65444]883
[b12ca16]884 if (level == 1) {
885 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
886 block->dirty = true;
[1e65444]887 }
888
889 rc = block_put(block);
890 if (rc != EOK) {
891 return rc;
892 }
893
894 level -= 1;
895
896 /* If we are on the last level, break here as
897 * there is no next level to visit
898 */
899 if (level == 0) {
900 break;
901 }
902
903 /* Visit the next level */
904 block_offset_in_level %= fs->inode_blocks_per_level[level];
905 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
906 }
[35f48f2]907
908 return EOK;
909}
910
[9fc72fb3]911/** TODO comment
912 *
913 */
[1ac1ab4]914int ext4_filesystem_release_inode_block(
[052e82d]915 ext4_inode_ref_t *inode_ref, uint32_t iblock)
[d5a78e28]916{
917 int rc;
[d9bbe45]918
[fffb061]919 uint32_t fblock;
920
[1ac1ab4]921 ext4_filesystem_t *fs = inode_ref->fs;
922
[ce6de59]923 // EXTENTS are handled otherwise
[5b0a3946]924 assert(! (ext4_superblock_has_feature_incompatible(fs->superblock,
925 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
926 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)));
[12b4a7f]927
[d9bbe45]928 ext4_inode_t *inode = inode_ref->inode;
929
[052e82d]930 /* Handle simple case when we are dealing with direct reference */
931 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
[12b4a7f]932 fblock = ext4_inode_get_direct_block(inode, iblock);
[052e82d]933 // Sparse file
934 if (fblock == 0) {
935 return EOK;
936 }
[d5a78e28]937
[052e82d]938 ext4_inode_set_direct_block(inode, iblock, 0);
[1ac1ab4]939 return ext4_balloc_free_block(inode_ref, fblock);
[12b4a7f]940 }
941
942
943 /* Determine the indirection level needed to get the desired block */
[d9bbe45]944 int level = -1;
945 for (int i = 1; i < 4; i++) {
[12b4a7f]946 if (iblock < fs->inode_block_limits[i]) {
947 level = i;
948 break;
[052e82d]949 }
[12b4a7f]950 }
951
952 if (level == -1) {
953 return EIO;
954 }
955
956 /* Compute offsets for the topmost level */
[d9bbe45]957 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
958 uint32_t current_block = ext4_inode_get_indirect_block(inode, level-1);
959 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
[d5a78e28]960
[12b4a7f]961 /* Navigate through other levels, until we find the block number
962 * or find null reference meaning we are dealing with sparse file
963 */
[d9bbe45]964 block_t *block;
[12b4a7f]965 while (level > 0) {
966 rc = block_get(&block, fs->device, current_block, 0);
967 if (rc != EOK) {
968 return rc;
[052e82d]969 }
[d5a78e28]970
[12b4a7f]971 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
[d5a78e28]972
[12b4a7f]973 // Set zero
974 if (level == 1) {
975 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
976 block->dirty = true;
[052e82d]977 }
[d5a78e28]978
[12b4a7f]979 rc = block_put(block);
980 if (rc != EOK) {
981 return rc;
982 }
[d5a78e28]983
[12b4a7f]984 level -= 1;
985
986 /* If we are on the last level, break here as
987 * there is no next level to visit
988 */
989 if (level == 0) {
990 break;
[052e82d]991 }
[12b4a7f]992
993 /* Visit the next level */
994 block_offset_in_level %= fs->inode_blocks_per_level[level];
995 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
996 }
997
998 fblock = current_block;
999
1000 if (fblock == 0) {
1001 return EOK;
[052e82d]1002 }
[d5a78e28]1003
[1ac1ab4]1004 return ext4_balloc_free_block(inode_ref, fblock);
[d5a78e28]1005
1006}
1007
[9fc72fb3]1008/** TODO comment
1009 *
1010 */
[5b16912]1011int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1012 uint32_t *fblock, uint32_t *iblock)
1013{
[ce6de59]1014 int rc;
1015
[7eb033ce]1016 EXT4FS_DBG("");
1017
[ce6de59]1018 // Handle extents separately
1019 if (ext4_superblock_has_feature_incompatible(
1020 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1021 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
[5b16912]1022
[ce6de59]1023 return ext4_extent_append_block(inode_ref, iblock, fblock);
[5b16912]1024
[ce6de59]1025 }
[5b16912]1026
1027 ext4_superblock_t *sb = inode_ref->fs->superblock;
1028
1029 // Compute next block index and allocate data block
1030 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1031 uint32_t block_size = ext4_superblock_get_block_size(sb);
1032
[7eb033ce]1033 // TODO zarovnat inode size a ne assert!!!
[5b16912]1034 assert(inode_size % block_size == 0);
1035
1036 // Logical blocks are numbered from 0
1037 uint32_t new_block_idx = inode_size / block_size;
1038
1039 uint32_t phys_block;
1040 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1041 if (rc != EOK) {
1042 return rc;
1043 }
1044
1045 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, new_block_idx, phys_block);
1046 if (rc != EOK) {
1047 ext4_balloc_free_block(inode_ref, phys_block);
1048 return rc;
1049 }
1050
1051 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1052
1053 inode_ref->dirty = true;
1054
1055 *fblock = phys_block;
1056 *iblock = new_block_idx;
1057 return EOK;
1058}
1059
[9fc72fb3]1060/** TODO comment
1061 *
1062 */
[1ac1ab4]1063int ext4_filesystem_add_orphan(ext4_inode_ref_t *inode_ref)
[ebcaff4]1064{
[1ac1ab4]1065 uint32_t next_orphan = ext4_superblock_get_last_orphan(
1066 inode_ref->fs->superblock);
[ebcaff4]1067 ext4_inode_set_deletion_time(inode_ref->inode, next_orphan);
[1ac1ab4]1068 ext4_superblock_set_last_orphan(
1069 inode_ref->fs->superblock, inode_ref->index);
[ebcaff4]1070 inode_ref->dirty = true;
1071
1072 return EOK;
1073}
1074
[9fc72fb3]1075/** TODO comment
1076 *
1077 */
[1ac1ab4]1078int ext4_filesystem_delete_orphan(ext4_inode_ref_t *inode_ref)
[ebcaff4]1079{
1080 int rc;
1081
[1ac1ab4]1082 uint32_t last_orphan = ext4_superblock_get_last_orphan(
1083 inode_ref->fs->superblock);
[ebcaff4]1084 assert(last_orphan > 0);
1085
1086 uint32_t next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1087
1088 if (last_orphan == inode_ref->index) {
[1ac1ab4]1089 ext4_superblock_set_last_orphan(inode_ref->fs->superblock, next_orphan);
[ebcaff4]1090 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1091 inode_ref->dirty = true;
1092 return EOK;
1093 }
1094
1095 ext4_inode_ref_t *current;
[1ac1ab4]1096 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, last_orphan, &current);
[ebcaff4]1097 if (rc != EOK) {
1098 return rc;
1099 }
1100 next_orphan = ext4_inode_get_deletion_time(current->inode);
1101
1102 bool found;
1103
1104 while (next_orphan != 0) {
1105 if (next_orphan == inode_ref->index) {
1106 next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1107 ext4_inode_set_deletion_time(current->inode, next_orphan);
1108 current->dirty = true;
1109 found = true;
1110 break;
1111 }
1112
1113 ext4_filesystem_put_inode_ref(current);
1114
[1ac1ab4]1115 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, next_orphan, &current);
[ebcaff4]1116 if (rc != EOK) {
1117 return rc;
1118 }
1119 next_orphan = ext4_inode_get_deletion_time(current->inode);
1120
1121 }
1122
1123 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1124
1125 rc = ext4_filesystem_put_inode_ref(current);
1126 if (rc != EOK) {
1127 return rc;
1128 }
1129
1130 if (!found) {
1131 return ENOENT;
1132 }
1133
1134 return EOK;
1135}
1136
[6c501f8]1137/**
1138 * @}
1139 */
Note: See TracBrowser for help on using the repository browser.