]>
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() */ |
d1154eb4 | 45 | #include "config.h" |
48e6e813 TT |
46 | #include <string.h> |
47 | ||
3839e657 | 48 | #include "e2fsck.h" |
21c84b71 | 49 | #include "problem.h" |
0926668d | 50 | #include "dict.h" |
3839e657 | 51 | |
aa4115a4 TT |
52 | #ifdef NO_INLINE_FUNCS |
53 | #define _INLINE_ | |
54 | #else | |
55 | #define _INLINE_ inline | |
56 | #endif | |
57 | ||
ad4fa466 | 58 | /* #define DX_DEBUG */ |
8fdc9985 | 59 | |
3839e657 TT |
60 | /* |
61 | * Keeps track of how many times an inode is referenced. | |
62 | */ | |
4035f40b | 63 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf); |
3839e657 | 64 | static int check_dir_block(ext2_filsys fs, |
6dc64392 | 65 | struct ext2_db_entry2 *dir_blocks_info, |
54dc7ca2 | 66 | void *priv_data); |
1b6bf175 | 67 | static int allocate_dir_block(e2fsck_t ctx, |
6dc64392 | 68 | struct ext2_db_entry2 *dir_blocks_info, |
21c84b71 | 69 | char *buf, struct problem_context *pctx); |
8fdc9985 | 70 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino); |
ad4fa466 TT |
71 | static int htree_depth(struct dx_dir_info *dx_dir, |
72 | struct dx_dirblock_info *dx_db); | |
ea1959f0 | 73 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b); |
3839e657 | 74 | |
21c84b71 TT |
75 | struct check_dir_struct { |
76 | char *buf; | |
77 | struct problem_context pctx; | |
f8188fff | 78 | int count, max; |
1b6bf175 | 79 | e2fsck_t ctx; |
efc6f628 | 80 | }; |
21c84b71 | 81 | |
08b21301 | 82 | void e2fsck_pass2(e2fsck_t ctx) |
3839e657 | 83 | { |
a4742691 TT |
84 | struct ext2_super_block *sb = ctx->fs->super; |
85 | struct problem_context pctx; | |
86 | ext2_filsys fs = ctx->fs; | |
87 | char *buf; | |
8bf191e8 | 88 | #ifdef RESOURCE_TRACK |
3839e657 | 89 | struct resource_track rtrack; |
8bf191e8 | 90 | #endif |
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; | |
830b44f4 | 94 | unsigned int save_type; |
54434927 | 95 | int b; |
ad4fa466 | 96 | int i, depth; |
8fdc9985 TT |
97 | problem_t code; |
98 | int bad_dir; | |
99 | ||
6d96b00d | 100 | init_resource_track(&rtrack, ctx->fs->io); |
1b6bf175 TT |
101 | clear_problem_context(&cd.pctx); |
102 | ||
3839e657 TT |
103 | #ifdef MTRACE |
104 | mtrace_print("Pass 2"); | |
105 | #endif | |
106 | ||
1b6bf175 TT |
107 | if (!(ctx->options & E2F_OPT_PREEN)) |
108 | fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); | |
109 | ||
efc6f628 | 110 | e2fsck_setup_tdb_icount(ctx, EXT2_ICOUNT_OPT_INCREMENT, |
34b9f796 TT |
111 | &ctx->inode_count); |
112 | if (ctx->inode_count) | |
113 | cd.pctx.errcode = 0; | |
830b44f4 TT |
114 | else { |
115 | e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE, | |
116 | "inode_count", &save_type); | |
efc6f628 | 117 | cd.pctx.errcode = ext2fs_create_icount2(fs, |
34b9f796 | 118 | EXT2_ICOUNT_OPT_INCREMENT, |
1b6bf175 TT |
119 | 0, ctx->inode_link_info, |
120 | &ctx->inode_count); | |
830b44f4 TT |
121 | fs->default_bitmap_type = save_type; |
122 | } | |
1b6bf175 TT |
123 | if (cd.pctx.errcode) { |
124 | fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); | |
08b21301 TT |
125 | ctx->flags |= E2F_FLAG_ABORT; |
126 | return; | |
21c84b71 | 127 | } |
bcf9c5d4 | 128 | buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize, |
54dc7ca2 | 129 | "directory scan buffer"); |
3839e657 | 130 | |
21c84b71 TT |
131 | /* |
132 | * Set up the parent pointer for the root directory, if | |
133 | * present. (If the root directory is not present, we will | |
134 | * create it in pass 3.) | |
135 | */ | |
28db82a8 | 136 | (void) e2fsck_dir_info_set_parent(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); |
21c84b71 TT |
137 | |
138 | cd.buf = buf; | |
1b6bf175 | 139 | cd.ctx = ctx; |
f75c28de | 140 | cd.count = 1; |
6dc64392 | 141 | cd.max = ext2fs_dblist_count2(fs->dblist); |
f75c28de TT |
142 | |
143 | if (ctx->progress) | |
144 | (void) (ctx->progress)(ctx, 2, 0, cd.max); | |
ea1959f0 TT |
145 | |
146 | if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) | |
6dc64392 | 147 | ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp); |
efc6f628 | 148 | |
6dc64392 VAH |
149 | cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_block, |
150 | &cd); | |
49a7360b | 151 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) |
08b21301 | 152 | return; |
6267ee49 AD |
153 | |
154 | if (ctx->flags & E2F_FLAG_RESTART_LATER) { | |
155 | ctx->flags |= E2F_FLAG_RESTART; | |
156 | return; | |
157 | } | |
158 | ||
1b6bf175 TT |
159 | if (cd.pctx.errcode) { |
160 | fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); | |
08b21301 TT |
161 | ctx->flags |= E2F_FLAG_ABORT; |
162 | return; | |
7ac02a5e | 163 | } |
8fdc9985 TT |
164 | |
165 | #ifdef ENABLE_HTREE | |
166 | for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) { | |
4cae0452 TT |
167 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
168 | return; | |
d02d0195 DW |
169 | if (e2fsck_dir_will_be_rehashed(ctx, dx_dir->ino) || |
170 | dx_dir->numblocks == 0) | |
8fdc9985 TT |
171 | continue; |
172 | clear_problem_context(&pctx); | |
173 | bad_dir = 0; | |
174 | pctx.dir = dx_dir->ino; | |
175 | dx_db = dx_dir->dx_block; | |
176 | if (dx_db->flags & DX_FLAG_REFERENCED) | |
177 | dx_db->flags |= DX_FLAG_DUP_REF; | |
178 | else | |
179 | dx_db->flags |= DX_FLAG_REFERENCED; | |
180 | /* | |
181 | * Find all of the first and last leaf blocks, and | |
182 | * update their parent's min and max hash values | |
183 | */ | |
184 | for (b=0, dx_db = dx_dir->dx_block; | |
185 | b < dx_dir->numblocks; | |
186 | b++, dx_db++) { | |
187 | if ((dx_db->type != DX_DIRBLOCK_LEAF) || | |
188 | !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST))) | |
189 | continue; | |
190 | dx_parent = &dx_dir->dx_block[dx_db->parent]; | |
191 | /* | |
192 | * XXX Make sure dx_parent->min_hash > dx_db->min_hash | |
193 | */ | |
194 | if (dx_db->flags & DX_FLAG_FIRST) | |
195 | dx_parent->min_hash = dx_db->min_hash; | |
196 | /* | |
197 | * XXX Make sure dx_parent->max_hash < dx_db->max_hash | |
198 | */ | |
199 | if (dx_db->flags & DX_FLAG_LAST) | |
200 | dx_parent->max_hash = dx_db->max_hash; | |
201 | } | |
efc6f628 | 202 | |
8fdc9985 TT |
203 | for (b=0, dx_db = dx_dir->dx_block; |
204 | b < dx_dir->numblocks; | |
205 | b++, dx_db++) { | |
206 | pctx.blkcount = b; | |
207 | pctx.group = dx_db->parent; | |
208 | code = 0; | |
209 | if (!(dx_db->flags & DX_FLAG_FIRST) && | |
210 | (dx_db->min_hash < dx_db->node_min_hash)) { | |
211 | pctx.blk = dx_db->min_hash; | |
212 | pctx.blk2 = dx_db->node_min_hash; | |
213 | code = PR_2_HTREE_MIN_HASH; | |
214 | fix_problem(ctx, code, &pctx); | |
215 | bad_dir++; | |
216 | } | |
ad4fa466 TT |
217 | if (dx_db->type == DX_DIRBLOCK_LEAF) { |
218 | depth = htree_depth(dx_dir, dx_db); | |
219 | if (depth != dx_dir->depth) { | |
e5e12db9 | 220 | pctx.num = dx_dir->depth; |
ad4fa466 TT |
221 | code = PR_2_HTREE_BAD_DEPTH; |
222 | fix_problem(ctx, code, &pctx); | |
223 | bad_dir++; | |
224 | } | |
225 | } | |
8fdc9985 | 226 | /* |
efc6f628 | 227 | * This test doesn't apply for the root block |
8fdc9985 TT |
228 | * at block #0 |
229 | */ | |
230 | if (b && | |
231 | (dx_db->max_hash > dx_db->node_max_hash)) { | |
232 | pctx.blk = dx_db->max_hash; | |
233 | pctx.blk2 = dx_db->node_max_hash; | |
234 | code = PR_2_HTREE_MAX_HASH; | |
235 | fix_problem(ctx, code, &pctx); | |
503f9e7f | 236 | bad_dir++; |
8fdc9985 TT |
237 | } |
238 | if (!(dx_db->flags & DX_FLAG_REFERENCED)) { | |
239 | code = PR_2_HTREE_NOTREF; | |
240 | fix_problem(ctx, code, &pctx); | |
241 | bad_dir++; | |
242 | } else if (dx_db->flags & DX_FLAG_DUP_REF) { | |
243 | code = PR_2_HTREE_DUPREF; | |
244 | fix_problem(ctx, code, &pctx); | |
245 | bad_dir++; | |
246 | } | |
8fdc9985 TT |
247 | } |
248 | if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) { | |
249 | clear_htree(ctx, dx_dir->ino); | |
62acaa1d | 250 | dx_dir->numblocks = 0; |
8fdc9985 | 251 | } |
8fdc9985 | 252 | } |
23f75f6e | 253 | e2fsck_free_dx_dir_info(ctx); |
8fdc9985 | 254 | #endif |
c4e3d3f3 | 255 | ext2fs_free_mem(&buf); |
21c84b71 TT |
256 | ext2fs_free_dblist(fs->dblist); |
257 | ||
1b6bf175 TT |
258 | if (ctx->inode_bad_map) { |
259 | ext2fs_free_inode_bitmap(ctx->inode_bad_map); | |
260 | ctx->inode_bad_map = 0; | |
3839e657 | 261 | } |
aa4115a4 TT |
262 | if (ctx->inode_reg_map) { |
263 | ext2fs_free_inode_bitmap(ctx->inode_reg_map); | |
264 | ctx->inode_reg_map = 0; | |
265 | } | |
a4742691 TT |
266 | |
267 | clear_problem_context(&pctx); | |
268 | if (ctx->large_files) { | |
269 | if (!(sb->s_feature_ro_compat & | |
270 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE) && | |
271 | fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) { | |
272 | sb->s_feature_ro_compat |= | |
273 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE; | |
0cfce7f7 | 274 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
a4742691 TT |
275 | ext2fs_mark_super_dirty(fs); |
276 | } | |
277 | if (sb->s_rev_level == EXT2_GOOD_OLD_REV && | |
278 | fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) { | |
279 | ext2fs_update_dynamic_rev(fs); | |
280 | ext2fs_mark_super_dirty(fs); | |
281 | } | |
a4742691 | 282 | } |
efc6f628 | 283 | |
9facd076 | 284 | print_resource_track(ctx, _("Pass 2"), &rtrack, fs->io); |
3839e657 TT |
285 | } |
286 | ||
ad4fa466 TT |
287 | #define MAX_DEPTH 32000 |
288 | static int htree_depth(struct dx_dir_info *dx_dir, | |
289 | struct dx_dirblock_info *dx_db) | |
290 | { | |
291 | int depth = 0; | |
292 | ||
293 | while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) { | |
294 | dx_db = &dx_dir->dx_block[dx_db->parent]; | |
295 | depth++; | |
296 | } | |
297 | return depth; | |
298 | } | |
299 | ||
0926668d TT |
300 | static int dict_de_cmp(const void *a, const void *b) |
301 | { | |
520ead37 | 302 | const struct ext2_dir_entry *de_a, *de_b; |
0926668d TT |
303 | int a_len, b_len; |
304 | ||
520ead37 | 305 | de_a = (const struct ext2_dir_entry *) a; |
70f4632b | 306 | a_len = ext2fs_dirent_name_len(de_a); |
520ead37 | 307 | de_b = (const struct ext2_dir_entry *) b; |
70f4632b | 308 | b_len = ext2fs_dirent_name_len(de_b); |
0926668d TT |
309 | |
310 | if (a_len != b_len) | |
311 | return (a_len - b_len); | |
312 | ||
313 | return strncmp(de_a->name, de_b->name, a_len); | |
314 | } | |
ad4fa466 | 315 | |
ea1959f0 TT |
316 | /* |
317 | * This is special sort function that makes sure that directory blocks | |
318 | * with a dirblock of zero are sorted to the beginning of the list. | |
319 | * This guarantees that the root node of the htree directories are | |
320 | * processed first, so we know what hash version to use. | |
321 | */ | |
322 | static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b) | |
323 | { | |
6dc64392 VAH |
324 | const struct ext2_db_entry2 *db_a = |
325 | (const struct ext2_db_entry2 *) a; | |
326 | const struct ext2_db_entry2 *db_b = | |
327 | (const struct ext2_db_entry2 *) b; | |
ea1959f0 TT |
328 | |
329 | if (db_a->blockcnt && !db_b->blockcnt) | |
330 | return 1; | |
331 | ||
332 | if (!db_a->blockcnt && db_b->blockcnt) | |
333 | return -1; | |
efc6f628 | 334 | |
ea1959f0 TT |
335 | if (db_a->blk != db_b->blk) |
336 | return (int) (db_a->blk - db_b->blk); | |
efc6f628 | 337 | |
ea1959f0 TT |
338 | if (db_a->ino != db_b->ino) |
339 | return (int) (db_a->ino - db_b->ino); | |
340 | ||
341 | return (int) (db_a->blockcnt - db_b->blockcnt); | |
342 | } | |
343 | ||
344 | ||
3839e657 TT |
345 | /* |
346 | * Make sure the first entry in the directory is '.', and that the | |
347 | * directory entry is sane. | |
348 | */ | |
1b6bf175 | 349 | static int check_dot(e2fsck_t ctx, |
3839e657 | 350 | struct ext2_dir_entry *dirent, |
86c627ec | 351 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 TT |
352 | { |
353 | struct ext2_dir_entry *nextdir; | |
8a480350 | 354 | unsigned int rec_len, new_len; |
3c7c6d73 TT |
355 | int status = 0; |
356 | int created = 0; | |
357 | problem_t problem = 0; | |
efc6f628 | 358 | |
21c84b71 TT |
359 | if (!dirent->inode) |
360 | problem = PR_2_MISSING_DOT; | |
70f4632b | 361 | else if ((ext2fs_dirent_name_len(dirent) != 1) || |
21c84b71 TT |
362 | (dirent->name[0] != '.')) |
363 | problem = PR_2_1ST_NOT_DOT; | |
364 | else if (dirent->name[1] != '\0') | |
365 | problem = PR_2_DOT_NULL_TERM; | |
5dd77dbe | 366 | |
8a480350 | 367 | (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); |
21c84b71 | 368 | if (problem) { |
1b6bf175 | 369 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe TT |
370 | if (rec_len < 12) |
371 | rec_len = dirent->rec_len = 12; | |
3839e657 | 372 | dirent->inode = ino; |
70f4632b JK |
373 | ext2fs_dirent_set_name_len(dirent, 1); |
374 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); | |
3839e657 | 375 | dirent->name[0] = '.'; |
21c84b71 | 376 | dirent->name[1] = '\0'; |
3839e657 TT |
377 | status = 1; |
378 | created = 1; | |
3839e657 TT |
379 | } |
380 | } | |
3839e657 | 381 | if (dirent->inode != ino) { |
1b6bf175 | 382 | if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { |
3839e657 TT |
383 | dirent->inode = ino; |
384 | status = 1; | |
21c84b71 | 385 | } |
3839e657 | 386 | } |
5dd77dbe TT |
387 | if (rec_len > 12) { |
388 | new_len = rec_len - 12; | |
3839e657 | 389 | if (new_len > 12) { |
3839e657 | 390 | if (created || |
f8188fff | 391 | fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { |
3839e657 TT |
392 | nextdir = (struct ext2_dir_entry *) |
393 | ((char *) dirent + 12); | |
394 | dirent->rec_len = 12; | |
8a480350 TT |
395 | (void) ext2fs_set_rec_len(ctx->fs, new_len, |
396 | nextdir); | |
3839e657 | 397 | nextdir->inode = 0; |
70f4632b JK |
398 | ext2fs_dirent_set_name_len(nextdir, 0); |
399 | ext2fs_dirent_set_file_type(nextdir, | |
400 | EXT2_FT_UNKNOWN); | |
3839e657 TT |
401 | status = 1; |
402 | } | |
403 | } | |
404 | } | |
405 | return status; | |
406 | } | |
407 | ||
408 | /* | |
409 | * Make sure the second entry in the directory is '..', and that the | |
410 | * directory entry is sane. We do not check the inode number of '..' | |
411 | * here; this gets done in pass 3. | |
412 | */ | |
1b6bf175 | 413 | static int check_dotdot(e2fsck_t ctx, |
3839e657 | 414 | struct ext2_dir_entry *dirent, |
28db82a8 | 415 | ext2_ino_t ino, struct problem_context *pctx) |
3839e657 | 416 | { |
3c7c6d73 | 417 | problem_t problem = 0; |
cf5301d7 | 418 | unsigned int rec_len; |
efc6f628 | 419 | |
21c84b71 TT |
420 | if (!dirent->inode) |
421 | problem = PR_2_MISSING_DOT_DOT; | |
70f4632b | 422 | else if ((ext2fs_dirent_name_len(dirent) != 2) || |
21c84b71 TT |
423 | (dirent->name[0] != '.') || |
424 | (dirent->name[1] != '.')) | |
425 | problem = PR_2_2ND_NOT_DOT_DOT; | |
426 | else if (dirent->name[2] != '\0') | |
427 | problem = PR_2_DOT_DOT_NULL_TERM; | |
428 | ||
8a480350 | 429 | (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); |
21c84b71 | 430 | if (problem) { |
1b6bf175 | 431 | if (fix_problem(ctx, problem, pctx)) { |
5dd77dbe | 432 | if (rec_len < 12) |
21c84b71 | 433 | dirent->rec_len = 12; |
3839e657 TT |
434 | /* |
435 | * Note: we don't have the parent inode just | |
436 | * yet, so we will fill it in with the root | |
437 | * inode. This will get fixed in pass 3. | |
438 | */ | |
439 | dirent->inode = EXT2_ROOT_INO; | |
70f4632b JK |
440 | ext2fs_dirent_set_name_len(dirent, 2); |
441 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); | |
3839e657 TT |
442 | dirent->name[0] = '.'; |
443 | dirent->name[1] = '.'; | |
21c84b71 | 444 | dirent->name[2] = '\0'; |
3839e657 | 445 | return 1; |
efc6f628 | 446 | } |
3839e657 TT |
447 | return 0; |
448 | } | |
28db82a8 TT |
449 | if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) { |
450 | fix_problem(ctx, PR_2_NO_DIRINFO, pctx); | |
451 | return -1; | |
452 | } | |
3839e657 TT |
453 | return 0; |
454 | } | |
455 | ||
456 | /* | |
457 | * Check to make sure a directory entry doesn't contain any illegal | |
458 | * characters. | |
459 | */ | |
1b6bf175 | 460 | static int check_name(e2fsck_t ctx, |
3839e657 | 461 | struct ext2_dir_entry *dirent, |
efc6f628 | 462 | ext2_ino_t dir_ino EXT2FS_ATTR((unused)), |
54434927 | 463 | struct problem_context *pctx) |
3839e657 TT |
464 | { |
465 | int i; | |
466 | int fixup = -1; | |
3839e657 | 467 | int ret = 0; |
efc6f628 | 468 | |
70f4632b | 469 | for ( i = 0; i < ext2fs_dirent_name_len(dirent); i++) { |
3839e657 TT |
470 | if (dirent->name[i] == '/' || dirent->name[i] == '\0') { |
471 | if (fixup < 0) { | |
1b6bf175 | 472 | fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); |
3839e657 TT |
473 | } |
474 | if (fixup) { | |
475 | dirent->name[i] = '.'; | |
476 | ret = 1; | |
21c84b71 | 477 | } |
3839e657 TT |
478 | } |
479 | } | |
480 | return ret; | |
481 | } | |
482 | ||
aa4115a4 TT |
483 | /* |
484 | * Check the directory filetype (if present) | |
485 | */ | |
486 | static _INLINE_ int check_filetype(e2fsck_t ctx, | |
54434927 TT |
487 | struct ext2_dir_entry *dirent, |
488 | ext2_ino_t dir_ino EXT2FS_ATTR((unused)), | |
489 | struct problem_context *pctx) | |
aa4115a4 | 490 | { |
70f4632b | 491 | int filetype = ext2fs_dirent_file_type(dirent); |
aa4115a4 TT |
492 | int should_be = EXT2_FT_UNKNOWN; |
493 | struct ext2_inode inode; | |
494 | ||
495 | if (!(ctx->fs->super->s_feature_incompat & | |
7847c1d4 TT |
496 | EXT2_FEATURE_INCOMPAT_FILETYPE)) { |
497 | if (filetype == 0 || | |
498 | !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx)) | |
499 | return 0; | |
70f4632b | 500 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); |
7847c1d4 TT |
501 | return 1; |
502 | } | |
aa4115a4 | 503 | |
c5d2f50d | 504 | if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) { |
aa4115a4 | 505 | should_be = EXT2_FT_DIR; |
c5d2f50d | 506 | } else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, |
aa4115a4 TT |
507 | dirent->inode)) { |
508 | should_be = EXT2_FT_REG_FILE; | |
509 | } else if (ctx->inode_bad_map && | |
c5d2f50d | 510 | ext2fs_test_inode_bitmap2(ctx->inode_bad_map, |
aa4115a4 TT |
511 | dirent->inode)) |
512 | should_be = 0; | |
513 | else { | |
514 | e2fsck_read_inode(ctx, dirent->inode, &inode, | |
515 | "check_filetype"); | |
6fdc7a32 | 516 | should_be = ext2_file_type(inode.i_mode); |
aa4115a4 TT |
517 | } |
518 | if (filetype == should_be) | |
519 | return 0; | |
520 | pctx->num = should_be; | |
521 | ||
522 | if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, | |
523 | pctx) == 0) | |
524 | return 0; | |
efc6f628 | 525 | |
70f4632b | 526 | ext2fs_dirent_set_file_type(dirent, should_be); |
aa4115a4 TT |
527 | return 1; |
528 | } | |
529 | ||
8fdc9985 TT |
530 | #ifdef ENABLE_HTREE |
531 | static void parse_int_node(ext2_filsys fs, | |
6dc64392 | 532 | struct ext2_db_entry2 *db, |
8fdc9985 TT |
533 | struct check_dir_struct *cd, |
534 | struct dx_dir_info *dx_dir, | |
07307114 | 535 | char *block_buf, int failed_csum) |
8fdc9985 TT |
536 | { |
537 | struct ext2_dx_root_info *root; | |
538 | struct ext2_dx_entry *ent; | |
539 | struct ext2_dx_countlimit *limit; | |
540 | struct dx_dirblock_info *dx_db; | |
ad4fa466 | 541 | int i, expect_limit, count; |
8fdc9985 TT |
542 | blk_t blk; |
543 | ext2_dirhash_t min_hash = 0xffffffff; | |
544 | ext2_dirhash_t max_hash = 0; | |
ad4fa466 | 545 | ext2_dirhash_t hash = 0, prev_hash; |
07307114 | 546 | int csum_size = 0; |
8fdc9985 TT |
547 | |
548 | if (db->blockcnt == 0) { | |
549 | root = (struct ext2_dx_root_info *) (block_buf + 24); | |
efc6f628 | 550 | |
8fdc9985 TT |
551 | #ifdef DX_DEBUG |
552 | printf("Root node dump:\n"); | |
8deb80a5 | 553 | printf("\t Reserved zero: %u\n", root->reserved_zero); |
8fdc9985 TT |
554 | printf("\t Hash Version: %d\n", root->hash_version); |
555 | printf("\t Info length: %d\n", root->info_length); | |
556 | printf("\t Indirect levels: %d\n", root->indirect_levels); | |
557 | printf("\t Flags: %d\n", root->unused_flags); | |
558 | #endif | |
559 | ||
560 | ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length); | |
07307114 DW |
561 | |
562 | if (failed_csum && | |
563 | (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || | |
564 | fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID, | |
565 | &cd->pctx))) | |
566 | goto clear_and_exit; | |
8fdc9985 TT |
567 | } else { |
568 | ent = (struct ext2_dx_entry *) (block_buf+8); | |
07307114 DW |
569 | |
570 | if (failed_csum && | |
571 | (e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) || | |
572 | fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID, | |
573 | &cd->pctx))) | |
574 | goto clear_and_exit; | |
8fdc9985 | 575 | } |
07307114 | 576 | |
8fdc9985 TT |
577 | limit = (struct ext2_dx_countlimit *) ent; |
578 | ||
579 | #ifdef DX_DEBUG | |
efc6f628 | 580 | printf("Number of entries (count): %d\n", |
8132d840 | 581 | ext2fs_le16_to_cpu(limit->count)); |
efc6f628 | 582 | printf("Number of entries (limit): %d\n", |
8132d840 | 583 | ext2fs_le16_to_cpu(limit->limit)); |
8fdc9985 TT |
584 | #endif |
585 | ||
8132d840 | 586 | count = ext2fs_le16_to_cpu(limit->count); |
07307114 DW |
587 | if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
588 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) | |
589 | csum_size = sizeof(struct ext2_dx_tail); | |
590 | expect_limit = (fs->blocksize - | |
591 | (csum_size + ((char *) ent - block_buf))) / | |
592 | sizeof(struct ext2_dx_entry); | |
8132d840 TT |
593 | if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) { |
594 | cd->pctx.num = ext2fs_le16_to_cpu(limit->limit); | |
ad4fa466 TT |
595 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx)) |
596 | goto clear_and_exit; | |
597 | } | |
8132d840 TT |
598 | if (count > expect_limit) { |
599 | cd->pctx.num = count; | |
ad4fa466 TT |
600 | if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx)) |
601 | goto clear_and_exit; | |
602 | count = expect_limit; | |
603 | } | |
efc6f628 | 604 | |
ad4fa466 TT |
605 | for (i=0; i < count; i++) { |
606 | prev_hash = hash; | |
8132d840 | 607 | hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0; |
8fdc9985 | 608 | #ifdef DX_DEBUG |
8deb80a5 | 609 | printf("Entry #%d: Hash 0x%08x, block %u\n", i, |
8132d840 | 610 | hash, ext2fs_le32_to_cpu(ent[i].block)); |
8fdc9985 | 611 | #endif |
8132d840 | 612 | blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff; |
8fdc9985 | 613 | /* Check to make sure the block is valid */ |
977ac873 | 614 | if (blk >= (blk_t) dx_dir->numblocks) { |
b7a00563 | 615 | cd->pctx.blk = blk; |
8fdc9985 | 616 | if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK, |
ad4fa466 TT |
617 | &cd->pctx)) |
618 | goto clear_and_exit; | |
977ac873 | 619 | continue; |
8fdc9985 | 620 | } |
ad4fa466 TT |
621 | if (hash < prev_hash && |
622 | fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx)) | |
623 | goto clear_and_exit; | |
8fdc9985 TT |
624 | dx_db = &dx_dir->dx_block[blk]; |
625 | if (dx_db->flags & DX_FLAG_REFERENCED) { | |
626 | dx_db->flags |= DX_FLAG_DUP_REF; | |
627 | } else { | |
628 | dx_db->flags |= DX_FLAG_REFERENCED; | |
629 | dx_db->parent = db->blockcnt; | |
630 | } | |
631 | if (hash < min_hash) | |
632 | min_hash = hash; | |
633 | if (hash > max_hash) | |
634 | max_hash = hash; | |
635 | dx_db->node_min_hash = hash; | |
8132d840 | 636 | if ((i+1) < count) |
efc6f628 | 637 | dx_db->node_max_hash = |
8132d840 | 638 | ext2fs_le32_to_cpu(ent[i+1].hash) & ~1; |
8fdc9985 TT |
639 | else { |
640 | dx_db->node_max_hash = 0xfffffffe; | |
641 | dx_db->flags |= DX_FLAG_LAST; | |
642 | } | |
643 | if (i == 0) | |
644 | dx_db->flags |= DX_FLAG_FIRST; | |
645 | } | |
646 | #ifdef DX_DEBUG | |
647 | printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n", | |
648 | db->blockcnt, min_hash, max_hash); | |
649 | #endif | |
650 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
651 | dx_db->min_hash = min_hash; | |
652 | dx_db->max_hash = max_hash; | |
ad4fa466 TT |
653 | return; |
654 | ||
655 | clear_and_exit: | |
656 | clear_htree(cd->ctx, cd->pctx.ino); | |
657 | dx_dir->numblocks = 0; | |
07307114 | 658 | e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino); |
8fdc9985 TT |
659 | } |
660 | #endif /* ENABLE_HTREE */ | |
aa4115a4 | 661 | |
e70ae99e TT |
662 | /* |
663 | * Given a busted directory, try to salvage it somehow. | |
efc6f628 | 664 | * |
e70ae99e | 665 | */ |
ad4fa466 | 666 | static void salvage_directory(ext2_filsys fs, |
e70ae99e TT |
667 | struct ext2_dir_entry *dirent, |
668 | struct ext2_dir_entry *prev, | |
82ad476d DW |
669 | unsigned int *offset, |
670 | unsigned int block_len) | |
e70ae99e TT |
671 | { |
672 | char *cp = (char *) dirent; | |
8a480350 TT |
673 | int left; |
674 | unsigned int rec_len, prev_rec_len; | |
70f4632b | 675 | unsigned int name_len = ext2fs_dirent_name_len(dirent); |
e70ae99e | 676 | |
8a480350 | 677 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); |
82ad476d | 678 | left = block_len - *offset - rec_len; |
5dd77dbe | 679 | |
e70ae99e TT |
680 | /* |
681 | * Special case of directory entry of size 8: copy what's left | |
682 | * of the directory block up to cover up the invalid hole. | |
683 | */ | |
5dd77dbe | 684 | if ((left >= 12) && (rec_len == 8)) { |
e70ae99e TT |
685 | memmove(cp, cp+8, left); |
686 | memset(cp + left, 0, 8); | |
ad4fa466 TT |
687 | return; |
688 | } | |
689 | /* | |
690 | * If the directory entry overruns the end of the directory | |
691 | * block, and the name is small enough to fit, then adjust the | |
692 | * record length. | |
693 | */ | |
694 | if ((left < 0) && | |
8a480350 | 695 | ((int) rec_len + left > 8) && |
68477355 | 696 | ((int) name_len + 8 <= (int) rec_len + left) && |
ad4fa466 TT |
697 | dirent->inode <= fs->super->s_inodes_count && |
698 | strnlen(dirent->name, name_len) == name_len) { | |
8a480350 | 699 | (void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent); |
ad4fa466 | 700 | return; |
e70ae99e TT |
701 | } |
702 | /* | |
575307cc KS |
703 | * If the record length of the directory entry is a multiple |
704 | * of four, and not too big, such that it is valid, let the | |
705 | * previous directory entry absorb the invalid one. | |
e70ae99e | 706 | */ |
5dd77dbe | 707 | if (prev && rec_len && (rec_len % 4) == 0 && |
82ad476d | 708 | (*offset + rec_len <= block_len)) { |
8a480350 TT |
709 | (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); |
710 | prev_rec_len += rec_len; | |
711 | (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); | |
5dd77dbe | 712 | *offset += rec_len; |
ad4fa466 | 713 | return; |
e70ae99e TT |
714 | } |
715 | /* | |
716 | * Default salvage method --- kill all of the directory | |
717 | * entries for the rest of the block. We will either try to | |
718 | * absorb it into the previous directory entry, or create a | |
719 | * new empty directory entry the rest of the directory block. | |
720 | */ | |
721 | if (prev) { | |
8a480350 | 722 | (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); |
82ad476d | 723 | prev_rec_len += block_len - *offset; |
8a480350 | 724 | (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); |
ad4fa466 | 725 | *offset = fs->blocksize; |
e70ae99e | 726 | } else { |
82ad476d | 727 | rec_len = block_len - *offset; |
8a480350 | 728 | (void) ext2fs_set_rec_len(fs, rec_len, dirent); |
70f4632b JK |
729 | ext2fs_dirent_set_name_len(dirent, 0); |
730 | ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); | |
e70ae99e | 731 | dirent->inode = 0; |
e70ae99e | 732 | } |
e70ae99e TT |
733 | } |
734 | ||
d3eb1502 | 735 | #define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len)) |
17641bf2 DW |
736 | static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf) |
737 | { | |
738 | struct ext2_dir_entry *d; | |
739 | void *top; | |
740 | struct ext2_dir_entry_tail *t; | |
17641bf2 DW |
741 | |
742 | d = dirbuf; | |
743 | top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize); | |
744 | ||
d3eb1502 DW |
745 | while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top) |
746 | d = NEXT_DIRENT(d); | |
17641bf2 DW |
747 | |
748 | if (d != top) { | |
749 | size_t min_size = EXT2_DIR_REC_LEN( | |
750 | ext2fs_dirent_name_len(dirbuf)); | |
d3eb1502 | 751 | if (min_size > top - (void *)d) |
17641bf2 | 752 | return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; |
d3eb1502 | 753 | d->rec_len = top - (void *)d; |
17641bf2 DW |
754 | } |
755 | ||
756 | t = (struct ext2_dir_entry_tail *)top; | |
757 | if (t->det_reserved_zero1 || | |
758 | t->det_rec_len != sizeof(struct ext2_dir_entry_tail) || | |
759 | t->det_reserved_name_len != EXT2_DIR_NAME_LEN_CSUM) | |
760 | ext2fs_initialize_dirent_tail(fs, t); | |
761 | ||
762 | return 0; | |
763 | } | |
d3eb1502 | 764 | #undef NEXT_DIRENT |
17641bf2 | 765 | |
52b0c6e6 DW |
766 | static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino, |
767 | size_t *inline_data_size, | |
768 | struct problem_context *pctx, | |
769 | char *buf) | |
770 | { | |
771 | ext2_filsys fs = ctx->fs; | |
772 | struct ext2_inode inode; | |
773 | size_t new_size, old_size; | |
774 | errcode_t retval; | |
775 | ||
776 | old_size = *inline_data_size; | |
0ac4b397 DW |
777 | /* |
778 | * If there's not enough bytes to start the "second" dir block | |
779 | * (in the EA space) then truncate everything to the first block. | |
780 | */ | |
781 | if (old_size > EXT4_MIN_INLINE_DATA_SIZE && | |
782 | old_size < EXT4_MIN_INLINE_DATA_SIZE + | |
783 | EXT2_DIR_REC_LEN(1)) { | |
784 | old_size = EXT4_MIN_INLINE_DATA_SIZE; | |
785 | new_size = old_size; | |
786 | } else | |
787 | /* Increase to the next four-byte boundary for salvaging */ | |
788 | new_size = old_size + (4 - (old_size & 3)); | |
52b0c6e6 DW |
789 | memset(buf + old_size, 0, new_size - old_size); |
790 | retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size); | |
791 | if (retval == EXT2_ET_INLINE_DATA_NO_SPACE) { | |
0ac4b397 | 792 | /* Or we can't, so truncate. */ |
52b0c6e6 DW |
793 | new_size -= 4; |
794 | retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size); | |
795 | if (retval) { | |
796 | if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED, | |
797 | pctx)) { | |
798 | new_size = 0; | |
799 | goto write_inode; | |
800 | } | |
801 | goto err; | |
802 | } | |
803 | } else if (retval) { | |
804 | if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED, | |
805 | pctx)) { | |
806 | new_size = 0; | |
807 | goto write_inode; | |
808 | } | |
809 | goto err; | |
810 | } | |
811 | ||
812 | write_inode: | |
813 | retval = ext2fs_read_inode(fs, ino, &inode); | |
814 | if (retval) | |
815 | goto err; | |
816 | ||
817 | retval = ext2fs_inode_size_set(fs, &inode, new_size); | |
818 | if (retval) | |
819 | goto err; | |
820 | if (new_size == 0) | |
821 | inode.i_flags &= ~EXT4_INLINE_DATA_FL; | |
822 | retval = ext2fs_write_inode(fs, ino, &inode); | |
823 | if (retval) | |
824 | goto err; | |
825 | *inline_data_size = new_size; | |
826 | ||
827 | err: | |
828 | return retval; | |
829 | } | |
830 | ||
3839e657 | 831 | static int check_dir_block(ext2_filsys fs, |
6dc64392 | 832 | struct ext2_db_entry2 *db, |
54dc7ca2 | 833 | void *priv_data) |
3839e657 | 834 | { |
8fdc9985 TT |
835 | struct dx_dir_info *dx_dir; |
836 | #ifdef ENABLE_HTREE | |
837 | struct dx_dirblock_info *dx_db = 0; | |
838 | #endif /* ENABLE_HTREE */ | |
6582dbe9 | 839 | struct ext2_dir_entry *dirent, *prev, dot, dotdot; |
8fdc9985 | 840 | ext2_dirhash_t hash; |
54434927 | 841 | unsigned int offset = 0; |
3839e657 | 842 | int dir_modified = 0; |
21c84b71 | 843 | int dot_state; |
03fa6f8a | 844 | unsigned int rec_len; |
6dc64392 | 845 | blk64_t block_nr = db->blk; |
86c627ec | 846 | ext2_ino_t ino = db->ino; |
28db82a8 | 847 | ext2_ino_t subdir_parent; |
21c84b71 | 848 | __u16 links; |
54dc7ca2 | 849 | struct check_dir_struct *cd; |
0ac4b397 | 850 | char *buf, *ibuf; |
1b6bf175 | 851 | e2fsck_t ctx; |
3c7c6d73 | 852 | problem_t problem; |
ea1959f0 | 853 | struct ext2_dx_root_info *root; |
e8254bfd | 854 | struct ext2_dx_countlimit *limit; |
0926668d TT |
855 | static dict_t de_dict; |
856 | struct problem_context pctx; | |
857 | int dups_found = 0; | |
28db82a8 | 858 | int ret; |
e8548796 | 859 | int dx_csum_size = 0, de_csum_size = 0; |
81683c6a | 860 | int failed_csum = 0; |
e8548796 | 861 | int is_leaf = 1; |
24997f1c | 862 | size_t inline_data_size = 0; |
6582dbe9 | 863 | int filetype = 0; |
0ac4b397 | 864 | size_t max_block_size; |
1b6bf175 | 865 | |
54dc7ca2 | 866 | cd = (struct check_dir_struct *) priv_data; |
0ac4b397 | 867 | ibuf = buf = cd->buf; |
1b6bf175 | 868 | ctx = cd->ctx; |
f8188fff | 869 | |
49a7360b | 870 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART) |
4cae0452 | 871 | return DIRENT_ABORT; |
efc6f628 | 872 | |
4cae0452 TT |
873 | if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max)) |
874 | return DIRENT_ABORT; | |
efc6f628 | 875 | |
07307114 | 876 | if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
e8548796 | 877 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { |
07307114 | 878 | dx_csum_size = sizeof(struct ext2_dx_tail); |
e8548796 DW |
879 | de_csum_size = sizeof(struct ext2_dir_entry_tail); |
880 | } | |
07307114 | 881 | |
6582dbe9 ZL |
882 | if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, |
883 | EXT2_FEATURE_INCOMPAT_FILETYPE)) | |
884 | filetype = EXT2_FT_DIR << 8; | |
885 | ||
3839e657 | 886 | /* |
efc6f628 | 887 | * Make sure the inode is still in use (could have been |
3839e657 TT |
888 | * deleted in the duplicate/bad blocks pass. |
889 | */ | |
c5d2f50d | 890 | if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino))) |
3839e657 | 891 | return 0; |
50e1e10f | 892 | |
21c84b71 TT |
893 | cd->pctx.ino = ino; |
894 | cd->pctx.blk = block_nr; | |
895 | cd->pctx.blkcount = db->blockcnt; | |
896 | cd->pctx.ino2 = 0; | |
897 | cd->pctx.dirent = 0; | |
898 | cd->pctx.num = 0; | |
899 | ||
6582dbe9 ZL |
900 | if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, |
901 | EXT4_FEATURE_INCOMPAT_INLINE_DATA)) { | |
902 | errcode_t ec; | |
903 | ||
904 | ec = ext2fs_inline_data_size(fs, ino, &inline_data_size); | |
905 | if (ec && ec != EXT2_ET_NO_INLINE_DATA) | |
906 | return DIRENT_ABORT; | |
907 | } | |
908 | ||
909 | if (db->blk == 0 && !inline_data_size) { | |
1b6bf175 | 910 | if (allocate_dir_block(ctx, db, buf, &cd->pctx)) |
50e1e10f TT |
911 | return 0; |
912 | block_nr = db->blk; | |
913 | } | |
efc6f628 | 914 | |
3839e657 TT |
915 | if (db->blockcnt) |
916 | dot_state = 2; | |
917 | else | |
918 | dot_state = 0; | |
919 | ||
0926668d TT |
920 | if (ctx->dirs_to_hash && |
921 | ext2fs_u32_list_test(ctx->dirs_to_hash, ino)) | |
922 | dups_found++; | |
923 | ||
3839e657 | 924 | #if 0 |
f3db3566 | 925 | printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, |
3839e657 TT |
926 | db->blockcnt, ino); |
927 | #endif | |
efc6f628 | 928 | |
f85a9ae6 | 929 | ehandler_operation(_("reading directory block")); |
cd971869 | 930 | if (inline_data_size) { |
6582dbe9 | 931 | cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0); |
cd971869 DW |
932 | if (cd->pctx.errcode) |
933 | goto inline_read_fail; | |
934 | #ifdef WORDS_BIGENDIAN | |
0ac4b397 DW |
935 | if (db->blockcnt) |
936 | goto skip_first_read_swab; | |
cd971869 DW |
937 | *((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf)); |
938 | cd->pctx.errcode = ext2fs_dirent_swab_in2(fs, | |
939 | buf + EXT4_INLINE_DATA_DOTDOT_SIZE, | |
0ac4b397 DW |
940 | EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE, |
941 | 0); | |
942 | if (cd->pctx.errcode) | |
943 | goto inline_read_fail; | |
944 | skip_first_read_swab: | |
945 | if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE || | |
946 | !db->blockcnt) | |
947 | goto inline_read_fail; | |
948 | cd->pctx.errcode = ext2fs_dirent_swab_in2(fs, | |
949 | buf + EXT4_MIN_INLINE_DATA_SIZE, | |
950 | inline_data_size - EXT4_MIN_INLINE_DATA_SIZE, | |
cd971869 DW |
951 | 0); |
952 | #endif | |
953 | } else | |
6582dbe9 ZL |
954 | cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, |
955 | buf, 0, ino); | |
cd971869 | 956 | inline_read_fail: |
52b0c6e6 DW |
957 | pctx.ino = ino; |
958 | pctx.num = inline_data_size; | |
0ac4b397 DW |
959 | if (((inline_data_size & 3) || |
960 | (inline_data_size > EXT4_MIN_INLINE_DATA_SIZE && | |
961 | inline_data_size < EXT4_MIN_INLINE_DATA_SIZE + | |
962 | EXT2_DIR_REC_LEN(1))) && | |
52b0c6e6 DW |
963 | fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) { |
964 | errcode_t err = fix_inline_dir_size(ctx, ino, | |
965 | &inline_data_size, &pctx, | |
966 | buf); | |
967 | if (err) | |
968 | return DIRENT_ABORT; | |
969 | ||
970 | } | |
e94bc631 | 971 | ehandler_operation(0); |
b9852cd8 TT |
972 | if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED) |
973 | cd->pctx.errcode = 0; /* We'll handle this ourselves */ | |
81683c6a DW |
974 | else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) { |
975 | cd->pctx.errcode = 0; /* We'll handle this ourselves */ | |
976 | failed_csum = 1; | |
977 | } | |
1b6bf175 | 978 | if (cd->pctx.errcode) { |
e8548796 | 979 | char *buf2; |
08b21301 TT |
980 | if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { |
981 | ctx->flags |= E2F_FLAG_ABORT; | |
982 | return DIRENT_ABORT; | |
983 | } | |
e8548796 DW |
984 | ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0, |
985 | EXT2_ROOT_INO, &buf2); | |
986 | memcpy(buf, buf2, fs->blocksize); | |
987 | ext2fs_free_mem(&buf2); | |
3839e657 | 988 | } |
8fdc9985 TT |
989 | #ifdef ENABLE_HTREE |
990 | dx_dir = e2fsck_get_dx_dir_info(ctx, ino); | |
62acaa1d | 991 | if (dx_dir && dx_dir->numblocks) { |
8fdc9985 | 992 | if (db->blockcnt >= dx_dir->numblocks) { |
ea9085c7 | 993 | pctx.dir = ino; |
efc6f628 | 994 | if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK, |
d45edec0 TT |
995 | &pctx)) { |
996 | clear_htree(ctx, ino); | |
997 | dx_dir->numblocks = 0; | |
998 | dx_db = 0; | |
999 | goto out_htree; | |
1000 | } | |
1001 | fatal_error(ctx, _("Can not continue.")); | |
8fdc9985 TT |
1002 | } |
1003 | dx_db = &dx_dir->dx_block[db->blockcnt]; | |
1004 | dx_db->type = DX_DIRBLOCK_LEAF; | |
1005 | dx_db->phys = block_nr; | |
1006 | dx_db->min_hash = ~0; | |
1007 | dx_db->max_hash = 0; | |
efc6f628 | 1008 | |
8fdc9985 | 1009 | dirent = (struct ext2_dir_entry *) buf; |
8a480350 | 1010 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); |
e8254bfd | 1011 | limit = (struct ext2_dx_countlimit *) (buf+8); |
8fdc9985 | 1012 | if (db->blockcnt == 0) { |
ea1959f0 | 1013 | root = (struct ext2_dx_root_info *) (buf + 24); |
8fdc9985 TT |
1014 | dx_db->type = DX_DIRBLOCK_ROOT; |
1015 | dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST; | |
ea1959f0 TT |
1016 | if ((root->reserved_zero || |
1017 | root->info_length < 8 || | |
1018 | root->indirect_levels > 1) && | |
1019 | fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) { | |
1020 | clear_htree(ctx, ino); | |
1021 | dx_dir->numblocks = 0; | |
1022 | dx_db = 0; | |
f77704e4 | 1023 | } |
ea1959f0 | 1024 | dx_dir->hashversion = root->hash_version; |
f77704e4 TT |
1025 | if ((dx_dir->hashversion <= EXT2_HASH_TEA) && |
1026 | (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) | |
1027 | dx_dir->hashversion += 3; | |
ad4fa466 | 1028 | dx_dir->depth = root->indirect_levels + 1; |
8fdc9985 | 1029 | } else if ((dirent->inode == 0) && |
5dd77dbe | 1030 | (rec_len == fs->blocksize) && |
70f4632b | 1031 | (ext2fs_dirent_name_len(dirent) == 0) && |
efc6f628 | 1032 | (ext2fs_le16_to_cpu(limit->limit) == |
07307114 | 1033 | ((fs->blocksize - (8 + dx_csum_size)) / |
8132d840 | 1034 | sizeof(struct ext2_dx_entry)))) |
8fdc9985 | 1035 | dx_db->type = DX_DIRBLOCK_NODE; |
e8548796 | 1036 | is_leaf = 0; |
8fdc9985 | 1037 | } |
d45edec0 | 1038 | out_htree: |
8fdc9985 | 1039 | #endif /* ENABLE_HTREE */ |
3839e657 | 1040 | |
7f43a46f DW |
1041 | /* Leaf node with no space for csum? Rebuild dirs in pass 3A. */ |
1042 | if (is_leaf && !inline_data_size && failed_csum && | |
1043 | !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { | |
1044 | de_csum_size = 0; | |
d02d0195 DW |
1045 | if (e2fsck_dir_will_be_rehashed(ctx, ino)) { |
1046 | failed_csum = 0; | |
1047 | goto skip_checksum; | |
1048 | } | |
1049 | if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM, | |
7f43a46f | 1050 | &cd->pctx)) |
e8548796 | 1051 | goto skip_checksum; |
7f43a46f | 1052 | e2fsck_rehash_dir_later(ctx, ino); |
d02d0195 | 1053 | failed_csum = 0; |
7f43a46f | 1054 | goto skip_checksum; |
e8548796 DW |
1055 | } |
1056 | /* htree nodes don't use fake dirents to store checksums */ | |
1057 | if (!is_leaf) | |
1058 | de_csum_size = 0; | |
1059 | ||
1060 | skip_checksum: | |
0ac4b397 DW |
1061 | if (inline_data_size) { |
1062 | if (db->blockcnt) { | |
1063 | buf += EXT4_MIN_INLINE_DATA_SIZE; | |
1064 | max_block_size = inline_data_size - EXT4_MIN_INLINE_DATA_SIZE; | |
1065 | /* Zero-length second block, just exit */ | |
1066 | if (max_block_size == 0) | |
1067 | return 0; | |
1068 | } else { | |
1069 | max_block_size = EXT4_MIN_INLINE_DATA_SIZE; | |
1070 | } | |
1071 | } else | |
1072 | max_block_size = fs->blocksize - de_csum_size; | |
1073 | ||
0926668d | 1074 | dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp); |
e70ae99e | 1075 | prev = 0; |
3839e657 | 1076 | do { |
3971bfe8 | 1077 | dgrp_t group; |
49a7360b | 1078 | ext2_ino_t first_unused_inode; |
70f4632b | 1079 | unsigned int name_len; |
49a7360b | 1080 | |
1b6bf175 | 1081 | problem = 0; |
6582dbe9 ZL |
1082 | if (!inline_data_size || dot_state > 1) { |
1083 | dirent = (struct ext2_dir_entry *) (buf + offset); | |
1084 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); | |
1085 | cd->pctx.dirent = dirent; | |
1086 | cd->pctx.num = offset; | |
1087 | if (((offset + rec_len) > fs->blocksize) || | |
3a748dfc DW |
1088 | (inline_data_size > 0 && |
1089 | (offset + rec_len) > inline_data_size) || | |
6582dbe9 ZL |
1090 | (rec_len < 12) || |
1091 | ((rec_len % 4) != 0) || | |
1092 | ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) { | |
82ad476d DW |
1093 | if (fix_problem(ctx, PR_2_DIR_CORRUPTED, |
1094 | &cd->pctx)) { | |
4348709c DW |
1095 | #ifdef WORDS_BIGENDIAN |
1096 | /* | |
1097 | * On big-endian systems, if the dirent | |
1098 | * swap routine finds a rec_len that it | |
1099 | * doesn't like, it continues | |
1100 | * processing the block as if rec_len | |
1101 | * == 8. This means that the name | |
1102 | * field gets byte swapped, which means | |
1103 | * that salvage will not detect the | |
1104 | * correct name length (unless the name | |
1105 | * has a length that's an exact | |
1106 | * multiple of four bytes), and it'll | |
1107 | * discard the entry (unnecessarily) | |
1108 | * and the rest of the dirent block. | |
1109 | * Therefore, swap the rest of the | |
1110 | * block back to disk order, run | |
1111 | * salvage, and re-swap anything after | |
1112 | * the salvaged dirent. | |
1113 | */ | |
1114 | int need_reswab = 0; | |
1115 | if (rec_len < 8 || rec_len % 4) { | |
1116 | need_reswab = 1; | |
1117 | ext2fs_dirent_swab_in2(fs, | |
1118 | ((char *)dirent) + 8, | |
1119 | max_block_size - offset - 8, | |
1120 | 0); | |
1121 | } | |
1122 | #endif | |
82ad476d DW |
1123 | salvage_directory(fs, dirent, prev, |
1124 | &offset, | |
3a748dfc | 1125 | max_block_size); |
4348709c DW |
1126 | #ifdef WORDS_BIGENDIAN |
1127 | if (need_reswab) { | |
1128 | (void) ext2fs_get_rec_len(fs, | |
1129 | dirent, &rec_len); | |
1130 | ext2fs_dirent_swab_in2(fs, | |
1131 | ((char *)dirent) + offset + rec_len, | |
1132 | max_block_size - offset - rec_len, | |
1133 | 0); | |
1134 | } | |
1135 | #endif | |
6582dbe9 ZL |
1136 | dir_modified++; |
1137 | continue; | |
1138 | } else | |
1139 | goto abort_free_dict; | |
1140 | } | |
1141 | } else { | |
1142 | if (dot_state == 0) { | |
1143 | memset(&dot, 0, sizeof(dot)); | |
1144 | dirent = ˙ | |
1145 | dirent->inode = ino; | |
1146 | dirent->rec_len = EXT2_DIR_REC_LEN(1); | |
1147 | dirent->name_len = 1 | filetype; | |
1148 | dirent->name[0] = '.'; | |
1149 | } else if (dot_state == 1) { | |
1150 | memset(&dotdot, 0, sizeof(dotdot)); | |
1151 | dirent = &dotdot; | |
1152 | dirent->inode = | |
1153 | ((struct ext2_dir_entry *)buf)->inode; | |
1154 | dirent->rec_len = EXT2_DIR_REC_LEN(2); | |
1155 | dirent->name_len = 2 | filetype; | |
1156 | dirent->name[0] = '.'; | |
1157 | dirent->name[1] = '.'; | |
1158 | } else { | |
1159 | fatal_error(ctx, _("Can not continue.")); | |
1160 | } | |
1161 | cd->pctx.dirent = dirent; | |
1162 | cd->pctx.num = offset; | |
3839e657 | 1163 | } |
50e1e10f | 1164 | |
e70ae99e | 1165 | if (dot_state == 0) { |
1b6bf175 | 1166 | if (check_dot(ctx, dirent, ino, &cd->pctx)) |
3839e657 | 1167 | dir_modified++; |
e70ae99e | 1168 | } else if (dot_state == 1) { |
28db82a8 TT |
1169 | ret = check_dotdot(ctx, dirent, ino, &cd->pctx); |
1170 | if (ret < 0) | |
0926668d | 1171 | goto abort_free_dict; |
28db82a8 | 1172 | if (ret) |
3839e657 TT |
1173 | dir_modified++; |
1174 | } else if (dirent->inode == ino) { | |
1b6bf175 TT |
1175 | problem = PR_2_LINK_DOT; |
1176 | if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { | |
3839e657 TT |
1177 | dirent->inode = 0; |
1178 | dir_modified++; | |
21c84b71 | 1179 | goto next; |
3839e657 TT |
1180 | } |
1181 | } | |
efc6f628 | 1182 | if (!dirent->inode) |
3839e657 | 1183 | goto next; |
efc6f628 | 1184 | |
3839e657 TT |
1185 | /* |
1186 | * Make sure the inode listed is a legal one. | |
efc6f628 | 1187 | */ |
70f4632b | 1188 | name_len = ext2fs_dirent_name_len(dirent); |
3839e657 | 1189 | if (((dirent->inode != EXT2_ROOT_INO) && |
7f88b043 | 1190 | (dirent->inode < EXT2_FIRST_INODE(fs->super))) || |
3839e657 | 1191 | (dirent->inode > fs->super->s_inodes_count)) { |
1b6bf175 | 1192 | problem = PR_2_BAD_INO; |
1b6bf175 | 1193 | } else if (ctx->inode_bb_map && |
c5d2f50d | 1194 | (ext2fs_test_inode_bitmap2(ctx->inode_bb_map, |
1b6bf175 TT |
1195 | dirent->inode))) { |
1196 | /* | |
1197 | * If the inode is in a bad block, offer to | |
1198 | * clear it. | |
1199 | */ | |
1200 | problem = PR_2_BB_INODE; | |
70f4632b | 1201 | } else if ((dot_state > 1) && (name_len == 1) && |
1b6bf175 TT |
1202 | (dirent->name[0] == '.')) { |
1203 | /* | |
1204 | * If there's a '.' entry in anything other | |
1205 | * than the first directory entry, it's a | |
1206 | * duplicate entry that should be removed. | |
1207 | */ | |
1208 | problem = PR_2_DUP_DOT; | |
70f4632b | 1209 | } else if ((dot_state > 1) && (name_len == 2) && |
efc6f628 | 1210 | (dirent->name[0] == '.') && |
1b6bf175 TT |
1211 | (dirent->name[1] == '.')) { |
1212 | /* | |
1213 | * If there's a '..' entry in anything other | |
1214 | * than the second directory entry, it's a | |
1215 | * duplicate entry that should be removed. | |
1216 | */ | |
1217 | problem = PR_2_DUP_DOT_DOT; | |
e70ae99e | 1218 | } else if ((dot_state > 1) && |
1b6bf175 TT |
1219 | (dirent->inode == EXT2_ROOT_INO)) { |
1220 | /* | |
1221 | * Don't allow links to the root directory. | |
1222 | * We check this specially to make sure we | |
1223 | * catch this error case even if the root | |
1224 | * directory hasn't been created yet. | |
1225 | */ | |
1226 | problem = PR_2_LINK_ROOT; | |
70f4632b | 1227 | } else if ((dot_state > 1) && (name_len == 0)) { |
c40db6d5 TT |
1228 | /* |
1229 | * Don't allow zero-length directory names. | |
1230 | */ | |
1231 | problem = PR_2_NULL_NAME; | |
21c84b71 TT |
1232 | } |
1233 | ||
1b6bf175 TT |
1234 | if (problem) { |
1235 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
21c84b71 TT |
1236 | dirent->inode = 0; |
1237 | dir_modified++; | |
1238 | goto next; | |
1b6bf175 TT |
1239 | } else { |
1240 | ext2fs_unmark_valid(fs); | |
1241 | if (problem == PR_2_BAD_INO) | |
1242 | goto next; | |
21c84b71 | 1243 | } |
3839e657 TT |
1244 | } |
1245 | ||
1246 | /* | |
1247 | * If the inode was marked as having bad fields in | |
1248 | * pass1, process it and offer to fix/clear it. | |
1249 | * (We wait until now so that we can display the | |
1250 | * pathname to the user.) | |
1251 | */ | |
1b6bf175 | 1252 | if (ctx->inode_bad_map && |
c5d2f50d | 1253 | ext2fs_test_inode_bitmap2(ctx->inode_bad_map, |
3839e657 | 1254 | dirent->inode)) { |
e72a9ba3 | 1255 | if (e2fsck_process_bad_inode(ctx, ino, |
bcf9c5d4 TT |
1256 | dirent->inode, |
1257 | buf + fs->blocksize)) { | |
3839e657 TT |
1258 | dirent->inode = 0; |
1259 | dir_modified++; | |
1260 | goto next; | |
1261 | } | |
a02ce9df | 1262 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 1263 | return DIRENT_ABORT; |
3839e657 TT |
1264 | } |
1265 | ||
49a7360b JS |
1266 | group = ext2fs_group_of_ino(fs, dirent->inode); |
1267 | first_unused_inode = group * fs->super->s_inodes_per_group + | |
1268 | 1 + fs->super->s_inodes_per_group - | |
d7cca6b0 | 1269 | ext2fs_bg_itable_unused(fs, group); |
49a7360b JS |
1270 | cd->pctx.group = group; |
1271 | ||
1272 | /* | |
42e89ce7 TT |
1273 | * Check if the inode was missed out because |
1274 | * _INODE_UNINIT flag was set or bg_itable_unused was | |
1275 | * incorrect. If so, clear the _INODE_UNINIT flag and | |
1276 | * restart e2fsck. In the future it would be nice if | |
1277 | * we could call a function in pass1.c that checks the | |
1278 | * newly visible inodes. | |
49a7360b | 1279 | */ |
cd65a24e | 1280 | if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) { |
6267ee49 | 1281 | pctx.num = dirent->inode; |
49a7360b JS |
1282 | if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT, |
1283 | &cd->pctx)){ | |
e633b58a | 1284 | ext2fs_bg_flags_clear(fs, group, |
732c8cd5 | 1285 | EXT2_BG_INODE_UNINIT); |
42e89ce7 | 1286 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1287 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1288 | } else { |
1289 | ext2fs_unmark_valid(fs); | |
1290 | if (problem == PR_2_BAD_INO) | |
1291 | goto next; | |
1292 | } | |
1293 | } else if (dirent->inode >= first_unused_inode) { | |
6267ee49 | 1294 | pctx.num = dirent->inode; |
49a7360b | 1295 | if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){ |
d7cca6b0 | 1296 | ext2fs_bg_itable_unused_set(fs, group, 0); |
49a7360b | 1297 | ext2fs_mark_super_dirty(fs); |
6267ee49 | 1298 | ctx->flags |= E2F_FLAG_RESTART_LATER; |
49a7360b JS |
1299 | } else { |
1300 | ext2fs_unmark_valid(fs); | |
1301 | if (problem == PR_2_BAD_INO) | |
1302 | goto next; | |
1303 | } | |
1304 | } | |
1305 | ||
0433c1f1 TT |
1306 | /* |
1307 | * Offer to clear unused inodes; if we are going to be | |
1308 | * restarting the scan due to bg_itable_unused being | |
1309 | * wrong, then don't clear any inodes to avoid zapping | |
1310 | * inodes that were skipped during pass1 due to an | |
1311 | * incorrect bg_itable_unused; we'll get any real | |
1312 | * problems after we restart. | |
1313 | */ | |
1314 | if (!(ctx->flags & E2F_FLAG_RESTART_LATER) && | |
97d26ce9 TT |
1315 | !(ext2fs_test_inode_bitmap2(ctx->inode_used_map, |
1316 | dirent->inode))) | |
49a7360b | 1317 | problem = PR_2_UNUSED_INODE; |
49a7360b JS |
1318 | |
1319 | if (problem) { | |
1320 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
1321 | dirent->inode = 0; | |
1322 | dir_modified++; | |
1323 | goto next; | |
1324 | } else { | |
1325 | ext2fs_unmark_valid(fs); | |
1326 | if (problem == PR_2_BAD_INO) | |
1327 | goto next; | |
1328 | } | |
1329 | } | |
1330 | ||
1b6bf175 TT |
1331 | if (check_name(ctx, dirent, ino, &cd->pctx)) |
1332 | dir_modified++; | |
1333 | ||
aa4115a4 TT |
1334 | if (check_filetype(ctx, dirent, ino, &cd->pctx)) |
1335 | dir_modified++; | |
1336 | ||
8fdc9985 TT |
1337 | #ifdef ENABLE_HTREE |
1338 | if (dx_db) { | |
1339 | ext2fs_dirhash(dx_dir->hashversion, dirent->name, | |
70f4632b | 1340 | ext2fs_dirent_name_len(dirent), |
503f9e7f | 1341 | fs->super->s_hash_seed, &hash, 0); |
8fdc9985 TT |
1342 | if (hash < dx_db->min_hash) |
1343 | dx_db->min_hash = hash; | |
1344 | if (hash > dx_db->max_hash) | |
1345 | dx_db->max_hash = hash; | |
1346 | } | |
1347 | #endif | |
1348 | ||
3839e657 TT |
1349 | /* |
1350 | * If this is a directory, then mark its parent in its | |
1351 | * dir_info structure. If the parent field is already | |
1352 | * filled in, then this directory has more than one | |
1353 | * hard link. We assume the first link is correct, | |
1354 | * and ask the user if he/she wants to clear this one. | |
1355 | */ | |
e70ae99e | 1356 | if ((dot_state > 1) && |
c5d2f50d | 1357 | (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, |
3839e657 | 1358 | dirent->inode))) { |
28db82a8 TT |
1359 | if (e2fsck_dir_info_get_parent(ctx, dirent->inode, |
1360 | &subdir_parent)) { | |
1b6bf175 TT |
1361 | cd->pctx.ino = dirent->inode; |
1362 | fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); | |
0926668d | 1363 | goto abort_free_dict; |
3839e657 | 1364 | } |
28db82a8 TT |
1365 | if (subdir_parent) { |
1366 | cd->pctx.ino2 = subdir_parent; | |
1b6bf175 | 1367 | if (fix_problem(ctx, PR_2_LINK_DIR, |
21c84b71 | 1368 | &cd->pctx)) { |
3839e657 TT |
1369 | dirent->inode = 0; |
1370 | dir_modified++; | |
1371 | goto next; | |
21c84b71 TT |
1372 | } |
1373 | cd->pctx.ino2 = 0; | |
28db82a8 | 1374 | } else { |
efc6f628 | 1375 | (void) e2fsck_dir_info_set_parent(ctx, |
28db82a8 TT |
1376 | dirent->inode, ino); |
1377 | } | |
3839e657 | 1378 | } |
0926668d TT |
1379 | |
1380 | if (dups_found) { | |
1381 | ; | |
1382 | } else if (dict_lookup(&de_dict, dirent)) { | |
1383 | clear_problem_context(&pctx); | |
1384 | pctx.ino = ino; | |
1385 | pctx.dirent = dirent; | |
1386 | fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx); | |
e8548796 | 1387 | e2fsck_rehash_dir_later(ctx, ino); |
0926668d TT |
1388 | dups_found++; |
1389 | } else | |
1390 | dict_alloc_insert(&de_dict, dirent, dirent); | |
efc6f628 | 1391 | |
1b6bf175 TT |
1392 | ext2fs_icount_increment(ctx->inode_count, dirent->inode, |
1393 | &links); | |
21c84b71 | 1394 | if (links > 1) |
1b6bf175 TT |
1395 | ctx->fs_links_count++; |
1396 | ctx->fs_total_count++; | |
3839e657 | 1397 | next: |
e70ae99e | 1398 | prev = dirent; |
5dd77dbe | 1399 | if (dir_modified) |
8a480350 | 1400 | (void) ext2fs_get_rec_len(fs, dirent, &rec_len); |
6582dbe9 ZL |
1401 | if (!inline_data_size || dot_state > 1) { |
1402 | offset += rec_len; | |
1403 | } else { | |
6698374c | 1404 | if (dot_state == 1) { |
6582dbe9 | 1405 | offset = 4; |
6698374c DW |
1406 | /* |
1407 | * If we get here, we're checking an inline | |
1408 | * directory and we've just checked a (fake) | |
1409 | * dotdot entry that we created on the stack. | |
1410 | * Therefore set 'prev' to NULL so that if we | |
1411 | * call salvage_directory on the next entry, | |
1412 | * it won't try to absorb the next entry into | |
1413 | * the on-stack dotdot entry. | |
1414 | */ | |
1415 | prev = NULL; | |
1416 | } | |
6582dbe9 | 1417 | } |
e70ae99e | 1418 | dot_state++; |
0ac4b397 | 1419 | } while (offset < max_block_size); |
3839e657 TT |
1420 | #if 0 |
1421 | printf("\n"); | |
1422 | #endif | |
8fdc9985 TT |
1423 | #ifdef ENABLE_HTREE |
1424 | if (dx_db) { | |
1425 | #ifdef DX_DEBUG | |
1426 | printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n", | |
1427 | db->blockcnt, dx_db->type, | |
1428 | dx_db->min_hash, dx_db->max_hash); | |
1429 | #endif | |
b7a00563 | 1430 | cd->pctx.dir = cd->pctx.ino; |
8fdc9985 TT |
1431 | if ((dx_db->type == DX_DIRBLOCK_ROOT) || |
1432 | (dx_db->type == DX_DIRBLOCK_NODE)) | |
81683c6a | 1433 | parse_int_node(fs, db, cd, dx_dir, buf, failed_csum); |
8fdc9985 TT |
1434 | } |
1435 | #endif /* ENABLE_HTREE */ | |
e8548796 | 1436 | |
0ac4b397 DW |
1437 | if (offset != max_block_size) { |
1438 | cd->pctx.num = rec_len + offset - max_block_size; | |
1439 | if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { | |
1440 | dirent->rec_len = cd->pctx.num; | |
1441 | dir_modified++; | |
1b6bf175 | 1442 | } |
3839e657 TT |
1443 | } |
1444 | if (dir_modified) { | |
2e9d8391 | 1445 | int flags, will_rehash; |
e8548796 DW |
1446 | /* leaf block with no tail? Rehash dirs later. */ |
1447 | if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, | |
1448 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) && | |
1449 | is_leaf && | |
17641bf2 DW |
1450 | !inline_data_size && |
1451 | !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) { | |
1452 | if (insert_dirent_tail(fs, buf) == 0) | |
1453 | goto write_and_fix; | |
e8548796 | 1454 | e2fsck_rehash_dir_later(ctx, ino); |
17641bf2 | 1455 | } |
e8548796 DW |
1456 | |
1457 | write_and_fix: | |
2e9d8391 DW |
1458 | will_rehash = e2fsck_dir_will_be_rehashed(ctx, ino); |
1459 | if (will_rehash) { | |
1460 | flags = ctx->fs->flags; | |
e8548796 | 1461 | ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; |
2e9d8391 | 1462 | } |
6582dbe9 | 1463 | if (inline_data_size) { |
0ac4b397 | 1464 | buf = ibuf; |
cd971869 | 1465 | #ifdef WORDS_BIGENDIAN |
0ac4b397 DW |
1466 | if (db->blockcnt) |
1467 | goto skip_first_write_swab; | |
cd971869 DW |
1468 | *((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf)); |
1469 | cd->pctx.errcode = ext2fs_dirent_swab_out2(fs, | |
1470 | buf + EXT4_INLINE_DATA_DOTDOT_SIZE, | |
0ac4b397 | 1471 | EXT4_MIN_INLINE_DATA_SIZE - |
cd971869 DW |
1472 | EXT4_INLINE_DATA_DOTDOT_SIZE, |
1473 | 0); | |
0ac4b397 DW |
1474 | if (cd->pctx.errcode) |
1475 | goto skip_second_write_swab; | |
1476 | skip_first_write_swab: | |
1477 | if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE || | |
1478 | !db->blockcnt) | |
1479 | goto skip_second_write_swab; | |
1480 | cd->pctx.errcode = ext2fs_dirent_swab_out2(fs, | |
1481 | buf + EXT4_MIN_INLINE_DATA_SIZE, | |
1482 | inline_data_size - | |
1483 | EXT4_MIN_INLINE_DATA_SIZE, | |
1484 | 0); | |
1485 | skip_second_write_swab: | |
cd971869 DW |
1486 | if (cd->pctx.errcode && |
1487 | !fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx)) | |
1488 | goto abort_free_dict; | |
1489 | #endif | |
6582dbe9 ZL |
1490 | cd->pctx.errcode = |
1491 | ext2fs_inline_data_set(fs, ino, 0, buf, | |
1492 | inline_data_size); | |
1493 | } else | |
1494 | cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, | |
1495 | buf, 0, ino); | |
2e9d8391 DW |
1496 | if (will_rehash) |
1497 | ctx->fs->flags = (flags & | |
1498 | EXT2_FLAG_IGNORE_CSUM_ERRORS) | | |
1499 | (ctx->fs->flags & | |
1500 | ~EXT2_FLAG_IGNORE_CSUM_ERRORS); | |
1b6bf175 | 1501 | if (cd->pctx.errcode) { |
08b21301 | 1502 | if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, |
0926668d TT |
1503 | &cd->pctx)) |
1504 | goto abort_free_dict; | |
3839e657 TT |
1505 | } |
1506 | ext2fs_mark_changed(fs); | |
e8548796 DW |
1507 | } else if (is_leaf && failed_csum && !dir_modified) { |
1508 | /* | |
1509 | * If a leaf node that fails csum makes it this far without | |
1510 | * alteration, ask the user if the checksum should be fixed. | |
1511 | */ | |
1512 | if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID, | |
1513 | &cd->pctx)) | |
1514 | goto write_and_fix; | |
3839e657 | 1515 | } |
0926668d | 1516 | dict_free_nodes(&de_dict); |
3839e657 | 1517 | return 0; |
0926668d | 1518 | abort_free_dict: |
0926668d | 1519 | ctx->flags |= E2F_FLAG_ABORT; |
49a7360b | 1520 | dict_free_nodes(&de_dict); |
0926668d | 1521 | return DIRENT_ABORT; |
3839e657 TT |
1522 | } |
1523 | ||
624e4a64 AK |
1524 | struct del_block { |
1525 | e2fsck_t ctx; | |
1526 | e2_blkcnt_t num; | |
1527 | }; | |
1528 | ||
3839e657 TT |
1529 | /* |
1530 | * This function is called to deallocate a block, and is an interator | |
1531 | * functioned called by deallocate inode via ext2fs_iterate_block(). | |
1532 | */ | |
1533 | static int deallocate_inode_block(ext2_filsys fs, | |
6dc64392 | 1534 | blk64_t *block_nr, |
54434927 | 1535 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), |
6dc64392 | 1536 | blk64_t ref_block EXT2FS_ATTR((unused)), |
54434927 | 1537 | int ref_offset EXT2FS_ATTR((unused)), |
133a56dc | 1538 | void *priv_data) |
3839e657 | 1539 | { |
624e4a64 | 1540 | struct del_block *p = priv_data; |
efc6f628 | 1541 | |
1917875f | 1542 | if (HOLE_BLKADDR(*block_nr)) |
3839e657 | 1543 | return 0; |
1ba7a2f2 | 1544 | if ((*block_nr < fs->super->s_first_data_block) || |
4efbac6f | 1545 | (*block_nr >= ext2fs_blocks_count(fs->super))) |
1ba7a2f2 | 1546 | return 0; |
8dd650ab DW |
1547 | if ((*block_nr % EXT2FS_CLUSTER_RATIO(fs)) == 0) |
1548 | ext2fs_block_alloc_stats2(fs, *block_nr, -1); | |
624e4a64 | 1549 | p->num++; |
3839e657 TT |
1550 | return 0; |
1551 | } | |
efc6f628 | 1552 | |
3839e657 TT |
1553 | /* |
1554 | * This fuction deallocates an inode | |
1555 | */ | |
4035f40b | 1556 | static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) |
3839e657 | 1557 | { |
1b6bf175 | 1558 | ext2_filsys fs = ctx->fs; |
3839e657 | 1559 | struct ext2_inode inode; |
1b6bf175 | 1560 | struct problem_context pctx; |
0684a4f3 | 1561 | __u32 count; |
624e4a64 | 1562 | struct del_block del_block; |
efc6f628 | 1563 | |
08b21301 | 1564 | e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); |
1b6bf175 TT |
1565 | clear_problem_context(&pctx); |
1566 | pctx.ino = ino; | |
f3db3566 | 1567 | |
3839e657 TT |
1568 | /* |
1569 | * Fix up the bitmaps... | |
1570 | */ | |
f8188fff | 1571 | e2fsck_read_bitmaps(ctx); |
0684a4f3 TT |
1572 | ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); |
1573 | ||
0c80c44b | 1574 | if (ext2fs_file_acl_block(fs, &inode) && |
0684a4f3 | 1575 | (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { |
39f5659a DW |
1576 | pctx.errcode = ext2fs_adjust_ea_refcount3(fs, |
1577 | ext2fs_file_acl_block(fs, &inode), | |
1578 | block_buf, -1, &count, ino); | |
0684a4f3 TT |
1579 | if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { |
1580 | pctx.errcode = 0; | |
1581 | count = 1; | |
1582 | } | |
1583 | if (pctx.errcode) { | |
0c80c44b | 1584 | pctx.blk = ext2fs_file_acl_block(fs, &inode); |
0684a4f3 TT |
1585 | fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); |
1586 | ctx->flags |= E2F_FLAG_ABORT; | |
1587 | return; | |
1588 | } | |
1589 | if (count == 0) { | |
48f23054 | 1590 | ext2fs_block_alloc_stats2(fs, |
0c80c44b | 1591 | ext2fs_file_acl_block(fs, &inode), -1); |
0684a4f3 | 1592 | } |
0c80c44b | 1593 | ext2fs_file_acl_block_set(fs, &inode, 0); |
0684a4f3 | 1594 | } |
3839e657 | 1595 | |
0c80c44b | 1596 | if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) |
c8ec2bad | 1597 | goto clear_inode; |
a4742691 | 1598 | |
2ece8390 DW |
1599 | /* Inline data inodes don't have blocks to iterate */ |
1600 | if (inode.i_flags & EXT4_INLINE_DATA_FL) | |
1601 | goto clear_inode; | |
1602 | ||
3b6c0938 DW |
1603 | if (LINUX_S_ISREG(inode.i_mode) && |
1604 | ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode))) | |
a4742691 TT |
1605 | ctx->large_files--; |
1606 | ||
624e4a64 AK |
1607 | del_block.ctx = ctx; |
1608 | del_block.num = 0; | |
6dc64392 | 1609 | pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf, |
624e4a64 AK |
1610 | deallocate_inode_block, |
1611 | &del_block); | |
1b6bf175 TT |
1612 | if (pctx.errcode) { |
1613 | fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); | |
08b21301 TT |
1614 | ctx->flags |= E2F_FLAG_ABORT; |
1615 | return; | |
1b6bf175 | 1616 | } |
c8ec2bad TT |
1617 | clear_inode: |
1618 | /* Inode may have changed by block_iterate, so reread it */ | |
1619 | e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); | |
1620 | e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); | |
3839e657 TT |
1621 | } |
1622 | ||
8fdc9985 TT |
1623 | /* |
1624 | * This fuction clears the htree flag on an inode | |
1625 | */ | |
1626 | static void clear_htree(e2fsck_t ctx, ext2_ino_t ino) | |
1627 | { | |
1628 | struct ext2_inode inode; | |
efc6f628 | 1629 | |
8fdc9985 TT |
1630 | e2fsck_read_inode(ctx, ino, &inode, "clear_htree"); |
1631 | inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL; | |
1632 | e2fsck_write_inode(ctx, ino, &inode, "clear_htree"); | |
b7a00563 TT |
1633 | if (ctx->dirs_to_hash) |
1634 | ext2fs_u32_list_add(ctx->dirs_to_hash, ino); | |
8fdc9985 TT |
1635 | } |
1636 | ||
1637 | ||
f404167d TT |
1638 | int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, |
1639 | ext2_ino_t ino, char *buf) | |
3839e657 | 1640 | { |
1b6bf175 | 1641 | ext2_filsys fs = ctx->fs; |
3839e657 | 1642 | struct ext2_inode inode; |
3839e657 | 1643 | int inode_modified = 0; |
6c313fd4 | 1644 | int not_fixed = 0; |
1e3472c5 | 1645 | unsigned char *frag, *fsize; |
21c84b71 | 1646 | struct problem_context pctx; |
3c7c6d73 | 1647 | problem_t problem = 0; |
3839e657 | 1648 | |
08b21301 | 1649 | e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); |
21c84b71 TT |
1650 | |
1651 | clear_problem_context(&pctx); | |
1652 | pctx.ino = ino; | |
1653 | pctx.dir = dir; | |
1654 | pctx.inode = &inode; | |
1655 | ||
0c80c44b | 1656 | if (ext2fs_file_acl_block(fs, &inode) && |
f76344fb TT |
1657 | !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { |
1658 | if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { | |
0c80c44b | 1659 | ext2fs_file_acl_block_set(fs, &inode, 0); |
f76344fb TT |
1660 | inode_modified++; |
1661 | } else | |
1662 | not_fixed++; | |
1663 | } | |
6c313fd4 | 1664 | |
50e1e10f TT |
1665 | if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && |
1666 | !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && | |
1667 | !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && | |
08b21301 TT |
1668 | !(LINUX_S_ISSOCK(inode.i_mode))) |
1669 | problem = PR_2_BAD_MODE; | |
fdbdea09 | 1670 | else if (LINUX_S_ISCHR(inode.i_mode) |
0684a4f3 | 1671 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 1672 | problem = PR_2_BAD_CHAR_DEV; |
fdbdea09 | 1673 | else if (LINUX_S_ISBLK(inode.i_mode) |
0684a4f3 | 1674 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
08b21301 | 1675 | problem = PR_2_BAD_BLOCK_DEV; |
fdbdea09 | 1676 | else if (LINUX_S_ISFIFO(inode.i_mode) |
0684a4f3 | 1677 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 1678 | problem = PR_2_BAD_FIFO; |
fdbdea09 | 1679 | else if (LINUX_S_ISSOCK(inode.i_mode) |
0684a4f3 | 1680 | && !e2fsck_pass1_check_device_inode(fs, &inode)) |
1dde43f0 | 1681 | problem = PR_2_BAD_SOCKET; |
fdbdea09 | 1682 | else if (LINUX_S_ISLNK(inode.i_mode) |
7cadc577 | 1683 | && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) { |
bcf9c5d4 | 1684 | problem = PR_2_INVALID_SYMLINK; |
67052a8a | 1685 | } |
1dde43f0 | 1686 | |
08b21301 TT |
1687 | if (problem) { |
1688 | if (fix_problem(ctx, problem, &pctx)) { | |
1b6bf175 | 1689 | deallocate_inode(ctx, ino, 0); |
a02ce9df | 1690 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 1691 | return 0; |
7cf73dcd | 1692 | return 1; |
6c313fd4 TT |
1693 | } else |
1694 | not_fixed++; | |
08b21301 | 1695 | problem = 0; |
7cf73dcd | 1696 | } |
efc6f628 | 1697 | |
6c313fd4 TT |
1698 | if (inode.i_faddr) { |
1699 | if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { | |
1700 | inode.i_faddr = 0; | |
1701 | inode_modified++; | |
1702 | } else | |
1703 | not_fixed++; | |
3839e657 | 1704 | } |
1e3472c5 TT |
1705 | |
1706 | switch (fs->super->s_creator_os) { | |
1e3472c5 TT |
1707 | case EXT2_OS_HURD: |
1708 | frag = &inode.osd2.hurd2.h_i_frag; | |
1709 | fsize = &inode.osd2.hurd2.h_i_fsize; | |
1710 | break; | |
1e3472c5 TT |
1711 | default: |
1712 | frag = fsize = 0; | |
1713 | } | |
21c84b71 TT |
1714 | if (frag && *frag) { |
1715 | pctx.num = *frag; | |
1b6bf175 | 1716 | if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { |
21c84b71 TT |
1717 | *frag = 0; |
1718 | inode_modified++; | |
7e0282c5 TT |
1719 | } else |
1720 | not_fixed++; | |
21c84b71 TT |
1721 | pctx.num = 0; |
1722 | } | |
1723 | if (fsize && *fsize) { | |
1724 | pctx.num = *fsize; | |
1b6bf175 | 1725 | if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { |
21c84b71 TT |
1726 | *fsize = 0; |
1727 | inode_modified++; | |
6c313fd4 TT |
1728 | } else |
1729 | not_fixed++; | |
21c84b71 TT |
1730 | pctx.num = 0; |
1731 | } | |
1732 | ||
5d17119d | 1733 | if ((fs->super->s_creator_os == EXT2_OS_LINUX) && |
efc6f628 | 1734 | !(fs->super->s_feature_ro_compat & |
5d17119d TT |
1735 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && |
1736 | (inode.osd2.linux2.l_i_blocks_hi != 0)) { | |
1737 | pctx.num = inode.osd2.linux2.l_i_blocks_hi; | |
1738 | if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) { | |
1739 | inode.osd2.linux2.l_i_blocks_hi = 0; | |
1740 | inode_modified++; | |
1741 | } | |
1742 | } | |
1743 | ||
911ec626 TT |
1744 | if (!(fs->super->s_feature_incompat & |
1745 | EXT4_FEATURE_INCOMPAT_64BIT) && | |
1746 | inode.osd2.linux2.l_i_file_acl_high != 0) { | |
1747 | pctx.num = inode.osd2.linux2.l_i_file_acl_high; | |
1748 | if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) { | |
1749 | inode.osd2.linux2.l_i_file_acl_high = 0; | |
1750 | inode_modified++; | |
1751 | } else | |
1752 | not_fixed++; | |
1753 | } | |
1754 | ||
0c80c44b TT |
1755 | if (ext2fs_file_acl_block(fs, &inode) && |
1756 | ((ext2fs_file_acl_block(fs, &inode) < fs->super->s_first_data_block) || | |
1757 | (ext2fs_file_acl_block(fs, &inode) >= ext2fs_blocks_count(fs->super)))) { | |
6c313fd4 | 1758 | if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { |
0c80c44b | 1759 | ext2fs_file_acl_block_set(fs, &inode, 0); |
6c313fd4 TT |
1760 | inode_modified++; |
1761 | } else | |
1762 | not_fixed++; | |
342d847d | 1763 | } |
21c84b71 | 1764 | if (inode.i_dir_acl && |
6c313fd4 TT |
1765 | LINUX_S_ISDIR(inode.i_mode)) { |
1766 | if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { | |
1767 | inode.i_dir_acl = 0; | |
1768 | inode_modified++; | |
1769 | } else | |
1770 | not_fixed++; | |
21c84b71 | 1771 | } |
6c313fd4 | 1772 | |
f3db3566 | 1773 | if (inode_modified) |
08b21301 | 1774 | e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); |
f76344fb | 1775 | if (!not_fixed && ctx->inode_bad_map) |
c5d2f50d | 1776 | ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); |
3839e657 TT |
1777 | return 0; |
1778 | } | |
1779 | ||
50e1e10f TT |
1780 | /* |
1781 | * allocate_dir_block --- this function allocates a new directory | |
1782 | * block for a particular inode; this is done if a directory has | |
1783 | * a "hole" in it, or if a directory has a illegal block number | |
1784 | * that was zeroed out and now needs to be replaced. | |
1785 | */ | |
1b6bf175 | 1786 | static int allocate_dir_block(e2fsck_t ctx, |
6dc64392 | 1787 | struct ext2_db_entry2 *db, |
efc6f628 | 1788 | char *buf EXT2FS_ATTR((unused)), |
54434927 | 1789 | struct problem_context *pctx) |
50e1e10f | 1790 | { |
1b6bf175 | 1791 | ext2_filsys fs = ctx->fs; |
ff11309e | 1792 | blk64_t blk = 0; |
50e1e10f TT |
1793 | char *block; |
1794 | struct ext2_inode inode; | |
50e1e10f | 1795 | |
1b6bf175 | 1796 | if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) |
50e1e10f TT |
1797 | return 1; |
1798 | ||
1799 | /* | |
1800 | * Read the inode and block bitmaps in; we'll be messing with | |
1801 | * them. | |
1802 | */ | |
f8188fff | 1803 | e2fsck_read_bitmaps(ctx); |
efc6f628 | 1804 | |
50e1e10f TT |
1805 | /* |
1806 | * First, find a free block | |
1807 | */ | |
ff11309e DW |
1808 | e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
1809 | pctx->errcode = ext2fs_map_cluster_block(fs, db->ino, &inode, | |
1810 | db->blockcnt, &blk); | |
1811 | if (pctx->errcode || blk == 0) { | |
7b486ec0 DW |
1812 | blk = ext2fs_find_inode_goal(fs, db->ino, &inode, db->blockcnt); |
1813 | pctx->errcode = ext2fs_new_block2(fs, blk, | |
ff11309e DW |
1814 | ctx->block_found_map, &blk); |
1815 | if (pctx->errcode) { | |
1816 | pctx->str = "ext2fs_new_block"; | |
1817 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
1818 | return 1; | |
1819 | } | |
50e1e10f | 1820 | } |
c5d2f50d VAH |
1821 | ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); |
1822 | ext2fs_mark_block_bitmap2(fs->block_map, blk); | |
50e1e10f TT |
1823 | ext2fs_mark_bb_dirty(fs); |
1824 | ||
1825 | /* | |
1826 | * Now let's create the actual data block for the inode | |
1827 | */ | |
1828 | if (db->blockcnt) | |
1b6bf175 | 1829 | pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); |
50e1e10f | 1830 | else |
1b6bf175 TT |
1831 | pctx->errcode = ext2fs_new_dir_block(fs, db->ino, |
1832 | EXT2_ROOT_INO, &block); | |
50e1e10f | 1833 | |
1b6bf175 TT |
1834 | if (pctx->errcode) { |
1835 | pctx->str = "ext2fs_new_dir_block"; | |
1836 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1837 | return 1; |
1838 | } | |
1839 | ||
81683c6a | 1840 | pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino); |
c4e3d3f3 | 1841 | ext2fs_free_mem(&block); |
1b6bf175 TT |
1842 | if (pctx->errcode) { |
1843 | pctx->str = "ext2fs_write_dir_block"; | |
1844 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1845 | return 1; |
1846 | } | |
1847 | ||
1848 | /* | |
1849 | * Update the inode block count | |
1850 | */ | |
1ca1059f | 1851 | ext2fs_iblk_add_blocks(fs, &inode, 1); |
97c607b1 DW |
1852 | if (EXT2_I_SIZE(&inode) < (db->blockcnt+1) * fs->blocksize) { |
1853 | pctx->errcode = ext2fs_inode_size_set(fs, &inode, | |
1854 | (db->blockcnt+1) * fs->blocksize); | |
1855 | if (pctx->errcode) { | |
1856 | pctx->str = "ext2fs_inode_size_set"; | |
1857 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
1858 | return 1; | |
1859 | } | |
1860 | } | |
08b21301 | 1861 | e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
50e1e10f TT |
1862 | |
1863 | /* | |
1864 | * Finally, update the block pointers for the inode | |
1865 | */ | |
1866 | db->blk = blk; | |
2d07b3ad TT |
1867 | pctx->errcode = ext2fs_bmap2(fs, db->ino, &inode, 0, BMAP_SET, |
1868 | db->blockcnt, 0, &blk); | |
1b6bf175 TT |
1869 | if (pctx->errcode) { |
1870 | pctx->str = "ext2fs_block_iterate"; | |
1871 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
1872 | return 1; |
1873 | } | |
1874 | ||
1875 | return 0; | |
1876 | } |