]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * pass2.c --- check directory structure | |
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 | * Pass 2 of e2fsck iterates through all active directory inodes, and |
12 | * applies to following tests to each directory entry in the directory | |
13 | * blocks in the inodes: | |
14 | * | |
15 | * - The length of the directory entry (rec_len) should be at | |
16 | * least 8 bytes, and no more than the remaining space | |
17 | * left in the directory block. | |
18 | * - The length of the name in the directory entry (name_len) | |
efc6f628 | 19 | * should be less than (rec_len - 8). |
3839e657 TT |
20 | * - The inode number in the directory entry should be within |
21 | * legal bounds. | |
22 | * - The inode number should refer to a in-use inode. | |
23 | * - The first entry should be '.', and its inode should be | |
24 | * the inode of the directory. | |
25 | * - The second entry should be '..'. | |
26 | * | |
27 | * To minimize disk seek time, the directory blocks are processed in | |
28 | * sorted order of block numbers. | |
29 | * | |
30 | * Pass 2 also collects the following information: | |
31 | * - The inode numbers of the subdirectories for each directory. | |
32 | * | |
33 | * Pass 2 relies on the following information from previous passes: | |
34 | * - The directory information collected in pass 1. | |
35 | * - The inode_used_map bitmap | |
36 | * - The inode_bad_map bitmap | |
37 | * - The inode_dir_map bitmap | |
3839e657 TT |
38 | * |
39 | * Pass 2 frees the following data structures | |
40 | * - The inode_bad_map bitmap | |
aa4115a4 | 41 | * - The inode_reg_map bitmap |
3839e657 TT |
42 | */ |
43 | ||
b969b1b8 | 44 | #define _GNU_SOURCE 1 /* get strnlen() */ |
48e6e813 TT |
45 | #include <string.h> |
46 | ||
3839e657 | 47 | #include "e2fsck.h" |
21c84b71 | 48 | #include "problem.h" |
0926668d | 49 | #include "dict.h" |
3839e657 | 50 | |
aa4115a4 TT |
51 | #ifdef NO_INLINE_FUNCS |
52 | #define _INLINE_ | |
53 | #else | |
54 | #define _INLINE_ inline | |
55 | #endif | |
56 | ||
ad4fa466 | 57 | /* #define DX_DEBUG */ |
8fdc9985 | 58 | |
3839e657 TT |
59 | /* |
60 | * Keeps track of how many times an inode is referenced. | |
61 | */ | |
4035f40b | 62 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf); |
3839e657 | 63 | static int check_dir_block(ext2_filsys fs, |
21c84b71 | 64 | struct ext2_db_entry *dir_blocks_info, |
54dc7ca2 | 65 | void *priv_data); |
1b6bf175 | 66 | static int allocate_dir_block(e2fsck_t ctx, |
21c84b71 TT |
67 | struct ext2_db_entry *dir_blocks_info, |
68 | char *buf, struct problem_context *pctx); | |
50e1e10f TT |
69 | static int update_dir_block(ext2_filsys fs, |
70 | blk_t *block_nr, | |
133a56dc | 71 | e2_blkcnt_t blockcnt, |
4035f40b | 72 | blk_t ref_block, |
efc6f628 | 73 | int ref_offset, |
4035f40b | 74 | void *priv_data); |
8fdc9985 | 75 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); |
ad4fa466 TT |
76 | static int htree_depth(struct dx_dir_info *dx_dir, |
77 | struct dx_dirblock_info *dx_db); | |
ea1959f0 | 78 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b); |
3839e657 | 79 | |
21c84b71 TT |
80 | struct check_dir_struct { |
81 | char *buf; | |
82 | struct problem_context pctx; | |
f8188fff | 83 | int count, max; |
1b6bf175 | 84 | e2fsck_t ctx; |
efc6f628 | 85 | }; |
21c84b71 | 86 | |
08b21301 | 87 | void e2fsck_pass2(e2fsck_t ctx) |
3839e657 | 88 | { |
a4742691 TT |
89 | struct ext2_super_block *sb = ctx->fs->super; |
90 | struct problem_context pctx; | |
91 | ext2_filsys fs = ctx->fs; | |
92 | char *buf; | |
8bf191e8 | 93 | #ifdef RESOURCE_TRACK |
3839e657 | 94 | struct resource_track rtrack; |
8bf191e8 | 95 | #endif |
21c84b71 | 96 | struct check_dir_struct cd; |
8fdc9985 TT |
97 | struct dx_dir_info *dx_dir; |
98 | struct dx_dirblock_info *dx_db, *dx_parent; | |
54434927 | 99 | int b; |
ad4fa466 | 100 | int i, depth; |
8fdc9985 TT |
101 | problem_t code; |
102 | int bad_dir; | |
103 | ||
8bf191e8 | 104 | #ifdef RESOURCE_TRACK |
6d96b00d | 105 | init_resource_track(&rtrack, ctx->fs->io); |
8bf191e8 | 106 | #endif |
3839e657 | 107 | |
1b6bf175 TT |
108 | clear_problem_context(&cd.pctx); |
109 | ||
3839e657 TT |
110 | #ifdef MTRACE |
111 | mtrace_print("Pass 2"); | |
112 | #endif | |
113 | ||
1b6bf175 TT |
114 | if (!(ctx->options & E2F_OPT_PREEN)) |
115 | fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); | |
116 | ||
efc6f628 | 117 | e2fsck_setup_tdb_icount(ctx, EXT2_ICOUNT_OPT_INCREMENT, |
34b9f796 TT |
118 | &ctx->inode_count); |
119 | if (ctx->inode_count) | |
120 | cd.pctx.errcode = 0; | |
efc6f628 TT |
121 | else |
122 | cd.pctx.errcode = ext2fs_create_icount2(fs, | |
34b9f796 | 123 | EXT2_ICOUNT_OPT_INCREMENT, |
1b6bf175 TT |
124 | 0, ctx->inode_link_info, |
125 | &ctx->inode_count); | |
126 | if (cd.pctx.errcode) { | |
127 | fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); | |
08b21301 TT |
128 | ctx->flags |= E2F_FLAG_ABORT; |
129 | return; | |
21c84b71 | 130 | } |
bcf9c5d4 | 131 | buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, |
54dc7ca2 | 132 | "directory scan buffer"); |
3839e657 | 133 | |
21c84b71 TT |
134 | /* |
135 | * Set up the parent pointer for the root directory, if | |
136 | * present. (If the root directory is not present, we will | |
137 | * create it in pass 3.) | |
138 | */ | |
28db82a8 | 139 | (void) e2fsck_dir_info_set_parent(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); |
21c84b71 TT |
140 | |
141 | cd.buf = buf; | |
1b6bf175 | 142 | cd.ctx = ctx; |
f75c28de | 143 | cd.count = 1; |
f8188fff | 144 | cd.max = ext2fs_dblist_count(fs->dblist); |
f75c28de TT |
145 | |
146 | if (ctx->progress) | |
147 | (void) (ctx->progress)(ctx, 2, 0, cd.max); | |
ea1959f0 TT |
148 | |
149 | if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) | |
150 | ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp); | |
efc6f628 | 151 | |
1b6bf175 TT |
152 | cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, |
153 | &cd); | |
49a7360b | 154 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) |
08b21301 | 155 | return; |
6267ee49 AD |
156 | |
157 | if (ctx->flags & E2F_FLAG_RESTART_LATER) { | |
158 | ctx->flags |= E2F_FLAG_RESTART; | |
159 | return; | |
160 | } | |
161 | ||
1b6bf175 TT |
162 | if (cd.pctx.errcode) { |
163 | fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); | |
08b21301 TT |
164 | ctx->flags |= E2F_FLAG_ABORT; |
165 | return; | |
7ac02a5e | 166 | } |
8fdc9985 TT |
167 | |
168 | #ifdef ENABLE_HTREE | |
169 | for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { | |
4cae0452 TT |
170 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
171 | return; | |
62acaa1d | 172 | if (dx_dir->numblocks == 0) |
8fdc9985 TT |
173 | continue; |
174 | clear_problem_context(&pctx); | |
175 | bad_dir = 0; | |
176 | pctx.dir = dx_dir->ino; | |
177 | dx_db = dx_dir->dx_block; | |
178 | if (dx_db->flags & DX_FLAG_REFERENCED) | |
179 | dx_db->flags |= DX_FLAG_DUP_REF; | |
180 | else | |
181 | dx_db->flags |= DX_FLAG_REFERENCED; | |
182 | /* | |
183 | * Find all of the first and last leaf blocks, and | |
184 | * update their parent's min and max hash values | |
185 | */ | |
186 | for (b=0, dx_db = dx_dir->dx_block; | |
187 | b < dx_dir->numblocks; | |
188 | b++, dx_db++) { | |
189 | if ((dx_db->type != DX_DIRBLOCK_LEAF) || | |
190 | !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST))) | |
191 | continue; | |
192 | dx_parent = &dx_dir->dx_block[dx_db->parent]; | |
193 | /* | |
194 | * XXX Make sure dx_parent->min_hash > dx_db->min_hash | |
195 | */ | |
196 | if (dx_db->flags & DX_FLAG_FIRST) | |
197 | dx_parent->min_hash = dx_db->min_hash; | |
198 | /* | |
199 | * XXX Make sure dx_parent->max_hash < dx_db->max_hash | |
200 | */ | |
201 | if (dx_db->flags & DX_FLAG_LAST) | |
202 | dx_parent->max_hash = dx_db->max_hash; | |
203 | } | |
efc6f628 | 204 | |
8fdc9985 TT |
205 | for (b=0, dx_db = dx_dir->dx_block; |
206 | b < dx_dir->numblocks; | |
207 | b++, dx_db++) { | |
208 | pctx.blkcount = b; | |
209 | pctx.group = dx_db->parent; | |
210 | code = 0; | |
211 | if (!(dx_db->flags & DX_FLAG_FIRST) && | |
212 | (dx_db->min_hash < dx_db->node_min_hash)) { | |
213 | pctx.blk = dx_db->min_hash; | |
214 | pctx.blk2 = dx_db->node_min_hash; | |
215 | code = PR_2_HTREE_MIN_HASH; | |
216 | fix_problem(ctx, code, &pctx); | |
217 | bad_dir++; | |
218 | } | |
ad4fa466 TT |
219 | if (dx_db->type == DX_DIRBLOCK_LEAF) { |
220 | depth = htree_depth(dx_dir, dx_db); | |
221 | if (depth != dx_dir->depth) { | |
e5e12db9 | 222 | pctx.num = dx_dir->depth; |
ad4fa466 TT |
223 | code = PR_2_HTREE_BAD_DEPTH; |
224 | fix_problem(ctx, code, &pctx); | |
225 | bad_dir++; | |
226 | } | |
227 | } | |
8fdc9985 | 228 | /* |
efc6f628 | 229 | * This test doesn't apply for the root block |
8fdc9985 TT |
230 | * at block #0 |
231 | */ | |
232 | if (b && | |
233 | (dx_db->max_hash > dx_db->node_max_hash)) { | |
234 | pctx.blk = dx_db->max_hash; | |
235 | pctx.blk2 = dx_db->node_max_hash; | |
236 | code = PR_2_HTREE_MAX_HASH; | |
237 | fix_problem(ctx, code, &pctx); | |
503f9e7f | 238 | bad_dir++; |
8fdc9985 TT |
239 | } |
240 | if (!(dx_db->flags & DX_FLAG_REFERENCED)) { | |
241 | code = PR_2_HTREE_NOTREF; | |
242 | fix_problem(ctx, code, &pctx); | |
243 | bad_dir++; | |
244 | } else if (dx_db->flags & DX_FLAG_DUP_REF) { | |
245 | code = PR_2_HTREE_DUPREF; | |
246 | fix_problem(ctx, code, &pctx); | |
247 | bad_dir++; | |
248 | } | |
249 | if (code == 0) | |
250 | continue; | |
251 | } | |
252 | if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { | |
253 | clear_htree(ctx, dx_dir->ino); | |
62acaa1d | 254 | dx_dir->numblocks = 0; |
8fdc9985 | 255 | } |
8fdc9985 TT |
256 | } |
257 | #endif | |
c4e3d3f3 | 258 | ext2fs_free_mem(&buf); |
21c84b71 TT |
259 | ext2fs_free_dblist(fs->dblist); |
260 | ||
1b6bf175 TT |
261 | if (ctx->inode_bad_map) { |
262 | ext2fs_free_inode_bitmap(ctx->inode_bad_map); | |
263 | ctx->inode_bad_map = 0; | |
3839e657 | 264 | } |
aa4115a4 TT |
265 | if (ctx->inode_reg_map) { |
266 | ext2fs_free_inode_bitmap(ctx->inode_reg_map); | |
267 | ctx->inode_reg_map = 0; | |
268 | } | |
a4742691 TT |
269 | |
270 | clear_problem_context(&pctx); | |
271 | if (ctx->large_files) { | |
272 | if (!(sb->s_feature_ro_compat & | |
273 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && | |
274 | fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { | |
275 | sb->s_feature_ro_compat |= | |
276 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE; | |
0cfce7f7 | 277 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
a4742691 TT |
278 | ext2fs_mark_super_dirty(fs); |
279 | } | |
280 | if (sb->s_rev_level == EXT2_GOOD_OLD_REV && | |
281 | fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { | |
282 | ext2fs_update_dynamic_rev(fs); | |
283 | ext2fs_mark_super_dirty(fs); | |
284 | } | |
a4742691 | 285 | } |
efc6f628 | 286 | |
8bf191e8 | 287 | #ifdef RESOURCE_TRACK |
5596defa TT |
288 | if (ctx->options & E2F_OPT_TIME2) { |
289 | e2fsck_clear_progbar(ctx); | |
6d96b00d | 290 | print_resource_track(_("Pass 2"), &rtrack, fs->io); |
5596defa | 291 | } |
8bf191e8 | 292 | #endif |
3839e657 TT |
293 | } |
294 | ||
ad4fa466 TT |
295 | #define MAX_DEPTH 32000 |
296 | static int htree_depth(struct dx_dir_info *dx_dir, | |
297 | struct dx_dirblock_info *dx_db) | |
298 | { | |
299 | int depth = 0; | |
300 | ||
301 | while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) { | |
302 | dx_db = &dx_dir->dx_block[dx_db->parent]; | |
303 | depth++; | |
304 | } | |
305 | return depth; | |
306 | } | |
307 | ||
0926668d TT |
308 | static int dict_de_cmp(const void *a, const void *b) |
309 | { | |
520ead37 | 310 | const struct ext2_dir_entry *de_a, *de_b; |
0926668d TT |
311 | int a_len, b_len; |
312 | ||
520ead37 | 313 | de_a = (const struct ext2_dir_entry *) a; |
0926668d | 314 | a_len = de_a->name_len & 0xFF; |
520ead37 | 315 | de_b = (const struct ext2_dir_entry *) b; |
0926668d TT |
316 | b_len = de_b->name_len & 0xFF; |
317 | ||
318 | if (a_len != b_len) | |
319 | return (a_len - b_len); | |
320 | ||
321 | return strncmp(de_a->name, de_b->name, a_len); | |
322 | } | |
ad4fa466 | 323 | |
ea1959f0 TT |
324 | /* |
325 | * This is special sort function that makes sure that directory blocks | |
326 | * with a dirblock of zero are sorted to the beginning of the list. | |
327 | * This guarantees that the root node of the htree directories are | |
328 | * processed first, so we know what hash version to use. | |
329 | */ | |
330 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) | |
331 | { | |
332 | const struct ext2_db_entry *db_a = | |
333 | (const struct ext2_db_entry *) a; | |
334 | const struct ext2_db_entry *db_b = | |
335 | (const struct ext2_db_entry *) b; | |
336 | ||
337 | if (db_a->blockcnt && !db_b->blockcnt) | |
338 | return 1; | |
339 | ||
340 | if (!db_a->blockcnt && db_b->blockcnt) | |
341 | return -1; | |
efc6f628 | 342 | |
ea1959f0 TT |
343 | if (db_a->blk != db_b->blk) |
344 | return (int) (db_a->blk - db_b->blk); | |
efc6f628 | 345 | |
ea1959f0 TT |
346 | if (db_a->ino != db_b->ino) |
347 | return (int) (db_a->ino - db_b->ino); | |
348 | ||
349 | return (int) (db_a->blockcnt - db_b->blockcnt); | |
350 | } | |
351 | ||
352 | ||
3839e657 TT |
353 | /* |
354 | * Make sure the first entry in the directory is '.', and that the | |
355 | * directory entry is sane. | |
356 | */ | |
1b6bf175 | 357 | static int check_dot(e2fsck_t ctx, |
3839e657 | 358 | struct ext2_dir_entry *dirent, |
86c627ec | 359 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 TT |
360 | { |
361 | struct ext2_dir_entry *nextdir; | |
362 | int status = 0; | |
363 | int created = 0; | |
5dd77dbe | 364 | int rec_len, new_len; |
21c84b71 | 365 | int problem = 0; |
efc6f628 | 366 | |
21c84b71 TT |
367 | if (!dirent->inode) |
368 | problem = PR_2_MISSING_DOT; | |
b6f79831 | 369 | else if (((dirent->name_len & 0xFF) != 1) || |
21c84b71 TT |
370 | (dirent->name[0] != '.')) |
371 | problem = PR_2_1ST_NOT_DOT; | |
372 | else if (dirent->name[1] != '\0') | |
373 | problem = PR_2_DOT_NULL_TERM; | |
5dd77dbe TT |
374 | |
375 | rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ? | |
376 | dirent->rec_len : 65536; | |
21c84b71 | 377 | if (problem) { |
1b6bf175 | 378 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe TT |
379 | if (rec_len < 12) |
380 | rec_len = dirent->rec_len = 12; | |
3839e657 TT |
381 | dirent->inode = ino; |
382 | dirent->name_len = 1; | |
383 | dirent->name[0] = '.'; | |
21c84b71 | 384 | dirent->name[1] = '\0'; |
3839e657 TT |
385 | status = 1; |
386 | created = 1; | |
3839e657 TT |
387 | } |
388 | } | |
3839e657 | 389 | if (dirent->inode != ino) { |
1b6bf175 | 390 | if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { |
3839e657 TT |
391 | dirent->inode = ino; |
392 | status = 1; | |
21c84b71 | 393 | } |
3839e657 | 394 | } |
5dd77dbe TT |
395 | if (rec_len > 12) { |
396 | new_len = rec_len - 12; | |
3839e657 | 397 | if (new_len > 12) { |
3839e657 | 398 | if (created || |
f8188fff | 399 | fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { |
3839e657 TT |
400 | nextdir = (struct ext2_dir_entry *) |
401 | ((char *) dirent + 12); | |
402 | dirent->rec_len = 12; | |
403 | nextdir->rec_len = new_len; | |
404 | nextdir->inode = 0; | |
405 | nextdir->name_len = 0; | |
406 | status = 1; | |
407 | } | |
408 | } | |
409 | } | |
410 | return status; | |
411 | } | |
412 | ||
413 | /* | |
414 | * Make sure the second entry in the directory is '..', and that the | |
415 | * directory entry is sane. We do not check the inode number of '..' | |
416 | * here; this gets done in pass 3. | |
417 | */ | |
1b6bf175 | 418 | static int check_dotdot(e2fsck_t ctx, |
3839e657 | 419 | struct ext2_dir_entry *dirent, |
28db82a8 | 420 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 | 421 | { |
5dd77dbe | 422 | int rec_len, problem = 0; |
efc6f628 | 423 | |
21c84b71 TT |
424 | if (!dirent->inode) |
425 | problem = PR_2_MISSING_DOT_DOT; | |
b6f79831 | 426 | else if (((dirent->name_len & 0xFF) != 2) || |
21c84b71 TT |
427 | (dirent->name[0] != '.') || |
428 | (dirent->name[1] != '.')) | |
429 | problem = PR_2_2ND_NOT_DOT_DOT; | |
430 | else if (dirent->name[2] != '\0') | |
431 | problem = PR_2_DOT_DOT_NULL_TERM; | |
432 | ||
5dd77dbe TT |
433 | rec_len = (dirent->rec_len || ctx->fs->blocksize < 65536) ? |
434 | dirent->rec_len : 65536; | |
21c84b71 | 435 | if (problem) { |
1b6bf175 | 436 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe | 437 | if (rec_len < 12) |
21c84b71 | 438 | dirent->rec_len = 12; |
3839e657 TT |
439 | /* |
440 | * Note: we don't have the parent inode just | |
441 | * yet, so we will fill it in with the root | |
442 | * inode. This will get fixed in pass 3. | |
443 | */ | |
444 | dirent->inode = EXT2_ROOT_INO; | |
445 | dirent->name_len = 2; | |
446 | dirent->name[0] = '.'; | |
447 | dirent->name[1] = '.'; | |
21c84b71 | 448 | dirent->name[2] = '\0'; |
3839e657 | 449 | return 1; |
efc6f628 | 450 | } |
3839e657 TT |
451 | return 0; |
452 | } | |
28db82a8 TT |
453 | if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) { |
454 | fix_problem(ctx, PR_2_NO_DIRINFO, pctx); | |
455 | return -1; | |
456 | } | |
3839e657 TT |
457 | return 0; |
458 | } | |
459 | ||
460 | /* | |
461 | * Check to make sure a directory entry doesn't contain any illegal | |
462 | * characters. | |
463 | */ | |
1b6bf175 | 464 | static int check_name(e2fsck_t ctx, |
3839e657 | 465 | struct ext2_dir_entry *dirent, |
efc6f628 | 466 | ext2_ino_t dir_ino EXT2FS_ATTR((unused)), |
54434927 | 467 | struct problem_context *pctx) |
3839e657 TT |
468 | { |
469 | int i; | |
470 | int fixup = -1; | |
3839e657 | 471 | int ret = 0; |
efc6f628 | 472 | |
b6f79831 | 473 | for ( i = 0; i < (dirent->name_len & 0xFF); i++) { |
3839e657 TT |
474 | if (dirent->name[i] == '/' || dirent->name[i] == '\0') { |
475 | if (fixup < 0) { | |
1b6bf175 | 476 | fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); |
3839e657 TT |
477 | } |
478 | if (fixup) { | |
479 | dirent->name[i] = '.'; | |
480 | ret = 1; | |
21c84b71 | 481 | } |
3839e657 TT |
482 | } |
483 | } | |
484 | return ret; | |
485 | } | |
486 | ||
aa4115a4 TT |
487 | /* |
488 | * Check the directory filetype (if present) | |
489 | */ | |
490 | static _INLINE_ int check_filetype(e2fsck_t ctx, | |
54434927 TT |
491 | struct ext2_dir_entry *dirent, |
492 | ext2_ino_t dir_ino EXT2FS_ATTR((unused)), | |
493 | struct problem_context *pctx) | |
aa4115a4 TT |
494 | { |
495 | int filetype = dirent->name_len >> 8; | |
496 | int should_be = EXT2_FT_UNKNOWN; | |
497 | struct ext2_inode inode; | |
498 | ||
499 | if (!(ctx->fs->super->s_feature_incompat & | |
7847c1d4 TT |
500 | EXT2_FEATURE_INCOMPAT_FILETYPE)) { |
501 | if (filetype == 0 || | |
502 | !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) | |
503 | return 0; | |
504 | dirent->name_len = dirent->name_len & 0xFF; | |
505 | return 1; | |
506 | } | |
aa4115a4 TT |
507 | |
508 | if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) { | |
509 | should_be = EXT2_FT_DIR; | |
510 | } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map, | |
511 | dirent->inode)) { | |
512 | should_be = EXT2_FT_REG_FILE; | |
513 | } else if (ctx->inode_bad_map && | |
514 | ext2fs_test_inode_bitmap(ctx->inode_bad_map, | |
515 | dirent->inode)) | |
516 | should_be = 0; | |
517 | else { | |
518 | e2fsck_read_inode(ctx, dirent->inode, &inode, | |
519 | "check_filetype"); | |
6fdc7a32 | 520 | should_be = ext2_file_type(inode.i_mode); |
aa4115a4 TT |
521 | } |
522 | if (filetype == should_be) | |
523 | return 0; | |
524 | pctx->num = should_be; | |
525 | ||
526 | if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, | |
527 | pctx) == 0) | |
528 | return 0; | |
efc6f628 | 529 | |
aa4115a4 TT |
530 | dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; |
531 | return 1; | |
532 | } | |
533 | ||
8fdc9985 TT |
534 | #ifdef ENABLE_HTREE |
535 | static void parse_int_node(ext2_filsys fs, | |
536 | struct ext2_db_entry *db, | |
537 | struct check_dir_struct *cd, | |
538 | struct dx_dir_info *dx_dir, | |
539 | char *block_buf) | |
540 | { | |
541 | struct ext2_dx_root_info *root; | |
542 | struct ext2_dx_entry *ent; | |
543 | struct ext2_dx_countlimit *limit; | |
544 | struct dx_dirblock_info *dx_db; | |
ad4fa466 | 545 | int i, expect_limit, count; |
8fdc9985 TT |
546 | blk_t blk; |
547 | ext2_dirhash_t min_hash = 0xffffffff; | |
548 | ext2_dirhash_t max_hash = 0; | |
ad4fa466 | 549 | ext2_dirhash_t hash = 0, prev_hash; |
8fdc9985 TT |
550 | |
551 | if (db->blockcnt == 0) { | |
552 | root = (struct ext2_dx_root_info *) (block_buf + 24); | |
efc6f628 | 553 | |
8fdc9985 TT |
554 | #ifdef DX_DEBUG |
555 | printf("Root node dump:\n"); | |
8deb80a5 | 556 | printf("\t Reserved zero: %u\n", root->reserved_zero); |
8fdc9985 TT |
557 | printf("\t Hash Version: %d\n", root->hash_version); |
558 | printf("\t Info length: %d\n", root->info_length); | |
559 | printf("\t Indirect levels: %d\n", root->indirect_levels); | |
560 | printf("\t Flags: %d\n", root->unused_flags); | |
561 | #endif | |
562 | ||
563 | ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); | |
564 | } else { | |
565 | ent = (struct ext2_dx_entry *) (block_buf+8); | |
566 | } | |
567 | limit = (struct ext2_dx_countlimit *) ent; | |
568 | ||
569 | #ifdef DX_DEBUG | |
efc6f628 | 570 | printf("Number of entries (count): %d\n", |
8132d840 | 571 | ext2fs_le16_to_cpu(limit->count)); |
efc6f628 | 572 | printf("Number of entries (limit): %d\n", |
8132d840 | 573 | ext2fs_le16_to_cpu(limit->limit)); |
8fdc9985 TT |
574 | #endif |
575 | ||
8132d840 | 576 | count = ext2fs_le16_to_cpu(limit->count); |
ad4fa466 TT |
577 | expect_limit = (fs->blocksize - ((char *) ent - block_buf)) / |
578 | sizeof(struct ext2_dx_entry); | |
8132d840 TT |
579 | if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { |
580 | cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); | |
ad4fa466 TT |
581 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) |
582 | goto clear_and_exit; | |
583 | } | |
8132d840 TT |
584 | if (count > expect_limit) { |
585 | cd->pctx.num = count; | |
ad4fa466 TT |
586 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx)) |
587 | goto clear_and_exit; | |
588 | count = expect_limit; | |
589 | } | |
efc6f628 | 590 | |
ad4fa466 TT |
591 | for (i=0; i < count; i++) { |
592 | prev_hash = hash; | |
8132d840 | 593 | hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0; |
8fdc9985 | 594 | #ifdef DX_DEBUG |
8deb80a5 | 595 | printf("Entry #%d: Hash 0x%08x, block %u\n", i, |
8132d840 | 596 | hash, ext2fs_le32_to_cpu(ent[i].block)); |
8fdc9985 | 597 | #endif |
8132d840 | 598 | blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff; |
8fdc9985 | 599 | /* Check to make sure the block is valid */ |
977ac873 | 600 | if (blk >= (blk_t) dx_dir->numblocks) { |
b7a00563 | 601 | cd->pctx.blk = blk; |
8fdc9985 | 602 | if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK, |
ad4fa466 TT |
603 | &cd->pctx)) |
604 | goto clear_and_exit; | |
977ac873 | 605 | continue; |
8fdc9985 | 606 | } |
ad4fa466 TT |
607 | if (hash < prev_hash && |
608 | fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx)) | |
609 | goto clear_and_exit; | |
8fdc9985 TT |
610 | dx_db = &dx_dir->dx_block[blk]; |
611 | if (dx_db->flags & DX_FLAG_REFERENCED) { | |
612 | dx_db->flags |= DX_FLAG_DUP_REF; | |
613 | } else { | |
614 | dx_db->flags |= DX_FLAG_REFERENCED; | |
615 | dx_db->parent = db->blockcnt; | |
616 | } | |
617 | if (hash < min_hash) | |
618 | min_hash = hash; | |
619 | if (hash > max_hash) | |
620 | max_hash = hash; | |
621 | dx_db->node_min_hash = hash; | |
8132d840 | 622 | if ((i+1) < count) |
efc6f628 | 623 | dx_db->node_max_hash = |
8132d840 | 624 | ext2fs_le32_to_cpu(ent[i+1].hash) & ~1; |
8fdc9985 TT |
625 | else { |
626 | dx_db->node_max_hash = 0xfffffffe; | |
627 | dx_db->flags |= DX_FLAG_LAST; | |
628 | } | |
629 | if (i == 0) | |
630 | dx_db->flags |= DX_FLAG_FIRST; | |
631 | } | |
632 | #ifdef DX_DEBUG | |
633 | printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n", | |
634 | db->blockcnt, min_hash, max_hash); | |
635 | #endif | |
636 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
637 | dx_db->min_hash = min_hash; | |
638 | dx_db->max_hash = max_hash; | |
ad4fa466 TT |
639 | return; |
640 | ||
641 | clear_and_exit: | |
642 | clear_htree(cd->ctx, cd->pctx.ino); | |
643 | dx_dir->numblocks = 0; | |
8fdc9985 TT |
644 | } |
645 | #endif /* ENABLE_HTREE */ | |
aa4115a4 | 646 | |
e70ae99e TT |
647 | /* |
648 | * Given a busted directory, try to salvage it somehow. | |
efc6f628 | 649 | * |
e70ae99e | 650 | */ |
ad4fa466 | 651 | static void salvage_directory(ext2_filsys fs, |
e70ae99e TT |
652 | struct ext2_dir_entry *dirent, |
653 | struct ext2_dir_entry *prev, | |
54434927 | 654 | unsigned int *offset) |
e70ae99e TT |
655 | { |
656 | char *cp = (char *) dirent; | |
5dd77dbe | 657 | int left, rec_len; |
642935c0 | 658 | unsigned int name_len = dirent->name_len & 0xFF; |
e70ae99e | 659 | |
5dd77dbe TT |
660 | rec_len = (dirent->rec_len || fs->blocksize < 65536) ? |
661 | dirent->rec_len : 65536; | |
662 | left = fs->blocksize - *offset - rec_len; | |
663 | ||
e70ae99e TT |
664 | /* |
665 | * Special case of directory entry of size 8: copy what's left | |
666 | * of the directory block up to cover up the invalid hole. | |
667 | */ | |
5dd77dbe | 668 | if ((left >= 12) && (rec_len == 8)) { |
e70ae99e TT |
669 | memmove(cp, cp+8, left); |
670 | memset(cp + left, 0, 8); | |
ad4fa466 TT |
671 | return; |
672 | } | |
673 | /* | |
674 | * If the directory entry overruns the end of the directory | |
675 | * block, and the name is small enough to fit, then adjust the | |
676 | * record length. | |
677 | */ | |
678 | if ((left < 0) && | |
5dd77dbe | 679 | (name_len + 8 <= rec_len + (unsigned) left) && |
ad4fa466 TT |
680 | dirent->inode <= fs->super->s_inodes_count && |
681 | strnlen(dirent->name, name_len) == name_len) { | |
682 | dirent->rec_len += left; | |
683 | return; | |
e70ae99e TT |
684 | } |
685 | /* | |
575307cc KS |
686 | * If the record length of the directory entry is a multiple |
687 | * of four, and not too big, such that it is valid, let the | |
688 | * previous directory entry absorb the invalid one. | |
e70ae99e | 689 | */ |
5dd77dbe TT |
690 | if (prev && rec_len && (rec_len % 4) == 0 && |
691 | (*offset + rec_len <= fs->blocksize)) { | |
692 | prev->rec_len += rec_len; | |
693 | *offset += rec_len; | |
ad4fa466 | 694 | return; |
e70ae99e TT |
695 | } |
696 | /* | |
697 | * Default salvage method --- kill all of the directory | |
698 | * entries for the rest of the block. We will either try to | |
699 | * absorb it into the previous directory entry, or create a | |
700 | * new empty directory entry the rest of the directory block. | |
701 | */ | |
702 | if (prev) { | |
ad4fa466 TT |
703 | prev->rec_len += fs->blocksize - *offset; |
704 | *offset = fs->blocksize; | |
e70ae99e | 705 | } else { |
ad4fa466 | 706 | dirent->rec_len = fs->blocksize - *offset; |
e70ae99e TT |
707 | dirent->name_len = 0; |
708 | dirent->inode = 0; | |
e70ae99e | 709 | } |
e70ae99e TT |
710 | } |
711 | ||
3839e657 | 712 | static int check_dir_block(ext2_filsys fs, |
21c84b71 | 713 | struct ext2_db_entry *db, |
54dc7ca2 | 714 | void *priv_data) |
3839e657 | 715 | { |
8fdc9985 TT |
716 | struct dx_dir_info *dx_dir; |
717 | #ifdef ENABLE_HTREE | |
718 | struct dx_dirblock_info *dx_db = 0; | |
719 | #endif /* ENABLE_HTREE */ | |
e70ae99e | 720 | struct ext2_dir_entry *dirent, *prev; |
8fdc9985 | 721 | ext2_dirhash_t hash; |
54434927 | 722 | unsigned int offset = 0; |
e94bc631 | 723 | const char * old_op; |
3839e657 | 724 | int dir_modified = 0; |
21c84b71 | 725 | int dot_state; |
03fa6f8a | 726 | unsigned int rec_len; |
3839e657 | 727 | blk_t block_nr = db->blk; |
86c627ec | 728 | ext2_ino_t ino = db->ino; |
28db82a8 | 729 | ext2_ino_t subdir_parent; |
21c84b71 | 730 | __u16 links; |
54dc7ca2 | 731 | struct check_dir_struct *cd; |
1b6bf175 TT |
732 | char *buf; |
733 | e2fsck_t ctx; | |
734 | int problem; | |
ea1959f0 | 735 | struct ext2_dx_root_info *root; |
e8254bfd | 736 | struct ext2_dx_countlimit *limit; |
0926668d TT |
737 | static dict_t de_dict; |
738 | struct problem_context pctx; | |
739 | int dups_found = 0; | |
28db82a8 | 740 | int ret; |
1b6bf175 | 741 | |
54dc7ca2 | 742 | cd = (struct check_dir_struct *) priv_data; |
1b6bf175 TT |
743 | buf = cd->buf; |
744 | ctx = cd->ctx; | |
f8188fff | 745 | |
49a7360b | 746 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) |
4cae0452 | 747 | return DIRENT_ABORT; |
efc6f628 | 748 | |
4cae0452 TT |
749 | if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) |
750 | return DIRENT_ABORT; | |
efc6f628 | 751 | |
3839e657 | 752 | /* |
efc6f628 | 753 | * Make sure the inode is still in use (could have been |
3839e657 TT |
754 | * deleted in the duplicate/bad blocks pass. |
755 | */ | |
efc6f628 | 756 | if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) |
3839e657 | 757 | return 0; |
50e1e10f | 758 | |
21c84b71 TT |
759 | cd->pctx.ino = ino; |
760 | cd->pctx.blk = block_nr; | |
761 | cd->pctx.blkcount = db->blockcnt; | |
762 | cd->pctx.ino2 = 0; | |
763 | cd->pctx.dirent = 0; | |
764 | cd->pctx.num = 0; | |
765 | ||
50e1e10f | 766 | if (db->blk == 0) { |
1b6bf175 | 767 | if (allocate_dir_block(ctx, db, buf, &cd->pctx)) |
50e1e10f TT |
768 | return 0; |
769 | block_nr = db->blk; | |
770 | } | |
efc6f628 | 771 | |
3839e657 TT |
772 | if (db->blockcnt) |
773 | dot_state = 2; | |
774 | else | |
775 | dot_state = 0; | |
776 | ||
0926668d TT |
777 | if (ctx->dirs_to_hash && |
778 | ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) | |
779 | dups_found++; | |
780 | ||
3839e657 | 781 | #if 0 |
f3db3566 | 782 | printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, |
3839e657 TT |
783 | db->blockcnt, ino); |
784 | #endif | |
efc6f628 | 785 | |
e94bc631 | 786 | old_op = ehandler_operation(_("reading directory block")); |
1b6bf175 | 787 | cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); |
e94bc631 | 788 | ehandler_operation(0); |
b9852cd8 TT |
789 | if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) |
790 | cd->pctx.errcode = 0; /* We'll handle this ourselves */ | |
1b6bf175 | 791 | if (cd->pctx.errcode) { |
08b21301 TT |
792 | if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { |
793 | ctx->flags |= E2F_FLAG_ABORT; | |
794 | return DIRENT_ABORT; | |
795 | } | |
1b6bf175 | 796 | memset(buf, 0, fs->blocksize); |
3839e657 | 797 | } |
8fdc9985 TT |
798 | #ifdef ENABLE_HTREE |
799 | dx_dir = e2fsck_get_dx_dir_info(ctx, ino); | |
62acaa1d | 800 | if (dx_dir && dx_dir->numblocks) { |
8fdc9985 | 801 | if (db->blockcnt >= dx_dir->numblocks) { |
efc6f628 | 802 | if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, |
d45edec0 TT |
803 | &pctx)) { |
804 | clear_htree(ctx, ino); | |
805 | dx_dir->numblocks = 0; | |
806 | dx_db = 0; | |
807 | goto out_htree; | |
808 | } | |
809 | fatal_error(ctx, _("Can not continue.")); | |
8fdc9985 TT |
810 | } |
811 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
812 | dx_db->type = DX_DIRBLOCK_LEAF; | |
813 | dx_db->phys = block_nr; | |
814 | dx_db->min_hash = ~0; | |
815 | dx_db->max_hash = 0; | |
efc6f628 | 816 | |
8fdc9985 | 817 | dirent = (struct ext2_dir_entry *) buf; |
5dd77dbe TT |
818 | rec_len = (dirent->rec_len || fs->blocksize < 65536) ? |
819 | dirent->rec_len : 65536; | |
e8254bfd | 820 | limit = (struct ext2_dx_countlimit *) (buf+8); |
8fdc9985 | 821 | if (db->blockcnt == 0) { |
ea1959f0 | 822 | root = (struct ext2_dx_root_info *) (buf + 24); |
8fdc9985 TT |
823 | dx_db->type = DX_DIRBLOCK_ROOT; |
824 | dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; | |
ea1959f0 TT |
825 | if ((root->reserved_zero || |
826 | root->info_length < 8 || | |
827 | root->indirect_levels > 1) && | |
828 | fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { | |
829 | clear_htree(ctx, ino); | |
830 | dx_dir->numblocks = 0; | |
831 | dx_db = 0; | |
f77704e4 | 832 | } |
ea1959f0 | 833 | dx_dir->hashversion = root->hash_version; |
f77704e4 TT |
834 | if ((dx_dir->hashversion <= EXT2_HASH_TEA) && |
835 | (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) | |
836 | dx_dir->hashversion += 3; | |
ad4fa466 | 837 | dx_dir->depth = root->indirect_levels + 1; |
8fdc9985 | 838 | } else if ((dirent->inode == 0) && |
5dd77dbe | 839 | (rec_len == fs->blocksize) && |
e8254bfd | 840 | (dirent->name_len == 0) && |
efc6f628 TT |
841 | (ext2fs_le16_to_cpu(limit->limit) == |
842 | ((fs->blocksize-8) / | |
8132d840 | 843 | sizeof(struct ext2_dx_entry)))) |
8fdc9985 TT |
844 | dx_db->type = DX_DIRBLOCK_NODE; |
845 | } | |
d45edec0 | 846 | out_htree: |
8fdc9985 | 847 | #endif /* ENABLE_HTREE */ |
3839e657 | 848 | |
0926668d | 849 | dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); |
e70ae99e | 850 | prev = 0; |
3839e657 | 851 | do { |
49a7360b JS |
852 | int group; |
853 | ext2_ino_t first_unused_inode; | |
854 | ||
1b6bf175 | 855 | problem = 0; |
3839e657 | 856 | dirent = (struct ext2_dir_entry *) (buf + offset); |
5dd77dbe TT |
857 | rec_len = (dirent->rec_len || fs->blocksize < 65536) ? |
858 | dirent->rec_len : 65536; | |
21c84b71 TT |
859 | cd->pctx.dirent = dirent; |
860 | cd->pctx.num = offset; | |
5dd77dbe TT |
861 | if (((offset + rec_len) > fs->blocksize) || |
862 | (rec_len < 12) || | |
863 | ((rec_len % 4) != 0) || | |
03fa6f8a | 864 | (((dirent->name_len & (unsigned) 0xFF)+8) > rec_len)) { |
1b6bf175 | 865 | if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { |
ad4fa466 | 866 | salvage_directory(fs, dirent, prev, &offset); |
3839e657 | 867 | dir_modified++; |
e70ae99e | 868 | continue; |
21c84b71 | 869 | } else |
0926668d | 870 | goto abort_free_dict; |
3839e657 | 871 | } |
b6f79831 | 872 | if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { |
1b6bf175 | 873 | if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { |
50e1e10f TT |
874 | dirent->name_len = EXT2_NAME_LEN; |
875 | dir_modified++; | |
876 | } | |
50e1e10f TT |
877 | } |
878 | ||
e70ae99e | 879 | if (dot_state == 0) { |
1b6bf175 | 880 | if (check_dot(ctx, dirent, ino, &cd->pctx)) |
3839e657 | 881 | dir_modified++; |
e70ae99e | 882 | } else if (dot_state == 1) { |
28db82a8 TT |
883 | ret = check_dotdot(ctx, dirent, ino, &cd->pctx); |
884 | if (ret < 0) | |
0926668d | 885 | goto abort_free_dict; |
28db82a8 | 886 | if (ret) |
3839e657 TT |
887 | dir_modified++; |
888 | } else if (dirent->inode == ino) { | |
1b6bf175 TT |
889 | problem = PR_2_LINK_DOT; |
890 | if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { | |
3839e657 TT |
891 | dirent->inode = 0; |
892 | dir_modified++; | |
21c84b71 | 893 | goto next; |
3839e657 TT |
894 | } |
895 | } | |
efc6f628 | 896 | if (!dirent->inode) |
3839e657 | 897 | goto next; |
efc6f628 | 898 | |
3839e657 TT |
899 | /* |
900 | * Make sure the inode listed is a legal one. | |
efc6f628 | 901 | */ |
3839e657 | 902 | if (((dirent->inode != EXT2_ROOT_INO) && |
7f88b043 | 903 | (dirent->inode < EXT2_FIRST_INODE(fs->super))) || |
3839e657 | 904 | (dirent->inode > fs->super->s_inodes_count)) { |
1b6bf175 | 905 | problem = PR_2_BAD_INO; |
1b6bf175 TT |
906 | } else if (ctx->inode_bb_map && |
907 | (ext2fs_test_inode_bitmap(ctx->inode_bb_map, | |
908 | dirent->inode))) { | |
909 | /* | |
910 | * If the inode is in a bad block, offer to | |
911 | * clear it. | |
912 | */ | |
913 | problem = PR_2_BB_INODE; | |
e70ae99e | 914 | } else if ((dot_state > 1) && |
b6f79831 | 915 | ((dirent->name_len & 0xFF) == 1) && |
1b6bf175 TT |
916 | (dirent->name[0] == '.')) { |
917 | /* | |
918 | * If there's a '.' entry in anything other | |
919 | * than the first directory entry, it's a | |
920 | * duplicate entry that should be removed. | |
921 | */ | |
922 | problem = PR_2_DUP_DOT; | |
e70ae99e | 923 | } else if ((dot_state > 1) && |
b6f79831 | 924 | ((dirent->name_len & 0xFF) == 2) && |
efc6f628 | 925 | (dirent->name[0] == '.') && |
1b6bf175 TT |
926 | (dirent->name[1] == '.')) { |
927 | /* | |
928 | * If there's a '..' entry in anything other | |
929 | * than the second directory entry, it's a | |
930 | * duplicate entry that should be removed. | |
931 | */ | |
932 | problem = PR_2_DUP_DOT_DOT; | |
e70ae99e | 933 | } else if ((dot_state > 1) && |
1b6bf175 TT |
934 | (dirent->inode == EXT2_ROOT_INO)) { |
935 | /* | |
936 | * Don't allow links to the root directory. | |
937 | * We check this specially to make sure we | |
938 | * catch this error case even if the root | |
939 | * directory hasn't been created yet. | |
940 | */ | |
941 | problem = PR_2_LINK_ROOT; | |
e70ae99e | 942 | } else if ((dot_state > 1) && |
c40db6d5 TT |
943 | (dirent->name_len & 0xFF) == 0) { |
944 | /* | |
945 | * Don't allow zero-length directory names. | |
946 | */ | |
947 | problem = PR_2_NULL_NAME; | |
21c84b71 TT |
948 | } |
949 | ||
1b6bf175 TT |
950 | if (problem) { |
951 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
21c84b71 TT |
952 | dirent->inode = 0; |
953 | dir_modified++; | |
954 | goto next; | |
1b6bf175 TT |
955 | } else { |
956 | ext2fs_unmark_valid(fs); | |
957 | if (problem == PR_2_BAD_INO) | |
958 | goto next; | |
21c84b71 | 959 | } |
3839e657 TT |
960 | } |
961 | ||
962 | /* | |
963 | * If the inode was marked as having bad fields in | |
964 | * pass1, process it and offer to fix/clear it. | |
965 | * (We wait until now so that we can display the | |
966 | * pathname to the user.) | |
967 | */ | |
1b6bf175 TT |
968 | if (ctx->inode_bad_map && |
969 | ext2fs_test_inode_bitmap(ctx->inode_bad_map, | |
3839e657 | 970 | dirent->inode)) { |
e72a9ba3 | 971 | if (e2fsck_process_bad_inode(ctx, ino, |
bcf9c5d4 TT |
972 | dirent->inode, |
973 | buf + fs->blocksize)) { | |
3839e657 TT |
974 | dirent->inode = 0; |
975 | dir_modified++; | |
976 | goto next; | |
977 | } | |
a02ce9df | 978 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 979 | return DIRENT_ABORT; |
3839e657 TT |
980 | } |
981 | ||
49a7360b JS |
982 | group = ext2fs_group_of_ino(fs, dirent->inode); |
983 | first_unused_inode = group * fs->super->s_inodes_per_group + | |
984 | 1 + fs->super->s_inodes_per_group - | |
985 | fs->group_desc[group].bg_itable_unused; | |
986 | cd->pctx.group = group; | |
987 | ||
988 | /* | |
42e89ce7 TT |
989 | * Check if the inode was missed out because |
990 | * _INODE_UNINIT flag was set or bg_itable_unused was | |
991 | * incorrect. If so, clear the _INODE_UNINIT flag and | |
992 | * restart e2fsck. In the future it would be nice if | |
993 | * we could call a function in pass1.c that checks the | |
994 | * newly visible inodes. | |
49a7360b JS |
995 | */ |
996 | if (fs->group_desc[group].bg_flags & EXT2_BG_INODE_UNINIT) { | |
6267ee49 | 997 | pctx.num = dirent->inode; |
49a7360b JS |
998 | if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, |
999 | &cd->pctx)){ | |
1000 | fs->group_desc[group].bg_flags &= | |
1001 | ~EXT2_BG_INODE_UNINIT; | |
42e89ce7 | 1002 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1003 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1004 | } else { |
1005 | ext2fs_unmark_valid(fs); | |
1006 | if (problem == PR_2_BAD_INO) | |
1007 | goto next; | |
1008 | } | |
1009 | } else if (dirent->inode >= first_unused_inode) { | |
6267ee49 | 1010 | pctx.num = dirent->inode; |
49a7360b JS |
1011 | if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ |
1012 | fs->group_desc[group].bg_itable_unused = 0; | |
49a7360b | 1013 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1014 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1015 | } else { |
1016 | ext2fs_unmark_valid(fs); | |
1017 | if (problem == PR_2_BAD_INO) | |
1018 | goto next; | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, | |
1023 | dirent->inode))) { | |
1024 | /* | |
1025 | * If the inode is unused, offer to clear it. | |
1026 | */ | |
1027 | problem = PR_2_UNUSED_INODE; | |
1028 | } | |
1029 | ||
1030 | if (problem) { | |
1031 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
1032 | dirent->inode = 0; | |
1033 | dir_modified++; | |
1034 | goto next; | |
1035 | } else { | |
1036 | ext2fs_unmark_valid(fs); | |
1037 | if (problem == PR_2_BAD_INO) | |
1038 | goto next; | |
1039 | } | |
1040 | } | |
1041 | ||
1b6bf175 TT |
1042 | if (check_name(ctx, dirent, ino, &cd->pctx)) |
1043 | dir_modified++; | |
1044 | ||
aa4115a4 TT |
1045 | if (check_filetype(ctx, dirent, ino, &cd->pctx)) |
1046 | dir_modified++; | |
1047 | ||
8fdc9985 TT |
1048 | #ifdef ENABLE_HTREE |
1049 | if (dx_db) { | |
1050 | ext2fs_dirhash(dx_dir->hashversion, dirent->name, | |
503f9e7f TT |
1051 | (dirent->name_len & 0xFF), |
1052 | fs->super->s_hash_seed, &hash, 0); | |
8fdc9985 TT |
1053 | if (hash < dx_db->min_hash) |
1054 | dx_db->min_hash = hash; | |
1055 | if (hash > dx_db->max_hash) | |
1056 | dx_db->max_hash = hash; | |
1057 | } | |
1058 | #endif | |
1059 | ||
3839e657 TT |
1060 | /* |
1061 | * If this is a directory, then mark its parent in its | |
1062 | * dir_info structure. If the parent field is already | |
1063 | * filled in, then this directory has more than one | |
1064 | * hard link. We assume the first link is correct, | |
1065 | * and ask the user if he/she wants to clear this one. | |
1066 | */ | |
e70ae99e | 1067 | if ((dot_state > 1) && |
1b6bf175 | 1068 | (ext2fs_test_inode_bitmap(ctx->inode_dir_map, |
3839e657 | 1069 | dirent->inode))) { |
28db82a8 TT |
1070 | if (e2fsck_dir_info_get_parent(ctx, dirent->inode, |
1071 | &subdir_parent)) { | |
1b6bf175 TT |
1072 | cd->pctx.ino = dirent->inode; |
1073 | fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); | |
0926668d | 1074 | goto abort_free_dict; |
3839e657 | 1075 | } |
28db82a8 TT |
1076 | if (subdir_parent) { |
1077 | cd->pctx.ino2 = subdir_parent; | |
1b6bf175 | 1078 | if (fix_problem(ctx, PR_2_LINK_DIR, |
21c84b71 | 1079 | &cd->pctx)) { |
3839e657 TT |
1080 | dirent->inode = 0; |
1081 | dir_modified++; | |
1082 | goto next; | |
21c84b71 TT |
1083 | } |
1084 | cd->pctx.ino2 = 0; | |
28db82a8 | 1085 | } else { |
efc6f628 | 1086 | (void) e2fsck_dir_info_set_parent(ctx, |
28db82a8 TT |
1087 | dirent->inode, ino); |
1088 | } | |
3839e657 | 1089 | } |
0926668d TT |
1090 | |
1091 | if (dups_found) { | |
1092 | ; | |
1093 | } else if (dict_lookup(&de_dict, dirent)) { | |
1094 | clear_problem_context(&pctx); | |
1095 | pctx.ino = ino; | |
1096 | pctx.dirent = dirent; | |
1097 | fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); | |
1098 | if (!ctx->dirs_to_hash) | |
1099 | ext2fs_u32_list_create(&ctx->dirs_to_hash, 50); | |
1100 | if (ctx->dirs_to_hash) | |
1101 | ext2fs_u32_list_add(ctx->dirs_to_hash, ino); | |
1102 | dups_found++; | |
1103 | } else | |
1104 | dict_alloc_insert(&de_dict, dirent, dirent); | |
efc6f628 | 1105 | |
1b6bf175 TT |
1106 | ext2fs_icount_increment(ctx->inode_count, dirent->inode, |
1107 | &links); | |
21c84b71 | 1108 | if (links > 1) |
1b6bf175 TT |
1109 | ctx->fs_links_count++; |
1110 | ctx->fs_total_count++; | |
3839e657 | 1111 | next: |
e70ae99e | 1112 | prev = dirent; |
5dd77dbe TT |
1113 | if (dir_modified) |
1114 | rec_len = (dirent->rec_len || fs->blocksize < 65536) ? | |
1115 | dirent->rec_len : 65536; | |
1116 | offset += rec_len; | |
e70ae99e | 1117 | dot_state++; |
3839e657 TT |
1118 | } while (offset < fs->blocksize); |
1119 | #if 0 | |
1120 | printf("\n"); | |
1121 | #endif | |
8fdc9985 TT |
1122 | #ifdef ENABLE_HTREE |
1123 | if (dx_db) { | |
1124 | #ifdef DX_DEBUG | |
1125 | printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", | |
1126 | db->blockcnt, dx_db->type, | |
1127 | dx_db->min_hash, dx_db->max_hash); | |
1128 | #endif | |
b7a00563 | 1129 | cd->pctx.dir = cd->pctx.ino; |
8fdc9985 TT |
1130 | if ((dx_db->type == DX_DIRBLOCK_ROOT) || |
1131 | (dx_db->type == DX_DIRBLOCK_NODE)) | |
1132 | parse_int_node(fs, db, cd, dx_dir, buf); | |
1133 | } | |
1134 | #endif /* ENABLE_HTREE */ | |
3839e657 | 1135 | if (offset != fs->blocksize) { |
5dd77dbe | 1136 | cd->pctx.num = rec_len - fs->blocksize + offset; |
1b6bf175 TT |
1137 | if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { |
1138 | dirent->rec_len = cd->pctx.num; | |
1139 | dir_modified++; | |
1140 | } | |
3839e657 TT |
1141 | } |
1142 | if (dir_modified) { | |
1b6bf175 TT |
1143 | cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); |
1144 | if (cd->pctx.errcode) { | |
08b21301 | 1145 | if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, |
0926668d TT |
1146 | &cd->pctx)) |
1147 | goto abort_free_dict; | |
3839e657 TT |
1148 | } |
1149 | ext2fs_mark_changed(fs); | |
1150 | } | |
0926668d | 1151 | dict_free_nodes(&de_dict); |
3839e657 | 1152 | return 0; |
0926668d | 1153 | abort_free_dict: |
0926668d | 1154 | ctx->flags |= E2F_FLAG_ABORT; |
49a7360b | 1155 | dict_free_nodes(&de_dict); |
0926668d | 1156 | return DIRENT_ABORT; |
3839e657 TT |
1157 | } |
1158 | ||
1159 | /* | |
1160 | * This function is called to deallocate a block, and is an interator | |
1161 | * functioned called by deallocate inode via ext2fs_iterate_block(). | |
1162 | */ | |
1163 | static int deallocate_inode_block(ext2_filsys fs, | |
133a56dc | 1164 | blk_t *block_nr, |
54434927 TT |
1165 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), |
1166 | blk_t ref_block EXT2FS_ATTR((unused)), | |
1167 | int ref_offset EXT2FS_ATTR((unused)), | |
133a56dc | 1168 | void *priv_data) |
3839e657 | 1169 | { |
54dc7ca2 | 1170 | e2fsck_t ctx = (e2fsck_t) priv_data; |
efc6f628 | 1171 | |
1917875f | 1172 | if (HOLE_BLKADDR(*block_nr)) |
3839e657 | 1173 | return 0; |
1ba7a2f2 TT |
1174 | if ((*block_nr < fs->super->s_first_data_block) || |
1175 | (*block_nr >= fs->super->s_blocks_count)) | |
1176 | return 0; | |
1b6bf175 | 1177 | ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); |
0684a4f3 | 1178 | ext2fs_block_alloc_stats(fs, *block_nr, -1); |
3839e657 TT |
1179 | return 0; |
1180 | } | |
efc6f628 | 1181 | |
3839e657 TT |
1182 | /* |
1183 | * This fuction deallocates an inode | |
1184 | */ | |
4035f40b | 1185 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) |
3839e657 | 1186 | { |
1b6bf175 | 1187 | ext2_filsys fs = ctx->fs; |
3839e657 | 1188 | struct ext2_inode inode; |
1b6bf175 | 1189 | struct problem_context pctx; |
0684a4f3 | 1190 | __u32 count; |
efc6f628 | 1191 | |
08b21301 | 1192 | e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); |
e3df15ab | 1193 | e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); |
1b6bf175 TT |
1194 | clear_problem_context(&pctx); |
1195 | pctx.ino = ino; | |
f3db3566 | 1196 | |
3839e657 TT |
1197 | /* |
1198 | * Fix up the bitmaps... | |
1199 | */ | |
f8188fff | 1200 | e2fsck_read_bitmaps(ctx); |
0684a4f3 TT |
1201 | ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); |
1202 | ||
1203 | if (inode.i_file_acl && | |
1204 | (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { | |
1205 | pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, | |
1206 | block_buf, -1, &count); | |
1207 | if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { | |
1208 | pctx.errcode = 0; | |
1209 | count = 1; | |
1210 | } | |
1211 | if (pctx.errcode) { | |
1212 | pctx.blk = inode.i_file_acl; | |
1213 | fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); | |
1214 | ctx->flags |= E2F_FLAG_ABORT; | |
1215 | return; | |
1216 | } | |
1217 | if (count == 0) { | |
1218 | ext2fs_unmark_block_bitmap(ctx->block_found_map, | |
1219 | inode.i_file_acl); | |
1220 | ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1); | |
1221 | } | |
1222 | inode.i_file_acl = 0; | |
1223 | } | |
3839e657 | 1224 | |
21c84b71 | 1225 | if (!ext2fs_inode_has_valid_blocks(&inode)) |
3839e657 | 1226 | return; |
a4742691 | 1227 | |
b94a052a | 1228 | if (LINUX_S_ISREG(inode.i_mode) && |
a4742691 TT |
1229 | (inode.i_size_high || inode.i_size & 0x80000000UL)) |
1230 | ctx->large_files--; | |
1231 | ||
133a56dc | 1232 | pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, |
1b6bf175 TT |
1233 | deallocate_inode_block, ctx); |
1234 | if (pctx.errcode) { | |
1235 | fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); | |
08b21301 TT |
1236 | ctx->flags |= E2F_FLAG_ABORT; |
1237 | return; | |
1b6bf175 | 1238 | } |
3839e657 TT |
1239 | } |
1240 | ||
8fdc9985 TT |
1241 | /* |
1242 | * This fuction clears the htree flag on an inode | |
1243 | */ | |
1244 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) | |
1245 | { | |
1246 | struct ext2_inode inode; | |
efc6f628 | 1247 | |
8fdc9985 TT |
1248 | e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); |
1249 | inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; | |
1250 | e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); | |
b7a00563 TT |
1251 | if (ctx->dirs_to_hash) |
1252 | ext2fs_u32_list_add(ctx->dirs_to_hash, ino); | |
8fdc9985 TT |
1253 | } |
1254 | ||
1255 | ||
86c627ec | 1256 | extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, |
bcf9c5d4 | 1257 | ext2_ino_t ino, char *buf) |
3839e657 | 1258 | { |
1b6bf175 | 1259 | ext2_filsys fs = ctx->fs; |
3839e657 | 1260 | struct ext2_inode inode; |
3839e657 | 1261 | int inode_modified = 0; |
6c313fd4 | 1262 | int not_fixed = 0; |
1e3472c5 | 1263 | unsigned char *frag, *fsize; |
21c84b71 | 1264 | struct problem_context pctx; |
08b21301 | 1265 | int problem = 0; |
3839e657 | 1266 | |
08b21301 | 1267 | e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); |
21c84b71 TT |
1268 | |
1269 | clear_problem_context(&pctx); | |
1270 | pctx.ino = ino; | |
1271 | pctx.dir = dir; | |
1272 | pctx.inode = &inode; | |
1273 | ||
6c313fd4 | 1274 | if (inode.i_file_acl && |
f76344fb TT |
1275 | !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { |
1276 | if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { | |
1277 | inode.i_file_acl = 0; | |
f76344fb TT |
1278 | inode_modified++; |
1279 | } else | |
1280 | not_fixed++; | |
1281 | } | |
6c313fd4 | 1282 | |
50e1e10f TT |
1283 | if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && |
1284 | !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && | |
1285 | !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && | |
08b21301 TT |
1286 | !(LINUX_S_ISSOCK(inode.i_mode))) |
1287 | problem = PR_2_BAD_MODE; | |
fdbdea09 | 1288 | else if (LINUX_S_ISCHR(inode.i_mode) |
0684a4f3 | 1289 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 1290 | problem = PR_2_BAD_CHAR_DEV; |
fdbdea09 | 1291 | else if (LINUX_S_ISBLK(inode.i_mode) |
0684a4f3 | 1292 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 1293 | problem = PR_2_BAD_BLOCK_DEV; |
fdbdea09 | 1294 | else if (LINUX_S_ISFIFO(inode.i_mode) |
0684a4f3 | 1295 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 1296 | problem = PR_2_BAD_FIFO; |
fdbdea09 | 1297 | else if (LINUX_S_ISSOCK(inode.i_mode) |
0684a4f3 | 1298 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 1299 | problem = PR_2_BAD_SOCKET; |
fdbdea09 | 1300 | else if (LINUX_S_ISLNK(inode.i_mode) |
7cadc577 | 1301 | && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) { |
bcf9c5d4 | 1302 | problem = PR_2_INVALID_SYMLINK; |
67052a8a | 1303 | } |
1dde43f0 | 1304 | |
08b21301 TT |
1305 | if (problem) { |
1306 | if (fix_problem(ctx, problem, &pctx)) { | |
1b6bf175 | 1307 | deallocate_inode(ctx, ino, 0); |
a02ce9df | 1308 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 1309 | return 0; |
7cf73dcd | 1310 | return 1; |
6c313fd4 TT |
1311 | } else |
1312 | not_fixed++; | |
08b21301 | 1313 | problem = 0; |
7cf73dcd | 1314 | } |
efc6f628 | 1315 | |
6c313fd4 TT |
1316 | if (inode.i_faddr) { |
1317 | if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { | |
1318 | inode.i_faddr = 0; | |
1319 | inode_modified++; | |
1320 | } else | |
1321 | not_fixed++; | |
3839e657 | 1322 | } |
1e3472c5 TT |
1323 | |
1324 | switch (fs->super->s_creator_os) { | |
1e3472c5 TT |
1325 | case EXT2_OS_HURD: |
1326 | frag = &inode.osd2.hurd2.h_i_frag; | |
1327 | fsize = &inode.osd2.hurd2.h_i_fsize; | |
1328 | break; | |
1e3472c5 TT |
1329 | default: |
1330 | frag = fsize = 0; | |
1331 | } | |
21c84b71 TT |
1332 | if (frag && *frag) { |
1333 | pctx.num = *frag; | |
1b6bf175 | 1334 | if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { |
21c84b71 TT |
1335 | *frag = 0; |
1336 | inode_modified++; | |
7e0282c5 TT |
1337 | } else |
1338 | not_fixed++; | |
21c84b71 TT |
1339 | pctx.num = 0; |
1340 | } | |
1341 | if (fsize && *fsize) { | |
1342 | pctx.num = *fsize; | |
1b6bf175 | 1343 | if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { |
21c84b71 TT |
1344 | *fsize = 0; |
1345 | inode_modified++; | |
6c313fd4 TT |
1346 | } else |
1347 | not_fixed++; | |
21c84b71 TT |
1348 | pctx.num = 0; |
1349 | } | |
1350 | ||
5d17119d | 1351 | if ((fs->super->s_creator_os == EXT2_OS_LINUX) && |
efc6f628 | 1352 | !(fs->super->s_feature_ro_compat & |
5d17119d TT |
1353 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && |
1354 | (inode.osd2.linux2.l_i_blocks_hi != 0)) { | |
1355 | pctx.num = inode.osd2.linux2.l_i_blocks_hi; | |
1356 | if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) { | |
1357 | inode.osd2.linux2.l_i_blocks_hi = 0; | |
1358 | inode_modified++; | |
1359 | } | |
1360 | } | |
1361 | ||
911ec626 TT |
1362 | if (!(fs->super->s_feature_incompat & |
1363 | EXT4_FEATURE_INCOMPAT_64BIT) && | |
1364 | inode.osd2.linux2.l_i_file_acl_high != 0) { | |
1365 | pctx.num = inode.osd2.linux2.l_i_file_acl_high; | |
1366 | if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) { | |
1367 | inode.osd2.linux2.l_i_file_acl_high = 0; | |
1368 | inode_modified++; | |
1369 | } else | |
1370 | not_fixed++; | |
1371 | } | |
1372 | ||
342d847d TT |
1373 | if (inode.i_file_acl && |
1374 | ((inode.i_file_acl < fs->super->s_first_data_block) || | |
6c313fd4 TT |
1375 | (inode.i_file_acl >= fs->super->s_blocks_count))) { |
1376 | if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { | |
1377 | inode.i_file_acl = 0; | |
1378 | inode_modified++; | |
1379 | } else | |
1380 | not_fixed++; | |
342d847d | 1381 | } |
21c84b71 | 1382 | if (inode.i_dir_acl && |
6c313fd4 TT |
1383 | LINUX_S_ISDIR(inode.i_mode)) { |
1384 | if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { | |
1385 | inode.i_dir_acl = 0; | |
1386 | inode_modified++; | |
1387 | } else | |
1388 | not_fixed++; | |
21c84b71 | 1389 | } |
6c313fd4 | 1390 | |
f3db3566 | 1391 | if (inode_modified) |
08b21301 | 1392 | e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); |
f76344fb | 1393 | if (!not_fixed && ctx->inode_bad_map) |
6c313fd4 | 1394 | ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); |
3839e657 TT |
1395 | return 0; |
1396 | } | |
1397 | ||
50e1e10f TT |
1398 | |
1399 | /* | |
1400 | * allocate_dir_block --- this function allocates a new directory | |
1401 | * block for a particular inode; this is done if a directory has | |
1402 | * a "hole" in it, or if a directory has a illegal block number | |
1403 | * that was zeroed out and now needs to be replaced. | |
1404 | */ | |
1b6bf175 | 1405 | static int allocate_dir_block(e2fsck_t ctx, |
21c84b71 | 1406 | struct ext2_db_entry *db, |
efc6f628 | 1407 | char *buf EXT2FS_ATTR((unused)), |
54434927 | 1408 | struct problem_context *pctx) |
50e1e10f | 1409 | { |
1b6bf175 | 1410 | ext2_filsys fs = ctx->fs; |
50e1e10f TT |
1411 | blk_t blk; |
1412 | char *block; | |
1413 | struct ext2_inode inode; | |
50e1e10f | 1414 | |
1b6bf175 | 1415 | if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) |
50e1e10f TT |
1416 | return 1; |
1417 | ||
1418 | /* | |
1419 | * Read the inode and block bitmaps in; we'll be messing with | |
1420 | * them. | |
1421 | */ | |
f8188fff | 1422 | e2fsck_read_bitmaps(ctx); |
efc6f628 | 1423 | |
50e1e10f TT |
1424 | /* |
1425 | * First, find a free block | |
1426 | */ | |
1b6bf175 TT |
1427 | pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); |
1428 | if (pctx->errcode) { | |
1429 | pctx->str = "ext2fs_new_block"; | |
1430 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1431 | return 1; |
1432 | } | |
1b6bf175 | 1433 | ext2fs_mark_block_bitmap(ctx->block_found_map, blk); |
50e1e10f TT |
1434 | ext2fs_mark_block_bitmap(fs->block_map, blk); |
1435 | ext2fs_mark_bb_dirty(fs); | |
1436 | ||
1437 | /* | |
1438 | * Now let's create the actual data block for the inode | |
1439 | */ | |
1440 | if (db->blockcnt) | |
1b6bf175 | 1441 | pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); |
50e1e10f | 1442 | else |
1b6bf175 TT |
1443 | pctx->errcode = ext2fs_new_dir_block(fs, db->ino, |
1444 | EXT2_ROOT_INO, &block); | |
50e1e10f | 1445 | |
1b6bf175 TT |
1446 | if (pctx->errcode) { |
1447 | pctx->str = "ext2fs_new_dir_block"; | |
1448 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1449 | return 1; |
1450 | } | |
1451 | ||
1b6bf175 | 1452 | pctx->errcode = ext2fs_write_dir_block(fs, blk, block); |
c4e3d3f3 | 1453 | ext2fs_free_mem(&block); |
1b6bf175 TT |
1454 | if (pctx->errcode) { |
1455 | pctx->str = "ext2fs_write_dir_block"; | |
1456 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1457 | return 1; |
1458 | } | |
1459 | ||
1460 | /* | |
1461 | * Update the inode block count | |
1462 | */ | |
08b21301 | 1463 | e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
1ca1059f | 1464 | ext2fs_iblk_add_blocks(fs, &inode, 1); |
50e1e10f TT |
1465 | if (inode.i_size < (db->blockcnt+1) * fs->blocksize) |
1466 | inode.i_size = (db->blockcnt+1) * fs->blocksize; | |
08b21301 | 1467 | e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
50e1e10f TT |
1468 | |
1469 | /* | |
1470 | * Finally, update the block pointers for the inode | |
1471 | */ | |
1472 | db->blk = blk; | |
133a56dc | 1473 | pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE, |
50e1e10f | 1474 | 0, update_dir_block, db); |
1b6bf175 TT |
1475 | if (pctx->errcode) { |
1476 | pctx->str = "ext2fs_block_iterate"; | |
1477 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1478 | return 1; |
1479 | } | |
1480 | ||
1481 | return 0; | |
1482 | } | |
1483 | ||
1484 | /* | |
1485 | * This is a helper function for allocate_dir_block(). | |
1486 | */ | |
54434927 | 1487 | static int update_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)), |
50e1e10f | 1488 | blk_t *block_nr, |
133a56dc | 1489 | e2_blkcnt_t blockcnt, |
54434927 | 1490 | blk_t ref_block EXT2FS_ATTR((unused)), |
efc6f628 | 1491 | int ref_offset EXT2FS_ATTR((unused)), |
54dc7ca2 | 1492 | void *priv_data) |
50e1e10f | 1493 | { |
54dc7ca2 | 1494 | struct ext2_db_entry *db; |
50e1e10f | 1495 | |
54dc7ca2 | 1496 | db = (struct ext2_db_entry *) priv_data; |
133a56dc | 1497 | if (db->blockcnt == (int) blockcnt) { |
50e1e10f TT |
1498 | *block_nr = db->blk; |
1499 | return BLOCK_CHANGED; | |
1500 | } | |
1501 | return 0; | |
1502 | } |