blob: fe112b31f81509562fe9649555d9d10a1d563c38 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * block.c --- iterate over all blocks in an inode
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <unistd.h>
11#include <stdlib.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000012#if HAVE_ERRNO_H
13#include <errno.h>
14#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000015
Theodore Ts'o3839e651997-04-26 13:21:57 +000016#include <linux/ext2_fs.h>
17
18#include "ext2fs.h"
19
20struct block_context {
21 ext2_filsys fs;
22 int (*func)(ext2_filsys fs,
23 blk_t *blocknr,
24 int bcount,
25 void *private);
26 int bcount;
27 int bsize;
28 int flags;
29 errcode_t errcode;
30 char *ind_buf;
31 char *dind_buf;
32 char *tind_buf;
33 void *private;
34};
35
36static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx)
37{
38 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +000039 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +000040 blk_t *block_nr;
41
42 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
43 ret = (*ctx->func)(ctx->fs, ind_block, -1, ctx->private);
44 if (!*ind_block || (ret & BLOCK_ABORT))
45 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +000046 if (*ind_block >= ctx->fs->super->s_blocks_count ||
47 *ind_block < ctx->fs->super->s_first_data_block) {
48 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
49 ret |= BLOCK_ERROR;
50 return ret;
51 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000052 ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
53 1, ctx->ind_buf);
54 if (ctx->errcode) {
55 ret |= BLOCK_ERROR;
56 return ret;
57 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +000058 limit = ctx->fs->blocksize >> 2;
59 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
60 block_nr = (blk_t *) ctx->ind_buf;
61 for (i = 0; i < limit; i++, block_nr++)
62 *block_nr = ext2fs_swab32(*block_nr);
63 }
64 block_nr = (blk_t *) ctx->ind_buf;
65 if (ctx->flags & BLOCK_FLAG_APPEND) {
66 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +000067 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
68 ctx->private);
Theodore Ts'o50e1e101997-04-26 13:58:21 +000069 changed |= flags;
70 if (flags & BLOCK_ABORT) {
71 ret |= BLOCK_ABORT;
72 break;
73 }
74 }
75 } else {
76 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
77 if (*block_nr == 0)
78 continue;
79 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
80 ctx->private);
81 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +000082 if (flags & BLOCK_ABORT) {
83 ret |= BLOCK_ABORT;
84 break;
85 }
86 }
87 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +000088 if (changed & BLOCK_CHANGED) {
89 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
90 block_nr = (blk_t *) ctx->ind_buf;
91 for (i = 0; i < limit; i++, block_nr++)
92 *block_nr = ext2fs_swab32(*block_nr);
93 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000094 ctx->errcode = io_channel_write_blk(ctx->fs->io, *ind_block,
95 1, ctx->ind_buf);
96 if (ctx->errcode)
97 ret |= BLOCK_ERROR | BLOCK_ABORT;
98 }
99 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
100 !(ret & BLOCK_ABORT))
101 ret |= (*ctx->func)(ctx->fs, ind_block, -1, ctx->private);
102 return ret;
103}
104
105static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx)
106{
107 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000108 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000109 blk_t *block_nr;
110
111 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
112 ret = (*ctx->func)(ctx->fs, dind_block, -2, ctx->private);
113 if (!*dind_block || (ret & BLOCK_ABORT))
114 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000115 if (*dind_block >= ctx->fs->super->s_blocks_count ||
116 *dind_block < ctx->fs->super->s_first_data_block) {
117 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
118 ret |= BLOCK_ERROR;
119 return ret;
120 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000121 ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
122 1, ctx->dind_buf);
123 if (ctx->errcode) {
124 ret |= BLOCK_ERROR;
125 return ret;
126 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000127 limit = ctx->fs->blocksize >> 2;
128 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
129 block_nr = (blk_t *) ctx->dind_buf;
130 for (i = 0; i < limit; i++, block_nr++)
131 *block_nr = ext2fs_swab32(*block_nr);
132 }
133 block_nr = (blk_t *) ctx->dind_buf;
134 if (ctx->flags & BLOCK_FLAG_APPEND) {
135 for (i = 0; i < limit; i++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000136 flags = block_iterate_ind(block_nr, ctx);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000137 changed |= flags;
138 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
139 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
140 break;
141 }
142 }
143 } else {
144 for (i = 0; i < limit; i++, block_nr++) {
145 if (*block_nr == 0)
146 continue;
147 flags = block_iterate_ind(block_nr, ctx);
148 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000149 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
150 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
151 break;
152 }
153 }
154 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000155 if (changed & BLOCK_CHANGED) {
156 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
157 block_nr = (blk_t *) ctx->dind_buf;
158 for (i = 0; i < limit; i++, block_nr++)
159 *block_nr = ext2fs_swab32(*block_nr);
160 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000161 ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
162 1, ctx->dind_buf);
163 if (ctx->errcode)
164 ret |= BLOCK_ERROR | BLOCK_ABORT;
165 }
166 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
167 !(ret & BLOCK_ABORT))
168 ret |= (*ctx->func)(ctx->fs, dind_block, -2, ctx->private);
169 return ret;
170}
171
172static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx)
173{
174 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000175 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000176 blk_t *block_nr;
177
178 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
179 ret = (*ctx->func)(ctx->fs, tind_block, -3, ctx->private);
180 if (!*tind_block || (ret & BLOCK_ABORT))
181 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182 if (*tind_block >= ctx->fs->super->s_blocks_count ||
183 *tind_block < ctx->fs->super->s_first_data_block) {
184 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
185 ret |= BLOCK_ERROR;
186 return ret;
187 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188 ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
189 1, ctx->tind_buf);
190 if (ctx->errcode) {
191 ret |= BLOCK_ERROR;
192 return ret;
193 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000194 limit = ctx->fs->blocksize >> 2;
195 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
196 block_nr = (blk_t *) ctx->tind_buf;
197 for (i = 0; i < limit; i++, block_nr++)
198 *block_nr = ext2fs_swab32(*block_nr);
199 }
200 block_nr = (blk_t *) ctx->tind_buf;
201 if (ctx->flags & BLOCK_FLAG_APPEND) {
202 for (i = 0; i < limit; i++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000203 flags = block_iterate_dind(block_nr, ctx);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000204 changed |= flags;
205 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
206 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
207 break;
208 }
209 }
210 } else {
211 for (i = 0; i < limit; i++, block_nr++) {
212 if (*block_nr == 0)
213 continue;
214 flags = block_iterate_dind(block_nr, ctx);
215 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000216 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
217 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
218 break;
219 }
220 }
221 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000222 if (changed & BLOCK_CHANGED) {
223 if (ctx->fs->flags & EXT2_SWAP_BYTES) {
224 block_nr = (blk_t *) ctx->tind_buf;
225 for (i = 0; i < limit; i++, block_nr++)
226 *block_nr = ext2fs_swab32(*block_nr);
227 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000228 ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
229 1, ctx->tind_buf);
230 if (ctx->errcode)
231 ret |= BLOCK_ERROR | BLOCK_ABORT;
232 }
233 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
234 !(ret & BLOCK_ABORT))
235 ret |= (*ctx->func)(ctx->fs, tind_block, -3, ctx->private);
236
237 return ret;
238}
239
240errcode_t ext2fs_block_iterate(ext2_filsys fs,
241 ino_t ino,
242 int flags,
243 char *block_buf,
244 int (*func)(ext2_filsys fs,
245 blk_t *blocknr,
246 int blockcnt,
247 void *private),
248 void *private)
249{
250 int i;
251 int ret = 0;
252 struct block_context ctx;
253 blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
254 struct ext2_inode inode;
255 errcode_t retval;
256
Theodore Ts'of3db3561997-04-26 13:34:30 +0000257 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
258
Theodore Ts'o3839e651997-04-26 13:21:57 +0000259 ret = ext2fs_get_blocks(fs, ino, blocks);
260 if (ret)
261 return ret;
262
263 ctx.fs = fs;
264 ctx.func = func;
265 ctx.private = private;
266 ctx.bcount = 0;
267 ctx.flags = flags;
268 if (block_buf) {
269 ctx.ind_buf = block_buf;
270 } else {
271 ctx.ind_buf = malloc(fs->blocksize * 3);
272 if (!ctx.ind_buf)
273 return ENOMEM;
274 }
275 ctx.dind_buf = ctx.ind_buf + fs->blocksize;
276 ctx.tind_buf = ctx.dind_buf + fs->blocksize;
277
278 for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
279 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
280 ret |= (*func)(fs, &blocks[i], ctx.bcount, private);
281 if (ret & BLOCK_ABORT)
282 goto abort;
283 }
284 }
285 if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
286 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, &ctx);
287 if (ret & BLOCK_ABORT)
288 goto abort;
289 }
290 if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
291 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, &ctx);
292 if (ret & BLOCK_ABORT)
293 goto abort;
294 }
295 if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND))
296 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, &ctx);
297
298abort:
299 if (ret & BLOCK_CHANGED) {
300 retval = ext2fs_read_inode(fs, ino, &inode);
301 if (retval)
302 return retval;
303 for (i=0; i < EXT2_N_BLOCKS; i++)
304 inode.i_block[i] = blocks[i];
305 retval = ext2fs_write_inode(fs, ino, &inode);
306 if (retval)
307 return retval;
308 }
309
310 if (!block_buf)
311 free(ctx.ind_buf);
312
313 return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
314}