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