]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * pass5.c --- check block and inode bitmaps against on-disk bitmaps | |
efc6f628 | 3 | * |
21c84b71 TT |
4 | * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. |
5 | * | |
6 | * %Begin-Header% | |
7 | * This file may be redistributed under the terms of the GNU Public | |
8 | * License. | |
9 | * %End-Header% | |
efc6f628 | 10 | * |
3839e657 TT |
11 | */ |
12 | ||
d1154eb4 | 13 | #include "config.h" |
efa1a355 LC |
14 | #include <stdint.h> |
15 | #include <sys/types.h> | |
16 | #include <sys/stat.h> | |
17 | #include <sys/ioctl.h> | |
18 | #include <fcntl.h> | |
19 | #include <errno.h> | |
20 | ||
3839e657 | 21 | #include "e2fsck.h" |
1b6bf175 | 22 | #include "problem.h" |
3839e657 | 23 | |
efa1a355 LC |
24 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) |
25 | ||
1b6bf175 TT |
26 | static void check_block_bitmaps(e2fsck_t ctx); |
27 | static void check_inode_bitmaps(e2fsck_t ctx); | |
28 | static void check_inode_end(e2fsck_t ctx); | |
29 | static void check_block_end(e2fsck_t ctx); | |
3839e657 | 30 | |
08b21301 | 31 | void e2fsck_pass5(e2fsck_t ctx) |
3839e657 | 32 | { |
8bf191e8 | 33 | #ifdef RESOURCE_TRACK |
3839e657 | 34 | struct resource_track rtrack; |
8bf191e8 | 35 | #endif |
1b6bf175 | 36 | struct problem_context pctx; |
efc6f628 | 37 | |
3839e657 TT |
38 | #ifdef MTRACE |
39 | mtrace_print("Pass 5"); | |
40 | #endif | |
41 | ||
6d96b00d | 42 | init_resource_track(&rtrack, ctx->fs->io); |
1b6bf175 | 43 | clear_problem_context(&pctx); |
3839e657 | 44 | |
1b6bf175 TT |
45 | if (!(ctx->options & E2F_OPT_PREEN)) |
46 | fix_problem(ctx, PR_5_PASS_HEADER, &pctx); | |
3839e657 | 47 | |
f8188fff | 48 | if (ctx->progress) |
efac9a1b | 49 | if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2)) |
a02ce9df | 50 | return; |
f8188fff TT |
51 | |
52 | e2fsck_read_bitmaps(ctx); | |
53 | ||
1b6bf175 | 54 | check_block_bitmaps(ctx); |
a02ce9df | 55 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 56 | return; |
1b6bf175 | 57 | check_inode_bitmaps(ctx); |
a02ce9df | 58 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 59 | return; |
1b6bf175 | 60 | check_inode_end(ctx); |
a02ce9df | 61 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 62 | return; |
1b6bf175 | 63 | check_block_end(ctx); |
a02ce9df | 64 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 65 | return; |
3839e657 | 66 | |
1b6bf175 TT |
67 | ext2fs_free_inode_bitmap(ctx->inode_used_map); |
68 | ctx->inode_used_map = 0; | |
69 | ext2fs_free_inode_bitmap(ctx->inode_dir_map); | |
70 | ctx->inode_dir_map = 0; | |
71 | ext2fs_free_block_bitmap(ctx->block_found_map); | |
72 | ctx->block_found_map = 0; | |
73 | ||
9facd076 | 74 | print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io); |
3839e657 TT |
75 | } |
76 | ||
46795326 LC |
77 | static void e2fsck_discard_blocks(e2fsck_t ctx, blk64_t start, |
78 | blk64_t count) | |
efa1a355 LC |
79 | { |
80 | ext2_filsys fs = ctx->fs; | |
efa1a355 LC |
81 | |
82 | /* | |
83 | * If the filesystem has changed it means that there was an corruption | |
84 | * which should be repaired, but in some cases just one e2fsck run is | |
85 | * not enough to fix the problem, hence it is not safe to run discard | |
86 | * in this case. | |
87 | */ | |
46795326 | 88 | if (ext2fs_test_changed(fs)) |
efa1a355 LC |
89 | ctx->options &= ~E2F_OPT_DISCARD; |
90 | ||
f0fe5dae | 91 | if ((ctx->options & E2F_OPT_DISCARD) && |
efa1a355 LC |
92 | (io_channel_discard(fs->io, start, count))) |
93 | ctx->options &= ~E2F_OPT_DISCARD; | |
94 | } | |
95 | ||
57581c10 LC |
96 | /* |
97 | * This will try to discard number 'count' inodes starting at | |
98 | * inode number 'start' within the 'group'. Note that 'start' | |
99 | * is 1-based, it means that we need to adjust it by -1 in this | |
100 | * function to compute right offset in the particular inode table. | |
101 | */ | |
e64e6761 TT |
102 | static void e2fsck_discard_inodes(e2fsck_t ctx, dgrp_t group, |
103 | ext2_ino_t start, int count) | |
57581c10 LC |
104 | { |
105 | ext2_filsys fs = ctx->fs; | |
106 | blk64_t blk, num; | |
57581c10 LC |
107 | |
108 | /* | |
109 | * Sanity check for 'start' | |
110 | */ | |
111 | if ((start < 1) || (start > EXT2_INODES_PER_GROUP(fs->super))) { | |
112 | printf("PROGRAMMING ERROR: Got start %d outside of group %d!" | |
113 | " Disabling discard\n", | |
114 | start, group); | |
115 | ctx->options &= ~E2F_OPT_DISCARD; | |
116 | } | |
117 | ||
c15386cd LC |
118 | /* |
119 | * Do not attempt to discard if E2F_OPT_DISCARD is not set. And also | |
120 | * skip the discard on this group if discard does not zero data. | |
121 | * The reason is that if the inode table is not zeroed discard would | |
122 | * no help us since we need to zero it anyway, or if the inode table | |
123 | * is zeroed then the read after discard would not be deterministic | |
124 | * anyway and we would not be able to assume that this inode table | |
125 | * was zeroed anymore so we would have to zero it again, which does | |
126 | * not really make sense. | |
127 | */ | |
128 | if (!(ctx->options & E2F_OPT_DISCARD) || | |
129 | !io_channel_discard_zeroes_data(fs->io)) | |
57581c10 LC |
130 | return; |
131 | ||
132 | /* | |
133 | * Start is inode number within the group which starts | |
134 | * counting from 1, so we need to adjust it. | |
135 | */ | |
136 | start -= 1; | |
137 | ||
138 | /* | |
139 | * We can discard only blocks containing only unused | |
140 | * inodes in the table. | |
141 | */ | |
142 | blk = DIV_ROUND_UP(start, | |
143 | EXT2_INODES_PER_BLOCK(fs->super)); | |
144 | count -= (blk * EXT2_INODES_PER_BLOCK(fs->super) - start); | |
145 | blk += ext2fs_inode_table_loc(fs, group); | |
146 | num = count / EXT2_INODES_PER_BLOCK(fs->super); | |
147 | ||
148 | if (num > 0) | |
46795326 | 149 | e2fsck_discard_blocks(ctx, blk, num); |
57581c10 LC |
150 | } |
151 | ||
6dc64392 | 152 | #define NO_BLK ((blk64_t) -1) |
f122632e | 153 | |
546a1ff1 | 154 | static void print_bitmap_problem(e2fsck_t ctx, int problem, |
f122632e TT |
155 | struct problem_context *pctx) |
156 | { | |
157 | switch (problem) { | |
158 | case PR_5_BLOCK_UNUSED: | |
159 | if (pctx->blk == pctx->blk2) | |
160 | pctx->blk2 = 0; | |
161 | else | |
162 | problem = PR_5_BLOCK_RANGE_UNUSED; | |
163 | break; | |
164 | case PR_5_BLOCK_USED: | |
165 | if (pctx->blk == pctx->blk2) | |
166 | pctx->blk2 = 0; | |
167 | else | |
168 | problem = PR_5_BLOCK_RANGE_USED; | |
169 | break; | |
170 | case PR_5_INODE_UNUSED: | |
171 | if (pctx->ino == pctx->ino2) | |
172 | pctx->ino2 = 0; | |
173 | else | |
174 | problem = PR_5_INODE_RANGE_UNUSED; | |
175 | break; | |
176 | case PR_5_INODE_USED: | |
177 | if (pctx->ino == pctx->ino2) | |
178 | pctx->ino2 = 0; | |
179 | else | |
180 | problem = PR_5_INODE_RANGE_USED; | |
181 | break; | |
182 | } | |
183 | fix_problem(ctx, problem, pctx); | |
184 | pctx->blk = pctx->blk2 = NO_BLK; | |
185 | pctx->ino = pctx->ino2 = 0; | |
186 | } | |
49e2df29 | 187 | |
3385a254 TT |
188 | /* Just to be more succint */ |
189 | #define B2C(x) EXT2FS_B2C(fs, (x)) | |
190 | #define EQ_CLSTR(x, y) (B2C(x) == B2C(y)) | |
191 | #define LE_CLSTR(x, y) (B2C(x) <= B2C(y)) | |
192 | #define GE_CLSTR(x, y) (B2C(x) >= B2C(y)) | |
193 | ||
1b6bf175 | 194 | static void check_block_bitmaps(e2fsck_t ctx) |
3839e657 | 195 | { |
1b6bf175 | 196 | ext2_filsys fs = ctx->fs; |
20f2ccb3 | 197 | blk64_t i; |
e64e6761 | 198 | unsigned int *free_array; |
3839e657 | 199 | int group = 0; |
e64e6761 | 200 | unsigned int blocks = 0; |
6dc64392 | 201 | blk64_t free_blocks = 0; |
efa1a355 | 202 | blk64_t first_free = ext2fs_blocks_count(fs->super); |
e64e6761 | 203 | unsigned int group_free = 0; |
3839e657 | 204 | int actual, bitmap; |
1b6bf175 | 205 | struct problem_context pctx; |
f122632e | 206 | int problem, save_problem, fixit, had_problem; |
1b6bf175 | 207 | errcode_t retval; |
16b851cd | 208 | int csum_flag; |
f5fa2007 | 209 | int skip_group = 0; |
479463aa KM |
210 | int old_desc_blocks = 0; |
211 | int count = 0; | |
212 | int cmp_block = 0; | |
213 | int redo_flag = 0; | |
20f2ccb3 | 214 | blk64_t super_blk, old_desc_blk, new_desc_blk; |
53e3120c TT |
215 | char *actual_buf, *bitmap_buf; |
216 | ||
217 | actual_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize, | |
218 | "actual bitmap buffer"); | |
219 | bitmap_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize, | |
220 | "bitmap block buffer"); | |
49e2df29 | 221 | |
1b6bf175 | 222 | clear_problem_context(&pctx); |
e64e6761 TT |
223 | free_array = (unsigned int *) e2fsck_allocate_memory(ctx, |
224 | fs->group_desc_count * sizeof(unsigned int), "free block count array"); | |
50e1e10f | 225 | |
3385a254 | 226 | if ((B2C(fs->super->s_first_data_block) < |
c5d2f50d | 227 | ext2fs_get_block_bitmap_start2(ctx->block_found_map)) || |
3385a254 | 228 | (B2C(ext2fs_blocks_count(fs->super)-1) > |
c5d2f50d | 229 | ext2fs_get_block_bitmap_end2(ctx->block_found_map))) { |
1b6bf175 | 230 | pctx.num = 1; |
3385a254 TT |
231 | pctx.blk = B2C(fs->super->s_first_data_block); |
232 | pctx.blk2 = B2C(ext2fs_blocks_count(fs->super) - 1); | |
c5d2f50d VAH |
233 | pctx.ino = ext2fs_get_block_bitmap_start2(ctx->block_found_map); |
234 | pctx.ino2 = ext2fs_get_block_bitmap_end2(ctx->block_found_map); | |
1b6bf175 | 235 | fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); |
08b21301 TT |
236 | |
237 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ | |
49e2df29 | 238 | goto errout; |
50e1e10f | 239 | } |
49e2df29 | 240 | |
3385a254 | 241 | if ((B2C(fs->super->s_first_data_block) < |
c5d2f50d | 242 | ext2fs_get_block_bitmap_start2(fs->block_map)) || |
3385a254 | 243 | (B2C(ext2fs_blocks_count(fs->super)-1) > |
c5d2f50d | 244 | ext2fs_get_block_bitmap_end2(fs->block_map))) { |
1b6bf175 | 245 | pctx.num = 2; |
3385a254 TT |
246 | pctx.blk = B2C(fs->super->s_first_data_block); |
247 | pctx.blk2 = B2C(ext2fs_blocks_count(fs->super) - 1); | |
c5d2f50d VAH |
248 | pctx.ino = ext2fs_get_block_bitmap_start2(fs->block_map); |
249 | pctx.ino2 = ext2fs_get_block_bitmap_end2(fs->block_map); | |
1b6bf175 | 250 | fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); |
08b21301 TT |
251 | |
252 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ | |
49e2df29 | 253 | goto errout; |
50e1e10f | 254 | } |
49e2df29 | 255 | |
49a7360b JS |
256 | csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
257 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM); | |
63c4969c TT |
258 | redo_counts: |
259 | had_problem = 0; | |
f122632e TT |
260 | save_problem = 0; |
261 | pctx.blk = pctx.blk2 = NO_BLK; | |
16b851cd | 262 | if (csum_flag && |
cd65a24e | 263 | (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT))) |
f5fa2007 | 264 | skip_group++; |
3385a254 | 265 | for (i = B2C(fs->super->s_first_data_block); |
4efbac6f | 266 | i < ext2fs_blocks_count(fs->super); |
44fe08f1 | 267 | i += EXT2FS_CLUSTER_RATIO(fs)) { |
53e3120c TT |
268 | int first_block_in_bg = (B2C(i) - |
269 | B2C(fs->super->s_first_data_block)) % | |
270 | fs->super->s_clusters_per_group == 0; | |
271 | int n, nbytes = fs->super->s_clusters_per_group / 8; | |
272 | ||
c5d2f50d | 273 | actual = ext2fs_fast_test_block_bitmap2(ctx->block_found_map, i); |
f5fa2007 | 274 | |
53e3120c TT |
275 | /* |
276 | * Try to optimize pass5 by extracting a bitmap block | |
277 | * as expected from what we have on disk, and then | |
278 | * comparing the two. If they are identical, then | |
279 | * update the free block counts and go on to the next | |
280 | * block group. This is much faster than doing the | |
281 | * individual bit-by-bit comparison. The one downside | |
282 | * is that this doesn't work if we are asking e2fsck | |
283 | * to do a discard operation. | |
284 | */ | |
285 | if (!first_block_in_bg || | |
286 | (group == (int)fs->group_desc_count - 1) || | |
287 | (ctx->options & E2F_OPT_DISCARD)) | |
288 | goto no_optimize; | |
289 | ||
290 | retval = ext2fs_get_block_bitmap_range2(ctx->block_found_map, | |
291 | B2C(i), fs->super->s_clusters_per_group, | |
292 | actual_buf); | |
293 | if (retval) | |
294 | goto no_optimize; | |
295 | if (ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)) | |
296 | memset(bitmap_buf, 0, nbytes); | |
297 | else { | |
298 | retval = ext2fs_get_block_bitmap_range2(fs->block_map, | |
299 | B2C(i), fs->super->s_clusters_per_group, | |
300 | bitmap_buf); | |
301 | if (retval) | |
302 | goto no_optimize; | |
303 | } | |
304 | if (memcmp(actual_buf, bitmap_buf, nbytes) != 0) | |
305 | goto no_optimize; | |
306 | n = ext2fs_bitcount(actual_buf, nbytes); | |
307 | group_free = fs->super->s_clusters_per_group - n; | |
308 | free_blocks += group_free; | |
309 | i += fs->super->s_clusters_per_group - 1; | |
310 | goto next_group; | |
311 | no_optimize: | |
312 | ||
f5fa2007 | 313 | if (skip_group) { |
53e3120c | 314 | if (first_block_in_bg) { |
479463aa KM |
315 | super_blk = 0; |
316 | old_desc_blk = 0; | |
317 | new_desc_blk = 0; | |
20f2ccb3 | 318 | ext2fs_super_and_bgd_loc2(fs, group, &super_blk, |
4a2924ea TT |
319 | &old_desc_blk, &new_desc_blk, 0); |
320 | ||
479463aa KM |
321 | if (fs->super->s_feature_incompat & |
322 | EXT2_FEATURE_INCOMPAT_META_BG) | |
323 | old_desc_blocks = | |
324 | fs->super->s_first_meta_bg; | |
325 | else | |
326 | old_desc_blocks = fs->desc_blocks + | |
4a2924ea TT |
327 | fs->super->s_reserved_gdt_blocks; |
328 | ||
479463aa | 329 | count = 0; |
3385a254 | 330 | cmp_block = fs->super->s_clusters_per_group; |
479463aa | 331 | if (group == (int)fs->group_desc_count - 1) |
32318ff2 YY |
332 | cmp_block = EXT2FS_NUM_B2C(fs, |
333 | ext2fs_group_blocks_count(fs, group)); | |
479463aa KM |
334 | } |
335 | ||
4a2924ea | 336 | bitmap = 0; |
3385a254 | 337 | if (EQ_CLSTR(i, super_blk) || |
d7cca6b0 | 338 | (old_desc_blk && old_desc_blocks && |
3385a254 TT |
339 | GE_CLSTR(i, old_desc_blk) && |
340 | LE_CLSTR(i, old_desc_blk + old_desc_blocks-1)) || | |
341 | (new_desc_blk && EQ_CLSTR(i, new_desc_blk)) || | |
342 | EQ_CLSTR(i, ext2fs_block_bitmap_loc(fs, group)) || | |
343 | EQ_CLSTR(i, ext2fs_inode_bitmap_loc(fs, group)) || | |
344 | (GE_CLSTR(i, ext2fs_inode_table_loc(fs, group)) && | |
345 | LE_CLSTR(i, (ext2fs_inode_table_loc(fs, group) + | |
346 | fs->inode_blocks_per_group - 1)))) { | |
4a2924ea | 347 | bitmap = 1; |
479463aa KM |
348 | actual = (actual != 0); |
349 | count++; | |
350 | cmp_block--; | |
3385a254 TT |
351 | } else if ((EXT2FS_B2C(fs, i) - count - |
352 | EXT2FS_B2C(fs, fs->super->s_first_data_block)) % | |
353 | fs->super->s_clusters_per_group == 0) { | |
479463aa KM |
354 | /* |
355 | * When the compare data blocks in block bitmap | |
356 | * are 0, count the free block, | |
357 | * skip the current block group. | |
358 | */ | |
c5d2f50d | 359 | if (ext2fs_test_block_bitmap_range2( |
3385a254 TT |
360 | ctx->block_found_map, |
361 | EXT2FS_B2C(fs, i), | |
479463aa KM |
362 | cmp_block)) { |
363 | /* | |
364 | * -1 means to skip the current block | |
365 | * group. | |
366 | */ | |
3385a254 | 367 | blocks = fs->super->s_clusters_per_group - 1; |
479463aa KM |
368 | group_free = cmp_block; |
369 | free_blocks += cmp_block; | |
370 | /* | |
371 | * The current block group's last block | |
372 | * is set to i. | |
373 | */ | |
3385a254 | 374 | i += EXT2FS_C2B(fs, cmp_block - 1); |
479463aa KM |
375 | bitmap = 1; |
376 | goto do_counts; | |
377 | } | |
378 | } | |
379 | } else if (redo_flag) | |
380 | bitmap = actual; | |
381 | else | |
c5d2f50d | 382 | bitmap = ext2fs_fast_test_block_bitmap2(fs->block_map, i); |
49e2df29 | 383 | |
e35ff9b9 | 384 | if (!actual == !bitmap) |
3839e657 | 385 | goto do_counts; |
f5fa2007 | 386 | |
3839e657 TT |
387 | if (!actual && bitmap) { |
388 | /* | |
389 | * Block not used, but marked in use in the bitmap. | |
390 | */ | |
f122632e | 391 | problem = PR_5_BLOCK_UNUSED; |
3839e657 TT |
392 | } else { |
393 | /* | |
394 | * Block used, but not marked in use in the bitmap. | |
395 | */ | |
1b6bf175 | 396 | problem = PR_5_BLOCK_USED; |
49a7360b JS |
397 | |
398 | if (skip_group) { | |
399 | struct problem_context pctx2; | |
400 | pctx2.blk = i; | |
401 | pctx2.group = group; | |
402 | if (fix_problem(ctx, PR_5_BLOCK_UNINIT,&pctx2)){ | |
e633b58a | 403 | ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); |
49a7360b JS |
404 | skip_group = 0; |
405 | } | |
406 | } | |
3839e657 | 407 | } |
f122632e TT |
408 | if (pctx.blk == NO_BLK) { |
409 | pctx.blk = pctx.blk2 = i; | |
410 | save_problem = problem; | |
411 | } else { | |
412 | if ((problem == save_problem) && | |
413 | (pctx.blk2 == i-1)) | |
414 | pctx.blk2++; | |
415 | else { | |
416 | print_bitmap_problem(ctx, save_problem, &pctx); | |
417 | pctx.blk = pctx.blk2 = i; | |
418 | save_problem = problem; | |
419 | } | |
420 | } | |
5596defa | 421 | ctx->flags |= E2F_FLAG_PROG_SUPPRESS; |
63c4969c | 422 | had_problem++; |
49e2df29 | 423 | |
efa1a355 LC |
424 | /* |
425 | * If there a problem we should turn off the discard so we | |
426 | * do not compromise the filesystem. | |
427 | */ | |
428 | ctx->options &= ~E2F_OPT_DISCARD; | |
429 | ||
3839e657 | 430 | do_counts: |
d2c9c42a | 431 | if (!bitmap) { |
3839e657 TT |
432 | group_free++; |
433 | free_blocks++; | |
efa1a355 LC |
434 | if (first_free > i) |
435 | first_free = i; | |
d2c9c42a TT |
436 | } else if (i > first_free) { |
437 | e2fsck_discard_blocks(ctx, first_free, | |
438 | (i - first_free)); | |
efa1a355 | 439 | first_free = ext2fs_blocks_count(fs->super); |
3839e657 TT |
440 | } |
441 | blocks ++; | |
44fe08f1 TT |
442 | if ((blocks == fs->super->s_clusters_per_group) || |
443 | (EXT2FS_B2C(fs, i) == | |
444 | EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super)-1))) { | |
deae60a0 LC |
445 | /* |
446 | * If the last block of this group is free, then we can | |
447 | * discard it as well. | |
448 | */ | |
449 | if (!bitmap && i >= first_free) | |
450 | e2fsck_discard_blocks(ctx, first_free, | |
451 | (i - first_free) + 1); | |
53e3120c | 452 | next_group: |
deae60a0 LC |
453 | first_free = ext2fs_blocks_count(fs->super); |
454 | ||
3839e657 TT |
455 | free_array[group] = group_free; |
456 | group ++; | |
457 | blocks = 0; | |
458 | group_free = 0; | |
f5fa2007 | 459 | skip_group = 0; |
efac9a1b TT |
460 | if (ctx->progress) |
461 | if ((ctx->progress)(ctx, 5, group, | |
462 | fs->group_desc_count*2)) | |
49e2df29 | 463 | goto errout; |
16b851cd | 464 | if (csum_flag && |
4efbac6f | 465 | (i != ext2fs_blocks_count(fs->super)-1) && |
cd65a24e | 466 | ext2fs_bg_flags_test(fs, group, |
732c8cd5 | 467 | EXT2_BG_BLOCK_UNINIT)) |
f5fa2007 | 468 | skip_group++; |
3839e657 TT |
469 | } |
470 | } | |
f122632e TT |
471 | if (pctx.blk != NO_BLK) |
472 | print_bitmap_problem(ctx, save_problem, &pctx); | |
63c4969c | 473 | if (had_problem) |
f122632e | 474 | fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP); |
63c4969c TT |
475 | else |
476 | fixit = -1; | |
5596defa | 477 | ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; |
49e2df29 | 478 | |
1b6bf175 TT |
479 | if (fixit == 1) { |
480 | ext2fs_free_block_bitmap(fs->block_map); | |
481 | retval = ext2fs_copy_bitmap(ctx->block_found_map, | |
482 | &fs->block_map); | |
bbd47d76 TT |
483 | if (retval) { |
484 | clear_problem_context(&pctx); | |
485 | fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx); | |
486 | ctx->flags |= E2F_FLAG_ABORT; | |
49e2df29 | 487 | goto errout; |
bbd47d76 | 488 | } |
1b6bf175 TT |
489 | ext2fs_set_bitmap_padding(fs->block_map); |
490 | ext2fs_mark_bb_dirty(fs); | |
49e2df29 | 491 | |
1b6bf175 TT |
492 | /* Redo the counts */ |
493 | blocks = 0; free_blocks = 0; group_free = 0; group = 0; | |
494 | memset(free_array, 0, fs->group_desc_count * sizeof(int)); | |
479463aa | 495 | redo_flag++; |
1b6bf175 TT |
496 | goto redo_counts; |
497 | } else if (fixit == 0) | |
498 | ext2fs_unmark_valid(fs); | |
499 | ||
3839e657 | 500 | for (i = 0; i < fs->group_desc_count; i++) { |
d7cca6b0 | 501 | if (free_array[i] != ext2fs_bg_free_blocks_count(fs, i)) { |
1b6bf175 | 502 | pctx.group = i; |
d7cca6b0 | 503 | pctx.blk = ext2fs_bg_free_blocks_count(fs, i); |
1b6bf175 TT |
504 | pctx.blk2 = free_array[i]; |
505 | ||
506 | if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP, | |
507 | &pctx)) { | |
e633b58a | 508 | ext2fs_bg_free_blocks_count_set(fs, i, free_array[i]); |
3839e657 TT |
509 | ext2fs_mark_super_dirty(fs); |
510 | } else | |
511 | ext2fs_unmark_valid(fs); | |
512 | } | |
513 | } | |
fe75afbf | 514 | free_blocks = EXT2FS_C2B(fs, free_blocks); |
4efbac6f | 515 | if (free_blocks != ext2fs_free_blocks_count(fs->super)) { |
1b6bf175 | 516 | pctx.group = 0; |
4efbac6f | 517 | pctx.blk = ext2fs_free_blocks_count(fs->super); |
1b6bf175 TT |
518 | pctx.blk2 = free_blocks; |
519 | ||
520 | if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) { | |
4efbac6f | 521 | ext2fs_free_blocks_count_set(fs->super, free_blocks); |
3839e657 | 522 | ext2fs_mark_super_dirty(fs); |
2788cc87 | 523 | } |
3839e657 | 524 | } |
49e2df29 | 525 | errout: |
c4e3d3f3 | 526 | ext2fs_free_mem(&free_array); |
53e3120c TT |
527 | ext2fs_free_mem(&actual_buf); |
528 | ext2fs_free_mem(&bitmap_buf); | |
3839e657 | 529 | } |
49e2df29 | 530 | |
1b6bf175 | 531 | static void check_inode_bitmaps(e2fsck_t ctx) |
3839e657 | 532 | { |
1b6bf175 | 533 | ext2_filsys fs = ctx->fs; |
86c627ec | 534 | ext2_ino_t i; |
54434927 TT |
535 | unsigned int free_inodes = 0; |
536 | int group_free = 0; | |
537 | int dirs_count = 0; | |
538 | int group = 0; | |
539 | unsigned int inodes = 0; | |
e64e6761 TT |
540 | ext2_ino_t *free_array; |
541 | ext2_ino_t *dir_array; | |
54434927 | 542 | int actual, bitmap; |
1b6bf175 TT |
543 | errcode_t retval; |
544 | struct problem_context pctx; | |
54434927 | 545 | int problem, save_problem, fixit, had_problem; |
16b851cd | 546 | int csum_flag; |
f5fa2007 | 547 | int skip_group = 0; |
479463aa | 548 | int redo_flag = 0; |
e64e6761 | 549 | ext2_ino_t first_free = fs->super->s_inodes_per_group + 1; |
49e2df29 | 550 | |
1b6bf175 | 551 | clear_problem_context(&pctx); |
e64e6761 TT |
552 | free_array = (ext2_ino_t *) e2fsck_allocate_memory(ctx, |
553 | fs->group_desc_count * sizeof(ext2_ino_t), "free inode count array"); | |
49e2df29 | 554 | |
e64e6761 TT |
555 | dir_array = (ext2_ino_t *) e2fsck_allocate_memory(ctx, |
556 | fs->group_desc_count * sizeof(ext2_ino_t), "directory count array"); | |
49e2df29 | 557 | |
c5d2f50d | 558 | if ((1 < ext2fs_get_inode_bitmap_start2(ctx->inode_used_map)) || |
49e2df29 | 559 | (fs->super->s_inodes_count > |
c5d2f50d | 560 | ext2fs_get_inode_bitmap_end2(ctx->inode_used_map))) { |
1b6bf175 TT |
561 | pctx.num = 3; |
562 | pctx.blk = 1; | |
563 | pctx.blk2 = fs->super->s_inodes_count; | |
c5d2f50d VAH |
564 | pctx.ino = ext2fs_get_inode_bitmap_start2(ctx->inode_used_map); |
565 | pctx.ino2 = ext2fs_get_inode_bitmap_end2(ctx->inode_used_map); | |
1b6bf175 | 566 | fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); |
08b21301 TT |
567 | |
568 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ | |
49e2df29 | 569 | goto errout; |
50e1e10f | 570 | } |
c5d2f50d | 571 | if ((1 < ext2fs_get_inode_bitmap_start2(fs->inode_map)) || |
49e2df29 | 572 | (fs->super->s_inodes_count > |
c5d2f50d | 573 | ext2fs_get_inode_bitmap_end2(fs->inode_map))) { |
1b6bf175 TT |
574 | pctx.num = 4; |
575 | pctx.blk = 1; | |
576 | pctx.blk2 = fs->super->s_inodes_count; | |
c5d2f50d VAH |
577 | pctx.ino = ext2fs_get_inode_bitmap_start2(fs->inode_map); |
578 | pctx.ino2 = ext2fs_get_inode_bitmap_end2(fs->inode_map); | |
1b6bf175 | 579 | fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx); |
08b21301 TT |
580 | |
581 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ | |
49e2df29 | 582 | goto errout; |
50e1e10f TT |
583 | } |
584 | ||
49a7360b JS |
585 | csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
586 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM); | |
1b6bf175 | 587 | redo_counts: |
63c4969c | 588 | had_problem = 0; |
f122632e TT |
589 | save_problem = 0; |
590 | pctx.ino = pctx.ino2 = 0; | |
16b851cd | 591 | if (csum_flag && |
cd65a24e | 592 | (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT))) |
f5fa2007 TT |
593 | skip_group++; |
594 | ||
5830d6be ES |
595 | /* Protect loop from wrap-around if inodes_count is maxed */ |
596 | for (i = 1; i <= fs->super->s_inodes_count && i > 0; i++) { | |
479463aa KM |
597 | bitmap = 0; |
598 | if (skip_group && | |
599 | i % fs->super->s_inodes_per_group == 1) { | |
600 | /* | |
601 | * Current inode is the first inode | |
602 | * in the current block group. | |
603 | */ | |
604 | if (ext2fs_test_inode_bitmap_range( | |
605 | ctx->inode_used_map, i, | |
606 | fs->super->s_inodes_per_group)) { | |
607 | /* | |
608 | * When the compared inodes in inodes bitmap | |
609 | * are 0, count the free inode, | |
610 | * skip the current block group. | |
611 | */ | |
57581c10 | 612 | first_free = 1; |
479463aa KM |
613 | inodes = fs->super->s_inodes_per_group - 1; |
614 | group_free = inodes; | |
615 | free_inodes += inodes; | |
616 | i += inodes; | |
617 | skip_group = 0; | |
618 | goto do_counts; | |
619 | } | |
620 | } | |
621 | ||
c5d2f50d | 622 | actual = ext2fs_fast_test_inode_bitmap2(ctx->inode_used_map, i); |
479463aa KM |
623 | if (redo_flag) |
624 | bitmap = actual; | |
625 | else if (!skip_group) | |
c5d2f50d | 626 | bitmap = ext2fs_fast_test_inode_bitmap2(fs->inode_map, i); |
e35ff9b9 | 627 | if (!actual == !bitmap) |
3839e657 | 628 | goto do_counts; |
49e2df29 | 629 | |
3839e657 TT |
630 | if (!actual && bitmap) { |
631 | /* | |
632 | * Inode wasn't used, but marked in bitmap | |
633 | */ | |
f122632e | 634 | problem = PR_5_INODE_UNUSED; |
1b6bf175 | 635 | } else /* if (actual && !bitmap) */ { |
3839e657 TT |
636 | /* |
637 | * Inode used, but not in bitmap | |
638 | */ | |
1b6bf175 | 639 | problem = PR_5_INODE_USED; |
49a7360b JS |
640 | |
641 | /* We should never hit this, because it means that | |
642 | * inodes were marked in use that weren't noticed | |
643 | * in pass1 or pass 2. It is easier to fix the problem | |
644 | * than to kill e2fsck and leave the user stuck. */ | |
645 | if (skip_group) { | |
646 | struct problem_context pctx2; | |
647 | pctx2.blk = i; | |
648 | pctx2.group = group; | |
649 | if (fix_problem(ctx, PR_5_INODE_UNINIT,&pctx2)){ | |
e633b58a | 650 | ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); |
49a7360b JS |
651 | skip_group = 0; |
652 | } | |
653 | } | |
3839e657 | 654 | } |
f122632e TT |
655 | if (pctx.ino == 0) { |
656 | pctx.ino = pctx.ino2 = i; | |
657 | save_problem = problem; | |
658 | } else { | |
659 | if ((problem == save_problem) && | |
660 | (pctx.ino2 == i-1)) | |
661 | pctx.ino2++; | |
662 | else { | |
663 | print_bitmap_problem(ctx, save_problem, &pctx); | |
664 | pctx.ino = pctx.ino2 = i; | |
665 | save_problem = problem; | |
666 | } | |
667 | } | |
5596defa | 668 | ctx->flags |= E2F_FLAG_PROG_SUPPRESS; |
63c4969c | 669 | had_problem++; |
efa1a355 LC |
670 | /* |
671 | * If there a problem we should turn off the discard so we | |
672 | * do not compromise the filesystem. | |
673 | */ | |
674 | ctx->options &= ~E2F_OPT_DISCARD; | |
49e2df29 | 675 | |
3839e657 | 676 | do_counts: |
57581c10 | 677 | inodes++; |
f5fa2007 | 678 | if (bitmap) { |
c5d2f50d | 679 | if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, i)) |
3839e657 | 680 | dirs_count++; |
57581c10 LC |
681 | if (inodes > first_free) { |
682 | e2fsck_discard_inodes(ctx, group, first_free, | |
683 | inodes - first_free); | |
684 | first_free = fs->super->s_inodes_per_group + 1; | |
685 | } | |
d2c9c42a | 686 | } else { |
f5fa2007 TT |
687 | group_free++; |
688 | free_inodes++; | |
57581c10 LC |
689 | if (first_free > inodes) |
690 | first_free = inodes; | |
3839e657 | 691 | } |
efa1a355 | 692 | |
3839e657 TT |
693 | if ((inodes == fs->super->s_inodes_per_group) || |
694 | (i == fs->super->s_inodes_count)) { | |
57581c10 LC |
695 | /* |
696 | * If the last inode is free, we can discard it as well. | |
697 | */ | |
698 | if (!bitmap && inodes >= first_free) | |
699 | e2fsck_discard_inodes(ctx, group, first_free, | |
700 | inodes - first_free + 1); | |
efa1a355 LC |
701 | /* |
702 | * If discard zeroes data and the group inode table | |
703 | * was not zeroed yet, set itable as zeroed | |
704 | */ | |
705 | if ((ctx->options & E2F_OPT_DISCARD) && | |
57581c10 | 706 | io_channel_discard_zeroes_data(fs->io) && |
efa1a355 | 707 | !(ext2fs_bg_flags_test(fs, group, |
57581c10 | 708 | EXT2_BG_INODE_ZEROED))) { |
efa1a355 LC |
709 | ext2fs_bg_flags_set(fs, group, |
710 | EXT2_BG_INODE_ZEROED); | |
711 | ext2fs_group_desc_csum_set(fs, group); | |
712 | } | |
713 | ||
57581c10 LC |
714 | first_free = fs->super->s_inodes_per_group + 1; |
715 | free_array[group] = group_free; | |
716 | dir_array[group] = dirs_count; | |
3839e657 TT |
717 | group ++; |
718 | inodes = 0; | |
f5fa2007 | 719 | skip_group = 0; |
3839e657 TT |
720 | group_free = 0; |
721 | dirs_count = 0; | |
efac9a1b TT |
722 | if (ctx->progress) |
723 | if ((ctx->progress)(ctx, 5, | |
724 | group + fs->group_desc_count, | |
725 | fs->group_desc_count*2)) | |
49e2df29 | 726 | goto errout; |
16b851cd | 727 | if (csum_flag && |
f5fa2007 | 728 | (i != fs->super->s_inodes_count) && |
cd65a24e | 729 | (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT) |
732c8cd5 | 730 | )) |
f5fa2007 | 731 | skip_group++; |
3839e657 TT |
732 | } |
733 | } | |
f122632e TT |
734 | if (pctx.ino) |
735 | print_bitmap_problem(ctx, save_problem, &pctx); | |
49e2df29 | 736 | |
63c4969c TT |
737 | if (had_problem) |
738 | fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP); | |
739 | else | |
740 | fixit = -1; | |
5596defa | 741 | ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS; |
49e2df29 | 742 | |
1b6bf175 TT |
743 | if (fixit == 1) { |
744 | ext2fs_free_inode_bitmap(fs->inode_map); | |
745 | retval = ext2fs_copy_bitmap(ctx->inode_used_map, | |
746 | &fs->inode_map); | |
bbd47d76 TT |
747 | if (retval) { |
748 | clear_problem_context(&pctx); | |
749 | fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx); | |
750 | ctx->flags |= E2F_FLAG_ABORT; | |
49e2df29 | 751 | goto errout; |
bbd47d76 | 752 | } |
1b6bf175 TT |
753 | ext2fs_set_bitmap_padding(fs->inode_map); |
754 | ext2fs_mark_ib_dirty(fs); | |
755 | ||
756 | /* redo counts */ | |
757 | inodes = 0; free_inodes = 0; group_free = 0; | |
758 | dirs_count = 0; group = 0; | |
759 | memset(free_array, 0, fs->group_desc_count * sizeof(int)); | |
760 | memset(dir_array, 0, fs->group_desc_count * sizeof(int)); | |
479463aa | 761 | redo_flag++; |
1b6bf175 TT |
762 | goto redo_counts; |
763 | } else if (fixit == 0) | |
764 | ext2fs_unmark_valid(fs); | |
49e2df29 | 765 | |
3839e657 | 766 | for (i = 0; i < fs->group_desc_count; i++) { |
d7cca6b0 | 767 | if (free_array[i] != ext2fs_bg_free_inodes_count(fs, i)) { |
1b6bf175 | 768 | pctx.group = i; |
d7cca6b0 | 769 | pctx.ino = ext2fs_bg_free_inodes_count(fs, i); |
1b6bf175 TT |
770 | pctx.ino2 = free_array[i]; |
771 | if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP, | |
772 | &pctx)) { | |
d7cca6b0 | 773 | ext2fs_bg_free_inodes_count_set(fs, i, free_array[i]); |
3839e657 TT |
774 | ext2fs_mark_super_dirty(fs); |
775 | } else | |
776 | ext2fs_unmark_valid(fs); | |
777 | } | |
d7cca6b0 | 778 | if (dir_array[i] != ext2fs_bg_used_dirs_count(fs, i)) { |
1b6bf175 | 779 | pctx.group = i; |
d7cca6b0 | 780 | pctx.ino = ext2fs_bg_used_dirs_count(fs, i); |
1b6bf175 TT |
781 | pctx.ino2 = dir_array[i]; |
782 | ||
783 | if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP, | |
784 | &pctx)) { | |
d7cca6b0 | 785 | ext2fs_bg_used_dirs_count_set(fs, i, dir_array[i]); |
3839e657 TT |
786 | ext2fs_mark_super_dirty(fs); |
787 | } else | |
788 | ext2fs_unmark_valid(fs); | |
789 | } | |
790 | } | |
791 | if (free_inodes != fs->super->s_free_inodes_count) { | |
1b6bf175 TT |
792 | pctx.group = -1; |
793 | pctx.ino = fs->super->s_free_inodes_count; | |
794 | pctx.ino2 = free_inodes; | |
795 | ||
796 | if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) { | |
3839e657 TT |
797 | fs->super->s_free_inodes_count = free_inodes; |
798 | ext2fs_mark_super_dirty(fs); | |
2788cc87 | 799 | } |
3839e657 | 800 | } |
49e2df29 | 801 | errout: |
c4e3d3f3 TT |
802 | ext2fs_free_mem(&free_array); |
803 | ext2fs_free_mem(&dir_array); | |
3839e657 TT |
804 | } |
805 | ||
1b6bf175 | 806 | static void check_inode_end(e2fsck_t ctx) |
3839e657 | 807 | { |
1b6bf175 | 808 | ext2_filsys fs = ctx->fs; |
86c627ec | 809 | ext2_ino_t end, save_inodes_count, i; |
1b6bf175 TT |
810 | struct problem_context pctx; |
811 | ||
812 | clear_problem_context(&pctx); | |
3839e657 TT |
813 | |
814 | end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; | |
1b6bf175 TT |
815 | pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end, |
816 | &save_inodes_count); | |
817 | if (pctx.errcode) { | |
818 | pctx.num = 1; | |
819 | fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); | |
08b21301 TT |
820 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ |
821 | return; | |
f3db3566 | 822 | } |
3839e657 TT |
823 | if (save_inodes_count == end) |
824 | return; | |
5830d6be | 825 | |
efc6f628 | 826 | /* protect loop from wrap-around if end is maxed */ |
5830d6be | 827 | for (i = save_inodes_count + 1; i <= end && i > save_inodes_count; i++) { |
f3db3566 | 828 | if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) { |
1b6bf175 | 829 | if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) { |
01ec1268 | 830 | for (; i <= end; i++) |
f3db3566 | 831 | ext2fs_mark_inode_bitmap(fs->inode_map, |
3839e657 TT |
832 | i); |
833 | ext2fs_mark_ib_dirty(fs); | |
834 | } else | |
835 | ext2fs_unmark_valid(fs); | |
836 | break; | |
837 | } | |
838 | } | |
839 | ||
1b6bf175 TT |
840 | pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, |
841 | save_inodes_count, 0); | |
842 | if (pctx.errcode) { | |
843 | pctx.num = 2; | |
844 | fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); | |
08b21301 TT |
845 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ |
846 | return; | |
f3db3566 | 847 | } |
3839e657 TT |
848 | } |
849 | ||
1b6bf175 | 850 | static void check_block_end(e2fsck_t ctx) |
3839e657 | 851 | { |
1b6bf175 | 852 | ext2_filsys fs = ctx->fs; |
c5d2f50d | 853 | blk64_t end, save_blocks_count, i; |
1b6bf175 TT |
854 | struct problem_context pctx; |
855 | ||
856 | clear_problem_context(&pctx); | |
3839e657 | 857 | |
c5d2f50d | 858 | end = ext2fs_get_block_bitmap_start2(fs->block_map) + |
44fe08f1 | 859 | ((blk64_t)EXT2_CLUSTERS_PER_GROUP(fs->super) * fs->group_desc_count) - 1; |
c5d2f50d | 860 | pctx.errcode = ext2fs_fudge_block_bitmap_end2(fs->block_map, end, |
1b6bf175 TT |
861 | &save_blocks_count); |
862 | if (pctx.errcode) { | |
863 | pctx.num = 3; | |
864 | fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); | |
08b21301 TT |
865 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ |
866 | return; | |
f3db3566 | 867 | } |
3839e657 TT |
868 | if (save_blocks_count == end) |
869 | return; | |
5830d6be | 870 | |
efc6f628 | 871 | /* Protect loop from wrap-around if end is maxed */ |
5830d6be | 872 | for (i = save_blocks_count + 1; i <= end && i > save_blocks_count; i++) { |
44fe08f1 TT |
873 | if (!ext2fs_test_block_bitmap2(fs->block_map, |
874 | EXT2FS_C2B(fs, i))) { | |
1b6bf175 | 875 | if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) { |
01ec1268 | 876 | for (; i <= end; i++) |
c5d2f50d | 877 | ext2fs_mark_block_bitmap2(fs->block_map, |
44fe08f1 | 878 | EXT2FS_C2B(fs, i)); |
3839e657 TT |
879 | ext2fs_mark_bb_dirty(fs); |
880 | } else | |
881 | ext2fs_unmark_valid(fs); | |
882 | break; | |
883 | } | |
884 | } | |
885 | ||
c5d2f50d | 886 | pctx.errcode = ext2fs_fudge_block_bitmap_end2(fs->block_map, |
1b6bf175 TT |
887 | save_blocks_count, 0); |
888 | if (pctx.errcode) { | |
889 | pctx.num = 4; | |
890 | fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx); | |
08b21301 TT |
891 | ctx->flags |= E2F_FLAG_ABORT; /* fatal */ |
892 | return; | |
f3db3566 | 893 | } |
3839e657 TT |
894 | } |
895 | ||
1b6bf175 TT |
896 | |
897 |