blob: 5b96ea2328575a315020c22b26d80a7fc8c67812 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2007 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <string.h>
25#include <stdlib.h>
26#include <debug.h>
27#include <trace.h>
28#include "ext2_priv.h"
29
30#define LOCAL_TRACE 0
31
32int ext2_read_block(ext2_t *ext2, void *buf, blocknum_t bnum)
33{
34 return bcache_read_block(ext2->cache, buf, bnum);
35}
36
37int ext2_get_block(ext2_t *ext2, void **ptr, blocknum_t bnum)
38{
39 return bcache_get_block(ext2->cache, ptr, bnum);
40}
41
42int ext2_put_block(ext2_t *ext2, blocknum_t bnum)
43{
44 return bcache_put_block(ext2->cache, bnum);
45}
46
47static int ext2_calculate_block_pointer_pos(ext2_t *ext2, blocknum_t block_to_find, uint32_t *level, uint32_t pos[])
48{
49 uint32_t block_ptr_per_block, block_ptr_per_2nd_block;
50
51 // XXX optimize this
52
53 // See if it's in the direct blocks
54 if (block_to_find < EXT2_NDIR_BLOCKS) {
55 *level = 0;
56 pos[0] = block_to_find;
57 return 0;
58 }
59
60 block_ptr_per_block = EXT2_ADDR_PER_BLOCK(ext2->sb);
61 block_to_find -= EXT2_NDIR_BLOCKS;
62 // See if it's in the first indirect block
63 if (block_to_find < block_ptr_per_block) {
64 *level = 1;
65 pos[0] = EXT2_IND_BLOCK;
66 pos[1] = block_to_find;
67 return 0;
68 }
69
70 block_to_find -= block_ptr_per_block;
71 block_ptr_per_2nd_block = block_ptr_per_block * block_ptr_per_block;
72 // See if it's in the second indirect block
73 if (block_to_find < (block_ptr_per_2nd_block)) {
74 *level = 2;
75 pos[0] = EXT2_DIND_BLOCK;
76 pos[1] = block_to_find / block_ptr_per_block;
77 pos[2] = block_to_find % block_ptr_per_block;
78 return 0;
79 }
80
81 block_to_find -= block_ptr_per_2nd_block;
82 // See if it's in the third indirect block
83 if (block_to_find < (block_ptr_per_2nd_block * block_ptr_per_block)) {
84 *level = 3;
85 pos[0] = EXT2_TIND_BLOCK;
86 pos[1] = block_to_find / block_ptr_per_2nd_block;
87 pos[2] = (block_to_find % block_ptr_per_2nd_block) / block_ptr_per_block;
88 pos[3] = (block_to_find % block_ptr_per_2nd_block) % block_ptr_per_block;
89 return 0;
90 }
91
92 // The block requested must be too big.
93 return -1;
94}
95
96// This function returns a pointer to the cache block that corresponds to the indirect block pointer.
97int ext2_get_indirect_block_pointer_cache_block(ext2_t *ext2, struct ext2_inode *inode, blocknum_t **cache_block, uint32_t level, uint32_t pos[], uint *block_loaded)
98{
99 uint32_t current_level = 0;
100 uint current_block = 0, last_block;
101 blocknum_t *block = NULL;
102 int err;
103
104 if ((level > 3) || (level == 0)) {
105 err = -1;
106 goto error;
107 }
108
109 // Dig down into the indirect blocks. When done, current_block should point to the target.
110 while (current_level < level) {
111 if (current_level == 0) {
112 // read the direct block, simulates a prior loop
113 current_block = LE32(inode->i_block[pos[0]]);
114 }
115
116 if (current_block == 0) {
117 err = -1;
118 goto error;
119 }
120
121 last_block = current_block;
122 current_level++;
123 *block_loaded = current_block;
124
125 err = ext2_get_block(ext2, (void **)(void *)&block, current_block);
126 if (err < 0) {
127 goto error;
128 }
129
130 if (current_level < level) {
131 current_block = LE32(block[pos[current_level]]);
132 ext2_put_block(ext2, last_block);
133 }
134 }
135
136 *cache_block = block;
137 return 0;
138
139error:
140 *cache_block = NULL;
141 *block_loaded = 0;
142 return err;
143}
144
145/* translate a file block to a physical block */
146static blocknum_t file_block_to_fs_block(ext2_t *ext2, struct ext2_inode *inode, uint fileblock)
147{
148 int err;
149 blocknum_t block;
150
151 LTRACEF("inode %p, fileblock %u\n", inode, fileblock);
152
153 uint32_t pos[4];
154 uint32_t level = 0;
155 ext2_calculate_block_pointer_pos(ext2, fileblock, &level, pos);
156
157 LTRACEF("level %d, pos 0x%x 0x%x 0x%x 0x%x\n", level, pos[0], pos[1], pos[2], pos[3]);
158
159 if (level == 0) {
160 /* direct block, just return it directly */
161 block = LE32(inode->i_block[fileblock]);
162 } else {
163 /* at least one level of indirection, get a pointer to the final indirect block table and dereference it */
164 blocknum_t *ind_table;
165 blocknum_t phys_block;
166 err = ext2_get_indirect_block_pointer_cache_block(ext2, inode, &ind_table, level, pos, &phys_block);
167 if (err < 0)
168 return 0;
169
170 /* dereference the final entry in the final table */
171 block = LE32(ind_table[pos[level]]);
172 LTRACEF("block %u, indirect_block %u\n", block, phys_block);
173
174 /* release the ref on the cache block */
175 ext2_put_block(ext2, phys_block);
176 }
177
178 LTRACEF("returning %u\n", block);
179
180 return block;
181}
182
183ssize_t ext2_read_inode(ext2_t *ext2, struct ext2_inode *inode, void *_buf, off_t offset, size_t len)
184{
185 int err = 0;
186 size_t bytes_read = 0;
187 uint8_t *buf = _buf;
188
189 /* calculate the file size */
190 off_t file_size = ext2_file_len(ext2, inode);
191
192 LTRACEF("inode %p, offset %lld, len %zd, file_size %lld\n", inode, offset, len, file_size);
193
194 /* trim the read */
195 if (offset > file_size)
196 return 0;
197 if (offset + len >= file_size)
198 len = file_size - offset;
199 if (len == 0)
200 return 0;
201
202 /* calculate the starting file block */
203 uint file_block = offset / EXT2_BLOCK_SIZE(ext2->sb);
204
205 /* handle partial first block */
206 if ((offset % EXT2_BLOCK_SIZE(ext2->sb)) != 0) {
207 uint8_t temp[EXT2_BLOCK_SIZE(ext2->sb)];
208
209 /* calculate the block and read it */
210 blocknum_t phys_block = file_block_to_fs_block(ext2, inode, file_block);
211 if (phys_block == 0) {
212 memset(temp, 0, EXT2_BLOCK_SIZE(ext2->sb));
213 } else {
214 ext2_read_block(ext2, temp, phys_block);
215 }
216
217 /* copy out what we need */
218 size_t block_offset = offset % EXT2_BLOCK_SIZE(ext2->sb);
219 size_t tocopy = MIN(len, EXT2_BLOCK_SIZE(ext2->sb) - block_offset);
220 memcpy(buf, temp + block_offset, tocopy);
221
222 /* increment our stuff */
223 file_block++;
224 len -= tocopy;
225 bytes_read += tocopy;
226 buf += tocopy;
227 }
228
229 /* handle middle blocks */
230 while (len >= EXT2_BLOCK_SIZE(ext2->sb)) {
231 /* calculate the block and read it */
232 blocknum_t phys_block = file_block_to_fs_block(ext2, inode, file_block);
233 if (phys_block == 0) {
234 memset(buf, 0, EXT2_BLOCK_SIZE(ext2->sb));
235 } else {
236 ext2_read_block(ext2, buf, phys_block);
237 }
238
239 /* increment our stuff */
240 file_block++;
241 len -= EXT2_BLOCK_SIZE(ext2->sb);
242 bytes_read += EXT2_BLOCK_SIZE(ext2->sb);
243 buf += EXT2_BLOCK_SIZE(ext2->sb);
244 }
245
246 /* handle partial last block */
247 if (len > 0) {
248 uint8_t temp[EXT2_BLOCK_SIZE(ext2->sb)];
249
250 /* calculate the block and read it */
251 blocknum_t phys_block = file_block_to_fs_block(ext2, inode, file_block);
252 if (phys_block == 0) {
253 memset(temp, 0, EXT2_BLOCK_SIZE(ext2->sb));
254 } else {
255 ext2_read_block(ext2, temp, phys_block);
256 }
257
258 /* copy out what we need */
259 memcpy(buf, temp, len);
260
261 /* increment our stuff */
262 bytes_read += len;
263 }
264
265 LTRACEF("err %d, bytes_read %zu\n", err, bytes_read);
266
267 return (err < 0) ? err : (ssize_t)bytes_read;
268}
269