]>
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 | ||
3839e657 TT |
53 | /* |
54 | * Keeps track of how many times an inode is referenced. | |
55 | */ | |
1b6bf175 | 56 | static void deallocate_inode(e2fsck_t ctx, ino_t ino, |
3839e657 | 57 | char* block_buf); |
3839e657 | 58 | static int check_dir_block(ext2_filsys fs, |
21c84b71 | 59 | struct ext2_db_entry *dir_blocks_info, |
54dc7ca2 | 60 | void *priv_data); |
1b6bf175 | 61 | static int allocate_dir_block(e2fsck_t ctx, |
21c84b71 TT |
62 | struct ext2_db_entry *dir_blocks_info, |
63 | char *buf, struct problem_context *pctx); | |
50e1e10f TT |
64 | static int update_dir_block(ext2_filsys fs, |
65 | blk_t *block_nr, | |
66 | int blockcnt, | |
54dc7ca2 | 67 | void *priv_data); |
3839e657 | 68 | |
21c84b71 TT |
69 | struct check_dir_struct { |
70 | char *buf; | |
71 | struct problem_context pctx; | |
f8188fff | 72 | int count, max; |
1b6bf175 | 73 | e2fsck_t ctx; |
21c84b71 TT |
74 | }; |
75 | ||
08b21301 | 76 | void e2fsck_pass2(e2fsck_t ctx) |
3839e657 | 77 | { |
1b6bf175 | 78 | ext2_filsys fs = ctx->fs; |
3839e657 | 79 | char *buf; |
8bf191e8 | 80 | #ifdef RESOURCE_TRACK |
3839e657 | 81 | struct resource_track rtrack; |
8bf191e8 | 82 | #endif |
21c84b71 | 83 | struct dir_info *dir; |
21c84b71 TT |
84 | struct check_dir_struct cd; |
85 | ||
8bf191e8 | 86 | #ifdef RESOURCE_TRACK |
3839e657 | 87 | init_resource_track(&rtrack); |
8bf191e8 | 88 | #endif |
3839e657 | 89 | |
1b6bf175 TT |
90 | clear_problem_context(&cd.pctx); |
91 | ||
3839e657 TT |
92 | #ifdef MTRACE |
93 | mtrace_print("Pass 2"); | |
94 | #endif | |
95 | ||
1b6bf175 TT |
96 | if (!(ctx->options & E2F_OPT_PREEN)) |
97 | fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx); | |
98 | ||
99 | cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT, | |
100 | 0, ctx->inode_link_info, | |
101 | &ctx->inode_count); | |
102 | if (cd.pctx.errcode) { | |
103 | fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx); | |
08b21301 TT |
104 | ctx->flags |= E2F_FLAG_ABORT; |
105 | return; | |
21c84b71 | 106 | } |
54dc7ca2 TT |
107 | buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize, |
108 | "directory scan buffer"); | |
3839e657 | 109 | |
21c84b71 TT |
110 | /* |
111 | * Set up the parent pointer for the root directory, if | |
112 | * present. (If the root directory is not present, we will | |
113 | * create it in pass 3.) | |
114 | */ | |
08b21301 | 115 | dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO); |
21c84b71 TT |
116 | if (dir) |
117 | dir->parent = EXT2_ROOT_INO; | |
118 | ||
119 | cd.buf = buf; | |
1b6bf175 | 120 | cd.ctx = ctx; |
f75c28de | 121 | cd.count = 1; |
f8188fff | 122 | cd.max = ext2fs_dblist_count(fs->dblist); |
f75c28de TT |
123 | |
124 | if (ctx->progress) | |
125 | (void) (ctx->progress)(ctx, 2, 0, cd.max); | |
21c84b71 | 126 | |
1b6bf175 TT |
127 | cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block, |
128 | &cd); | |
a02ce9df | 129 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 130 | return; |
1b6bf175 TT |
131 | if (cd.pctx.errcode) { |
132 | fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx); | |
08b21301 TT |
133 | ctx->flags |= E2F_FLAG_ABORT; |
134 | return; | |
7ac02a5e TT |
135 | } |
136 | ||
08b21301 | 137 | ext2fs_free_mem((void **) &buf); |
21c84b71 TT |
138 | ext2fs_free_dblist(fs->dblist); |
139 | ||
1b6bf175 TT |
140 | if (ctx->inode_bad_map) { |
141 | ext2fs_free_inode_bitmap(ctx->inode_bad_map); | |
142 | ctx->inode_bad_map = 0; | |
3839e657 | 143 | } |
aa4115a4 TT |
144 | if (ctx->inode_reg_map) { |
145 | ext2fs_free_inode_bitmap(ctx->inode_reg_map); | |
146 | ctx->inode_reg_map = 0; | |
147 | } | |
8bf191e8 | 148 | #ifdef RESOURCE_TRACK |
5596defa TT |
149 | if (ctx->options & E2F_OPT_TIME2) { |
150 | e2fsck_clear_progbar(ctx); | |
1b6bf175 | 151 | print_resource_track("Pass 2", &rtrack); |
5596defa | 152 | } |
8bf191e8 | 153 | #endif |
3839e657 TT |
154 | } |
155 | ||
156 | /* | |
157 | * Make sure the first entry in the directory is '.', and that the | |
158 | * directory entry is sane. | |
159 | */ | |
1b6bf175 | 160 | static int check_dot(e2fsck_t ctx, |
3839e657 | 161 | struct ext2_dir_entry *dirent, |
21c84b71 | 162 | ino_t ino, struct problem_context *pctx) |
3839e657 TT |
163 | { |
164 | struct ext2_dir_entry *nextdir; | |
165 | int status = 0; | |
166 | int created = 0; | |
167 | int new_len; | |
21c84b71 | 168 | int problem = 0; |
3839e657 | 169 | |
21c84b71 TT |
170 | if (!dirent->inode) |
171 | problem = PR_2_MISSING_DOT; | |
b6f79831 | 172 | else if (((dirent->name_len & 0xFF) != 1) || |
21c84b71 TT |
173 | (dirent->name[0] != '.')) |
174 | problem = PR_2_1ST_NOT_DOT; | |
175 | else if (dirent->name[1] != '\0') | |
176 | problem = PR_2_DOT_NULL_TERM; | |
177 | ||
178 | if (problem) { | |
1b6bf175 | 179 | if (fix_problem(ctx, problem, pctx)) { |
21c84b71 TT |
180 | if (dirent->rec_len < 12) |
181 | dirent->rec_len = 12; | |
3839e657 TT |
182 | dirent->inode = ino; |
183 | dirent->name_len = 1; | |
184 | dirent->name[0] = '.'; | |
21c84b71 | 185 | dirent->name[1] = '\0'; |
3839e657 TT |
186 | status = 1; |
187 | created = 1; | |
3839e657 TT |
188 | } |
189 | } | |
3839e657 | 190 | if (dirent->inode != ino) { |
1b6bf175 | 191 | if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { |
3839e657 TT |
192 | dirent->inode = ino; |
193 | status = 1; | |
21c84b71 | 194 | } |
3839e657 TT |
195 | } |
196 | if (dirent->rec_len > 12) { | |
197 | new_len = dirent->rec_len - 12; | |
198 | if (new_len > 12) { | |
3839e657 | 199 | if (created || |
f8188fff | 200 | fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { |
3839e657 TT |
201 | nextdir = (struct ext2_dir_entry *) |
202 | ((char *) dirent + 12); | |
203 | dirent->rec_len = 12; | |
204 | nextdir->rec_len = new_len; | |
205 | nextdir->inode = 0; | |
206 | nextdir->name_len = 0; | |
207 | status = 1; | |
208 | } | |
209 | } | |
210 | } | |
211 | return status; | |
212 | } | |
213 | ||
214 | /* | |
215 | * Make sure the second entry in the directory is '..', and that the | |
216 | * directory entry is sane. We do not check the inode number of '..' | |
217 | * here; this gets done in pass 3. | |
218 | */ | |
1b6bf175 | 219 | static int check_dotdot(e2fsck_t ctx, |
3839e657 | 220 | struct ext2_dir_entry *dirent, |
21c84b71 | 221 | struct dir_info *dir, struct problem_context *pctx) |
3839e657 | 222 | { |
21c84b71 | 223 | int problem = 0; |
3839e657 | 224 | |
21c84b71 TT |
225 | if (!dirent->inode) |
226 | problem = PR_2_MISSING_DOT_DOT; | |
b6f79831 | 227 | else if (((dirent->name_len & 0xFF) != 2) || |
21c84b71 TT |
228 | (dirent->name[0] != '.') || |
229 | (dirent->name[1] != '.')) | |
230 | problem = PR_2_2ND_NOT_DOT_DOT; | |
231 | else if (dirent->name[2] != '\0') | |
232 | problem = PR_2_DOT_DOT_NULL_TERM; | |
233 | ||
234 | if (problem) { | |
1b6bf175 | 235 | if (fix_problem(ctx, problem, pctx)) { |
21c84b71 TT |
236 | if (dirent->rec_len < 12) |
237 | dirent->rec_len = 12; | |
3839e657 TT |
238 | /* |
239 | * Note: we don't have the parent inode just | |
240 | * yet, so we will fill it in with the root | |
241 | * inode. This will get fixed in pass 3. | |
242 | */ | |
243 | dirent->inode = EXT2_ROOT_INO; | |
244 | dirent->name_len = 2; | |
245 | dirent->name[0] = '.'; | |
246 | dirent->name[1] = '.'; | |
21c84b71 | 247 | dirent->name[2] = '\0'; |
3839e657 | 248 | return 1; |
21c84b71 | 249 | } |
3839e657 TT |
250 | return 0; |
251 | } | |
3839e657 TT |
252 | dir->dotdot = dirent->inode; |
253 | return 0; | |
254 | } | |
255 | ||
256 | /* | |
257 | * Check to make sure a directory entry doesn't contain any illegal | |
258 | * characters. | |
259 | */ | |
1b6bf175 | 260 | static int check_name(e2fsck_t ctx, |
3839e657 | 261 | struct ext2_dir_entry *dirent, |
21c84b71 | 262 | ino_t dir_ino, struct problem_context *pctx) |
3839e657 TT |
263 | { |
264 | int i; | |
265 | int fixup = -1; | |
3839e657 | 266 | int ret = 0; |
3839e657 | 267 | |
b6f79831 | 268 | for ( i = 0; i < (dirent->name_len & 0xFF); i++) { |
3839e657 TT |
269 | if (dirent->name[i] == '/' || dirent->name[i] == '\0') { |
270 | if (fixup < 0) { | |
1b6bf175 | 271 | fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx); |
3839e657 TT |
272 | } |
273 | if (fixup) { | |
274 | dirent->name[i] = '.'; | |
275 | ret = 1; | |
21c84b71 | 276 | } |
3839e657 TT |
277 | } |
278 | } | |
279 | return ret; | |
280 | } | |
281 | ||
aa4115a4 TT |
282 | /* |
283 | * Check the directory filetype (if present) | |
284 | */ | |
285 | static _INLINE_ int check_filetype(e2fsck_t ctx, | |
286 | struct ext2_dir_entry *dirent, | |
287 | ino_t dir_ino, struct problem_context *pctx) | |
288 | { | |
289 | int filetype = dirent->name_len >> 8; | |
290 | int should_be = EXT2_FT_UNKNOWN; | |
291 | struct ext2_inode inode; | |
292 | ||
293 | if (!(ctx->fs->super->s_feature_incompat & | |
294 | EXT2_FEATURE_INCOMPAT_FILETYPE)) | |
295 | return 0; | |
296 | ||
297 | if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) { | |
298 | should_be = EXT2_FT_DIR; | |
299 | } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map, | |
300 | dirent->inode)) { | |
301 | should_be = EXT2_FT_REG_FILE; | |
302 | } else if (ctx->inode_bad_map && | |
303 | ext2fs_test_inode_bitmap(ctx->inode_bad_map, | |
304 | dirent->inode)) | |
305 | should_be = 0; | |
306 | else { | |
307 | e2fsck_read_inode(ctx, dirent->inode, &inode, | |
308 | "check_filetype"); | |
309 | if (LINUX_S_ISCHR (inode.i_mode)) | |
310 | should_be = EXT2_FT_CHRDEV; | |
311 | else if (LINUX_S_ISBLK (inode.i_mode)) | |
312 | should_be = EXT2_FT_BLKDEV; | |
313 | else if (LINUX_S_ISLNK (inode.i_mode)) | |
314 | should_be = EXT2_FT_SYMLINK; | |
315 | else if (LINUX_S_ISFIFO (inode.i_mode)) | |
316 | should_be = EXT2_FT_FIFO; | |
317 | else if (LINUX_S_ISSOCK (inode.i_mode)) | |
318 | should_be = EXT2_FT_SOCK; | |
319 | } | |
320 | if (filetype == should_be) | |
321 | return 0; | |
322 | pctx->num = should_be; | |
323 | ||
324 | if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE, | |
325 | pctx) == 0) | |
326 | return 0; | |
327 | ||
328 | dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8; | |
329 | return 1; | |
330 | } | |
331 | ||
332 | ||
3839e657 | 333 | static int check_dir_block(ext2_filsys fs, |
21c84b71 | 334 | struct ext2_db_entry *db, |
54dc7ca2 | 335 | void *priv_data) |
3839e657 TT |
336 | { |
337 | struct dir_info *subdir, *dir; | |
338 | struct ext2_dir_entry *dirent; | |
3839e657 TT |
339 | int offset = 0; |
340 | int dir_modified = 0; | |
21c84b71 | 341 | int dot_state; |
3839e657 TT |
342 | blk_t block_nr = db->blk; |
343 | ino_t ino = db->ino; | |
21c84b71 | 344 | __u16 links; |
54dc7ca2 | 345 | struct check_dir_struct *cd; |
1b6bf175 TT |
346 | char *buf; |
347 | e2fsck_t ctx; | |
348 | int problem; | |
349 | ||
54dc7ca2 | 350 | cd = (struct check_dir_struct *) priv_data; |
1b6bf175 TT |
351 | buf = cd->buf; |
352 | ctx = cd->ctx; | |
f8188fff TT |
353 | |
354 | if (ctx->progress) | |
a02ce9df TT |
355 | if ((ctx->progress)(ctx, 2, cd->count++, cd->max)) |
356 | return DIRENT_ABORT; | |
21c84b71 | 357 | |
3839e657 TT |
358 | /* |
359 | * Make sure the inode is still in use (could have been | |
360 | * deleted in the duplicate/bad blocks pass. | |
361 | */ | |
1b6bf175 | 362 | if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) |
3839e657 | 363 | return 0; |
50e1e10f | 364 | |
21c84b71 TT |
365 | cd->pctx.ino = ino; |
366 | cd->pctx.blk = block_nr; | |
367 | cd->pctx.blkcount = db->blockcnt; | |
368 | cd->pctx.ino2 = 0; | |
369 | cd->pctx.dirent = 0; | |
370 | cd->pctx.num = 0; | |
371 | ||
50e1e10f | 372 | if (db->blk == 0) { |
1b6bf175 | 373 | if (allocate_dir_block(ctx, db, buf, &cd->pctx)) |
50e1e10f TT |
374 | return 0; |
375 | block_nr = db->blk; | |
376 | } | |
3839e657 TT |
377 | |
378 | if (db->blockcnt) | |
379 | dot_state = 2; | |
380 | else | |
381 | dot_state = 0; | |
382 | ||
383 | #if 0 | |
f3db3566 | 384 | printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr, |
3839e657 TT |
385 | db->blockcnt, ino); |
386 | #endif | |
387 | ||
1b6bf175 TT |
388 | cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf); |
389 | if (cd->pctx.errcode) { | |
08b21301 TT |
390 | if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) { |
391 | ctx->flags |= E2F_FLAG_ABORT; | |
392 | return DIRENT_ABORT; | |
393 | } | |
1b6bf175 | 394 | memset(buf, 0, fs->blocksize); |
3839e657 TT |
395 | } |
396 | ||
397 | do { | |
398 | dot_state++; | |
1b6bf175 | 399 | problem = 0; |
3839e657 | 400 | dirent = (struct ext2_dir_entry *) (buf + offset); |
21c84b71 TT |
401 | cd->pctx.dirent = dirent; |
402 | cd->pctx.num = offset; | |
3839e657 TT |
403 | if (((offset + dirent->rec_len) > fs->blocksize) || |
404 | (dirent->rec_len < 8) || | |
21c84b71 | 405 | ((dirent->rec_len % 4) != 0) || |
b6f79831 | 406 | (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) { |
1b6bf175 | 407 | if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) { |
3839e657 TT |
408 | dirent->rec_len = fs->blocksize - offset; |
409 | dirent->name_len = 0; | |
410 | dirent->inode = 0; | |
411 | dir_modified++; | |
21c84b71 | 412 | } else |
3839e657 | 413 | return DIRENT_ABORT; |
3839e657 | 414 | } |
b6f79831 | 415 | if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) { |
1b6bf175 | 416 | if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) { |
50e1e10f TT |
417 | dirent->name_len = EXT2_NAME_LEN; |
418 | dir_modified++; | |
419 | } | |
50e1e10f TT |
420 | } |
421 | ||
3839e657 | 422 | if (dot_state == 1) { |
1b6bf175 | 423 | if (check_dot(ctx, dirent, ino, &cd->pctx)) |
3839e657 TT |
424 | dir_modified++; |
425 | } else if (dot_state == 2) { | |
08b21301 | 426 | dir = e2fsck_get_dir_info(ctx, ino); |
3839e657 | 427 | if (!dir) { |
1b6bf175 | 428 | fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); |
08b21301 TT |
429 | ctx->flags |= E2F_FLAG_ABORT; |
430 | return DIRENT_ABORT; | |
3839e657 | 431 | } |
1b6bf175 | 432 | if (check_dotdot(ctx, dirent, dir, &cd->pctx)) |
3839e657 TT |
433 | dir_modified++; |
434 | } else if (dirent->inode == ino) { | |
1b6bf175 TT |
435 | problem = PR_2_LINK_DOT; |
436 | if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) { | |
3839e657 TT |
437 | dirent->inode = 0; |
438 | dir_modified++; | |
21c84b71 | 439 | goto next; |
3839e657 TT |
440 | } |
441 | } | |
442 | if (!dirent->inode) | |
443 | goto next; | |
444 | ||
3839e657 TT |
445 | /* |
446 | * Make sure the inode listed is a legal one. | |
447 | */ | |
448 | if (((dirent->inode != EXT2_ROOT_INO) && | |
7f88b043 | 449 | (dirent->inode < EXT2_FIRST_INODE(fs->super))) || |
3839e657 | 450 | (dirent->inode > fs->super->s_inodes_count)) { |
1b6bf175 TT |
451 | problem = PR_2_BAD_INO; |
452 | } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, | |
3839e657 | 453 | dirent->inode))) { |
1b6bf175 TT |
454 | /* |
455 | * If the inode is unused, offer to clear it. | |
456 | */ | |
457 | problem = PR_2_UNUSED_INODE; | |
458 | } else if (ctx->inode_bb_map && | |
459 | (ext2fs_test_inode_bitmap(ctx->inode_bb_map, | |
460 | dirent->inode))) { | |
461 | /* | |
462 | * If the inode is in a bad block, offer to | |
463 | * clear it. | |
464 | */ | |
465 | problem = PR_2_BB_INODE; | |
466 | } else if ((dot_state > 2) && | |
b6f79831 | 467 | ((dirent->name_len & 0xFF) == 1) && |
1b6bf175 TT |
468 | (dirent->name[0] == '.')) { |
469 | /* | |
470 | * If there's a '.' entry in anything other | |
471 | * than the first directory entry, it's a | |
472 | * duplicate entry that should be removed. | |
473 | */ | |
474 | problem = PR_2_DUP_DOT; | |
475 | } else if ((dot_state > 2) && | |
b6f79831 | 476 | ((dirent->name_len & 0xFF) == 2) && |
1b6bf175 TT |
477 | (dirent->name[0] == '.') && |
478 | (dirent->name[1] == '.')) { | |
479 | /* | |
480 | * If there's a '..' entry in anything other | |
481 | * than the second directory entry, it's a | |
482 | * duplicate entry that should be removed. | |
483 | */ | |
484 | problem = PR_2_DUP_DOT_DOT; | |
485 | } else if ((dot_state > 2) && | |
486 | (dirent->inode == EXT2_ROOT_INO)) { | |
487 | /* | |
488 | * Don't allow links to the root directory. | |
489 | * We check this specially to make sure we | |
490 | * catch this error case even if the root | |
491 | * directory hasn't been created yet. | |
492 | */ | |
493 | problem = PR_2_LINK_ROOT; | |
21c84b71 TT |
494 | } |
495 | ||
1b6bf175 TT |
496 | if (problem) { |
497 | if (fix_problem(ctx, problem, &cd->pctx)) { | |
21c84b71 TT |
498 | dirent->inode = 0; |
499 | dir_modified++; | |
500 | goto next; | |
1b6bf175 TT |
501 | } else { |
502 | ext2fs_unmark_valid(fs); | |
503 | if (problem == PR_2_BAD_INO) | |
504 | goto next; | |
21c84b71 | 505 | } |
3839e657 TT |
506 | } |
507 | ||
508 | /* | |
509 | * If the inode was marked as having bad fields in | |
510 | * pass1, process it and offer to fix/clear it. | |
511 | * (We wait until now so that we can display the | |
512 | * pathname to the user.) | |
513 | */ | |
1b6bf175 TT |
514 | if (ctx->inode_bad_map && |
515 | ext2fs_test_inode_bitmap(ctx->inode_bad_map, | |
3839e657 | 516 | dirent->inode)) { |
e72a9ba3 TT |
517 | if (e2fsck_process_bad_inode(ctx, ino, |
518 | dirent->inode)) { | |
3839e657 TT |
519 | dirent->inode = 0; |
520 | dir_modified++; | |
521 | goto next; | |
522 | } | |
a02ce9df | 523 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 524 | return DIRENT_ABORT; |
3839e657 TT |
525 | } |
526 | ||
1b6bf175 TT |
527 | if (check_name(ctx, dirent, ino, &cd->pctx)) |
528 | dir_modified++; | |
529 | ||
aa4115a4 TT |
530 | if (check_filetype(ctx, dirent, ino, &cd->pctx)) |
531 | dir_modified++; | |
532 | ||
3839e657 TT |
533 | /* |
534 | * If this is a directory, then mark its parent in its | |
535 | * dir_info structure. If the parent field is already | |
536 | * filled in, then this directory has more than one | |
537 | * hard link. We assume the first link is correct, | |
538 | * and ask the user if he/she wants to clear this one. | |
539 | */ | |
540 | if ((dot_state > 2) && | |
1b6bf175 | 541 | (ext2fs_test_inode_bitmap(ctx->inode_dir_map, |
3839e657 | 542 | dirent->inode))) { |
08b21301 | 543 | subdir = e2fsck_get_dir_info(ctx, dirent->inode); |
3839e657 | 544 | if (!subdir) { |
1b6bf175 TT |
545 | cd->pctx.ino = dirent->inode; |
546 | fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx); | |
08b21301 TT |
547 | ctx->flags |= E2F_FLAG_ABORT; |
548 | return DIRENT_ABORT; | |
3839e657 TT |
549 | } |
550 | if (subdir->parent) { | |
21c84b71 | 551 | cd->pctx.ino2 = subdir->parent; |
1b6bf175 | 552 | if (fix_problem(ctx, PR_2_LINK_DIR, |
21c84b71 | 553 | &cd->pctx)) { |
3839e657 TT |
554 | dirent->inode = 0; |
555 | dir_modified++; | |
556 | goto next; | |
21c84b71 TT |
557 | } |
558 | cd->pctx.ino2 = 0; | |
559 | } else | |
560 | subdir->parent = ino; | |
3839e657 TT |
561 | } |
562 | ||
1b6bf175 TT |
563 | ext2fs_icount_increment(ctx->inode_count, dirent->inode, |
564 | &links); | |
21c84b71 | 565 | if (links > 1) |
1b6bf175 TT |
566 | ctx->fs_links_count++; |
567 | ctx->fs_total_count++; | |
3839e657 TT |
568 | next: |
569 | offset += dirent->rec_len; | |
570 | } while (offset < fs->blocksize); | |
571 | #if 0 | |
572 | printf("\n"); | |
573 | #endif | |
574 | if (offset != fs->blocksize) { | |
1b6bf175 TT |
575 | cd->pctx.num = dirent->rec_len - fs->blocksize + offset; |
576 | if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) { | |
577 | dirent->rec_len = cd->pctx.num; | |
578 | dir_modified++; | |
579 | } | |
3839e657 TT |
580 | } |
581 | if (dir_modified) { | |
1b6bf175 TT |
582 | cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf); |
583 | if (cd->pctx.errcode) { | |
08b21301 TT |
584 | if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, |
585 | &cd->pctx)) { | |
586 | ctx->flags |= E2F_FLAG_ABORT; | |
587 | return DIRENT_ABORT; | |
588 | } | |
3839e657 TT |
589 | } |
590 | ext2fs_mark_changed(fs); | |
591 | } | |
592 | return 0; | |
593 | } | |
594 | ||
595 | /* | |
596 | * This function is called to deallocate a block, and is an interator | |
597 | * functioned called by deallocate inode via ext2fs_iterate_block(). | |
598 | */ | |
599 | static int deallocate_inode_block(ext2_filsys fs, | |
600 | blk_t *block_nr, | |
601 | int blockcnt, | |
54dc7ca2 | 602 | void *priv_data) |
3839e657 | 603 | { |
54dc7ca2 | 604 | e2fsck_t ctx = (e2fsck_t) priv_data; |
1b6bf175 | 605 | |
3839e657 TT |
606 | if (!*block_nr) |
607 | return 0; | |
1b6bf175 | 608 | ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); |
f3db3566 | 609 | ext2fs_unmark_block_bitmap(fs->block_map, *block_nr); |
3839e657 TT |
610 | return 0; |
611 | } | |
612 | ||
613 | /* | |
614 | * This fuction deallocates an inode | |
615 | */ | |
1b6bf175 | 616 | static void deallocate_inode(e2fsck_t ctx, ino_t ino, |
3839e657 TT |
617 | char* block_buf) |
618 | { | |
1b6bf175 | 619 | ext2_filsys fs = ctx->fs; |
3839e657 | 620 | struct ext2_inode inode; |
1b6bf175 TT |
621 | struct problem_context pctx; |
622 | ||
623 | ext2fs_icount_store(ctx->inode_link_info, ino, 0); | |
08b21301 | 624 | e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); |
3839e657 TT |
625 | inode.i_links_count = 0; |
626 | inode.i_dtime = time(0); | |
08b21301 | 627 | e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode"); |
1b6bf175 TT |
628 | clear_problem_context(&pctx); |
629 | pctx.ino = ino; | |
f3db3566 | 630 | |
3839e657 TT |
631 | /* |
632 | * Fix up the bitmaps... | |
633 | */ | |
f8188fff | 634 | e2fsck_read_bitmaps(ctx); |
1b6bf175 TT |
635 | ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); |
636 | ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); | |
637 | if (ctx->inode_bad_map) | |
638 | ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); | |
f3db3566 | 639 | ext2fs_unmark_inode_bitmap(fs->inode_map, ino); |
3839e657 TT |
640 | ext2fs_mark_ib_dirty(fs); |
641 | ||
21c84b71 | 642 | if (!ext2fs_inode_has_valid_blocks(&inode)) |
3839e657 TT |
643 | return; |
644 | ||
645 | ext2fs_mark_bb_dirty(fs); | |
1b6bf175 TT |
646 | pctx.errcode = ext2fs_block_iterate(fs, ino, 0, block_buf, |
647 | deallocate_inode_block, ctx); | |
648 | if (pctx.errcode) { | |
649 | fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); | |
08b21301 TT |
650 | ctx->flags |= E2F_FLAG_ABORT; |
651 | return; | |
1b6bf175 | 652 | } |
3839e657 TT |
653 | } |
654 | ||
e72a9ba3 | 655 | extern int e2fsck_process_bad_inode(e2fsck_t ctx, ino_t dir, ino_t ino) |
3839e657 | 656 | { |
1b6bf175 | 657 | ext2_filsys fs = ctx->fs; |
3839e657 | 658 | struct ext2_inode inode; |
3839e657 | 659 | int inode_modified = 0; |
1e3472c5 | 660 | unsigned char *frag, *fsize; |
21c84b71 | 661 | struct problem_context pctx; |
08b21301 | 662 | int problem = 0; |
3839e657 | 663 | |
08b21301 | 664 | e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); |
21c84b71 TT |
665 | |
666 | clear_problem_context(&pctx); | |
667 | pctx.ino = ino; | |
668 | pctx.dir = dir; | |
669 | pctx.inode = &inode; | |
670 | ||
50e1e10f TT |
671 | if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && |
672 | !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && | |
673 | !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && | |
08b21301 TT |
674 | !(LINUX_S_ISSOCK(inode.i_mode))) |
675 | problem = PR_2_BAD_MODE; | |
7cf73dcd TT |
676 | |
677 | if (LINUX_S_ISCHR(inode.i_mode) | |
08b21301 TT |
678 | && !e2fsck_pass1_check_device_inode(&inode)) |
679 | problem = PR_2_BAD_CHAR_DEV; | |
7cf73dcd TT |
680 | |
681 | if (LINUX_S_ISBLK(inode.i_mode) | |
08b21301 TT |
682 | && !e2fsck_pass1_check_device_inode(&inode)) |
683 | problem = PR_2_BAD_BLOCK_DEV; | |
684 | ||
1dde43f0 TT |
685 | if (LINUX_S_ISFIFO(inode.i_mode) |
686 | && !e2fsck_pass1_check_device_inode(&inode)) | |
687 | problem = PR_2_BAD_FIFO; | |
688 | ||
689 | if (LINUX_S_ISSOCK(inode.i_mode) | |
690 | && !e2fsck_pass1_check_device_inode(&inode)) | |
691 | problem = PR_2_BAD_SOCKET; | |
692 | ||
08b21301 TT |
693 | if (problem) { |
694 | if (fix_problem(ctx, problem, &pctx)) { | |
1b6bf175 | 695 | deallocate_inode(ctx, ino, 0); |
a02ce9df | 696 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
08b21301 | 697 | return 0; |
7cf73dcd TT |
698 | return 1; |
699 | } | |
08b21301 | 700 | problem = 0; |
7cf73dcd | 701 | } |
7cf73dcd | 702 | |
21c84b71 | 703 | if (inode.i_faddr && |
1b6bf175 | 704 | fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { |
21c84b71 TT |
705 | inode.i_faddr = 0; |
706 | inode_modified++; | |
3839e657 | 707 | } |
1e3472c5 TT |
708 | |
709 | switch (fs->super->s_creator_os) { | |
710 | case EXT2_OS_LINUX: | |
711 | frag = &inode.osd2.linux2.l_i_frag; | |
712 | fsize = &inode.osd2.linux2.l_i_fsize; | |
713 | break; | |
714 | case EXT2_OS_HURD: | |
715 | frag = &inode.osd2.hurd2.h_i_frag; | |
716 | fsize = &inode.osd2.hurd2.h_i_fsize; | |
717 | break; | |
718 | case EXT2_OS_MASIX: | |
719 | frag = &inode.osd2.masix2.m_i_frag; | |
720 | fsize = &inode.osd2.masix2.m_i_fsize; | |
721 | break; | |
722 | default: | |
723 | frag = fsize = 0; | |
724 | } | |
21c84b71 TT |
725 | if (frag && *frag) { |
726 | pctx.num = *frag; | |
1b6bf175 | 727 | if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { |
21c84b71 TT |
728 | *frag = 0; |
729 | inode_modified++; | |
730 | } | |
731 | pctx.num = 0; | |
732 | } | |
733 | if (fsize && *fsize) { | |
734 | pctx.num = *fsize; | |
1b6bf175 | 735 | if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { |
21c84b71 TT |
736 | *fsize = 0; |
737 | inode_modified++; | |
738 | } | |
739 | pctx.num = 0; | |
740 | } | |
741 | ||
742 | if (inode.i_file_acl && | |
1b6bf175 | 743 | fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { |
21c84b71 TT |
744 | inode.i_file_acl = 0; |
745 | inode_modified++; | |
746 | } | |
747 | if (inode.i_dir_acl && | |
246501c6 | 748 | LINUX_S_ISDIR(inode.i_mode) && |
1b6bf175 | 749 | fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { |
21c84b71 TT |
750 | inode.i_dir_acl = 0; |
751 | inode_modified++; | |
752 | } | |
f3db3566 | 753 | if (inode_modified) |
08b21301 | 754 | e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); |
3839e657 TT |
755 | return 0; |
756 | } | |
757 | ||
50e1e10f TT |
758 | |
759 | /* | |
760 | * allocate_dir_block --- this function allocates a new directory | |
761 | * block for a particular inode; this is done if a directory has | |
762 | * a "hole" in it, or if a directory has a illegal block number | |
763 | * that was zeroed out and now needs to be replaced. | |
764 | */ | |
1b6bf175 | 765 | static int allocate_dir_block(e2fsck_t ctx, |
21c84b71 TT |
766 | struct ext2_db_entry *db, |
767 | char *buf, struct problem_context *pctx) | |
50e1e10f | 768 | { |
1b6bf175 | 769 | ext2_filsys fs = ctx->fs; |
50e1e10f TT |
770 | blk_t blk; |
771 | char *block; | |
772 | struct ext2_inode inode; | |
50e1e10f | 773 | |
1b6bf175 | 774 | if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0) |
50e1e10f TT |
775 | return 1; |
776 | ||
777 | /* | |
778 | * Read the inode and block bitmaps in; we'll be messing with | |
779 | * them. | |
780 | */ | |
f8188fff | 781 | e2fsck_read_bitmaps(ctx); |
50e1e10f TT |
782 | |
783 | /* | |
784 | * First, find a free block | |
785 | */ | |
1b6bf175 TT |
786 | pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); |
787 | if (pctx->errcode) { | |
788 | pctx->str = "ext2fs_new_block"; | |
789 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
790 | return 1; |
791 | } | |
1b6bf175 | 792 | ext2fs_mark_block_bitmap(ctx->block_found_map, blk); |
50e1e10f TT |
793 | ext2fs_mark_block_bitmap(fs->block_map, blk); |
794 | ext2fs_mark_bb_dirty(fs); | |
795 | ||
796 | /* | |
797 | * Now let's create the actual data block for the inode | |
798 | */ | |
799 | if (db->blockcnt) | |
1b6bf175 | 800 | pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block); |
50e1e10f | 801 | else |
1b6bf175 TT |
802 | pctx->errcode = ext2fs_new_dir_block(fs, db->ino, |
803 | EXT2_ROOT_INO, &block); | |
50e1e10f | 804 | |
1b6bf175 TT |
805 | if (pctx->errcode) { |
806 | pctx->str = "ext2fs_new_dir_block"; | |
807 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
808 | return 1; |
809 | } | |
810 | ||
1b6bf175 | 811 | pctx->errcode = ext2fs_write_dir_block(fs, blk, block); |
08b21301 | 812 | ext2fs_free_mem((void **) &block); |
1b6bf175 TT |
813 | if (pctx->errcode) { |
814 | pctx->str = "ext2fs_write_dir_block"; | |
815 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
816 | return 1; |
817 | } | |
818 | ||
819 | /* | |
820 | * Update the inode block count | |
821 | */ | |
08b21301 | 822 | e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
50e1e10f TT |
823 | inode.i_blocks += fs->blocksize / 512; |
824 | if (inode.i_size < (db->blockcnt+1) * fs->blocksize) | |
825 | inode.i_size = (db->blockcnt+1) * fs->blocksize; | |
08b21301 | 826 | e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block"); |
50e1e10f TT |
827 | |
828 | /* | |
829 | * Finally, update the block pointers for the inode | |
830 | */ | |
831 | db->blk = blk; | |
1b6bf175 | 832 | pctx->errcode = ext2fs_block_iterate(fs, db->ino, BLOCK_FLAG_HOLE, |
50e1e10f | 833 | 0, update_dir_block, db); |
1b6bf175 TT |
834 | if (pctx->errcode) { |
835 | pctx->str = "ext2fs_block_iterate"; | |
836 | fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx); | |
50e1e10f TT |
837 | return 1; |
838 | } | |
839 | ||
840 | return 0; | |
841 | } | |
842 | ||
843 | /* | |
844 | * This is a helper function for allocate_dir_block(). | |
845 | */ | |
846 | static int update_dir_block(ext2_filsys fs, | |
847 | blk_t *block_nr, | |
848 | int blockcnt, | |
54dc7ca2 | 849 | void *priv_data) |
50e1e10f | 850 | { |
54dc7ca2 | 851 | struct ext2_db_entry *db; |
50e1e10f | 852 | |
54dc7ca2 | 853 | db = (struct ext2_db_entry *) priv_data; |
50e1e10f TT |
854 | if (db->blockcnt == blockcnt) { |
855 | *block_nr = db->blk; | |
856 | return BLOCK_CHANGED; | |
857 | } | |
858 | return 0; | |
859 | } |