1#include <errno.h>
2#include <fs/ext2.h>
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdint.h>
6
7#define EXT2_NDIR_BLOCKS 12
8#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
9#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
10#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
11#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
12
13static uint32_t blocks_per_indirection(struct ext2_fs *fs) {
14 return fs->block_size / sizeof(uint32_t);
15}
16
17static void clear_block_pointer(struct ext2_fs *fs, struct ext2_inode *inode,
18 uint32_t block_index) {
19 uint32_t bpi = blocks_per_indirection(fs);
20
21 /* current entry and indirection levels */
22 struct bcache_entry *ent, *i2, *i3;
23
24 if (block_index < EXT2_NDIR_BLOCKS) {
25 inode->block[block_index] = 0;
26 } else if (block_index < EXT2_NDIR_BLOCKS + bpi) {
27 if (inode->block[EXT2_IND_BLOCK]) {
28 uint32_t *ind = (uint32_t *) ext2_block_read(
29 fs, block_num: inode->block[EXT2_IND_BLOCK], out: &ent);
30
31 if (!ind)
32 return;
33
34 ind[block_index - EXT2_NDIR_BLOCKS] = 0;
35 bcache_ent_release(ent);
36
37 ext2_block_write(fs, ent, EXT2_PRIO_INODE);
38 }
39 } else if (block_index < EXT2_NDIR_BLOCKS + bpi + bpi * bpi) {
40 uint32_t dbl_index = block_index - EXT2_NDIR_BLOCKS - bpi;
41 uint32_t ind1 = dbl_index / bpi;
42 uint32_t ind2 = dbl_index % bpi;
43
44 if (inode->block[EXT2_DIND_BLOCK]) {
45 uint32_t *dind = (uint32_t *) ext2_block_read(
46 fs, block_num: inode->block[EXT2_DIND_BLOCK], out: &i2);
47 if (!dind)
48 return;
49
50 if (dind[ind1]) {
51 uint32_t *ind =
52 (uint32_t *) ext2_block_read(fs, block_num: dind[ind1], out: &ent);
53 if (!ind)
54 return;
55
56 ind[ind2] = 0;
57 bcache_ent_release(ent);
58
59 ext2_block_write(fs, ent, EXT2_PRIO_INODE);
60 }
61 bcache_ent_release(ent: i2);
62 }
63
64 } else {
65 uint32_t tpl_index = block_index - EXT2_NDIR_BLOCKS - bpi - bpi * bpi;
66 uint32_t ind1 = tpl_index / (bpi * bpi);
67 uint32_t rem = tpl_index % (bpi * bpi);
68 uint32_t ind2 = rem / bpi;
69 uint32_t ind3 = rem % bpi;
70
71 if (inode->block[EXT2_TIND_BLOCK]) {
72
73 uint32_t *tind = (uint32_t *) ext2_block_read(
74 fs, block_num: inode->block[EXT2_TIND_BLOCK], out: &i3);
75 if (!tind)
76 return;
77
78 if (tind[ind1]) {
79 uint32_t *dind =
80 (uint32_t *) ext2_block_read(fs, block_num: tind[ind1], out: &i2);
81 if (!dind) {
82 bcache_ent_release(ent: i3);
83 return;
84 }
85
86 if (dind[ind2]) {
87 uint32_t *ind =
88 (uint32_t *) ext2_block_read(fs, block_num: dind[ind2], out: &ent);
89
90 ind[ind3] = 0;
91 bcache_ent_release(ent);
92
93 ext2_block_write(fs, ent, EXT2_PRIO_INODE);
94 }
95 bcache_ent_release(ent: i2);
96 }
97 bcache_ent_release(ent: i3);
98 }
99 }
100}
101
102enum errno ext2_truncate_file(struct ext2_fs *fs, struct ext2_full_inode *inode,
103 uint32_t new_size) {
104 uint32_t old_block_count =
105 (inode->node.size + fs->block_size - 1) / fs->block_size;
106 uint32_t new_block_count = (new_size + fs->block_size - 1) / fs->block_size;
107 uint32_t bpi = blocks_per_indirection(fs);
108
109 for (uint32_t i = new_block_count; i < old_block_count; i++) {
110 uint32_t block_num =
111 ext2_get_or_set_block(fs, inode: &inode->node, block_index: i, new_block_num: 1, false, NULL);
112 if (block_num) {
113 ext2_free_block(fs, block_num);
114 clear_block_pointer(fs, inode: &inode->node, block_index: i);
115 }
116 }
117
118 if (new_block_count <= EXT2_NDIR_BLOCKS &&
119 inode->node.block[EXT2_IND_BLOCK]) {
120 ext2_free_block(fs, block_num: inode->node.block[EXT2_IND_BLOCK]);
121 inode->node.block[EXT2_IND_BLOCK] = 0;
122 }
123
124 if (new_block_count <= EXT2_NDIR_BLOCKS + bpi &&
125 inode->node.block[EXT2_DIND_BLOCK]) {
126 ext2_free_block(fs, block_num: inode->node.block[EXT2_DIND_BLOCK]);
127 inode->node.block[EXT2_DIND_BLOCK] = 0;
128 }
129
130 if (new_block_count <= EXT2_NDIR_BLOCKS + bpi + bpi * bpi &&
131 inode->node.block[EXT2_TIND_BLOCK]) {
132 ext2_free_block(fs, block_num: inode->node.block[EXT2_TIND_BLOCK]);
133 inode->node.block[EXT2_TIND_BLOCK] = 0;
134 }
135
136 inode->node.size = new_size;
137 inode->node.blocks =
138 new_block_count * (fs->block_size / fs->drive->sector_size);
139
140 return ext2_inode_write(fs, inode_num: inode->inode_num, inode: &inode->node)
141 ? ERR_OK
142 : ERR_FS_INTERNAL;
143}
144