blob: 788da4df4fdc3b4995812d86e8d37c6c1805db27 [file] [log] [blame]
Mike Frysinger51a43b42005-09-24 07:11:16 +00001/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002 * e2fsck
3 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 * This file may be
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006 * redistributed under the terms of the GNU Public License.
7 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008 *
9 * Dictionary Abstract Data Type
10 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
11 * Free Software License:
12 * All rights are reserved by the author, with the following exceptions:
13 * Permission is granted to freely reproduce and distribute this software,
14 * possibly in exchange for a fee, provided that this copyright notice appears
15 * intact. Permission is also granted to adapt this software to produce
16 * derivative works, as long as the modified versions carry this copyright
17 * notice and additional notices stating that the work has been modified.
18 * This source code may be translated into executable form and incorporated
19 * into proprietary software; there is no requirement for such software to
20 * contain a copyright notice related to this source.
21 *
22 * linux/fs/recovery and linux/fs/revoke
23 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
24 *
25 * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
26 *
27 * This file is part of the Linux kernel and is made available under
28 * the terms of the GNU General Public License, version 2, or at your
29 * option, any later version, incorporated herein by reference.
30 *
31 * Journal recovery routines for the generic filesystem journaling code;
32 * part of the ext2fs journaling system.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000033 */
34
35#ifndef _GNU_SOURCE
36#define _GNU_SOURCE 1 /* get strnlen() */
37#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000038
Rob Landley43ac8882006-04-01 00:40:33 +000039#include "e2fsck.h" /*Put all of our defines here to clean things up*/
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000040
41#ifdef __GNUC__
42#define _INLINE_ __inline__
43#define EXT2FS_ATTR(x) __attribute__(x)
44#else
45#define _INLINE_
46#define EXT2FS_ATTR(x)
47#endif
48
49/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000050 * Procedure declarations
51 */
52
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000053static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000054
55/* pass1.c */
56static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000057
58/* pass2.c */
59static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
60 ext2_ino_t ino, char *buf);
61
62/* pass3.c */
63static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
64static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
65 int num, int gauranteed_size);
66static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
67static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
68 int adj);
69
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000070/* rehash.c */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000071static void e2fsck_rehash_directories(e2fsck_t ctx);
72
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000073/* util.c */
74static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
75 const char *description);
76static int ask(e2fsck_t ctx, const char * string, int def);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000077static void e2fsck_read_bitmaps(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000078static void preenhalt(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000079static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
80 struct ext2_inode * inode, const char * proc);
81static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
82 struct ext2_inode * inode, const char * proc);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000083static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
84 const char *name, io_manager manager);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000085
86/* unix.c */
87static void e2fsck_clear_progbar(e2fsck_t ctx);
88static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
89 float percent, unsigned int dpynum);
Rob Landley43ac8882006-04-01 00:40:33 +000090
91
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000092/*
93 * problem.h --- e2fsck problem error codes
94 */
95
96typedef __u32 problem_t;
97
98struct problem_context {
99 errcode_t errcode;
100 ext2_ino_t ino, ino2, dir;
101 struct ext2_inode *inode;
102 struct ext2_dir_entry *dirent;
103 blk_t blk, blk2;
104 e2_blkcnt_t blkcount;
105 int group;
106 __u64 num;
107 const char *str;
108};
109
110/*
111 * We define a set of "latch groups"; these are problems which are
112 * handled as a set. The user answers once for a particular latch
113 * group.
114 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000115#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
116#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
117#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
118#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
119#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
120#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
121#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
122#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
123#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000124#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
125
126#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
127
128/*
129 * Latch group descriptor flags
130 */
131#define PRL_YES 0x0001 /* Answer yes */
132#define PRL_NO 0x0002 /* Answer no */
133#define PRL_LATCHED 0x0004 /* The latch group is latched */
134#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
135
136#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
137
138/*
139 * Pre-Pass 1 errors
140 */
141
142/* Block bitmap not in group */
143#define PR_0_BB_NOT_GROUP 0x000001
144
145/* Inode bitmap not in group */
146#define PR_0_IB_NOT_GROUP 0x000002
147
148/* Inode table not in group */
149#define PR_0_ITABLE_NOT_GROUP 0x000003
150
151/* Superblock corrupt */
152#define PR_0_SB_CORRUPT 0x000004
153
154/* Filesystem size is wrong */
155#define PR_0_FS_SIZE_WRONG 0x000005
156
157/* Fragments not supported */
158#define PR_0_NO_FRAGMENTS 0x000006
159
160/* Bad blocks_per_group */
161#define PR_0_BLOCKS_PER_GROUP 0x000007
162
163/* Bad first_data_block */
164#define PR_0_FIRST_DATA_BLOCK 0x000008
165
166/* Adding UUID to filesystem */
167#define PR_0_ADD_UUID 0x000009
168
169/* Relocate hint */
170#define PR_0_RELOCATE_HINT 0x00000A
171
172/* Miscellaneous superblock corruption */
173#define PR_0_MISC_CORRUPT_SUPER 0x00000B
174
175/* Error determing physical device size of filesystem */
176#define PR_0_GETSIZE_ERROR 0x00000C
177
178/* Inode count in the superblock incorrect */
179#define PR_0_INODE_COUNT_WRONG 0x00000D
180
181/* The Hurd does not support the filetype feature */
182#define PR_0_HURD_CLEAR_FILETYPE 0x00000E
183
184/* Journal inode is invalid */
185#define PR_0_JOURNAL_BAD_INODE 0x00000F
186
187/* The external journal has multiple filesystems (which we can't handle yet) */
188#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010
189
190/* Can't find external journal */
191#define PR_0_CANT_FIND_JOURNAL 0x000011
192
193/* External journal has bad superblock */
194#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012
195
196/* Superblock has a bad journal UUID */
197#define PR_0_JOURNAL_BAD_UUID 0x000013
198
199/* Journal has an unknown superblock type */
200#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014
201
202/* Journal superblock is corrupt */
203#define PR_0_JOURNAL_BAD_SUPER 0x000015
204
205/* Journal superblock is corrupt */
206#define PR_0_JOURNAL_HAS_JOURNAL 0x000016
207
208/* Superblock has recovery flag set but no journal */
209#define PR_0_JOURNAL_RECOVER_SET 0x000017
210
211/* Journal has data, but recovery flag is clear */
212#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018
213
214/* Ask if we should clear the journal */
215#define PR_0_JOURNAL_RESET_JOURNAL 0x000019
216
217/* Filesystem revision is 0, but feature flags are set */
218#define PR_0_FS_REV_LEVEL 0x00001A
219
220/* Clearing orphan inode */
221#define PR_0_ORPHAN_CLEAR_INODE 0x000020
222
223/* Illegal block found in orphaned inode */
224#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021
225
226/* Already cleared block found in orphaned inode */
227#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022
228
229/* Illegal orphan inode in superblock */
230#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023
231
232/* Illegal inode in orphaned inode list */
233#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024
234
235/* Journal has unsupported read-only feature - abort */
236#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025
237
238/* Journal has unsupported incompatible feature - abort */
239#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026
240
241/* Journal has unsupported version number */
242#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027
243
244/* Moving journal to hidden file */
245#define PR_0_MOVE_JOURNAL 0x000028
246
247/* Error moving journal */
248#define PR_0_ERR_MOVE_JOURNAL 0x000029
249
250/* Clearing V2 journal superblock */
251#define PR_0_CLEAR_V2_JOURNAL 0x00002A
252
253/* Run journal anyway */
254#define PR_0_JOURNAL_RUN 0x00002B
255
256/* Run journal anyway by default */
257#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C
258
259/* Backup journal inode blocks */
260#define PR_0_BACKUP_JNL 0x00002D
261
262/* Reserved blocks w/o resize_inode */
263#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E
264
265/* Resize_inode not enabled, but resize inode is non-zero */
266#define PR_0_CLEAR_RESIZE_INODE 0x00002F
267
268/* Resize inode invalid */
269#define PR_0_RESIZE_INODE_INVALID 0x000030
270
271/*
272 * Pass 1 errors
273 */
274
275/* Pass 1: Checking inodes, blocks, and sizes */
276#define PR_1_PASS_HEADER 0x010000
277
278/* Root directory is not an inode */
279#define PR_1_ROOT_NO_DIR 0x010001
280
281/* Root directory has dtime set */
282#define PR_1_ROOT_DTIME 0x010002
283
284/* Reserved inode has bad mode */
285#define PR_1_RESERVED_BAD_MODE 0x010003
286
287/* Deleted inode has zero dtime */
288#define PR_1_ZERO_DTIME 0x010004
289
290/* Inode in use, but dtime set */
291#define PR_1_SET_DTIME 0x010005
292
293/* Zero-length directory */
294#define PR_1_ZERO_LENGTH_DIR 0x010006
295
296/* Block bitmap conflicts with some other fs block */
297#define PR_1_BB_CONFLICT 0x010007
298
299/* Inode bitmap conflicts with some other fs block */
300#define PR_1_IB_CONFLICT 0x010008
301
302/* Inode table conflicts with some other fs block */
303#define PR_1_ITABLE_CONFLICT 0x010009
304
305/* Block bitmap is on a bad block */
306#define PR_1_BB_BAD_BLOCK 0x01000A
307
308/* Inode bitmap is on a bad block */
309#define PR_1_IB_BAD_BLOCK 0x01000B
310
311/* Inode has incorrect i_size */
312#define PR_1_BAD_I_SIZE 0x01000C
313
314/* Inode has incorrect i_blocks */
315#define PR_1_BAD_I_BLOCKS 0x01000D
316
317/* Illegal block number in inode */
318#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E
319
320/* Block number overlaps fs metadata */
321#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F
322
323/* Inode has illegal blocks (latch question) */
324#define PR_1_INODE_BLOCK_LATCH 0x010010
325
326/* Too many bad blocks in inode */
327#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011
328
329/* Illegal block number in bad block inode */
330#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012
331
332/* Bad block inode has illegal blocks (latch question) */
333#define PR_1_INODE_BBLOCK_LATCH 0x010013
334
335/* Duplicate or bad blocks in use! */
336#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014
337
338/* Bad block used as bad block indirect block */
339#define PR_1_BBINODE_BAD_METABLOCK 0x010015
340
341/* Inconsistency can't be fixed prompt */
342#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016
343
344/* Bad primary block */
345#define PR_1_BAD_PRIMARY_BLOCK 0x010017
346
347/* Bad primary block prompt */
348#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018
349
350/* Bad primary superblock */
351#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019
352
353/* Bad primary block group descriptors */
354#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A
355
356/* Bad superblock in group */
357#define PR_1_BAD_SUPERBLOCK 0x01001B
358
359/* Bad block group descriptors in group */
360#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C
361
362/* Block claimed for no reason */
363#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D
364
365/* Error allocating blocks for relocating metadata */
366#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E
367
368/* Error allocating block buffer during relocation process */
369#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F
370
371/* Relocating metadata group information from X to Y */
372#define PR_1_RELOC_FROM_TO 0x010020
373
374/* Relocating metatdata group information to X */
375#define PR_1_RELOC_TO 0x010021
376
377/* Block read error during relocation process */
378#define PR_1_RELOC_READ_ERR 0x010022
379
380/* Block write error during relocation process */
381#define PR_1_RELOC_WRITE_ERR 0x010023
382
383/* Error allocating inode bitmap */
384#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024
385
386/* Error allocating block bitmap */
387#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025
388
389/* Error allocating icount structure */
390#define PR_1_ALLOCATE_ICOUNT 0x010026
391
392/* Error allocating dbcount */
393#define PR_1_ALLOCATE_DBCOUNT 0x010027
394
395/* Error while scanning inodes */
396#define PR_1_ISCAN_ERROR 0x010028
397
398/* Error while iterating over blocks */
399#define PR_1_BLOCK_ITERATE 0x010029
400
401/* Error while storing inode count information */
402#define PR_1_ICOUNT_STORE 0x01002A
403
404/* Error while storing directory block information */
405#define PR_1_ADD_DBLOCK 0x01002B
406
407/* Error while reading inode (for clearing) */
408#define PR_1_READ_INODE 0x01002C
409
410/* Suppress messages prompt */
411#define PR_1_SUPPRESS_MESSAGES 0x01002D
412
413/* Imagic flag set on an inode when filesystem doesn't support it */
414#define PR_1_SET_IMAGIC 0x01002F
415
416/* Immutable flag set on a device or socket inode */
417#define PR_1_SET_IMMUTABLE 0x010030
418
419/* Compression flag set on a non-compressed filesystem */
420#define PR_1_COMPR_SET 0x010031
421
422/* Non-zero size on on device, fifo or socket inode */
423#define PR_1_SET_NONZSIZE 0x010032
424
425/* Filesystem revision is 0, but feature flags are set */
426#define PR_1_FS_REV_LEVEL 0x010033
427
428/* Journal inode not in use, needs clearing */
429#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034
430
431/* Journal inode has wrong mode */
432#define PR_1_JOURNAL_BAD_MODE 0x010035
433
434/* Inode that was part of orphan linked list */
435#define PR_1_LOW_DTIME 0x010036
436
437/* Latch question which asks how to deal with low dtime inodes */
438#define PR_1_ORPHAN_LIST_REFUGEES 0x010037
439
440/* Error allocating refcount structure */
441#define PR_1_ALLOCATE_REFCOUNT 0x010038
442
443/* Error reading Extended Attribute block */
444#define PR_1_READ_EA_BLOCK 0x010039
445
446/* Invalid Extended Attribute block */
447#define PR_1_BAD_EA_BLOCK 0x01003A
448
449/* Error reading Extended Attribute block while fixing refcount -- abort */
450#define PR_1_EXTATTR_READ_ABORT 0x01003B
451
452/* Extended attribute reference count incorrect */
453#define PR_1_EXTATTR_REFCOUNT 0x01003C
454
455/* Error writing Extended Attribute block while fixing refcount */
456#define PR_1_EXTATTR_WRITE 0x01003D
457
458/* Multiple EA blocks not supported */
459#define PR_1_EA_MULTI_BLOCK 0x01003E
460
461/* Error allocating EA region allocation structure */
462#define PR_1_EA_ALLOC_REGION 0x01003F
463
464/* Error EA allocation collision */
465#define PR_1_EA_ALLOC_COLLISION 0x010040
466
467/* Bad extended attribute name */
468#define PR_1_EA_BAD_NAME 0x010041
469
470/* Bad extended attribute value */
471#define PR_1_EA_BAD_VALUE 0x010042
472
473/* Inode too big (latch question) */
474#define PR_1_INODE_TOOBIG 0x010043
475
476/* Directory too big */
477#define PR_1_TOOBIG_DIR 0x010044
478
479/* Regular file too big */
480#define PR_1_TOOBIG_REG 0x010045
481
482/* Symlink too big */
483#define PR_1_TOOBIG_SYMLINK 0x010046
484
485/* INDEX_FL flag set on a non-HTREE filesystem */
486#define PR_1_HTREE_SET 0x010047
487
488/* INDEX_FL flag set on a non-directory */
489#define PR_1_HTREE_NODIR 0x010048
490
491/* Invalid root node in HTREE directory */
492#define PR_1_HTREE_BADROOT 0x010049
493
494/* Unsupported hash version in HTREE directory */
495#define PR_1_HTREE_HASHV 0x01004A
496
497/* Incompatible flag in HTREE root node */
498#define PR_1_HTREE_INCOMPAT 0x01004B
499
500/* HTREE too deep */
501#define PR_1_HTREE_DEPTH 0x01004C
502
503/* Bad block has indirect block that conflicts with filesystem block */
504#define PR_1_BB_FS_BLOCK 0x01004D
505
506/* Resize inode failed */
507#define PR_1_RESIZE_INODE_CREATE 0x01004E
508
509/* inode->i_size is too long */
510#define PR_1_EXTRA_ISIZE 0x01004F
511
512/* attribute name is too long */
513#define PR_1_ATTR_NAME_LEN 0x010050
514
515/* wrong EA value offset */
516#define PR_1_ATTR_VALUE_OFFSET 0x010051
517
518/* wrong EA blocknumber */
519#define PR_1_ATTR_VALUE_BLOCK 0x010052
520
521/* wrong EA value size */
522#define PR_1_ATTR_VALUE_SIZE 0x010053
523
524/* wrong EA hash value */
525#define PR_1_ATTR_HASH 0x010054
526
527/*
528 * Pass 1b errors
529 */
530
531/* Pass 1B: Rescan for duplicate/bad blocks */
532#define PR_1B_PASS_HEADER 0x011000
533
534/* Duplicate/bad block(s) header */
535#define PR_1B_DUP_BLOCK_HEADER 0x011001
536
537/* Duplicate/bad block(s) in inode */
538#define PR_1B_DUP_BLOCK 0x011002
539
540/* Duplicate/bad block(s) end */
541#define PR_1B_DUP_BLOCK_END 0x011003
542
543/* Error while scanning inodes */
544#define PR_1B_ISCAN_ERROR 0x011004
545
546/* Error allocating inode bitmap */
547#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005
548
549/* Error while iterating over blocks */
550#define PR_1B_BLOCK_ITERATE 0x0110006
551
552/* Error adjusting EA refcount */
553#define PR_1B_ADJ_EA_REFCOUNT 0x0110007
554
555
556/* Pass 1C: Scan directories for inodes with dup blocks. */
557#define PR_1C_PASS_HEADER 0x012000
558
559
560/* Pass 1D: Reconciling duplicate blocks */
561#define PR_1D_PASS_HEADER 0x013000
562
563/* File has duplicate blocks */
564#define PR_1D_DUP_FILE 0x013001
565
566/* List of files sharing duplicate blocks */
567#define PR_1D_DUP_FILE_LIST 0x013002
568
569/* File sharing blocks with filesystem metadata */
570#define PR_1D_SHARE_METADATA 0x013003
571
572/* Report of how many duplicate/bad inodes */
573#define PR_1D_NUM_DUP_INODES 0x013004
574
575/* Duplicated blocks already reassigned or cloned. */
576#define PR_1D_DUP_BLOCKS_DEALT 0x013005
577
578/* Clone duplicate/bad blocks? */
579#define PR_1D_CLONE_QUESTION 0x013006
580
581/* Delete file? */
582#define PR_1D_DELETE_QUESTION 0x013007
583
584/* Couldn't clone file (error) */
585#define PR_1D_CLONE_ERROR 0x013008
586
587/*
588 * Pass 2 errors
589 */
590
591/* Pass 2: Checking directory structure */
592#define PR_2_PASS_HEADER 0x020000
593
594/* Bad inode number for '.' */
595#define PR_2_BAD_INODE_DOT 0x020001
596
597/* Directory entry has bad inode number */
598#define PR_2_BAD_INO 0x020002
599
600/* Directory entry has deleted or unused inode */
601#define PR_2_UNUSED_INODE 0x020003
602
603/* Directry entry is link to '.' */
604#define PR_2_LINK_DOT 0x020004
605
606/* Directory entry points to inode now located in a bad block */
607#define PR_2_BB_INODE 0x020005
608
609/* Directory entry contains a link to a directory */
610#define PR_2_LINK_DIR 0x020006
611
612/* Directory entry contains a link to the root directry */
613#define PR_2_LINK_ROOT 0x020007
614
615/* Directory entry has illegal characters in its name */
616#define PR_2_BAD_NAME 0x020008
617
618/* Missing '.' in directory inode */
619#define PR_2_MISSING_DOT 0x020009
620
621/* Missing '..' in directory inode */
622#define PR_2_MISSING_DOT_DOT 0x02000A
623
624/* First entry in directory inode doesn't contain '.' */
625#define PR_2_1ST_NOT_DOT 0x02000B
626
627/* Second entry in directory inode doesn't contain '..' */
628#define PR_2_2ND_NOT_DOT_DOT 0x02000C
629
630/* i_faddr should be zero */
631#define PR_2_FADDR_ZERO 0x02000D
632
633/* i_file_acl should be zero */
634#define PR_2_FILE_ACL_ZERO 0x02000E
635
636/* i_dir_acl should be zero */
637#define PR_2_DIR_ACL_ZERO 0x02000F
638
639/* i_frag should be zero */
640#define PR_2_FRAG_ZERO 0x020010
641
642/* i_fsize should be zero */
643#define PR_2_FSIZE_ZERO 0x020011
644
645/* inode has bad mode */
646#define PR_2_BAD_MODE 0x020012
647
648/* directory corrupted */
649#define PR_2_DIR_CORRUPTED 0x020013
650
651/* filename too long */
652#define PR_2_FILENAME_LONG 0x020014
653
654/* Directory inode has a missing block (hole) */
655#define PR_2_DIRECTORY_HOLE 0x020015
656
657/* '.' is not NULL terminated */
658#define PR_2_DOT_NULL_TERM 0x020016
659
660/* '..' is not NULL terminated */
661#define PR_2_DOT_DOT_NULL_TERM 0x020017
662
663/* Illegal character device in inode */
664#define PR_2_BAD_CHAR_DEV 0x020018
665
666/* Illegal block device in inode */
667#define PR_2_BAD_BLOCK_DEV 0x020019
668
669/* Duplicate '.' entry */
670#define PR_2_DUP_DOT 0x02001A
671
672/* Duplicate '..' entry */
673#define PR_2_DUP_DOT_DOT 0x02001B
674
675/* Internal error: couldn't find dir_info */
676#define PR_2_NO_DIRINFO 0x02001C
677
678/* Final rec_len is wrong */
679#define PR_2_FINAL_RECLEN 0x02001D
680
681/* Error allocating icount structure */
682#define PR_2_ALLOCATE_ICOUNT 0x02001E
683
684/* Error iterating over directory blocks */
685#define PR_2_DBLIST_ITERATE 0x02001F
686
687/* Error reading directory block */
688#define PR_2_READ_DIRBLOCK 0x020020
689
690/* Error writing directory block */
691#define PR_2_WRITE_DIRBLOCK 0x020021
692
693/* Error allocating new directory block */
694#define PR_2_ALLOC_DIRBOCK 0x020022
695
696/* Error deallocating inode */
697#define PR_2_DEALLOC_INODE 0x020023
698
699/* Directory entry for '.' is big. Split? */
700#define PR_2_SPLIT_DOT 0x020024
701
702/* Illegal FIFO */
703#define PR_2_BAD_FIFO 0x020025
704
705/* Illegal socket */
706#define PR_2_BAD_SOCKET 0x020026
707
708/* Directory filetype not set */
709#define PR_2_SET_FILETYPE 0x020027
710
711/* Directory filetype incorrect */
712#define PR_2_BAD_FILETYPE 0x020028
713
714/* Directory filetype set when it shouldn't be */
715#define PR_2_CLEAR_FILETYPE 0x020029
716
717/* Directory filename can't be zero-length */
718#define PR_2_NULL_NAME 0x020030
719
720/* Invalid symlink */
721#define PR_2_INVALID_SYMLINK 0x020031
722
723/* i_file_acl (extended attribute) is bad */
724#define PR_2_FILE_ACL_BAD 0x020032
725
726/* Filesystem contains large files, but has no such flag in sb */
727#define PR_2_FEATURE_LARGE_FILES 0x020033
728
729/* Node in HTREE directory not referenced */
730#define PR_2_HTREE_NOTREF 0x020034
731
732/* Node in HTREE directory referenced twice */
733#define PR_2_HTREE_DUPREF 0x020035
734
735/* Node in HTREE directory has bad min hash */
736#define PR_2_HTREE_MIN_HASH 0x020036
737
738/* Node in HTREE directory has bad max hash */
739#define PR_2_HTREE_MAX_HASH 0x020037
740
741/* Clear invalid HTREE directory */
742#define PR_2_HTREE_CLEAR 0x020038
743
744/* Clear the htree flag forcibly */
745/* #define PR_2_HTREE_FCLR 0x020039 */
746
747/* Bad block in htree interior node */
748#define PR_2_HTREE_BADBLK 0x02003A
749
750/* Error adjusting EA refcount */
751#define PR_2_ADJ_EA_REFCOUNT 0x02003B
752
753/* Invalid HTREE root node */
754#define PR_2_HTREE_BAD_ROOT 0x02003C
755
756/* Invalid HTREE limit */
757#define PR_2_HTREE_BAD_LIMIT 0x02003D
758
759/* Invalid HTREE count */
760#define PR_2_HTREE_BAD_COUNT 0x02003E
761
762/* HTREE interior node has out-of-order hashes in table */
763#define PR_2_HTREE_HASH_ORDER 0x02003F
764
765/* Node in HTREE directory has bad depth */
766#define PR_2_HTREE_BAD_DEPTH 0x020040
767
768/* Duplicate directory entry found */
769#define PR_2_DUPLICATE_DIRENT 0x020041
770
771/* Non-unique filename found */
772#define PR_2_NON_UNIQUE_FILE 0x020042
773
774/* Duplicate directory entry found */
775#define PR_2_REPORT_DUP_DIRENT 0x020043
776
777/*
778 * Pass 3 errors
779 */
780
781/* Pass 3: Checking directory connectivity */
782#define PR_3_PASS_HEADER 0x030000
783
784/* Root inode not allocated */
785#define PR_3_NO_ROOT_INODE 0x030001
786
787/* No room in lost+found */
788#define PR_3_EXPAND_LF_DIR 0x030002
789
790/* Unconnected directory inode */
791#define PR_3_UNCONNECTED_DIR 0x030003
792
793/* /lost+found not found */
794#define PR_3_NO_LF_DIR 0x030004
795
796/* .. entry is incorrect */
797#define PR_3_BAD_DOT_DOT 0x030005
798
799/* Bad or non-existent /lost+found. Cannot reconnect */
800#define PR_3_NO_LPF 0x030006
801
802/* Could not expand /lost+found */
803#define PR_3_CANT_EXPAND_LPF 0x030007
804
805/* Could not reconnect inode */
806#define PR_3_CANT_RECONNECT 0x030008
807
808/* Error while trying to find /lost+found */
809#define PR_3_ERR_FIND_LPF 0x030009
810
811/* Error in ext2fs_new_block while creating /lost+found */
812#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A
813
814/* Error in ext2fs_new_inode while creating /lost+found */
815#define PR_3_ERR_LPF_NEW_INODE 0x03000B
816
817/* Error in ext2fs_new_dir_block while creating /lost+found */
818#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C
819
820/* Error while writing directory block for /lost+found */
821#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D
822
823/* Error while adjusting inode count */
824#define PR_3_ADJUST_INODE 0x03000E
825
826/* Couldn't fix parent directory -- error */
827#define PR_3_FIX_PARENT_ERR 0x03000F
828
829/* Couldn't fix parent directory -- couldn't find it */
830#define PR_3_FIX_PARENT_NOFIND 0x030010
831
832/* Error allocating inode bitmap */
833#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011
834
835/* Error creating root directory */
836#define PR_3_CREATE_ROOT_ERROR 0x030012
837
838/* Error creating lost and found directory */
839#define PR_3_CREATE_LPF_ERROR 0x030013
840
841/* Root inode is not directory; aborting */
842#define PR_3_ROOT_NOT_DIR_ABORT 0x030014
843
844/* Cannot proceed without a root inode. */
845#define PR_3_NO_ROOT_INODE_ABORT 0x030015
846
847/* Internal error: couldn't find dir_info */
848#define PR_3_NO_DIRINFO 0x030016
849
850/* Lost+found is not a directory */
851#define PR_3_LPF_NOTDIR 0x030017
852
853/*
854 * Pass 3a --- rehashing diretories
855 */
856/* Pass 3a: Reindexing directories */
857#define PR_3A_PASS_HEADER 0x031000
858
859/* Error iterating over directories */
860#define PR_3A_OPTIMIZE_ITER 0x031001
861
862/* Error rehash directory */
863#define PR_3A_OPTIMIZE_DIR_ERR 0x031002
864
865/* Rehashing dir header */
866#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003
867
868/* Rehashing directory %d */
869#define PR_3A_OPTIMIZE_DIR 0x031004
870
871/* Rehashing dir end */
872#define PR_3A_OPTIMIZE_DIR_END 0x031005
873
874/*
875 * Pass 4 errors
876 */
877
878/* Pass 4: Checking reference counts */
879#define PR_4_PASS_HEADER 0x040000
880
881/* Unattached zero-length inode */
882#define PR_4_ZERO_LEN_INODE 0x040001
883
884/* Unattached inode */
885#define PR_4_UNATTACHED_INODE 0x040002
886
887/* Inode ref count wrong */
888#define PR_4_BAD_REF_COUNT 0x040003
889
890/* Inconsistent inode count information cached */
891#define PR_4_INCONSISTENT_COUNT 0x040004
892
893/*
894 * Pass 5 errors
895 */
896
897/* Pass 5: Checking group summary information */
898#define PR_5_PASS_HEADER 0x050000
899
900/* Padding at end of inode bitmap is not set. */
901#define PR_5_INODE_BMAP_PADDING 0x050001
902
903/* Padding at end of block bitmap is not set. */
904#define PR_5_BLOCK_BMAP_PADDING 0x050002
905
906/* Block bitmap differences header */
907#define PR_5_BLOCK_BITMAP_HEADER 0x050003
908
909/* Block not used, but marked in bitmap */
910#define PR_5_BLOCK_UNUSED 0x050004
911
912/* Block used, but not marked used in bitmap */
913#define PR_5_BLOCK_USED 0x050005
914
915/* Block bitmap differences end */
916#define PR_5_BLOCK_BITMAP_END 0x050006
917
918/* Inode bitmap differences header */
919#define PR_5_INODE_BITMAP_HEADER 0x050007
920
921/* Inode not used, but marked in bitmap */
922#define PR_5_INODE_UNUSED 0x050008
923
924/* Inode used, but not marked used in bitmap */
925#define PR_5_INODE_USED 0x050009
926
927/* Inode bitmap differences end */
928#define PR_5_INODE_BITMAP_END 0x05000A
929
930/* Free inodes count for group wrong */
931#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B
932
933/* Directories count for group wrong */
934#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C
935
936/* Free inodes count wrong */
937#define PR_5_FREE_INODE_COUNT 0x05000D
938
939/* Free blocks count for group wrong */
940#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E
941
942/* Free blocks count wrong */
943#define PR_5_FREE_BLOCK_COUNT 0x05000F
944
945/* Programming error: bitmap endpoints don't match */
946#define PR_5_BMAP_ENDPOINTS 0x050010
947
948/* Internal error: fudging end of bitmap */
949#define PR_5_FUDGE_BITMAP_ERROR 0x050011
950
951/* Error copying in replacement inode bitmap */
952#define PR_5_COPY_IBITMAP_ERROR 0x050012
953
954/* Error copying in replacement block bitmap */
955#define PR_5_COPY_BBITMAP_ERROR 0x050013
956
957/* Block range not used, but marked in bitmap */
958#define PR_5_BLOCK_RANGE_UNUSED 0x050014
959
960/* Block range used, but not marked used in bitmap */
961#define PR_5_BLOCK_RANGE_USED 0x050015
962
963/* Inode range not used, but marked in bitmap */
964#define PR_5_INODE_RANGE_UNUSED 0x050016
965
966/* Inode rangeused, but not marked used in bitmap */
967#define PR_5_INODE_RANGE_USED 0x050017
968
969/*
970 * Function declarations
971 */
972static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
973static int end_problem_latch(e2fsck_t ctx, int mask);
974static int set_latch_flags(int mask, int setflags, int clearflags);
975static void clear_problem_context(struct problem_context *ctx);
976
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000977/*
978 * Dictionary Abstract Data Type
979 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
980 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000981 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
982 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000983 */
984
985#ifndef DICT_H
986#define DICT_H
987
988/*
989 * Blurb for inclusion into C++ translation units
990 */
991
992typedef unsigned long dictcount_t;
993#define DICTCOUNT_T_MAX ULONG_MAX
994
995/*
996 * The dictionary is implemented as a red-black tree
997 */
998
999typedef enum { dnode_red, dnode_black } dnode_color_t;
1000
1001typedef struct dnode_t {
1002 struct dnode_t *dict_left;
1003 struct dnode_t *dict_right;
1004 struct dnode_t *dict_parent;
1005 dnode_color_t dict_color;
1006 const void *dict_key;
1007 void *dict_data;
1008} dnode_t;
1009
1010typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001011typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001012
1013typedef struct dict_t {
1014 dnode_t dict_nilnode;
1015 dictcount_t dict_nodecount;
1016 dictcount_t dict_maxcount;
1017 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001018 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001019 int dict_dupes;
1020} dict_t;
1021
1022typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
1023
1024typedef struct dict_load_t {
1025 dict_t *dict_dictptr;
1026 dnode_t dict_nilnode;
1027} dict_load_t;
1028
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001029#define dict_count(D) ((D)->dict_nodecount)
1030#define dnode_get(N) ((N)->dict_data)
1031#define dnode_getkey(N) ((N)->dict_key)
1032
1033#endif
1034
1035/*
1036 * Compatibility header file for e2fsck which should be included
1037 * instead of linux/jfs.h
1038 *
1039 * Copyright (C) 2000 Stephen C. Tweedie
1040 */
1041
1042/*
1043 * Pull in the definition of the e2fsck context structure
1044 */
1045
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001046struct buffer_head {
1047 char b_data[8192];
1048 e2fsck_t b_ctx;
1049 io_channel b_io;
1050 int b_size;
1051 blk_t b_blocknr;
1052 int b_dirty;
1053 int b_uptodate;
1054 int b_err;
1055};
1056
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001057
1058#define K_DEV_FS 1
1059#define K_DEV_JOURNAL 2
1060
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001061#define lock_buffer(bh) do {} while(0)
1062#define unlock_buffer(bh) do {} while(0)
1063#define buffer_req(bh) 1
1064#define do_readahead(journal, start) do {} while(0)
1065
1066static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
1067
1068typedef struct {
1069 int object_length;
1070} kmem_cache_t;
1071
1072#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001073
1074/*
1075 * We use the standard libext2fs portability tricks for inline
1076 * functions.
1077 */
1078
1079static _INLINE_ kmem_cache_t * do_cache_create(int len)
1080{
1081 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001082
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001083 new_cache = malloc(sizeof(*new_cache));
1084 if (new_cache)
1085 new_cache->object_length = len;
1086 return new_cache;
1087}
1088
1089static _INLINE_ void do_cache_destroy(kmem_cache_t *cache)
1090{
1091 free(cache);
1092}
1093
1094/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001095 * badblocks.c --- replace/append bad blocks to the bad block inode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001096 */
1097
1098static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
1099 void *priv_data);
1100
1101
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001102static void invalid_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t blk)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001103{
1104 printf(_("Bad block %u out of range; ignored.\n"), blk);
1105 return;
1106}
1107
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001108static void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001109 int replace_bad_blocks)
1110{
1111 ext2_filsys fs = ctx->fs;
1112 errcode_t retval;
1113 badblocks_list bb_list = 0;
1114 FILE *f;
1115 char buf[1024];
1116
1117 e2fsck_read_bitmaps(ctx);
1118
1119 /*
1120 * Make sure the bad block inode is sane. If there are any
1121 * illegal blocks, clear them.
1122 */
1123 retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
1124 check_bb_inode_blocks, 0);
1125 if (retval) {
1126 com_err("ext2fs_block_iterate", retval,
1127 _("while sanity checking the bad blocks inode"));
1128 goto fatal;
1129 }
1130
1131 /*
1132 * If we're appending to the bad blocks inode, read in the
1133 * current bad blocks.
1134 */
1135 if (!replace_bad_blocks) {
1136 retval = ext2fs_read_bb_inode(fs, &bb_list);
1137 if (retval) {
1138 com_err("ext2fs_read_bb_inode", retval,
1139 _("while reading the bad blocks inode"));
1140 goto fatal;
1141 }
1142 }
1143
1144 /*
1145 * Now read in the bad blocks from the file; if
1146 * bad_blocks_file is null, then try to run the badblocks
1147 * command.
1148 */
1149 if (bad_blocks_file) {
1150 f = fopen(bad_blocks_file, "r");
1151 if (!f) {
1152 com_err("read_bad_blocks_file", errno,
1153 _("while trying to open %s"), bad_blocks_file);
1154 goto fatal;
1155 }
1156 } else {
1157 sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
1158 (ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
1159 (ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
1160 fs->device_name, fs->super->s_blocks_count);
1161 f = popen(buf, "r");
1162 if (!f) {
1163 com_err("read_bad_blocks_file", errno,
1164 _("while trying popen '%s'"), buf);
1165 goto fatal;
1166 }
1167 }
1168 retval = ext2fs_read_bb_FILE(fs, f, &bb_list, invalid_block);
1169 if (bad_blocks_file)
1170 fclose(f);
1171 else
1172 pclose(f);
1173 if (retval) {
1174 com_err("ext2fs_read_bb_FILE", retval,
1175 _("while reading in list of bad blocks from file"));
1176 goto fatal;
1177 }
1178
1179 /*
1180 * Finally, update the bad blocks from the bad_block_map
1181 */
1182 retval = ext2fs_update_bb_inode(fs, bb_list);
1183 if (retval) {
1184 com_err("ext2fs_update_bb_inode", retval,
1185 _("while updating bad block inode"));
1186 goto fatal;
1187 }
1188
1189 ext2fs_badblocks_list_free(bb_list);
1190 return;
1191
1192fatal:
1193 ctx->flags |= E2F_FLAG_ABORT;
1194 return;
1195
1196}
1197
1198static int check_bb_inode_blocks(ext2_filsys fs,
1199 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001200 int blockcnt FSCK_ATTR((unused)),
1201 void *priv_data FSCK_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001202{
1203 if (!*block_nr)
1204 return 0;
1205
1206 /*
1207 * If the block number is outrageous, clear it and ignore it.
1208 */
1209 if (*block_nr >= fs->super->s_blocks_count ||
1210 *block_nr < fs->super->s_first_data_block) {
1211 printf(_("Warning illegal block %u found in bad block inode. Cleared.\n"), *block_nr);
1212 *block_nr = 0;
1213 return BLOCK_CHANGED;
1214 }
1215
1216 return 0;
1217}
1218
1219/*
1220 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001221 */
1222
1223
1224/*
1225 * These macros provide short convenient names for structure members,
1226 * which are embellished with dict_ prefixes so that they are
1227 * properly confined to the documented namespace. It's legal for a
1228 * program which uses dict to define, for instance, a macro called ``parent''.
1229 * Such a macro would interfere with the dnode_t struct definition.
1230 * In general, highly portable and reusable C modules which expose their
1231 * structures need to confine structure member names to well-defined spaces.
1232 * The resulting identifiers aren't necessarily convenient to use, nor
1233 * readable, in the implementation, however!
1234 */
1235
1236#define left dict_left
1237#define right dict_right
1238#define parent dict_parent
1239#define color dict_color
1240#define key dict_key
1241#define data dict_data
1242
1243#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001244#define maxcount dict_maxcount
1245#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001246#define dupes dict_dupes
1247
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001248#define dict_root(D) ((D)->nilnode.left)
1249#define dict_nil(D) (&(D)->nilnode)
1250#define DICT_DEPTH_MAX 64
1251
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001252static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001253
1254/*
1255 * Perform a ``left rotation'' adjustment on the tree. The given node P and
1256 * its right child C are rearranged so that the P instead becomes the left
1257 * child of C. The left subtree of C is inherited as the new right subtree
1258 * for P. The ordering of the keys within the tree is thus preserved.
1259 */
1260
1261static void rotate_left(dnode_t *upper)
1262{
1263 dnode_t *lower, *lowleft, *upparent;
1264
1265 lower = upper->right;
1266 upper->right = lowleft = lower->left;
1267 lowleft->parent = upper;
1268
1269 lower->parent = upparent = upper->parent;
1270
1271 /* don't need to check for root node here because root->parent is
1272 the sentinel nil node, and root->parent->left points back to root */
1273
1274 if (upper == upparent->left) {
1275 upparent->left = lower;
1276 } else {
1277 assert (upper == upparent->right);
1278 upparent->right = lower;
1279 }
1280
1281 lower->left = upper;
1282 upper->parent = lower;
1283}
1284
1285/*
1286 * This operation is the ``mirror'' image of rotate_left. It is
1287 * the same procedure, but with left and right interchanged.
1288 */
1289
1290static void rotate_right(dnode_t *upper)
1291{
1292 dnode_t *lower, *lowright, *upparent;
1293
1294 lower = upper->left;
1295 upper->left = lowright = lower->right;
1296 lowright->parent = upper;
1297
1298 lower->parent = upparent = upper->parent;
1299
1300 if (upper == upparent->right) {
1301 upparent->right = lower;
1302 } else {
1303 assert (upper == upparent->left);
1304 upparent->left = lower;
1305 }
1306
1307 lower->right = upper;
1308 upper->parent = lower;
1309}
1310
1311/*
1312 * Do a postorder traversal of the tree rooted at the specified
1313 * node and free everything under it. Used by dict_free().
1314 */
1315
1316static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
1317{
1318 if (node == nil)
1319 return;
1320 free_nodes(dict, node->left, nil);
1321 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001322 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001323}
1324
1325/*
1326 * Verify that the tree contains the given node. This is done by
1327 * traversing all of the nodes and comparing their pointers to the
1328 * given pointer. Returns 1 if the node is found, otherwise
1329 * returns zero. It is intended for debugging purposes.
1330 */
1331
1332static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
1333{
1334 if (root != nil) {
1335 return root == node
1336 || verify_dict_has_node(nil, root->left, node)
1337 || verify_dict_has_node(nil, root->right, node);
1338 }
1339 return 0;
1340}
1341
1342
1343/*
1344 * Select a different set of node allocator routines.
1345 */
1346
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001347static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001348{
1349 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001350 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001351}
1352
1353/*
1354 * Free all the nodes in the dictionary by using the dictionary's
1355 * installed free routine. The dictionary is emptied.
1356 */
1357
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001358static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001359{
1360 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
1361 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001362 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001363 dict->nilnode.left = &dict->nilnode;
1364 dict->nilnode.right = &dict->nilnode;
1365}
1366
1367/*
1368 * Initialize a user-supplied dictionary object.
1369 */
1370
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001371static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001372{
1373 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001374 dict->dict_freenode = dnode_free;
1375 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001376 dict->maxcount = maxcount;
1377 dict->nilnode.left = &dict->nilnode;
1378 dict->nilnode.right = &dict->nilnode;
1379 dict->nilnode.parent = &dict->nilnode;
1380 dict->nilnode.color = dnode_black;
1381 dict->dupes = 0;
1382 return dict;
1383}
1384
1385/*
1386 * Locate a node in the dictionary having the given key.
1387 * If the node is not found, a null a pointer is returned (rather than
1388 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
1389 * located node is returned.
1390 */
1391
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001392static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001393{
1394 dnode_t *root = dict_root(dict);
1395 dnode_t *nil = dict_nil(dict);
1396 dnode_t *saved;
1397 int result;
1398
1399 /* simple binary search adapted for trees that contain duplicate keys */
1400
1401 while (root != nil) {
1402 result = dict->compare(key, root->key);
1403 if (result < 0)
1404 root = root->left;
1405 else if (result > 0)
1406 root = root->right;
1407 else {
1408 if (!dict->dupes) { /* no duplicates, return match */
1409 return root;
1410 } else { /* could be dupes, find leftmost one */
1411 do {
1412 saved = root;
1413 root = root->left;
1414 while (root != nil && dict->compare(key, root->key))
1415 root = root->right;
1416 } while (root != nil);
1417 return saved;
1418 }
1419 }
1420 }
1421
1422 return NULL;
1423}
1424
1425/*
1426 * Insert a node into the dictionary. The node should have been
1427 * initialized with a data field. All other fields are ignored.
1428 * The behavior is undefined if the user attempts to insert into
1429 * a dictionary that is already full (for which the dict_isfull()
1430 * function returns true).
1431 */
1432
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001433static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001434{
1435 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
1436 dnode_t *parent = nil, *uncle, *grandpa;
1437 int result = -1;
1438
1439 node->key = key;
1440
1441 /* basic binary tree insert */
1442
1443 while (where != nil) {
1444 parent = where;
1445 result = dict->compare(key, where->key);
1446 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
1447 assert (dict->dupes || result != 0);
1448 if (result < 0)
1449 where = where->left;
1450 else
1451 where = where->right;
1452 }
1453
1454 assert (where == nil);
1455
1456 if (result < 0)
1457 parent->left = node;
1458 else
1459 parent->right = node;
1460
1461 node->parent = parent;
1462 node->left = nil;
1463 node->right = nil;
1464
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001465 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001466
1467 /* red black adjustments */
1468
1469 node->color = dnode_red;
1470
1471 while (parent->color == dnode_red) {
1472 grandpa = parent->parent;
1473 if (parent == grandpa->left) {
1474 uncle = grandpa->right;
1475 if (uncle->color == dnode_red) { /* red parent, red uncle */
1476 parent->color = dnode_black;
1477 uncle->color = dnode_black;
1478 grandpa->color = dnode_red;
1479 node = grandpa;
1480 parent = grandpa->parent;
1481 } else { /* red parent, black uncle */
1482 if (node == parent->right) {
1483 rotate_left(parent);
1484 parent = node;
1485 assert (grandpa == parent->parent);
1486 /* rotation between parent and child preserves grandpa */
1487 }
1488 parent->color = dnode_black;
1489 grandpa->color = dnode_red;
1490 rotate_right(grandpa);
1491 break;
1492 }
1493 } else { /* symmetric cases: parent == parent->parent->right */
1494 uncle = grandpa->left;
1495 if (uncle->color == dnode_red) {
1496 parent->color = dnode_black;
1497 uncle->color = dnode_black;
1498 grandpa->color = dnode_red;
1499 node = grandpa;
1500 parent = grandpa->parent;
1501 } else {
1502 if (node == parent->left) {
1503 rotate_right(parent);
1504 parent = node;
1505 assert (grandpa == parent->parent);
1506 }
1507 parent->color = dnode_black;
1508 grandpa->color = dnode_red;
1509 rotate_left(grandpa);
1510 break;
1511 }
1512 }
1513 }
1514
1515 dict_root(dict)->color = dnode_black;
1516
1517}
1518
1519/*
1520 * Allocate a node using the dictionary's allocator routine, give it
1521 * the data item.
1522 */
1523
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001524static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001525{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001526 dnode->data = data;
1527 dnode->parent = NULL;
1528 dnode->left = NULL;
1529 dnode->right = NULL;
1530 return dnode;
1531}
1532
1533static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
1534{
1535 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001536
1537 if (node) {
1538 dnode_init(node, data);
1539 dict_insert(dict, node, key);
1540 return 1;
1541 }
1542 return 0;
1543}
1544
1545/*
1546 * Return the node with the lowest (leftmost) key. If the dictionary is empty
1547 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
1548 */
1549
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001550static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001551{
1552 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
1553
1554 if (root != nil)
1555 while ((left = root->left) != nil)
1556 root = left;
1557
1558 return (root == nil) ? NULL : root;
1559}
1560
1561/*
1562 * Return the given node's successor node---the node which has the
1563 * next key in the the left to right ordering. If the node has
1564 * no successor, a null pointer is returned rather than a pointer to
1565 * the nil node.
1566 */
1567
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001568static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001569{
1570 dnode_t *nil = dict_nil(dict), *parent, *left;
1571
1572 if (curr->right != nil) {
1573 curr = curr->right;
1574 while ((left = curr->left) != nil)
1575 curr = left;
1576 return curr;
1577 }
1578
1579 parent = curr->parent;
1580
1581 while (parent != nil && curr == parent->right) {
1582 curr = parent;
1583 parent = curr->parent;
1584 }
1585
1586 return (parent == nil) ? NULL : parent;
1587}
1588
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001589
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001590static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001591{
1592 free(node);
1593}
1594
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001595
1596#undef left
1597#undef right
1598#undef parent
1599#undef color
1600#undef key
1601#undef data
1602
1603#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001604#undef maxcount
1605#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001606#undef dupes
1607
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001608
1609/*
1610 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001611 */
1612
1613/*
1614 * This subroutine is called during pass1 to create a directory info
1615 * entry. During pass1, the passed-in parent is 0; it will get filled
1616 * in during pass2.
1617 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001618static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001619{
1620 struct dir_info *dir;
1621 int i, j;
1622 ext2_ino_t num_dirs;
1623 errcode_t retval;
1624 unsigned long old_size;
1625
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001626 if (!ctx->dir_info) {
1627 ctx->dir_info_count = 0;
1628 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
1629 if (retval)
1630 num_dirs = 1024; /* Guess */
1631 ctx->dir_info_size = num_dirs + 10;
1632 ctx->dir_info = (struct dir_info *)
1633 e2fsck_allocate_memory(ctx, ctx->dir_info_size
1634 * sizeof (struct dir_info),
1635 "directory map");
1636 }
1637
1638 if (ctx->dir_info_count >= ctx->dir_info_size) {
1639 old_size = ctx->dir_info_size * sizeof(struct dir_info);
1640 ctx->dir_info_size += 10;
1641 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
1642 sizeof(struct dir_info),
1643 &ctx->dir_info);
1644 if (retval) {
1645 ctx->dir_info_size -= 10;
1646 return;
1647 }
1648 }
1649
1650 /*
1651 * Normally, add_dir_info is called with each inode in
1652 * sequential order; but once in a while (like when pass 3
1653 * needs to recreate the root directory or lost+found
1654 * directory) it is called out of order. In those cases, we
1655 * need to move the dir_info entries down to make room, since
1656 * the dir_info array needs to be sorted by inode number for
1657 * get_dir_info()'s sake.
1658 */
1659 if (ctx->dir_info_count &&
1660 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
1661 for (i = ctx->dir_info_count-1; i > 0; i--)
1662 if (ctx->dir_info[i-1].ino < ino)
1663 break;
1664 dir = &ctx->dir_info[i];
1665 if (dir->ino != ino)
1666 for (j = ctx->dir_info_count++; j > i; j--)
1667 ctx->dir_info[j] = ctx->dir_info[j-1];
1668 } else
1669 dir = &ctx->dir_info[ctx->dir_info_count++];
1670
1671 dir->ino = ino;
1672 dir->dotdot = parent;
1673 dir->parent = parent;
1674}
1675
1676/*
1677 * get_dir_info() --- given an inode number, try to find the directory
1678 * information entry for it.
1679 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001680static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001681{
1682 int low, high, mid;
1683
1684 low = 0;
1685 high = ctx->dir_info_count-1;
1686 if (!ctx->dir_info)
1687 return 0;
1688 if (ino == ctx->dir_info[low].ino)
1689 return &ctx->dir_info[low];
1690 if (ino == ctx->dir_info[high].ino)
1691 return &ctx->dir_info[high];
1692
1693 while (low < high) {
1694 mid = (low+high)/2;
1695 if (mid == low || mid == high)
1696 break;
1697 if (ino == ctx->dir_info[mid].ino)
1698 return &ctx->dir_info[mid];
1699 if (ino < ctx->dir_info[mid].ino)
1700 high = mid;
1701 else
1702 low = mid;
1703 }
1704 return 0;
1705}
1706
1707/*
1708 * Free the dir_info structure when it isn't needed any more.
1709 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001710static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001711{
Rob Landleye7c43b62006-03-01 16:39:45 +00001712 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001713 ctx->dir_info_size = 0;
1714 ctx->dir_info_count = 0;
1715}
1716
1717/*
1718 * Return the count of number of directories in the dir_info structure
1719 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001720static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001721{
1722 return ctx->dir_info_count;
1723}
1724
1725/*
1726 * A simple interator function
1727 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001728static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001729{
1730 if (*control >= ctx->dir_info_count)
1731 return 0;
1732
1733 return(ctx->dir_info + (*control)++);
1734}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001735
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001736/*
1737 * dirinfo.c --- maintains the directory information table for e2fsck.
1738 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001739 */
1740
1741#ifdef ENABLE_HTREE
1742
1743/*
1744 * This subroutine is called during pass1 to create a directory info
1745 * entry. During pass1, the passed-in parent is 0; it will get filled
1746 * in during pass2.
1747 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001748static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001749{
1750 struct dx_dir_info *dir;
1751 int i, j;
1752 errcode_t retval;
1753 unsigned long old_size;
1754
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001755 if (!ctx->dx_dir_info) {
1756 ctx->dx_dir_info_count = 0;
1757 ctx->dx_dir_info_size = 100; /* Guess */
1758 ctx->dx_dir_info = (struct dx_dir_info *)
1759 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
1760 * sizeof (struct dx_dir_info),
1761 "directory map");
1762 }
1763
1764 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
1765 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
1766 ctx->dx_dir_info_size += 10;
1767 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
1768 sizeof(struct dx_dir_info),
1769 &ctx->dx_dir_info);
1770 if (retval) {
1771 ctx->dx_dir_info_size -= 10;
1772 return;
1773 }
1774 }
1775
1776 /*
1777 * Normally, add_dx_dir_info is called with each inode in
1778 * sequential order; but once in a while (like when pass 3
1779 * needs to recreate the root directory or lost+found
1780 * directory) it is called out of order. In those cases, we
1781 * need to move the dx_dir_info entries down to make room, since
1782 * the dx_dir_info array needs to be sorted by inode number for
1783 * get_dx_dir_info()'s sake.
1784 */
1785 if (ctx->dx_dir_info_count &&
1786 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
1787 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
1788 if (ctx->dx_dir_info[i-1].ino < ino)
1789 break;
1790 dir = &ctx->dx_dir_info[i];
1791 if (dir->ino != ino)
1792 for (j = ctx->dx_dir_info_count++; j > i; j--)
1793 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
1794 } else
1795 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
1796
1797 dir->ino = ino;
1798 dir->numblocks = num_blocks;
1799 dir->hashversion = 0;
1800 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
1801 * sizeof (struct dx_dirblock_info),
1802 "dx_block info array");
1803
1804}
1805
1806/*
1807 * get_dx_dir_info() --- given an inode number, try to find the directory
1808 * information entry for it.
1809 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001810static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001811{
1812 int low, high, mid;
1813
1814 low = 0;
1815 high = ctx->dx_dir_info_count-1;
1816 if (!ctx->dx_dir_info)
1817 return 0;
1818 if (ino == ctx->dx_dir_info[low].ino)
1819 return &ctx->dx_dir_info[low];
1820 if (ino == ctx->dx_dir_info[high].ino)
1821 return &ctx->dx_dir_info[high];
1822
1823 while (low < high) {
1824 mid = (low+high)/2;
1825 if (mid == low || mid == high)
1826 break;
1827 if (ino == ctx->dx_dir_info[mid].ino)
1828 return &ctx->dx_dir_info[mid];
1829 if (ino < ctx->dx_dir_info[mid].ino)
1830 high = mid;
1831 else
1832 low = mid;
1833 }
1834 return 0;
1835}
1836
1837/*
1838 * Free the dx_dir_info structure when it isn't needed any more.
1839 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001840static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001841{
1842 int i;
1843 struct dx_dir_info *dir;
1844
1845 if (ctx->dx_dir_info) {
1846 dir = ctx->dx_dir_info;
1847 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +00001848 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001849 }
1850 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001851 }
1852 ctx->dx_dir_info_size = 0;
1853 ctx->dx_dir_info_count = 0;
1854}
1855
1856/*
1857 * A simple interator function
1858 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001859static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001860{
1861 if (*control >= ctx->dx_dir_info_count)
1862 return 0;
1863
1864 return(ctx->dx_dir_info + (*control)++);
1865}
1866
1867#endif /* ENABLE_HTREE */
1868/*
1869 * e2fsck.c - a consistency checker for the new extended file system.
1870 *
Mike Frysinger51a43b42005-09-24 07:11:16 +00001871 */
1872
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001873/*
1874 * This function allocates an e2fsck context
1875 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001876static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001877{
1878 e2fsck_t context;
1879 errcode_t retval;
1880
1881 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
1882 if (retval)
1883 return retval;
1884
1885 memset(context, 0, sizeof(struct e2fsck_struct));
1886
1887 context->process_inode_size = 256;
1888 context->ext_attr_ver = 2;
1889
1890 *ret = context;
1891 return 0;
1892}
1893
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001894struct ea_refcount_el {
1895 blk_t ea_blk;
1896 int ea_count;
1897};
1898
1899struct ea_refcount {
1900 blk_t count;
1901 blk_t size;
1902 blk_t cursor;
1903 struct ea_refcount_el *list;
1904};
1905
1906static void ea_refcount_free(ext2_refcount_t refcount)
1907{
1908 if (!refcount)
1909 return;
1910
Rob Landleye7c43b62006-03-01 16:39:45 +00001911 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001912 ext2fs_free_mem(&refcount);
1913}
1914
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001915/*
1916 * This function resets an e2fsck context; it is called when e2fsck
1917 * needs to be restarted.
1918 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001919static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001920{
1921 ctx->flags = 0;
1922 ctx->lost_and_found = 0;
1923 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00001924 ext2fs_free_inode_bitmap(ctx->inode_used_map);
1925 ctx->inode_used_map = 0;
1926 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
1927 ctx->inode_dir_map = 0;
1928 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
1929 ctx->inode_reg_map = 0;
1930 ext2fs_free_block_bitmap(ctx->block_found_map);
1931 ctx->block_found_map = 0;
1932 ext2fs_free_icount(ctx->inode_link_info);
1933 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001934 if (ctx->journal_io) {
1935 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1936 io_channel_close(ctx->journal_io);
1937 ctx->journal_io = 0;
1938 }
Rob Landleye7c43b62006-03-01 16:39:45 +00001939 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001940 ext2fs_free_dblist(ctx->fs->dblist);
1941 ctx->fs->dblist = 0;
1942 }
1943 e2fsck_free_dir_info(ctx);
1944#ifdef ENABLE_HTREE
1945 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +00001946#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001947 ea_refcount_free(ctx->refcount);
1948 ctx->refcount = 0;
1949 ea_refcount_free(ctx->refcount_extra);
1950 ctx->refcount_extra = 0;
1951 ext2fs_free_block_bitmap(ctx->block_dup_map);
1952 ctx->block_dup_map = 0;
1953 ext2fs_free_block_bitmap(ctx->block_ea_map);
1954 ctx->block_ea_map = 0;
1955 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
1956 ctx->inode_bb_map = 0;
1957 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
1958 ctx->inode_bad_map = 0;
1959 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
1960 ctx->inode_imagic_map = 0;
1961 ext2fs_u32_list_free(ctx->dirs_to_hash);
1962 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001963
1964 /*
1965 * Clear the array of invalid meta-data flags
1966 */
Rob Landleye7c43b62006-03-01 16:39:45 +00001967 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
1968 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
1969 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001970
1971 /* Clear statistic counters */
1972 ctx->fs_directory_count = 0;
1973 ctx->fs_regular_count = 0;
1974 ctx->fs_blockdev_count = 0;
1975 ctx->fs_chardev_count = 0;
1976 ctx->fs_links_count = 0;
1977 ctx->fs_symlinks_count = 0;
1978 ctx->fs_fast_symlinks_count = 0;
1979 ctx->fs_fifo_count = 0;
1980 ctx->fs_total_count = 0;
1981 ctx->fs_badblocks_count = 0;
1982 ctx->fs_sockets_count = 0;
1983 ctx->fs_ind_count = 0;
1984 ctx->fs_dind_count = 0;
1985 ctx->fs_tind_count = 0;
1986 ctx->fs_fragmented = 0;
1987 ctx->large_files = 0;
1988
1989 /* Reset the superblock to the user's requested value */
1990 ctx->superblock = ctx->use_superblock;
1991
1992 return 0;
1993}
1994
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001995static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001996{
1997 if (!ctx)
1998 return;
1999
2000 e2fsck_reset_context(ctx);
2001 if (ctx->blkid)
2002 blkid_put_cache(ctx->blkid);
2003
2004 ext2fs_free_mem(&ctx);
2005}
2006
2007/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002008 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002009 */
2010
2011/*
2012 * The strategy we use for keeping track of EA refcounts is as
2013 * follows. We keep a sorted array of first EA blocks and its
2014 * reference counts. Once the refcount has dropped to zero, it is
2015 * removed from the array to save memory space. Once the EA block is
2016 * checked, its bit is set in the block_ea_map bitmap.
2017 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002018
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002019
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002020static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002021{
2022 ext2_refcount_t refcount;
2023 errcode_t retval;
2024 size_t bytes;
2025
2026 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
2027 if (retval)
2028 return retval;
2029 memset(refcount, 0, sizeof(struct ea_refcount));
2030
2031 if (!size)
2032 size = 500;
2033 refcount->size = size;
2034 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
2035#ifdef DEBUG
2036 printf("Refcount allocated %d entries, %d bytes.\n",
2037 refcount->size, bytes);
2038#endif
2039 retval = ext2fs_get_mem(bytes, &refcount->list);
2040 if (retval)
2041 goto errout;
2042 memset(refcount->list, 0, bytes);
2043
2044 refcount->count = 0;
2045 refcount->cursor = 0;
2046
2047 *ret = refcount;
2048 return 0;
2049
2050errout:
2051 ea_refcount_free(refcount);
2052 return(retval);
2053}
2054
2055/*
2056 * collapse_refcount() --- go through the refcount array, and get rid
2057 * of any count == zero entries
2058 */
2059static void refcount_collapse(ext2_refcount_t refcount)
2060{
2061 unsigned int i, j;
2062 struct ea_refcount_el *list;
2063
2064 list = refcount->list;
2065 for (i = 0, j = 0; i < refcount->count; i++) {
2066 if (list[i].ea_count) {
2067 if (i != j)
2068 list[j] = list[i];
2069 j++;
2070 }
2071 }
2072#if defined(DEBUG) || defined(TEST_PROGRAM)
2073 printf("Refcount_collapse: size was %d, now %d\n",
2074 refcount->count, j);
2075#endif
2076 refcount->count = j;
2077}
2078
2079
2080/*
2081 * insert_refcount_el() --- Insert a new entry into the sorted list at a
2082 * specified position.
2083 */
2084static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
2085 blk_t blk, int pos)
2086{
2087 struct ea_refcount_el *el;
2088 errcode_t retval;
2089 blk_t new_size = 0;
2090 int num;
2091
2092 if (refcount->count >= refcount->size) {
2093 new_size = refcount->size + 100;
2094#ifdef DEBUG
2095 printf("Reallocating refcount %d entries...\n", new_size);
2096#endif
2097 retval = ext2fs_resize_mem((size_t) refcount->size *
2098 sizeof(struct ea_refcount_el),
2099 (size_t) new_size *
2100 sizeof(struct ea_refcount_el),
2101 &refcount->list);
2102 if (retval)
2103 return 0;
2104 refcount->size = new_size;
2105 }
2106 num = (int) refcount->count - pos;
2107 if (num < 0)
2108 return 0; /* should never happen */
2109 if (num) {
2110 memmove(&refcount->list[pos+1], &refcount->list[pos],
2111 sizeof(struct ea_refcount_el) * num);
2112 }
2113 refcount->count++;
2114 el = &refcount->list[pos];
2115 el->ea_count = 0;
2116 el->ea_blk = blk;
2117 return el;
2118}
2119
2120
2121/*
2122 * get_refcount_el() --- given an block number, try to find refcount
2123 * information in the sorted list. If the create flag is set,
2124 * and we can't find an entry, create one in the sorted list.
2125 */
2126static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
2127 blk_t blk, int create)
2128{
2129 float range;
2130 int low, high, mid;
2131 blk_t lowval, highval;
2132
2133 if (!refcount || !refcount->list)
2134 return 0;
2135retry:
2136 low = 0;
2137 high = (int) refcount->count-1;
2138 if (create && ((refcount->count == 0) ||
2139 (blk > refcount->list[high].ea_blk))) {
2140 if (refcount->count >= refcount->size)
2141 refcount_collapse(refcount);
2142
2143 return insert_refcount_el(refcount, blk,
2144 (unsigned) refcount->count);
2145 }
2146 if (refcount->count == 0)
2147 return 0;
2148
2149 if (refcount->cursor >= refcount->count)
2150 refcount->cursor = 0;
2151 if (blk == refcount->list[refcount->cursor].ea_blk)
2152 return &refcount->list[refcount->cursor++];
2153#ifdef DEBUG
2154 printf("Non-cursor get_refcount_el: %u\n", blk);
2155#endif
2156 while (low <= high) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002157 if (low == high)
2158 mid = low;
2159 else {
2160 /* Interpolate for efficiency */
2161 lowval = refcount->list[low].ea_blk;
2162 highval = refcount->list[high].ea_blk;
2163
2164 if (blk < lowval)
2165 range = 0;
2166 else if (blk > highval)
2167 range = 1;
2168 else
2169 range = ((float) (blk - lowval)) /
2170 (highval - lowval);
2171 mid = low + ((int) (range * (high-low)));
2172 }
Rob Landley3e72c592006-04-06 22:49:04 +00002173
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002174 if (blk == refcount->list[mid].ea_blk) {
2175 refcount->cursor = mid+1;
2176 return &refcount->list[mid];
2177 }
2178 if (blk < refcount->list[mid].ea_blk)
2179 high = mid-1;
2180 else
2181 low = mid+1;
2182 }
2183 /*
2184 * If we need to create a new entry, it should be right at
2185 * low (where high will be left at low-1).
2186 */
2187 if (create) {
2188 if (refcount->count >= refcount->size) {
2189 refcount_collapse(refcount);
2190 if (refcount->count < refcount->size)
2191 goto retry;
2192 }
2193 return insert_refcount_el(refcount, blk, low);
2194 }
2195 return 0;
2196}
2197
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002198static errcode_t
2199ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002200{
2201 struct ea_refcount_el *el;
2202
2203 el = get_refcount_el(refcount, blk, 1);
2204 if (!el)
2205 return EXT2_ET_NO_MEMORY;
2206 el->ea_count++;
2207
2208 if (ret)
2209 *ret = el->ea_count;
2210 return 0;
2211}
2212
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002213static errcode_t
2214ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002215{
2216 struct ea_refcount_el *el;
2217
2218 el = get_refcount_el(refcount, blk, 0);
2219 if (!el || el->ea_count == 0)
2220 return EXT2_ET_INVALID_ARGUMENT;
2221
2222 el->ea_count--;
2223
2224 if (ret)
2225 *ret = el->ea_count;
2226 return 0;
2227}
2228
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002229static errcode_t
2230ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002231{
2232 struct ea_refcount_el *el;
2233
2234 /*
2235 * Get the refcount element
2236 */
2237 el = get_refcount_el(refcount, blk, count ? 1 : 0);
2238 if (!el)
2239 return count ? EXT2_ET_NO_MEMORY : 0;
2240 el->ea_count = count;
2241 return 0;
2242}
2243
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002244static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002245{
2246 refcount->cursor = 0;
2247}
2248
2249
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002250static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002251{
2252 struct ea_refcount_el *list;
2253
2254 while (1) {
2255 if (refcount->cursor >= refcount->count)
2256 return 0;
2257 list = refcount->list;
2258 if (list[refcount->cursor].ea_count) {
2259 if (ret)
2260 *ret = list[refcount->cursor].ea_count;
2261 return list[refcount->cursor++].ea_blk;
2262 }
2263 refcount->cursor++;
2264 }
2265}
2266
2267
2268/*
2269 * ehandler.c --- handle bad block errors which come up during the
2270 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002271 */
2272
2273
2274static const char *operation;
2275
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002276static errcode_t
2277e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00002278 void *data, size_t size FSCK_ATTR((unused)),
2279 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002280{
2281 int i;
2282 char *p;
2283 ext2_filsys fs = (ext2_filsys) channel->app_data;
2284 e2fsck_t ctx;
2285
2286 ctx = (e2fsck_t) fs->priv_data;
2287
2288 /*
2289 * If more than one block was read, try reading each block
2290 * separately. We could use the actual bytes read to figure
2291 * out where to start, but we don't bother.
2292 */
2293 if (count > 1) {
2294 p = (char *) data;
2295 for (i=0; i < count; i++, p += channel->block_size, block++) {
2296 error = io_channel_read_blk(channel, block,
2297 1, p);
2298 if (error)
2299 return error;
2300 }
2301 return 0;
2302 }
2303 if (operation)
2304 printf(_("Error reading block %lu (%s) while %s. "), block,
2305 error_message(error), operation);
2306 else
2307 printf(_("Error reading block %lu (%s). "), block,
2308 error_message(error));
2309 preenhalt(ctx);
2310 if (ask(ctx, _("Ignore error"), 1)) {
2311 if (ask(ctx, _("Force rewrite"), 1))
2312 io_channel_write_blk(channel, block, 1, data);
2313 return 0;
2314 }
2315
2316 return error;
2317}
2318
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002319static errcode_t
2320e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00002321 const void *data, size_t size FSCK_ATTR((unused)),
2322 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002323{
2324 int i;
2325 const char *p;
2326 ext2_filsys fs = (ext2_filsys) channel->app_data;
2327 e2fsck_t ctx;
2328
2329 ctx = (e2fsck_t) fs->priv_data;
2330
2331 /*
2332 * If more than one block was written, try writing each block
2333 * separately. We could use the actual bytes read to figure
2334 * out where to start, but we don't bother.
2335 */
2336 if (count > 1) {
2337 p = (const char *) data;
2338 for (i=0; i < count; i++, p += channel->block_size, block++) {
2339 error = io_channel_write_blk(channel, block,
2340 1, p);
2341 if (error)
2342 return error;
2343 }
2344 return 0;
2345 }
2346
2347 if (operation)
2348 printf(_("Error writing block %lu (%s) while %s. "), block,
2349 error_message(error), operation);
2350 else
2351 printf(_("Error writing block %lu (%s). "), block,
2352 error_message(error));
2353 preenhalt(ctx);
2354 if (ask(ctx, _("Ignore error"), 1))
2355 return 0;
2356
2357 return error;
2358}
2359
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002360static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002361{
2362 const char *ret = operation;
2363
2364 operation = op;
2365 return ret;
2366}
2367
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002368static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002369{
2370 channel->read_error = e2fsck_handle_read_error;
2371 channel->write_error = e2fsck_handle_write_error;
2372}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002373
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002374/*
2375 * journal.c --- code for handling the "ext3" journal
2376 *
2377 * Copyright (C) 2000 Andreas Dilger
2378 * Copyright (C) 2000 Theodore Ts'o
2379 *
2380 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
2381 * Copyright (C) 1999 Red Hat Software
2382 *
2383 * This file may be redistributed under the terms of the
2384 * GNU General Public License version 2 or at your discretion
2385 * any later version.
2386 */
2387
2388#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
2389
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002390/*
2391 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
2392 * This creates a larger static binary, and a smaller binary using
2393 * shared libraries. It's also probably slightly less CPU-efficient,
2394 * which is why it's not on by default. But, it's a good way of
2395 * testing the functions in inode_io.c and fileio.c.
2396 */
2397#undef USE_INODE_IO
2398
2399/* Kernel compatibility functions for handling the journal. These allow us
2400 * to use the recovery.c file virtually unchanged from the kernel, so we
2401 * don't have to do much to keep kernel and user recovery in sync.
2402 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002403static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002404{
2405#ifdef USE_INODE_IO
2406 *phys = block;
2407 return 0;
2408#else
2409 struct inode *inode = journal->j_inode;
2410 errcode_t retval;
2411 blk_t pblk;
2412
2413 if (!inode) {
2414 *phys = block;
2415 return 0;
2416 }
2417
2418 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
2419 &inode->i_ext2, NULL, 0, block, &pblk);
2420 *phys = pblk;
2421 return (retval);
2422#endif
2423}
2424
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002425static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002426{
2427 struct buffer_head *bh;
2428
2429 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
2430 if (!bh)
2431 return NULL;
2432
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002433 bh->b_ctx = kdev->k_ctx;
2434 if (kdev->k_dev == K_DEV_FS)
2435 bh->b_io = kdev->k_ctx->fs->io;
2436 else
2437 bh->b_io = kdev->k_ctx->journal_io;
2438 bh->b_size = blocksize;
2439 bh->b_blocknr = blocknr;
2440
2441 return bh;
2442}
2443
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002444static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002445{
2446 io_channel io;
2447
2448 if (kdev->k_dev == K_DEV_FS)
2449 io = kdev->k_ctx->fs->io;
2450 else
2451 io = kdev->k_ctx->journal_io;
2452
2453 io_channel_flush(io);
2454}
2455
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002456static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002457{
2458 int retval;
2459 struct buffer_head *bh;
2460
2461 for (; nr > 0; --nr) {
2462 bh = *bhp++;
2463 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002464 retval = io_channel_read_blk(bh->b_io,
2465 bh->b_blocknr,
2466 1, bh->b_data);
2467 if (retval) {
2468 com_err(bh->b_ctx->device_name, retval,
2469 "while reading block %lu\n",
2470 (unsigned long) bh->b_blocknr);
2471 bh->b_err = retval;
2472 continue;
2473 }
2474 bh->b_uptodate = 1;
2475 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002476 retval = io_channel_write_blk(bh->b_io,
2477 bh->b_blocknr,
2478 1, bh->b_data);
2479 if (retval) {
2480 com_err(bh->b_ctx->device_name, retval,
2481 "while writing block %lu\n",
2482 (unsigned long) bh->b_blocknr);
2483 bh->b_err = retval;
2484 continue;
2485 }
2486 bh->b_dirty = 0;
2487 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002488 }
2489 }
2490}
2491
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002492static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002493{
2494 bh->b_dirty = 1;
2495}
2496
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002497static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002498{
2499 bh->b_dirty = 0;
2500}
2501
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002502static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002503{
2504 if (bh->b_dirty)
2505 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002506 ext2fs_free_mem(&bh);
2507}
2508
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002509static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002510{
2511 return bh->b_uptodate;
2512}
2513
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002514static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002515{
2516 bh->b_uptodate = val;
2517}
2518
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002519static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002520{
2521 if (!bh->b_uptodate)
2522 ll_rw_block(READ, 1, &bh);
2523}
2524
2525
2526static void e2fsck_clear_recover(e2fsck_t ctx, int error)
2527{
2528 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
2529
2530 /* if we had an error doing journal recovery, we need a full fsck */
2531 if (error)
2532 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
2533 ext2fs_mark_super_dirty(ctx->fs);
2534}
2535
2536static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
2537{
2538 struct ext2_super_block *sb = ctx->fs->super;
2539 struct ext2_super_block jsuper;
2540 struct problem_context pctx;
2541 struct buffer_head *bh;
2542 struct inode *j_inode = NULL;
2543 struct kdev_s *dev_fs = NULL, *dev_journal;
2544 const char *journal_name = 0;
2545 journal_t *journal = NULL;
2546 errcode_t retval = 0;
2547 io_manager io_ptr = 0;
2548 unsigned long start = 0;
2549 blk_t blk;
2550 int ext_journal = 0;
2551 int tried_backup_jnl = 0;
2552 int i;
2553
2554 clear_problem_context(&pctx);
2555
2556 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
2557 if (!journal) {
2558 return EXT2_ET_NO_MEMORY;
2559 }
2560
2561 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
2562 if (!dev_fs) {
2563 retval = EXT2_ET_NO_MEMORY;
2564 goto errout;
2565 }
2566 dev_journal = dev_fs+1;
2567
2568 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
2569 dev_fs->k_dev = K_DEV_FS;
2570 dev_journal->k_dev = K_DEV_JOURNAL;
2571
2572 journal->j_dev = dev_journal;
2573 journal->j_fs_dev = dev_fs;
2574 journal->j_inode = NULL;
2575 journal->j_blocksize = ctx->fs->blocksize;
2576
2577 if (uuid_is_null(sb->s_journal_uuid)) {
2578 if (!sb->s_journal_inum)
2579 return EXT2_ET_BAD_INODE_NUM;
2580 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
2581 "journal inode");
2582 if (!j_inode) {
2583 retval = EXT2_ET_NO_MEMORY;
2584 goto errout;
2585 }
2586
2587 j_inode->i_ctx = ctx;
2588 j_inode->i_ino = sb->s_journal_inum;
2589
2590 if ((retval = ext2fs_read_inode(ctx->fs,
2591 sb->s_journal_inum,
2592 &j_inode->i_ext2))) {
2593 try_backup_journal:
2594 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
2595 tried_backup_jnl)
2596 goto errout;
2597 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
2598 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
2599 EXT2_N_BLOCKS*4);
2600 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
2601 j_inode->i_ext2.i_links_count = 1;
2602 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
2603 tried_backup_jnl++;
2604 }
2605 if (!j_inode->i_ext2.i_links_count ||
2606 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
2607 retval = EXT2_ET_NO_JOURNAL;
2608 goto try_backup_journal;
2609 }
2610 if (j_inode->i_ext2.i_size / journal->j_blocksize <
2611 JFS_MIN_JOURNAL_BLOCKS) {
2612 retval = EXT2_ET_JOURNAL_TOO_SMALL;
2613 goto try_backup_journal;
2614 }
2615 for (i=0; i < EXT2_N_BLOCKS; i++) {
2616 blk = j_inode->i_ext2.i_block[i];
2617 if (!blk) {
2618 if (i < EXT2_NDIR_BLOCKS) {
2619 retval = EXT2_ET_JOURNAL_TOO_SMALL;
2620 goto try_backup_journal;
2621 }
2622 continue;
2623 }
2624 if (blk < sb->s_first_data_block ||
2625 blk >= sb->s_blocks_count) {
2626 retval = EXT2_ET_BAD_BLOCK_NUM;
2627 goto try_backup_journal;
2628 }
2629 }
2630 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
2631
2632#ifdef USE_INODE_IO
2633 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
2634 &j_inode->i_ext2,
2635 &journal_name);
2636 if (retval)
2637 goto errout;
2638
2639 io_ptr = inode_io_manager;
2640#else
2641 journal->j_inode = j_inode;
2642 ctx->journal_io = ctx->fs->io;
2643 if ((retval = journal_bmap(journal, 0, &start)) != 0)
2644 goto errout;
2645#endif
2646 } else {
2647 ext_journal = 1;
2648 if (!ctx->journal_name) {
2649 char uuid[37];
2650
2651 uuid_unparse(sb->s_journal_uuid, uuid);
2652 ctx->journal_name = blkid_get_devname(ctx->blkid,
2653 "UUID", uuid);
2654 if (!ctx->journal_name)
2655 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
2656 }
2657 journal_name = ctx->journal_name;
2658
2659 if (!journal_name) {
2660 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
2661 return EXT2_ET_LOAD_EXT_JOURNAL;
2662 }
2663
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002664 io_ptr = unix_io_manager;
2665 }
2666
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002667#ifndef USE_INODE_IO
2668 if (ext_journal)
2669#endif
2670 retval = io_ptr->open(journal_name, IO_FLAG_RW,
2671 &ctx->journal_io);
2672 if (retval)
2673 goto errout;
2674
2675 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
2676
2677 if (ext_journal) {
2678 if (ctx->fs->blocksize == 1024)
2679 start = 1;
2680 bh = getblk(dev_journal, start, ctx->fs->blocksize);
2681 if (!bh) {
2682 retval = EXT2_ET_NO_MEMORY;
2683 goto errout;
2684 }
2685 ll_rw_block(READ, 1, &bh);
2686 if ((retval = bh->b_err) != 0)
2687 goto errout;
2688 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
2689 sizeof(jsuper));
2690 brelse(bh);
2691#ifdef EXT2FS_ENABLE_SWAPFS
2692 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
2693 ext2fs_swap_super(&jsuper);
2694#endif
2695 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
2696 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
2697 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
2698 retval = EXT2_ET_LOAD_EXT_JOURNAL;
2699 goto errout;
2700 }
2701 /* Make sure the journal UUID is correct */
2702 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
2703 sizeof(jsuper.s_uuid))) {
2704 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
2705 retval = EXT2_ET_LOAD_EXT_JOURNAL;
2706 goto errout;
2707 }
2708
2709 journal->j_maxlen = jsuper.s_blocks_count;
2710 start++;
2711 }
2712
2713 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
2714 retval = EXT2_ET_NO_MEMORY;
2715 goto errout;
2716 }
2717
2718 journal->j_sb_buffer = bh;
2719 journal->j_superblock = (journal_superblock_t *)bh->b_data;
2720
2721#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00002722 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002723#endif
2724
2725 *ret_journal = journal;
2726 return 0;
2727
2728errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00002729 ext2fs_free_mem(&dev_fs);
2730 ext2fs_free_mem(&j_inode);
2731 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002732 return retval;
2733
2734}
2735
2736static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
2737 struct problem_context *pctx)
2738{
2739 struct ext2_super_block *sb = ctx->fs->super;
2740 int recover = ctx->fs->super->s_feature_incompat &
2741 EXT3_FEATURE_INCOMPAT_RECOVER;
2742 int has_journal = ctx->fs->super->s_feature_compat &
2743 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2744
2745 if (has_journal || sb->s_journal_inum) {
2746 /* The journal inode is bogus, remove and force full fsck */
2747 pctx->ino = sb->s_journal_inum;
2748 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
2749 if (has_journal && sb->s_journal_inum)
2750 printf("*** ext3 journal has been deleted - "
2751 "filesystem is now ext2 only ***\n\n");
2752 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2753 sb->s_journal_inum = 0;
2754 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
2755 e2fsck_clear_recover(ctx, 1);
2756 return 0;
2757 }
2758 return EXT2_ET_BAD_INODE_NUM;
2759 } else if (recover) {
2760 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
2761 e2fsck_clear_recover(ctx, 1);
2762 return 0;
2763 }
2764 return EXT2_ET_UNSUPP_FEATURE;
2765 }
2766 return 0;
2767}
2768
2769#define V1_SB_SIZE 0x0024
2770static void clear_v2_journal_fields(journal_t *journal)
2771{
2772 e2fsck_t ctx = journal->j_dev->k_ctx;
2773 struct problem_context pctx;
2774
2775 clear_problem_context(&pctx);
2776
2777 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
2778 return;
2779
2780 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
2781 ctx->fs->blocksize-V1_SB_SIZE);
2782 mark_buffer_dirty(journal->j_sb_buffer);
2783}
2784
2785
2786static errcode_t e2fsck_journal_load(journal_t *journal)
2787{
2788 e2fsck_t ctx = journal->j_dev->k_ctx;
2789 journal_superblock_t *jsb;
2790 struct buffer_head *jbh = journal->j_sb_buffer;
2791 struct problem_context pctx;
2792
2793 clear_problem_context(&pctx);
2794
2795 ll_rw_block(READ, 1, &jbh);
2796 if (jbh->b_err) {
2797 com_err(ctx->device_name, jbh->b_err,
2798 _("reading journal superblock\n"));
2799 return jbh->b_err;
2800 }
2801
2802 jsb = journal->j_superblock;
2803 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
2804 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
2805 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2806
2807 switch (ntohl(jsb->s_header.h_blocktype)) {
2808 case JFS_SUPERBLOCK_V1:
2809 journal->j_format_version = 1;
2810 if (jsb->s_feature_compat ||
2811 jsb->s_feature_incompat ||
2812 jsb->s_feature_ro_compat ||
2813 jsb->s_nr_users)
2814 clear_v2_journal_fields(journal);
2815 break;
2816
2817 case JFS_SUPERBLOCK_V2:
2818 journal->j_format_version = 2;
2819 if (ntohl(jsb->s_nr_users) > 1 &&
2820 uuid_is_null(ctx->fs->super->s_journal_uuid))
2821 clear_v2_journal_fields(journal);
2822 if (ntohl(jsb->s_nr_users) > 1) {
2823 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
2824 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
2825 }
2826 break;
2827
2828 /*
2829 * These should never appear in a journal super block, so if
2830 * they do, the journal is badly corrupted.
2831 */
2832 case JFS_DESCRIPTOR_BLOCK:
2833 case JFS_COMMIT_BLOCK:
2834 case JFS_REVOKE_BLOCK:
2835 return EXT2_ET_CORRUPT_SUPERBLOCK;
2836
2837 /* If we don't understand the superblock major type, but there
2838 * is a magic number, then it is likely to be a new format we
2839 * just don't understand, so leave it alone. */
2840 default:
2841 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
2842 }
2843
2844 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
2845 return EXT2_ET_UNSUPP_FEATURE;
2846
2847 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
2848 return EXT2_ET_RO_UNSUPP_FEATURE;
2849
2850 /* We have now checked whether we know enough about the journal
2851 * format to be able to proceed safely, so any other checks that
2852 * fail we should attempt to recover from. */
2853 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
2854 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
2855 _("%s: no valid journal superblock found\n"),
2856 ctx->device_name);
2857 return EXT2_ET_CORRUPT_SUPERBLOCK;
2858 }
2859
2860 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
2861 journal->j_maxlen = ntohl(jsb->s_maxlen);
2862 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
2863 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
2864 _("%s: journal too short\n"),
2865 ctx->device_name);
2866 return EXT2_ET_CORRUPT_SUPERBLOCK;
2867 }
2868
2869 journal->j_tail_sequence = ntohl(jsb->s_sequence);
2870 journal->j_transaction_sequence = journal->j_tail_sequence;
2871 journal->j_tail = ntohl(jsb->s_start);
2872 journal->j_first = ntohl(jsb->s_first);
2873 journal->j_last = ntohl(jsb->s_maxlen);
2874
2875 return 0;
2876}
2877
2878static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
2879 journal_t *journal)
2880{
2881 char *p;
2882 union {
2883 uuid_t uuid;
2884 __u32 val[4];
2885 } u;
2886 __u32 new_seq = 0;
2887 int i;
2888
2889 /* Leave a valid existing V1 superblock signature alone.
2890 * Anything unrecognisable we overwrite with a new V2
2891 * signature. */
2892
2893 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
2894 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
2895 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
2896 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
2897 }
2898
2899 /* Zero out everything else beyond the superblock header */
2900
2901 p = ((char *) jsb) + sizeof(journal_header_t);
2902 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
2903
2904 jsb->s_blocksize = htonl(ctx->fs->blocksize);
2905 jsb->s_maxlen = htonl(journal->j_maxlen);
2906 jsb->s_first = htonl(1);
2907
2908 /* Initialize the journal sequence number so that there is "no"
2909 * chance we will find old "valid" transactions in the journal.
2910 * This avoids the need to zero the whole journal (slow to do,
2911 * and risky when we are just recovering the filesystem).
2912 */
2913 uuid_generate(u.uuid);
2914 for (i = 0; i < 4; i ++)
2915 new_seq ^= u.val[i];
2916 jsb->s_sequence = htonl(new_seq);
2917
2918 mark_buffer_dirty(journal->j_sb_buffer);
2919 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
2920}
2921
2922static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
2923 journal_t *journal,
2924 struct problem_context *pctx)
2925{
2926 struct ext2_super_block *sb = ctx->fs->super;
2927 int recover = ctx->fs->super->s_feature_incompat &
2928 EXT3_FEATURE_INCOMPAT_RECOVER;
2929
2930 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
2931 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
2932 e2fsck_journal_reset_super(ctx, journal->j_superblock,
2933 journal);
2934 journal->j_transaction_sequence = 1;
2935 e2fsck_clear_recover(ctx, recover);
2936 return 0;
2937 }
2938 return EXT2_ET_CORRUPT_SUPERBLOCK;
2939 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
2940 return EXT2_ET_CORRUPT_SUPERBLOCK;
2941
2942 return 0;
2943}
2944
2945static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
2946 int reset, int drop)
2947{
2948 journal_superblock_t *jsb;
2949
2950 if (drop)
2951 mark_buffer_clean(journal->j_sb_buffer);
2952 else if (!(ctx->options & E2F_OPT_READONLY)) {
2953 jsb = journal->j_superblock;
2954 jsb->s_sequence = htonl(journal->j_transaction_sequence);
2955 if (reset)
2956 jsb->s_start = 0; /* this marks the journal as empty */
2957 mark_buffer_dirty(journal->j_sb_buffer);
2958 }
2959 brelse(journal->j_sb_buffer);
2960
2961 if (ctx->journal_io) {
2962 if (ctx->fs && ctx->fs->io != ctx->journal_io)
2963 io_channel_close(ctx->journal_io);
2964 ctx->journal_io = 0;
2965 }
2966
2967#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00002968 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002969#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00002970 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002971 ext2fs_free_mem(&journal);
2972}
2973
2974/*
2975 * This function makes sure that the superblock fields regarding the
2976 * journal are consistent.
2977 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002978static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002979{
2980 struct ext2_super_block *sb = ctx->fs->super;
2981 journal_t *journal;
2982 int recover = ctx->fs->super->s_feature_incompat &
2983 EXT3_FEATURE_INCOMPAT_RECOVER;
2984 struct problem_context pctx;
2985 problem_t problem;
2986 int reset = 0, force_fsck = 0;
2987 int retval;
2988
2989 /* If we don't have any journal features, don't do anything more */
2990 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
2991 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
2992 uuid_is_null(sb->s_journal_uuid))
2993 return 0;
2994
2995 clear_problem_context(&pctx);
2996 pctx.num = sb->s_journal_inum;
2997
2998 retval = e2fsck_get_journal(ctx, &journal);
2999 if (retval) {
3000 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
3001 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
3002 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
3003 (retval == EXT2_ET_NO_JOURNAL))
3004 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
3005 return retval;
3006 }
3007
3008 retval = e2fsck_journal_load(journal);
3009 if (retval) {
3010 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
3011 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
3012 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
3013 &pctx))) ||
3014 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
3015 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
3016 &pctx))) ||
3017 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
3018 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
3019 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
3020 &pctx);
3021 e2fsck_journal_release(ctx, journal, 0, 1);
3022 return retval;
3023 }
3024
3025 /*
3026 * We want to make the flags consistent here. We will not leave with
3027 * needs_recovery set but has_journal clear. We can't get in a loop
3028 * with -y, -n, or -p, only if a user isn't making up their mind.
3029 */
3030no_has_journal:
3031 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
3032 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
3033 pctx.str = "inode";
3034 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
3035 if (recover &&
3036 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
3037 goto no_has_journal;
3038 /*
3039 * Need a full fsck if we are releasing a
3040 * journal stored on a reserved inode.
3041 */
3042 force_fsck = recover ||
3043 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
3044 /* Clear all of the journal fields */
3045 sb->s_journal_inum = 0;
3046 sb->s_journal_dev = 0;
3047 memset(sb->s_journal_uuid, 0,
3048 sizeof(sb->s_journal_uuid));
3049 e2fsck_clear_recover(ctx, force_fsck);
3050 } else if (!(ctx->options & E2F_OPT_READONLY)) {
3051 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
3052 ext2fs_mark_super_dirty(ctx->fs);
3053 }
3054 }
3055
3056 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
3057 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
3058 journal->j_superblock->s_start != 0) {
3059 /* Print status information */
3060 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
3061 if (ctx->superblock)
3062 problem = PR_0_JOURNAL_RUN_DEFAULT;
3063 else
3064 problem = PR_0_JOURNAL_RUN;
3065 if (fix_problem(ctx, problem, &pctx)) {
3066 ctx->options |= E2F_OPT_FORCE;
3067 sb->s_feature_incompat |=
3068 EXT3_FEATURE_INCOMPAT_RECOVER;
3069 ext2fs_mark_super_dirty(ctx->fs);
3070 } else if (fix_problem(ctx,
3071 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
3072 reset = 1;
3073 sb->s_state &= ~EXT2_VALID_FS;
3074 ext2fs_mark_super_dirty(ctx->fs);
3075 }
3076 /*
3077 * If the user answers no to the above question, we
3078 * ignore the fact that journal apparently has data;
3079 * accidentally replaying over valid data would be far
3080 * worse than skipping a questionable recovery.
3081 *
3082 * XXX should we abort with a fatal error here? What
3083 * will the ext3 kernel code do if a filesystem with
3084 * !NEEDS_RECOVERY but with a non-zero
3085 * journal->j_superblock->s_start is mounted?
3086 */
3087 }
3088
3089 e2fsck_journal_release(ctx, journal, reset, 0);
3090 return retval;
3091}
3092
3093static errcode_t recover_ext3_journal(e2fsck_t ctx)
3094{
3095 journal_t *journal;
3096 int retval;
3097
3098 journal_init_revoke_caches();
3099 retval = e2fsck_get_journal(ctx, &journal);
3100 if (retval)
3101 return retval;
3102
3103 retval = e2fsck_journal_load(journal);
3104 if (retval)
3105 goto errout;
3106
3107 retval = journal_init_revoke(journal, 1024);
3108 if (retval)
3109 goto errout;
3110
3111 retval = -journal_recover(journal);
3112 if (retval)
3113 goto errout;
3114
3115 if (journal->j_superblock->s_errno) {
3116 ctx->fs->super->s_state |= EXT2_ERROR_FS;
3117 ext2fs_mark_super_dirty(ctx->fs);
3118 journal->j_superblock->s_errno = 0;
3119 mark_buffer_dirty(journal->j_sb_buffer);
3120 }
3121
3122errout:
3123 journal_destroy_revoke(journal);
3124 journal_destroy_revoke_caches();
3125 e2fsck_journal_release(ctx, journal, 1, 0);
3126 return retval;
3127}
3128
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003129static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003130{
3131 io_manager io_ptr = ctx->fs->io->manager;
3132 int blocksize = ctx->fs->blocksize;
3133 errcode_t retval, recover_retval;
3134
3135 printf(_("%s: recovering journal\n"), ctx->device_name);
3136 if (ctx->options & E2F_OPT_READONLY) {
3137 printf(_("%s: won't do journal recovery while read-only\n"),
3138 ctx->device_name);
3139 return EXT2_ET_FILE_RO;
3140 }
3141
3142 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
3143 ext2fs_flush(ctx->fs); /* Force out any modifications */
3144
3145 recover_retval = recover_ext3_journal(ctx);
3146
3147 /*
3148 * Reload the filesystem context to get up-to-date data from disk
3149 * because journal recovery will change the filesystem under us.
3150 */
3151 ext2fs_close(ctx->fs);
3152 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
3153 ctx->superblock, blocksize, io_ptr,
3154 &ctx->fs);
3155
3156 if (retval) {
3157 com_err(ctx->program_name, retval,
3158 _("while trying to re-open %s"),
3159 ctx->device_name);
3160 fatal_error(ctx, 0);
3161 }
3162 ctx->fs->priv_data = ctx;
3163
3164 /* Set the superblock flags */
3165 e2fsck_clear_recover(ctx, recover_retval);
3166 return recover_retval;
3167}
3168
3169/*
3170 * This function will move the journal inode from a visible file in
3171 * the filesystem directory hierarchy to the reserved inode if necessary.
3172 */
3173static const char * const journal_names[] = {
3174 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
3175
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003176static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003177{
3178 struct ext2_super_block *sb = ctx->fs->super;
3179 struct problem_context pctx;
3180 struct ext2_inode inode;
3181 ext2_filsys fs = ctx->fs;
3182 ext2_ino_t ino;
3183 errcode_t retval;
3184 const char * const * cpp;
3185 int group, mount_flags;
3186
3187 clear_problem_context(&pctx);
3188
3189 /*
3190 * If the filesystem is opened read-only, or there is no
3191 * journal, then do nothing.
3192 */
3193 if ((ctx->options & E2F_OPT_READONLY) ||
3194 (sb->s_journal_inum == 0) ||
3195 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
3196 return;
3197
3198 /*
3199 * Read in the journal inode
3200 */
3201 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
3202 return;
3203
3204 /*
3205 * If it's necessary to backup the journal inode, do so.
3206 */
3207 if ((sb->s_jnl_backup_type == 0) ||
3208 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
3209 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
3210 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
3211 memcpy(sb->s_jnl_blocks, inode.i_block,
3212 EXT2_N_BLOCKS*4);
3213 sb->s_jnl_blocks[16] = inode.i_size;
3214 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
3215 ext2fs_mark_super_dirty(fs);
3216 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
3217 }
3218 }
3219
3220 /*
3221 * If the journal is already the hidden inode, then do nothing
3222 */
3223 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
3224 return;
3225
3226 /*
3227 * The journal inode had better have only one link and not be readable.
3228 */
3229 if (inode.i_links_count != 1)
3230 return;
3231
3232 /*
3233 * If the filesystem is mounted, or we can't tell whether
3234 * or not it's mounted, do nothing.
3235 */
3236 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
3237 if (retval || (mount_flags & EXT2_MF_MOUNTED))
3238 return;
3239
3240 /*
3241 * If we can't find the name of the journal inode, then do
3242 * nothing.
3243 */
3244 for (cpp = journal_names; *cpp; cpp++) {
3245 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
3246 strlen(*cpp), 0, &ino);
3247 if ((retval == 0) && (ino == sb->s_journal_inum))
3248 break;
3249 }
3250 if (*cpp == 0)
3251 return;
3252
3253 /* We need the inode bitmap to be loaded */
3254 retval = ext2fs_read_bitmaps(fs);
3255 if (retval)
3256 return;
3257
3258 pctx.str = *cpp;
3259 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
3260 return;
3261
3262 /*
3263 * OK, we've done all the checks, let's actually move the
3264 * journal inode. Errors at this point mean we need to force
3265 * an ext2 filesystem check.
3266 */
3267 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
3268 goto err_out;
3269 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
3270 goto err_out;
3271 sb->s_journal_inum = EXT2_JOURNAL_INO;
3272 ext2fs_mark_super_dirty(fs);
3273 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
3274 inode.i_links_count = 0;
3275 inode.i_dtime = time(0);
3276 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
3277 goto err_out;
3278
3279 group = ext2fs_group_of_ino(fs, ino);
3280 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
3281 ext2fs_mark_ib_dirty(fs);
3282 fs->group_desc[group].bg_free_inodes_count++;
3283 fs->super->s_free_inodes_count++;
3284 return;
3285
3286err_out:
3287 pctx.errcode = retval;
3288 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
3289 fs->super->s_state &= ~EXT2_VALID_FS;
3290 ext2fs_mark_super_dirty(fs);
3291 return;
3292}
3293
3294/*
3295 * message.c --- print e2fsck messages (with compression)
3296 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003297 * print_e2fsck_message() prints a message to the user, using
3298 * compression techniques and expansions of abbreviations.
3299 *
3300 * The following % expansions are supported:
3301 *
3302 * %b <blk> block number
3303 * %B <blkcount> integer
3304 * %c <blk2> block number
3305 * %Di <dirent>->ino inode number
3306 * %Dn <dirent>->name string
3307 * %Dr <dirent>->rec_len
3308 * %Dl <dirent>->name_len
3309 * %Dt <dirent>->filetype
3310 * %d <dir> inode number
3311 * %g <group> integer
3312 * %i <ino> inode number
3313 * %Is <inode> -> i_size
3314 * %IS <inode> -> i_extra_isize
3315 * %Ib <inode> -> i_blocks
3316 * %Il <inode> -> i_links_count
3317 * %Im <inode> -> i_mode
3318 * %IM <inode> -> i_mtime
3319 * %IF <inode> -> i_faddr
3320 * %If <inode> -> i_file_acl
3321 * %Id <inode> -> i_dir_acl
3322 * %Iu <inode> -> i_uid
3323 * %Ig <inode> -> i_gid
3324 * %j <ino2> inode number
3325 * %m <com_err error message>
3326 * %N <num>
3327 * %p ext2fs_get_pathname of directory <ino>
3328 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
3329 * the containing directory. (If dirent is NULL
3330 * then return the pathname of directory <ino2>)
3331 * %q ext2fs_get_pathname of directory <dir>
3332 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
3333 * the containing directory.
3334 * %s <str> miscellaneous string
3335 * %S backup superblock
3336 * %X <num> hexadecimal format
3337 *
3338 * The following '@' expansions are supported:
3339 *
3340 * @a extended attribute
3341 * @A error allocating
3342 * @b block
3343 * @B bitmap
3344 * @c compress
3345 * @C conflicts with some other fs block
3346 * @D deleted
3347 * @d directory
3348 * @e entry
3349 * @E Entry '%Dn' in %p (%i)
3350 * @f filesystem
3351 * @F for @i %i (%Q) is
3352 * @g group
3353 * @h HTREE directory inode
3354 * @i inode
3355 * @I illegal
3356 * @j journal
3357 * @l lost+found
3358 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00003359 * @m multiply-claimed
3360 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003361 * @o orphaned
3362 * @p problem in
3363 * @r root inode
3364 * @s should be
3365 * @S superblock
3366 * @u unattached
3367 * @v device
3368 * @z zero-length
3369 */
3370
3371
3372/*
3373 * This structure defines the abbreviations used by the text strings
3374 * below. The first character in the string is the index letter. An
3375 * abbreviation of the form '@<i>' is expanded by looking up the index
3376 * letter <i> in the table below.
3377 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003378static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003379 N_("aextended attribute"),
3380 N_("Aerror allocating"),
3381 N_("bblock"),
3382 N_("Bbitmap"),
3383 N_("ccompress"),
3384 N_("Cconflicts with some other fs @b"),
3385 N_("iinode"),
3386 N_("Iillegal"),
3387 N_("jjournal"),
3388 N_("Ddeleted"),
3389 N_("ddirectory"),
3390 N_("eentry"),
3391 N_("E@e '%Dn' in %p (%i)"),
3392 N_("ffilesystem"),
3393 N_("Ffor @i %i (%Q) is"),
3394 N_("ggroup"),
3395 N_("hHTREE @d @i"),
3396 N_("llost+found"),
3397 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00003398 N_("mmultiply-claimed"),
3399 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003400 N_("oorphaned"),
3401 N_("pproblem in"),
3402 N_("rroot @i"),
3403 N_("sshould be"),
3404 N_("Ssuper@b"),
3405 N_("uunattached"),
3406 N_("vdevice"),
3407 N_("zzero-length"),
3408 "@@",
3409 0
3410 };
3411
3412/*
3413 * Give more user friendly names to the "special" inodes.
3414 */
3415#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003416static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003417{
3418 N_("<The NULL inode>"), /* 0 */
3419 N_("<The bad blocks inode>"), /* 1 */
3420 "/", /* 2 */
3421 N_("<The ACL index inode>"), /* 3 */
3422 N_("<The ACL data inode>"), /* 4 */
3423 N_("<The boot loader inode>"), /* 5 */
3424 N_("<The undelete directory inode>"), /* 6 */
3425 N_("<The group descriptor inode>"), /* 7 */
3426 N_("<The journal inode>"), /* 8 */
3427 N_("<Reserved inode 9>"), /* 9 */
3428 N_("<Reserved inode 10>"), /* 10 */
3429};
3430
3431/*
3432 * This function does "safe" printing. It will convert non-printable
3433 * ASCII characters using '^' and M- notation.
3434 */
3435static void safe_print(const char *cp, int len)
3436{
3437 unsigned char ch;
3438
3439 if (len < 0)
3440 len = strlen(cp);
3441
3442 while (len--) {
3443 ch = *cp++;
3444 if (ch > 128) {
3445 fputs("M-", stdout);
3446 ch -= 128;
3447 }
3448 if ((ch < 32) || (ch == 0x7f)) {
3449 fputc('^', stdout);
3450 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
3451 }
3452 fputc(ch, stdout);
3453 }
3454}
3455
3456
3457/*
3458 * This function prints a pathname, using the ext2fs_get_pathname
3459 * function
3460 */
3461static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
3462{
3463 errcode_t retval;
3464 char *path;
3465
3466 if (!dir && (ino < num_special_inodes)) {
3467 fputs(_(special_inode_name[ino]), stdout);
3468 return;
3469 }
3470
3471 retval = ext2fs_get_pathname(fs, dir, ino, &path);
3472 if (retval)
3473 fputs("???", stdout);
3474 else {
3475 safe_print(path, -1);
3476 ext2fs_free_mem(&path);
3477 }
3478}
3479
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003480static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
3481 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003482/*
3483 * This function handles the '@' expansion. We allow recursive
3484 * expansion; an @ expression can contain further '@' and '%'
3485 * expressions.
3486 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003487static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003488 struct problem_context *pctx,
3489 int *first)
3490{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003491 const char * const *cpp;
3492 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003493
3494 /* Search for the abbreviation */
3495 for (cpp = abbrevs; *cpp; cpp++) {
3496 if (ch == *cpp[0])
3497 break;
3498 }
3499 if (*cpp) {
3500 str = _(*cpp) + 1;
3501 if (*first && islower(*str)) {
3502 *first = 0;
3503 fputc(toupper(*str++), stdout);
3504 }
3505 print_e2fsck_message(ctx, str, pctx, *first);
3506 } else
3507 printf("@%c", ch);
3508}
3509
3510/*
3511 * This function expands '%IX' expressions
3512 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003513static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003514 struct problem_context *ctx)
3515{
3516 struct ext2_inode *inode;
3517 struct ext2_inode_large *large_inode;
3518 char * time_str;
3519 time_t t;
3520 int do_gmt = -1;
3521
3522 if (!ctx || !ctx->inode)
3523 goto no_inode;
3524
3525 inode = ctx->inode;
3526 large_inode = (struct ext2_inode_large *) inode;
3527
3528 switch (ch) {
3529 case 's':
3530 if (LINUX_S_ISDIR(inode->i_mode))
3531 printf("%u", inode->i_size);
3532 else {
3533#ifdef EXT2_NO_64_TYPE
3534 if (inode->i_size_high)
3535 printf("0x%x%08x", inode->i_size_high,
3536 inode->i_size);
3537 else
3538 printf("%u", inode->i_size);
3539#else
3540 printf("%llu", (inode->i_size |
3541 ((__u64) inode->i_size_high << 32)));
3542#endif
3543 }
3544 break;
3545 case 'S':
3546 printf("%u", large_inode->i_extra_isize);
3547 break;
3548 case 'b':
3549 printf("%u", inode->i_blocks);
3550 break;
3551 case 'l':
3552 printf("%d", inode->i_links_count);
3553 break;
3554 case 'm':
3555 printf("0%o", inode->i_mode);
3556 break;
3557 case 'M':
3558 /* The diet libc doesn't respect the TZ environemnt variable */
3559 if (do_gmt == -1) {
3560 time_str = getenv("TZ");
3561 if (!time_str)
3562 time_str = "";
3563 do_gmt = !strcmp(time_str, "GMT");
3564 }
3565 t = inode->i_mtime;
3566 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
3567 printf("%.24s", time_str);
3568 break;
3569 case 'F':
3570 printf("%u", inode->i_faddr);
3571 break;
3572 case 'f':
3573 printf("%u", inode->i_file_acl);
3574 break;
3575 case 'd':
3576 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
3577 inode->i_dir_acl : 0));
3578 break;
3579 case 'u':
3580 printf("%d", (inode->i_uid |
3581 (inode->osd2.linux2.l_i_uid_high << 16)));
3582 break;
3583 case 'g':
3584 printf("%d", (inode->i_gid |
3585 (inode->osd2.linux2.l_i_gid_high << 16)));
3586 break;
3587 default:
3588 no_inode:
3589 printf("%%I%c", ch);
3590 break;
3591 }
3592}
3593
3594/*
3595 * This function expands '%dX' expressions
3596 */
3597static _INLINE_ void expand_dirent_expression(char ch,
3598 struct problem_context *ctx)
3599{
3600 struct ext2_dir_entry *dirent;
3601 int len;
3602
3603 if (!ctx || !ctx->dirent)
3604 goto no_dirent;
3605
3606 dirent = ctx->dirent;
3607
3608 switch (ch) {
3609 case 'i':
3610 printf("%u", dirent->inode);
3611 break;
3612 case 'n':
3613 len = dirent->name_len & 0xFF;
3614 if (len > EXT2_NAME_LEN)
3615 len = EXT2_NAME_LEN;
3616 if (len > dirent->rec_len)
3617 len = dirent->rec_len;
3618 safe_print(dirent->name, len);
3619 break;
3620 case 'r':
3621 printf("%u", dirent->rec_len);
3622 break;
3623 case 'l':
3624 printf("%u", dirent->name_len & 0xFF);
3625 break;
3626 case 't':
3627 printf("%u", dirent->name_len >> 8);
3628 break;
3629 default:
3630 no_dirent:
3631 printf("%%D%c", ch);
3632 break;
3633 }
3634}
3635
3636static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
3637 struct problem_context *ctx)
3638{
3639 if (!ctx)
3640 goto no_context;
3641
3642 switch (ch) {
3643 case '%':
3644 fputc('%', stdout);
3645 break;
3646 case 'b':
3647 printf("%u", ctx->blk);
3648 break;
3649 case 'B':
3650#ifdef EXT2_NO_64_TYPE
3651 printf("%d", ctx->blkcount);
3652#else
3653 printf("%lld", ctx->blkcount);
3654#endif
3655 break;
3656 case 'c':
3657 printf("%u", ctx->blk2);
3658 break;
3659 case 'd':
3660 printf("%u", ctx->dir);
3661 break;
3662 case 'g':
3663 printf("%d", ctx->group);
3664 break;
3665 case 'i':
3666 printf("%u", ctx->ino);
3667 break;
3668 case 'j':
3669 printf("%u", ctx->ino2);
3670 break;
3671 case 'm':
3672 printf("%s", error_message(ctx->errcode));
3673 break;
3674 case 'N':
3675#ifdef EXT2_NO_64_TYPE
3676 printf("%u", ctx->num);
3677#else
3678 printf("%llu", ctx->num);
3679#endif
3680 break;
3681 case 'p':
3682 print_pathname(fs, ctx->ino, 0);
3683 break;
3684 case 'P':
3685 print_pathname(fs, ctx->ino2,
3686 ctx->dirent ? ctx->dirent->inode : 0);
3687 break;
3688 case 'q':
3689 print_pathname(fs, ctx->dir, 0);
3690 break;
3691 case 'Q':
3692 print_pathname(fs, ctx->dir, ctx->ino);
3693 break;
3694 case 'S':
3695 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
3696 break;
3697 case 's':
3698 printf("%s", ctx->str ? ctx->str : "NULL");
3699 break;
3700 case 'X':
3701#ifdef EXT2_NO_64_TYPE
3702 printf("0x%x", ctx->num);
3703#else
3704 printf("0x%llx", ctx->num);
3705#endif
3706 break;
3707 default:
3708 no_context:
3709 printf("%%%c", ch);
3710 break;
3711 }
3712}
3713
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003714
3715static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003716 struct problem_context *pctx, int first)
3717{
3718 ext2_filsys fs = ctx->fs;
3719 const char * cp;
3720 int i;
3721
3722 e2fsck_clear_progbar(ctx);
3723 for (cp = msg; *cp; cp++) {
3724 if (cp[0] == '@') {
3725 cp++;
3726 expand_at_expression(ctx, *cp, pctx, &first);
3727 } else if (cp[0] == '%' && cp[1] == 'I') {
3728 cp += 2;
3729 expand_inode_expression(*cp, pctx);
3730 } else if (cp[0] == '%' && cp[1] == 'D') {
3731 cp += 2;
3732 expand_dirent_expression(*cp, pctx);
3733 } else if ((cp[0] == '%')) {
3734 cp++;
3735 expand_percent_expression(fs, *cp, pctx);
3736 } else {
3737 for (i=0; cp[i]; i++)
3738 if ((cp[i] == '@') || cp[i] == '%')
3739 break;
3740 printf("%.*s", i, cp);
3741 cp += i-1;
3742 }
3743 first = 0;
3744 }
3745}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003746
3747
3748/*
3749 * region.c --- code which manages allocations within a region.
3750 */
3751
3752struct region_el {
3753 region_addr_t start;
3754 region_addr_t end;
3755 struct region_el *next;
3756};
3757
3758struct region_struct {
3759 region_addr_t min;
3760 region_addr_t max;
3761 struct region_el *allocated;
3762};
3763
3764static region_t region_create(region_addr_t min, region_addr_t max)
3765{
3766 region_t region;
3767
3768 region = malloc(sizeof(struct region_struct));
3769 if (!region)
3770 return NULL;
3771 memset(region, 0, sizeof(struct region_struct));
3772 region->min = min;
3773 region->max = max;
3774 return region;
3775}
3776
3777static void region_free(region_t region)
3778{
3779 struct region_el *r, *next;
3780
3781 for (r = region->allocated; r; r = next) {
3782 next = r->next;
3783 free(r);
3784 }
3785 memset(region, 0, sizeof(struct region_struct));
3786 free(region);
3787}
3788
3789static int region_allocate(region_t region, region_addr_t start, int n)
3790{
3791 struct region_el *r, *new_region, *prev, *next;
3792 region_addr_t end;
3793
3794 end = start+n;
3795 if ((start < region->min) || (end > region->max))
3796 return -1;
3797 if (n == 0)
3798 return 1;
3799
3800 /*
3801 * Search through the linked list. If we find that it
3802 * conflicts witih something that's already allocated, return
3803 * 1; if we can find an existing region which we can grow, do
3804 * so. Otherwise, stop when we find the appropriate place
3805 * insert a new region element into the linked list.
3806 */
3807 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
3808 if (((start >= r->start) && (start < r->end)) ||
3809 ((end > r->start) && (end <= r->end)) ||
3810 ((start <= r->start) && (end >= r->end)))
3811 return 1;
3812 if (end == r->start) {
3813 r->start = start;
3814 return 0;
3815 }
3816 if (start == r->end) {
3817 if ((next = r->next)) {
3818 if (end > next->start)
3819 return 1;
3820 if (end == next->start) {
3821 r->end = next->end;
3822 r->next = next->next;
3823 free(next);
3824 return 0;
3825 }
3826 }
3827 r->end = end;
3828 return 0;
3829 }
3830 if (start < r->start)
3831 break;
3832 }
3833 /*
3834 * Insert a new region element structure into the linked list
3835 */
3836 new_region = malloc(sizeof(struct region_el));
3837 if (!new_region)
3838 return -1;
3839 new_region->start = start;
3840 new_region->end = start + n;
3841 new_region->next = r;
3842 if (prev)
3843 prev->next = new_region;
3844 else
3845 region->allocated = new_region;
3846 return 0;
3847}
3848
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003849/*
3850 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
3851 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003852 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
3853 * and applies the following tests to each inode:
3854 *
3855 * - The mode field of the inode must be legal.
3856 * - The size and block count fields of the inode are correct.
3857 * - A data block must not be used by another inode
3858 *
3859 * Pass 1 also gathers the collects the following information:
3860 *
3861 * - A bitmap of which inodes are in use. (inode_used_map)
3862 * - A bitmap of which inodes are directories. (inode_dir_map)
3863 * - A bitmap of which inodes are regular files. (inode_reg_map)
3864 * - A bitmap of which inodes have bad fields. (inode_bad_map)
3865 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
3866 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
3867 * - A bitmap of which blocks are in use. (block_found_map)
3868 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
3869 * - The data blocks of the directory inodes. (dir_map)
3870 *
3871 * Pass 1 is designed to stash away enough information so that the
3872 * other passes should not need to read in the inode information
3873 * during the normal course of a filesystem check. (Althogh if an
3874 * inconsistency is detected, other passes may need to read in an
3875 * inode to fix it.)
3876 *
3877 * Note that pass 1B will be invoked if there are any duplicate blocks
3878 * found.
3879 */
3880
3881
3882static int process_block(ext2_filsys fs, blk_t *blocknr,
3883 e2_blkcnt_t blockcnt, blk_t ref_blk,
3884 int ref_offset, void *priv_data);
3885static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
3886 e2_blkcnt_t blockcnt, blk_t ref_blk,
3887 int ref_offset, void *priv_data);
3888static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
3889 char *block_buf);
3890static void mark_table_blocks(e2fsck_t ctx);
3891static void alloc_bb_map(e2fsck_t ctx);
3892static void alloc_imagic_map(e2fsck_t ctx);
3893static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
3894static void handle_fs_bad_blocks(e2fsck_t ctx);
3895static void process_inodes(e2fsck_t ctx, char *block_buf);
3896static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
3897static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
3898 dgrp_t group, void * priv_data);
3899static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3900 char *block_buf, int adjust_sign);
3901/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
3902
3903static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
3904 struct ext2_inode * inode, int bufsize,
3905 const char *proc);
3906
3907struct process_block_struct_1 {
3908 ext2_ino_t ino;
3909 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
3910 fragmented:1, compressed:1, bbcheck:1;
3911 blk_t num_blocks;
3912 blk_t max_blocks;
3913 e2_blkcnt_t last_block;
3914 int num_illegal_blocks;
3915 blk_t previous_block;
3916 struct ext2_inode *inode;
3917 struct problem_context *pctx;
3918 ext2fs_block_bitmap fs_meta_blocks;
3919 e2fsck_t ctx;
3920};
3921
3922struct process_inode_block {
3923 ext2_ino_t ino;
3924 struct ext2_inode inode;
3925};
3926
3927struct scan_callback_struct {
3928 e2fsck_t ctx;
3929 char *block_buf;
3930};
3931
3932/*
3933 * For the inodes to process list.
3934 */
3935static struct process_inode_block *inodes_to_process;
3936static int process_inode_count;
3937
3938static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
3939 EXT2_MIN_BLOCK_LOG_SIZE + 1];
3940
3941/*
3942 * Free all memory allocated by pass1 in preparation for restarting
3943 * things.
3944 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003945static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003946{
3947 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003948}
3949
3950/*
3951 * Check to make sure a device inode is real. Returns 1 if the device
3952 * checks out, 0 if not.
3953 *
3954 * Note: this routine is now also used to check FIFO's and Sockets,
3955 * since they have the same requirement; the i_block fields should be
3956 * zero.
3957 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003958static int
3959e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003960{
3961 int i;
3962
3963 /*
3964 * If i_blocks is non-zero, or the index flag is set, then
3965 * this is a bogus device/fifo/socket
3966 */
3967 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
3968 (inode->i_flags & EXT2_INDEX_FL))
3969 return 0;
3970
3971 /*
3972 * We should be able to do the test below all the time, but
3973 * because the kernel doesn't forcibly clear the device
3974 * inode's additional i_block fields, there are some rare
3975 * occasions when a legitimate device inode will have non-zero
3976 * additional i_block fields. So for now, we only complain
3977 * when the immutable flag is set, which should never happen
3978 * for devices. (And that's when the problem is caused, since
3979 * you can't set or clear immutable flags for devices.) Once
3980 * the kernel has been fixed we can change this...
3981 */
3982 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
3983 for (i=4; i < EXT2_N_BLOCKS; i++)
3984 if (inode->i_block[i])
3985 return 0;
3986 }
3987 return 1;
3988}
3989
3990/*
3991 * Check to make sure a symlink inode is real. Returns 1 if the symlink
3992 * checks out, 0 if not.
3993 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003994static int
3995e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003996{
3997 unsigned int len;
3998 int i;
3999 blk_t blocks;
4000
4001 if ((inode->i_size_high || inode->i_size == 0) ||
4002 (inode->i_flags & EXT2_INDEX_FL))
4003 return 0;
4004
4005 blocks = ext2fs_inode_data_blocks(fs, inode);
4006 if (blocks) {
4007 if ((inode->i_size >= fs->blocksize) ||
4008 (blocks != fs->blocksize >> 9) ||
4009 (inode->i_block[0] < fs->super->s_first_data_block) ||
4010 (inode->i_block[0] >= fs->super->s_blocks_count))
4011 return 0;
4012
4013 for (i = 1; i < EXT2_N_BLOCKS; i++)
4014 if (inode->i_block[i])
4015 return 0;
4016
4017 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
4018 return 0;
4019
4020 len = strnlen(buf, fs->blocksize);
4021 if (len == fs->blocksize)
4022 return 0;
4023 } else {
4024 if (inode->i_size >= sizeof(inode->i_block))
4025 return 0;
4026
4027 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
4028 if (len == sizeof(inode->i_block))
4029 return 0;
4030 }
4031 if (len != inode->i_size)
4032 return 0;
4033 return 1;
4034}
4035
4036/*
4037 * If the immutable (or append-only) flag is set on the inode, offer
4038 * to clear it.
4039 */
4040#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
4041static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
4042{
4043 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
4044 return;
4045
4046 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
4047 return;
4048
4049 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
4050 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
4051}
4052
4053/*
4054 * If device, fifo or socket, check size is zero -- if not offer to
4055 * clear it
4056 */
4057static void check_size(e2fsck_t ctx, struct problem_context *pctx)
4058{
4059 struct ext2_inode *inode = pctx->inode;
4060
4061 if ((inode->i_size == 0) && (inode->i_size_high == 0))
4062 return;
4063
4064 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
4065 return;
4066
4067 inode->i_size = 0;
4068 inode->i_size_high = 0;
4069 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
4070}
4071
4072static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
4073{
4074 struct ext2_super_block *sb = ctx->fs->super;
4075 struct ext2_inode_large *inode;
4076 struct ext2_ext_attr_entry *entry;
4077 char *start, *end;
4078 int storage_size, remain, offs;
4079 int problem = 0;
4080
4081 inode = (struct ext2_inode_large *) pctx->inode;
4082 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
4083 inode->i_extra_isize;
4084 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
4085 inode->i_extra_isize + sizeof(__u32);
4086 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
4087 entry = (struct ext2_ext_attr_entry *) start;
4088
4089 /* scan all entry's headers first */
4090
4091 /* take finish entry 0UL into account */
4092 remain = storage_size - sizeof(__u32);
4093 offs = end - start;
4094
4095 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
4096
4097 /* header eats this space */
4098 remain -= sizeof(struct ext2_ext_attr_entry);
4099
4100 /* is attribute name valid? */
4101 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
4102 pctx->num = entry->e_name_len;
4103 problem = PR_1_ATTR_NAME_LEN;
4104 goto fix;
4105 }
4106
4107 /* attribute len eats this space */
4108 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
4109
4110 /* check value size */
4111 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
4112 pctx->num = entry->e_value_size;
4113 problem = PR_1_ATTR_VALUE_SIZE;
4114 goto fix;
4115 }
4116
4117 /* check value placement */
4118 if (entry->e_value_offs +
4119 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
4120 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
4121 pctx->num = entry->e_value_offs;
4122 problem = PR_1_ATTR_VALUE_OFFSET;
4123 goto fix;
4124 }
4125
4126 /* e_value_block must be 0 in inode's ea */
4127 if (entry->e_value_block != 0) {
4128 pctx->num = entry->e_value_block;
4129 problem = PR_1_ATTR_VALUE_BLOCK;
4130 goto fix;
4131 }
4132
4133 /* e_hash must be 0 in inode's ea */
4134 if (entry->e_hash != 0) {
4135 pctx->num = entry->e_hash;
4136 problem = PR_1_ATTR_HASH;
4137 goto fix;
4138 }
4139
4140 remain -= entry->e_value_size;
4141 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
4142
4143 entry = EXT2_EXT_ATTR_NEXT(entry);
4144 }
4145fix:
4146 /*
4147 * it seems like a corruption. it's very unlikely we could repair
4148 * EA(s) in automatic fashion -bzzz
4149 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004150 if (problem == 0 || !fix_problem(ctx, problem, pctx))
4151 return;
4152
4153 /* simple remove all possible EA(s) */
4154 *((__u32 *)start) = 0UL;
4155 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
4156 EXT2_INODE_SIZE(sb), "pass1");
4157}
4158
4159static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
4160{
4161 struct ext2_super_block *sb = ctx->fs->super;
4162 struct ext2_inode_large *inode;
4163 __u32 *eamagic;
4164 int min, max;
4165
4166 inode = (struct ext2_inode_large *) pctx->inode;
4167 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
4168 /* this isn't large inode. so, nothing to check */
4169 return;
4170 }
4171
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004172 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
4173 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
4174 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
4175 /*
4176 * For now we will allow i_extra_isize to be 0, but really
4177 * implementations should never allow i_extra_isize to be 0
4178 */
4179 if (inode->i_extra_isize &&
4180 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
4181 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
4182 return;
4183 inode->i_extra_isize = min;
4184 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
4185 EXT2_INODE_SIZE(sb), "pass1");
4186 return;
4187 }
4188
4189 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
4190 inode->i_extra_isize);
4191 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
4192 /* it seems inode has an extended attribute(s) in body */
4193 check_ea_in_inode(ctx, pctx);
4194 }
4195}
4196
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004197static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004198{
4199 int i;
4200 __u64 max_sizes;
4201 ext2_filsys fs = ctx->fs;
4202 ext2_ino_t ino;
4203 struct ext2_inode *inode;
4204 ext2_inode_scan scan;
4205 char *block_buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004206 unsigned char frag, fsize;
4207 struct problem_context pctx;
4208 struct scan_callback_struct scan_struct;
4209 struct ext2_super_block *sb = ctx->fs->super;
4210 int imagic_fs;
4211 int busted_fs_time = 0;
4212 int inode_size;
4213
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004214 clear_problem_context(&pctx);
4215
4216 if (!(ctx->options & E2F_OPT_PREEN))
4217 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
4218
4219 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4220 !(ctx->options & E2F_OPT_NO)) {
4221 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
4222 ctx->dirs_to_hash = 0;
4223 }
4224
Rob Landley3e72c592006-04-06 22:49:04 +00004225 /* Pass 1 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004226
4227#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
4228
4229 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
4230 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
4231 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
4232 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
4233 max_sizes = (max_sizes * (1UL << i)) - 1;
4234 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
4235 }
4236#undef EXT2_BPP
4237
4238 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
4239
4240 /*
4241 * Allocate bitmaps structures
4242 */
4243 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
4244 &ctx->inode_used_map);
4245 if (pctx.errcode) {
4246 pctx.num = 1;
4247 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4248 ctx->flags |= E2F_FLAG_ABORT;
4249 return;
4250 }
4251 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4252 _("directory inode map"), &ctx->inode_dir_map);
4253 if (pctx.errcode) {
4254 pctx.num = 2;
4255 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4256 ctx->flags |= E2F_FLAG_ABORT;
4257 return;
4258 }
4259 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4260 _("regular file inode map"), &ctx->inode_reg_map);
4261 if (pctx.errcode) {
4262 pctx.num = 6;
4263 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4264 ctx->flags |= E2F_FLAG_ABORT;
4265 return;
4266 }
4267 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
4268 &ctx->block_found_map);
4269 if (pctx.errcode) {
4270 pctx.num = 1;
4271 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
4272 ctx->flags |= E2F_FLAG_ABORT;
4273 return;
4274 }
4275 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
4276 &ctx->inode_link_info);
4277 if (pctx.errcode) {
4278 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
4279 ctx->flags |= E2F_FLAG_ABORT;
4280 return;
4281 }
4282 inode_size = EXT2_INODE_SIZE(fs->super);
4283 inode = (struct ext2_inode *)
4284 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
4285
4286 inodes_to_process = (struct process_inode_block *)
4287 e2fsck_allocate_memory(ctx,
4288 (ctx->process_inode_size *
4289 sizeof(struct process_inode_block)),
4290 "array of inodes to process");
4291 process_inode_count = 0;
4292
4293 pctx.errcode = ext2fs_init_dblist(fs, 0);
4294 if (pctx.errcode) {
4295 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
4296 ctx->flags |= E2F_FLAG_ABORT;
4297 return;
4298 }
4299
4300 /*
4301 * If the last orphan field is set, clear it, since the pass1
4302 * processing will automatically find and clear the orphans.
4303 * In the future, we may want to try using the last_orphan
4304 * linked list ourselves, but for now, we clear it so that the
4305 * ext3 mount code won't get confused.
4306 */
4307 if (!(ctx->options & E2F_OPT_READONLY)) {
4308 if (fs->super->s_last_orphan) {
4309 fs->super->s_last_orphan = 0;
4310 ext2fs_mark_super_dirty(fs);
4311 }
4312 }
4313
4314 mark_table_blocks(ctx);
4315 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
4316 "block interate buffer");
4317 e2fsck_use_inode_shortcuts(ctx, 1);
4318 ehandler_operation(_("doing inode scan"));
4319 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4320 &scan);
4321 if (pctx.errcode) {
4322 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
4323 ctx->flags |= E2F_FLAG_ABORT;
4324 return;
4325 }
4326 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
4327 ctx->stashed_inode = inode;
4328 scan_struct.ctx = ctx;
4329 scan_struct.block_buf = block_buf;
4330 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
4331 if (ctx->progress)
4332 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
4333 return;
Mike Frysinger874af852006-03-08 07:03:27 +00004334 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
4335 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004336 busted_fs_time = 1;
4337
4338 while (1) {
4339 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
4340 inode, inode_size);
4341 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4342 return;
4343 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
4344 if (!ctx->inode_bb_map)
4345 alloc_bb_map(ctx);
4346 ext2fs_mark_inode_bitmap(ctx->inode_bb_map, ino);
4347 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4348 continue;
4349 }
4350 if (pctx.errcode) {
4351 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
4352 ctx->flags |= E2F_FLAG_ABORT;
4353 return;
4354 }
4355 if (!ino)
4356 break;
4357 pctx.ino = ino;
4358 pctx.inode = inode;
4359 ctx->stashed_ino = ino;
4360 if (inode->i_links_count) {
4361 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
4362 ino, inode->i_links_count);
4363 if (pctx.errcode) {
4364 pctx.num = inode->i_links_count;
4365 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
4366 ctx->flags |= E2F_FLAG_ABORT;
4367 return;
4368 }
4369 }
4370 if (ino == EXT2_BAD_INO) {
4371 struct process_block_struct_1 pb;
4372
4373 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
4374 &pb.fs_meta_blocks);
4375 if (pctx.errcode) {
4376 pctx.num = 4;
4377 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
4378 ctx->flags |= E2F_FLAG_ABORT;
4379 return;
4380 }
4381 pb.ino = EXT2_BAD_INO;
4382 pb.num_blocks = pb.last_block = 0;
4383 pb.num_illegal_blocks = 0;
4384 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
4385 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
4386 pb.inode = inode;
4387 pb.pctx = &pctx;
4388 pb.ctx = ctx;
4389 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
4390 block_buf, process_bad_block, &pb);
4391 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
4392 if (pctx.errcode) {
4393 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
4394 ctx->flags |= E2F_FLAG_ABORT;
4395 return;
4396 }
4397 if (pb.bbcheck)
4398 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
4399 ctx->flags |= E2F_FLAG_ABORT;
4400 return;
4401 }
4402 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4403 clear_problem_context(&pctx);
4404 continue;
4405 } else if (ino == EXT2_ROOT_INO) {
4406 /*
4407 * Make sure the root inode is a directory; if
4408 * not, offer to clear it. It will be
4409 * regnerated in pass #3.
4410 */
4411 if (!LINUX_S_ISDIR(inode->i_mode)) {
4412 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
4413 inode->i_dtime = time(0);
4414 inode->i_links_count = 0;
4415 ext2fs_icount_store(ctx->inode_link_info,
4416 ino, 0);
4417 e2fsck_write_inode(ctx, ino, inode,
4418 "pass1");
4419 }
4420
4421 }
4422 /*
4423 * If dtime is set, offer to clear it. mke2fs
4424 * version 0.2b created filesystems with the
4425 * dtime field set for the root and lost+found
4426 * directories. We won't worry about
4427 * /lost+found, since that can be regenerated
4428 * easily. But we will fix the root directory
4429 * as a special case.
4430 */
4431 if (inode->i_dtime && inode->i_links_count) {
4432 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
4433 inode->i_dtime = 0;
4434 e2fsck_write_inode(ctx, ino, inode,
4435 "pass1");
4436 }
4437 }
4438 } else if (ino == EXT2_JOURNAL_INO) {
4439 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4440 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
4441 if (!LINUX_S_ISREG(inode->i_mode) &&
4442 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
4443 &pctx)) {
4444 inode->i_mode = LINUX_S_IFREG;
4445 e2fsck_write_inode(ctx, ino, inode,
4446 "pass1");
4447 }
4448 check_blocks(ctx, &pctx, block_buf);
4449 continue;
4450 }
4451 if ((inode->i_links_count || inode->i_blocks ||
4452 inode->i_blocks || inode->i_block[0]) &&
4453 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
4454 &pctx)) {
4455 memset(inode, 0, inode_size);
4456 ext2fs_icount_store(ctx->inode_link_info,
4457 ino, 0);
4458 e2fsck_write_inode_full(ctx, ino, inode,
4459 inode_size, "pass1");
4460 }
4461 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
4462 int problem = 0;
4463
4464 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4465 if (ino == EXT2_BOOT_LOADER_INO) {
4466 if (LINUX_S_ISDIR(inode->i_mode))
4467 problem = PR_1_RESERVED_BAD_MODE;
4468 } else if (ino == EXT2_RESIZE_INO) {
4469 if (inode->i_mode &&
4470 !LINUX_S_ISREG(inode->i_mode))
4471 problem = PR_1_RESERVED_BAD_MODE;
4472 } else {
4473 if (inode->i_mode != 0)
4474 problem = PR_1_RESERVED_BAD_MODE;
4475 }
4476 if (problem) {
4477 if (fix_problem(ctx, problem, &pctx)) {
4478 inode->i_mode = 0;
4479 e2fsck_write_inode(ctx, ino, inode,
4480 "pass1");
4481 }
4482 }
4483 check_blocks(ctx, &pctx, block_buf);
4484 continue;
4485 }
4486 /*
4487 * Check for inodes who might have been part of the
4488 * orphaned list linked list. They should have gotten
4489 * dealt with by now, unless the list had somehow been
4490 * corrupted.
4491 *
4492 * FIXME: In the future, inodes which are still in use
4493 * (and which are therefore) pending truncation should
4494 * be handled specially. Right now we just clear the
4495 * dtime field, and the normal e2fsck handling of
4496 * inodes where i_size and the inode blocks are
4497 * inconsistent is to fix i_size, instead of releasing
4498 * the extra blocks. This won't catch the inodes that
4499 * was at the end of the orphan list, but it's better
4500 * than nothing. The right answer is that there
4501 * shouldn't be any bugs in the orphan list handling. :-)
4502 */
4503 if (inode->i_dtime && !busted_fs_time &&
4504 inode->i_dtime < ctx->fs->super->s_inodes_count) {
4505 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
4506 inode->i_dtime = inode->i_links_count ?
4507 0 : time(0);
4508 e2fsck_write_inode(ctx, ino, inode,
4509 "pass1");
4510 }
4511 }
4512
4513 /*
4514 * This code assumes that deleted inodes have
4515 * i_links_count set to 0.
4516 */
4517 if (!inode->i_links_count) {
4518 if (!inode->i_dtime && inode->i_mode) {
4519 if (fix_problem(ctx,
4520 PR_1_ZERO_DTIME, &pctx)) {
4521 inode->i_dtime = time(0);
4522 e2fsck_write_inode(ctx, ino, inode,
4523 "pass1");
4524 }
4525 }
4526 continue;
4527 }
4528 /*
4529 * n.b. 0.3c ext2fs code didn't clear i_links_count for
4530 * deleted files. Oops.
4531 *
4532 * Since all new ext2 implementations get this right,
4533 * we now assume that the case of non-zero
4534 * i_links_count and non-zero dtime means that we
4535 * should keep the file, not delete it.
4536 *
4537 */
4538 if (inode->i_dtime) {
4539 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
4540 inode->i_dtime = 0;
4541 e2fsck_write_inode(ctx, ino, inode, "pass1");
4542 }
4543 }
4544
4545 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4546 switch (fs->super->s_creator_os) {
4547 case EXT2_OS_LINUX:
4548 frag = inode->osd2.linux2.l_i_frag;
4549 fsize = inode->osd2.linux2.l_i_fsize;
4550 break;
4551 case EXT2_OS_HURD:
4552 frag = inode->osd2.hurd2.h_i_frag;
4553 fsize = inode->osd2.hurd2.h_i_fsize;
4554 break;
4555 case EXT2_OS_MASIX:
4556 frag = inode->osd2.masix2.m_i_frag;
4557 fsize = inode->osd2.masix2.m_i_fsize;
4558 break;
4559 default:
4560 frag = fsize = 0;
4561 }
4562
4563 if (inode->i_faddr || frag || fsize ||
4564 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
4565 mark_inode_bad(ctx, ino);
4566 if (inode->i_flags & EXT2_IMAGIC_FL) {
4567 if (imagic_fs) {
4568 if (!ctx->inode_imagic_map)
4569 alloc_imagic_map(ctx);
4570 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
4571 ino);
4572 } else {
4573 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
4574 inode->i_flags &= ~EXT2_IMAGIC_FL;
4575 e2fsck_write_inode(ctx, ino,
4576 inode, "pass1");
4577 }
4578 }
4579 }
4580
4581 check_inode_extra_space(ctx, &pctx);
4582
4583 if (LINUX_S_ISDIR(inode->i_mode)) {
4584 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
4585 e2fsck_add_dir_info(ctx, ino, 0);
4586 ctx->fs_directory_count++;
4587 } else if (LINUX_S_ISREG (inode->i_mode)) {
4588 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
4589 ctx->fs_regular_count++;
4590 } else if (LINUX_S_ISCHR (inode->i_mode) &&
4591 e2fsck_pass1_check_device_inode(fs, inode)) {
4592 check_immutable(ctx, &pctx);
4593 check_size(ctx, &pctx);
4594 ctx->fs_chardev_count++;
4595 } else if (LINUX_S_ISBLK (inode->i_mode) &&
4596 e2fsck_pass1_check_device_inode(fs, inode)) {
4597 check_immutable(ctx, &pctx);
4598 check_size(ctx, &pctx);
4599 ctx->fs_blockdev_count++;
4600 } else if (LINUX_S_ISLNK (inode->i_mode) &&
4601 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
4602 check_immutable(ctx, &pctx);
4603 ctx->fs_symlinks_count++;
4604 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
4605 ctx->fs_fast_symlinks_count++;
4606 check_blocks(ctx, &pctx, block_buf);
4607 continue;
4608 }
4609 }
4610 else if (LINUX_S_ISFIFO (inode->i_mode) &&
4611 e2fsck_pass1_check_device_inode(fs, inode)) {
4612 check_immutable(ctx, &pctx);
4613 check_size(ctx, &pctx);
4614 ctx->fs_fifo_count++;
4615 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
4616 e2fsck_pass1_check_device_inode(fs, inode)) {
4617 check_immutable(ctx, &pctx);
4618 check_size(ctx, &pctx);
4619 ctx->fs_sockets_count++;
4620 } else
4621 mark_inode_bad(ctx, ino);
4622 if (inode->i_block[EXT2_IND_BLOCK])
4623 ctx->fs_ind_count++;
4624 if (inode->i_block[EXT2_DIND_BLOCK])
4625 ctx->fs_dind_count++;
4626 if (inode->i_block[EXT2_TIND_BLOCK])
4627 ctx->fs_tind_count++;
4628 if (inode->i_block[EXT2_IND_BLOCK] ||
4629 inode->i_block[EXT2_DIND_BLOCK] ||
4630 inode->i_block[EXT2_TIND_BLOCK] ||
4631 inode->i_file_acl) {
4632 inodes_to_process[process_inode_count].ino = ino;
4633 inodes_to_process[process_inode_count].inode = *inode;
4634 process_inode_count++;
4635 } else
4636 check_blocks(ctx, &pctx, block_buf);
4637
4638 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4639 return;
4640
4641 if (process_inode_count >= ctx->process_inode_size) {
4642 process_inodes(ctx, block_buf);
4643
4644 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4645 return;
4646 }
4647 }
4648 process_inodes(ctx, block_buf);
4649 ext2fs_close_inode_scan(scan);
4650 ehandler_operation(0);
4651
4652 /*
4653 * If any extended attribute blocks' reference counts need to
4654 * be adjusted, either up (ctx->refcount_extra), or down
4655 * (ctx->refcount), then fix them.
4656 */
4657 if (ctx->refcount) {
4658 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
4659 ea_refcount_free(ctx->refcount);
4660 ctx->refcount = 0;
4661 }
4662 if (ctx->refcount_extra) {
4663 adjust_extattr_refcount(ctx, ctx->refcount_extra,
4664 block_buf, +1);
4665 ea_refcount_free(ctx->refcount_extra);
4666 ctx->refcount_extra = 0;
4667 }
4668
4669 if (ctx->invalid_bitmaps)
4670 handle_fs_bad_blocks(ctx);
4671
4672 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00004673 ext2fs_free_block_bitmap(ctx->block_ea_map);
4674 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004675
4676 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
4677 ext2fs_block_bitmap save_bmap;
4678
4679 save_bmap = fs->block_map;
4680 fs->block_map = ctx->block_found_map;
4681 clear_problem_context(&pctx);
4682 pctx.errcode = ext2fs_create_resize_inode(fs);
4683 if (pctx.errcode) {
4684 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
4685 /* Should never get here */
4686 ctx->flags |= E2F_FLAG_ABORT;
4687 return;
4688 }
Mike Frysinger874af852006-03-08 07:03:27 +00004689 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
4690 "recreate inode");
4691 inode->i_mtime = time(0);
4692 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
4693 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004694 fs->block_map = save_bmap;
4695 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
4696 }
4697
4698 if (ctx->flags & E2F_FLAG_RESTART) {
4699 /*
4700 * Only the master copy of the superblock and block
4701 * group descriptors are going to be written during a
4702 * restart, so set the superblock to be used to be the
4703 * master superblock.
4704 */
4705 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004706 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004707 goto endit;
4708 }
4709
4710 if (ctx->block_dup_map) {
4711 if (ctx->options & E2F_OPT_PREEN) {
4712 clear_problem_context(&pctx);
4713 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
4714 }
4715 e2fsck_pass1_dupblocks(ctx, block_buf);
4716 }
4717 ext2fs_free_mem(&inodes_to_process);
4718endit:
4719 e2fsck_use_inode_shortcuts(ctx, 0);
4720
4721 ext2fs_free_mem(&block_buf);
4722 ext2fs_free_mem(&inode);
4723
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004724}
4725
4726/*
4727 * When the inode_scan routines call this callback at the end of the
4728 * glock group, call process_inodes.
4729 */
4730static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004731 ext2_inode_scan scan FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004732 dgrp_t group, void * priv_data)
4733{
4734 struct scan_callback_struct *scan_struct;
4735 e2fsck_t ctx;
4736
4737 scan_struct = (struct scan_callback_struct *) priv_data;
4738 ctx = scan_struct->ctx;
4739
4740 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
4741
4742 if (ctx->progress)
4743 if ((ctx->progress)(ctx, 1, group+1,
4744 ctx->fs->group_desc_count))
4745 return EXT2_ET_CANCEL_REQUESTED;
4746
4747 return 0;
4748}
4749
4750/*
4751 * Process the inodes in the "inodes to process" list.
4752 */
4753static void process_inodes(e2fsck_t ctx, char *block_buf)
4754{
4755 int i;
4756 struct ext2_inode *old_stashed_inode;
4757 ext2_ino_t old_stashed_ino;
4758 const char *old_operation;
4759 char buf[80];
4760 struct problem_context pctx;
4761
Rob Landley3e72c592006-04-06 22:49:04 +00004762 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004763 if (process_inode_count == 0)
4764 return;
4765 old_operation = ehandler_operation(0);
4766 old_stashed_inode = ctx->stashed_inode;
4767 old_stashed_ino = ctx->stashed_ino;
4768 qsort(inodes_to_process, process_inode_count,
4769 sizeof(struct process_inode_block), process_inode_cmp);
4770 clear_problem_context(&pctx);
4771 for (i=0; i < process_inode_count; i++) {
4772 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
4773 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004774 sprintf(buf, _("reading indirect blocks of inode %u"),
4775 pctx.ino);
4776 ehandler_operation(buf);
4777 check_blocks(ctx, &pctx, block_buf);
4778 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4779 break;
4780 }
4781 ctx->stashed_inode = old_stashed_inode;
4782 ctx->stashed_ino = old_stashed_ino;
4783 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00004784 /* end process inodes */
4785
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004786 ehandler_operation(old_operation);
4787}
4788
4789static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
4790{
4791 const struct process_inode_block *ib_a =
4792 (const struct process_inode_block *) a;
4793 const struct process_inode_block *ib_b =
4794 (const struct process_inode_block *) b;
4795 int ret;
4796
4797 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
4798 ib_b->inode.i_block[EXT2_IND_BLOCK]);
4799 if (ret == 0)
4800 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
4801 return ret;
4802}
4803
4804/*
4805 * Mark an inode as being bad in some what
4806 */
4807static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
4808{
4809 struct problem_context pctx;
4810
4811 if (!ctx->inode_bad_map) {
4812 clear_problem_context(&pctx);
4813
4814 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4815 _("bad inode map"), &ctx->inode_bad_map);
4816 if (pctx.errcode) {
4817 pctx.num = 3;
4818 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4819 /* Should never get here */
4820 ctx->flags |= E2F_FLAG_ABORT;
4821 return;
4822 }
4823 }
4824 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
4825}
4826
4827
4828/*
4829 * This procedure will allocate the inode "bb" (badblock) map table
4830 */
4831static void alloc_bb_map(e2fsck_t ctx)
4832{
4833 struct problem_context pctx;
4834
4835 clear_problem_context(&pctx);
4836 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4837 _("inode in bad block map"),
4838 &ctx->inode_bb_map);
4839 if (pctx.errcode) {
4840 pctx.num = 4;
4841 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4842 /* Should never get here */
4843 ctx->flags |= E2F_FLAG_ABORT;
4844 return;
4845 }
4846}
4847
4848/*
4849 * This procedure will allocate the inode imagic table
4850 */
4851static void alloc_imagic_map(e2fsck_t ctx)
4852{
4853 struct problem_context pctx;
4854
4855 clear_problem_context(&pctx);
4856 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4857 _("imagic inode map"),
4858 &ctx->inode_imagic_map);
4859 if (pctx.errcode) {
4860 pctx.num = 5;
4861 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4862 /* Should never get here */
4863 ctx->flags |= E2F_FLAG_ABORT;
4864 return;
4865 }
4866}
4867
4868/*
4869 * Marks a block as in use, setting the dup_map if it's been set
4870 * already. Called by process_block and process_bad_block.
4871 *
4872 * WARNING: Assumes checks have already been done to make sure block
4873 * is valid. This is true in both process_block and process_bad_block.
4874 */
4875static _INLINE_ void mark_block_used(e2fsck_t ctx, blk_t block)
4876{
4877 struct problem_context pctx;
4878
4879 clear_problem_context(&pctx);
4880
4881 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
4882 if (!ctx->block_dup_map) {
4883 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
4884 _("multiply claimed block map"),
4885 &ctx->block_dup_map);
4886 if (pctx.errcode) {
4887 pctx.num = 3;
4888 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
4889 &pctx);
4890 /* Should never get here */
4891 ctx->flags |= E2F_FLAG_ABORT;
4892 return;
4893 }
4894 }
4895 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
4896 } else {
4897 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
4898 }
4899}
4900
4901/*
4902 * Adjust the extended attribute block's reference counts at the end
4903 * of pass 1, either by subtracting out references for EA blocks that
4904 * are still referenced in ctx->refcount, or by adding references for
4905 * EA blocks that had extra references as accounted for in
4906 * ctx->refcount_extra.
4907 */
4908static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
4909 char *block_buf, int adjust_sign)
4910{
4911 struct ext2_ext_attr_header *header;
4912 struct problem_context pctx;
4913 ext2_filsys fs = ctx->fs;
4914 blk_t blk;
4915 __u32 should_be;
4916 int count;
4917
4918 clear_problem_context(&pctx);
4919
4920 ea_refcount_intr_begin(refcount);
4921 while (1) {
4922 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
4923 break;
4924 pctx.blk = blk;
4925 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4926 if (pctx.errcode) {
4927 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
4928 return;
4929 }
4930 header = (struct ext2_ext_attr_header *) block_buf;
4931 pctx.blkcount = header->h_refcount;
4932 should_be = header->h_refcount + adjust_sign * count;
4933 pctx.num = should_be;
4934 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
4935 header->h_refcount = should_be;
4936 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
4937 block_buf);
4938 if (pctx.errcode) {
4939 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
4940 continue;
4941 }
4942 }
4943 }
4944}
4945
4946/*
4947 * Handle processing the extended attribute blocks
4948 */
4949static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
4950 char *block_buf)
4951{
4952 ext2_filsys fs = ctx->fs;
4953 ext2_ino_t ino = pctx->ino;
4954 struct ext2_inode *inode = pctx->inode;
4955 blk_t blk;
4956 char * end;
4957 struct ext2_ext_attr_header *header;
4958 struct ext2_ext_attr_entry *entry;
4959 int count;
4960 region_t region;
4961
4962 blk = inode->i_file_acl;
4963 if (blk == 0)
4964 return 0;
4965
4966 /*
4967 * If the Extended attribute flag isn't set, then a non-zero
4968 * file acl means that the inode is corrupted.
4969 *
4970 * Or if the extended attribute block is an invalid block,
4971 * then the inode is also corrupted.
4972 */
4973 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
4974 (blk < fs->super->s_first_data_block) ||
4975 (blk >= fs->super->s_blocks_count)) {
4976 mark_inode_bad(ctx, ino);
4977 return 0;
4978 }
4979
4980 /* If ea bitmap hasn't been allocated, create it */
4981 if (!ctx->block_ea_map) {
4982 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
4983 _("ext attr block map"),
4984 &ctx->block_ea_map);
4985 if (pctx->errcode) {
4986 pctx->num = 2;
4987 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
4988 ctx->flags |= E2F_FLAG_ABORT;
4989 return 0;
4990 }
4991 }
4992
4993 /* Create the EA refcount structure if necessary */
4994 if (!ctx->refcount) {
4995 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
4996 if (pctx->errcode) {
4997 pctx->num = 1;
4998 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
4999 ctx->flags |= E2F_FLAG_ABORT;
5000 return 0;
5001 }
5002 }
5003
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005004 /* Have we seen this EA block before? */
5005 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
5006 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
5007 return 1;
5008 /* Ooops, this EA was referenced more than it stated */
5009 if (!ctx->refcount_extra) {
5010 pctx->errcode = ea_refcount_create(0,
5011 &ctx->refcount_extra);
5012 if (pctx->errcode) {
5013 pctx->num = 2;
5014 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
5015 ctx->flags |= E2F_FLAG_ABORT;
5016 return 0;
5017 }
5018 }
5019 ea_refcount_increment(ctx->refcount_extra, blk, 0);
5020 return 1;
5021 }
5022
5023 /*
5024 * OK, we haven't seen this EA block yet. So we need to
5025 * validate it
5026 */
5027 pctx->blk = blk;
5028 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
5029 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
5030 goto clear_extattr;
5031 header = (struct ext2_ext_attr_header *) block_buf;
5032 pctx->blk = inode->i_file_acl;
5033 if (((ctx->ext_attr_ver == 1) &&
5034 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
5035 ((ctx->ext_attr_ver == 2) &&
5036 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
5037 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
5038 goto clear_extattr;
5039 }
5040
5041 if (header->h_blocks != 1) {
5042 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
5043 goto clear_extattr;
5044 }
5045
5046 region = region_create(0, fs->blocksize);
5047 if (!region) {
5048 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
5049 ctx->flags |= E2F_FLAG_ABORT;
5050 return 0;
5051 }
5052 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
5053 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5054 goto clear_extattr;
5055 }
5056
5057 entry = (struct ext2_ext_attr_entry *)(header+1);
5058 end = block_buf + fs->blocksize;
5059 while ((char *)entry < end && *(__u32 *)entry) {
5060 if (region_allocate(region, (char *)entry - (char *)header,
5061 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
5062 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5063 goto clear_extattr;
5064 }
5065 if ((ctx->ext_attr_ver == 1 &&
5066 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
5067 (ctx->ext_attr_ver == 2 &&
5068 entry->e_name_index == 0)) {
5069 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
5070 goto clear_extattr;
5071 }
5072 if (entry->e_value_block != 0) {
5073 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
5074 goto clear_extattr;
5075 }
5076 if (entry->e_value_size &&
5077 region_allocate(region, entry->e_value_offs,
5078 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
5079 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5080 goto clear_extattr;
5081 }
5082 entry = EXT2_EXT_ATTR_NEXT(entry);
5083 }
5084 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
5085 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5086 goto clear_extattr;
5087 }
5088 region_free(region);
5089
5090 count = header->h_refcount - 1;
5091 if (count)
5092 ea_refcount_store(ctx->refcount, blk, count);
5093 mark_block_used(ctx, blk);
5094 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
5095
5096 return 1;
5097
5098clear_extattr:
5099 inode->i_file_acl = 0;
5100 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
5101 return 0;
5102}
5103
5104/* Returns 1 if bad htree, 0 if OK */
5105static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005106 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005107 struct ext2_inode *inode,
5108 char *block_buf)
5109{
5110 struct ext2_dx_root_info *root;
5111 ext2_filsys fs = ctx->fs;
5112 errcode_t retval;
5113 blk_t blk;
5114
5115 if ((!LINUX_S_ISDIR(inode->i_mode) &&
5116 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
5117 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
5118 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
5119 return 1;
5120
5121 blk = inode->i_block[0];
5122 if (((blk == 0) ||
5123 (blk < fs->super->s_first_data_block) ||
5124 (blk >= fs->super->s_blocks_count)) &&
5125 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5126 return 1;
5127
5128 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
5129 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5130 return 1;
5131
5132 /* XXX should check that beginning matches a directory */
5133 root = (struct ext2_dx_root_info *) (block_buf + 24);
5134
5135 if ((root->reserved_zero || root->info_length < 8) &&
5136 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5137 return 1;
5138
5139 pctx->num = root->hash_version;
5140 if ((root->hash_version != EXT2_HASH_LEGACY) &&
5141 (root->hash_version != EXT2_HASH_HALF_MD4) &&
5142 (root->hash_version != EXT2_HASH_TEA) &&
5143 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
5144 return 1;
5145
5146 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
5147 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
5148 return 1;
5149
5150 pctx->num = root->indirect_levels;
5151 if ((root->indirect_levels > 1) &&
5152 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
5153 return 1;
5154
5155 return 0;
5156}
5157
5158/*
5159 * This subroutine is called on each inode to account for all of the
5160 * blocks used by that inode.
5161 */
5162static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
5163 char *block_buf)
5164{
5165 ext2_filsys fs = ctx->fs;
5166 struct process_block_struct_1 pb;
5167 ext2_ino_t ino = pctx->ino;
5168 struct ext2_inode *inode = pctx->inode;
5169 int bad_size = 0;
5170 int dirty_inode = 0;
5171 __u64 size;
5172
5173 pb.ino = ino;
5174 pb.num_blocks = 0;
5175 pb.last_block = -1;
5176 pb.num_illegal_blocks = 0;
5177 pb.suppress = 0; pb.clear = 0;
5178 pb.fragmented = 0;
5179 pb.compressed = 0;
5180 pb.previous_block = 0;
5181 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
5182 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
5183 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
5184 pb.inode = inode;
5185 pb.pctx = pctx;
5186 pb.ctx = ctx;
5187 pctx->ino = ino;
5188 pctx->errcode = 0;
5189
5190 if (inode->i_flags & EXT2_COMPRBLK_FL) {
5191 if (fs->super->s_feature_incompat &
5192 EXT2_FEATURE_INCOMPAT_COMPRESSION)
5193 pb.compressed = 1;
5194 else {
5195 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
5196 inode->i_flags &= ~EXT2_COMPRBLK_FL;
5197 dirty_inode++;
5198 }
5199 }
5200 }
5201
5202 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
5203 pb.num_blocks++;
5204
5205 if (ext2fs_inode_has_valid_blocks(inode))
5206 pctx->errcode = ext2fs_block_iterate2(fs, ino,
5207 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
5208 block_buf, process_block, &pb);
5209 end_problem_latch(ctx, PR_LATCH_BLOCK);
5210 end_problem_latch(ctx, PR_LATCH_TOOBIG);
5211 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5212 goto out;
5213 if (pctx->errcode)
5214 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
5215
5216 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
5217 ctx->fs_fragmented++;
5218
5219 if (pb.clear) {
5220 inode->i_links_count = 0;
5221 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
5222 inode->i_dtime = time(0);
5223 dirty_inode++;
5224 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5225 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
5226 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5227 /*
5228 * The inode was probably partially accounted for
5229 * before processing was aborted, so we need to
5230 * restart the pass 1 scan.
5231 */
5232 ctx->flags |= E2F_FLAG_RESTART;
5233 goto out;
5234 }
5235
5236 if (inode->i_flags & EXT2_INDEX_FL) {
5237 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
5238 inode->i_flags &= ~EXT2_INDEX_FL;
5239 dirty_inode++;
5240 } else {
5241#ifdef ENABLE_HTREE
5242 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
5243#endif
5244 }
5245 }
5246 if (ctx->dirs_to_hash && pb.is_dir &&
5247 !(inode->i_flags & EXT2_INDEX_FL) &&
5248 ((inode->i_size / fs->blocksize) >= 3))
5249 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
5250
5251 if (!pb.num_blocks && pb.is_dir) {
5252 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
5253 inode->i_links_count = 0;
5254 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
5255 inode->i_dtime = time(0);
5256 dirty_inode++;
5257 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5258 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
5259 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5260 ctx->fs_directory_count--;
5261 goto out;
5262 }
5263 }
5264
5265 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00005266
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005267 if (pb.is_dir) {
5268 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
5269 if (nblock > (pb.last_block + 1))
5270 bad_size = 1;
5271 else if (nblock < (pb.last_block + 1)) {
5272 if (((pb.last_block + 1) - nblock) >
5273 fs->super->s_prealloc_dir_blocks)
5274 bad_size = 2;
5275 }
5276 } else {
5277 size = EXT2_I_SIZE(inode);
5278 if ((pb.last_block >= 0) &&
5279 (size < (__u64) pb.last_block * fs->blocksize))
5280 bad_size = 3;
5281 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
5282 bad_size = 4;
5283 }
5284 /* i_size for symlinks is checked elsewhere */
5285 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
5286 pctx->num = (pb.last_block+1) * fs->blocksize;
5287 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
5288 inode->i_size = pctx->num;
5289 if (!LINUX_S_ISDIR(inode->i_mode))
5290 inode->i_size_high = pctx->num >> 32;
5291 dirty_inode++;
5292 }
5293 pctx->num = 0;
5294 }
5295 if (LINUX_S_ISREG(inode->i_mode) &&
5296 (inode->i_size_high || inode->i_size & 0x80000000UL))
5297 ctx->large_files++;
5298 if (pb.num_blocks != inode->i_blocks) {
5299 pctx->num = pb.num_blocks;
5300 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
5301 inode->i_blocks = pb.num_blocks;
5302 dirty_inode++;
5303 }
5304 pctx->num = 0;
5305 }
5306out:
5307 if (dirty_inode)
5308 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
5309}
5310
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005311
5312/*
5313 * This is a helper function for check_blocks().
5314 */
5315static int process_block(ext2_filsys fs,
5316 blk_t *block_nr,
5317 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005318 blk_t ref_block FSCK_ATTR((unused)),
5319 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005320 void *priv_data)
5321{
5322 struct process_block_struct_1 *p;
5323 struct problem_context *pctx;
5324 blk_t blk = *block_nr;
5325 int ret_code = 0;
5326 int problem = 0;
5327 e2fsck_t ctx;
5328
5329 p = (struct process_block_struct_1 *) priv_data;
5330 pctx = p->pctx;
5331 ctx = p->ctx;
5332
5333 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
5334 /* todo: Check that the comprblk_fl is high, that the
5335 blkaddr pattern looks right (all non-holes up to
5336 first EXT2FS_COMPRESSED_BLKADDR, then all
5337 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
5338 that the feature_incompat bit is high, and that the
5339 inode is a regular file. If we're doing a "full
5340 check" (a concept introduced to e2fsck by e2compr,
5341 meaning that we look at data blocks as well as
5342 metadata) then call some library routine that
5343 checks the compressed data. I'll have to think
5344 about this, because one particularly important
5345 problem to be able to fix is to recalculate the
5346 cluster size if necessary. I think that perhaps
5347 we'd better do most/all e2compr-specific checks
5348 separately, after the non-e2compr checks. If not
5349 doing a full check, it may be useful to test that
5350 the personality is linux; e.g. if it isn't then
5351 perhaps this really is just an illegal block. */
5352 return 0;
5353 }
5354
5355 if (blk == 0) {
5356 if (p->is_dir == 0) {
5357 /*
5358 * Should never happen, since only directories
5359 * get called with BLOCK_FLAG_HOLE
5360 */
5361#if DEBUG_E2FSCK
5362 printf("process_block() called with blk == 0, "
5363 "blockcnt=%d, inode %lu???\n",
5364 blockcnt, p->ino);
5365#endif
5366 return 0;
5367 }
5368 if (blockcnt < 0)
5369 return 0;
5370 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005371 goto mark_dir;
5372 }
5373 return 0;
5374 }
5375
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005376 /*
5377 * Simplistic fragmentation check. We merely require that the
5378 * file be contiguous. (Which can never be true for really
5379 * big files that are greater than a block group.)
5380 */
5381 if (!HOLE_BLKADDR(p->previous_block)) {
5382 if (p->previous_block+1 != blk)
5383 p->fragmented = 1;
5384 }
5385 p->previous_block = blk;
5386
5387 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
5388 problem = PR_1_TOOBIG_DIR;
5389 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
5390 problem = PR_1_TOOBIG_REG;
5391 if (!p->is_dir && !p->is_reg && blockcnt > 0)
5392 problem = PR_1_TOOBIG_SYMLINK;
5393
5394 if (blk < fs->super->s_first_data_block ||
5395 blk >= fs->super->s_blocks_count)
5396 problem = PR_1_ILLEGAL_BLOCK_NUM;
5397
5398 if (problem) {
5399 p->num_illegal_blocks++;
5400 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
5401 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
5402 p->clear = 1;
5403 return BLOCK_ABORT;
5404 }
5405 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
5406 p->suppress = 1;
5407 set_latch_flags(PR_LATCH_BLOCK,
5408 PRL_SUPPRESS, 0);
5409 }
5410 }
5411 pctx->blk = blk;
5412 pctx->blkcount = blockcnt;
5413 if (fix_problem(ctx, problem, pctx)) {
5414 blk = *block_nr = 0;
5415 ret_code = BLOCK_CHANGED;
5416 goto mark_dir;
5417 } else
5418 return 0;
5419 }
5420
5421 if (p->ino == EXT2_RESIZE_INO) {
5422 /*
5423 * The resize inode has already be sanity checked
5424 * during pass #0 (the superblock checks). All we
5425 * have to do is mark the double indirect block as
5426 * being in use; all of the other blocks are handled
5427 * by mark_table_blocks()).
5428 */
5429 if (blockcnt == BLOCK_COUNT_DIND)
5430 mark_block_used(ctx, blk);
5431 } else
5432 mark_block_used(ctx, blk);
5433 p->num_blocks++;
5434 if (blockcnt >= 0)
5435 p->last_block = blockcnt;
5436mark_dir:
5437 if (p->is_dir && (blockcnt >= 0)) {
5438 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
5439 blk, blockcnt);
5440 if (pctx->errcode) {
5441 pctx->blk = blk;
5442 pctx->num = blockcnt;
5443 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
5444 /* Should never get here */
5445 ctx->flags |= E2F_FLAG_ABORT;
5446 return BLOCK_ABORT;
5447 }
5448 }
5449 return ret_code;
5450}
5451
5452static int process_bad_block(ext2_filsys fs,
5453 blk_t *block_nr,
5454 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005455 blk_t ref_block FSCK_ATTR((unused)),
5456 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005457 void *priv_data)
5458{
5459 struct process_block_struct_1 *p;
5460 blk_t blk = *block_nr;
5461 blk_t first_block;
5462 dgrp_t i;
5463 struct problem_context *pctx;
5464 e2fsck_t ctx;
5465
5466 /*
5467 * Note: This function processes blocks for the bad blocks
5468 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
5469 */
5470
5471 if (!blk)
5472 return 0;
5473
5474 p = (struct process_block_struct_1 *) priv_data;
5475 ctx = p->ctx;
5476 pctx = p->pctx;
5477
5478 pctx->ino = EXT2_BAD_INO;
5479 pctx->blk = blk;
5480 pctx->blkcount = blockcnt;
5481
5482 if ((blk < fs->super->s_first_data_block) ||
5483 (blk >= fs->super->s_blocks_count)) {
5484 if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
5485 *block_nr = 0;
5486 return BLOCK_CHANGED;
5487 } else
5488 return 0;
5489 }
5490
5491 if (blockcnt < 0) {
5492 if (ext2fs_test_block_bitmap(p->fs_meta_blocks, blk)) {
5493 p->bbcheck = 1;
5494 if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
5495 *block_nr = 0;
5496 return BLOCK_CHANGED;
5497 }
5498 } else if (ext2fs_test_block_bitmap(ctx->block_found_map,
5499 blk)) {
5500 p->bbcheck = 1;
5501 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
5502 pctx)) {
5503 *block_nr = 0;
5504 return BLOCK_CHANGED;
5505 }
5506 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5507 return BLOCK_ABORT;
5508 } else
5509 mark_block_used(ctx, blk);
5510 return 0;
5511 }
Rob Landley3e72c592006-04-06 22:49:04 +00005512
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005513 ctx->fs_badblocks_count++;
5514 /*
5515 * If the block is not used, then mark it as used and return.
5516 * If it is already marked as found, this must mean that
5517 * there's an overlap between the filesystem table blocks
5518 * (bitmaps and inode table) and the bad block list.
5519 */
5520 if (!ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
5521 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
5522 return 0;
5523 }
5524 /*
5525 * Try to find the where the filesystem block was used...
5526 */
5527 first_block = fs->super->s_first_data_block;
5528
5529 for (i = 0; i < fs->group_desc_count; i++ ) {
5530 pctx->group = i;
5531 pctx->blk = blk;
5532 if (!ext2fs_bg_has_super(fs, i))
5533 goto skip_super;
5534 if (blk == first_block) {
5535 if (i == 0) {
5536 if (fix_problem(ctx,
5537 PR_1_BAD_PRIMARY_SUPERBLOCK,
5538 pctx)) {
5539 *block_nr = 0;
5540 return BLOCK_CHANGED;
5541 }
5542 return 0;
5543 }
5544 fix_problem(ctx, PR_1_BAD_SUPERBLOCK, pctx);
5545 return 0;
5546 }
5547 if ((blk > first_block) &&
5548 (blk <= first_block + fs->desc_blocks)) {
5549 if (i == 0) {
5550 pctx->blk = *block_nr;
5551 if (fix_problem(ctx,
5552 PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, pctx)) {
5553 *block_nr = 0;
5554 return BLOCK_CHANGED;
5555 }
5556 return 0;
5557 }
5558 fix_problem(ctx, PR_1_BAD_GROUP_DESCRIPTORS, pctx);
5559 return 0;
5560 }
5561 skip_super:
5562 if (blk == fs->group_desc[i].bg_block_bitmap) {
5563 if (fix_problem(ctx, PR_1_BB_BAD_BLOCK, pctx)) {
5564 ctx->invalid_block_bitmap_flag[i]++;
5565 ctx->invalid_bitmaps++;
5566 }
5567 return 0;
5568 }
5569 if (blk == fs->group_desc[i].bg_inode_bitmap) {
5570 if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
5571 ctx->invalid_inode_bitmap_flag[i]++;
5572 ctx->invalid_bitmaps++;
5573 }
5574 return 0;
5575 }
5576 if ((blk >= fs->group_desc[i].bg_inode_table) &&
5577 (blk < (fs->group_desc[i].bg_inode_table +
5578 fs->inode_blocks_per_group))) {
5579 /*
5580 * If there are bad blocks in the inode table,
5581 * the inode scan code will try to do
5582 * something reasonable automatically.
5583 */
5584 return 0;
5585 }
5586 first_block += fs->super->s_blocks_per_group;
5587 }
5588 /*
5589 * If we've gotten to this point, then the only
5590 * possibility is that the bad block inode meta data
5591 * is using a bad block.
5592 */
5593 if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
5594 (blk == p->inode->i_block[EXT2_DIND_BLOCK]) ||
5595 (blk == p->inode->i_block[EXT2_TIND_BLOCK])) {
5596 p->bbcheck = 1;
5597 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK, pctx)) {
5598 *block_nr = 0;
5599 return BLOCK_CHANGED;
5600 }
5601 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5602 return BLOCK_ABORT;
5603 return 0;
5604 }
5605
5606 pctx->group = -1;
5607
5608 /* Warn user that the block wasn't claimed */
5609 fix_problem(ctx, PR_1_PROGERR_CLAIMED_BLOCK, pctx);
5610
5611 return 0;
5612}
5613
5614static void new_table_block(e2fsck_t ctx, blk_t first_block, int group,
5615 const char *name, int num, blk_t *new_block)
5616{
5617 ext2_filsys fs = ctx->fs;
5618 blk_t old_block = *new_block;
5619 int i;
5620 char *buf;
5621 struct problem_context pctx;
5622
5623 clear_problem_context(&pctx);
5624
5625 pctx.group = group;
5626 pctx.blk = old_block;
5627 pctx.str = name;
5628
5629 pctx.errcode = ext2fs_get_free_blocks(fs, first_block,
5630 first_block + fs->super->s_blocks_per_group,
5631 num, ctx->block_found_map, new_block);
5632 if (pctx.errcode) {
5633 pctx.num = num;
5634 fix_problem(ctx, PR_1_RELOC_BLOCK_ALLOCATE, &pctx);
5635 ext2fs_unmark_valid(fs);
5636 return;
5637 }
5638 pctx.errcode = ext2fs_get_mem(fs->blocksize, &buf);
5639 if (pctx.errcode) {
5640 fix_problem(ctx, PR_1_RELOC_MEMORY_ALLOCATE, &pctx);
5641 ext2fs_unmark_valid(fs);
5642 return;
5643 }
5644 ext2fs_mark_super_dirty(fs);
5645 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
5646 pctx.blk2 = *new_block;
5647 fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
5648 PR_1_RELOC_TO), &pctx);
5649 pctx.blk2 = 0;
5650 for (i = 0; i < num; i++) {
5651 pctx.blk = i;
5652 ext2fs_mark_block_bitmap(ctx->block_found_map, (*new_block)+i);
5653 if (old_block) {
5654 pctx.errcode = io_channel_read_blk(fs->io,
5655 old_block + i, 1, buf);
5656 if (pctx.errcode)
5657 fix_problem(ctx, PR_1_RELOC_READ_ERR, &pctx);
5658 } else
5659 memset(buf, 0, fs->blocksize);
5660
5661 pctx.blk = (*new_block) + i;
5662 pctx.errcode = io_channel_write_blk(fs->io, pctx.blk,
5663 1, buf);
5664 if (pctx.errcode)
5665 fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
5666 }
5667 ext2fs_free_mem(&buf);
5668}
5669
5670/*
5671 * This routine gets called at the end of pass 1 if bad blocks are
5672 * detected in the superblock, group descriptors, inode_bitmaps, or
5673 * block bitmaps. At this point, all of the blocks have been mapped
5674 * out, so we can try to allocate new block(s) to replace the bad
5675 * blocks.
5676 */
5677static void handle_fs_bad_blocks(e2fsck_t ctx)
5678{
5679 ext2_filsys fs = ctx->fs;
5680 dgrp_t i;
5681 int first_block = fs->super->s_first_data_block;
5682
5683 for (i = 0; i < fs->group_desc_count; i++) {
5684 if (ctx->invalid_block_bitmap_flag[i]) {
5685 new_table_block(ctx, first_block, i, _("block bitmap"),
5686 1, &fs->group_desc[i].bg_block_bitmap);
5687 }
5688 if (ctx->invalid_inode_bitmap_flag[i]) {
5689 new_table_block(ctx, first_block, i, _("inode bitmap"),
5690 1, &fs->group_desc[i].bg_inode_bitmap);
5691 }
5692 if (ctx->invalid_inode_table_flag[i]) {
5693 new_table_block(ctx, first_block, i, _("inode table"),
5694 fs->inode_blocks_per_group,
5695 &fs->group_desc[i].bg_inode_table);
5696 ctx->flags |= E2F_FLAG_RESTART;
5697 }
5698 first_block += fs->super->s_blocks_per_group;
5699 }
5700 ctx->invalid_bitmaps = 0;
5701}
5702
5703/*
5704 * This routine marks all blocks which are used by the superblock,
5705 * group descriptors, inode bitmaps, and block bitmaps.
5706 */
5707static void mark_table_blocks(e2fsck_t ctx)
5708{
5709 ext2_filsys fs = ctx->fs;
5710 blk_t block, b;
5711 dgrp_t i;
5712 int j;
5713 struct problem_context pctx;
5714
5715 clear_problem_context(&pctx);
5716
5717 block = fs->super->s_first_data_block;
5718 for (i = 0; i < fs->group_desc_count; i++) {
5719 pctx.group = i;
5720
5721 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
5722
5723 /*
5724 * Mark the blocks used for the inode table
5725 */
5726 if (fs->group_desc[i].bg_inode_table) {
5727 for (j = 0, b = fs->group_desc[i].bg_inode_table;
5728 j < fs->inode_blocks_per_group;
5729 j++, b++) {
5730 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5731 b)) {
5732 pctx.blk = b;
5733 if (fix_problem(ctx,
5734 PR_1_ITABLE_CONFLICT, &pctx)) {
5735 ctx->invalid_inode_table_flag[i]++;
5736 ctx->invalid_bitmaps++;
5737 }
5738 } else {
5739 ext2fs_mark_block_bitmap(ctx->block_found_map,
5740 b);
5741 }
5742 }
5743 }
5744
5745 /*
5746 * Mark block used for the block bitmap
5747 */
5748 if (fs->group_desc[i].bg_block_bitmap) {
5749 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5750 fs->group_desc[i].bg_block_bitmap)) {
5751 pctx.blk = fs->group_desc[i].bg_block_bitmap;
5752 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
5753 ctx->invalid_block_bitmap_flag[i]++;
5754 ctx->invalid_bitmaps++;
5755 }
5756 } else {
5757 ext2fs_mark_block_bitmap(ctx->block_found_map,
5758 fs->group_desc[i].bg_block_bitmap);
5759 }
5760
5761 }
5762 /*
5763 * Mark block used for the inode bitmap
5764 */
5765 if (fs->group_desc[i].bg_inode_bitmap) {
5766 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5767 fs->group_desc[i].bg_inode_bitmap)) {
5768 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
5769 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
5770 ctx->invalid_inode_bitmap_flag[i]++;
5771 ctx->invalid_bitmaps++;
5772 }
5773 } else {
5774 ext2fs_mark_block_bitmap(ctx->block_found_map,
5775 fs->group_desc[i].bg_inode_bitmap);
5776 }
5777 }
5778 block += fs->super->s_blocks_per_group;
5779 }
5780}
5781
5782/*
5783 * Thes subroutines short circuits ext2fs_get_blocks and
5784 * ext2fs_check_directory; we use them since we already have the inode
5785 * structure, so there's no point in letting the ext2fs library read
5786 * the inode again.
5787 */
5788static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
5789 blk_t *blocks)
5790{
5791 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5792 int i;
5793
5794 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5795 return EXT2_ET_CALLBACK_NOTHANDLED;
5796
5797 for (i=0; i < EXT2_N_BLOCKS; i++)
5798 blocks[i] = ctx->stashed_inode->i_block[i];
5799 return 0;
5800}
5801
5802static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
5803 struct ext2_inode *inode)
5804{
5805 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5806
5807 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5808 return EXT2_ET_CALLBACK_NOTHANDLED;
5809 *inode = *ctx->stashed_inode;
5810 return 0;
5811}
5812
5813static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
5814 struct ext2_inode *inode)
5815{
5816 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5817
5818 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
5819 *ctx->stashed_inode = *inode;
5820 return EXT2_ET_CALLBACK_NOTHANDLED;
5821}
5822
5823static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
5824{
5825 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5826
5827 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5828 return EXT2_ET_CALLBACK_NOTHANDLED;
5829
5830 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
5831 return EXT2_ET_NO_DIRECTORY;
5832 return 0;
5833}
5834
5835void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
5836{
5837 ext2_filsys fs = ctx->fs;
5838
5839 if (bool) {
5840 fs->get_blocks = pass1_get_blocks;
5841 fs->check_directory = pass1_check_directory;
5842 fs->read_inode = pass1_read_inode;
5843 fs->write_inode = pass1_write_inode;
5844 ctx->stashed_ino = 0;
5845 } else {
5846 fs->get_blocks = 0;
5847 fs->check_directory = 0;
5848 fs->read_inode = 0;
5849 fs->write_inode = 0;
5850 }
5851}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005852
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005853/*
5854 * pass1b.c --- Pass #1b of e2fsck
5855 *
5856 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
5857 * only invoked if pass 1 discovered blocks which are in use by more
5858 * than one inode.
5859 *
5860 * Pass1B scans the data blocks of all the inodes again, generating a
5861 * complete list of duplicate blocks and which inodes have claimed
5862 * them.
5863 *
5864 * Pass1C does a tree-traversal of the filesystem, to determine the
5865 * parent directories of these inodes. This step is necessary so that
5866 * e2fsck can print out the pathnames of affected inodes.
5867 *
5868 * Pass1D is a reconciliation pass. For each inode with duplicate
5869 * blocks, the user is prompted if s/he would like to clone the file
5870 * (so that the file gets a fresh copy of the duplicated blocks) or
5871 * simply to delete the file.
5872 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005873 */
5874
5875
5876/* Needed for architectures where sizeof(int) != sizeof(void *) */
5877#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
5878#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
5879
5880/* Define an extension to the ext2 library's block count information */
5881#define BLOCK_COUNT_EXTATTR (-5)
5882
5883struct block_el {
5884 blk_t block;
5885 struct block_el *next;
5886};
5887
5888struct inode_el {
5889 ext2_ino_t inode;
5890 struct inode_el *next;
5891};
5892
5893struct dup_block {
5894 int num_bad;
5895 struct inode_el *inode_list;
5896};
5897
5898/*
5899 * This structure stores information about a particular inode which
5900 * is sharing blocks with other inodes. This information is collected
5901 * to display to the user, so that the user knows what files he or she
5902 * is dealing with, when trying to decide how to resolve the conflict
5903 * of multiply-claimed blocks.
5904 */
5905struct dup_inode {
5906 ext2_ino_t dir;
5907 int num_dupblocks;
5908 struct ext2_inode inode;
5909 struct block_el *block_list;
5910};
5911
5912static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
5913 e2_blkcnt_t blockcnt, blk_t ref_blk,
5914 int ref_offset, void *priv_data);
5915static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5916 struct dup_inode *dp, char *block_buf);
5917static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5918 struct dup_inode *dp, char* block_buf);
5919static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
5920
5921static void pass1b(e2fsck_t ctx, char *block_buf);
5922static void pass1c(e2fsck_t ctx, char *block_buf);
5923static void pass1d(e2fsck_t ctx, char *block_buf);
5924
5925static int dup_inode_count = 0;
5926
5927static dict_t blk_dict, ino_dict;
5928
5929static ext2fs_inode_bitmap inode_dup_map;
5930
5931static int dict_int_cmp(const void *a, const void *b)
5932{
5933 intptr_t ia, ib;
5934
5935 ia = (intptr_t)a;
5936 ib = (intptr_t)b;
5937
5938 return (ia-ib);
5939}
5940
5941/*
5942 * Add a duplicate block record
5943 */
5944static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
5945 struct ext2_inode *inode)
5946{
5947 dnode_t *n;
5948 struct dup_block *db;
5949 struct dup_inode *di;
5950 struct block_el *blk_el;
5951 struct inode_el *ino_el;
5952
5953 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5954 if (n)
5955 db = (struct dup_block *) dnode_get(n);
5956 else {
5957 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
5958 sizeof(struct dup_block), "duplicate block header");
5959 db->num_bad = 0;
5960 db->inode_list = 0;
5961 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
5962 }
5963 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
5964 sizeof(struct inode_el), "inode element");
5965 ino_el->inode = ino;
5966 ino_el->next = db->inode_list;
5967 db->inode_list = ino_el;
5968 db->num_bad++;
5969
5970 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
5971 if (n)
5972 di = (struct dup_inode *) dnode_get(n);
5973 else {
5974 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
5975 sizeof(struct dup_inode), "duplicate inode header");
5976 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
5977 di->num_dupblocks = 0;
5978 di->block_list = 0;
5979 di->inode = *inode;
5980 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
5981 }
5982 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
5983 sizeof(struct block_el), "block element");
5984 blk_el->block = blk;
5985 blk_el->next = di->block_list;
5986 di->block_list = blk_el;
5987 di->num_dupblocks++;
5988}
5989
5990/*
5991 * Free a duplicate inode record
5992 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005993static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005994{
5995 struct dup_inode *di;
5996 struct block_el *p, *next;
5997
5998 di = (struct dup_inode *) dnode_get(node);
5999 for (p = di->block_list; p; p = next) {
6000 next = p->next;
6001 free(p);
6002 }
6003 free(node);
6004}
6005
6006/*
6007 * Free a duplicate block record
6008 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006009static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006010{
6011 struct dup_block *db;
6012 struct inode_el *p, *next;
6013
6014 db = (struct dup_block *) dnode_get(node);
6015 for (p = db->inode_list; p; p = next) {
6016 next = p->next;
6017 free(p);
6018 }
6019 free(node);
6020}
6021
6022
6023/*
6024 * Main procedure for handling duplicate blocks
6025 */
6026void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
6027{
6028 ext2_filsys fs = ctx->fs;
6029 struct problem_context pctx;
6030
6031 clear_problem_context(&pctx);
6032
6033 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
6034 _("multiply claimed inode map"), &inode_dup_map);
6035 if (pctx.errcode) {
6036 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
6037 ctx->flags |= E2F_FLAG_ABORT;
6038 return;
6039 }
6040
6041 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
6042 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006043 dict_set_allocator(&ino_dict, inode_dnode_free);
6044 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006045
6046 pass1b(ctx, block_buf);
6047 pass1c(ctx, block_buf);
6048 pass1d(ctx, block_buf);
6049
6050 /*
6051 * Time to free all of the accumulated data structures that we
6052 * don't need anymore.
6053 */
6054 dict_free_nodes(&ino_dict);
6055 dict_free_nodes(&blk_dict);
6056}
6057
6058/*
6059 * Scan the inodes looking for inodes that contain duplicate blocks.
6060 */
6061struct process_block_struct_1b {
6062 e2fsck_t ctx;
6063 ext2_ino_t ino;
6064 int dup_blocks;
6065 struct ext2_inode *inode;
6066 struct problem_context *pctx;
6067};
6068
6069static void pass1b(e2fsck_t ctx, char *block_buf)
6070{
6071 ext2_filsys fs = ctx->fs;
6072 ext2_ino_t ino;
6073 struct ext2_inode inode;
6074 ext2_inode_scan scan;
6075 struct process_block_struct_1b pb;
6076 struct problem_context pctx;
6077
6078 clear_problem_context(&pctx);
6079
6080 if (!(ctx->options & E2F_OPT_PREEN))
6081 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
6082 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
6083 &scan);
6084 if (pctx.errcode) {
6085 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
6086 ctx->flags |= E2F_FLAG_ABORT;
6087 return;
6088 }
6089 ctx->stashed_inode = &inode;
6090 pb.ctx = ctx;
6091 pb.pctx = &pctx;
6092 pctx.str = "pass1b";
6093 while (1) {
6094 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
6095 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
6096 continue;
6097 if (pctx.errcode) {
6098 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
6099 ctx->flags |= E2F_FLAG_ABORT;
6100 return;
6101 }
6102 if (!ino)
6103 break;
6104 pctx.ino = ctx->stashed_ino = ino;
6105 if ((ino != EXT2_BAD_INO) &&
6106 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
6107 continue;
6108
6109 pb.ino = ino;
6110 pb.dup_blocks = 0;
6111 pb.inode = &inode;
6112
6113 if (ext2fs_inode_has_valid_blocks(&inode) ||
6114 (ino == EXT2_BAD_INO))
6115 pctx.errcode = ext2fs_block_iterate2(fs, ino,
6116 0, block_buf, process_pass1b_block, &pb);
6117 if (inode.i_file_acl)
6118 process_pass1b_block(fs, &inode.i_file_acl,
6119 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
6120 if (pb.dup_blocks) {
6121 end_problem_latch(ctx, PR_LATCH_DBLOCK);
6122 if (ino >= EXT2_FIRST_INODE(fs->super) ||
6123 ino == EXT2_ROOT_INO)
6124 dup_inode_count++;
6125 }
6126 if (pctx.errcode)
6127 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6128 }
6129 ext2fs_close_inode_scan(scan);
6130 e2fsck_use_inode_shortcuts(ctx, 0);
6131}
6132
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006133static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006134 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006135 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6136 blk_t ref_blk FSCK_ATTR((unused)),
6137 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006138 void *priv_data)
6139{
6140 struct process_block_struct_1b *p;
6141 e2fsck_t ctx;
6142
6143 if (HOLE_BLKADDR(*block_nr))
6144 return 0;
6145 p = (struct process_block_struct_1b *) priv_data;
6146 ctx = p->ctx;
6147
6148 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
6149 return 0;
6150
6151 /* OK, this is a duplicate block */
6152 if (p->ino != EXT2_BAD_INO) {
6153 p->pctx->blk = *block_nr;
6154 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
6155 }
6156 p->dup_blocks++;
6157 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
6158
6159 add_dupe(ctx, p->ino, *block_nr, p->inode);
6160
6161 return 0;
6162}
6163
6164/*
6165 * Pass 1c: Scan directories for inodes with duplicate blocks. This
6166 * is used so that we can print pathnames when prompting the user for
6167 * what to do.
6168 */
6169struct search_dir_struct {
6170 int count;
6171 ext2_ino_t first_inode;
6172 ext2_ino_t max_inode;
6173};
6174
6175static int search_dirent_proc(ext2_ino_t dir, int entry,
6176 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006177 int offset FSCK_ATTR((unused)),
6178 int blocksize FSCK_ATTR((unused)),
6179 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006180 void *priv_data)
6181{
6182 struct search_dir_struct *sd;
6183 struct dup_inode *p;
6184 dnode_t *n;
6185
6186 sd = (struct search_dir_struct *) priv_data;
6187
6188 if (dirent->inode > sd->max_inode)
6189 /* Should abort this inode, but not everything */
6190 return 0;
6191
6192 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
6193 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
6194 return 0;
6195
6196 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
6197 if (!n)
6198 return 0;
6199 p = (struct dup_inode *) dnode_get(n);
6200 p->dir = dir;
6201 sd->count--;
6202
6203 return(sd->count ? 0 : DIRENT_ABORT);
6204}
6205
6206
6207static void pass1c(e2fsck_t ctx, char *block_buf)
6208{
6209 ext2_filsys fs = ctx->fs;
6210 struct search_dir_struct sd;
6211 struct problem_context pctx;
6212
6213 clear_problem_context(&pctx);
6214
6215 if (!(ctx->options & E2F_OPT_PREEN))
6216 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
6217
6218 /*
6219 * Search through all directories to translate inodes to names
6220 * (by searching for the containing directory for that inode.)
6221 */
6222 sd.count = dup_inode_count;
6223 sd.first_inode = EXT2_FIRST_INODE(fs->super);
6224 sd.max_inode = fs->super->s_inodes_count;
6225 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
6226 search_dirent_proc, &sd);
6227}
6228
6229static void pass1d(e2fsck_t ctx, char *block_buf)
6230{
6231 ext2_filsys fs = ctx->fs;
6232 struct dup_inode *p, *t;
6233 struct dup_block *q;
6234 ext2_ino_t *shared, ino;
6235 int shared_len;
6236 int i;
6237 int file_ok;
6238 int meta_data = 0;
6239 struct problem_context pctx;
6240 dnode_t *n, *m;
6241 struct block_el *s;
6242 struct inode_el *r;
6243
6244 clear_problem_context(&pctx);
6245
6246 if (!(ctx->options & E2F_OPT_PREEN))
6247 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
6248 e2fsck_read_bitmaps(ctx);
6249
6250 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
6251 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
6252 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
6253 sizeof(ext2_ino_t) * dict_count(&ino_dict),
6254 "Shared inode list");
6255 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
6256 p = (struct dup_inode *) dnode_get(n);
6257 shared_len = 0;
6258 file_ok = 1;
6259 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00006260 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006261 continue;
6262
6263 /*
6264 * Find all of the inodes which share blocks with this
6265 * one. First we find all of the duplicate blocks
6266 * belonging to this inode, and then search each block
6267 * get the list of inodes, and merge them together.
6268 */
6269 for (s = p->block_list; s; s = s->next) {
6270 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
6271 if (!m)
6272 continue; /* Should never happen... */
6273 q = (struct dup_block *) dnode_get(m);
6274 if (q->num_bad > 1)
6275 file_ok = 0;
6276 if (check_if_fs_block(ctx, s->block)) {
6277 file_ok = 0;
6278 meta_data = 1;
6279 }
6280
6281 /*
6282 * Add all inodes used by this block to the
6283 * shared[] --- which is a unique list, so
6284 * if an inode is already in shared[], don't
6285 * add it again.
6286 */
6287 for (r = q->inode_list; r; r = r->next) {
6288 if (r->inode == ino)
6289 continue;
6290 for (i = 0; i < shared_len; i++)
6291 if (shared[i] == r->inode)
6292 break;
6293 if (i == shared_len) {
6294 shared[shared_len++] = r->inode;
6295 }
6296 }
6297 }
6298
6299 /*
6300 * Report the inode that we are working on
6301 */
6302 pctx.inode = &p->inode;
6303 pctx.ino = ino;
6304 pctx.dir = p->dir;
6305 pctx.blkcount = p->num_dupblocks;
6306 pctx.num = meta_data ? shared_len+1 : shared_len;
6307 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
6308 pctx.blkcount = 0;
6309 pctx.num = 0;
6310
6311 if (meta_data)
6312 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
6313
6314 for (i = 0; i < shared_len; i++) {
6315 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
6316 if (!m)
6317 continue; /* should never happen */
6318 t = (struct dup_inode *) dnode_get(m);
6319 /*
6320 * Report the inode that we are sharing with
6321 */
6322 pctx.inode = &t->inode;
6323 pctx.ino = shared[i];
6324 pctx.dir = t->dir;
6325 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
6326 }
6327 if (file_ok) {
6328 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
6329 continue;
6330 }
6331 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
6332 pctx.errcode = clone_file(ctx, ino, p, block_buf);
6333 if (pctx.errcode)
6334 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
6335 else
6336 continue;
6337 }
6338 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
6339 delete_file(ctx, ino, p, block_buf);
6340 else
6341 ext2fs_unmark_valid(fs);
6342 }
6343 ext2fs_free_mem(&shared);
6344}
6345
6346/*
6347 * Drop the refcount on the dup_block structure, and clear the entry
6348 * in the block_dup_map if appropriate.
6349 */
6350static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
6351{
6352 p->num_bad--;
6353 if (p->num_bad <= 0 ||
6354 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
6355 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
6356}
6357
6358static int delete_file_block(ext2_filsys fs,
6359 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006360 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6361 blk_t ref_block FSCK_ATTR((unused)),
6362 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006363 void *priv_data)
6364{
6365 struct process_block_struct_1b *pb;
6366 struct dup_block *p;
6367 dnode_t *n;
6368 e2fsck_t ctx;
6369
6370 pb = (struct process_block_struct_1b *) priv_data;
6371 ctx = pb->ctx;
6372
6373 if (HOLE_BLKADDR(*block_nr))
6374 return 0;
6375
6376 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
6377 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
6378 if (n) {
6379 p = (struct dup_block *) dnode_get(n);
6380 decrement_badcount(ctx, *block_nr, p);
6381 } else
6382 com_err("delete_file_block", 0,
6383 _("internal error; can't find dup_blk for %d\n"),
6384 *block_nr);
6385 } else {
6386 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6387 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6388 }
6389
6390 return 0;
6391}
6392
6393static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
6394 struct dup_inode *dp, char* block_buf)
6395{
6396 ext2_filsys fs = ctx->fs;
6397 struct process_block_struct_1b pb;
6398 struct ext2_inode inode;
6399 struct problem_context pctx;
6400 unsigned int count;
6401
6402 clear_problem_context(&pctx);
6403 pctx.ino = pb.ino = ino;
6404 pb.dup_blocks = dp->num_dupblocks;
6405 pb.ctx = ctx;
6406 pctx.str = "delete_file";
6407
6408 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
6409 if (ext2fs_inode_has_valid_blocks(&inode))
6410 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6411 delete_file_block, &pb);
6412 if (pctx.errcode)
6413 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6414 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6415 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6416 if (ctx->inode_bad_map)
6417 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6418 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6419
6420 /* Inode may have changed by block_iterate, so reread it */
6421 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
6422 inode.i_links_count = 0;
6423 inode.i_dtime = time(0);
6424 if (inode.i_file_acl &&
6425 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6426 count = 1;
6427 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6428 block_buf, -1, &count);
6429 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6430 pctx.errcode = 0;
6431 count = 1;
6432 }
6433 if (pctx.errcode) {
6434 pctx.blk = inode.i_file_acl;
6435 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
6436 }
6437 /*
6438 * If the count is zero, then arrange to have the
6439 * block deleted. If the block is in the block_dup_map,
6440 * also call delete_file_block since it will take care
6441 * of keeping the accounting straight.
6442 */
6443 if ((count == 0) ||
6444 ext2fs_test_block_bitmap(ctx->block_dup_map,
6445 inode.i_file_acl))
6446 delete_file_block(fs, &inode.i_file_acl,
6447 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
6448 }
6449 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
6450}
6451
6452struct clone_struct {
6453 errcode_t errcode;
6454 ext2_ino_t dir;
6455 char *buf;
6456 e2fsck_t ctx;
6457};
6458
6459static int clone_file_block(ext2_filsys fs,
6460 blk_t *block_nr,
6461 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006462 blk_t ref_block FSCK_ATTR((unused)),
6463 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006464 void *priv_data)
6465{
6466 struct dup_block *p;
6467 blk_t new_block;
6468 errcode_t retval;
6469 struct clone_struct *cs = (struct clone_struct *) priv_data;
6470 dnode_t *n;
6471 e2fsck_t ctx;
6472
6473 ctx = cs->ctx;
6474
6475 if (HOLE_BLKADDR(*block_nr))
6476 return 0;
6477
6478 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
6479 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
6480 if (n) {
6481 p = (struct dup_block *) dnode_get(n);
6482 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
6483 &new_block);
6484 if (retval) {
6485 cs->errcode = retval;
6486 return BLOCK_ABORT;
6487 }
6488 if (cs->dir && (blockcnt >= 0)) {
6489 retval = ext2fs_set_dir_block(fs->dblist,
6490 cs->dir, new_block, blockcnt);
6491 if (retval) {
6492 cs->errcode = retval;
6493 return BLOCK_ABORT;
6494 }
6495 }
Rob Landley3e72c592006-04-06 22:49:04 +00006496
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006497 retval = io_channel_read_blk(fs->io, *block_nr, 1,
6498 cs->buf);
6499 if (retval) {
6500 cs->errcode = retval;
6501 return BLOCK_ABORT;
6502 }
6503 retval = io_channel_write_blk(fs->io, new_block, 1,
6504 cs->buf);
6505 if (retval) {
6506 cs->errcode = retval;
6507 return BLOCK_ABORT;
6508 }
6509 decrement_badcount(ctx, *block_nr, p);
6510 *block_nr = new_block;
6511 ext2fs_mark_block_bitmap(ctx->block_found_map,
6512 new_block);
6513 ext2fs_mark_block_bitmap(fs->block_map, new_block);
6514 return BLOCK_CHANGED;
6515 } else
6516 com_err("clone_file_block", 0,
6517 _("internal error; can't find dup_blk for %d\n"),
6518 *block_nr);
6519 }
6520 return 0;
6521}
6522
6523static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
6524 struct dup_inode *dp, char* block_buf)
6525{
6526 ext2_filsys fs = ctx->fs;
6527 errcode_t retval;
6528 struct clone_struct cs;
6529 struct problem_context pctx;
6530 blk_t blk;
6531 dnode_t *n;
6532 struct inode_el *ino_el;
6533 struct dup_block *db;
6534 struct dup_inode *di;
6535
6536 clear_problem_context(&pctx);
6537 cs.errcode = 0;
6538 cs.dir = 0;
6539 cs.ctx = ctx;
6540 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
6541 if (retval)
6542 return retval;
6543
6544 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
6545 cs.dir = ino;
6546
6547 pctx.ino = ino;
6548 pctx.str = "clone_file";
6549 if (ext2fs_inode_has_valid_blocks(&dp->inode))
6550 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6551 clone_file_block, &cs);
6552 ext2fs_mark_bb_dirty(fs);
6553 if (pctx.errcode) {
6554 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6555 retval = pctx.errcode;
6556 goto errout;
6557 }
6558 if (cs.errcode) {
6559 com_err("clone_file", cs.errcode,
6560 _("returned from clone_file_block"));
6561 retval = cs.errcode;
6562 goto errout;
6563 }
6564 /* The inode may have changed on disk, so we have to re-read it */
6565 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
6566 blk = dp->inode.i_file_acl;
6567 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
6568 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
6569 BLOCK_CHANGED)) {
6570 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
6571 /*
6572 * If we cloned the EA block, find all other inodes
6573 * which refered to that EA block, and modify
6574 * them to point to the new EA block.
6575 */
6576 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
6577 db = (struct dup_block *) dnode_get(n);
6578 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
6579 if (ino_el->inode == ino)
6580 continue;
6581 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
6582 di = (struct dup_inode *) dnode_get(n);
6583 if (di->inode.i_file_acl == blk) {
6584 di->inode.i_file_acl = dp->inode.i_file_acl;
6585 e2fsck_write_inode(ctx, ino_el->inode,
6586 &di->inode, "clone file EA");
6587 decrement_badcount(ctx, blk, db);
6588 }
6589 }
6590 }
6591 retval = 0;
6592errout:
6593 ext2fs_free_mem(&cs.buf);
6594 return retval;
6595}
6596
6597/*
6598 * This routine returns 1 if a block overlaps with one of the superblocks,
6599 * group descriptors, inode bitmaps, or block bitmaps.
6600 */
6601static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
6602{
6603 ext2_filsys fs = ctx->fs;
6604 blk_t block;
6605 dgrp_t i;
6606
6607 block = fs->super->s_first_data_block;
6608 for (i = 0; i < fs->group_desc_count; i++) {
6609
6610 /* Check superblocks/block group descriptros */
6611 if (ext2fs_bg_has_super(fs, i)) {
6612 if (test_block >= block &&
6613 (test_block <= block + fs->desc_blocks))
6614 return 1;
6615 }
6616
6617 /* Check the inode table */
6618 if ((fs->group_desc[i].bg_inode_table) &&
6619 (test_block >= fs->group_desc[i].bg_inode_table) &&
6620 (test_block < (fs->group_desc[i].bg_inode_table +
6621 fs->inode_blocks_per_group)))
6622 return 1;
6623
6624 /* Check the bitmap blocks */
6625 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
6626 (test_block == fs->group_desc[i].bg_inode_bitmap))
6627 return 1;
6628
6629 block += fs->super->s_blocks_per_group;
6630 }
6631 return 0;
6632}
6633/*
6634 * pass2.c --- check directory structure
6635 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006636 * Pass 2 of e2fsck iterates through all active directory inodes, and
6637 * applies to following tests to each directory entry in the directory
6638 * blocks in the inodes:
6639 *
6640 * - The length of the directory entry (rec_len) should be at
6641 * least 8 bytes, and no more than the remaining space
6642 * left in the directory block.
6643 * - The length of the name in the directory entry (name_len)
6644 * should be less than (rec_len - 8).
6645 * - The inode number in the directory entry should be within
6646 * legal bounds.
6647 * - The inode number should refer to a in-use inode.
6648 * - The first entry should be '.', and its inode should be
6649 * the inode of the directory.
6650 * - The second entry should be '..'.
6651 *
6652 * To minimize disk seek time, the directory blocks are processed in
6653 * sorted order of block numbers.
6654 *
6655 * Pass 2 also collects the following information:
6656 * - The inode numbers of the subdirectories for each directory.
6657 *
6658 * Pass 2 relies on the following information from previous passes:
6659 * - The directory information collected in pass 1.
6660 * - The inode_used_map bitmap
6661 * - The inode_bad_map bitmap
6662 * - The inode_dir_map bitmap
6663 *
6664 * Pass 2 frees the following data structures
6665 * - The inode_bad_map bitmap
6666 * - The inode_reg_map bitmap
6667 */
6668
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006669/*
6670 * Keeps track of how many times an inode is referenced.
6671 */
6672static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
6673static int check_dir_block(ext2_filsys fs,
6674 struct ext2_db_entry *dir_blocks_info,
6675 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006676static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
6677 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006678static int update_dir_block(ext2_filsys fs,
6679 blk_t *block_nr,
6680 e2_blkcnt_t blockcnt,
6681 blk_t ref_block,
6682 int ref_offset,
6683 void *priv_data);
6684static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
6685static int htree_depth(struct dx_dir_info *dx_dir,
6686 struct dx_dirblock_info *dx_db);
6687static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b);
6688
6689struct check_dir_struct {
6690 char *buf;
6691 struct problem_context pctx;
6692 int count, max;
6693 e2fsck_t ctx;
6694};
6695
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006696static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006697{
6698 struct ext2_super_block *sb = ctx->fs->super;
6699 struct problem_context pctx;
6700 ext2_filsys fs = ctx->fs;
6701 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006702 struct dir_info *dir;
6703 struct check_dir_struct cd;
6704 struct dx_dir_info *dx_dir;
6705 struct dx_dirblock_info *dx_db, *dx_parent;
6706 int b;
6707 int i, depth;
6708 problem_t code;
6709 int bad_dir;
6710
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006711 clear_problem_context(&cd.pctx);
6712
Rob Landley3e72c592006-04-06 22:49:04 +00006713 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006714
6715 if (!(ctx->options & E2F_OPT_PREEN))
6716 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
6717
6718 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
6719 0, ctx->inode_link_info,
6720 &ctx->inode_count);
6721 if (cd.pctx.errcode) {
6722 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
6723 ctx->flags |= E2F_FLAG_ABORT;
6724 return;
6725 }
6726 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
6727 "directory scan buffer");
6728
6729 /*
6730 * Set up the parent pointer for the root directory, if
6731 * present. (If the root directory is not present, we will
6732 * create it in pass 3.)
6733 */
6734 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
6735 if (dir)
6736 dir->parent = EXT2_ROOT_INO;
6737
6738 cd.buf = buf;
6739 cd.ctx = ctx;
6740 cd.count = 1;
6741 cd.max = ext2fs_dblist_count(fs->dblist);
6742
6743 if (ctx->progress)
6744 (void) (ctx->progress)(ctx, 2, 0, cd.max);
6745
6746 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
6747 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
6748
6749 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
6750 &cd);
6751 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6752 return;
6753 if (cd.pctx.errcode) {
6754 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
6755 ctx->flags |= E2F_FLAG_ABORT;
6756 return;
6757 }
6758
6759#ifdef ENABLE_HTREE
6760 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
6761 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6762 return;
6763 if (dx_dir->numblocks == 0)
6764 continue;
6765 clear_problem_context(&pctx);
6766 bad_dir = 0;
6767 pctx.dir = dx_dir->ino;
6768 dx_db = dx_dir->dx_block;
6769 if (dx_db->flags & DX_FLAG_REFERENCED)
6770 dx_db->flags |= DX_FLAG_DUP_REF;
6771 else
6772 dx_db->flags |= DX_FLAG_REFERENCED;
6773 /*
6774 * Find all of the first and last leaf blocks, and
6775 * update their parent's min and max hash values
6776 */
6777 for (b=0, dx_db = dx_dir->dx_block;
6778 b < dx_dir->numblocks;
6779 b++, dx_db++) {
6780 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
6781 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
6782 continue;
6783 dx_parent = &dx_dir->dx_block[dx_db->parent];
6784 /*
6785 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
6786 */
6787 if (dx_db->flags & DX_FLAG_FIRST)
6788 dx_parent->min_hash = dx_db->min_hash;
6789 /*
6790 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
6791 */
6792 if (dx_db->flags & DX_FLAG_LAST)
6793 dx_parent->max_hash = dx_db->max_hash;
6794 }
6795
6796 for (b=0, dx_db = dx_dir->dx_block;
6797 b < dx_dir->numblocks;
6798 b++, dx_db++) {
6799 pctx.blkcount = b;
6800 pctx.group = dx_db->parent;
6801 code = 0;
6802 if (!(dx_db->flags & DX_FLAG_FIRST) &&
6803 (dx_db->min_hash < dx_db->node_min_hash)) {
6804 pctx.blk = dx_db->min_hash;
6805 pctx.blk2 = dx_db->node_min_hash;
6806 code = PR_2_HTREE_MIN_HASH;
6807 fix_problem(ctx, code, &pctx);
6808 bad_dir++;
6809 }
6810 if (dx_db->type == DX_DIRBLOCK_LEAF) {
6811 depth = htree_depth(dx_dir, dx_db);
6812 if (depth != dx_dir->depth) {
6813 code = PR_2_HTREE_BAD_DEPTH;
6814 fix_problem(ctx, code, &pctx);
6815 bad_dir++;
6816 }
6817 }
6818 /*
6819 * This test doesn't apply for the root block
6820 * at block #0
6821 */
6822 if (b &&
6823 (dx_db->max_hash > dx_db->node_max_hash)) {
6824 pctx.blk = dx_db->max_hash;
6825 pctx.blk2 = dx_db->node_max_hash;
6826 code = PR_2_HTREE_MAX_HASH;
6827 fix_problem(ctx, code, &pctx);
6828 bad_dir++;
6829 }
6830 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
6831 code = PR_2_HTREE_NOTREF;
6832 fix_problem(ctx, code, &pctx);
6833 bad_dir++;
6834 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
6835 code = PR_2_HTREE_DUPREF;
6836 fix_problem(ctx, code, &pctx);
6837 bad_dir++;
6838 }
6839 if (code == 0)
6840 continue;
6841 }
6842 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
6843 clear_htree(ctx, dx_dir->ino);
6844 dx_dir->numblocks = 0;
6845 }
6846 }
6847#endif
6848 ext2fs_free_mem(&buf);
6849 ext2fs_free_dblist(fs->dblist);
6850
Rob Landleye7c43b62006-03-01 16:39:45 +00006851 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
6852 ctx->inode_bad_map = 0;
6853 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
6854 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006855
6856 clear_problem_context(&pctx);
6857 if (ctx->large_files) {
6858 if (!(sb->s_feature_ro_compat &
6859 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
6860 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
6861 sb->s_feature_ro_compat |=
6862 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
6863 ext2fs_mark_super_dirty(fs);
6864 }
6865 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
6866 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
6867 ext2fs_update_dynamic_rev(fs);
6868 ext2fs_mark_super_dirty(fs);
6869 }
6870 } else if (!ctx->large_files &&
6871 (sb->s_feature_ro_compat &
6872 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
6873 if (fs->flags & EXT2_FLAG_RW) {
6874 sb->s_feature_ro_compat &=
6875 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
6876 ext2fs_mark_super_dirty(fs);
6877 }
6878 }
6879
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006880}
6881
6882#define MAX_DEPTH 32000
6883static int htree_depth(struct dx_dir_info *dx_dir,
6884 struct dx_dirblock_info *dx_db)
6885{
6886 int depth = 0;
6887
6888 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
6889 dx_db = &dx_dir->dx_block[dx_db->parent];
6890 depth++;
6891 }
6892 return depth;
6893}
6894
6895static int dict_de_cmp(const void *a, const void *b)
6896{
6897 const struct ext2_dir_entry *de_a, *de_b;
6898 int a_len, b_len;
6899
6900 de_a = (const struct ext2_dir_entry *) a;
6901 a_len = de_a->name_len & 0xFF;
6902 de_b = (const struct ext2_dir_entry *) b;
6903 b_len = de_b->name_len & 0xFF;
6904
6905 if (a_len != b_len)
6906 return (a_len - b_len);
6907
6908 return strncmp(de_a->name, de_b->name, a_len);
6909}
6910
6911/*
6912 * This is special sort function that makes sure that directory blocks
6913 * with a dirblock of zero are sorted to the beginning of the list.
6914 * This guarantees that the root node of the htree directories are
6915 * processed first, so we know what hash version to use.
6916 */
6917static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b)
6918{
6919 const struct ext2_db_entry *db_a =
6920 (const struct ext2_db_entry *) a;
6921 const struct ext2_db_entry *db_b =
6922 (const struct ext2_db_entry *) b;
6923
6924 if (db_a->blockcnt && !db_b->blockcnt)
6925 return 1;
6926
6927 if (!db_a->blockcnt && db_b->blockcnt)
6928 return -1;
6929
6930 if (db_a->blk != db_b->blk)
6931 return (int) (db_a->blk - db_b->blk);
6932
6933 if (db_a->ino != db_b->ino)
6934 return (int) (db_a->ino - db_b->ino);
6935
6936 return (int) (db_a->blockcnt - db_b->blockcnt);
6937}
6938
6939
6940/*
6941 * Make sure the first entry in the directory is '.', and that the
6942 * directory entry is sane.
6943 */
6944static int check_dot(e2fsck_t ctx,
6945 struct ext2_dir_entry *dirent,
6946 ext2_ino_t ino, struct problem_context *pctx)
6947{
6948 struct ext2_dir_entry *nextdir;
6949 int status = 0;
6950 int created = 0;
6951 int new_len;
6952 int problem = 0;
6953
6954 if (!dirent->inode)
6955 problem = PR_2_MISSING_DOT;
6956 else if (((dirent->name_len & 0xFF) != 1) ||
6957 (dirent->name[0] != '.'))
6958 problem = PR_2_1ST_NOT_DOT;
6959 else if (dirent->name[1] != '\0')
6960 problem = PR_2_DOT_NULL_TERM;
6961
6962 if (problem) {
6963 if (fix_problem(ctx, problem, pctx)) {
6964 if (dirent->rec_len < 12)
6965 dirent->rec_len = 12;
6966 dirent->inode = ino;
6967 dirent->name_len = 1;
6968 dirent->name[0] = '.';
6969 dirent->name[1] = '\0';
6970 status = 1;
6971 created = 1;
6972 }
6973 }
6974 if (dirent->inode != ino) {
6975 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
6976 dirent->inode = ino;
6977 status = 1;
6978 }
6979 }
6980 if (dirent->rec_len > 12) {
6981 new_len = dirent->rec_len - 12;
6982 if (new_len > 12) {
6983 if (created ||
6984 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
6985 nextdir = (struct ext2_dir_entry *)
6986 ((char *) dirent + 12);
6987 dirent->rec_len = 12;
6988 nextdir->rec_len = new_len;
6989 nextdir->inode = 0;
6990 nextdir->name_len = 0;
6991 status = 1;
6992 }
6993 }
6994 }
6995 return status;
6996}
6997
6998/*
6999 * Make sure the second entry in the directory is '..', and that the
7000 * directory entry is sane. We do not check the inode number of '..'
7001 * here; this gets done in pass 3.
7002 */
7003static int check_dotdot(e2fsck_t ctx,
7004 struct ext2_dir_entry *dirent,
7005 struct dir_info *dir, struct problem_context *pctx)
7006{
7007 int problem = 0;
7008
7009 if (!dirent->inode)
7010 problem = PR_2_MISSING_DOT_DOT;
7011 else if (((dirent->name_len & 0xFF) != 2) ||
7012 (dirent->name[0] != '.') ||
7013 (dirent->name[1] != '.'))
7014 problem = PR_2_2ND_NOT_DOT_DOT;
7015 else if (dirent->name[2] != '\0')
7016 problem = PR_2_DOT_DOT_NULL_TERM;
7017
7018 if (problem) {
7019 if (fix_problem(ctx, problem, pctx)) {
7020 if (dirent->rec_len < 12)
7021 dirent->rec_len = 12;
7022 /*
7023 * Note: we don't have the parent inode just
7024 * yet, so we will fill it in with the root
7025 * inode. This will get fixed in pass 3.
7026 */
7027 dirent->inode = EXT2_ROOT_INO;
7028 dirent->name_len = 2;
7029 dirent->name[0] = '.';
7030 dirent->name[1] = '.';
7031 dirent->name[2] = '\0';
7032 return 1;
7033 }
7034 return 0;
7035 }
7036 dir->dotdot = dirent->inode;
7037 return 0;
7038}
7039
7040/*
7041 * Check to make sure a directory entry doesn't contain any illegal
7042 * characters.
7043 */
7044static int check_name(e2fsck_t ctx,
7045 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007046 struct problem_context *pctx)
7047{
7048 int i;
7049 int fixup = -1;
7050 int ret = 0;
7051
7052 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
7053 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
7054 if (fixup < 0) {
7055 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
7056 }
7057 if (fixup) {
7058 dirent->name[i] = '.';
7059 ret = 1;
7060 }
7061 }
7062 }
7063 return ret;
7064}
7065
7066/*
7067 * Check the directory filetype (if present)
7068 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007069
7070/*
7071 * Given a mode, return the ext2 file type
7072 */
7073static int ext2_file_type(unsigned int mode)
7074{
7075 if (LINUX_S_ISREG(mode))
7076 return EXT2_FT_REG_FILE;
7077
7078 if (LINUX_S_ISDIR(mode))
7079 return EXT2_FT_DIR;
7080
7081 if (LINUX_S_ISCHR(mode))
7082 return EXT2_FT_CHRDEV;
7083
7084 if (LINUX_S_ISBLK(mode))
7085 return EXT2_FT_BLKDEV;
7086
7087 if (LINUX_S_ISLNK(mode))
7088 return EXT2_FT_SYMLINK;
7089
7090 if (LINUX_S_ISFIFO(mode))
7091 return EXT2_FT_FIFO;
7092
7093 if (LINUX_S_ISSOCK(mode))
7094 return EXT2_FT_SOCK;
7095
7096 return 0;
7097}
7098
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007099static _INLINE_ int check_filetype(e2fsck_t ctx,
7100 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007101 struct problem_context *pctx)
7102{
7103 int filetype = dirent->name_len >> 8;
7104 int should_be = EXT2_FT_UNKNOWN;
7105 struct ext2_inode inode;
7106
7107 if (!(ctx->fs->super->s_feature_incompat &
7108 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
7109 if (filetype == 0 ||
7110 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
7111 return 0;
7112 dirent->name_len = dirent->name_len & 0xFF;
7113 return 1;
7114 }
7115
7116 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
7117 should_be = EXT2_FT_DIR;
7118 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
7119 dirent->inode)) {
7120 should_be = EXT2_FT_REG_FILE;
7121 } else if (ctx->inode_bad_map &&
7122 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
7123 dirent->inode))
7124 should_be = 0;
7125 else {
7126 e2fsck_read_inode(ctx, dirent->inode, &inode,
7127 "check_filetype");
7128 should_be = ext2_file_type(inode.i_mode);
7129 }
7130 if (filetype == should_be)
7131 return 0;
7132 pctx->num = should_be;
7133
7134 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
7135 pctx) == 0)
7136 return 0;
7137
7138 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
7139 return 1;
7140}
7141
7142#ifdef ENABLE_HTREE
7143static void parse_int_node(ext2_filsys fs,
7144 struct ext2_db_entry *db,
7145 struct check_dir_struct *cd,
7146 struct dx_dir_info *dx_dir,
7147 char *block_buf)
7148{
7149 struct ext2_dx_root_info *root;
7150 struct ext2_dx_entry *ent;
7151 struct ext2_dx_countlimit *limit;
7152 struct dx_dirblock_info *dx_db;
7153 int i, expect_limit, count;
7154 blk_t blk;
7155 ext2_dirhash_t min_hash = 0xffffffff;
7156 ext2_dirhash_t max_hash = 0;
7157 ext2_dirhash_t hash = 0, prev_hash;
7158
7159 if (db->blockcnt == 0) {
7160 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007161 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
7162 } else {
7163 ent = (struct ext2_dx_entry *) (block_buf+8);
7164 }
7165 limit = (struct ext2_dx_countlimit *) ent;
7166
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007167 count = ext2fs_le16_to_cpu(limit->count);
7168 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
7169 sizeof(struct ext2_dx_entry);
7170 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
7171 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
7172 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
7173 goto clear_and_exit;
7174 }
7175 if (count > expect_limit) {
7176 cd->pctx.num = count;
7177 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
7178 goto clear_and_exit;
7179 count = expect_limit;
7180 }
7181
7182 for (i=0; i < count; i++) {
7183 prev_hash = hash;
7184 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007185 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
7186 /* Check to make sure the block is valid */
7187 if (blk > (blk_t) dx_dir->numblocks) {
7188 cd->pctx.blk = blk;
7189 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
7190 &cd->pctx))
7191 goto clear_and_exit;
7192 }
7193 if (hash < prev_hash &&
7194 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
7195 goto clear_and_exit;
7196 dx_db = &dx_dir->dx_block[blk];
7197 if (dx_db->flags & DX_FLAG_REFERENCED) {
7198 dx_db->flags |= DX_FLAG_DUP_REF;
7199 } else {
7200 dx_db->flags |= DX_FLAG_REFERENCED;
7201 dx_db->parent = db->blockcnt;
7202 }
7203 if (hash < min_hash)
7204 min_hash = hash;
7205 if (hash > max_hash)
7206 max_hash = hash;
7207 dx_db->node_min_hash = hash;
7208 if ((i+1) < count)
7209 dx_db->node_max_hash =
7210 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
7211 else {
7212 dx_db->node_max_hash = 0xfffffffe;
7213 dx_db->flags |= DX_FLAG_LAST;
7214 }
7215 if (i == 0)
7216 dx_db->flags |= DX_FLAG_FIRST;
7217 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007218 dx_db = &dx_dir->dx_block[db->blockcnt];
7219 dx_db->min_hash = min_hash;
7220 dx_db->max_hash = max_hash;
7221 return;
7222
7223clear_and_exit:
7224 clear_htree(cd->ctx, cd->pctx.ino);
7225 dx_dir->numblocks = 0;
7226}
7227#endif /* ENABLE_HTREE */
7228
7229/*
7230 * Given a busted directory, try to salvage it somehow.
7231 *
7232 */
7233static void salvage_directory(ext2_filsys fs,
7234 struct ext2_dir_entry *dirent,
7235 struct ext2_dir_entry *prev,
7236 unsigned int *offset)
7237{
7238 char *cp = (char *) dirent;
7239 int left = fs->blocksize - *offset - dirent->rec_len;
7240 int name_len = dirent->name_len & 0xFF;
7241
7242 /*
7243 * Special case of directory entry of size 8: copy what's left
7244 * of the directory block up to cover up the invalid hole.
7245 */
7246 if ((left >= 12) && (dirent->rec_len == 8)) {
7247 memmove(cp, cp+8, left);
7248 memset(cp + left, 0, 8);
7249 return;
7250 }
7251 /*
7252 * If the directory entry overruns the end of the directory
7253 * block, and the name is small enough to fit, then adjust the
7254 * record length.
7255 */
7256 if ((left < 0) &&
7257 (name_len + 8 <= dirent->rec_len + left) &&
7258 dirent->inode <= fs->super->s_inodes_count &&
7259 strnlen(dirent->name, name_len) == name_len) {
7260 dirent->rec_len += left;
7261 return;
7262 }
7263 /*
7264 * If the directory entry is a multiple of four, so it is
7265 * valid, let the previous directory entry absorb the invalid
7266 * one.
7267 */
7268 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
7269 prev->rec_len += dirent->rec_len;
7270 *offset += dirent->rec_len;
7271 return;
7272 }
7273 /*
7274 * Default salvage method --- kill all of the directory
7275 * entries for the rest of the block. We will either try to
7276 * absorb it into the previous directory entry, or create a
7277 * new empty directory entry the rest of the directory block.
7278 */
7279 if (prev) {
7280 prev->rec_len += fs->blocksize - *offset;
7281 *offset = fs->blocksize;
7282 } else {
7283 dirent->rec_len = fs->blocksize - *offset;
7284 dirent->name_len = 0;
7285 dirent->inode = 0;
7286 }
7287}
7288
7289static int check_dir_block(ext2_filsys fs,
7290 struct ext2_db_entry *db,
7291 void *priv_data)
7292{
7293 struct dir_info *subdir, *dir;
7294 struct dx_dir_info *dx_dir;
7295#ifdef ENABLE_HTREE
7296 struct dx_dirblock_info *dx_db = 0;
7297#endif /* ENABLE_HTREE */
7298 struct ext2_dir_entry *dirent, *prev;
7299 ext2_dirhash_t hash;
7300 unsigned int offset = 0;
7301 int dir_modified = 0;
7302 int dot_state;
7303 blk_t block_nr = db->blk;
7304 ext2_ino_t ino = db->ino;
7305 __u16 links;
7306 struct check_dir_struct *cd;
7307 char *buf;
7308 e2fsck_t ctx;
7309 int problem;
7310 struct ext2_dx_root_info *root;
7311 struct ext2_dx_countlimit *limit;
7312 static dict_t de_dict;
7313 struct problem_context pctx;
7314 int dups_found = 0;
7315
7316 cd = (struct check_dir_struct *) priv_data;
7317 buf = cd->buf;
7318 ctx = cd->ctx;
7319
7320 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7321 return DIRENT_ABORT;
7322
7323 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
7324 return DIRENT_ABORT;
7325
7326 /*
7327 * Make sure the inode is still in use (could have been
7328 * deleted in the duplicate/bad blocks pass.
7329 */
7330 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
7331 return 0;
7332
7333 cd->pctx.ino = ino;
7334 cd->pctx.blk = block_nr;
7335 cd->pctx.blkcount = db->blockcnt;
7336 cd->pctx.ino2 = 0;
7337 cd->pctx.dirent = 0;
7338 cd->pctx.num = 0;
7339
7340 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007341 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007342 return 0;
7343 block_nr = db->blk;
7344 }
7345
7346 if (db->blockcnt)
7347 dot_state = 2;
7348 else
7349 dot_state = 0;
7350
7351 if (ctx->dirs_to_hash &&
7352 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
7353 dups_found++;
7354
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007355 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
7356 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
7357 cd->pctx.errcode = 0; /* We'll handle this ourselves */
7358 if (cd->pctx.errcode) {
7359 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
7360 ctx->flags |= E2F_FLAG_ABORT;
7361 return DIRENT_ABORT;
7362 }
7363 memset(buf, 0, fs->blocksize);
7364 }
7365#ifdef ENABLE_HTREE
7366 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
7367 if (dx_dir && dx_dir->numblocks) {
7368 if (db->blockcnt >= dx_dir->numblocks) {
7369 printf("XXX should never happen!!!\n");
7370 abort();
7371 }
7372 dx_db = &dx_dir->dx_block[db->blockcnt];
7373 dx_db->type = DX_DIRBLOCK_LEAF;
7374 dx_db->phys = block_nr;
7375 dx_db->min_hash = ~0;
7376 dx_db->max_hash = 0;
7377
7378 dirent = (struct ext2_dir_entry *) buf;
7379 limit = (struct ext2_dx_countlimit *) (buf+8);
7380 if (db->blockcnt == 0) {
7381 root = (struct ext2_dx_root_info *) (buf + 24);
7382 dx_db->type = DX_DIRBLOCK_ROOT;
7383 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
7384 if ((root->reserved_zero ||
7385 root->info_length < 8 ||
7386 root->indirect_levels > 1) &&
7387 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
7388 clear_htree(ctx, ino);
7389 dx_dir->numblocks = 0;
7390 dx_db = 0;
7391 }
7392 dx_dir->hashversion = root->hash_version;
7393 dx_dir->depth = root->indirect_levels + 1;
7394 } else if ((dirent->inode == 0) &&
7395 (dirent->rec_len == fs->blocksize) &&
7396 (dirent->name_len == 0) &&
7397 (ext2fs_le16_to_cpu(limit->limit) ==
7398 ((fs->blocksize-8) /
7399 sizeof(struct ext2_dx_entry))))
7400 dx_db->type = DX_DIRBLOCK_NODE;
7401 }
7402#endif /* ENABLE_HTREE */
7403
7404 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
7405 prev = 0;
7406 do {
7407 problem = 0;
7408 dirent = (struct ext2_dir_entry *) (buf + offset);
7409 cd->pctx.dirent = dirent;
7410 cd->pctx.num = offset;
7411 if (((offset + dirent->rec_len) > fs->blocksize) ||
7412 (dirent->rec_len < 12) ||
7413 ((dirent->rec_len % 4) != 0) ||
7414 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
7415 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
7416 salvage_directory(fs, dirent, prev, &offset);
7417 dir_modified++;
7418 continue;
7419 } else
7420 goto abort_free_dict;
7421 }
7422 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
7423 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
7424 dirent->name_len = EXT2_NAME_LEN;
7425 dir_modified++;
7426 }
7427 }
7428
7429 if (dot_state == 0) {
7430 if (check_dot(ctx, dirent, ino, &cd->pctx))
7431 dir_modified++;
7432 } else if (dot_state == 1) {
7433 dir = e2fsck_get_dir_info(ctx, ino);
7434 if (!dir) {
7435 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
7436 goto abort_free_dict;
7437 }
7438 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
7439 dir_modified++;
7440 } else if (dirent->inode == ino) {
7441 problem = PR_2_LINK_DOT;
7442 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
7443 dirent->inode = 0;
7444 dir_modified++;
7445 goto next;
7446 }
7447 }
7448 if (!dirent->inode)
7449 goto next;
7450
7451 /*
7452 * Make sure the inode listed is a legal one.
7453 */
7454 if (((dirent->inode != EXT2_ROOT_INO) &&
7455 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
7456 (dirent->inode > fs->super->s_inodes_count)) {
7457 problem = PR_2_BAD_INO;
7458 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
7459 dirent->inode))) {
7460 /*
7461 * If the inode is unused, offer to clear it.
7462 */
7463 problem = PR_2_UNUSED_INODE;
7464 } else if (ctx->inode_bb_map &&
7465 (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
7466 dirent->inode))) {
7467 /*
7468 * If the inode is in a bad block, offer to
7469 * clear it.
7470 */
7471 problem = PR_2_BB_INODE;
7472 } else if ((dot_state > 1) &&
7473 ((dirent->name_len & 0xFF) == 1) &&
7474 (dirent->name[0] == '.')) {
7475 /*
7476 * If there's a '.' entry in anything other
7477 * than the first directory entry, it's a
7478 * duplicate entry that should be removed.
7479 */
7480 problem = PR_2_DUP_DOT;
7481 } else if ((dot_state > 1) &&
7482 ((dirent->name_len & 0xFF) == 2) &&
7483 (dirent->name[0] == '.') &&
7484 (dirent->name[1] == '.')) {
7485 /*
7486 * If there's a '..' entry in anything other
7487 * than the second directory entry, it's a
7488 * duplicate entry that should be removed.
7489 */
7490 problem = PR_2_DUP_DOT_DOT;
7491 } else if ((dot_state > 1) &&
7492 (dirent->inode == EXT2_ROOT_INO)) {
7493 /*
7494 * Don't allow links to the root directory.
7495 * We check this specially to make sure we
7496 * catch this error case even if the root
7497 * directory hasn't been created yet.
7498 */
7499 problem = PR_2_LINK_ROOT;
7500 } else if ((dot_state > 1) &&
7501 (dirent->name_len & 0xFF) == 0) {
7502 /*
7503 * Don't allow zero-length directory names.
7504 */
7505 problem = PR_2_NULL_NAME;
7506 }
7507
7508 if (problem) {
7509 if (fix_problem(ctx, problem, &cd->pctx)) {
7510 dirent->inode = 0;
7511 dir_modified++;
7512 goto next;
7513 } else {
7514 ext2fs_unmark_valid(fs);
7515 if (problem == PR_2_BAD_INO)
7516 goto next;
7517 }
7518 }
7519
7520 /*
7521 * If the inode was marked as having bad fields in
7522 * pass1, process it and offer to fix/clear it.
7523 * (We wait until now so that we can display the
7524 * pathname to the user.)
7525 */
7526 if (ctx->inode_bad_map &&
7527 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
7528 dirent->inode)) {
7529 if (e2fsck_process_bad_inode(ctx, ino,
7530 dirent->inode,
7531 buf + fs->blocksize)) {
7532 dirent->inode = 0;
7533 dir_modified++;
7534 goto next;
7535 }
7536 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7537 return DIRENT_ABORT;
7538 }
7539
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007540 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007541 dir_modified++;
7542
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007543 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007544 dir_modified++;
7545
7546#ifdef ENABLE_HTREE
7547 if (dx_db) {
7548 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
7549 (dirent->name_len & 0xFF),
7550 fs->super->s_hash_seed, &hash, 0);
7551 if (hash < dx_db->min_hash)
7552 dx_db->min_hash = hash;
7553 if (hash > dx_db->max_hash)
7554 dx_db->max_hash = hash;
7555 }
7556#endif
7557
7558 /*
7559 * If this is a directory, then mark its parent in its
7560 * dir_info structure. If the parent field is already
7561 * filled in, then this directory has more than one
7562 * hard link. We assume the first link is correct,
7563 * and ask the user if he/she wants to clear this one.
7564 */
7565 if ((dot_state > 1) &&
7566 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
7567 dirent->inode))) {
7568 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
7569 if (!subdir) {
7570 cd->pctx.ino = dirent->inode;
7571 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
7572 goto abort_free_dict;
7573 }
7574 if (subdir->parent) {
7575 cd->pctx.ino2 = subdir->parent;
7576 if (fix_problem(ctx, PR_2_LINK_DIR,
7577 &cd->pctx)) {
7578 dirent->inode = 0;
7579 dir_modified++;
7580 goto next;
7581 }
7582 cd->pctx.ino2 = 0;
7583 } else
7584 subdir->parent = ino;
7585 }
7586
7587 if (dups_found) {
7588 ;
7589 } else if (dict_lookup(&de_dict, dirent)) {
7590 clear_problem_context(&pctx);
7591 pctx.ino = ino;
7592 pctx.dirent = dirent;
7593 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
7594 if (!ctx->dirs_to_hash)
7595 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
7596 if (ctx->dirs_to_hash)
7597 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
7598 dups_found++;
7599 } else
7600 dict_alloc_insert(&de_dict, dirent, dirent);
7601
7602 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
7603 &links);
7604 if (links > 1)
7605 ctx->fs_links_count++;
7606 ctx->fs_total_count++;
7607 next:
7608 prev = dirent;
7609 offset += dirent->rec_len;
7610 dot_state++;
7611 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007612#ifdef ENABLE_HTREE
7613 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007614 cd->pctx.dir = cd->pctx.ino;
7615 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
7616 (dx_db->type == DX_DIRBLOCK_NODE))
7617 parse_int_node(fs, db, cd, dx_dir, buf);
7618 }
7619#endif /* ENABLE_HTREE */
7620 if (offset != fs->blocksize) {
7621 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
7622 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
7623 dirent->rec_len = cd->pctx.num;
7624 dir_modified++;
7625 }
7626 }
7627 if (dir_modified) {
7628 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
7629 if (cd->pctx.errcode) {
7630 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
7631 &cd->pctx))
7632 goto abort_free_dict;
7633 }
7634 ext2fs_mark_changed(fs);
7635 }
7636 dict_free_nodes(&de_dict);
7637 return 0;
7638abort_free_dict:
7639 dict_free_nodes(&de_dict);
7640 ctx->flags |= E2F_FLAG_ABORT;
7641 return DIRENT_ABORT;
7642}
7643
7644/*
7645 * This function is called to deallocate a block, and is an interator
7646 * functioned called by deallocate inode via ext2fs_iterate_block().
7647 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007648static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007649 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
7650 blk_t ref_block FSCK_ATTR((unused)),
7651 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007652 void *priv_data)
7653{
7654 e2fsck_t ctx = (e2fsck_t) priv_data;
7655
7656 if (HOLE_BLKADDR(*block_nr))
7657 return 0;
7658 if ((*block_nr < fs->super->s_first_data_block) ||
7659 (*block_nr >= fs->super->s_blocks_count))
7660 return 0;
7661 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
7662 ext2fs_block_alloc_stats(fs, *block_nr, -1);
7663 return 0;
7664}
7665
7666/*
7667 * This fuction deallocates an inode
7668 */
7669static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
7670{
7671 ext2_filsys fs = ctx->fs;
7672 struct ext2_inode inode;
7673 struct problem_context pctx;
7674 __u32 count;
7675
7676 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
7677 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
7678 inode.i_links_count = 0;
7679 inode.i_dtime = time(0);
7680 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
7681 clear_problem_context(&pctx);
7682 pctx.ino = ino;
7683
7684 /*
7685 * Fix up the bitmaps...
7686 */
7687 e2fsck_read_bitmaps(ctx);
7688 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
7689 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
7690 if (ctx->inode_bad_map)
7691 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
7692 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
7693
7694 if (inode.i_file_acl &&
7695 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
7696 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
7697 block_buf, -1, &count);
7698 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
7699 pctx.errcode = 0;
7700 count = 1;
7701 }
7702 if (pctx.errcode) {
7703 pctx.blk = inode.i_file_acl;
7704 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
7705 ctx->flags |= E2F_FLAG_ABORT;
7706 return;
7707 }
7708 if (count == 0) {
7709 ext2fs_unmark_block_bitmap(ctx->block_found_map,
7710 inode.i_file_acl);
7711 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
7712 }
7713 inode.i_file_acl = 0;
7714 }
7715
7716 if (!ext2fs_inode_has_valid_blocks(&inode))
7717 return;
7718
7719 if (LINUX_S_ISREG(inode.i_mode) &&
7720 (inode.i_size_high || inode.i_size & 0x80000000UL))
7721 ctx->large_files--;
7722
7723 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
7724 deallocate_inode_block, ctx);
7725 if (pctx.errcode) {
7726 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
7727 ctx->flags |= E2F_FLAG_ABORT;
7728 return;
7729 }
7730}
7731
7732/*
7733 * This fuction clears the htree flag on an inode
7734 */
7735static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
7736{
7737 struct ext2_inode inode;
7738
7739 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
7740 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
7741 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
7742 if (ctx->dirs_to_hash)
7743 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
7744}
7745
7746
7747static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
7748 ext2_ino_t ino, char *buf)
7749{
7750 ext2_filsys fs = ctx->fs;
7751 struct ext2_inode inode;
7752 int inode_modified = 0;
7753 int not_fixed = 0;
7754 unsigned char *frag, *fsize;
7755 struct problem_context pctx;
7756 int problem = 0;
7757
7758 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
7759
7760 clear_problem_context(&pctx);
7761 pctx.ino = ino;
7762 pctx.dir = dir;
7763 pctx.inode = &inode;
7764
7765 if (inode.i_file_acl &&
7766 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
7767 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
7768 inode.i_file_acl = 0;
7769#ifdef EXT2FS_ENABLE_SWAPFS
7770 /*
7771 * This is a special kludge to deal with long symlinks
7772 * on big endian systems. i_blocks had already been
7773 * decremented earlier in pass 1, but since i_file_acl
7774 * hadn't yet been cleared, ext2fs_read_inode()
7775 * assumed that the file was short symlink and would
7776 * not have byte swapped i_block[0]. Hence, we have
7777 * to byte-swap it here.
7778 */
7779 if (LINUX_S_ISLNK(inode.i_mode) &&
7780 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
7781 (inode.i_blocks == fs->blocksize >> 9))
7782 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
7783#endif
7784 inode_modified++;
7785 } else
7786 not_fixed++;
7787
7788 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
7789 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
7790 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
7791 !(LINUX_S_ISSOCK(inode.i_mode)))
7792 problem = PR_2_BAD_MODE;
7793 else if (LINUX_S_ISCHR(inode.i_mode)
7794 && !e2fsck_pass1_check_device_inode(fs, &inode))
7795 problem = PR_2_BAD_CHAR_DEV;
7796 else if (LINUX_S_ISBLK(inode.i_mode)
7797 && !e2fsck_pass1_check_device_inode(fs, &inode))
7798 problem = PR_2_BAD_BLOCK_DEV;
7799 else if (LINUX_S_ISFIFO(inode.i_mode)
7800 && !e2fsck_pass1_check_device_inode(fs, &inode))
7801 problem = PR_2_BAD_FIFO;
7802 else if (LINUX_S_ISSOCK(inode.i_mode)
7803 && !e2fsck_pass1_check_device_inode(fs, &inode))
7804 problem = PR_2_BAD_SOCKET;
7805 else if (LINUX_S_ISLNK(inode.i_mode)
7806 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
7807 problem = PR_2_INVALID_SYMLINK;
7808 }
7809
7810 if (problem) {
7811 if (fix_problem(ctx, problem, &pctx)) {
7812 deallocate_inode(ctx, ino, 0);
7813 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7814 return 0;
7815 return 1;
7816 } else
7817 not_fixed++;
7818 problem = 0;
7819 }
7820
7821 if (inode.i_faddr) {
7822 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
7823 inode.i_faddr = 0;
7824 inode_modified++;
7825 } else
7826 not_fixed++;
7827 }
7828
7829 switch (fs->super->s_creator_os) {
7830 case EXT2_OS_LINUX:
7831 frag = &inode.osd2.linux2.l_i_frag;
7832 fsize = &inode.osd2.linux2.l_i_fsize;
7833 break;
7834 case EXT2_OS_HURD:
7835 frag = &inode.osd2.hurd2.h_i_frag;
7836 fsize = &inode.osd2.hurd2.h_i_fsize;
7837 break;
7838 case EXT2_OS_MASIX:
7839 frag = &inode.osd2.masix2.m_i_frag;
7840 fsize = &inode.osd2.masix2.m_i_fsize;
7841 break;
7842 default:
7843 frag = fsize = 0;
7844 }
7845 if (frag && *frag) {
7846 pctx.num = *frag;
7847 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
7848 *frag = 0;
7849 inode_modified++;
7850 } else
7851 not_fixed++;
7852 pctx.num = 0;
7853 }
7854 if (fsize && *fsize) {
7855 pctx.num = *fsize;
7856 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
7857 *fsize = 0;
7858 inode_modified++;
7859 } else
7860 not_fixed++;
7861 pctx.num = 0;
7862 }
7863
7864 if (inode.i_file_acl &&
7865 ((inode.i_file_acl < fs->super->s_first_data_block) ||
7866 (inode.i_file_acl >= fs->super->s_blocks_count))) {
7867 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
7868 inode.i_file_acl = 0;
7869 inode_modified++;
7870 } else
7871 not_fixed++;
7872 }
7873 if (inode.i_dir_acl &&
7874 LINUX_S_ISDIR(inode.i_mode)) {
7875 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
7876 inode.i_dir_acl = 0;
7877 inode_modified++;
7878 } else
7879 not_fixed++;
7880 }
7881
7882 if (inode_modified)
7883 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
7884 if (!not_fixed)
7885 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
7886 return 0;
7887}
7888
7889
7890/*
7891 * allocate_dir_block --- this function allocates a new directory
7892 * block for a particular inode; this is done if a directory has
7893 * a "hole" in it, or if a directory has a illegal block number
7894 * that was zeroed out and now needs to be replaced.
7895 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007896static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007897 struct problem_context *pctx)
7898{
7899 ext2_filsys fs = ctx->fs;
7900 blk_t blk;
7901 char *block;
7902 struct ext2_inode inode;
7903
7904 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
7905 return 1;
7906
7907 /*
7908 * Read the inode and block bitmaps in; we'll be messing with
7909 * them.
7910 */
7911 e2fsck_read_bitmaps(ctx);
7912
7913 /*
7914 * First, find a free block
7915 */
7916 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7917 if (pctx->errcode) {
7918 pctx->str = "ext2fs_new_block";
7919 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7920 return 1;
7921 }
7922 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7923 ext2fs_mark_block_bitmap(fs->block_map, blk);
7924 ext2fs_mark_bb_dirty(fs);
7925
7926 /*
7927 * Now let's create the actual data block for the inode
7928 */
7929 if (db->blockcnt)
7930 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
7931 else
7932 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
7933 EXT2_ROOT_INO, &block);
7934
7935 if (pctx->errcode) {
7936 pctx->str = "ext2fs_new_dir_block";
7937 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7938 return 1;
7939 }
7940
7941 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
7942 ext2fs_free_mem(&block);
7943 if (pctx->errcode) {
7944 pctx->str = "ext2fs_write_dir_block";
7945 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7946 return 1;
7947 }
7948
7949 /*
7950 * Update the inode block count
7951 */
7952 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
7953 inode.i_blocks += fs->blocksize / 512;
7954 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
7955 inode.i_size = (db->blockcnt+1) * fs->blocksize;
7956 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
7957
7958 /*
7959 * Finally, update the block pointers for the inode
7960 */
7961 db->blk = blk;
7962 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
7963 0, update_dir_block, db);
7964 if (pctx->errcode) {
7965 pctx->str = "ext2fs_block_iterate";
7966 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7967 return 1;
7968 }
7969
7970 return 0;
7971}
7972
7973/*
7974 * This is a helper function for allocate_dir_block().
7975 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007976static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007977 blk_t *block_nr,
7978 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007979 blk_t ref_block FSCK_ATTR((unused)),
7980 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007981 void *priv_data)
7982{
7983 struct ext2_db_entry *db;
7984
7985 db = (struct ext2_db_entry *) priv_data;
7986 if (db->blockcnt == (int) blockcnt) {
7987 *block_nr = db->blk;
7988 return BLOCK_CHANGED;
7989 }
7990 return 0;
7991}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007992
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007993/*
7994 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
7995 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007996 * Pass #3 assures that all directories are connected to the
7997 * filesystem tree, using the following algorithm:
7998 *
7999 * First, the root directory is checked to make sure it exists; if
8000 * not, e2fsck will offer to create a new one. It is then marked as
8001 * "done".
8002 *
8003 * Then, pass3 interates over all directory inodes; for each directory
8004 * it attempts to trace up the filesystem tree, using dirinfo.parent
8005 * until it reaches a directory which has been marked "done". If it
8006 * can not do so, then the directory must be disconnected, and e2fsck
8007 * will offer to reconnect it to /lost+found. While it is chasing
8008 * parent pointers up the filesystem tree, if pass3 sees a directory
8009 * twice, then it has detected a filesystem loop, and it will again
8010 * offer to reconnect the directory to /lost+found in to break the
8011 * filesystem loop.
8012 *
8013 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
8014 * reconnect inodes to /lost+found; this subroutine is also used by
8015 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
8016 * is responsible for creating /lost+found if it does not exist.
8017 *
8018 * Pass 3 frees the following data structures:
8019 * - The dirinfo directory information cache.
8020 */
8021
8022static void check_root(e2fsck_t ctx);
8023static int check_directory(e2fsck_t ctx, struct dir_info *dir,
8024 struct problem_context *pctx);
8025static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
8026
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008027static ext2fs_inode_bitmap inode_loop_detect;
8028static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008029
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008030static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008031{
8032 ext2_filsys fs = ctx->fs;
8033 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008034 struct problem_context pctx;
8035 struct dir_info *dir;
8036 unsigned long maxdirs, count;
8037
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008038 clear_problem_context(&pctx);
8039
Rob Landley3e72c592006-04-06 22:49:04 +00008040 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008041
8042 if (!(ctx->options & E2F_OPT_PREEN))
8043 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
8044
8045 /*
8046 * Allocate some bitmaps to do loop detection.
8047 */
8048 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
8049 &inode_done_map);
8050 if (pctx.errcode) {
8051 pctx.num = 2;
8052 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
8053 ctx->flags |= E2F_FLAG_ABORT;
8054 goto abort_exit;
8055 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008056 check_root(ctx);
8057 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8058 goto abort_exit;
8059
8060 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
8061
8062 maxdirs = e2fsck_get_num_dirinfo(ctx);
8063 count = 1;
8064
8065 if (ctx->progress)
8066 if ((ctx->progress)(ctx, 3, 0, maxdirs))
8067 goto abort_exit;
8068
8069 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
8070 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8071 goto abort_exit;
8072 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
8073 goto abort_exit;
8074 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
8075 if (check_directory(ctx, dir, &pctx))
8076 goto abort_exit;
8077 }
8078
8079 /*
8080 * Force the creation of /lost+found if not present
8081 */
8082 if ((ctx->flags & E2F_OPT_READONLY) == 0)
8083 e2fsck_get_lost_and_found(ctx, 1);
8084
8085 /*
8086 * If there are any directories that need to be indexed or
8087 * optimized, do it here.
8088 */
8089 e2fsck_rehash_directories(ctx);
8090
8091abort_exit:
8092 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00008093 ext2fs_free_inode_bitmap(inode_loop_detect);
8094 inode_loop_detect = 0;
8095 ext2fs_free_inode_bitmap(inode_done_map);
8096 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008097}
8098
8099/*
8100 * This makes sure the root inode is present; if not, we ask if the
8101 * user wants us to create it. Not creating it is a fatal error.
8102 */
8103static void check_root(e2fsck_t ctx)
8104{
8105 ext2_filsys fs = ctx->fs;
8106 blk_t blk;
8107 struct ext2_inode inode;
8108 char * block;
8109 struct problem_context pctx;
8110
8111 clear_problem_context(&pctx);
8112
8113 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
8114 /*
8115 * If the root inode is not a directory, die here. The
8116 * user must have answered 'no' in pass1 when we
8117 * offered to clear it.
8118 */
8119 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
8120 EXT2_ROOT_INO))) {
8121 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
8122 ctx->flags |= E2F_FLAG_ABORT;
8123 }
8124 return;
8125 }
8126
8127 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
8128 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
8129 ctx->flags |= E2F_FLAG_ABORT;
8130 return;
8131 }
8132
8133 e2fsck_read_bitmaps(ctx);
8134
8135 /*
8136 * First, find a free block
8137 */
8138 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
8139 if (pctx.errcode) {
8140 pctx.str = "ext2fs_new_block";
8141 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8142 ctx->flags |= E2F_FLAG_ABORT;
8143 return;
8144 }
8145 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
8146 ext2fs_mark_block_bitmap(fs->block_map, blk);
8147 ext2fs_mark_bb_dirty(fs);
8148
8149 /*
8150 * Now let's create the actual data block for the inode
8151 */
8152 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
8153 &block);
8154 if (pctx.errcode) {
8155 pctx.str = "ext2fs_new_dir_block";
8156 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8157 ctx->flags |= E2F_FLAG_ABORT;
8158 return;
8159 }
8160
8161 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
8162 if (pctx.errcode) {
8163 pctx.str = "ext2fs_write_dir_block";
8164 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8165 ctx->flags |= E2F_FLAG_ABORT;
8166 return;
8167 }
8168 ext2fs_free_mem(&block);
8169
8170 /*
8171 * Set up the inode structure
8172 */
8173 memset(&inode, 0, sizeof(inode));
8174 inode.i_mode = 040755;
8175 inode.i_size = fs->blocksize;
8176 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
8177 inode.i_links_count = 2;
8178 inode.i_blocks = fs->blocksize / 512;
8179 inode.i_block[0] = blk;
8180
8181 /*
8182 * Write out the inode.
8183 */
8184 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
8185 if (pctx.errcode) {
8186 pctx.str = "ext2fs_write_inode";
8187 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8188 ctx->flags |= E2F_FLAG_ABORT;
8189 return;
8190 }
8191
8192 /*
8193 * Miscellaneous bookkeeping...
8194 */
8195 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
8196 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
8197 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
8198
8199 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
8200 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
8201 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
8202 ext2fs_mark_ib_dirty(fs);
8203}
8204
8205/*
8206 * This subroutine is responsible for making sure that a particular
8207 * directory is connected to the root; if it isn't we trace it up as
8208 * far as we can go, and then offer to connect the resulting parent to
8209 * the lost+found. We have to do loop detection; if we ever discover
8210 * a loop, we treat that as a disconnected directory and offer to
8211 * reparent it to lost+found.
8212 *
8213 * However, loop detection is expensive, because for very large
8214 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
8215 * is non-trivial. Loops in filesystems are also a rare error case,
8216 * and we shouldn't optimize for error cases. So we try two passes of
8217 * the algorithm. The first time, we ignore loop detection and merely
8218 * increment a counter; if the counter exceeds some extreme threshold,
8219 * then we try again with the loop detection bitmap enabled.
8220 */
8221static int check_directory(e2fsck_t ctx, struct dir_info *dir,
8222 struct problem_context *pctx)
8223{
8224 ext2_filsys fs = ctx->fs;
8225 struct dir_info *p = dir;
8226 int loop_pass = 0, parent_count = 0;
8227
8228 if (!p)
8229 return 0;
8230
8231 while (1) {
8232 /*
8233 * Mark this inode as being "done"; by the time we
8234 * return from this function, the inode we either be
8235 * verified as being connected to the directory tree,
8236 * or we will have offered to reconnect this to
8237 * lost+found.
8238 *
8239 * If it was marked done already, then we've reached a
8240 * parent we've already checked.
8241 */
8242 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
8243 break;
8244
8245 /*
8246 * If this directory doesn't have a parent, or we've
8247 * seen the parent once already, then offer to
8248 * reparent it to lost+found
8249 */
8250 if (!p->parent ||
8251 (loop_pass &&
8252 (ext2fs_test_inode_bitmap(inode_loop_detect,
8253 p->parent)))) {
8254 pctx->ino = p->ino;
8255 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
8256 if (e2fsck_reconnect_file(ctx, pctx->ino))
8257 ext2fs_unmark_valid(fs);
8258 else {
8259 p = e2fsck_get_dir_info(ctx, pctx->ino);
8260 p->parent = ctx->lost_and_found;
8261 fix_dotdot(ctx, p, ctx->lost_and_found);
8262 }
8263 }
8264 break;
8265 }
8266 p = e2fsck_get_dir_info(ctx, p->parent);
8267 if (!p) {
8268 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
8269 return 0;
8270 }
8271 if (loop_pass) {
8272 ext2fs_mark_inode_bitmap(inode_loop_detect,
8273 p->ino);
8274 } else if (parent_count++ > 2048) {
8275 /*
8276 * If we've run into a path depth that's
8277 * greater than 2048, try again with the inode
8278 * loop bitmap turned on and start from the
8279 * top.
8280 */
8281 loop_pass = 1;
8282 if (inode_loop_detect)
8283 ext2fs_clear_inode_bitmap(inode_loop_detect);
8284 else {
8285 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
8286 if (pctx->errcode) {
8287 pctx->num = 1;
8288 fix_problem(ctx,
8289 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
8290 ctx->flags |= E2F_FLAG_ABORT;
8291 return -1;
8292 }
8293 }
8294 p = dir;
8295 }
8296 }
8297
8298 /*
8299 * Make sure that .. and the parent directory are the same;
8300 * offer to fix it if not.
8301 */
8302 if (dir->parent != dir->dotdot) {
8303 pctx->ino = dir->ino;
8304 pctx->ino2 = dir->dotdot;
8305 pctx->dir = dir->parent;
8306 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
8307 fix_dotdot(ctx, dir, dir->parent);
8308 }
8309 return 0;
8310}
8311
8312/*
8313 * This routine gets the lost_and_found inode, making it a directory
8314 * if necessary
8315 */
8316ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
8317{
8318 ext2_filsys fs = ctx->fs;
8319 ext2_ino_t ino;
8320 blk_t blk;
8321 errcode_t retval;
8322 struct ext2_inode inode;
8323 char * block;
8324 static const char name[] = "lost+found";
8325 struct problem_context pctx;
8326 struct dir_info *dirinfo;
8327
8328 if (ctx->lost_and_found)
8329 return ctx->lost_and_found;
8330
8331 clear_problem_context(&pctx);
8332
8333 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
8334 sizeof(name)-1, 0, &ino);
8335 if (retval && !fix)
8336 return 0;
8337 if (!retval) {
8338 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
8339 ctx->lost_and_found = ino;
8340 return ino;
8341 }
8342
8343 /* Lost+found isn't a directory! */
8344 if (!fix)
8345 return 0;
8346 pctx.ino = ino;
8347 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
8348 return 0;
8349
8350 /* OK, unlink the old /lost+found file. */
8351 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
8352 if (pctx.errcode) {
8353 pctx.str = "ext2fs_unlink";
8354 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8355 return 0;
8356 }
8357 dirinfo = e2fsck_get_dir_info(ctx, ino);
8358 if (dirinfo)
8359 dirinfo->parent = 0;
8360 e2fsck_adjust_inode_count(ctx, ino, -1);
8361 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
8362 pctx.errcode = retval;
8363 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
8364 }
8365 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
8366 return 0;
8367
8368 /*
8369 * Read the inode and block bitmaps in; we'll be messing with
8370 * them.
8371 */
8372 e2fsck_read_bitmaps(ctx);
8373
8374 /*
8375 * First, find a free block
8376 */
8377 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
8378 if (retval) {
8379 pctx.errcode = retval;
8380 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
8381 return 0;
8382 }
8383 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
8384 ext2fs_block_alloc_stats(fs, blk, +1);
8385
8386 /*
8387 * Next find a free inode.
8388 */
8389 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
8390 ctx->inode_used_map, &ino);
8391 if (retval) {
8392 pctx.errcode = retval;
8393 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
8394 return 0;
8395 }
8396 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
8397 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
8398 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
8399
8400 /*
8401 * Now let's create the actual data block for the inode
8402 */
8403 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
8404 if (retval) {
8405 pctx.errcode = retval;
8406 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
8407 return 0;
8408 }
8409
8410 retval = ext2fs_write_dir_block(fs, blk, block);
8411 ext2fs_free_mem(&block);
8412 if (retval) {
8413 pctx.errcode = retval;
8414 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
8415 return 0;
8416 }
8417
8418 /*
8419 * Set up the inode structure
8420 */
8421 memset(&inode, 0, sizeof(inode));
8422 inode.i_mode = 040700;
8423 inode.i_size = fs->blocksize;
8424 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
8425 inode.i_links_count = 2;
8426 inode.i_blocks = fs->blocksize / 512;
8427 inode.i_block[0] = blk;
8428
8429 /*
8430 * Next, write out the inode.
8431 */
8432 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
8433 if (pctx.errcode) {
8434 pctx.str = "ext2fs_write_inode";
8435 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8436 return 0;
8437 }
8438 /*
8439 * Finally, create the directory link
8440 */
8441 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
8442 if (pctx.errcode) {
8443 pctx.str = "ext2fs_link";
8444 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8445 return 0;
8446 }
8447
8448 /*
8449 * Miscellaneous bookkeeping that needs to be kept straight.
8450 */
8451 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
8452 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
8453 ext2fs_icount_store(ctx->inode_count, ino, 2);
8454 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
8455 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008456 return ino;
8457}
8458
8459/*
8460 * This routine will connect a file to lost+found
8461 */
8462int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
8463{
8464 ext2_filsys fs = ctx->fs;
8465 errcode_t retval;
8466 char name[80];
8467 struct problem_context pctx;
8468 struct ext2_inode inode;
8469 int file_type = 0;
8470
8471 clear_problem_context(&pctx);
8472 pctx.ino = ino;
8473
8474 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
8475 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
8476 ctx->bad_lost_and_found++;
8477 }
8478 if (ctx->bad_lost_and_found) {
8479 fix_problem(ctx, PR_3_NO_LPF, &pctx);
8480 return 1;
8481 }
8482
8483 sprintf(name, "#%u", ino);
8484 if (ext2fs_read_inode(fs, ino, &inode) == 0)
8485 file_type = ext2_file_type(inode.i_mode);
8486 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
8487 if (retval == EXT2_ET_DIR_NO_SPACE) {
8488 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
8489 return 1;
8490 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
8491 1, 0);
8492 if (retval) {
8493 pctx.errcode = retval;
8494 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
8495 return 1;
8496 }
8497 retval = ext2fs_link(fs, ctx->lost_and_found, name,
8498 ino, file_type);
8499 }
8500 if (retval) {
8501 pctx.errcode = retval;
8502 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
8503 return 1;
8504 }
8505 e2fsck_adjust_inode_count(ctx, ino, 1);
8506
8507 return 0;
8508}
8509
8510/*
8511 * Utility routine to adjust the inode counts on an inode.
8512 */
8513errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
8514{
8515 ext2_filsys fs = ctx->fs;
8516 errcode_t retval;
8517 struct ext2_inode inode;
8518
8519 if (!ino)
8520 return 0;
8521
8522 retval = ext2fs_read_inode(fs, ino, &inode);
8523 if (retval)
8524 return retval;
8525
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008526 if (adj == 1) {
8527 ext2fs_icount_increment(ctx->inode_count, ino, 0);
8528 if (inode.i_links_count == (__u16) ~0)
8529 return 0;
8530 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
8531 inode.i_links_count++;
8532 } else if (adj == -1) {
8533 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
8534 if (inode.i_links_count == 0)
8535 return 0;
8536 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
8537 inode.i_links_count--;
8538 }
8539
8540 retval = ext2fs_write_inode(fs, ino, &inode);
8541 if (retval)
8542 return retval;
8543
8544 return 0;
8545}
8546
8547/*
8548 * Fix parent --- this routine fixes up the parent of a directory.
8549 */
8550struct fix_dotdot_struct {
8551 ext2_filsys fs;
8552 ext2_ino_t parent;
8553 int done;
8554 e2fsck_t ctx;
8555};
8556
8557static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008558 int offset FSCK_ATTR((unused)),
8559 int blocksize FSCK_ATTR((unused)),
8560 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008561 void *priv_data)
8562{
8563 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
8564 errcode_t retval;
8565 struct problem_context pctx;
8566
8567 if ((dirent->name_len & 0xFF) != 2)
8568 return 0;
8569 if (strncmp(dirent->name, "..", 2))
8570 return 0;
8571
8572 clear_problem_context(&pctx);
8573
8574 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
8575 if (retval) {
8576 pctx.errcode = retval;
8577 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
8578 }
8579 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
8580 if (retval) {
8581 pctx.errcode = retval;
8582 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
8583 }
8584 dirent->inode = fp->parent;
8585
8586 fp->done++;
8587 return DIRENT_ABORT | DIRENT_CHANGED;
8588}
8589
8590static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
8591{
8592 ext2_filsys fs = ctx->fs;
8593 errcode_t retval;
8594 struct fix_dotdot_struct fp;
8595 struct problem_context pctx;
8596
8597 fp.fs = fs;
8598 fp.parent = parent;
8599 fp.done = 0;
8600 fp.ctx = ctx;
8601
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008602 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
8603 0, fix_dotdot_proc, &fp);
8604 if (retval || !fp.done) {
8605 clear_problem_context(&pctx);
8606 pctx.ino = dir->ino;
8607 pctx.errcode = retval;
8608 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
8609 PR_3_FIX_PARENT_NOFIND, &pctx);
8610 ext2fs_unmark_valid(fs);
8611 }
8612 dir->dotdot = parent;
8613
8614 return;
8615}
8616
8617/*
8618 * These routines are responsible for expanding a /lost+found if it is
8619 * too small.
8620 */
8621
8622struct expand_dir_struct {
8623 int num;
8624 int guaranteed_size;
8625 int newblocks;
8626 int last_block;
8627 errcode_t err;
8628 e2fsck_t ctx;
8629};
8630
8631static int expand_dir_proc(ext2_filsys fs,
8632 blk_t *blocknr,
8633 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008634 blk_t ref_block FSCK_ATTR((unused)),
8635 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008636 void *priv_data)
8637{
8638 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
8639 blk_t new_blk;
8640 static blk_t last_blk = 0;
8641 char *block;
8642 errcode_t retval;
8643 e2fsck_t ctx;
8644
8645 ctx = es->ctx;
8646
8647 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
8648 return BLOCK_ABORT;
8649
8650 if (blockcnt > 0)
8651 es->last_block = blockcnt;
8652 if (*blocknr) {
8653 last_blk = *blocknr;
8654 return 0;
8655 }
8656 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
8657 &new_blk);
8658 if (retval) {
8659 es->err = retval;
8660 return BLOCK_ABORT;
8661 }
8662 if (blockcnt > 0) {
8663 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
8664 if (retval) {
8665 es->err = retval;
8666 return BLOCK_ABORT;
8667 }
8668 es->num--;
8669 retval = ext2fs_write_dir_block(fs, new_blk, block);
8670 } else {
8671 retval = ext2fs_get_mem(fs->blocksize, &block);
8672 if (retval) {
8673 es->err = retval;
8674 return BLOCK_ABORT;
8675 }
8676 memset(block, 0, fs->blocksize);
8677 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
8678 }
8679 if (retval) {
8680 es->err = retval;
8681 return BLOCK_ABORT;
8682 }
8683 ext2fs_free_mem(&block);
8684 *blocknr = new_blk;
8685 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
8686 ext2fs_block_alloc_stats(fs, new_blk, +1);
8687 es->newblocks++;
8688
8689 if (es->num == 0)
8690 return (BLOCK_CHANGED | BLOCK_ABORT);
8691 else
8692 return BLOCK_CHANGED;
8693}
8694
8695errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
8696 int num, int guaranteed_size)
8697{
8698 ext2_filsys fs = ctx->fs;
8699 errcode_t retval;
8700 struct expand_dir_struct es;
8701 struct ext2_inode inode;
8702
8703 if (!(fs->flags & EXT2_FLAG_RW))
8704 return EXT2_ET_RO_FILSYS;
8705
8706 /*
8707 * Read the inode and block bitmaps in; we'll be messing with
8708 * them.
8709 */
8710 e2fsck_read_bitmaps(ctx);
8711
8712 retval = ext2fs_check_directory(fs, dir);
8713 if (retval)
8714 return retval;
8715
8716 es.num = num;
8717 es.guaranteed_size = guaranteed_size;
8718 es.last_block = 0;
8719 es.err = 0;
8720 es.newblocks = 0;
8721 es.ctx = ctx;
8722
8723 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
8724 0, expand_dir_proc, &es);
8725
8726 if (es.err)
8727 return es.err;
8728
8729 /*
8730 * Update the size and block count fields in the inode.
8731 */
8732 retval = ext2fs_read_inode(fs, dir, &inode);
8733 if (retval)
8734 return retval;
8735
8736 inode.i_size = (es.last_block + 1) * fs->blocksize;
8737 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
8738
8739 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
8740
8741 return 0;
8742}
8743
8744/*
8745 * pass4.c -- pass #4 of e2fsck: Check reference counts
8746 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008747 * Pass 4 frees the following data structures:
8748 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
8749 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
8750 */
8751
8752/*
8753 * This routine is called when an inode is not connected to the
8754 * directory tree.
8755 *
8756 * This subroutine returns 1 then the caller shouldn't bother with the
8757 * rest of the pass 4 tests.
8758 */
8759static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
8760{
8761 ext2_filsys fs = ctx->fs;
8762 struct ext2_inode inode;
8763 struct problem_context pctx;
8764
8765 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
8766 clear_problem_context(&pctx);
8767 pctx.ino = i;
8768 pctx.inode = &inode;
8769
8770 /*
8771 * Offer to delete any zero-length files that does not have
8772 * blocks. If there is an EA block, it might have useful
8773 * information, so we won't prompt to delete it, but let it be
8774 * reconnected to lost+found.
8775 */
8776 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
8777 LINUX_S_ISDIR(inode.i_mode))) {
8778 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
8779 ext2fs_icount_store(ctx->inode_link_info, i, 0);
8780 inode.i_links_count = 0;
8781 inode.i_dtime = time(0);
8782 e2fsck_write_inode(ctx, i, &inode,
8783 "disconnect_inode");
8784 /*
8785 * Fix up the bitmaps...
8786 */
8787 e2fsck_read_bitmaps(ctx);
8788 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
8789 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
8790 ext2fs_inode_alloc_stats2(fs, i, -1,
8791 LINUX_S_ISDIR(inode.i_mode));
8792 return 0;
8793 }
8794 }
8795
8796 /*
8797 * Prompt to reconnect.
8798 */
8799 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
8800 if (e2fsck_reconnect_file(ctx, i))
8801 ext2fs_unmark_valid(fs);
8802 } else {
8803 /*
8804 * If we don't attach the inode, then skip the
8805 * i_links_test since there's no point in trying to
8806 * force i_links_count to zero.
8807 */
8808 ext2fs_unmark_valid(fs);
8809 return 1;
8810 }
8811 return 0;
8812}
8813
8814
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008815static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008816{
8817 ext2_filsys fs = ctx->fs;
8818 ext2_ino_t i;
8819 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008820 struct problem_context pctx;
8821 __u16 link_count, link_counted;
8822 char *buf = 0;
8823 int group, maxgroup;
8824
Rob Landley3e72c592006-04-06 22:49:04 +00008825 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008826
8827 clear_problem_context(&pctx);
8828
8829 if (!(ctx->options & E2F_OPT_PREEN))
8830 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
8831
8832 group = 0;
8833 maxgroup = fs->group_desc_count;
8834 if (ctx->progress)
8835 if ((ctx->progress)(ctx, 4, 0, maxgroup))
8836 return;
8837
8838 for (i=1; i <= fs->super->s_inodes_count; i++) {
8839 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8840 return;
8841 if ((i % fs->super->s_inodes_per_group) == 0) {
8842 group++;
8843 if (ctx->progress)
8844 if ((ctx->progress)(ctx, 4, group, maxgroup))
8845 return;
8846 }
8847 if (i == EXT2_BAD_INO ||
8848 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
8849 continue;
8850 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
8851 (ctx->inode_imagic_map &&
8852 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) ||
8853 (ctx->inode_bb_map &&
8854 ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
8855 continue;
8856 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
8857 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
8858 if (link_counted == 0) {
8859 if (!buf)
8860 buf = e2fsck_allocate_memory(ctx,
8861 fs->blocksize, "bad_inode buffer");
8862 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
8863 continue;
8864 if (disconnect_inode(ctx, i))
8865 continue;
8866 ext2fs_icount_fetch(ctx->inode_link_info, i,
8867 &link_count);
8868 ext2fs_icount_fetch(ctx->inode_count, i,
8869 &link_counted);
8870 }
8871 if (link_counted != link_count) {
8872 e2fsck_read_inode(ctx, i, &inode, "pass4");
8873 pctx.ino = i;
8874 pctx.inode = &inode;
8875 if (link_count != inode.i_links_count) {
8876 pctx.num = link_count;
8877 fix_problem(ctx,
8878 PR_4_INCONSISTENT_COUNT, &pctx);
8879 }
8880 pctx.num = link_counted;
8881 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
8882 inode.i_links_count = link_counted;
8883 e2fsck_write_inode(ctx, i, &inode, "pass4");
8884 }
8885 }
8886 }
8887 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
8888 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
8889 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
8890 ctx->inode_bb_map = 0;
8891 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
8892 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00008893 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008894}
8895
8896/*
8897 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008898 */
8899
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008900#define NO_BLK ((blk_t) -1)
8901
8902static void print_bitmap_problem(e2fsck_t ctx, int problem,
8903 struct problem_context *pctx)
8904{
8905 switch (problem) {
8906 case PR_5_BLOCK_UNUSED:
8907 if (pctx->blk == pctx->blk2)
8908 pctx->blk2 = 0;
8909 else
8910 problem = PR_5_BLOCK_RANGE_UNUSED;
8911 break;
8912 case PR_5_BLOCK_USED:
8913 if (pctx->blk == pctx->blk2)
8914 pctx->blk2 = 0;
8915 else
8916 problem = PR_5_BLOCK_RANGE_USED;
8917 break;
8918 case PR_5_INODE_UNUSED:
8919 if (pctx->ino == pctx->ino2)
8920 pctx->ino2 = 0;
8921 else
8922 problem = PR_5_INODE_RANGE_UNUSED;
8923 break;
8924 case PR_5_INODE_USED:
8925 if (pctx->ino == pctx->ino2)
8926 pctx->ino2 = 0;
8927 else
8928 problem = PR_5_INODE_RANGE_USED;
8929 break;
8930 }
8931 fix_problem(ctx, problem, pctx);
8932 pctx->blk = pctx->blk2 = NO_BLK;
8933 pctx->ino = pctx->ino2 = 0;
8934}
8935
8936static void check_block_bitmaps(e2fsck_t ctx)
8937{
8938 ext2_filsys fs = ctx->fs;
8939 blk_t i;
8940 int *free_array;
8941 int group = 0;
8942 unsigned int blocks = 0;
8943 unsigned int free_blocks = 0;
8944 int group_free = 0;
8945 int actual, bitmap;
8946 struct problem_context pctx;
8947 int problem, save_problem, fixit, had_problem;
8948 errcode_t retval;
8949
8950 clear_problem_context(&pctx);
8951 free_array = (int *) e2fsck_allocate_memory(ctx,
8952 fs->group_desc_count * sizeof(int), "free block count array");
8953
8954 if ((fs->super->s_first_data_block <
8955 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
8956 (fs->super->s_blocks_count-1 >
8957 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
8958 pctx.num = 1;
8959 pctx.blk = fs->super->s_first_data_block;
8960 pctx.blk2 = fs->super->s_blocks_count -1;
8961 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
8962 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
8963 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8964
8965 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8966 return;
8967 }
8968
8969 if ((fs->super->s_first_data_block <
8970 ext2fs_get_block_bitmap_start(fs->block_map)) ||
8971 (fs->super->s_blocks_count-1 >
8972 ext2fs_get_block_bitmap_end(fs->block_map))) {
8973 pctx.num = 2;
8974 pctx.blk = fs->super->s_first_data_block;
8975 pctx.blk2 = fs->super->s_blocks_count -1;
8976 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
8977 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
8978 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8979
8980 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8981 return;
8982 }
8983
8984redo_counts:
8985 had_problem = 0;
8986 save_problem = 0;
8987 pctx.blk = pctx.blk2 = NO_BLK;
8988 for (i = fs->super->s_first_data_block;
8989 i < fs->super->s_blocks_count;
8990 i++) {
8991 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
8992 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
8993
8994 if (actual == bitmap)
8995 goto do_counts;
8996
8997 if (!actual && bitmap) {
8998 /*
8999 * Block not used, but marked in use in the bitmap.
9000 */
9001 problem = PR_5_BLOCK_UNUSED;
9002 } else {
9003 /*
9004 * Block used, but not marked in use in the bitmap.
9005 */
9006 problem = PR_5_BLOCK_USED;
9007 }
9008 if (pctx.blk == NO_BLK) {
9009 pctx.blk = pctx.blk2 = i;
9010 save_problem = problem;
9011 } else {
9012 if ((problem == save_problem) &&
9013 (pctx.blk2 == i-1))
9014 pctx.blk2++;
9015 else {
9016 print_bitmap_problem(ctx, save_problem, &pctx);
9017 pctx.blk = pctx.blk2 = i;
9018 save_problem = problem;
9019 }
9020 }
9021 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
9022 had_problem++;
9023
9024 do_counts:
9025 if (!bitmap) {
9026 group_free++;
9027 free_blocks++;
9028 }
9029 blocks ++;
9030 if ((blocks == fs->super->s_blocks_per_group) ||
9031 (i == fs->super->s_blocks_count-1)) {
9032 free_array[group] = group_free;
9033 group ++;
9034 blocks = 0;
9035 group_free = 0;
9036 if (ctx->progress)
9037 if ((ctx->progress)(ctx, 5, group,
9038 fs->group_desc_count*2))
9039 return;
9040 }
9041 }
9042 if (pctx.blk != NO_BLK)
9043 print_bitmap_problem(ctx, save_problem, &pctx);
9044 if (had_problem)
9045 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
9046 else
9047 fixit = -1;
9048 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
9049
9050 if (fixit == 1) {
9051 ext2fs_free_block_bitmap(fs->block_map);
9052 retval = ext2fs_copy_bitmap(ctx->block_found_map,
9053 &fs->block_map);
9054 if (retval) {
9055 clear_problem_context(&pctx);
9056 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
9057 ctx->flags |= E2F_FLAG_ABORT;
9058 return;
9059 }
9060 ext2fs_set_bitmap_padding(fs->block_map);
9061 ext2fs_mark_bb_dirty(fs);
9062
9063 /* Redo the counts */
9064 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
9065 memset(free_array, 0, fs->group_desc_count * sizeof(int));
9066 goto redo_counts;
9067 } else if (fixit == 0)
9068 ext2fs_unmark_valid(fs);
9069
9070 for (i = 0; i < fs->group_desc_count; i++) {
9071 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
9072 pctx.group = i;
9073 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
9074 pctx.blk2 = free_array[i];
9075
9076 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
9077 &pctx)) {
9078 fs->group_desc[i].bg_free_blocks_count =
9079 free_array[i];
9080 ext2fs_mark_super_dirty(fs);
9081 } else
9082 ext2fs_unmark_valid(fs);
9083 }
9084 }
9085 if (free_blocks != fs->super->s_free_blocks_count) {
9086 pctx.group = 0;
9087 pctx.blk = fs->super->s_free_blocks_count;
9088 pctx.blk2 = free_blocks;
9089
9090 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
9091 fs->super->s_free_blocks_count = free_blocks;
9092 ext2fs_mark_super_dirty(fs);
9093 } else
9094 ext2fs_unmark_valid(fs);
9095 }
9096 ext2fs_free_mem(&free_array);
9097}
9098
9099static void check_inode_bitmaps(e2fsck_t ctx)
9100{
9101 ext2_filsys fs = ctx->fs;
9102 ext2_ino_t i;
9103 unsigned int free_inodes = 0;
9104 int group_free = 0;
9105 int dirs_count = 0;
9106 int group = 0;
9107 unsigned int inodes = 0;
9108 int *free_array;
9109 int *dir_array;
9110 int actual, bitmap;
9111 errcode_t retval;
9112 struct problem_context pctx;
9113 int problem, save_problem, fixit, had_problem;
9114
9115 clear_problem_context(&pctx);
9116 free_array = (int *) e2fsck_allocate_memory(ctx,
9117 fs->group_desc_count * sizeof(int), "free inode count array");
9118
9119 dir_array = (int *) e2fsck_allocate_memory(ctx,
9120 fs->group_desc_count * sizeof(int), "directory count array");
9121
9122 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
9123 (fs->super->s_inodes_count >
9124 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
9125 pctx.num = 3;
9126 pctx.blk = 1;
9127 pctx.blk2 = fs->super->s_inodes_count;
9128 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
9129 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
9130 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9131
9132 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9133 return;
9134 }
9135 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
9136 (fs->super->s_inodes_count >
9137 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
9138 pctx.num = 4;
9139 pctx.blk = 1;
9140 pctx.blk2 = fs->super->s_inodes_count;
9141 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
9142 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
9143 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9144
9145 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9146 return;
9147 }
9148
9149redo_counts:
9150 had_problem = 0;
9151 save_problem = 0;
9152 pctx.ino = pctx.ino2 = 0;
9153 for (i = 1; i <= fs->super->s_inodes_count; i++) {
9154 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
9155 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
9156
9157 if (actual == bitmap)
9158 goto do_counts;
9159
9160 if (!actual && bitmap) {
9161 /*
9162 * Inode wasn't used, but marked in bitmap
9163 */
9164 problem = PR_5_INODE_UNUSED;
9165 } else /* if (actual && !bitmap) */ {
9166 /*
9167 * Inode used, but not in bitmap
9168 */
9169 problem = PR_5_INODE_USED;
9170 }
9171 if (pctx.ino == 0) {
9172 pctx.ino = pctx.ino2 = i;
9173 save_problem = problem;
9174 } else {
9175 if ((problem == save_problem) &&
9176 (pctx.ino2 == i-1))
9177 pctx.ino2++;
9178 else {
9179 print_bitmap_problem(ctx, save_problem, &pctx);
9180 pctx.ino = pctx.ino2 = i;
9181 save_problem = problem;
9182 }
9183 }
9184 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
9185 had_problem++;
9186
9187do_counts:
9188 if (!bitmap) {
9189 group_free++;
9190 free_inodes++;
9191 } else {
9192 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
9193 dirs_count++;
9194 }
9195 inodes++;
9196 if ((inodes == fs->super->s_inodes_per_group) ||
9197 (i == fs->super->s_inodes_count)) {
9198 free_array[group] = group_free;
9199 dir_array[group] = dirs_count;
9200 group ++;
9201 inodes = 0;
9202 group_free = 0;
9203 dirs_count = 0;
9204 if (ctx->progress)
9205 if ((ctx->progress)(ctx, 5,
9206 group + fs->group_desc_count,
9207 fs->group_desc_count*2))
9208 return;
9209 }
9210 }
9211 if (pctx.ino)
9212 print_bitmap_problem(ctx, save_problem, &pctx);
9213
9214 if (had_problem)
9215 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
9216 else
9217 fixit = -1;
9218 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
9219
9220 if (fixit == 1) {
9221 ext2fs_free_inode_bitmap(fs->inode_map);
9222 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
9223 &fs->inode_map);
9224 if (retval) {
9225 clear_problem_context(&pctx);
9226 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
9227 ctx->flags |= E2F_FLAG_ABORT;
9228 return;
9229 }
9230 ext2fs_set_bitmap_padding(fs->inode_map);
9231 ext2fs_mark_ib_dirty(fs);
9232
9233 /* redo counts */
9234 inodes = 0; free_inodes = 0; group_free = 0;
9235 dirs_count = 0; group = 0;
9236 memset(free_array, 0, fs->group_desc_count * sizeof(int));
9237 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
9238 goto redo_counts;
9239 } else if (fixit == 0)
9240 ext2fs_unmark_valid(fs);
9241
9242 for (i = 0; i < fs->group_desc_count; i++) {
9243 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
9244 pctx.group = i;
9245 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
9246 pctx.ino2 = free_array[i];
9247 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
9248 &pctx)) {
9249 fs->group_desc[i].bg_free_inodes_count =
9250 free_array[i];
9251 ext2fs_mark_super_dirty(fs);
9252 } else
9253 ext2fs_unmark_valid(fs);
9254 }
9255 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
9256 pctx.group = i;
9257 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
9258 pctx.ino2 = dir_array[i];
9259
9260 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
9261 &pctx)) {
9262 fs->group_desc[i].bg_used_dirs_count =
9263 dir_array[i];
9264 ext2fs_mark_super_dirty(fs);
9265 } else
9266 ext2fs_unmark_valid(fs);
9267 }
9268 }
9269 if (free_inodes != fs->super->s_free_inodes_count) {
9270 pctx.group = -1;
9271 pctx.ino = fs->super->s_free_inodes_count;
9272 pctx.ino2 = free_inodes;
9273
9274 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
9275 fs->super->s_free_inodes_count = free_inodes;
9276 ext2fs_mark_super_dirty(fs);
9277 } else
9278 ext2fs_unmark_valid(fs);
9279 }
9280 ext2fs_free_mem(&free_array);
9281 ext2fs_free_mem(&dir_array);
9282}
9283
9284static void check_inode_end(e2fsck_t ctx)
9285{
9286 ext2_filsys fs = ctx->fs;
9287 ext2_ino_t end, save_inodes_count, i;
9288 struct problem_context pctx;
9289
9290 clear_problem_context(&pctx);
9291
9292 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
9293 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
9294 &save_inodes_count);
9295 if (pctx.errcode) {
9296 pctx.num = 1;
9297 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9298 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9299 return;
9300 }
9301 if (save_inodes_count == end)
9302 return;
9303
9304 for (i = save_inodes_count + 1; i <= end; i++) {
9305 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
9306 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
9307 for (i = save_inodes_count + 1; i <= end; i++)
9308 ext2fs_mark_inode_bitmap(fs->inode_map,
9309 i);
9310 ext2fs_mark_ib_dirty(fs);
9311 } else
9312 ext2fs_unmark_valid(fs);
9313 break;
9314 }
9315 }
9316
9317 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
9318 save_inodes_count, 0);
9319 if (pctx.errcode) {
9320 pctx.num = 2;
9321 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9322 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9323 return;
9324 }
9325}
9326
9327static void check_block_end(e2fsck_t ctx)
9328{
9329 ext2_filsys fs = ctx->fs;
9330 blk_t end, save_blocks_count, i;
9331 struct problem_context pctx;
9332
9333 clear_problem_context(&pctx);
9334
9335 end = fs->block_map->start +
9336 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
9337 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
9338 &save_blocks_count);
9339 if (pctx.errcode) {
9340 pctx.num = 3;
9341 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9342 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9343 return;
9344 }
9345 if (save_blocks_count == end)
9346 return;
9347
9348 for (i = save_blocks_count + 1; i <= end; i++) {
9349 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
9350 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
9351 for (i = save_blocks_count + 1; i <= end; i++)
9352 ext2fs_mark_block_bitmap(fs->block_map,
9353 i);
9354 ext2fs_mark_bb_dirty(fs);
9355 } else
9356 ext2fs_unmark_valid(fs);
9357 break;
9358 }
9359 }
9360
9361 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
9362 save_blocks_count, 0);
9363 if (pctx.errcode) {
9364 pctx.num = 4;
9365 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9366 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9367 return;
9368 }
9369}
9370
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009371static void e2fsck_pass5(e2fsck_t ctx)
9372{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009373 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009374
Rob Landley3e72c592006-04-06 22:49:04 +00009375 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009376
9377 clear_problem_context(&pctx);
9378
9379 if (!(ctx->options & E2F_OPT_PREEN))
9380 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
9381
9382 if (ctx->progress)
9383 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
9384 return;
9385
9386 e2fsck_read_bitmaps(ctx);
9387
9388 check_block_bitmaps(ctx);
9389 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9390 return;
9391 check_inode_bitmaps(ctx);
9392 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9393 return;
9394 check_inode_end(ctx);
9395 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9396 return;
9397 check_block_end(ctx);
9398 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9399 return;
9400
9401 ext2fs_free_inode_bitmap(ctx->inode_used_map);
9402 ctx->inode_used_map = 0;
9403 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
9404 ctx->inode_dir_map = 0;
9405 ext2fs_free_block_bitmap(ctx->block_found_map);
9406 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009407}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009408
9409/*
9410 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009411 */
9412
9413#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
9414#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
9415#define PR_NO_DEFAULT 0x000004 /* Default to no */
9416#define PR_MSG_ONLY 0x000008 /* Print message only */
9417
9418/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
9419
9420#define PR_FATAL 0x001000 /* Fatal error */
9421#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
9422 /* ask another */
9423#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
9424#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
9425#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
9426#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
9427#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
9428
9429
9430#define PROMPT_NONE 0
9431#define PROMPT_FIX 1
9432#define PROMPT_CLEAR 2
9433#define PROMPT_RELOCATE 3
9434#define PROMPT_ALLOCATE 4
9435#define PROMPT_EXPAND 5
9436#define PROMPT_CONNECT 6
9437#define PROMPT_CREATE 7
9438#define PROMPT_SALVAGE 8
9439#define PROMPT_TRUNCATE 9
9440#define PROMPT_CLEAR_INODE 10
9441#define PROMPT_ABORT 11
9442#define PROMPT_SPLIT 12
9443#define PROMPT_CONTINUE 13
9444#define PROMPT_CLONE 14
9445#define PROMPT_DELETE 15
9446#define PROMPT_SUPPRESS 16
9447#define PROMPT_UNLINK 17
9448#define PROMPT_CLEAR_HTREE 18
9449#define PROMPT_RECREATE 19
9450#define PROMPT_NULL 20
9451
9452struct e2fsck_problem {
9453 problem_t e2p_code;
9454 const char * e2p_description;
9455 char prompt;
9456 int flags;
9457 problem_t second_code;
9458};
9459
9460struct latch_descr {
9461 int latch_code;
9462 problem_t question;
9463 problem_t end_message;
9464 int flags;
9465};
9466
9467/*
9468 * These are the prompts which are used to ask the user if they want
9469 * to fix a problem.
9470 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009471static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009472 N_("(no prompt)"), /* 0 */
9473 N_("Fix"), /* 1 */
9474 N_("Clear"), /* 2 */
9475 N_("Relocate"), /* 3 */
9476 N_("Allocate"), /* 4 */
9477 N_("Expand"), /* 5 */
9478 N_("Connect to /lost+found"), /* 6 */
9479 N_("Create"), /* 7 */
9480 N_("Salvage"), /* 8 */
9481 N_("Truncate"), /* 9 */
9482 N_("Clear inode"), /* 10 */
9483 N_("Abort"), /* 11 */
9484 N_("Split"), /* 12 */
9485 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00009486 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009487 N_("Delete file"), /* 15 */
9488 N_("Suppress messages"),/* 16 */
9489 N_("Unlink"), /* 17 */
9490 N_("Clear HTree index"),/* 18 */
9491 N_("Recreate"), /* 19 */
9492 "", /* 20 */
9493};
9494
9495/*
9496 * These messages are printed when we are preen mode and we will be
9497 * automatically fixing the problem.
9498 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009499static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009500 N_("(NONE)"), /* 0 */
9501 N_("FIXED"), /* 1 */
9502 N_("CLEARED"), /* 2 */
9503 N_("RELOCATED"), /* 3 */
9504 N_("ALLOCATED"), /* 4 */
9505 N_("EXPANDED"), /* 5 */
9506 N_("RECONNECTED"), /* 6 */
9507 N_("CREATED"), /* 7 */
9508 N_("SALVAGED"), /* 8 */
9509 N_("TRUNCATED"), /* 9 */
9510 N_("INODE CLEARED"), /* 10 */
9511 N_("ABORTED"), /* 11 */
9512 N_("SPLIT"), /* 12 */
9513 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00009514 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009515 N_("FILE DELETED"), /* 15 */
9516 N_("SUPPRESSED"), /* 16 */
9517 N_("UNLINKED"), /* 17 */
9518 N_("HTREE INDEX CLEARED"),/* 18 */
9519 N_("WILL RECREATE"), /* 19 */
9520 "", /* 20 */
9521};
9522
9523static const struct e2fsck_problem problem_table[] = {
9524
9525 /* Pre-Pass 1 errors */
9526
9527 /* Block bitmap not in group */
9528 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
9529 PROMPT_RELOCATE, PR_LATCH_RELOC },
9530
9531 /* Inode bitmap not in group */
9532 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
9533 PROMPT_RELOCATE, PR_LATCH_RELOC },
9534
9535 /* Inode table not in group */
9536 { PR_0_ITABLE_NOT_GROUP,
9537 N_("@i table for @g %g is not in @g. (@b %b)\n"
9538 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
9539 PROMPT_RELOCATE, PR_LATCH_RELOC },
9540
9541 /* Superblock corrupt */
9542 { PR_0_SB_CORRUPT,
9543 N_("\nThe @S could not be read or does not describe a correct ext2\n"
9544 "@f. If the @v is valid and it really contains an ext2\n"
9545 "@f (and not swap or ufs or something else), then the @S\n"
9546 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
9547 " e2fsck -b %S <@v>\n\n"),
9548 PROMPT_NONE, PR_FATAL },
9549
9550 /* Filesystem size is wrong */
9551 { PR_0_FS_SIZE_WRONG,
9552 N_("The @f size (according to the @S) is %b @bs\n"
9553 "The physical size of the @v is %c @bs\n"
9554 "Either the @S or the partition table is likely to be corrupt!\n"),
9555 PROMPT_ABORT, 0 },
9556
9557 /* Fragments not supported */
9558 { PR_0_NO_FRAGMENTS,
9559 N_("@S @b_size = %b, fragsize = %c.\n"
9560 "This version of e2fsck does not support fragment sizes different\n"
9561 "from the @b size.\n"),
9562 PROMPT_NONE, PR_FATAL },
9563
9564 /* Bad blocks_per_group */
9565 { PR_0_BLOCKS_PER_GROUP,
9566 N_("@S @bs_per_group = %b, should have been %c\n"),
9567 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9568
9569 /* Bad first_data_block */
9570 { PR_0_FIRST_DATA_BLOCK,
9571 N_("@S first_data_@b = %b, should have been %c\n"),
9572 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9573
9574 /* Adding UUID to filesystem */
9575 { PR_0_ADD_UUID,
9576 N_("@f did not have a UUID; generating one.\n\n"),
9577 PROMPT_NONE, 0 },
9578
9579 /* Relocate hint */
9580 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00009581 N_("Note: if several inode or block bitmap blocks or part\n"
9582 "of the inode table require relocation, you may wish to try\n"
9583 "running e2fsck with the '-b %S' option first. The problem\n"
9584 "may lie only with the primary block group descriptors, and\n"
9585 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009586 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
9587
9588 /* Miscellaneous superblock corruption */
9589 { PR_0_MISC_CORRUPT_SUPER,
9590 N_("Corruption found in @S. (%s = %N).\n"),
9591 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9592
9593 /* Error determing physical device size of filesystem */
9594 { PR_0_GETSIZE_ERROR,
9595 N_("Error determining size of the physical @v: %m\n"),
9596 PROMPT_NONE, PR_FATAL },
9597
9598 /* Inode count in superblock is incorrect */
9599 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00009600 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009601 PROMPT_FIX, 0 },
9602
9603 { PR_0_HURD_CLEAR_FILETYPE,
9604 N_("The Hurd does not support the filetype feature.\n"),
9605 PROMPT_CLEAR, 0 },
9606
9607 /* Journal inode is invalid */
9608 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009609 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009610 PROMPT_CLEAR, PR_PREEN_OK },
9611
9612 /* The external journal has (unsupported) multiple filesystems */
9613 { PR_0_JOURNAL_UNSUPP_MULTIFS,
9614 N_("External @j has multiple @f users (unsupported).\n"),
9615 PROMPT_NONE, PR_FATAL },
9616
9617 /* Can't find external journal */
9618 { PR_0_CANT_FIND_JOURNAL,
9619 N_("Can't find external @j\n"),
9620 PROMPT_NONE, PR_FATAL },
9621
9622 /* External journal has bad superblock */
9623 { PR_0_EXT_JOURNAL_BAD_SUPER,
9624 N_("External @j has bad @S\n"),
9625 PROMPT_NONE, PR_FATAL },
9626
9627 /* Superblock has a bad journal UUID */
9628 { PR_0_JOURNAL_BAD_UUID,
9629 N_("External @j does not support this @f\n"),
9630 PROMPT_NONE, PR_FATAL },
9631
9632 /* Journal has an unknown superblock type */
9633 { PR_0_JOURNAL_UNSUPP_SUPER,
9634 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
9635 "It is likely that your copy of e2fsck is old and/or doesn't "
9636 "support this @j format.\n"
9637 "It is also possible the @j @S is corrupt.\n"),
9638 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
9639
9640 /* Journal superblock is corrupt */
9641 { PR_0_JOURNAL_BAD_SUPER,
9642 N_("Ext3 @j @S is corrupt.\n"),
9643 PROMPT_FIX, PR_PREEN_OK },
9644
9645 /* Superblock flag should be cleared */
9646 { PR_0_JOURNAL_HAS_JOURNAL,
9647 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
9648 PROMPT_CLEAR, PR_PREEN_OK },
9649
9650 /* Superblock flag is incorrect */
9651 { PR_0_JOURNAL_RECOVER_SET,
9652 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
9653 PROMPT_CLEAR, PR_PREEN_OK },
9654
9655 /* Journal has data, but recovery flag is clear */
9656 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009657 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009658 PROMPT_NONE, 0 },
9659
9660 /* Ask if we should clear the journal */
9661 { PR_0_JOURNAL_RESET_JOURNAL,
9662 N_("Clear @j"),
9663 PROMPT_NULL, PR_PREEN_NOMSG },
9664
9665 /* Ask if we should run the journal anyway */
9666 { PR_0_JOURNAL_RUN,
9667 N_("Run @j anyway"),
9668 PROMPT_NULL, 0 },
9669
9670 /* Run the journal by default */
9671 { PR_0_JOURNAL_RUN_DEFAULT,
9672 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
9673 PROMPT_NONE, 0 },
9674
9675 /* Clearing orphan inode */
9676 { PR_0_ORPHAN_CLEAR_INODE,
9677 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
9678 PROMPT_NONE, 0 },
9679
9680 /* Illegal block found in orphaned inode */
9681 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
9682 N_("@I @b #%B (%b) found in @o @i %i.\n"),
9683 PROMPT_NONE, 0 },
9684
9685 /* Already cleared block found in orphaned inode */
9686 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
9687 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
9688 PROMPT_NONE, 0 },
9689
9690 /* Illegal orphan inode in superblock */
9691 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
9692 N_("@I @o @i %i in @S.\n"),
9693 PROMPT_NONE, 0 },
9694
9695 /* Illegal inode in orphaned inode list */
9696 { PR_0_ORPHAN_ILLEGAL_INODE,
9697 N_("@I @i %i in @o @i list.\n"),
9698 PROMPT_NONE, 0 },
9699
9700 /* Filesystem revision is 0, but feature flags are set */
9701 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00009702 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009703 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
9704
9705 /* Journal superblock has an unknown read-only feature flag set */
9706 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
9707 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
9708 PROMPT_ABORT, 0 },
9709
9710 /* Journal superblock has an unknown incompatible feature flag set */
9711 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
9712 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
9713 PROMPT_ABORT, 0 },
9714
9715 /* Journal has unsupported version number */
9716 { PR_0_JOURNAL_UNSUPP_VERSION,
9717 N_("@j version not supported by this e2fsck.\n"),
9718 PROMPT_ABORT, 0 },
9719
9720 /* Moving journal to hidden file */
9721 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00009722 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009723 PROMPT_NONE, 0 },
9724
9725 /* Error moving journal to hidden file */
9726 { PR_0_ERR_MOVE_JOURNAL,
9727 N_("Error moving @j: %m\n\n"),
9728 PROMPT_NONE, 0 },
9729
9730 /* Clearing V2 journal superblock */
9731 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00009732 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009733 "Clearing fields beyond the V1 @j @S...\n\n"),
9734 PROMPT_NONE, 0 },
9735
9736 /* Backup journal inode blocks */
9737 { PR_0_BACKUP_JNL,
9738 N_("Backing up @j @i @b information.\n\n"),
9739 PROMPT_NONE, 0 },
9740
9741 /* Reserved blocks w/o resize_inode */
9742 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
9743 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
9744 "is %N; @s zero. "),
9745 PROMPT_FIX, 0 },
9746
9747 /* Resize_inode not enabled, but resize inode is non-zero */
9748 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009749 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009750 PROMPT_CLEAR, 0 },
9751
9752 /* Resize inode invalid */
9753 { PR_0_RESIZE_INODE_INVALID,
9754 N_("Resize @i not valid. "),
9755 PROMPT_RECREATE, 0 },
9756
9757 /* Pass 1 errors */
9758
9759 /* Pass 1: Checking inodes, blocks, and sizes */
9760 { PR_1_PASS_HEADER,
9761 N_("Pass 1: Checking @is, @bs, and sizes\n"),
9762 PROMPT_NONE, 0 },
9763
9764 /* Root directory is not an inode */
9765 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
9766 PROMPT_CLEAR, 0 },
9767
9768 /* Root directory has dtime set */
9769 { PR_1_ROOT_DTIME,
9770 N_("@r has dtime set (probably due to old mke2fs). "),
9771 PROMPT_FIX, PR_PREEN_OK },
9772
9773 /* Reserved inode has bad mode */
9774 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009775 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009776 PROMPT_CLEAR, PR_PREEN_OK },
9777
9778 /* Deleted inode has zero dtime */
9779 { PR_1_ZERO_DTIME,
9780 N_("@D @i %i has zero dtime. "),
9781 PROMPT_FIX, PR_PREEN_OK },
9782
9783 /* Inode in use, but dtime set */
9784 { PR_1_SET_DTIME,
9785 N_("@i %i is in use, but has dtime set. "),
9786 PROMPT_FIX, PR_PREEN_OK },
9787
9788 /* Zero-length directory */
9789 { PR_1_ZERO_LENGTH_DIR,
9790 N_("@i %i is a @z @d. "),
9791 PROMPT_CLEAR, PR_PREEN_OK },
9792
9793 /* Block bitmap conflicts with some other fs block */
9794 { PR_1_BB_CONFLICT,
9795 N_("@g %g's @b @B at %b @C.\n"),
9796 PROMPT_RELOCATE, 0 },
9797
9798 /* Inode bitmap conflicts with some other fs block */
9799 { PR_1_IB_CONFLICT,
9800 N_("@g %g's @i @B at %b @C.\n"),
9801 PROMPT_RELOCATE, 0 },
9802
9803 /* Inode table conflicts with some other fs block */
9804 { PR_1_ITABLE_CONFLICT,
9805 N_("@g %g's @i table at %b @C.\n"),
9806 PROMPT_RELOCATE, 0 },
9807
9808 /* Block bitmap is on a bad block */
9809 { PR_1_BB_BAD_BLOCK,
9810 N_("@g %g's @b @B (%b) is bad. "),
9811 PROMPT_RELOCATE, 0 },
9812
9813 /* Inode bitmap is on a bad block */
9814 { PR_1_IB_BAD_BLOCK,
9815 N_("@g %g's @i @B (%b) is bad. "),
9816 PROMPT_RELOCATE, 0 },
9817
9818 /* Inode has incorrect i_size */
9819 { PR_1_BAD_I_SIZE,
9820 N_("@i %i, i_size is %Is, @s %N. "),
9821 PROMPT_FIX, PR_PREEN_OK },
9822
9823 /* Inode has incorrect i_blocks */
9824 { PR_1_BAD_I_BLOCKS,
9825 N_("@i %i, i_@bs is %Ib, @s %N. "),
9826 PROMPT_FIX, PR_PREEN_OK },
9827
9828 /* Illegal blocknumber in inode */
9829 { PR_1_ILLEGAL_BLOCK_NUM,
9830 N_("@I @b #%B (%b) in @i %i. "),
9831 PROMPT_CLEAR, PR_LATCH_BLOCK },
9832
9833 /* Block number overlaps fs metadata */
9834 { PR_1_BLOCK_OVERLAPS_METADATA,
9835 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
9836 PROMPT_CLEAR, PR_LATCH_BLOCK },
9837
9838 /* Inode has illegal blocks (latch question) */
9839 { PR_1_INODE_BLOCK_LATCH,
9840 N_("@i %i has illegal @b(s). "),
9841 PROMPT_CLEAR, 0 },
9842
9843 /* Too many bad blocks in inode */
9844 { PR_1_TOO_MANY_BAD_BLOCKS,
9845 N_("Too many illegal @bs in @i %i.\n"),
9846 PROMPT_CLEAR_INODE, PR_NO_OK },
9847
9848 /* Illegal block number in bad block inode */
9849 { PR_1_BB_ILLEGAL_BLOCK_NUM,
9850 N_("@I @b #%B (%b) in bad @b @i. "),
9851 PROMPT_CLEAR, PR_LATCH_BBLOCK },
9852
9853 /* Bad block inode has illegal blocks (latch question) */
9854 { PR_1_INODE_BBLOCK_LATCH,
9855 N_("Bad @b @i has illegal @b(s). "),
9856 PROMPT_CLEAR, 0 },
9857
9858 /* Duplicate or bad blocks in use! */
9859 { PR_1_DUP_BLOCKS_PREENSTOP,
9860 N_("Duplicate or bad @b in use!\n"),
9861 PROMPT_NONE, 0 },
9862
9863 /* Bad block used as bad block indirect block */
9864 { PR_1_BBINODE_BAD_METABLOCK,
9865 N_("Bad @b %b used as bad @b @i indirect @b. "),
9866 PROMPT_CLEAR, PR_LATCH_BBLOCK },
9867
9868 /* Inconsistency can't be fixed prompt */
9869 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
9870 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
9871 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
9872 "in the @f.\n"),
9873 PROMPT_CONTINUE, PR_PREEN_NOMSG },
9874
9875 /* Bad primary block */
9876 { PR_1_BAD_PRIMARY_BLOCK,
9877 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
9878 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
9879
9880 /* Bad primary block prompt */
9881 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00009882 N_("You can remove this @b from the bad @b list and hope\n"
9883 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009884 PROMPT_CLEAR, PR_PREEN_NOMSG },
9885
9886 /* Bad primary superblock */
9887 { PR_1_BAD_PRIMARY_SUPERBLOCK,
9888 N_("The primary @S (%b) is on the bad @b list.\n"),
9889 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
9890
9891 /* Bad primary block group descriptors */
9892 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
9893 N_("Block %b in the primary @g descriptors "
9894 "is on the bad @b list\n"),
9895 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
9896
9897 /* Bad superblock in group */
9898 { PR_1_BAD_SUPERBLOCK,
9899 N_("Warning: Group %g's @S (%b) is bad.\n"),
9900 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9901
9902 /* Bad block group descriptors in group */
9903 { PR_1_BAD_GROUP_DESCRIPTORS,
9904 N_("Warning: Group %g's copy of the @g descriptors has a bad "
9905 "@b (%b).\n"),
9906 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9907
9908 /* Block claimed for no reason */
9909 { PR_1_PROGERR_CLAIMED_BLOCK,
9910 N_("Programming error? @b #%b claimed for no reason in "
9911 "process_bad_@b.\n"),
9912 PROMPT_NONE, PR_PREEN_OK },
9913
9914 /* Error allocating blocks for relocating metadata */
9915 { PR_1_RELOC_BLOCK_ALLOCATE,
9916 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
9917 PROMPT_NONE, PR_PREEN_OK },
9918
9919 /* Error allocating block buffer during relocation process */
9920 { PR_1_RELOC_MEMORY_ALLOCATE,
9921 N_("@A @b buffer for relocating %s\n"),
9922 PROMPT_NONE, PR_PREEN_OK },
9923
9924 /* Relocating metadata group information from X to Y */
9925 { PR_1_RELOC_FROM_TO,
9926 N_("Relocating @g %g's %s from %b to %c...\n"),
9927 PROMPT_NONE, PR_PREEN_OK },
9928
9929 /* Relocating metatdata group information to X */
9930 { PR_1_RELOC_TO,
9931 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
9932 PROMPT_NONE, PR_PREEN_OK },
9933
9934 /* Block read error during relocation process */
9935 { PR_1_RELOC_READ_ERR,
9936 N_("Warning: could not read @b %b of %s: %m\n"),
9937 PROMPT_NONE, PR_PREEN_OK },
9938
9939 /* Block write error during relocation process */
9940 { PR_1_RELOC_WRITE_ERR,
9941 N_("Warning: could not write @b %b for %s: %m\n"),
9942 PROMPT_NONE, PR_PREEN_OK },
9943
9944 /* Error allocating inode bitmap */
9945 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009946 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009947 PROMPT_NONE, PR_FATAL },
9948
9949 /* Error allocating block bitmap */
9950 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009951 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009952 PROMPT_NONE, PR_FATAL },
9953
9954 /* Error allocating icount structure */
9955 { PR_1_ALLOCATE_ICOUNT,
9956 N_("@A icount link information: %m\n"),
9957 PROMPT_NONE, PR_FATAL },
9958
9959 /* Error allocating dbcount */
9960 { PR_1_ALLOCATE_DBCOUNT,
9961 N_("@A @d @b array: %m\n"),
9962 PROMPT_NONE, PR_FATAL },
9963
9964 /* Error while scanning inodes */
9965 { PR_1_ISCAN_ERROR,
9966 N_("Error while scanning @is (%i): %m\n"),
9967 PROMPT_NONE, PR_FATAL },
9968
9969 /* Error while iterating over blocks */
9970 { PR_1_BLOCK_ITERATE,
9971 N_("Error while iterating over @bs in @i %i: %m\n"),
9972 PROMPT_NONE, PR_FATAL },
9973
9974 /* Error while storing inode count information */
9975 { PR_1_ICOUNT_STORE,
9976 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
9977 PROMPT_NONE, PR_FATAL },
9978
9979 /* Error while storing directory block information */
9980 { PR_1_ADD_DBLOCK,
9981 N_("Error storing @d @b information "
9982 "(@i=%i, @b=%b, num=%N): %m\n"),
9983 PROMPT_NONE, PR_FATAL },
9984
9985 /* Error while reading inode (for clearing) */
9986 { PR_1_READ_INODE,
9987 N_("Error reading @i %i: %m\n"),
9988 PROMPT_NONE, PR_FATAL },
9989
9990 /* Suppress messages prompt */
9991 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
9992
9993 /* Imagic flag set on an inode when filesystem doesn't support it */
9994 { PR_1_SET_IMAGIC,
9995 N_("@i %i has imagic flag set. "),
9996 PROMPT_CLEAR, 0 },
9997
9998 /* Immutable flag set on a device or socket inode */
9999 { PR_1_SET_IMMUTABLE,
10000 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
10001 "or append-only flag set. "),
10002 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
10003
10004 /* Compression flag set on an inode when filesystem doesn't support it */
10005 { PR_1_COMPR_SET,
10006 N_("@i %i has @cion flag set on @f without @cion support. "),
10007 PROMPT_CLEAR, 0 },
10008
10009 /* Non-zero size for device, fifo or socket inode */
10010 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010011 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010012 PROMPT_FIX, PR_PREEN_OK },
10013
10014 /* Filesystem revision is 0, but feature flags are set */
10015 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +000010016 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010017 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
10018
10019 /* Journal inode is not in use, but contains data */
10020 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +000010021 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010022 PROMPT_CLEAR, PR_PREEN_OK },
10023
10024 /* Journal has bad mode */
10025 { PR_1_JOURNAL_BAD_MODE,
10026 N_("@j is not regular file. "),
10027 PROMPT_FIX, PR_PREEN_OK },
10028
10029 /* Deal with inodes that were part of orphan linked list */
10030 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +000010031 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010032 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
10033
10034 /* Deal with inodes that were part of corrupted orphan linked
10035 list (latch question) */
10036 { PR_1_ORPHAN_LIST_REFUGEES,
10037 N_("@is that were part of a corrupted orphan linked list found. "),
10038 PROMPT_FIX, 0 },
10039
10040 /* Error allocating refcount structure */
10041 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010042 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010043 PROMPT_NONE, PR_FATAL },
10044
10045 /* Error reading extended attribute block */
10046 { PR_1_READ_EA_BLOCK,
10047 N_("Error reading @a @b %b for @i %i. "),
10048 PROMPT_CLEAR, 0 },
10049
10050 /* Invalid extended attribute block */
10051 { PR_1_BAD_EA_BLOCK,
10052 N_("@i %i has a bad @a @b %b. "),
10053 PROMPT_CLEAR, 0 },
10054
10055 /* Error reading Extended Attribute block while fixing refcount */
10056 { PR_1_EXTATTR_READ_ABORT,
10057 N_("Error reading @a @b %b (%m). "),
10058 PROMPT_ABORT, 0 },
10059
10060 /* Extended attribute reference count incorrect */
10061 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010062 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010063 PROMPT_FIX, 0 },
10064
10065 /* Error writing Extended Attribute block while fixing refcount */
10066 { PR_1_EXTATTR_WRITE,
10067 N_("Error writing @a @b %b (%m). "),
10068 PROMPT_ABORT, 0 },
10069
10070 /* Multiple EA blocks not supported */
10071 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +000010072 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010073 PROMPT_CLEAR, 0},
10074
10075 /* Error allocating EA region allocation structure */
10076 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +000010077 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010078 PROMPT_ABORT, 0},
10079
10080 /* Error EA allocation collision */
10081 { PR_1_EA_ALLOC_COLLISION,
10082 N_("@a @b %b is corrupt (allocation collision). "),
10083 PROMPT_CLEAR, 0},
10084
10085 /* Bad extended attribute name */
10086 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +000010087 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010088 PROMPT_CLEAR, 0},
10089
10090 /* Bad extended attribute value */
10091 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +000010092 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010093 PROMPT_CLEAR, 0},
10094
10095 /* Inode too big (latch question) */
10096 { PR_1_INODE_TOOBIG,
10097 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
10098
10099 /* Directory too big */
10100 { PR_1_TOOBIG_DIR,
10101 N_("@b #%B (%b) causes @d to be too big. "),
10102 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10103
10104 /* Regular file too big */
10105 { PR_1_TOOBIG_REG,
10106 N_("@b #%B (%b) causes file to be too big. "),
10107 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10108
10109 /* Symlink too big */
10110 { PR_1_TOOBIG_SYMLINK,
10111 N_("@b #%B (%b) causes symlink to be too big. "),
10112 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10113
10114 /* INDEX_FL flag set on a non-HTREE filesystem */
10115 { PR_1_HTREE_SET,
10116 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
10117 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10118
10119 /* INDEX_FL flag set on a non-directory */
10120 { PR_1_HTREE_NODIR,
10121 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
10122 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10123
10124 /* Invalid root node in HTREE directory */
10125 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010126 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010127 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10128
10129 /* Unsupported hash version in HTREE directory */
10130 { PR_1_HTREE_HASHV,
10131 N_("@h %i has an unsupported hash version (%N)\n"),
10132 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10133
10134 /* Incompatible flag in HTREE root node */
10135 { PR_1_HTREE_INCOMPAT,
10136 N_("@h %i uses an incompatible htree root node flag.\n"),
10137 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10138
10139 /* HTREE too deep */
10140 { PR_1_HTREE_DEPTH,
10141 N_("@h %i has a tree depth (%N) which is too big\n"),
10142 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10143
10144 /* Bad block has indirect block that conflicts with filesystem block */
10145 { PR_1_BB_FS_BLOCK,
10146 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
10147 "@f metadata. "),
10148 PROMPT_CLEAR, PR_LATCH_BBLOCK },
10149
10150 /* Resize inode failed */
10151 { PR_1_RESIZE_INODE_CREATE,
10152 N_("Resize @i (re)creation failed: %m."),
10153 PROMPT_ABORT, 0 },
10154
10155 /* invalid inode->i_extra_isize */
10156 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010157 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010158 PROMPT_FIX, PR_PREEN_OK },
10159
10160 /* invalid ea entry->e_name_len */
10161 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +000010162 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010163 PROMPT_CLEAR, PR_PREEN_OK },
10164
10165 /* invalid ea entry->e_value_size */
10166 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010167 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010168 PROMPT_CLEAR, PR_PREEN_OK },
10169
10170 /* invalid ea entry->e_value_offs */
10171 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +000010172 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010173 PROMPT_CLEAR, PR_PREEN_OK },
10174
10175 /* invalid ea entry->e_value_block */
10176 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +000010177 N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010178 PROMPT_CLEAR, PR_PREEN_OK },
10179
10180 /* invalid ea entry->e_hash */
10181 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +000010182 N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010183 PROMPT_CLEAR, PR_PREEN_OK },
10184
10185 /* Pass 1b errors */
10186
10187 /* Pass 1B: Rescan for duplicate/bad blocks */
10188 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010189 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
10190 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010191 PROMPT_NONE, 0 },
10192
10193 /* Duplicate/bad block(s) header */
10194 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010195 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010196 PROMPT_NONE, 0 },
10197
10198 /* Duplicate/bad block(s) in inode */
10199 { PR_1B_DUP_BLOCK,
10200 " %b",
10201 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
10202
10203 /* Duplicate/bad block(s) end */
10204 { PR_1B_DUP_BLOCK_END,
10205 "\n",
10206 PROMPT_NONE, PR_PREEN_NOHDR },
10207
10208 /* Error while scanning inodes */
10209 { PR_1B_ISCAN_ERROR,
10210 N_("Error while scanning inodes (%i): %m\n"),
10211 PROMPT_NONE, PR_FATAL },
10212
10213 /* Error allocating inode bitmap */
10214 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010215 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010216 PROMPT_NONE, PR_FATAL },
10217
10218 /* Error while iterating over blocks */
10219 { PR_1B_BLOCK_ITERATE,
10220 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
10221 PROMPT_NONE, 0 },
10222
10223 /* Error adjusting EA refcount */
10224 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010225 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010226 PROMPT_NONE, 0 },
10227
10228
Mike Frysinger874af852006-03-08 07:03:27 +000010229 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010230 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010231 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010232 PROMPT_NONE, 0 },
10233
10234
Mike Frysinger874af852006-03-08 07:03:27 +000010235 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010236 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010237 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010238 PROMPT_NONE, 0 },
10239
10240 /* File has duplicate blocks */
10241 { PR_1D_DUP_FILE,
10242 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +000010243 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010244 PROMPT_NONE, 0 },
10245
10246 /* List of files sharing duplicate blocks */
10247 { PR_1D_DUP_FILE_LIST,
10248 N_("\t%Q (@i #%i, mod time %IM)\n"),
10249 PROMPT_NONE, 0 },
10250
10251 /* File sharing blocks with filesystem metadata */
10252 { PR_1D_SHARE_METADATA,
10253 N_("\t<@f metadata>\n"),
10254 PROMPT_NONE, 0 },
10255
10256 /* Report of how many duplicate/bad inodes */
10257 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +000010258 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010259 PROMPT_NONE, 0 },
10260
10261 /* Duplicated blocks already reassigned or cloned. */
10262 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +000010263 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010264 PROMPT_NONE, 0 },
10265
10266 /* Clone duplicate/bad blocks? */
10267 { PR_1D_CLONE_QUESTION,
10268 "", PROMPT_CLONE, PR_NO_OK },
10269
10270 /* Delete file? */
10271 { PR_1D_DELETE_QUESTION,
10272 "", PROMPT_DELETE, 0 },
10273
10274 /* Couldn't clone file (error) */
10275 { PR_1D_CLONE_ERROR,
10276 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
10277
10278 /* Pass 2 errors */
10279
10280 /* Pass 2: Checking directory structure */
10281 { PR_2_PASS_HEADER,
10282 N_("Pass 2: Checking @d structure\n"),
10283 PROMPT_NONE, 0 },
10284
10285 /* Bad inode number for '.' */
10286 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010287 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010288 PROMPT_FIX, 0 },
10289
10290 /* Directory entry has bad inode number */
10291 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +000010292 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010293 PROMPT_CLEAR, 0 },
10294
10295 /* Directory entry has deleted or unused inode */
10296 { PR_2_UNUSED_INODE,
10297 N_("@E has @D/unused @i %Di. "),
10298 PROMPT_CLEAR, PR_PREEN_OK },
10299
10300 /* Directry entry is link to '.' */
10301 { PR_2_LINK_DOT,
10302 N_("@E @L to '.' "),
10303 PROMPT_CLEAR, 0 },
10304
10305 /* Directory entry points to inode now located in a bad block */
10306 { PR_2_BB_INODE,
10307 N_("@E points to @i (%Di) located in a bad @b.\n"),
10308 PROMPT_CLEAR, 0 },
10309
10310 /* Directory entry contains a link to a directory */
10311 { PR_2_LINK_DIR,
10312 N_("@E @L to @d %P (%Di).\n"),
10313 PROMPT_CLEAR, 0 },
10314
10315 /* Directory entry contains a link to the root directry */
10316 { PR_2_LINK_ROOT,
10317 N_("@E @L to the @r.\n"),
10318 PROMPT_CLEAR, 0 },
10319
10320 /* Directory entry has illegal characters in its name */
10321 { PR_2_BAD_NAME,
10322 N_("@E has illegal characters in its name.\n"),
10323 PROMPT_FIX, 0 },
10324
10325 /* Missing '.' in directory inode */
10326 { PR_2_MISSING_DOT,
10327 N_("Missing '.' in @d @i %i.\n"),
10328 PROMPT_FIX, 0 },
10329
10330 /* Missing '..' in directory inode */
10331 { PR_2_MISSING_DOT_DOT,
10332 N_("Missing '..' in @d @i %i.\n"),
10333 PROMPT_FIX, 0 },
10334
10335 /* First entry in directory inode doesn't contain '.' */
10336 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010337 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010338 PROMPT_FIX, 0 },
10339
10340 /* Second entry in directory inode doesn't contain '..' */
10341 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010342 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010343 PROMPT_FIX, 0 },
10344
10345 /* i_faddr should be zero */
10346 { PR_2_FADDR_ZERO,
10347 N_("i_faddr @F %IF, @s zero.\n"),
10348 PROMPT_CLEAR, 0 },
10349
10350 /* i_file_acl should be zero */
10351 { PR_2_FILE_ACL_ZERO,
10352 N_("i_file_acl @F %If, @s zero.\n"),
10353 PROMPT_CLEAR, 0 },
10354
10355 /* i_dir_acl should be zero */
10356 { PR_2_DIR_ACL_ZERO,
10357 N_("i_dir_acl @F %Id, @s zero.\n"),
10358 PROMPT_CLEAR, 0 },
10359
10360 /* i_frag should be zero */
10361 { PR_2_FRAG_ZERO,
10362 N_("i_frag @F %N, @s zero.\n"),
10363 PROMPT_CLEAR, 0 },
10364
10365 /* i_fsize should be zero */
10366 { PR_2_FSIZE_ZERO,
10367 N_("i_fsize @F %N, @s zero.\n"),
10368 PROMPT_CLEAR, 0 },
10369
10370 /* inode has bad mode */
10371 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010372 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010373 PROMPT_CLEAR, 0 },
10374
10375 /* directory corrupted */
10376 { PR_2_DIR_CORRUPTED,
10377 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
10378 PROMPT_SALVAGE, 0 },
10379
10380 /* filename too long */
10381 { PR_2_FILENAME_LONG,
10382 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
10383 PROMPT_TRUNCATE, 0 },
10384
10385 /* Directory inode has a missing block (hole) */
10386 { PR_2_DIRECTORY_HOLE,
10387 N_("@d @i %i has an unallocated @b #%B. "),
10388 PROMPT_ALLOCATE, 0 },
10389
10390 /* '.' is not NULL terminated */
10391 { PR_2_DOT_NULL_TERM,
10392 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
10393 PROMPT_FIX, 0 },
10394
10395 /* '..' is not NULL terminated */
10396 { PR_2_DOT_DOT_NULL_TERM,
10397 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
10398 PROMPT_FIX, 0 },
10399
10400 /* Illegal character device inode */
10401 { PR_2_BAD_CHAR_DEV,
10402 N_("@i %i (%Q) is an @I character @v.\n"),
10403 PROMPT_CLEAR, 0 },
10404
10405 /* Illegal block device inode */
10406 { PR_2_BAD_BLOCK_DEV,
10407 N_("@i %i (%Q) is an @I @b @v.\n"),
10408 PROMPT_CLEAR, 0 },
10409
10410 /* Duplicate '.' entry */
10411 { PR_2_DUP_DOT,
10412 N_("@E is duplicate '.' @e.\n"),
10413 PROMPT_FIX, 0 },
10414
10415 /* Duplicate '..' entry */
10416 { PR_2_DUP_DOT_DOT,
10417 N_("@E is duplicate '..' @e.\n"),
10418 PROMPT_FIX, 0 },
10419
10420 /* Internal error: couldn't find dir_info */
10421 { PR_2_NO_DIRINFO,
10422 N_("Internal error: couldn't find dir_info for %i.\n"),
10423 PROMPT_NONE, PR_FATAL },
10424
10425 /* Final rec_len is wrong */
10426 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +000010427 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010428 PROMPT_FIX, 0 },
10429
10430 /* Error allocating icount structure */
10431 { PR_2_ALLOCATE_ICOUNT,
10432 N_("@A icount structure: %m\n"),
10433 PROMPT_NONE, PR_FATAL },
10434
10435 /* Error iterating over directory blocks */
10436 { PR_2_DBLIST_ITERATE,
10437 N_("Error iterating over @d @bs: %m\n"),
10438 PROMPT_NONE, PR_FATAL },
10439
10440 /* Error reading directory block */
10441 { PR_2_READ_DIRBLOCK,
10442 N_("Error reading @d @b %b (@i %i): %m\n"),
10443 PROMPT_CONTINUE, 0 },
10444
10445 /* Error writing directory block */
10446 { PR_2_WRITE_DIRBLOCK,
10447 N_("Error writing @d @b %b (@i %i): %m\n"),
10448 PROMPT_CONTINUE, 0 },
10449
10450 /* Error allocating new directory block */
10451 { PR_2_ALLOC_DIRBOCK,
10452 N_("@A new @d @b for @i %i (%s): %m\n"),
10453 PROMPT_NONE, 0 },
10454
10455 /* Error deallocating inode */
10456 { PR_2_DEALLOC_INODE,
10457 N_("Error deallocating @i %i: %m\n"),
10458 PROMPT_NONE, PR_FATAL },
10459
10460 /* Directory entry for '.' is big. Split? */
10461 { PR_2_SPLIT_DOT,
10462 N_("@d @e for '.' is big. "),
10463 PROMPT_SPLIT, PR_NO_OK },
10464
10465 /* Illegal FIFO inode */
10466 { PR_2_BAD_FIFO,
10467 N_("@i %i (%Q) is an @I FIFO.\n"),
10468 PROMPT_CLEAR, 0 },
10469
10470 /* Illegal socket inode */
10471 { PR_2_BAD_SOCKET,
10472 N_("@i %i (%Q) is an @I socket.\n"),
10473 PROMPT_CLEAR, 0 },
10474
10475 /* Directory filetype not set */
10476 { PR_2_SET_FILETYPE,
10477 N_("Setting filetype for @E to %N.\n"),
10478 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
10479
10480 /* Directory filetype incorrect */
10481 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +000010482 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010483 PROMPT_FIX, 0 },
10484
10485 /* Directory filetype set on filesystem */
10486 { PR_2_CLEAR_FILETYPE,
10487 N_("@E has filetype set.\n"),
10488 PROMPT_CLEAR, PR_PREEN_OK },
10489
10490 /* Directory filename is null */
10491 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +000010492 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010493 PROMPT_CLEAR, 0 },
10494
10495 /* Invalid symlink */
10496 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +000010497 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010498 PROMPT_CLEAR, 0 },
10499
10500 /* i_file_acl (extended attribute block) is bad */
10501 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +000010502 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010503 PROMPT_CLEAR, 0 },
10504
10505 /* Filesystem contains large files, but has no such flag in sb */
10506 { PR_2_FEATURE_LARGE_FILES,
10507 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
10508 PROMPT_FIX, 0 },
10509
10510 /* Node in HTREE directory not referenced */
10511 { PR_2_HTREE_NOTREF,
10512 N_("@p @h %d: node (%B) not referenced\n"),
10513 PROMPT_NONE, 0 },
10514
10515 /* Node in HTREE directory referenced twice */
10516 { PR_2_HTREE_DUPREF,
10517 N_("@p @h %d: node (%B) referenced twice\n"),
10518 PROMPT_NONE, 0 },
10519
10520 /* Node in HTREE directory has bad min hash */
10521 { PR_2_HTREE_MIN_HASH,
10522 N_("@p @h %d: node (%B) has bad min hash\n"),
10523 PROMPT_NONE, 0 },
10524
10525 /* Node in HTREE directory has bad max hash */
10526 { PR_2_HTREE_MAX_HASH,
10527 N_("@p @h %d: node (%B) has bad max hash\n"),
10528 PROMPT_NONE, 0 },
10529
10530 /* Clear invalid HTREE directory */
10531 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +000010532 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010533
10534 /* Bad block in htree interior node */
10535 { PR_2_HTREE_BADBLK,
10536 N_("@p @h %d (%q): bad @b number %b.\n"),
10537 PROMPT_CLEAR_HTREE, 0 },
10538
10539 /* Error adjusting EA refcount */
10540 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010541 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010542 PROMPT_NONE, PR_FATAL },
10543
10544 /* Invalid HTREE root node */
10545 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010546 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010547 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10548
10549 /* Invalid HTREE limit */
10550 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +000010551 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010552 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10553
10554 /* Invalid HTREE count */
10555 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010556 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010557 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10558
10559 /* HTREE interior node has out-of-order hashes in table */
10560 { PR_2_HTREE_HASH_ORDER,
10561 N_("@p @h %d: node (%B) has an unordered hash table\n"),
10562 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10563
Mike Frysinger874af852006-03-08 07:03:27 +000010564 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010565 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +000010566 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010567 PROMPT_NONE, 0 },
10568
10569 /* Duplicate directory entry found */
10570 { PR_2_DUPLICATE_DIRENT,
10571 N_("Duplicate @E found. "),
10572 PROMPT_CLEAR, 0 },
10573
10574 /* Non-unique filename found */
10575 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
10576 N_("@E has a non-unique filename.\nRename to %s"),
10577 PROMPT_NULL, 0 },
10578
10579 /* Duplicate directory entry found */
10580 { PR_2_REPORT_DUP_DIRENT,
10581 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
10582 PROMPT_NONE, 0 },
10583
10584 /* Pass 3 errors */
10585
10586 /* Pass 3: Checking directory connectivity */
10587 { PR_3_PASS_HEADER,
10588 N_("Pass 3: Checking @d connectivity\n"),
10589 PROMPT_NONE, 0 },
10590
10591 /* Root inode not allocated */
10592 { PR_3_NO_ROOT_INODE,
10593 N_("@r not allocated. "),
10594 PROMPT_ALLOCATE, 0 },
10595
10596 /* No room in lost+found */
10597 { PR_3_EXPAND_LF_DIR,
10598 N_("No room in @l @d. "),
10599 PROMPT_EXPAND, 0 },
10600
10601 /* Unconnected directory inode */
10602 { PR_3_UNCONNECTED_DIR,
10603 N_("Unconnected @d @i %i (%p)\n"),
10604 PROMPT_CONNECT, 0 },
10605
10606 /* /lost+found not found */
10607 { PR_3_NO_LF_DIR,
10608 N_("/@l not found. "),
10609 PROMPT_CREATE, PR_PREEN_OK },
10610
10611 /* .. entry is incorrect */
10612 { PR_3_BAD_DOT_DOT,
10613 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
10614 PROMPT_FIX, 0 },
10615
10616 /* Bad or non-existent /lost+found. Cannot reconnect */
10617 { PR_3_NO_LPF,
10618 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
10619 PROMPT_NONE, 0 },
10620
10621 /* Could not expand /lost+found */
10622 { PR_3_CANT_EXPAND_LPF,
10623 N_("Could not expand /@l: %m\n"),
10624 PROMPT_NONE, 0 },
10625
10626 /* Could not reconnect inode */
10627 { PR_3_CANT_RECONNECT,
10628 N_("Could not reconnect %i: %m\n"),
10629 PROMPT_NONE, 0 },
10630
10631 /* Error while trying to find /lost+found */
10632 { PR_3_ERR_FIND_LPF,
10633 N_("Error while trying to find /@l: %m\n"),
10634 PROMPT_NONE, 0 },
10635
10636 /* Error in ext2fs_new_block while creating /lost+found */
10637 { PR_3_ERR_LPF_NEW_BLOCK,
10638 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
10639 PROMPT_NONE, 0 },
10640
10641 /* Error in ext2fs_new_inode while creating /lost+found */
10642 { PR_3_ERR_LPF_NEW_INODE,
10643 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
10644 PROMPT_NONE, 0 },
10645
10646 /* Error in ext2fs_new_dir_block while creating /lost+found */
10647 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
10648 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
10649 PROMPT_NONE, 0 },
10650
10651 /* Error while writing directory block for /lost+found */
10652 { PR_3_ERR_LPF_WRITE_BLOCK,
10653 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
10654 PROMPT_NONE, 0 },
10655
10656 /* Error while adjusting inode count */
10657 { PR_3_ADJUST_INODE,
10658 N_("Error while adjusting @i count on @i %i\n"),
10659 PROMPT_NONE, 0 },
10660
10661 /* Couldn't fix parent directory -- error */
10662 { PR_3_FIX_PARENT_ERR,
10663 N_("Couldn't fix parent of @i %i: %m\n\n"),
10664 PROMPT_NONE, 0 },
10665
10666 /* Couldn't fix parent directory -- couldn't find it */
10667 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +000010668 N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010669 PROMPT_NONE, 0 },
10670
10671 /* Error allocating inode bitmap */
10672 { PR_3_ALLOCATE_IBITMAP_ERROR,
10673 N_("@A @i @B (%N): %m\n"),
10674 PROMPT_NONE, PR_FATAL },
10675
10676 /* Error creating root directory */
10677 { PR_3_CREATE_ROOT_ERROR,
10678 N_("Error creating root @d (%s): %m\n"),
10679 PROMPT_NONE, PR_FATAL },
10680
10681 /* Error creating lost and found directory */
10682 { PR_3_CREATE_LPF_ERROR,
10683 N_("Error creating /@l @d (%s): %m\n"),
10684 PROMPT_NONE, PR_FATAL },
10685
10686 /* Root inode is not directory; aborting */
10687 { PR_3_ROOT_NOT_DIR_ABORT,
10688 N_("@r is not a @d; aborting.\n"),
10689 PROMPT_NONE, PR_FATAL },
10690
10691 /* Cannot proceed without a root inode. */
10692 { PR_3_NO_ROOT_INODE_ABORT,
10693 N_("Cannot proceed without a @r.\n"),
10694 PROMPT_NONE, PR_FATAL },
10695
10696 /* Internal error: couldn't find dir_info */
10697 { PR_3_NO_DIRINFO,
10698 N_("Internal error: couldn't find dir_info for %i.\n"),
10699 PROMPT_NONE, PR_FATAL },
10700
10701 /* Lost+found not a directory */
10702 { PR_3_LPF_NOTDIR,
10703 N_("/@l is not a @d (ino=%i)\n"),
10704 PROMPT_UNLINK, 0 },
10705
10706 /* Pass 3A Directory Optimization */
10707
10708 /* Pass 3A: Optimizing directories */
10709 { PR_3A_PASS_HEADER,
10710 N_("Pass 3A: Optimizing directories\n"),
10711 PROMPT_NONE, PR_PREEN_NOMSG },
10712
10713 /* Error iterating over directories */
10714 { PR_3A_OPTIMIZE_ITER,
10715 N_("Failed to create dirs_to_hash iterator: %m"),
10716 PROMPT_NONE, 0 },
10717
10718 /* Error rehash directory */
10719 { PR_3A_OPTIMIZE_DIR_ERR,
10720 N_("Failed to optimize directory %q (%d): %m"),
10721 PROMPT_NONE, 0 },
10722
10723 /* Rehashing dir header */
10724 { PR_3A_OPTIMIZE_DIR_HEADER,
10725 N_("Optimizing directories: "),
10726 PROMPT_NONE, PR_MSG_ONLY },
10727
10728 /* Rehashing directory %d */
10729 { PR_3A_OPTIMIZE_DIR,
10730 " %d",
10731 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
10732
10733 /* Rehashing dir end */
10734 { PR_3A_OPTIMIZE_DIR_END,
10735 "\n",
10736 PROMPT_NONE, PR_PREEN_NOHDR },
10737
10738 /* Pass 4 errors */
10739
10740 /* Pass 4: Checking reference counts */
10741 { PR_4_PASS_HEADER,
10742 N_("Pass 4: Checking reference counts\n"),
10743 PROMPT_NONE, 0 },
10744
10745 /* Unattached zero-length inode */
10746 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010747 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010748 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
10749
10750 /* Unattached inode */
10751 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010752 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010753 PROMPT_CONNECT, 0 },
10754
10755 /* Inode ref count wrong */
10756 { PR_4_BAD_REF_COUNT,
10757 N_("@i %i ref count is %Il, @s %N. "),
10758 PROMPT_FIX, PR_PREEN_OK },
10759
10760 { PR_4_INCONSISTENT_COUNT,
10761 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
10762 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
10763 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +000010764 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010765 PROMPT_NONE, 0 },
10766
10767 /* Pass 5 errors */
10768
10769 /* Pass 5: Checking group summary information */
10770 { PR_5_PASS_HEADER,
10771 N_("Pass 5: Checking @g summary information\n"),
10772 PROMPT_NONE, 0 },
10773
10774 /* Padding at end of inode bitmap is not set. */
10775 { PR_5_INODE_BMAP_PADDING,
10776 N_("Padding at end of @i @B is not set. "),
10777 PROMPT_FIX, PR_PREEN_OK },
10778
10779 /* Padding at end of block bitmap is not set. */
10780 { PR_5_BLOCK_BMAP_PADDING,
10781 N_("Padding at end of @b @B is not set. "),
10782 PROMPT_FIX, PR_PREEN_OK },
10783
10784 /* Block bitmap differences header */
10785 { PR_5_BLOCK_BITMAP_HEADER,
10786 N_("@b @B differences: "),
10787 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
10788
10789 /* Block not used, but marked in bitmap */
10790 { PR_5_BLOCK_UNUSED,
10791 " -%b",
10792 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10793
10794 /* Block used, but not marked used in bitmap */
10795 { PR_5_BLOCK_USED,
10796 " +%b",
10797 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10798
10799 /* Block bitmap differences end */
10800 { PR_5_BLOCK_BITMAP_END,
10801 "\n",
10802 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10803
10804 /* Inode bitmap differences header */
10805 { PR_5_INODE_BITMAP_HEADER,
10806 N_("@i @B differences: "),
10807 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
10808
10809 /* Inode not used, but marked in bitmap */
10810 { PR_5_INODE_UNUSED,
10811 " -%i",
10812 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10813
10814 /* Inode used, but not marked used in bitmap */
10815 { PR_5_INODE_USED,
10816 " +%i",
10817 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10818
10819 /* Inode bitmap differences end */
10820 { PR_5_INODE_BITMAP_END,
10821 "\n",
10822 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10823
10824 /* Free inodes count for group wrong */
10825 { PR_5_FREE_INODE_COUNT_GROUP,
10826 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
10827 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10828
10829 /* Directories count for group wrong */
10830 { PR_5_FREE_DIR_COUNT_GROUP,
10831 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
10832 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10833
10834 /* Free inodes count wrong */
10835 { PR_5_FREE_INODE_COUNT,
10836 N_("Free @is count wrong (%i, counted=%j).\n"),
10837 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10838
10839 /* Free blocks count for group wrong */
10840 { PR_5_FREE_BLOCK_COUNT_GROUP,
10841 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
10842 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10843
10844 /* Free blocks count wrong */
10845 { PR_5_FREE_BLOCK_COUNT,
10846 N_("Free @bs count wrong (%b, counted=%c).\n"),
10847 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
10848
10849 /* Programming error: bitmap endpoints don't match */
10850 { PR_5_BMAP_ENDPOINTS,
10851 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
10852 "match calculated @B endpoints (%i, %j)\n"),
10853 PROMPT_NONE, PR_FATAL },
10854
10855 /* Internal error: fudging end of bitmap */
10856 { PR_5_FUDGE_BITMAP_ERROR,
10857 N_("Internal error: fudging end of bitmap (%N)\n"),
10858 PROMPT_NONE, PR_FATAL },
10859
10860 /* Error copying in replacement inode bitmap */
10861 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010862 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010863 PROMPT_NONE, PR_FATAL },
10864
10865 /* Error copying in replacement block bitmap */
10866 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010867 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010868 PROMPT_NONE, PR_FATAL },
10869
10870 /* Block range not used, but marked in bitmap */
10871 { PR_5_BLOCK_RANGE_UNUSED,
10872 " -(%b--%c)",
10873 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10874
10875 /* Block range used, but not marked used in bitmap */
10876 { PR_5_BLOCK_RANGE_USED,
10877 " +(%b--%c)",
10878 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10879
10880 /* Inode range not used, but marked in bitmap */
10881 { PR_5_INODE_RANGE_UNUSED,
10882 " -(%i--%j)",
10883 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10884
10885 /* Inode range used, but not marked used in bitmap */
10886 { PR_5_INODE_RANGE_USED,
10887 " +(%i--%j)",
10888 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10889
10890 { 0 }
10891};
10892
10893/*
10894 * This is the latch flags register. It allows several problems to be
10895 * "latched" together. This means that the user has to answer but one
10896 * question for the set of problems, and all of the associated
10897 * problems will be either fixed or not fixed.
10898 */
10899static struct latch_descr pr_latch_info[] = {
10900 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
10901 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
10902 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
10903 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
10904 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
10905 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
10906 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
10907 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
10908 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
10909 { -1, 0, 0 },
10910};
10911
10912static const struct e2fsck_problem *find_problem(problem_t code)
10913{
10914 int i;
10915
10916 for (i=0; problem_table[i].e2p_code; i++) {
10917 if (problem_table[i].e2p_code == code)
10918 return &problem_table[i];
10919 }
10920 return 0;
10921}
10922
10923static struct latch_descr *find_latch(int code)
10924{
10925 int i;
10926
10927 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
10928 if (pr_latch_info[i].latch_code == code)
10929 return &pr_latch_info[i];
10930 }
10931 return 0;
10932}
10933
10934int end_problem_latch(e2fsck_t ctx, int mask)
10935{
10936 struct latch_descr *ldesc;
10937 struct problem_context pctx;
10938 int answer = -1;
10939
10940 ldesc = find_latch(mask);
10941 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
10942 clear_problem_context(&pctx);
10943 answer = fix_problem(ctx, ldesc->end_message, &pctx);
10944 }
10945 ldesc->flags &= ~(PRL_VARIABLE);
10946 return answer;
10947}
10948
10949int set_latch_flags(int mask, int setflags, int clearflags)
10950{
10951 struct latch_descr *ldesc;
10952
10953 ldesc = find_latch(mask);
10954 if (!ldesc)
10955 return -1;
10956 ldesc->flags |= setflags;
10957 ldesc->flags &= ~clearflags;
10958 return 0;
10959}
10960
10961void clear_problem_context(struct problem_context *ctx)
10962{
10963 memset(ctx, 0, sizeof(struct problem_context));
10964 ctx->blkcount = -1;
10965 ctx->group = -1;
10966}
10967
10968int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
10969{
10970 ext2_filsys fs = ctx->fs;
10971 const struct e2fsck_problem *ptr;
10972 struct latch_descr *ldesc = 0;
10973 const char *message;
10974 int def_yn, answer, ans;
10975 int print_answer = 0;
10976 int suppress = 0;
10977
10978 ptr = find_problem(code);
10979 if (!ptr) {
10980 printf(_("Unhandled error code (0x%x)!\n"), code);
10981 return 0;
10982 }
10983 def_yn = 1;
10984 if ((ptr->flags & PR_NO_DEFAULT) ||
10985 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
10986 (ctx->options & E2F_OPT_NO))
10987 def_yn= 0;
10988
10989 /*
10990 * Do special latch processing. This is where we ask the
10991 * latch question, if it exists
10992 */
10993 if (ptr->flags & PR_LATCH_MASK) {
10994 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
10995 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
10996 ans = fix_problem(ctx, ldesc->question, pctx);
10997 if (ans == 1)
10998 ldesc->flags |= PRL_YES;
10999 if (ans == 0)
11000 ldesc->flags |= PRL_NO;
11001 ldesc->flags |= PRL_LATCHED;
11002 }
11003 if (ldesc->flags & PRL_SUPPRESS)
11004 suppress++;
11005 }
11006 if ((ptr->flags & PR_PREEN_NOMSG) &&
11007 (ctx->options & E2F_OPT_PREEN))
11008 suppress++;
11009 if ((ptr->flags & PR_NO_NOMSG) &&
11010 (ctx->options & E2F_OPT_NO))
11011 suppress++;
11012 if (!suppress) {
11013 message = ptr->e2p_description;
11014 if ((ctx->options & E2F_OPT_PREEN) &&
11015 !(ptr->flags & PR_PREEN_NOHDR)) {
11016 printf("%s: ", ctx->device_name ?
11017 ctx->device_name : ctx->filesystem_name);
11018 }
11019 if (*message)
11020 print_e2fsck_message(ctx, _(message), pctx, 1);
11021 }
11022 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
11023 preenhalt(ctx);
11024
11025 if (ptr->flags & PR_FATAL)
11026 fatal_error(ctx, 0);
11027
11028 if (ptr->prompt == PROMPT_NONE) {
11029 if (ptr->flags & PR_NOCOLLATE)
11030 answer = -1;
11031 else
11032 answer = def_yn;
11033 } else {
11034 if (ctx->options & E2F_OPT_PREEN) {
11035 answer = def_yn;
11036 if (!(ptr->flags & PR_PREEN_NOMSG))
11037 print_answer = 1;
11038 } else if ((ptr->flags & PR_LATCH_MASK) &&
11039 (ldesc->flags & (PRL_YES | PRL_NO))) {
11040 if (!suppress)
11041 print_answer = 1;
11042 if (ldesc->flags & PRL_YES)
11043 answer = 1;
11044 else
11045 answer = 0;
11046 } else
11047 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
11048 if (!answer && !(ptr->flags & PR_NO_OK))
11049 ext2fs_unmark_valid(fs);
11050
11051 if (print_answer)
11052 printf("%s.\n", answer ?
11053 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
11054
11055 }
11056
11057 if ((ptr->prompt == PROMPT_ABORT) && answer)
11058 fatal_error(ctx, 0);
11059
11060 if (ptr->flags & PR_AFTER_CODE)
11061 answer = fix_problem(ctx, ptr->second_code, pctx);
11062
11063 return answer;
11064}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011065
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011066/*
11067 * linux/fs/recovery.c
11068 *
11069 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011070 */
11071
11072/*
11073 * Maintain information about the progress of the recovery job, so that
11074 * the different passes can carry information between them.
11075 */
11076struct recovery_info
11077{
11078 tid_t start_transaction;
11079 tid_t end_transaction;
11080
11081 int nr_replays;
11082 int nr_revokes;
11083 int nr_revoke_hits;
11084};
11085
11086enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
11087static int do_one_pass(journal_t *journal,
11088 struct recovery_info *info, enum passtype pass);
11089static int scan_revoke_records(journal_t *, struct buffer_head *,
11090 tid_t, struct recovery_info *);
11091
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011092/*
11093 * Read a block from the journal
11094 */
11095
11096static int jread(struct buffer_head **bhp, journal_t *journal,
11097 unsigned int offset)
11098{
11099 int err;
11100 unsigned long blocknr;
11101 struct buffer_head *bh;
11102
11103 *bhp = NULL;
11104
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011105 err = journal_bmap(journal, offset, &blocknr);
11106
11107 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +000011108 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011109 return err;
11110 }
11111
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011112 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011113 if (!bh)
11114 return -ENOMEM;
11115
11116 if (!buffer_uptodate(bh)) {
11117 /* If this is a brand new buffer, start readahead.
11118 Otherwise, we assume we are already reading it. */
11119 if (!buffer_req(bh))
11120 do_readahead(journal, offset);
11121 wait_on_buffer(bh);
11122 }
11123
11124 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +000011125 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011126 brelse(bh);
11127 return -EIO;
11128 }
11129
11130 *bhp = bh;
11131 return 0;
11132}
11133
11134
11135/*
11136 * Count the number of in-use tags in a journal descriptor block.
11137 */
11138
11139static int count_tags(struct buffer_head *bh, int size)
11140{
11141 char * tagp;
11142 journal_block_tag_t * tag;
11143 int nr = 0;
11144
11145 tagp = &bh->b_data[sizeof(journal_header_t)];
11146
11147 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
11148 tag = (journal_block_tag_t *) tagp;
11149
11150 nr++;
11151 tagp += sizeof(journal_block_tag_t);
11152 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
11153 tagp += 16;
11154
11155 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
11156 break;
11157 }
11158
11159 return nr;
11160}
11161
11162
11163/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000011164#define wrap(journal, var) \
11165do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011166 if (var >= (journal)->j_last) \
11167 var -= ((journal)->j_last - (journal)->j_first); \
11168} while (0)
11169
11170/**
11171 * int journal_recover(journal_t *journal) - recovers a on-disk journal
11172 * @journal: the journal to recover
11173 *
11174 * The primary function for recovering the log contents when mounting a
11175 * journaled device.
11176 *
11177 * Recovery is done in three passes. In the first pass, we look for the
11178 * end of the log. In the second, we assemble the list of revoke
11179 * blocks. In the third and final pass, we replay any un-revoked blocks
11180 * in the log.
11181 */
11182int journal_recover(journal_t *journal)
11183{
11184 int err;
11185 journal_superblock_t * sb;
11186
11187 struct recovery_info info;
11188
11189 memset(&info, 0, sizeof(info));
11190 sb = journal->j_superblock;
11191
11192 /*
11193 * The journal superblock's s_start field (the current log head)
11194 * is always zero if, and only if, the journal was cleanly
11195 * unmounted.
11196 */
11197
11198 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011199 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
11200 return 0;
11201 }
11202
11203 err = do_one_pass(journal, &info, PASS_SCAN);
11204 if (!err)
11205 err = do_one_pass(journal, &info, PASS_REVOKE);
11206 if (!err)
11207 err = do_one_pass(journal, &info, PASS_REPLAY);
11208
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011209 /* Restart the log at the next transaction ID, thus invalidating
11210 * any existing commit records in the log. */
11211 journal->j_transaction_sequence = ++info.end_transaction;
11212
11213 journal_clear_revoke(journal);
11214 sync_blockdev(journal->j_fs_dev);
11215 return err;
11216}
11217
11218static int do_one_pass(journal_t *journal,
11219 struct recovery_info *info, enum passtype pass)
11220{
11221 unsigned int first_commit_ID, next_commit_ID;
11222 unsigned long next_log_block;
11223 int err, success = 0;
11224 journal_superblock_t * sb;
11225 journal_header_t * tmp;
11226 struct buffer_head * bh;
11227 unsigned int sequence;
11228 int blocktype;
11229
11230 /* Precompute the maximum metadata descriptors in a descriptor block */
11231 int MAX_BLOCKS_PER_DESC;
11232 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
11233 / sizeof(journal_block_tag_t));
11234
11235 /*
11236 * First thing is to establish what we expect to find in the log
11237 * (in terms of transaction IDs), and where (in terms of log
11238 * block offsets): query the superblock.
11239 */
11240
11241 sb = journal->j_superblock;
11242 next_commit_ID = ntohl(sb->s_sequence);
11243 next_log_block = ntohl(sb->s_start);
11244
11245 first_commit_ID = next_commit_ID;
11246 if (pass == PASS_SCAN)
11247 info->start_transaction = first_commit_ID;
11248
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011249 /*
11250 * Now we walk through the log, transaction by transaction,
11251 * making sure that each transaction has a commit block in the
11252 * expected place. Each complete transaction gets replayed back
11253 * into the main filesystem.
11254 */
11255
11256 while (1) {
11257 int flags;
11258 char * tagp;
11259 journal_block_tag_t * tag;
11260 struct buffer_head * obh;
11261 struct buffer_head * nbh;
11262
11263 /* If we already know where to stop the log traversal,
11264 * check right now that we haven't gone past the end of
11265 * the log. */
11266
11267 if (pass != PASS_SCAN)
11268 if (tid_geq(next_commit_ID, info->end_transaction))
11269 break;
11270
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011271 /* Skip over each chunk of the transaction looking
11272 * either the next descriptor block or the final commit
11273 * record. */
11274
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011275 err = jread(&bh, journal, next_log_block);
11276 if (err)
11277 goto failed;
11278
11279 next_log_block++;
11280 wrap(journal, next_log_block);
11281
11282 /* What kind of buffer is it?
11283 *
11284 * If it is a descriptor block, check that it has the
11285 * expected sequence number. Otherwise, we're all done
11286 * here. */
11287
11288 tmp = (journal_header_t *)bh->b_data;
11289
11290 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
11291 brelse(bh);
11292 break;
11293 }
11294
11295 blocktype = ntohl(tmp->h_blocktype);
11296 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011297
11298 if (sequence != next_commit_ID) {
11299 brelse(bh);
11300 break;
11301 }
11302
11303 /* OK, we have a valid descriptor block which matches
11304 * all of the sequence number checks. What are we going
11305 * to do with it? That depends on the pass... */
11306
11307 switch(blocktype) {
11308 case JFS_DESCRIPTOR_BLOCK:
11309 /* If it is a valid descriptor block, replay it
11310 * in pass REPLAY; otherwise, just skip over the
11311 * blocks it describes. */
11312 if (pass != PASS_REPLAY) {
11313 next_log_block +=
11314 count_tags(bh, journal->j_blocksize);
11315 wrap(journal, next_log_block);
11316 brelse(bh);
11317 continue;
11318 }
11319
11320 /* A descriptor block: we can now write all of
11321 * the data blocks. Yay, useful work is finally
11322 * getting done here! */
11323
11324 tagp = &bh->b_data[sizeof(journal_header_t)];
11325 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
11326 <= journal->j_blocksize) {
11327 unsigned long io_block;
11328
11329 tag = (journal_block_tag_t *) tagp;
11330 flags = ntohl(tag->t_flags);
11331
11332 io_block = next_log_block++;
11333 wrap(journal, next_log_block);
11334 err = jread(&obh, journal, io_block);
11335 if (err) {
11336 /* Recover what we can, but
11337 * report failure at the end. */
11338 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000011339 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011340 "block %ld in log\n",
11341 err, io_block);
11342 } else {
11343 unsigned long blocknr;
11344
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011345 blocknr = ntohl(tag->t_blocknr);
11346
11347 /* If the block has been
11348 * revoked, then we're all done
11349 * here. */
11350 if (journal_test_revoke
11351 (journal, blocknr,
11352 next_commit_ID)) {
11353 brelse(obh);
11354 ++info->nr_revoke_hits;
11355 goto skip_write;
11356 }
11357
11358 /* Find a buffer for the new
11359 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011360 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011361 blocknr,
11362 journal->j_blocksize);
11363 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000011364 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011365 "during recovery.\n");
11366 err = -ENOMEM;
11367 brelse(bh);
11368 brelse(obh);
11369 goto failed;
11370 }
11371
11372 lock_buffer(nbh);
11373 memcpy(nbh->b_data, obh->b_data,
11374 journal->j_blocksize);
11375 if (flags & JFS_FLAG_ESCAPE) {
11376 *((unsigned int *)bh->b_data) =
11377 htonl(JFS_MAGIC_NUMBER);
11378 }
11379
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011380 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011381 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011382 ++info->nr_replays;
11383 /* ll_rw_block(WRITE, 1, &nbh); */
11384 unlock_buffer(nbh);
11385 brelse(obh);
11386 brelse(nbh);
11387 }
11388
11389 skip_write:
11390 tagp += sizeof(journal_block_tag_t);
11391 if (!(flags & JFS_FLAG_SAME_UUID))
11392 tagp += 16;
11393
11394 if (flags & JFS_FLAG_LAST_TAG)
11395 break;
11396 }
11397
11398 brelse(bh);
11399 continue;
11400
11401 case JFS_COMMIT_BLOCK:
11402 /* Found an expected commit block: not much to
11403 * do other than move on to the next sequence
11404 * number. */
11405 brelse(bh);
11406 next_commit_ID++;
11407 continue;
11408
11409 case JFS_REVOKE_BLOCK:
11410 /* If we aren't in the REVOKE pass, then we can
11411 * just skip over this block. */
11412 if (pass != PASS_REVOKE) {
11413 brelse(bh);
11414 continue;
11415 }
11416
11417 err = scan_revoke_records(journal, bh,
11418 next_commit_ID, info);
11419 brelse(bh);
11420 if (err)
11421 goto failed;
11422 continue;
11423
11424 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011425 goto done;
11426 }
11427 }
11428
11429 done:
11430 /*
11431 * We broke out of the log scan loop: either we came to the
11432 * known end of the log or we found an unexpected block in the
11433 * log. If the latter happened, then we know that the "current"
11434 * transaction marks the end of the valid log.
11435 */
11436
11437 if (pass == PASS_SCAN)
11438 info->end_transaction = next_commit_ID;
11439 else {
11440 /* It's really bad news if different passes end up at
11441 * different places (but possible due to IO errors). */
11442 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000011443 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011444 "transaction %u, expected %u\n",
11445 pass, next_commit_ID, info->end_transaction);
11446 if (!success)
11447 success = -EIO;
11448 }
11449 }
11450
11451 return success;
11452
11453 failed:
11454 return err;
11455}
11456
11457
11458/* Scan a revoke record, marking all blocks mentioned as revoked. */
11459
11460static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
11461 tid_t sequence, struct recovery_info *info)
11462{
11463 journal_revoke_header_t *header;
11464 int offset, max;
11465
11466 header = (journal_revoke_header_t *) bh->b_data;
11467 offset = sizeof(journal_revoke_header_t);
11468 max = ntohl(header->r_count);
11469
11470 while (offset < max) {
11471 unsigned long blocknr;
11472 int err;
11473
11474 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
11475 offset += 4;
11476 err = journal_set_revoke(journal, blocknr, sequence);
11477 if (err)
11478 return err;
11479 ++info->nr_revokes;
11480 }
11481 return 0;
11482}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011483
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011484
11485/*
11486 * rehash.c --- rebuild hash tree directories
11487 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011488 * This algorithm is designed for simplicity of implementation and to
11489 * pack the directory as much as possible. It however requires twice
11490 * as much memory as the size of the directory. The maximum size
11491 * directory supported using a 4k blocksize is roughly a gigabyte, and
11492 * so there may very well be problems with machines that don't have
11493 * virtual memory, and obscenely large directories.
11494 *
11495 * An alternate algorithm which is much more disk intensive could be
11496 * written, and probably will need to be written in the future. The
11497 * design goals of such an algorithm are: (a) use (roughly) constant
11498 * amounts of memory, no matter how large the directory, (b) the
11499 * directory must be safe at all times, even if e2fsck is interrupted
11500 * in the middle, (c) we must use minimal amounts of extra disk
11501 * blocks. This pretty much requires an incremental approach, where
11502 * we are reading from one part of the directory, and inserting into
11503 * the front half. So the algorithm will have to keep track of a
11504 * moving block boundary between the new tree and the old tree, and
11505 * files will need to be moved from the old directory and inserted
11506 * into the new tree. If the new directory requires space which isn't
11507 * yet available, blocks from the beginning part of the old directory
11508 * may need to be moved to the end of the directory to make room for
11509 * the new tree:
11510 *
11511 * --------------------------------------------------------
11512 * | new tree | | old tree |
11513 * --------------------------------------------------------
11514 * ^ ptr ^ptr
11515 * tail new head old
11516 *
11517 * This is going to be a pain in the tuckus to implement, and will
11518 * require a lot more disk accesses. So I'm going to skip it for now;
11519 * it's only really going to be an issue for really, really big
11520 * filesystems (when we reach the level of tens of millions of files
11521 * in a single directory). It will probably be easier to simply
11522 * require that e2fsck use VM first.
11523 */
11524
11525struct fill_dir_struct {
11526 char *buf;
11527 struct ext2_inode *inode;
11528 int err;
11529 e2fsck_t ctx;
11530 struct hash_entry *harray;
11531 int max_array, num_array;
11532 int dir_size;
11533 int compress;
11534 ino_t parent;
11535};
11536
11537struct hash_entry {
11538 ext2_dirhash_t hash;
11539 ext2_dirhash_t minor_hash;
11540 struct ext2_dir_entry *dir;
11541};
11542
11543struct out_dir {
11544 int num;
11545 int max;
11546 char *buf;
11547 ext2_dirhash_t *hashes;
11548};
11549
11550static int fill_dir_block(ext2_filsys fs,
11551 blk_t *block_nr,
11552 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011553 blk_t ref_block FSCK_ATTR((unused)),
11554 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011555 void *priv_data)
11556{
11557 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
11558 struct hash_entry *new_array, *ent;
11559 struct ext2_dir_entry *dirent;
11560 char *dir;
11561 unsigned int offset, dir_offset;
11562
11563 if (blockcnt < 0)
11564 return 0;
11565
11566 offset = blockcnt * fs->blocksize;
11567 if (offset + fs->blocksize > fd->inode->i_size) {
11568 fd->err = EXT2_ET_DIR_CORRUPTED;
11569 return BLOCK_ABORT;
11570 }
11571 dir = (fd->buf+offset);
11572 if (HOLE_BLKADDR(*block_nr)) {
11573 memset(dir, 0, fs->blocksize);
11574 dirent = (struct ext2_dir_entry *) dir;
11575 dirent->rec_len = fs->blocksize;
11576 } else {
11577 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
11578 if (fd->err)
11579 return BLOCK_ABORT;
11580 }
11581 /* While the directory block is "hot", index it. */
11582 dir_offset = 0;
11583 while (dir_offset < fs->blocksize) {
11584 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
11585 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
11586 (dirent->rec_len < 8) ||
11587 ((dirent->rec_len % 4) != 0) ||
11588 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
11589 fd->err = EXT2_ET_DIR_CORRUPTED;
11590 return BLOCK_ABORT;
11591 }
11592 dir_offset += dirent->rec_len;
11593 if (dirent->inode == 0)
11594 continue;
11595 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
11596 (dirent->name[0] == '.'))
11597 continue;
11598 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
11599 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
11600 fd->parent = dirent->inode;
11601 continue;
11602 }
11603 if (fd->num_array >= fd->max_array) {
11604 new_array = realloc(fd->harray,
11605 sizeof(struct hash_entry) * (fd->max_array+500));
11606 if (!new_array) {
11607 fd->err = ENOMEM;
11608 return BLOCK_ABORT;
11609 }
11610 fd->harray = new_array;
11611 fd->max_array += 500;
11612 }
11613 ent = fd->harray + fd->num_array++;
11614 ent->dir = dirent;
11615 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
11616 if (fd->compress)
11617 ent->hash = ent->minor_hash = 0;
11618 else {
11619 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
11620 dirent->name,
11621 dirent->name_len & 0xFF,
11622 fs->super->s_hash_seed,
11623 &ent->hash, &ent->minor_hash);
11624 if (fd->err)
11625 return BLOCK_ABORT;
11626 }
11627 }
11628
11629 return 0;
11630}
11631
11632/* Used for sorting the hash entry */
11633static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b)
11634{
11635 const struct hash_entry *he_a = (const struct hash_entry *) a;
11636 const struct hash_entry *he_b = (const struct hash_entry *) b;
11637 int ret;
11638 int min_len;
11639
11640 min_len = he_a->dir->name_len;
11641 if (min_len > he_b->dir->name_len)
11642 min_len = he_b->dir->name_len;
11643
11644 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
11645 if (ret == 0) {
11646 if (he_a->dir->name_len > he_b->dir->name_len)
11647 ret = 1;
11648 else if (he_a->dir->name_len < he_b->dir->name_len)
11649 ret = -1;
11650 else
11651 ret = he_b->dir->inode - he_a->dir->inode;
11652 }
11653 return ret;
11654}
11655
11656/* Used for sorting the hash entry */
11657static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b)
11658{
11659 const struct hash_entry *he_a = (const struct hash_entry *) a;
11660 const struct hash_entry *he_b = (const struct hash_entry *) b;
11661 int ret;
11662
11663 if (he_a->hash > he_b->hash)
11664 ret = 1;
11665 else if (he_a->hash < he_b->hash)
11666 ret = -1;
11667 else {
11668 if (he_a->minor_hash > he_b->minor_hash)
11669 ret = 1;
11670 else if (he_a->minor_hash < he_b->minor_hash)
11671 ret = -1;
11672 else
11673 ret = name_cmp(a, b);
11674 }
11675 return ret;
11676}
11677
11678static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
11679 int blocks)
11680{
11681 void *new_mem;
11682
11683 if (outdir->max) {
11684 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
11685 if (!new_mem)
11686 return ENOMEM;
11687 outdir->buf = new_mem;
11688 new_mem = realloc(outdir->hashes,
11689 blocks * sizeof(ext2_dirhash_t));
11690 if (!new_mem)
11691 return ENOMEM;
11692 outdir->hashes = new_mem;
11693 } else {
11694 outdir->buf = malloc(blocks * fs->blocksize);
11695 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
11696 outdir->num = 0;
11697 }
11698 outdir->max = blocks;
11699 return 0;
11700}
11701
11702static void free_out_dir(struct out_dir *outdir)
11703{
Rob Landleye7c43b62006-03-01 16:39:45 +000011704 free(outdir->buf);
11705 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011706 outdir->max = 0;
11707 outdir->num =0;
11708}
11709
11710static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
11711 char ** ret)
11712{
11713 errcode_t retval;
11714
11715 if (outdir->num >= outdir->max) {
11716 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
11717 if (retval)
11718 return retval;
11719 }
11720 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
11721 memset(*ret, 0, fs->blocksize);
11722 return 0;
11723}
11724
11725/*
11726 * This function is used to make a unique filename. We do this by
11727 * appending ~0, and then incrementing the number. However, we cannot
11728 * expand the length of the filename beyond the padding available in
11729 * the directory entry.
11730 */
11731static void mutate_name(char *str, __u16 *len)
11732{
11733 int i;
11734 __u16 l = *len & 0xFF, h = *len & 0xff00;
11735
11736 /*
11737 * First check to see if it looks the name has been mutated
11738 * already
11739 */
11740 for (i = l-1; i > 0; i--) {
11741 if (!isdigit(str[i]))
11742 break;
11743 }
11744 if ((i == l-1) || (str[i] != '~')) {
11745 if (((l-1) & 3) < 2)
11746 l += 2;
11747 else
11748 l = (l+3) & ~3;
11749 str[l-2] = '~';
11750 str[l-1] = '0';
11751 *len = l | h;
11752 return;
11753 }
11754 for (i = l-1; i >= 0; i--) {
11755 if (isdigit(str[i])) {
11756 if (str[i] == '9')
11757 str[i] = '0';
11758 else {
11759 str[i]++;
11760 return;
11761 }
11762 continue;
11763 }
11764 if (i == 1) {
11765 if (str[0] == 'z')
11766 str[0] = 'A';
11767 else if (str[0] == 'Z') {
11768 str[0] = '~';
11769 str[1] = '0';
11770 } else
11771 str[0]++;
11772 } else if (i > 0) {
11773 str[i] = '1';
11774 str[i-1] = '~';
11775 } else {
11776 if (str[0] == '~')
11777 str[0] = 'a';
11778 else
11779 str[0]++;
11780 }
11781 break;
11782 }
11783}
11784
11785static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
11786 ext2_ino_t ino,
11787 struct fill_dir_struct *fd)
11788{
11789 struct problem_context pctx;
11790 struct hash_entry *ent, *prev;
11791 int i, j;
11792 int fixed = 0;
11793 char new_name[256];
11794 __u16 new_len;
11795
11796 clear_problem_context(&pctx);
11797 pctx.ino = ino;
11798
11799 for (i=1; i < fd->num_array; i++) {
11800 ent = fd->harray + i;
11801 prev = ent - 1;
11802 if (!ent->dir->inode ||
11803 ((ent->dir->name_len & 0xFF) !=
11804 (prev->dir->name_len & 0xFF)) ||
11805 (strncmp(ent->dir->name, prev->dir->name,
11806 ent->dir->name_len & 0xFF)))
11807 continue;
11808 pctx.dirent = ent->dir;
11809 if ((ent->dir->inode == prev->dir->inode) &&
11810 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
11811 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
11812 ent->dir->inode = 0;
11813 fixed++;
11814 continue;
11815 }
11816 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
11817 new_len = ent->dir->name_len;
11818 mutate_name(new_name, &new_len);
11819 for (j=0; j < fd->num_array; j++) {
11820 if ((i==j) ||
11821 ((ent->dir->name_len & 0xFF) !=
11822 (fd->harray[j].dir->name_len & 0xFF)) ||
11823 (strncmp(new_name, fd->harray[j].dir->name,
11824 new_len & 0xFF)))
11825 continue;
11826 mutate_name(new_name, &new_len);
11827
11828 j = -1;
11829 }
11830 new_name[new_len & 0xFF] = 0;
11831 pctx.str = new_name;
11832 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
11833 memcpy(ent->dir->name, new_name, new_len & 0xFF);
11834 ent->dir->name_len = new_len;
11835 ext2fs_dirhash(fs->super->s_def_hash_version,
11836 ent->dir->name,
11837 ent->dir->name_len & 0xFF,
11838 fs->super->s_hash_seed,
11839 &ent->hash, &ent->minor_hash);
11840 fixed++;
11841 }
11842 }
11843 return fixed;
11844}
11845
11846
11847static errcode_t copy_dir_entries(ext2_filsys fs,
11848 struct fill_dir_struct *fd,
11849 struct out_dir *outdir)
11850{
11851 errcode_t retval;
11852 char *block_start;
11853 struct hash_entry *ent;
11854 struct ext2_dir_entry *dirent;
11855 int i, rec_len, left;
11856 ext2_dirhash_t prev_hash;
11857 int offset;
11858
11859 outdir->max = 0;
11860 retval = alloc_size_dir(fs, outdir,
11861 (fd->dir_size / fs->blocksize) + 2);
11862 if (retval)
11863 return retval;
11864 outdir->num = fd->compress ? 0 : 1;
11865 offset = 0;
11866 outdir->hashes[0] = 0;
11867 prev_hash = 1;
11868 if ((retval = get_next_block(fs, outdir, &block_start)))
11869 return retval;
11870 dirent = (struct ext2_dir_entry *) block_start;
11871 left = fs->blocksize;
11872 for (i=0; i < fd->num_array; i++) {
11873 ent = fd->harray + i;
11874 if (ent->dir->inode == 0)
11875 continue;
11876 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
11877 if (rec_len > left) {
11878 if (left)
11879 dirent->rec_len += left;
11880 if ((retval = get_next_block(fs, outdir,
11881 &block_start)))
11882 return retval;
11883 offset = 0;
11884 }
11885 left = fs->blocksize - offset;
11886 dirent = (struct ext2_dir_entry *) (block_start + offset);
11887 if (offset == 0) {
11888 if (ent->hash == prev_hash)
11889 outdir->hashes[outdir->num-1] = ent->hash | 1;
11890 else
11891 outdir->hashes[outdir->num-1] = ent->hash;
11892 }
11893 dirent->inode = ent->dir->inode;
11894 dirent->name_len = ent->dir->name_len;
11895 dirent->rec_len = rec_len;
11896 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
11897 offset += rec_len;
11898 left -= rec_len;
11899 if (left < 12) {
11900 dirent->rec_len += left;
11901 offset += left;
11902 left = 0;
11903 }
11904 prev_hash = ent->hash;
11905 }
11906 if (left)
11907 dirent->rec_len += left;
11908
11909 return 0;
11910}
11911
11912
11913static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
11914 ext2_ino_t ino, ext2_ino_t parent)
11915{
11916 struct ext2_dir_entry *dir;
11917 struct ext2_dx_root_info *root;
11918 struct ext2_dx_countlimit *limits;
11919 int filetype = 0;
11920
11921 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
11922 filetype = EXT2_FT_DIR << 8;
11923
11924 memset(buf, 0, fs->blocksize);
11925 dir = (struct ext2_dir_entry *) buf;
11926 dir->inode = ino;
11927 dir->name[0] = '.';
11928 dir->name_len = 1 | filetype;
11929 dir->rec_len = 12;
11930 dir = (struct ext2_dir_entry *) (buf + 12);
11931 dir->inode = parent;
11932 dir->name[0] = '.';
11933 dir->name[1] = '.';
11934 dir->name_len = 2 | filetype;
11935 dir->rec_len = fs->blocksize - 12;
11936
11937 root = (struct ext2_dx_root_info *) (buf+24);
11938 root->reserved_zero = 0;
11939 root->hash_version = fs->super->s_def_hash_version;
11940 root->info_length = 8;
11941 root->indirect_levels = 0;
11942 root->unused_flags = 0;
11943
11944 limits = (struct ext2_dx_countlimit *) (buf+32);
11945 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
11946 limits->count = 0;
11947
11948 return root;
11949}
11950
11951
11952static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
11953{
11954 struct ext2_dir_entry *dir;
11955 struct ext2_dx_countlimit *limits;
11956
11957 memset(buf, 0, fs->blocksize);
11958 dir = (struct ext2_dir_entry *) buf;
11959 dir->inode = 0;
11960 dir->rec_len = fs->blocksize;
11961
11962 limits = (struct ext2_dx_countlimit *) (buf+8);
11963 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
11964 limits->count = 0;
11965
11966 return (struct ext2_dx_entry *) limits;
11967}
11968
11969/*
11970 * This function takes the leaf nodes which have been written in
11971 * outdir, and populates the root node and any necessary interior nodes.
11972 */
11973static errcode_t calculate_tree(ext2_filsys fs,
11974 struct out_dir *outdir,
11975 ext2_ino_t ino,
11976 ext2_ino_t parent)
11977{
11978 struct ext2_dx_root_info *root_info;
11979 struct ext2_dx_entry *root, *dx_ent = 0;
11980 struct ext2_dx_countlimit *root_limit, *limit;
11981 errcode_t retval;
11982 char * block_start;
11983 int i, c1, c2, nblks;
11984 int limit_offset, root_offset;
11985
11986 root_info = set_root_node(fs, outdir->buf, ino, parent);
11987 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
11988 root_info->info_length;
11989 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
11990 c1 = root_limit->limit;
11991 nblks = outdir->num;
11992
11993 /* Write out the pointer blocks */
11994 if (nblks-1 <= c1) {
11995 /* Just write out the root block, and we're done */
11996 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
11997 for (i=1; i < nblks; i++) {
11998 root->block = ext2fs_cpu_to_le32(i);
11999 if (i != 1)
12000 root->hash =
12001 ext2fs_cpu_to_le32(outdir->hashes[i]);
12002 root++;
12003 c1--;
12004 }
12005 } else {
12006 c2 = 0;
12007 limit = 0;
12008 root_info->indirect_levels = 1;
12009 for (i=1; i < nblks; i++) {
12010 if (c1 == 0)
12011 return ENOSPC;
12012 if (c2 == 0) {
12013 if (limit)
12014 limit->limit = limit->count =
12015 ext2fs_cpu_to_le16(limit->limit);
12016 root = (struct ext2_dx_entry *)
12017 (outdir->buf + root_offset);
12018 root->block = ext2fs_cpu_to_le32(outdir->num);
12019 if (i != 1)
12020 root->hash =
12021 ext2fs_cpu_to_le32(outdir->hashes[i]);
12022 if ((retval = get_next_block(fs, outdir,
12023 &block_start)))
12024 return retval;
12025 dx_ent = set_int_node(fs, block_start);
12026 limit = (struct ext2_dx_countlimit *) dx_ent;
12027 c2 = limit->limit;
12028 root_offset += sizeof(struct ext2_dx_entry);
12029 c1--;
12030 }
12031 dx_ent->block = ext2fs_cpu_to_le32(i);
12032 if (c2 != limit->limit)
12033 dx_ent->hash =
12034 ext2fs_cpu_to_le32(outdir->hashes[i]);
12035 dx_ent++;
12036 c2--;
12037 }
12038 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
12039 limit->limit = ext2fs_cpu_to_le16(limit->limit);
12040 }
12041 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
12042 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
12043 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
12044
12045 return 0;
12046}
12047
12048struct write_dir_struct {
12049 struct out_dir *outdir;
12050 errcode_t err;
12051 e2fsck_t ctx;
12052 int cleared;
12053};
12054
12055/*
12056 * Helper function which writes out a directory block.
12057 */
12058static int write_dir_block(ext2_filsys fs,
12059 blk_t *block_nr,
12060 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012061 blk_t ref_block FSCK_ATTR((unused)),
12062 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012063 void *priv_data)
12064{
12065 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
12066 blk_t blk;
12067 char *dir;
12068
12069 if (*block_nr == 0)
12070 return 0;
12071 if (blockcnt >= wd->outdir->num) {
12072 e2fsck_read_bitmaps(wd->ctx);
12073 blk = *block_nr;
12074 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
12075 ext2fs_block_alloc_stats(fs, blk, -1);
12076 *block_nr = 0;
12077 wd->cleared++;
12078 return BLOCK_CHANGED;
12079 }
12080 if (blockcnt < 0)
12081 return 0;
12082
12083 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
12084 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
12085 if (wd->err)
12086 return BLOCK_ABORT;
12087 return 0;
12088}
12089
12090static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
12091 struct out_dir *outdir,
12092 ext2_ino_t ino, int compress)
12093{
12094 struct write_dir_struct wd;
12095 errcode_t retval;
12096 struct ext2_inode inode;
12097
12098 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
12099 if (retval)
12100 return retval;
12101
12102 wd.outdir = outdir;
12103 wd.err = 0;
12104 wd.ctx = ctx;
12105 wd.cleared = 0;
12106
12107 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
12108 write_dir_block, &wd);
12109 if (retval)
12110 return retval;
12111 if (wd.err)
12112 return wd.err;
12113
12114 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
12115 if (compress)
12116 inode.i_flags &= ~EXT2_INDEX_FL;
12117 else
12118 inode.i_flags |= EXT2_INDEX_FL;
12119 inode.i_size = outdir->num * fs->blocksize;
12120 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
12121 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
12122
12123 return 0;
12124}
12125
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012126static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012127{
12128 ext2_filsys fs = ctx->fs;
12129 errcode_t retval;
12130 struct ext2_inode inode;
12131 char *dir_buf = 0;
12132 struct fill_dir_struct fd;
12133 struct out_dir outdir;
12134
12135 outdir.max = outdir.num = 0;
12136 outdir.buf = 0;
12137 outdir.hashes = 0;
12138 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
12139
12140 retval = ENOMEM;
12141 fd.harray = 0;
12142 dir_buf = malloc(inode.i_size);
12143 if (!dir_buf)
12144 goto errout;
12145
12146 fd.max_array = inode.i_size / 32;
12147 fd.num_array = 0;
12148 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
12149 if (!fd.harray)
12150 goto errout;
12151
12152 fd.ctx = ctx;
12153 fd.buf = dir_buf;
12154 fd.inode = &inode;
12155 fd.err = 0;
12156 fd.dir_size = 0;
12157 fd.compress = 0;
12158 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
12159 (inode.i_size / fs->blocksize) < 2)
12160 fd.compress = 1;
12161 fd.parent = 0;
12162
12163 /* Read in the entire directory into memory */
12164 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
12165 fill_dir_block, &fd);
12166 if (fd.err) {
12167 retval = fd.err;
12168 goto errout;
12169 }
12170
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012171 /* Sort the list */
12172resort:
12173 if (fd.compress)
12174 qsort(fd.harray+2, fd.num_array-2,
12175 sizeof(struct hash_entry), name_cmp);
12176 else
12177 qsort(fd.harray, fd.num_array,
12178 sizeof(struct hash_entry), hash_cmp);
12179
12180 /*
12181 * Look for duplicates
12182 */
12183 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
12184 goto resort;
12185
12186 if (ctx->options & E2F_OPT_NO) {
12187 retval = 0;
12188 goto errout;
12189 }
12190
12191 /*
12192 * Copy the directory entries. In a htree directory these
12193 * will become the leaf nodes.
12194 */
12195 retval = copy_dir_entries(fs, &fd, &outdir);
12196 if (retval)
12197 goto errout;
12198
12199 free(dir_buf); dir_buf = 0;
12200
12201 if (!fd.compress) {
12202 /* Calculate the interior nodes */
12203 retval = calculate_tree(fs, &outdir, ino, fd.parent);
12204 if (retval)
12205 goto errout;
12206 }
12207
12208 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012209
12210errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000012211 free(dir_buf);
12212 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012213
12214 free_out_dir(&outdir);
12215 return retval;
12216}
12217
12218void e2fsck_rehash_directories(e2fsck_t ctx)
12219{
12220 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012221 struct dir_info *dir;
12222 ext2_u32_iterate iter;
12223 ext2_ino_t ino;
12224 errcode_t retval;
12225 int i, cur, max, all_dirs, dir_index, first = 1;
12226
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012227 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
12228
12229 if (!ctx->dirs_to_hash && !all_dirs)
12230 return;
12231
12232 e2fsck_get_lost_and_found(ctx, 0);
12233
12234 clear_problem_context(&pctx);
12235
12236 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
12237 cur = 0;
12238 if (all_dirs) {
12239 i = 0;
12240 max = e2fsck_get_num_dirinfo(ctx);
12241 } else {
12242 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
12243 &iter);
12244 if (retval) {
12245 pctx.errcode = retval;
12246 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
12247 return;
12248 }
12249 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
12250 }
12251 while (1) {
12252 if (all_dirs) {
12253 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
12254 break;
12255 ino = dir->ino;
12256 } else {
12257 if (!ext2fs_u32_list_iterate(iter, &ino))
12258 break;
12259 }
12260 if (ino == ctx->lost_and_found)
12261 continue;
12262 pctx.dir = ino;
12263 if (first) {
12264 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
12265 first = 0;
12266 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012267 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
12268 if (pctx.errcode) {
12269 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
12270 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
12271 }
12272 if (ctx->progress && !ctx->progress_fd)
12273 e2fsck_simple_progress(ctx, "Rebuilding directory",
12274 100.0 * (float) (++cur) / (float) max, ino);
12275 }
12276 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
12277 if (!all_dirs)
12278 ext2fs_u32_list_iterate_end(iter);
12279
Rob Landleye7c43b62006-03-01 16:39:45 +000012280 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012281 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012282}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012283
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012284/*
12285 * linux/fs/revoke.c
12286 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012287 * Journal revoke routines for the generic filesystem journaling code;
12288 * part of the ext2fs journaling system.
12289 *
12290 * Revoke is the mechanism used to prevent old log records for deleted
12291 * metadata from being replayed on top of newer data using the same
12292 * blocks. The revoke mechanism is used in two separate places:
12293 *
12294 * + Commit: during commit we write the entire list of the current
12295 * transaction's revoked blocks to the journal
12296 *
12297 * + Recovery: during recovery we record the transaction ID of all
12298 * revoked blocks. If there are multiple revoke records in the log
12299 * for a single block, only the last one counts, and if there is a log
12300 * entry for a block beyond the last revoke, then that log entry still
12301 * gets replayed.
12302 *
12303 * We can get interactions between revokes and new log data within a
12304 * single transaction:
12305 *
12306 * Block is revoked and then journaled:
12307 * The desired end result is the journaling of the new block, so we
12308 * cancel the revoke before the transaction commits.
12309 *
12310 * Block is journaled and then revoked:
12311 * The revoke must take precedence over the write of the block, so we
12312 * need either to cancel the journal entry or to write the revoke
12313 * later in the log than the log block. In this case, we choose the
12314 * latter: journaling a block cancels any revoke record for that block
12315 * in the current transaction, so any revoke for that block in the
12316 * transaction must have happened after the block was journaled and so
12317 * the revoke must take precedence.
12318 *
12319 * Block is revoked and then written as data:
12320 * The data write is allowed to succeed, but the revoke is _not_
12321 * cancelled. We still need to prevent old log records from
12322 * overwriting the new data. We don't even need to clear the revoke
12323 * bit here.
12324 *
12325 * Revoke information on buffers is a tri-state value:
12326 *
12327 * RevokeValid clear: no cached revoke status, need to look it up
12328 * RevokeValid set, Revoked clear:
12329 * buffer has not been revoked, and cancel_revoke
12330 * need do nothing.
12331 * RevokeValid set, Revoked set:
12332 * buffer has been revoked.
12333 */
12334
12335static kmem_cache_t *revoke_record_cache;
12336static kmem_cache_t *revoke_table_cache;
12337
12338/* Each revoke record represents one single revoked block. During
12339 journal replay, this involves recording the transaction ID of the
12340 last transaction to revoke this block. */
12341
12342struct jbd_revoke_record_s
12343{
12344 struct list_head hash;
12345 tid_t sequence; /* Used for recovery only */
12346 unsigned long blocknr;
12347};
12348
12349
12350/* The revoke table is just a simple hash table of revoke records. */
12351struct jbd_revoke_table_s
12352{
12353 /* It is conceivable that we might want a larger hash table
12354 * for recovery. Must be a power of two. */
12355 int hash_size;
12356 int hash_shift;
12357 struct list_head *hash_table;
12358};
12359
12360
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012361/* Utility functions to maintain the revoke table */
12362
12363/* Borrowed from buffer.c: this is a tried and tested block hash function */
12364static inline int hash(journal_t *journal, unsigned long block)
12365{
12366 struct jbd_revoke_table_s *table = journal->j_revoke;
12367 int hash_shift = table->hash_shift;
12368
12369 return ((block << (hash_shift - 6)) ^
12370 (block >> 13) ^
12371 (block << (hash_shift - 12))) & (table->hash_size - 1);
12372}
12373
12374static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
12375 tid_t seq)
12376{
12377 struct list_head *hash_list;
12378 struct jbd_revoke_record_s *record;
12379
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012380 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
12381 if (!record)
12382 goto oom;
12383
12384 record->sequence = seq;
12385 record->blocknr = blocknr;
12386 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
12387 list_add(&record->hash, hash_list);
12388 return 0;
12389
12390oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012391 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012392}
12393
12394/* Find a revoke record in the journal's hash table. */
12395
12396static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
12397 unsigned long blocknr)
12398{
12399 struct list_head *hash_list;
12400 struct jbd_revoke_record_s *record;
12401
12402 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
12403
12404 record = (struct jbd_revoke_record_s *) hash_list->next;
12405 while (&(record->hash) != hash_list) {
12406 if (record->blocknr == blocknr)
12407 return record;
12408 record = (struct jbd_revoke_record_s *) record->hash.next;
12409 }
12410 return NULL;
12411}
12412
12413int journal_init_revoke_caches(void)
12414{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012415 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012416 if (revoke_record_cache == 0)
12417 return -ENOMEM;
12418
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012419 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012420 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012421 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012422 revoke_record_cache = NULL;
12423 return -ENOMEM;
12424 }
12425 return 0;
12426}
12427
12428void journal_destroy_revoke_caches(void)
12429{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012430 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012431 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012432 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012433 revoke_table_cache = 0;
12434}
12435
12436/* Initialise the revoke table for a given journal to a given size. */
12437
12438int journal_init_revoke(journal_t *journal, int hash_size)
12439{
12440 int shift, tmp;
12441
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012442 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
12443 if (!journal->j_revoke)
12444 return -ENOMEM;
12445
12446 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012447 journal->j_revoke->hash_size = hash_size;
12448
12449 shift = 0;
12450 tmp = hash_size;
12451 while((tmp >>= 1UL) != 0UL)
12452 shift++;
12453 journal->j_revoke->hash_shift = shift;
12454
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012455 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012456 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012457 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012458 journal->j_revoke = NULL;
12459 return -ENOMEM;
12460 }
12461
12462 for (tmp = 0; tmp < hash_size; tmp++)
12463 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
12464
12465 return 0;
12466}
12467
12468/* Destoy a journal's revoke table. The table must already be empty! */
12469
12470void journal_destroy_revoke(journal_t *journal)
12471{
12472 struct jbd_revoke_table_s *table;
12473 struct list_head *hash_list;
12474 int i;
12475
12476 table = journal->j_revoke;
12477 if (!table)
12478 return;
12479
12480 for (i=0; i<table->hash_size; i++) {
12481 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012482 }
12483
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012484 free(table->hash_table);
12485 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012486 journal->j_revoke = NULL;
12487}
12488
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012489/*
12490 * Revoke support for recovery.
12491 *
12492 * Recovery needs to be able to:
12493 *
12494 * record all revoke records, including the tid of the latest instance
12495 * of each revoke in the journal
12496 *
12497 * check whether a given block in a given transaction should be replayed
12498 * (ie. has not been revoked by a revoke record in that or a subsequent
12499 * transaction)
12500 *
12501 * empty the revoke table after recovery.
12502 */
12503
12504/*
12505 * First, setting revoke records. We create a new revoke record for
12506 * every block ever revoked in the log as we scan it for recovery, and
12507 * we update the existing records if we find multiple revokes for a
12508 * single block.
12509 */
12510
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012511int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012512 tid_t sequence)
12513{
12514 struct jbd_revoke_record_s *record;
12515
12516 record = find_revoke_record(journal, blocknr);
12517 if (record) {
12518 /* If we have multiple occurences, only record the
12519 * latest sequence number in the hashed record */
12520 if (tid_gt(sequence, record->sequence))
12521 record->sequence = sequence;
12522 return 0;
12523 }
12524 return insert_revoke_hash(journal, blocknr, sequence);
12525}
12526
12527/*
12528 * Test revoke records. For a given block referenced in the log, has
12529 * that block been revoked? A revoke record with a given transaction
12530 * sequence number revokes all blocks in that transaction and earlier
12531 * ones, but later transactions still need replayed.
12532 */
12533
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012534int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012535 tid_t sequence)
12536{
12537 struct jbd_revoke_record_s *record;
12538
12539 record = find_revoke_record(journal, blocknr);
12540 if (!record)
12541 return 0;
12542 if (tid_gt(sequence, record->sequence))
12543 return 0;
12544 return 1;
12545}
12546
12547/*
12548 * Finally, once recovery is over, we need to clear the revoke table so
12549 * that it can be reused by the running filesystem.
12550 */
12551
12552void journal_clear_revoke(journal_t *journal)
12553{
12554 int i;
12555 struct list_head *hash_list;
12556 struct jbd_revoke_record_s *record;
12557 struct jbd_revoke_table_s *revoke_var;
12558
12559 revoke_var = journal->j_revoke;
12560
12561 for (i = 0; i < revoke_var->hash_size; i++) {
12562 hash_list = &revoke_var->hash_table[i];
12563 while (!list_empty(hash_list)) {
12564 record = (struct jbd_revoke_record_s*) hash_list->next;
12565 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012566 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012567 }
12568 }
12569}
12570
12571/*
12572 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012573 */
12574
12575#define MIN_CHECK 1
12576#define MAX_CHECK 2
12577
12578static void check_super_value(e2fsck_t ctx, const char *descr,
12579 unsigned long value, int flags,
12580 unsigned long min_val, unsigned long max_val)
12581{
12582 struct problem_context pctx;
12583
12584 if (((flags & MIN_CHECK) && (value < min_val)) ||
12585 ((flags & MAX_CHECK) && (value > max_val))) {
12586 clear_problem_context(&pctx);
12587 pctx.num = value;
12588 pctx.str = descr;
12589 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
12590 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
12591 }
12592}
12593
12594/*
12595 * This routine may get stubbed out in special compilations of the
12596 * e2fsck code..
12597 */
12598#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012599static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012600{
12601 return (ext2fs_get_device_size(ctx->filesystem_name,
12602 EXT2_BLOCK_SIZE(ctx->fs->super),
12603 &ctx->num_blocks));
12604}
12605#endif
12606
12607/*
12608 * helper function to release an inode
12609 */
12610struct process_block_struct {
12611 e2fsck_t ctx;
12612 char *buf;
12613 struct problem_context *pctx;
12614 int truncating;
12615 int truncate_offset;
12616 e2_blkcnt_t truncate_block;
12617 int truncated_blocks;
12618 int abort;
12619 errcode_t errcode;
12620};
12621
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012622static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012623 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012624 blk_t ref_blk FSCK_ATTR((unused)),
12625 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012626 void *priv_data)
12627{
12628 struct process_block_struct *pb;
12629 e2fsck_t ctx;
12630 struct problem_context *pctx;
12631 blk_t blk = *block_nr;
12632 int retval = 0;
12633
12634 pb = (struct process_block_struct *) priv_data;
12635 ctx = pb->ctx;
12636 pctx = pb->pctx;
12637
12638 pctx->blk = blk;
12639 pctx->blkcount = blockcnt;
12640
12641 if (HOLE_BLKADDR(blk))
12642 return 0;
12643
12644 if ((blk < fs->super->s_first_data_block) ||
12645 (blk >= fs->super->s_blocks_count)) {
12646 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
12647 return_abort:
12648 pb->abort = 1;
12649 return BLOCK_ABORT;
12650 }
12651
12652 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
12653 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
12654 goto return_abort;
12655 }
12656
12657 /*
12658 * If we are deleting an orphan, then we leave the fields alone.
12659 * If we are truncating an orphan, then update the inode fields
12660 * and clean up any partial block data.
12661 */
12662 if (pb->truncating) {
12663 /*
12664 * We only remove indirect blocks if they are
12665 * completely empty.
12666 */
12667 if (blockcnt < 0) {
12668 int i, limit;
12669 blk_t *bp;
12670
12671 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
12672 pb->buf);
12673 if (pb->errcode)
12674 goto return_abort;
12675
12676 limit = fs->blocksize >> 2;
12677 for (i = 0, bp = (blk_t *) pb->buf;
12678 i < limit; i++, bp++)
12679 if (*bp)
12680 return 0;
12681 }
12682 /*
12683 * We don't remove direct blocks until we've reached
12684 * the truncation block.
12685 */
12686 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
12687 return 0;
12688 /*
12689 * If part of the last block needs truncating, we do
12690 * it here.
12691 */
12692 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
12693 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
12694 pb->buf);
12695 if (pb->errcode)
12696 goto return_abort;
12697 memset(pb->buf + pb->truncate_offset, 0,
12698 fs->blocksize - pb->truncate_offset);
12699 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
12700 pb->buf);
12701 if (pb->errcode)
12702 goto return_abort;
12703 }
12704 pb->truncated_blocks++;
12705 *block_nr = 0;
12706 retval |= BLOCK_CHANGED;
12707 }
12708
12709 ext2fs_block_alloc_stats(fs, blk, -1);
12710 return retval;
12711}
12712
12713/*
12714 * This function releases an inode. Returns 1 if an inconsistency was
12715 * found. If the inode has a link count, then it is being truncated and
12716 * not deleted.
12717 */
12718static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
12719 struct ext2_inode *inode, char *block_buf,
12720 struct problem_context *pctx)
12721{
12722 struct process_block_struct pb;
12723 ext2_filsys fs = ctx->fs;
12724 errcode_t retval;
12725 __u32 count;
12726
12727 if (!ext2fs_inode_has_valid_blocks(inode))
12728 return 0;
12729
12730 pb.buf = block_buf + 3 * ctx->fs->blocksize;
12731 pb.ctx = ctx;
12732 pb.abort = 0;
12733 pb.errcode = 0;
12734 pb.pctx = pctx;
12735 if (inode->i_links_count) {
12736 pb.truncating = 1;
12737 pb.truncate_block = (e2_blkcnt_t)
12738 ((((long long)inode->i_size_high << 32) +
12739 inode->i_size + fs->blocksize - 1) /
12740 fs->blocksize);
12741 pb.truncate_offset = inode->i_size % fs->blocksize;
12742 } else {
12743 pb.truncating = 0;
12744 pb.truncate_block = 0;
12745 pb.truncate_offset = 0;
12746 }
12747 pb.truncated_blocks = 0;
12748 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
12749 block_buf, release_inode_block, &pb);
12750 if (retval) {
12751 com_err("release_inode_blocks", retval,
12752 _("while calling ext2fs_block_iterate for inode %d"),
12753 ino);
12754 return 1;
12755 }
12756 if (pb.abort)
12757 return 1;
12758
12759 /* Refresh the inode since ext2fs_block_iterate may have changed it */
12760 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
12761
12762 if (pb.truncated_blocks)
12763 inode->i_blocks -= pb.truncated_blocks *
12764 (fs->blocksize / 512);
12765
12766 if (inode->i_file_acl) {
12767 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
12768 block_buf, -1, &count);
12769 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
12770 retval = 0;
12771 count = 1;
12772 }
12773 if (retval) {
12774 com_err("release_inode_blocks", retval,
12775 _("while calling ext2fs_adjust_ea_refocunt for inode %d"),
12776 ino);
12777 return 1;
12778 }
12779 if (count == 0)
12780 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
12781 inode->i_file_acl = 0;
12782 }
12783 return 0;
12784}
12785
12786/*
12787 * This function releases all of the orphan inodes. It returns 1 if
12788 * it hit some error, and 0 on success.
12789 */
12790static int release_orphan_inodes(e2fsck_t ctx)
12791{
12792 ext2_filsys fs = ctx->fs;
12793 ext2_ino_t ino, next_ino;
12794 struct ext2_inode inode;
12795 struct problem_context pctx;
12796 char *block_buf;
12797
12798 if ((ino = fs->super->s_last_orphan) == 0)
12799 return 0;
12800
12801 /*
12802 * Win or lose, we won't be using the head of the orphan inode
12803 * list again.
12804 */
12805 fs->super->s_last_orphan = 0;
12806 ext2fs_mark_super_dirty(fs);
12807
12808 /*
12809 * If the filesystem contains errors, don't run the orphan
12810 * list, since the orphan list can't be trusted; and we're
12811 * going to be running a full e2fsck run anyway...
12812 */
12813 if (fs->super->s_state & EXT2_ERROR_FS)
12814 return 0;
12815
12816 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
12817 (ino > fs->super->s_inodes_count)) {
12818 clear_problem_context(&pctx);
12819 pctx.ino = ino;
12820 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
12821 return 1;
12822 }
12823
12824 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12825 "block iterate buffer");
12826 e2fsck_read_bitmaps(ctx);
12827
12828 while (ino) {
12829 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
12830 clear_problem_context(&pctx);
12831 pctx.ino = ino;
12832 pctx.inode = &inode;
12833 pctx.str = inode.i_links_count ? _("Truncating") :
12834 _("Clearing");
12835
12836 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
12837
12838 next_ino = inode.i_dtime;
12839 if (next_ino &&
12840 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
12841 (next_ino > fs->super->s_inodes_count))) {
12842 pctx.ino = next_ino;
12843 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
12844 goto return_abort;
12845 }
12846
12847 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
12848 goto return_abort;
12849
12850 if (!inode.i_links_count) {
12851 ext2fs_inode_alloc_stats2(fs, ino, -1,
12852 LINUX_S_ISDIR(inode.i_mode));
12853 inode.i_dtime = time(0);
12854 } else {
12855 inode.i_dtime = 0;
12856 }
12857 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
12858 ino = next_ino;
12859 }
12860 ext2fs_free_mem(&block_buf);
12861 return 0;
12862return_abort:
12863 ext2fs_free_mem(&block_buf);
12864 return 1;
12865}
12866
12867/*
12868 * Check the resize inode to make sure it is sane. We check both for
12869 * the case where on-line resizing is not enabled (in which case the
12870 * resize inode should be cleared) as well as the case where on-line
12871 * resizing is enabled.
12872 */
12873static void check_resize_inode(e2fsck_t ctx)
12874{
12875 ext2_filsys fs = ctx->fs;
12876 struct ext2_inode inode;
12877 struct problem_context pctx;
12878 int i, j, gdt_off, ind_off;
12879 blk_t blk, pblk, expect;
12880 __u32 *dind_buf = 0, *ind_buf;
12881 errcode_t retval;
12882
12883 clear_problem_context(&pctx);
12884
12885 /*
12886 * If the resize inode feature isn't set, then
12887 * s_reserved_gdt_blocks must be zero.
12888 */
12889 if (!(fs->super->s_feature_compat &
12890 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
12891 if (fs->super->s_reserved_gdt_blocks) {
12892 pctx.num = fs->super->s_reserved_gdt_blocks;
12893 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
12894 &pctx)) {
12895 fs->super->s_reserved_gdt_blocks = 0;
12896 ext2fs_mark_super_dirty(fs);
12897 }
12898 }
12899 }
12900
Mike Frysinger874af852006-03-08 07:03:27 +000012901 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012902 pctx.ino = EXT2_RESIZE_INO;
12903 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
12904 if (retval) {
12905 if (fs->super->s_feature_compat &
12906 EXT2_FEATURE_COMPAT_RESIZE_INODE)
12907 ctx->flags |= E2F_FLAG_RESIZE_INODE;
12908 return;
12909 }
12910
12911 /*
12912 * If the resize inode feature isn't set, check to make sure
12913 * the resize inode is cleared; then we're done.
12914 */
12915 if (!(fs->super->s_feature_compat &
12916 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
12917 for (i=0; i < EXT2_N_BLOCKS; i++) {
12918 if (inode.i_block[i])
12919 break;
12920 }
12921 if ((i < EXT2_N_BLOCKS) &&
12922 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
12923 memset(&inode, 0, sizeof(inode));
12924 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
12925 "clear_resize");
12926 }
12927 return;
12928 }
12929
12930 /*
12931 * The resize inode feature is enabled; check to make sure the
12932 * only block in use is the double indirect block
12933 */
12934 blk = inode.i_block[EXT2_DIND_BLOCK];
12935 for (i=0; i < EXT2_N_BLOCKS; i++) {
12936 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
12937 break;
12938 }
12939 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
12940 !(inode.i_mode & LINUX_S_IFREG) ||
12941 (blk < fs->super->s_first_data_block ||
12942 blk >= fs->super->s_blocks_count)) {
12943 resize_inode_invalid:
12944 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
12945 memset(&inode, 0, sizeof(inode));
12946 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
12947 "clear_resize");
12948 ctx->flags |= E2F_FLAG_RESIZE_INODE;
12949 }
12950 if (!(ctx->options & E2F_OPT_READONLY)) {
12951 fs->super->s_state &= ~EXT2_VALID_FS;
12952 ext2fs_mark_super_dirty(fs);
12953 }
12954 goto cleanup;
12955 }
12956 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
12957 "resize dind buffer");
12958 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
12959
12960 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
12961 if (retval)
12962 goto resize_inode_invalid;
12963
12964 gdt_off = fs->desc_blocks;
12965 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
12966 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
12967 i++, gdt_off++, pblk++) {
12968 gdt_off %= fs->blocksize/4;
12969 if (dind_buf[gdt_off] != pblk)
12970 goto resize_inode_invalid;
12971 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
12972 if (retval)
12973 goto resize_inode_invalid;
12974 ind_off = 0;
12975 for (j = 1; j < fs->group_desc_count; j++) {
12976 if (!ext2fs_bg_has_super(fs, j))
12977 continue;
12978 expect = pblk + (j * fs->super->s_blocks_per_group);
12979 if (ind_buf[ind_off] != expect)
12980 goto resize_inode_invalid;
12981 ind_off++;
12982 }
12983 }
12984
12985cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000012986 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012987
12988 }
12989
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012990static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012991{
12992 ext2_filsys fs = ctx->fs;
12993 blk_t first_block, last_block;
12994 struct ext2_super_block *sb = fs->super;
12995 struct ext2_group_desc *gd;
12996 blk_t blocks_per_group = fs->super->s_blocks_per_group;
12997 blk_t bpg_max;
12998 int inodes_per_block;
12999 int ipg_max;
13000 int inode_size;
13001 dgrp_t i;
13002 blk_t should_be;
13003 struct problem_context pctx;
13004 __u32 free_blocks = 0, free_inodes = 0;
13005
13006 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
13007 ipg_max = inodes_per_block * (blocks_per_group - 4);
13008 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
13009 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
13010 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
13011 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
13012 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
13013
13014 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
13015 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
13016 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
13017 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
13018 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
13019 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
13020
13021 clear_problem_context(&pctx);
13022
13023 /*
13024 * Verify the super block constants...
13025 */
13026 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
13027 MIN_CHECK, 1, 0);
13028 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
13029 MIN_CHECK, 1, 0);
13030 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
13031 MAX_CHECK, 0, sb->s_blocks_count);
13032 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
13033 MIN_CHECK | MAX_CHECK, 0,
13034 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
13035 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
13036 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
13037 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
13038 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
13039 bpg_max);
13040 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
13041 MIN_CHECK | MAX_CHECK, 8, bpg_max);
13042 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
13043 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
13044 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
13045 MAX_CHECK, 0, sb->s_blocks_count / 2);
13046 check_super_value(ctx, "reserved_gdt_blocks",
13047 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
13048 fs->blocksize/4);
13049 inode_size = EXT2_INODE_SIZE(sb);
13050 check_super_value(ctx, "inode_size",
13051 inode_size, MIN_CHECK | MAX_CHECK,
13052 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
13053 if (inode_size & (inode_size - 1)) {
13054 pctx.num = inode_size;
13055 pctx.str = "inode_size";
13056 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
13057 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
13058 return;
13059 }
13060
13061 if (!ctx->num_blocks) {
13062 pctx.errcode = e2fsck_get_device_size(ctx);
13063 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
13064 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
13065 ctx->flags |= E2F_FLAG_ABORT;
13066 return;
13067 }
13068 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
13069 (ctx->num_blocks < sb->s_blocks_count)) {
13070 pctx.blk = sb->s_blocks_count;
13071 pctx.blk2 = ctx->num_blocks;
13072 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
13073 ctx->flags |= E2F_FLAG_ABORT;
13074 return;
13075 }
13076 }
13077 }
13078
13079 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
13080 pctx.blk = EXT2_BLOCK_SIZE(sb);
13081 pctx.blk2 = EXT2_FRAG_SIZE(sb);
13082 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
13083 ctx->flags |= E2F_FLAG_ABORT;
13084 return;
13085 }
13086
13087 should_be = sb->s_frags_per_group >>
13088 (sb->s_log_block_size - sb->s_log_frag_size);
13089 if (sb->s_blocks_per_group != should_be) {
13090 pctx.blk = sb->s_blocks_per_group;
13091 pctx.blk2 = should_be;
13092 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
13093 ctx->flags |= E2F_FLAG_ABORT;
13094 return;
13095 }
13096
13097 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
13098 if (sb->s_first_data_block != should_be) {
13099 pctx.blk = sb->s_first_data_block;
13100 pctx.blk2 = should_be;
13101 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
13102 ctx->flags |= E2F_FLAG_ABORT;
13103 return;
13104 }
13105
13106 should_be = sb->s_inodes_per_group * fs->group_desc_count;
13107 if (sb->s_inodes_count != should_be) {
13108 pctx.ino = sb->s_inodes_count;
13109 pctx.ino2 = should_be;
13110 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
13111 sb->s_inodes_count = should_be;
13112 ext2fs_mark_super_dirty(fs);
13113 }
13114 }
13115
13116 /*
13117 * Verify the group descriptors....
13118 */
13119 first_block = sb->s_first_data_block;
13120 last_block = first_block + blocks_per_group;
13121
13122 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
13123 pctx.group = i;
13124
13125 if (i == fs->group_desc_count - 1)
13126 last_block = sb->s_blocks_count;
13127 if ((gd->bg_block_bitmap < first_block) ||
13128 (gd->bg_block_bitmap >= last_block)) {
13129 pctx.blk = gd->bg_block_bitmap;
13130 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
13131 gd->bg_block_bitmap = 0;
13132 }
13133 if (gd->bg_block_bitmap == 0) {
13134 ctx->invalid_block_bitmap_flag[i]++;
13135 ctx->invalid_bitmaps++;
13136 }
13137 if ((gd->bg_inode_bitmap < first_block) ||
13138 (gd->bg_inode_bitmap >= last_block)) {
13139 pctx.blk = gd->bg_inode_bitmap;
13140 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
13141 gd->bg_inode_bitmap = 0;
13142 }
13143 if (gd->bg_inode_bitmap == 0) {
13144 ctx->invalid_inode_bitmap_flag[i]++;
13145 ctx->invalid_bitmaps++;
13146 }
13147 if ((gd->bg_inode_table < first_block) ||
13148 ((gd->bg_inode_table +
13149 fs->inode_blocks_per_group - 1) >= last_block)) {
13150 pctx.blk = gd->bg_inode_table;
13151 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
13152 gd->bg_inode_table = 0;
13153 }
13154 if (gd->bg_inode_table == 0) {
13155 ctx->invalid_inode_table_flag[i]++;
13156 ctx->invalid_bitmaps++;
13157 }
13158 free_blocks += gd->bg_free_blocks_count;
13159 free_inodes += gd->bg_free_inodes_count;
13160 first_block += sb->s_blocks_per_group;
13161 last_block += sb->s_blocks_per_group;
13162
13163 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
13164 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
13165 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
13166 ext2fs_unmark_valid(fs);
13167
13168 }
13169
13170 /*
13171 * Update the global counts from the block group counts. This
13172 * is needed for an experimental patch which eliminates
13173 * locking the entire filesystem when allocating blocks or
13174 * inodes; if the filesystem is not unmounted cleanly, the
13175 * global counts may not be accurate.
13176 */
13177 if ((free_blocks != sb->s_free_blocks_count) ||
13178 (free_inodes != sb->s_free_inodes_count)) {
13179 if (ctx->options & E2F_OPT_READONLY)
13180 ext2fs_unmark_valid(fs);
13181 else {
13182 sb->s_free_blocks_count = free_blocks;
13183 sb->s_free_inodes_count = free_inodes;
13184 ext2fs_mark_super_dirty(fs);
13185 }
13186 }
13187
13188 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
13189 (sb->s_free_inodes_count > sb->s_inodes_count))
13190 ext2fs_unmark_valid(fs);
13191
13192
13193 /*
13194 * If we have invalid bitmaps, set the error state of the
13195 * filesystem.
13196 */
13197 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
13198 sb->s_state &= ~EXT2_VALID_FS;
13199 ext2fs_mark_super_dirty(fs);
13200 }
13201
13202 clear_problem_context(&pctx);
13203
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013204 /*
13205 * If the UUID field isn't assigned, assign it.
13206 */
13207 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
13208 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
13209 uuid_generate(sb->s_uuid);
13210 ext2fs_mark_super_dirty(fs);
13211 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
13212 }
13213 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013214
Rob Landley3e72c592006-04-06 22:49:04 +000013215 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013216 * For the Hurd, check to see if the filetype option is set,
13217 * since it doesn't support it.
13218 */
13219 if (!(ctx->options & E2F_OPT_READONLY) &&
13220 fs->super->s_creator_os == EXT2_OS_HURD &&
13221 (fs->super->s_feature_incompat &
13222 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
13223 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
13224 fs->super->s_feature_incompat &=
13225 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
13226 ext2fs_mark_super_dirty(fs);
13227
13228 }
13229 }
13230
13231 /*
13232 * If we have any of the compatibility flags set, we need to have a
13233 * revision 1 filesystem. Most kernels will not check the flags on
13234 * a rev 0 filesystem and we may have corruption issues because of
13235 * the incompatible changes to the filesystem.
13236 */
13237 if (!(ctx->options & E2F_OPT_READONLY) &&
13238 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
13239 (fs->super->s_feature_compat ||
13240 fs->super->s_feature_ro_compat ||
13241 fs->super->s_feature_incompat) &&
13242 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
13243 ext2fs_update_dynamic_rev(fs);
13244 ext2fs_mark_super_dirty(fs);
13245 }
13246
13247 check_resize_inode(ctx);
13248
13249 /*
13250 * Clean up any orphan inodes, if present.
13251 */
13252 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
13253 fs->super->s_state &= ~EXT2_VALID_FS;
13254 ext2fs_mark_super_dirty(fs);
13255 }
13256
13257 /*
13258 * Move the ext3 journal file, if necessary.
13259 */
13260 e2fsck_move_ext3_journal(ctx);
13261 return;
13262}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013263
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013264/*
13265 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013266 */
13267
13268#ifdef ENABLE_SWAPFS
13269
13270struct swap_block_struct {
13271 ext2_ino_t ino;
13272 int isdir;
13273 errcode_t errcode;
13274 char *dir_buf;
13275 struct ext2_inode *inode;
13276};
13277
13278/*
13279 * This is a helper function for block_iterate. We mark all of the
13280 * indirect and direct blocks as changed, so that block_iterate will
13281 * write them out.
13282 */
13283static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
13284 void *priv_data)
13285{
13286 errcode_t retval;
13287
13288 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
13289
13290 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
13291 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
13292 if (retval) {
13293 sb->errcode = retval;
13294 return BLOCK_ABORT;
13295 }
13296 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
13297 if (retval) {
13298 sb->errcode = retval;
13299 return BLOCK_ABORT;
13300 }
13301 }
13302 if (blockcnt >= 0) {
13303 if (blockcnt < EXT2_NDIR_BLOCKS)
13304 return 0;
13305 return BLOCK_CHANGED;
13306 }
13307 if (blockcnt == BLOCK_COUNT_IND) {
13308 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
13309 return 0;
13310 return BLOCK_CHANGED;
13311 }
13312 if (blockcnt == BLOCK_COUNT_DIND) {
13313 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
13314 return 0;
13315 return BLOCK_CHANGED;
13316 }
13317 if (blockcnt == BLOCK_COUNT_TIND) {
13318 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
13319 return 0;
13320 return BLOCK_CHANGED;
13321 }
13322 return BLOCK_CHANGED;
13323}
13324
13325/*
13326 * This function is responsible for byte-swapping all of the indirect,
13327 * block pointers. It is also responsible for byte-swapping directories.
13328 */
13329static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
13330 struct ext2_inode *inode)
13331{
13332 errcode_t retval;
13333 struct swap_block_struct sb;
13334
13335 sb.ino = ino;
13336 sb.inode = inode;
13337 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
13338 sb.errcode = 0;
13339 sb.isdir = 0;
13340 if (LINUX_S_ISDIR(inode->i_mode))
13341 sb.isdir = 1;
13342
13343 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
13344 swap_block, &sb);
13345 if (retval) {
13346 com_err("swap_inode_blocks", retval,
13347 _("while calling ext2fs_block_iterate"));
13348 ctx->flags |= E2F_FLAG_ABORT;
13349 return;
13350 }
13351 if (sb.errcode) {
13352 com_err("swap_inode_blocks", sb.errcode,
13353 _("while calling iterator function"));
13354 ctx->flags |= E2F_FLAG_ABORT;
13355 return;
13356 }
13357}
13358
13359static void swap_inodes(e2fsck_t ctx)
13360{
13361 ext2_filsys fs = ctx->fs;
13362 dgrp_t group;
13363 unsigned int i;
13364 ext2_ino_t ino = 1;
13365 char *buf, *block_buf;
13366 errcode_t retval;
13367 struct ext2_inode * inode;
13368
13369 e2fsck_use_inode_shortcuts(ctx, 1);
13370
13371 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
13372 &buf);
13373 if (retval) {
13374 com_err("swap_inodes", retval,
13375 _("while allocating inode buffer"));
13376 ctx->flags |= E2F_FLAG_ABORT;
13377 return;
13378 }
13379 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
13380 "block interate buffer");
13381 for (group = 0; group < fs->group_desc_count; group++) {
13382 retval = io_channel_read_blk(fs->io,
13383 fs->group_desc[group].bg_inode_table,
13384 fs->inode_blocks_per_group, buf);
13385 if (retval) {
13386 com_err("swap_inodes", retval,
13387 _("while reading inode table (group %d)"),
13388 group);
13389 ctx->flags |= E2F_FLAG_ABORT;
13390 return;
13391 }
13392 inode = (struct ext2_inode *) buf;
13393 for (i=0; i < fs->super->s_inodes_per_group;
13394 i++, ino++, inode++) {
13395 ctx->stashed_ino = ino;
13396 ctx->stashed_inode = inode;
13397
13398 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
13399 ext2fs_swap_inode(fs, inode, inode, 0);
13400
13401 /*
13402 * Skip deleted files.
13403 */
13404 if (inode->i_links_count == 0)
13405 continue;
13406
13407 if (LINUX_S_ISDIR(inode->i_mode) ||
13408 ((inode->i_block[EXT2_IND_BLOCK] ||
13409 inode->i_block[EXT2_DIND_BLOCK] ||
13410 inode->i_block[EXT2_TIND_BLOCK]) &&
13411 ext2fs_inode_has_valid_blocks(inode)))
13412 swap_inode_blocks(ctx, ino, block_buf, inode);
13413
13414 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
13415 return;
13416
13417 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
13418 ext2fs_swap_inode(fs, inode, inode, 1);
13419 }
13420 retval = io_channel_write_blk(fs->io,
13421 fs->group_desc[group].bg_inode_table,
13422 fs->inode_blocks_per_group, buf);
13423 if (retval) {
13424 com_err("swap_inodes", retval,
13425 _("while writing inode table (group %d)"),
13426 group);
13427 ctx->flags |= E2F_FLAG_ABORT;
13428 return;
13429 }
13430 }
13431 ext2fs_free_mem(&buf);
13432 ext2fs_free_mem(&block_buf);
13433 e2fsck_use_inode_shortcuts(ctx, 0);
13434 ext2fs_flush_icache(fs);
13435}
13436
13437#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
13438/*
13439 * On the PowerPC, the big-endian variant of the ext2 filesystem
13440 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
13441 * of each word. Thus a bitmap with only bit 0 set would be, as
13442 * a string of bytes, 00 00 00 01 00 ...
13443 * To cope with this, we byte-reverse each word of a bitmap if
13444 * we have a big-endian filesystem, that is, if we are *not*
13445 * byte-swapping other word-sized numbers.
13446 */
13447#define EXT2_BIG_ENDIAN_BITMAPS
13448#endif
13449
13450#ifdef EXT2_BIG_ENDIAN_BITMAPS
13451static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
13452{
13453 __u32 *p = (__u32 *) bmap->bitmap;
13454 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
13455
13456 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
13457 *p = ext2fs_swab32(*p);
13458}
13459#endif
13460
13461
13462#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013463static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013464{
13465 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013466 if (!(ctx->options & E2F_OPT_PREEN))
13467 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
13468
Rob Landley3e72c592006-04-06 22:49:04 +000013469 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013470
13471 if (fs->super->s_mnt_count) {
13472 fprintf(stderr, _("%s: the filesystem must be freshly "
13473 "checked using fsck\n"
13474 "and not mounted before trying to "
13475 "byte-swap it.\n"), ctx->device_name);
13476 ctx->flags |= E2F_FLAG_ABORT;
13477 return;
13478 }
13479 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
13480 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
13481 EXT2_FLAG_SWAP_BYTES_WRITE);
13482 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
13483 } else {
13484 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
13485 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
13486 }
13487 swap_inodes(ctx);
13488 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
13489 return;
13490 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
13491 fs->flags |= EXT2_FLAG_SWAP_BYTES;
13492 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
13493 EXT2_FLAG_SWAP_BYTES_WRITE);
13494
13495#ifdef EXT2_BIG_ENDIAN_BITMAPS
13496 e2fsck_read_bitmaps(ctx);
13497 ext2fs_swap_bitmap(fs->inode_map);
13498 ext2fs_swap_bitmap(fs->block_map);
13499 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
13500#endif
13501 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
13502 ext2fs_flush(fs);
13503 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013504}
13505#endif /* ENABLE_SWAPFS */
13506
13507#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013508
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013509/*
13510 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013511 */
13512
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013513
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013514void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
13515 const char *description)
13516{
13517 void *ret;
13518 char buf[256];
13519
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013520 ret = malloc(size);
13521 if (!ret) {
13522 sprintf(buf, "Can't allocate %s\n", description);
13523 fatal_error(ctx, buf);
13524 }
13525 memset(ret, 0, size);
13526 return ret;
13527}
13528
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013529static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013530{
13531 char *ret;
13532
13533 if (!str)
13534 return NULL;
13535 if (!len)
13536 len = strlen(str);
13537 ret = malloc(len+1);
13538 if (ret) {
13539 strncpy(ret, str, len);
13540 ret[len] = 0;
13541 }
13542 return ret;
13543}
13544
13545#ifndef HAVE_CONIO_H
13546static int read_a_char(void)
13547{
13548 char c;
13549 int r;
13550 int fail = 0;
13551
13552 while(1) {
13553 if (e2fsck_global_ctx &&
13554 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
13555 return 3;
13556 }
13557 r = read(0, &c, 1);
13558 if (r == 1)
13559 return c;
13560 if (fail++ > 100)
13561 break;
13562 }
13563 return EOF;
13564}
13565#endif
13566
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013567static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013568{
13569 int c;
13570 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013571 static const char short_yes[] = "yY";
13572 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013573
13574#ifdef HAVE_TERMIOS_H
13575 struct termios termios, tmp;
13576
13577 tcgetattr (0, &termios);
13578 tmp = termios;
13579 tmp.c_lflag &= ~(ICANON | ECHO);
13580 tmp.c_cc[VMIN] = 1;
13581 tmp.c_cc[VTIME] = 0;
13582 tcsetattr (0, TCSANOW, &tmp);
13583#endif
13584
13585 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013586 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013587 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013588 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013589 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013590 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013591 printf("%s%s? ", string, defstr);
13592 while (1) {
13593 fflush (stdout);
13594 if ((c = read_a_char()) == EOF)
13595 break;
13596 if (c == 3) {
13597#ifdef HAVE_TERMIOS_H
13598 tcsetattr (0, TCSANOW, &termios);
13599#endif
13600 if (e2fsck_global_ctx &&
13601 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
13602 puts("\n");
13603 longjmp(e2fsck_global_ctx->abort_loc, 1);
13604 }
13605 puts(_("cancelled!\n"));
13606 return 0;
13607 }
13608 if (strchr(short_yes, (char) c)) {
13609 def = 1;
13610 break;
13611 }
13612 else if (strchr(short_no, (char) c)) {
13613 def = 0;
13614 break;
13615 }
13616 else if ((c == ' ' || c == '\n') && (def != -1))
13617 break;
13618 }
13619 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013620 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013621 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013622 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013623#ifdef HAVE_TERMIOS_H
13624 tcsetattr (0, TCSANOW, &termios);
13625#endif
13626 return def;
13627}
13628
13629int ask (e2fsck_t ctx, const char * string, int def)
13630{
13631 if (ctx->options & E2F_OPT_NO) {
13632 printf (_("%s? no\n\n"), string);
13633 return 0;
13634 }
13635 if (ctx->options & E2F_OPT_YES) {
13636 printf (_("%s? yes\n\n"), string);
13637 return 1;
13638 }
13639 if (ctx->options & E2F_OPT_PREEN) {
13640 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
13641 return def;
13642 }
13643 return ask_yn(string, def);
13644}
13645
13646void e2fsck_read_bitmaps(e2fsck_t ctx)
13647{
13648 ext2_filsys fs = ctx->fs;
13649 errcode_t retval;
13650
13651 if (ctx->invalid_bitmaps) {
13652 com_err(ctx->program_name, 0,
13653 _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
13654 ctx->device_name);
13655 fatal_error(ctx, 0);
13656 }
13657
13658 ehandler_operation(_("reading inode and block bitmaps"));
13659 retval = ext2fs_read_bitmaps(fs);
13660 ehandler_operation(0);
13661 if (retval) {
13662 com_err(ctx->program_name, retval,
13663 _("while retrying to read bitmaps for %s"),
13664 ctx->device_name);
13665 fatal_error(ctx, 0);
13666 }
13667}
13668
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013669static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013670{
13671 ext2_filsys fs = ctx->fs;
13672 errcode_t retval;
13673
13674 if (ext2fs_test_bb_dirty(fs)) {
13675 ehandler_operation(_("writing block bitmaps"));
13676 retval = ext2fs_write_block_bitmap(fs);
13677 ehandler_operation(0);
13678 if (retval) {
13679 com_err(ctx->program_name, retval,
13680 _("while retrying to write block bitmaps for %s"),
13681 ctx->device_name);
13682 fatal_error(ctx, 0);
13683 }
13684 }
13685
13686 if (ext2fs_test_ib_dirty(fs)) {
13687 ehandler_operation(_("writing inode bitmaps"));
13688 retval = ext2fs_write_inode_bitmap(fs);
13689 ehandler_operation(0);
13690 if (retval) {
13691 com_err(ctx->program_name, retval,
13692 _("while retrying to write inode bitmaps for %s"),
13693 ctx->device_name);
13694 fatal_error(ctx, 0);
13695 }
13696 }
13697}
13698
13699void preenhalt(e2fsck_t ctx)
13700{
13701 ext2_filsys fs = ctx->fs;
13702
13703 if (!(ctx->options & E2F_OPT_PREEN))
13704 return;
13705 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
13706 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
13707 ctx->device_name);
13708 if (fs != NULL) {
13709 fs->super->s_state |= EXT2_ERROR_FS;
13710 ext2fs_mark_super_dirty(fs);
13711 ext2fs_close(fs);
13712 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013713 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013714}
13715
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013716void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
13717 struct ext2_inode * inode, const char *proc)
13718{
13719 int retval;
13720
13721 retval = ext2fs_read_inode(ctx->fs, ino, inode);
13722 if (retval) {
13723 com_err("ext2fs_read_inode", retval,
13724 _("while reading inode %ld in %s"), ino, proc);
13725 fatal_error(ctx, 0);
13726 }
13727}
13728
13729extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
13730 struct ext2_inode * inode, int bufsize,
13731 const char *proc)
13732{
13733 int retval;
13734
13735 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
13736 if (retval) {
13737 com_err("ext2fs_write_inode", retval,
13738 _("while writing inode %ld in %s"), ino, proc);
13739 fatal_error(ctx, 0);
13740 }
13741}
13742
13743extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
13744 struct ext2_inode * inode, const char *proc)
13745{
13746 int retval;
13747
13748 retval = ext2fs_write_inode(ctx->fs, ino, inode);
13749 if (retval) {
13750 com_err("ext2fs_write_inode", retval,
13751 _("while writing inode %ld in %s"), ino, proc);
13752 fatal_error(ctx, 0);
13753 }
13754}
13755
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013756blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
13757 io_manager manager)
13758{
13759 struct ext2_super_block *sb;
13760 io_channel io = NULL;
13761 void *buf = NULL;
13762 int blocksize;
13763 blk_t superblock, ret_sb = 8193;
13764
13765 if (fs && fs->super) {
13766 ret_sb = (fs->super->s_blocks_per_group +
13767 fs->super->s_first_data_block);
13768 if (ctx) {
13769 ctx->superblock = ret_sb;
13770 ctx->blocksize = fs->blocksize;
13771 }
13772 return ret_sb;
13773 }
13774
13775 if (ctx) {
13776 if (ctx->blocksize) {
13777 ret_sb = ctx->blocksize * 8;
13778 if (ctx->blocksize == 1024)
13779 ret_sb++;
13780 ctx->superblock = ret_sb;
13781 return ret_sb;
13782 }
13783 ctx->superblock = ret_sb;
13784 ctx->blocksize = 1024;
13785 }
13786
13787 if (!name || !manager)
13788 goto cleanup;
13789
13790 if (manager->open(name, 0, &io) != 0)
13791 goto cleanup;
13792
13793 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
13794 goto cleanup;
13795 sb = (struct ext2_super_block *) buf;
13796
13797 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13798 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
13799 superblock = blocksize*8;
13800 if (blocksize == 1024)
13801 superblock++;
13802 io_channel_set_blksize(io, blocksize);
13803 if (io_channel_read_blk(io, superblock,
13804 -SUPERBLOCK_SIZE, buf))
13805 continue;
13806#ifdef EXT2FS_ENABLE_SWAPFS
13807 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
13808 ext2fs_swap_super(sb);
13809#endif
13810 if (sb->s_magic == EXT2_SUPER_MAGIC) {
13811 ret_sb = superblock;
13812 if (ctx) {
13813 ctx->superblock = superblock;
13814 ctx->blocksize = blocksize;
13815 }
13816 break;
13817 }
13818 }
13819
13820cleanup:
13821 if (io)
13822 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000013823 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013824 return (ret_sb);
13825}
13826
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013827
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013828/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013829 * This function runs through the e2fsck passes and calls them all,
13830 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013831 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013832typedef void (*pass_t)(e2fsck_t ctx);
13833
13834static const pass_t e2fsck_passes[] = {
13835 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
13836 e2fsck_pass5, 0 };
13837
13838#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
13839
13840static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013841{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013842 int i;
13843 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013844
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013845 if (setjmp(ctx->abort_loc)) {
13846 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
13847 return (ctx->flags & E2F_FLAG_RUN_RETURN);
13848 }
13849 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013850
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013851 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
13852 if (ctx->flags & E2F_FLAG_RUN_RETURN)
13853 break;
13854 e2fsck_pass(ctx);
13855 if (ctx->progress)
13856 (void) (ctx->progress)(ctx, 0, 0, 0);
13857 }
13858 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013859
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013860 if (ctx->flags & E2F_FLAG_RUN_RETURN)
13861 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013862 return 0;
13863}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013864
13865
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013866/*
13867 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013868 */
13869
13870
Mike Frysinger51a43b42005-09-24 07:11:16 +000013871/* Command line options */
13872static int swapfs;
13873#ifdef ENABLE_SWAPFS
13874static int normalize_swapfs;
13875#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013876static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013877static int show_version_only;
13878static int verbose;
13879
13880static int replace_bad_blocks;
13881static int keep_bad_blocks;
13882static char *bad_blocks_file;
13883
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013884#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
13885
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013886static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013887{
13888 ext2_filsys fs = ctx->fs;
13889 int inodes, inodes_used, blocks, blocks_used;
13890 int dir_links;
13891 int num_files, num_links;
13892 int frag_percent;
13893
13894 dir_links = 2 * ctx->fs_directory_count - 1;
13895 num_files = ctx->fs_total_count - dir_links;
13896 num_links = ctx->fs_links_count - dir_links;
13897 inodes = fs->super->s_inodes_count;
13898 inodes_used = (fs->super->s_inodes_count -
13899 fs->super->s_free_inodes_count);
13900 blocks = fs->super->s_blocks_count;
13901 blocks_used = (fs->super->s_blocks_count -
13902 fs->super->s_free_blocks_count);
13903
13904 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
13905 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013906
Mike Frysinger51a43b42005-09-24 07:11:16 +000013907 if (!verbose) {
13908 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
13909 ctx->device_name, inodes_used, inodes,
13910 frag_percent / 10, frag_percent % 10,
13911 blocks_used, blocks);
13912 return;
13913 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013914 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
13915 100 * inodes_used / inodes);
13916 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
13917 P_E2("", "s", ctx->fs_fragmented),
13918 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013919 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
13920 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013921 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
13922 (int) ((long long) 100 * blocks_used / blocks));
13923 printf ("%8d bad block%s\n", P_E2("", "s", ctx->fs_badblocks_count));
13924 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
13925 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
13926 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
13927 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
13928 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
13929 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
13930 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
13931 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
13932 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
13933 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
13934 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013935}
13936
13937static void check_mount(e2fsck_t ctx)
13938{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013939 errcode_t retval;
13940 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013941
13942 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
13943 &ctx->mount_flags);
13944 if (retval) {
13945 com_err("ext2fs_check_if_mount", retval,
13946 _("while determining whether %s is mounted."),
13947 ctx->filesystem_name);
13948 return;
13949 }
13950
13951 /*
13952 * If the filesystem isn't mounted, or it's the root filesystem
13953 * and it's mounted read-only, then everything's fine.
13954 */
13955 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
13956 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
13957 (ctx->mount_flags & EXT2_MF_READONLY)))
13958 return;
13959
13960 if (ctx->options & E2F_OPT_READONLY) {
13961 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
13962 return;
13963 }
13964
13965 printf(_("%s is mounted. "), ctx->filesystem_name);
13966 if (!ctx->interactive)
13967 fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
13968 printf(_("\n\n\007\007\007\007WARNING!!! "
13969 "Running e2fsck on a mounted filesystem may cause\n"
13970 "SEVERE filesystem damage.\007\007\007\n\n"));
13971 cont = ask_yn(_("Do you really want to continue"), -1);
13972 if (!cont) {
13973 printf (_("check aborted.\n"));
13974 exit (0);
13975 }
13976 return;
13977}
13978
13979static int is_on_batt(void)
13980{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013981 FILE *f;
13982 DIR *d;
13983 char tmp[80], tmp2[80], fname[80];
13984 unsigned int acflag;
13985 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013986
13987 f = fopen("/proc/apm", "r");
13988 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013989 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013990 acflag = 1;
13991 fclose(f);
13992 return (acflag != 1);
13993 }
13994 d = opendir("/proc/acpi/ac_adapter");
13995 if (d) {
13996 while ((de=readdir(d)) != NULL) {
13997 if (!strncmp(".", de->d_name, 1))
13998 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013999 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000014000 de->d_name);
14001 f = fopen(fname, "r");
14002 if (!f)
14003 continue;
14004 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
14005 tmp[0] = 0;
14006 fclose(f);
14007 if (strncmp(tmp, "off-line", 8) == 0) {
14008 closedir(d);
14009 return 1;
14010 }
14011 }
14012 closedir(d);
14013 }
14014 return 0;
14015}
14016
14017/*
14018 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014019 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000014020 * message explaining why a check is being forced.
14021 */
14022static void check_if_skip(e2fsck_t ctx)
14023{
14024 ext2_filsys fs = ctx->fs;
14025 const char *reason = NULL;
14026 unsigned int reason_arg = 0;
14027 long next_check;
14028 int batt = is_on_batt();
14029 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014030
Mike Frysinger51a43b42005-09-24 07:11:16 +000014031 if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
14032 cflag || swapfs)
14033 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014034
Mike Frysinger51a43b42005-09-24 07:11:16 +000014035 if ((fs->super->s_state & EXT2_ERROR_FS) ||
14036 !ext2fs_test_valid(fs))
14037 reason = _(" contains a file system with errors");
14038 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
14039 reason = _(" was not cleanly unmounted");
14040 else if ((fs->super->s_max_mnt_count > 0) &&
14041 (fs->super->s_mnt_count >=
14042 (unsigned) fs->super->s_max_mnt_count)) {
14043 reason = _(" has been mounted %u times without being checked");
14044 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014045 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000014046 (unsigned) fs->super->s_max_mnt_count*2))
14047 reason = 0;
14048 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014049 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000014050 fs->super->s_checkinterval)) {
14051 reason = _(" has gone %u days without being checked");
14052 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014053 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000014054 fs->super->s_checkinterval*2))
14055 reason = 0;
14056 }
14057 if (reason) {
14058 fputs(ctx->device_name, stdout);
14059 printf(reason, reason_arg);
14060 fputs(_(", check forced.\n"), stdout);
14061 return;
14062 }
14063 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
14064 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
14065 fs->super->s_inodes_count,
14066 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
14067 fs->super->s_blocks_count);
14068 next_check = 100000;
14069 if (fs->super->s_max_mnt_count > 0) {
14070 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014071 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014072 next_check = 1;
14073 }
14074 if (fs->super->s_checkinterval &&
14075 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
14076 next_check = 1;
14077 if (next_check <= 5) {
14078 if (next_check == 1)
14079 fputs(_(" (check after next mount)"), stdout);
14080 else
14081 printf(_(" (check in %ld mounts)"), next_check);
14082 }
14083 fputc('\n', stdout);
14084 ext2fs_close(fs);
14085 ctx->fs = NULL;
14086 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014087 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014088}
14089
14090/*
14091 * For completion notice
14092 */
14093struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014094 int max_pass;
14095 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000014096};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014097static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000014098 5, { 0, 70, 90, 92, 95, 100 }
14099};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014100
Mike Frysinger51a43b42005-09-24 07:11:16 +000014101static char bar[128], spaces[128];
14102
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014103static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014104 int max)
14105{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014106 float percent;
14107
Mike Frysinger51a43b42005-09-24 07:11:16 +000014108 if (pass <= 0)
14109 return 0.0;
14110 if (pass > tbl->max_pass || max == 0)
14111 return 100.0;
14112 percent = ((float) curr) / ((float) max);
14113 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
14114 + tbl->table[pass-1]);
14115}
14116
Rob Landleydfba7412006-03-06 20:47:33 +000014117void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014118{
14119 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
14120 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014121
Mike Frysinger51a43b42005-09-24 07:11:16 +000014122 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
14123 ctx->stop_meta);
14124 fflush(stdout);
14125 ctx->flags &= ~E2F_FLAG_PROG_BAR;
14126}
14127
14128int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
14129 unsigned int dpynum)
14130{
14131 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014132 int i;
14133 unsigned int tick;
14134 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014135 int dpywidth;
14136 int fixed_percent;
14137
14138 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
14139 return 0;
14140
14141 /*
14142 * Calculate the new progress position. If the
14143 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014144 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000014145 */
14146 fixed_percent = (int) ((10 * percent) + 0.5);
14147 if (ctx->progress_last_percent == fixed_percent)
14148 return 0;
14149 ctx->progress_last_percent = fixed_percent;
14150
14151 /*
14152 * If we've already updated the spinner once within
14153 * the last 1/8th of a second, no point doing it
14154 * again.
14155 */
14156 gettimeofday(&tv, NULL);
14157 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
14158 if ((tick == ctx->progress_last_time) &&
14159 (fixed_percent != 0) && (fixed_percent != 1000))
14160 return 0;
14161 ctx->progress_last_time = tick;
14162
14163 /*
14164 * Advance the spinner, and note that the progress bar
14165 * will be on the screen
14166 */
14167 ctx->progress_pos = (ctx->progress_pos+1) & 3;
14168 ctx->flags |= E2F_FLAG_PROG_BAR;
14169
14170 dpywidth = 66 - strlen(label);
14171 dpywidth = 8 * (dpywidth / 8);
14172 if (dpynum)
14173 dpywidth -= 8;
14174
14175 i = ((percent * dpywidth) + 50) / 100;
14176 printf("%s%s: |%s%s", ctx->start_meta, label,
14177 bar + (sizeof(bar) - (i+1)),
14178 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
14179 if (fixed_percent == 1000)
14180 fputc('|', stdout);
14181 else
14182 fputc(spinner[ctx->progress_pos & 3], stdout);
14183 printf(" %4.1f%% ", percent);
14184 if (dpynum)
14185 printf("%u\r", dpynum);
14186 else
14187 fputs(" \r", stdout);
14188 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014189
Mike Frysinger51a43b42005-09-24 07:11:16 +000014190 if (fixed_percent == 1000)
14191 e2fsck_clear_progbar(ctx);
14192 fflush(stdout);
14193
14194 return 0;
14195}
14196
14197static int e2fsck_update_progress(e2fsck_t ctx, int pass,
14198 unsigned long cur, unsigned long max)
14199{
14200 char buf[80];
14201 float percent;
14202
14203 if (pass == 0)
14204 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014205
Mike Frysinger51a43b42005-09-24 07:11:16 +000014206 if (ctx->progress_fd) {
14207 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
14208 write(ctx->progress_fd, buf, strlen(buf));
14209 } else {
14210 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
14211 e2fsck_simple_progress(ctx, ctx->device_name,
14212 percent, 0);
14213 }
14214 return 0;
14215}
14216
Mike Frysinger51a43b42005-09-24 07:11:16 +000014217static void reserve_stdio_fds(void)
14218{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014219 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014220
14221 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000014222 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014223 if (fd > 2)
14224 break;
14225 if (fd < 0) {
14226 fprintf(stderr, _("ERROR: Couldn't open "
14227 "/dev/null (%s)\n"),
14228 strerror(errno));
14229 break;
14230 }
14231 }
14232 close(fd);
14233}
14234
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014235static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014236{
14237 e2fsck_t ctx = e2fsck_global_ctx;
14238
14239 if (!ctx)
14240 return;
14241
14242 ctx->progress = e2fsck_update_progress;
14243 ctx->progress_fd = 0;
14244}
14245
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014246static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014247{
14248 e2fsck_t ctx = e2fsck_global_ctx;
14249
14250 if (!ctx)
14251 return;
14252
14253 e2fsck_clear_progbar(ctx);
14254 ctx->progress = 0;
14255}
14256
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014257static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014258{
14259 e2fsck_t ctx = e2fsck_global_ctx;
14260
14261 if (!ctx)
14262 exit(FSCK_CANCELED);
14263
14264 ctx->flags |= E2F_FLAG_CANCEL;
14265}
Mike Frysinger51a43b42005-09-24 07:11:16 +000014266
14267static void parse_extended_opts(e2fsck_t ctx, const char *opts)
14268{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014269 char *buf, *token, *next, *p, *arg;
14270 int ea_ver;
14271 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014272
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014273 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014274 for (token = buf; token && *token; token = next) {
14275 p = strchr(token, ',');
14276 next = 0;
14277 if (p) {
14278 *p = 0;
14279 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014280 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014281 arg = strchr(token, '=');
14282 if (arg) {
14283 *arg = 0;
14284 arg++;
14285 }
14286 if (strcmp(token, "ea_ver") == 0) {
14287 if (!arg) {
14288 extended_usage++;
14289 continue;
14290 }
14291 ea_ver = strtoul(arg, &p, 0);
14292 if (*p ||
14293 ((ea_ver != 1) && (ea_ver != 2))) {
14294 fprintf(stderr,
14295 _("Invalid EA version.\n"));
14296 extended_usage++;
14297 continue;
14298 }
14299 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000014300 } else {
14301 fprintf(stderr, _("Unknown extended option: %s\n"),
14302 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014303 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000014304 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014305 }
14306 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014307 bb_error_msg_and_die(
14308 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000014309 "and may take an argument which\n"
14310 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000014311 "Valid extended options are:\n"
14312 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000014313 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014314}
Mike Frysinger51a43b42005-09-24 07:11:16 +000014315
14316
14317static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
14318{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014319 int flush = 0;
14320 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014321 e2fsck_t ctx;
14322 errcode_t retval;
14323 struct sigaction sa;
14324 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014325
14326 retval = e2fsck_allocate_context(&ctx);
14327 if (retval)
14328 return retval;
14329
14330 *ret_ctx = ctx;
14331
14332 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
14333 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
14334 if (isatty(0) && isatty(1)) {
14335 ctx->interactive = 1;
14336 } else {
14337 ctx->start_meta[0] = '\001';
14338 ctx->stop_meta[0] = '\002';
14339 }
14340 memset(bar, '=', sizeof(bar)-1);
14341 memset(spaces, ' ', sizeof(spaces)-1);
14342 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014343
Mike Frysinger51a43b42005-09-24 07:11:16 +000014344 if (argc && *argv)
14345 ctx->program_name = *argv;
14346 else
14347 ctx->program_name = "e2fsck";
14348 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
14349 switch (c) {
14350 case 'C':
14351 ctx->progress = e2fsck_update_progress;
14352 ctx->progress_fd = atoi(optarg);
14353 if (!ctx->progress_fd)
14354 break;
14355 /* Validate the file descriptor to avoid disasters */
14356 fd = dup(ctx->progress_fd);
14357 if (fd < 0) {
14358 fprintf(stderr,
14359 _("Error validating file descriptor %d: %s\n"),
14360 ctx->progress_fd,
14361 error_message(errno));
14362 fatal_error(ctx,
14363 _("Invalid completion information file descriptor"));
14364 } else
14365 close(fd);
14366 break;
14367 case 'D':
14368 ctx->options |= E2F_OPT_COMPRESS_DIRS;
14369 break;
14370 case 'E':
14371 extended_opts = optarg;
14372 break;
14373 case 'p':
14374 case 'a':
14375 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
14376 conflict_opt:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014377 fatal_error(ctx,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014378 _("Only one the options -p/-a, -n or -y may be specified."));
14379 }
14380 ctx->options |= E2F_OPT_PREEN;
14381 break;
14382 case 'n':
14383 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
14384 goto conflict_opt;
14385 ctx->options |= E2F_OPT_NO;
14386 break;
14387 case 'y':
14388 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
14389 goto conflict_opt;
14390 ctx->options |= E2F_OPT_YES;
14391 break;
14392 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000014393 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000014394 fprintf(stderr, _("The -t option is not "
14395 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000014396 break;
14397 case 'c':
14398 if (cflag++)
14399 ctx->options |= E2F_OPT_WRITECHECK;
14400 ctx->options |= E2F_OPT_CHECKBLOCKS;
14401 break;
14402 case 'r':
14403 /* What we do by default, anyway! */
14404 break;
14405 case 'b':
14406 ctx->use_superblock = atoi(optarg);
14407 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
14408 break;
14409 case 'B':
14410 ctx->blocksize = atoi(optarg);
14411 break;
14412 case 'I':
14413 ctx->inode_buffer_blocks = atoi(optarg);
14414 break;
14415 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014416 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014417 break;
14418 case 'P':
14419 ctx->process_inode_size = atoi(optarg);
14420 break;
14421 case 'L':
14422 replace_bad_blocks++;
14423 case 'l':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014424 bad_blocks_file = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014425 break;
14426 case 'd':
14427 ctx->options |= E2F_OPT_DEBUG;
14428 break;
14429 case 'f':
14430 ctx->options |= E2F_OPT_FORCE;
14431 break;
14432 case 'F':
14433 flush = 1;
14434 break;
14435 case 'v':
14436 verbose = 1;
14437 break;
14438 case 'V':
14439 show_version_only = 1;
14440 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014441 case 'N':
14442 ctx->device_name = optarg;
14443 break;
14444#ifdef ENABLE_SWAPFS
14445 case 's':
14446 normalize_swapfs = 1;
14447 case 'S':
14448 swapfs = 1;
14449 break;
14450#else
14451 case 's':
14452 case 'S':
14453 fprintf(stderr, _("Byte-swapping filesystems "
14454 "not compiled in this version "
14455 "of e2fsck\n"));
14456 exit(1);
14457#endif
14458 case 'k':
14459 keep_bad_blocks++;
14460 break;
14461 default:
14462 usage();
14463 }
14464 if (show_version_only)
14465 return 0;
14466 if (optind != argc - 1)
14467 usage();
14468 if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
14469 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
14470 ctx->options |= E2F_OPT_READONLY;
14471 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014472 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014473 *ctx->io_options++ = 0;
14474 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
14475 if (!ctx->filesystem_name) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014476 com_err(ctx->program_name, 0, _("Unable to resolve '%s'"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000014477 argv[optind]);
14478 fatal_error(ctx, 0);
14479 }
14480 if (extended_opts)
14481 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014482
Mike Frysinger51a43b42005-09-24 07:11:16 +000014483 if (flush) {
14484 fd = open(ctx->filesystem_name, O_RDONLY, 0);
14485 if (fd < 0) {
14486 com_err("open", errno,
14487 _("while opening %s for flushing"),
14488 ctx->filesystem_name);
14489 fatal_error(ctx, 0);
14490 }
14491 if ((retval = ext2fs_sync_device(fd, 1))) {
14492 com_err("ext2fs_sync_device", retval,
14493 _("while trying to flush %s"),
14494 ctx->filesystem_name);
14495 fatal_error(ctx, 0);
14496 }
14497 close(fd);
14498 }
14499#ifdef ENABLE_SWAPFS
14500 if (swapfs) {
14501 if (cflag || bad_blocks_file) {
14502 fprintf(stderr, _("Incompatible options not "
14503 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014504 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014505 }
14506 }
14507#endif
14508 if (cflag && bad_blocks_file) {
14509 fprintf(stderr, _("The -c and the -l/-L options may "
14510 "not be both used at the same time.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014511 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014512 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014513 /*
14514 * Set up signal action
14515 */
14516 memset(&sa, 0, sizeof(struct sigaction));
14517 sa.sa_handler = signal_cancel;
14518 sigaction(SIGINT, &sa, 0);
14519 sigaction(SIGTERM, &sa, 0);
14520#ifdef SA_RESTART
14521 sa.sa_flags = SA_RESTART;
14522#endif
14523 e2fsck_global_ctx = ctx;
14524 sa.sa_handler = signal_progress_on;
14525 sigaction(SIGUSR1, &sa, 0);
14526 sa.sa_handler = signal_progress_off;
14527 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014528
14529 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014530 if (cflag)
14531 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000014532 return 0;
14533}
14534
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014535static const char my_ver_string[] = E2FSPROGS_VERSION;
14536static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014537
Mike Frysinger51a43b42005-09-24 07:11:16 +000014538int e2fsck_main (int argc, char *argv[])
14539{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014540 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014541 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014542 ext2_filsys fs = 0;
14543 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014544 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014545 const char *lib_ver_date;
14546 int my_ver, lib_ver;
14547 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014548 struct problem_context pctx;
14549 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014550
Mike Frysinger51a43b42005-09-24 07:11:16 +000014551 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000014552
Mike Frysinger51a43b42005-09-24 07:11:16 +000014553 my_ver = ext2fs_parse_version_string(my_ver_string);
14554 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
14555 if (my_ver > lib_ver) {
14556 fprintf( stderr, _("Error: ext2fs library version "
14557 "out of date!\n"));
14558 show_version_only++;
14559 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014560
Mike Frysinger51a43b42005-09-24 07:11:16 +000014561 retval = PRS(argc, argv, &ctx);
14562 if (retval) {
14563 com_err("e2fsck", retval,
14564 _("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014565 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014566 }
14567 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014568
Mike Frysinger51a43b42005-09-24 07:11:16 +000014569 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
14570 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
14571 my_ver_date);
14572
14573 if (show_version_only) {
14574 fprintf(stderr, _("\tUsing %s, %s\n"),
14575 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014576 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014577 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014578
Mike Frysinger51a43b42005-09-24 07:11:16 +000014579 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014580
Mike Frysinger51a43b42005-09-24 07:11:16 +000014581 if (!(ctx->options & E2F_OPT_PREEN) &&
14582 !(ctx->options & E2F_OPT_NO) &&
14583 !(ctx->options & E2F_OPT_YES)) {
14584 if (!ctx->interactive)
14585 fatal_error(ctx,
14586 _("need terminal for interactive repairs"));
14587 }
14588 ctx->superblock = ctx->use_superblock;
14589restart:
14590#ifdef CONFIG_TESTIO_DEBUG
14591 io_ptr = test_io_manager;
14592 test_io_backing_manager = unix_io_manager;
14593#else
14594 io_ptr = unix_io_manager;
14595#endif
14596 flags = 0;
14597 if ((ctx->options & E2F_OPT_READONLY) == 0)
14598 flags |= EXT2_FLAG_RW;
14599
14600 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014601 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014602 flags, ctx->superblock, ctx->blocksize,
14603 io_ptr, &fs);
14604 } else if (ctx->superblock) {
14605 int blocksize;
14606 for (blocksize = EXT2_MIN_BLOCK_SIZE;
14607 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014608 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014609 ctx->io_options, flags,
14610 ctx->superblock, blocksize,
14611 io_ptr, &fs);
14612 if (!retval)
14613 break;
14614 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014615 } else
14616 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014617 flags, 0, 0, io_ptr, &fs);
14618 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
14619 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
14620 ((retval == EXT2_ET_BAD_MAGIC) ||
14621 ((retval == 0) && ext2fs_check_desc(fs)))) {
14622 if (!fs || (fs->group_desc_count > 1)) {
14623 printf(_("%s trying backup blocks...\n"),
14624 retval ? _("Couldn't find ext2 superblock,") :
14625 _("Group descriptors look bad..."));
14626 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
14627 if (fs)
14628 ext2fs_close(fs);
14629 goto restart;
14630 }
14631 }
14632 if (retval) {
14633 com_err(ctx->program_name, retval, _("while trying to open %s"),
14634 ctx->filesystem_name);
14635 if (retval == EXT2_ET_REV_TOO_HIGH) {
14636 printf(_("The filesystem revision is apparently "
14637 "too high for this version of e2fsck.\n"
14638 "(Or the filesystem superblock "
14639 "is corrupt)\n\n"));
14640 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
14641 } else if (retval == EXT2_ET_SHORT_READ)
14642 printf(_("Could this be a zero-length partition?\n"));
14643 else if ((retval == EPERM) || (retval == EACCES))
14644 printf(_("You must have %s access to the "
14645 "filesystem or be root\n"),
14646 (ctx->options & E2F_OPT_READONLY) ?
14647 "r/o" : "r/w");
14648 else if (retval == ENXIO)
14649 printf(_("Possibly non-existent or swap device?\n"));
14650#ifdef EROFS
14651 else if (retval == EROFS)
14652 printf(_("Disk write-protected; use the -n option "
14653 "to do a read-only\n"
14654 "check of the device.\n"));
14655#endif
14656 else
14657 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
14658 fatal_error(ctx, 0);
14659 }
14660 ctx->fs = fs;
14661 fs->priv_data = ctx;
14662 sb = fs->super;
14663 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
14664 com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
14665 _("while trying to open %s"),
14666 ctx->filesystem_name);
14667 get_newer:
14668 fatal_error(ctx, _("Get a newer version of e2fsck!"));
14669 }
14670
14671 /*
14672 * Set the device name, which is used whenever we print error
14673 * or informational messages to the user.
14674 */
14675 if (ctx->device_name == 0 &&
14676 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014677 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014678 sizeof(sb->s_volume_name));
14679 }
14680 if (ctx->device_name == 0)
14681 ctx->device_name = ctx->filesystem_name;
14682
14683 /*
14684 * Make sure the ext3 superblock fields are consistent.
14685 */
14686 retval = e2fsck_check_ext3_journal(ctx);
14687 if (retval) {
14688 com_err(ctx->program_name, retval,
14689 _("while checking ext3 journal for %s"),
14690 ctx->device_name);
14691 fatal_error(ctx, 0);
14692 }
14693
14694 /*
14695 * Check to see if we need to do ext3-style recovery. If so,
14696 * do it, and then restart the fsck.
14697 */
14698 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
14699 if (ctx->options & E2F_OPT_READONLY) {
14700 printf(_("Warning: skipping journal recovery "
14701 "because doing a read-only filesystem "
14702 "check.\n"));
14703 io_channel_flush(ctx->fs->io);
14704 } else {
14705 if (ctx->flags & E2F_FLAG_RESTARTED) {
14706 /*
14707 * Whoops, we attempted to run the
14708 * journal twice. This should never
14709 * happen, unless the hardware or
14710 * device driver is being bogus.
14711 */
14712 com_err(ctx->program_name, 0,
14713 _("unable to set superblock flags on %s\n"), ctx->device_name);
14714 fatal_error(ctx, 0);
14715 }
14716 retval = e2fsck_run_ext3_journal(ctx);
14717 if (retval) {
14718 com_err(ctx->program_name, retval,
14719 _("while recovering ext3 journal of %s"),
14720 ctx->device_name);
14721 fatal_error(ctx, 0);
14722 }
14723 ext2fs_close(ctx->fs);
14724 ctx->fs = 0;
14725 ctx->flags |= E2F_FLAG_RESTARTED;
14726 goto restart;
14727 }
14728 }
14729
14730 /*
14731 * Check for compatibility with the feature sets. We need to
14732 * be more stringent than ext2fs_open().
14733 */
14734 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
14735 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
14736 com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
14737 "(%s)", ctx->device_name);
14738 goto get_newer;
14739 }
14740 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
14741 com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
14742 "(%s)", ctx->device_name);
14743 goto get_newer;
14744 }
14745#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000014746 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000014747 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
14748 com_err(ctx->program_name, 0,
14749 _("Warning: compression support is experimental.\n"));
14750#endif
14751#ifndef ENABLE_HTREE
14752 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
14753 com_err(ctx->program_name, 0,
14754 _("E2fsck not compiled with HTREE support,\n\t"
14755 "but filesystem %s has HTREE directories.\n"),
14756 ctx->device_name);
14757 goto get_newer;
14758 }
14759#endif
14760
14761 /*
14762 * If the user specified a specific superblock, presumably the
14763 * master superblock has been trashed. So we mark the
14764 * superblock as dirty, so it can be written out.
14765 */
14766 if (ctx->superblock &&
14767 !(ctx->options & E2F_OPT_READONLY))
14768 ext2fs_mark_super_dirty(fs);
14769
14770 /*
14771 * We only update the master superblock because (a) paranoia;
14772 * we don't want to corrupt the backup superblocks, and (b) we
14773 * don't need to update the mount count and last checked
14774 * fields in the backup superblock (the kernel doesn't
14775 * update the backup superblocks anyway).
14776 */
14777 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
14778
14779 ehandler_init(fs->io);
14780
14781 if (ctx->superblock)
14782 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
14783 ext2fs_mark_valid(fs);
14784 check_super_block(ctx);
14785 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
14786 fatal_error(ctx, 0);
14787 check_if_skip(ctx);
14788 if (bad_blocks_file)
14789 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
14790 else if (cflag)
14791 read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
14792 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
14793 fatal_error(ctx, 0);
14794#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000014795
14796#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000014797#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000014798#else
Rob Landley8b606342006-01-24 02:38:28 +000014799#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000014800#endif
14801
14802
Mike Frysinger51a43b42005-09-24 07:11:16 +000014803 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000014804 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000014805 fprintf(stderr, _("%s: Filesystem byte order "
14806 "already normalized.\n"), ctx->device_name);
14807 fatal_error(ctx, 0);
14808 }
14809 }
14810 if (swapfs) {
14811 swap_filesys(ctx);
14812 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
14813 fatal_error(ctx, 0);
14814 }
14815#endif
14816
14817 /*
14818 * Mark the system as valid, 'til proven otherwise
14819 */
14820 ext2fs_mark_valid(fs);
14821
14822 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
14823 if (retval) {
14824 com_err(ctx->program_name, retval,
14825 _("while reading bad blocks inode"));
14826 preenhalt(ctx);
14827 printf(_("This doesn't bode well,"
14828 " but we'll try to go on...\n"));
14829 }
14830
14831 run_result = e2fsck_run(ctx);
14832 e2fsck_clear_progbar(ctx);
14833 if (run_result == E2F_FLAG_RESTART) {
14834 printf(_("Restarting e2fsck from the beginning...\n"));
14835 retval = e2fsck_reset_context(ctx);
14836 if (retval) {
14837 com_err(ctx->program_name, retval,
14838 _("while resetting context"));
14839 fatal_error(ctx, 0);
14840 }
14841 ext2fs_close(fs);
14842 goto restart;
14843 }
14844 if (run_result & E2F_FLAG_CANCEL) {
14845 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
14846 ctx->device_name : ctx->filesystem_name);
14847 exit_value |= FSCK_CANCELED;
14848 }
14849 if (run_result & E2F_FLAG_ABORT)
14850 fatal_error(ctx, _("aborted"));
14851
Rob Landley3e72c592006-04-06 22:49:04 +000014852 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000014853 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014854 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014855 if (!(ctx->options & E2F_OPT_PREEN))
14856 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
14857 ctx->device_name);
14858 if (ctx->mount_flags & EXT2_MF_ISROOT) {
14859 printf(_("%s: ***** REBOOT LINUX *****\n"),
14860 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014861 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014862 }
14863 }
14864 if (!ext2fs_test_valid(fs)) {
14865 printf(_("\n%s: ********** WARNING: Filesystem still has "
14866 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014867 exit_value |= EXIT_UNCORRECTED;
14868 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014869 }
14870 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014871 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014872 else {
14873 show_stats(ctx);
14874 if (!(ctx->options & E2F_OPT_READONLY)) {
14875 if (ext2fs_test_valid(fs)) {
14876 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014877 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014878 sb->s_state = EXT2_VALID_FS;
14879 } else
14880 sb->s_state &= ~EXT2_VALID_FS;
14881 sb->s_mnt_count = 0;
14882 sb->s_lastcheck = time(NULL);
14883 ext2fs_mark_super_dirty(fs);
14884 }
14885 }
14886
14887 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014888
Mike Frysinger51a43b42005-09-24 07:11:16 +000014889 ext2fs_close(fs);
14890 ctx->fs = NULL;
14891 free(ctx->filesystem_name);
14892 free(ctx->journal_name);
14893 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014894
Mike Frysinger51a43b42005-09-24 07:11:16 +000014895 return exit_value;
14896}