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