blob: 52069fe60096cf0081ac9a6c315d182f39b20859 [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 +000079#ifdef RESOURCE_TRACK
80static void print_resource_track(const char *desc,
81 struct resource_track *track);
82static void init_resource_track(struct resource_track *track);
83#endif
84static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
85 struct ext2_inode * inode, const char * proc);
86static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
87 struct ext2_inode * inode, const char * proc);
88#ifdef MTRACE
89static void mtrace_print(char *mesg);
90#endif
91static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
92 const char *name, io_manager manager);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000093
94/* unix.c */
95static void e2fsck_clear_progbar(e2fsck_t ctx);
96static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
97 float percent, unsigned int dpynum);
Rob Landley43ac8882006-04-01 00:40:33 +000098
99
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000100/*
101 * problem.h --- e2fsck problem error codes
102 */
103
104typedef __u32 problem_t;
105
106struct problem_context {
107 errcode_t errcode;
108 ext2_ino_t ino, ino2, dir;
109 struct ext2_inode *inode;
110 struct ext2_dir_entry *dirent;
111 blk_t blk, blk2;
112 e2_blkcnt_t blkcount;
113 int group;
114 __u64 num;
115 const char *str;
116};
117
118/*
119 * We define a set of "latch groups"; these are problems which are
120 * handled as a set. The user answers once for a particular latch
121 * group.
122 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000123#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
124#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
125#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
126#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
127#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
128#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
129#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
130#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
131#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000132#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
133
134#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
135
136/*
137 * Latch group descriptor flags
138 */
139#define PRL_YES 0x0001 /* Answer yes */
140#define PRL_NO 0x0002 /* Answer no */
141#define PRL_LATCHED 0x0004 /* The latch group is latched */
142#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
143
144#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
145
146/*
147 * Pre-Pass 1 errors
148 */
149
150/* Block bitmap not in group */
151#define PR_0_BB_NOT_GROUP 0x000001
152
153/* Inode bitmap not in group */
154#define PR_0_IB_NOT_GROUP 0x000002
155
156/* Inode table not in group */
157#define PR_0_ITABLE_NOT_GROUP 0x000003
158
159/* Superblock corrupt */
160#define PR_0_SB_CORRUPT 0x000004
161
162/* Filesystem size is wrong */
163#define PR_0_FS_SIZE_WRONG 0x000005
164
165/* Fragments not supported */
166#define PR_0_NO_FRAGMENTS 0x000006
167
168/* Bad blocks_per_group */
169#define PR_0_BLOCKS_PER_GROUP 0x000007
170
171/* Bad first_data_block */
172#define PR_0_FIRST_DATA_BLOCK 0x000008
173
174/* Adding UUID to filesystem */
175#define PR_0_ADD_UUID 0x000009
176
177/* Relocate hint */
178#define PR_0_RELOCATE_HINT 0x00000A
179
180/* Miscellaneous superblock corruption */
181#define PR_0_MISC_CORRUPT_SUPER 0x00000B
182
183/* Error determing physical device size of filesystem */
184#define PR_0_GETSIZE_ERROR 0x00000C
185
186/* Inode count in the superblock incorrect */
187#define PR_0_INODE_COUNT_WRONG 0x00000D
188
189/* The Hurd does not support the filetype feature */
190#define PR_0_HURD_CLEAR_FILETYPE 0x00000E
191
192/* Journal inode is invalid */
193#define PR_0_JOURNAL_BAD_INODE 0x00000F
194
195/* The external journal has multiple filesystems (which we can't handle yet) */
196#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010
197
198/* Can't find external journal */
199#define PR_0_CANT_FIND_JOURNAL 0x000011
200
201/* External journal has bad superblock */
202#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012
203
204/* Superblock has a bad journal UUID */
205#define PR_0_JOURNAL_BAD_UUID 0x000013
206
207/* Journal has an unknown superblock type */
208#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014
209
210/* Journal superblock is corrupt */
211#define PR_0_JOURNAL_BAD_SUPER 0x000015
212
213/* Journal superblock is corrupt */
214#define PR_0_JOURNAL_HAS_JOURNAL 0x000016
215
216/* Superblock has recovery flag set but no journal */
217#define PR_0_JOURNAL_RECOVER_SET 0x000017
218
219/* Journal has data, but recovery flag is clear */
220#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018
221
222/* Ask if we should clear the journal */
223#define PR_0_JOURNAL_RESET_JOURNAL 0x000019
224
225/* Filesystem revision is 0, but feature flags are set */
226#define PR_0_FS_REV_LEVEL 0x00001A
227
228/* Clearing orphan inode */
229#define PR_0_ORPHAN_CLEAR_INODE 0x000020
230
231/* Illegal block found in orphaned inode */
232#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021
233
234/* Already cleared block found in orphaned inode */
235#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022
236
237/* Illegal orphan inode in superblock */
238#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023
239
240/* Illegal inode in orphaned inode list */
241#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024
242
243/* Journal has unsupported read-only feature - abort */
244#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025
245
246/* Journal has unsupported incompatible feature - abort */
247#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026
248
249/* Journal has unsupported version number */
250#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027
251
252/* Moving journal to hidden file */
253#define PR_0_MOVE_JOURNAL 0x000028
254
255/* Error moving journal */
256#define PR_0_ERR_MOVE_JOURNAL 0x000029
257
258/* Clearing V2 journal superblock */
259#define PR_0_CLEAR_V2_JOURNAL 0x00002A
260
261/* Run journal anyway */
262#define PR_0_JOURNAL_RUN 0x00002B
263
264/* Run journal anyway by default */
265#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C
266
267/* Backup journal inode blocks */
268#define PR_0_BACKUP_JNL 0x00002D
269
270/* Reserved blocks w/o resize_inode */
271#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E
272
273/* Resize_inode not enabled, but resize inode is non-zero */
274#define PR_0_CLEAR_RESIZE_INODE 0x00002F
275
276/* Resize inode invalid */
277#define PR_0_RESIZE_INODE_INVALID 0x000030
278
279/*
280 * Pass 1 errors
281 */
282
283/* Pass 1: Checking inodes, blocks, and sizes */
284#define PR_1_PASS_HEADER 0x010000
285
286/* Root directory is not an inode */
287#define PR_1_ROOT_NO_DIR 0x010001
288
289/* Root directory has dtime set */
290#define PR_1_ROOT_DTIME 0x010002
291
292/* Reserved inode has bad mode */
293#define PR_1_RESERVED_BAD_MODE 0x010003
294
295/* Deleted inode has zero dtime */
296#define PR_1_ZERO_DTIME 0x010004
297
298/* Inode in use, but dtime set */
299#define PR_1_SET_DTIME 0x010005
300
301/* Zero-length directory */
302#define PR_1_ZERO_LENGTH_DIR 0x010006
303
304/* Block bitmap conflicts with some other fs block */
305#define PR_1_BB_CONFLICT 0x010007
306
307/* Inode bitmap conflicts with some other fs block */
308#define PR_1_IB_CONFLICT 0x010008
309
310/* Inode table conflicts with some other fs block */
311#define PR_1_ITABLE_CONFLICT 0x010009
312
313/* Block bitmap is on a bad block */
314#define PR_1_BB_BAD_BLOCK 0x01000A
315
316/* Inode bitmap is on a bad block */
317#define PR_1_IB_BAD_BLOCK 0x01000B
318
319/* Inode has incorrect i_size */
320#define PR_1_BAD_I_SIZE 0x01000C
321
322/* Inode has incorrect i_blocks */
323#define PR_1_BAD_I_BLOCKS 0x01000D
324
325/* Illegal block number in inode */
326#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E
327
328/* Block number overlaps fs metadata */
329#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F
330
331/* Inode has illegal blocks (latch question) */
332#define PR_1_INODE_BLOCK_LATCH 0x010010
333
334/* Too many bad blocks in inode */
335#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011
336
337/* Illegal block number in bad block inode */
338#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012
339
340/* Bad block inode has illegal blocks (latch question) */
341#define PR_1_INODE_BBLOCK_LATCH 0x010013
342
343/* Duplicate or bad blocks in use! */
344#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014
345
346/* Bad block used as bad block indirect block */
347#define PR_1_BBINODE_BAD_METABLOCK 0x010015
348
349/* Inconsistency can't be fixed prompt */
350#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016
351
352/* Bad primary block */
353#define PR_1_BAD_PRIMARY_BLOCK 0x010017
354
355/* Bad primary block prompt */
356#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018
357
358/* Bad primary superblock */
359#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019
360
361/* Bad primary block group descriptors */
362#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A
363
364/* Bad superblock in group */
365#define PR_1_BAD_SUPERBLOCK 0x01001B
366
367/* Bad block group descriptors in group */
368#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C
369
370/* Block claimed for no reason */
371#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D
372
373/* Error allocating blocks for relocating metadata */
374#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E
375
376/* Error allocating block buffer during relocation process */
377#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F
378
379/* Relocating metadata group information from X to Y */
380#define PR_1_RELOC_FROM_TO 0x010020
381
382/* Relocating metatdata group information to X */
383#define PR_1_RELOC_TO 0x010021
384
385/* Block read error during relocation process */
386#define PR_1_RELOC_READ_ERR 0x010022
387
388/* Block write error during relocation process */
389#define PR_1_RELOC_WRITE_ERR 0x010023
390
391/* Error allocating inode bitmap */
392#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024
393
394/* Error allocating block bitmap */
395#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025
396
397/* Error allocating icount structure */
398#define PR_1_ALLOCATE_ICOUNT 0x010026
399
400/* Error allocating dbcount */
401#define PR_1_ALLOCATE_DBCOUNT 0x010027
402
403/* Error while scanning inodes */
404#define PR_1_ISCAN_ERROR 0x010028
405
406/* Error while iterating over blocks */
407#define PR_1_BLOCK_ITERATE 0x010029
408
409/* Error while storing inode count information */
410#define PR_1_ICOUNT_STORE 0x01002A
411
412/* Error while storing directory block information */
413#define PR_1_ADD_DBLOCK 0x01002B
414
415/* Error while reading inode (for clearing) */
416#define PR_1_READ_INODE 0x01002C
417
418/* Suppress messages prompt */
419#define PR_1_SUPPRESS_MESSAGES 0x01002D
420
421/* Imagic flag set on an inode when filesystem doesn't support it */
422#define PR_1_SET_IMAGIC 0x01002F
423
424/* Immutable flag set on a device or socket inode */
425#define PR_1_SET_IMMUTABLE 0x010030
426
427/* Compression flag set on a non-compressed filesystem */
428#define PR_1_COMPR_SET 0x010031
429
430/* Non-zero size on on device, fifo or socket inode */
431#define PR_1_SET_NONZSIZE 0x010032
432
433/* Filesystem revision is 0, but feature flags are set */
434#define PR_1_FS_REV_LEVEL 0x010033
435
436/* Journal inode not in use, needs clearing */
437#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034
438
439/* Journal inode has wrong mode */
440#define PR_1_JOURNAL_BAD_MODE 0x010035
441
442/* Inode that was part of orphan linked list */
443#define PR_1_LOW_DTIME 0x010036
444
445/* Latch question which asks how to deal with low dtime inodes */
446#define PR_1_ORPHAN_LIST_REFUGEES 0x010037
447
448/* Error allocating refcount structure */
449#define PR_1_ALLOCATE_REFCOUNT 0x010038
450
451/* Error reading Extended Attribute block */
452#define PR_1_READ_EA_BLOCK 0x010039
453
454/* Invalid Extended Attribute block */
455#define PR_1_BAD_EA_BLOCK 0x01003A
456
457/* Error reading Extended Attribute block while fixing refcount -- abort */
458#define PR_1_EXTATTR_READ_ABORT 0x01003B
459
460/* Extended attribute reference count incorrect */
461#define PR_1_EXTATTR_REFCOUNT 0x01003C
462
463/* Error writing Extended Attribute block while fixing refcount */
464#define PR_1_EXTATTR_WRITE 0x01003D
465
466/* Multiple EA blocks not supported */
467#define PR_1_EA_MULTI_BLOCK 0x01003E
468
469/* Error allocating EA region allocation structure */
470#define PR_1_EA_ALLOC_REGION 0x01003F
471
472/* Error EA allocation collision */
473#define PR_1_EA_ALLOC_COLLISION 0x010040
474
475/* Bad extended attribute name */
476#define PR_1_EA_BAD_NAME 0x010041
477
478/* Bad extended attribute value */
479#define PR_1_EA_BAD_VALUE 0x010042
480
481/* Inode too big (latch question) */
482#define PR_1_INODE_TOOBIG 0x010043
483
484/* Directory too big */
485#define PR_1_TOOBIG_DIR 0x010044
486
487/* Regular file too big */
488#define PR_1_TOOBIG_REG 0x010045
489
490/* Symlink too big */
491#define PR_1_TOOBIG_SYMLINK 0x010046
492
493/* INDEX_FL flag set on a non-HTREE filesystem */
494#define PR_1_HTREE_SET 0x010047
495
496/* INDEX_FL flag set on a non-directory */
497#define PR_1_HTREE_NODIR 0x010048
498
499/* Invalid root node in HTREE directory */
500#define PR_1_HTREE_BADROOT 0x010049
501
502/* Unsupported hash version in HTREE directory */
503#define PR_1_HTREE_HASHV 0x01004A
504
505/* Incompatible flag in HTREE root node */
506#define PR_1_HTREE_INCOMPAT 0x01004B
507
508/* HTREE too deep */
509#define PR_1_HTREE_DEPTH 0x01004C
510
511/* Bad block has indirect block that conflicts with filesystem block */
512#define PR_1_BB_FS_BLOCK 0x01004D
513
514/* Resize inode failed */
515#define PR_1_RESIZE_INODE_CREATE 0x01004E
516
517/* inode->i_size is too long */
518#define PR_1_EXTRA_ISIZE 0x01004F
519
520/* attribute name is too long */
521#define PR_1_ATTR_NAME_LEN 0x010050
522
523/* wrong EA value offset */
524#define PR_1_ATTR_VALUE_OFFSET 0x010051
525
526/* wrong EA blocknumber */
527#define PR_1_ATTR_VALUE_BLOCK 0x010052
528
529/* wrong EA value size */
530#define PR_1_ATTR_VALUE_SIZE 0x010053
531
532/* wrong EA hash value */
533#define PR_1_ATTR_HASH 0x010054
534
535/*
536 * Pass 1b errors
537 */
538
539/* Pass 1B: Rescan for duplicate/bad blocks */
540#define PR_1B_PASS_HEADER 0x011000
541
542/* Duplicate/bad block(s) header */
543#define PR_1B_DUP_BLOCK_HEADER 0x011001
544
545/* Duplicate/bad block(s) in inode */
546#define PR_1B_DUP_BLOCK 0x011002
547
548/* Duplicate/bad block(s) end */
549#define PR_1B_DUP_BLOCK_END 0x011003
550
551/* Error while scanning inodes */
552#define PR_1B_ISCAN_ERROR 0x011004
553
554/* Error allocating inode bitmap */
555#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005
556
557/* Error while iterating over blocks */
558#define PR_1B_BLOCK_ITERATE 0x0110006
559
560/* Error adjusting EA refcount */
561#define PR_1B_ADJ_EA_REFCOUNT 0x0110007
562
563
564/* Pass 1C: Scan directories for inodes with dup blocks. */
565#define PR_1C_PASS_HEADER 0x012000
566
567
568/* Pass 1D: Reconciling duplicate blocks */
569#define PR_1D_PASS_HEADER 0x013000
570
571/* File has duplicate blocks */
572#define PR_1D_DUP_FILE 0x013001
573
574/* List of files sharing duplicate blocks */
575#define PR_1D_DUP_FILE_LIST 0x013002
576
577/* File sharing blocks with filesystem metadata */
578#define PR_1D_SHARE_METADATA 0x013003
579
580/* Report of how many duplicate/bad inodes */
581#define PR_1D_NUM_DUP_INODES 0x013004
582
583/* Duplicated blocks already reassigned or cloned. */
584#define PR_1D_DUP_BLOCKS_DEALT 0x013005
585
586/* Clone duplicate/bad blocks? */
587#define PR_1D_CLONE_QUESTION 0x013006
588
589/* Delete file? */
590#define PR_1D_DELETE_QUESTION 0x013007
591
592/* Couldn't clone file (error) */
593#define PR_1D_CLONE_ERROR 0x013008
594
595/*
596 * Pass 2 errors
597 */
598
599/* Pass 2: Checking directory structure */
600#define PR_2_PASS_HEADER 0x020000
601
602/* Bad inode number for '.' */
603#define PR_2_BAD_INODE_DOT 0x020001
604
605/* Directory entry has bad inode number */
606#define PR_2_BAD_INO 0x020002
607
608/* Directory entry has deleted or unused inode */
609#define PR_2_UNUSED_INODE 0x020003
610
611/* Directry entry is link to '.' */
612#define PR_2_LINK_DOT 0x020004
613
614/* Directory entry points to inode now located in a bad block */
615#define PR_2_BB_INODE 0x020005
616
617/* Directory entry contains a link to a directory */
618#define PR_2_LINK_DIR 0x020006
619
620/* Directory entry contains a link to the root directry */
621#define PR_2_LINK_ROOT 0x020007
622
623/* Directory entry has illegal characters in its name */
624#define PR_2_BAD_NAME 0x020008
625
626/* Missing '.' in directory inode */
627#define PR_2_MISSING_DOT 0x020009
628
629/* Missing '..' in directory inode */
630#define PR_2_MISSING_DOT_DOT 0x02000A
631
632/* First entry in directory inode doesn't contain '.' */
633#define PR_2_1ST_NOT_DOT 0x02000B
634
635/* Second entry in directory inode doesn't contain '..' */
636#define PR_2_2ND_NOT_DOT_DOT 0x02000C
637
638/* i_faddr should be zero */
639#define PR_2_FADDR_ZERO 0x02000D
640
641/* i_file_acl should be zero */
642#define PR_2_FILE_ACL_ZERO 0x02000E
643
644/* i_dir_acl should be zero */
645#define PR_2_DIR_ACL_ZERO 0x02000F
646
647/* i_frag should be zero */
648#define PR_2_FRAG_ZERO 0x020010
649
650/* i_fsize should be zero */
651#define PR_2_FSIZE_ZERO 0x020011
652
653/* inode has bad mode */
654#define PR_2_BAD_MODE 0x020012
655
656/* directory corrupted */
657#define PR_2_DIR_CORRUPTED 0x020013
658
659/* filename too long */
660#define PR_2_FILENAME_LONG 0x020014
661
662/* Directory inode has a missing block (hole) */
663#define PR_2_DIRECTORY_HOLE 0x020015
664
665/* '.' is not NULL terminated */
666#define PR_2_DOT_NULL_TERM 0x020016
667
668/* '..' is not NULL terminated */
669#define PR_2_DOT_DOT_NULL_TERM 0x020017
670
671/* Illegal character device in inode */
672#define PR_2_BAD_CHAR_DEV 0x020018
673
674/* Illegal block device in inode */
675#define PR_2_BAD_BLOCK_DEV 0x020019
676
677/* Duplicate '.' entry */
678#define PR_2_DUP_DOT 0x02001A
679
680/* Duplicate '..' entry */
681#define PR_2_DUP_DOT_DOT 0x02001B
682
683/* Internal error: couldn't find dir_info */
684#define PR_2_NO_DIRINFO 0x02001C
685
686/* Final rec_len is wrong */
687#define PR_2_FINAL_RECLEN 0x02001D
688
689/* Error allocating icount structure */
690#define PR_2_ALLOCATE_ICOUNT 0x02001E
691
692/* Error iterating over directory blocks */
693#define PR_2_DBLIST_ITERATE 0x02001F
694
695/* Error reading directory block */
696#define PR_2_READ_DIRBLOCK 0x020020
697
698/* Error writing directory block */
699#define PR_2_WRITE_DIRBLOCK 0x020021
700
701/* Error allocating new directory block */
702#define PR_2_ALLOC_DIRBOCK 0x020022
703
704/* Error deallocating inode */
705#define PR_2_DEALLOC_INODE 0x020023
706
707/* Directory entry for '.' is big. Split? */
708#define PR_2_SPLIT_DOT 0x020024
709
710/* Illegal FIFO */
711#define PR_2_BAD_FIFO 0x020025
712
713/* Illegal socket */
714#define PR_2_BAD_SOCKET 0x020026
715
716/* Directory filetype not set */
717#define PR_2_SET_FILETYPE 0x020027
718
719/* Directory filetype incorrect */
720#define PR_2_BAD_FILETYPE 0x020028
721
722/* Directory filetype set when it shouldn't be */
723#define PR_2_CLEAR_FILETYPE 0x020029
724
725/* Directory filename can't be zero-length */
726#define PR_2_NULL_NAME 0x020030
727
728/* Invalid symlink */
729#define PR_2_INVALID_SYMLINK 0x020031
730
731/* i_file_acl (extended attribute) is bad */
732#define PR_2_FILE_ACL_BAD 0x020032
733
734/* Filesystem contains large files, but has no such flag in sb */
735#define PR_2_FEATURE_LARGE_FILES 0x020033
736
737/* Node in HTREE directory not referenced */
738#define PR_2_HTREE_NOTREF 0x020034
739
740/* Node in HTREE directory referenced twice */
741#define PR_2_HTREE_DUPREF 0x020035
742
743/* Node in HTREE directory has bad min hash */
744#define PR_2_HTREE_MIN_HASH 0x020036
745
746/* Node in HTREE directory has bad max hash */
747#define PR_2_HTREE_MAX_HASH 0x020037
748
749/* Clear invalid HTREE directory */
750#define PR_2_HTREE_CLEAR 0x020038
751
752/* Clear the htree flag forcibly */
753/* #define PR_2_HTREE_FCLR 0x020039 */
754
755/* Bad block in htree interior node */
756#define PR_2_HTREE_BADBLK 0x02003A
757
758/* Error adjusting EA refcount */
759#define PR_2_ADJ_EA_REFCOUNT 0x02003B
760
761/* Invalid HTREE root node */
762#define PR_2_HTREE_BAD_ROOT 0x02003C
763
764/* Invalid HTREE limit */
765#define PR_2_HTREE_BAD_LIMIT 0x02003D
766
767/* Invalid HTREE count */
768#define PR_2_HTREE_BAD_COUNT 0x02003E
769
770/* HTREE interior node has out-of-order hashes in table */
771#define PR_2_HTREE_HASH_ORDER 0x02003F
772
773/* Node in HTREE directory has bad depth */
774#define PR_2_HTREE_BAD_DEPTH 0x020040
775
776/* Duplicate directory entry found */
777#define PR_2_DUPLICATE_DIRENT 0x020041
778
779/* Non-unique filename found */
780#define PR_2_NON_UNIQUE_FILE 0x020042
781
782/* Duplicate directory entry found */
783#define PR_2_REPORT_DUP_DIRENT 0x020043
784
785/*
786 * Pass 3 errors
787 */
788
789/* Pass 3: Checking directory connectivity */
790#define PR_3_PASS_HEADER 0x030000
791
792/* Root inode not allocated */
793#define PR_3_NO_ROOT_INODE 0x030001
794
795/* No room in lost+found */
796#define PR_3_EXPAND_LF_DIR 0x030002
797
798/* Unconnected directory inode */
799#define PR_3_UNCONNECTED_DIR 0x030003
800
801/* /lost+found not found */
802#define PR_3_NO_LF_DIR 0x030004
803
804/* .. entry is incorrect */
805#define PR_3_BAD_DOT_DOT 0x030005
806
807/* Bad or non-existent /lost+found. Cannot reconnect */
808#define PR_3_NO_LPF 0x030006
809
810/* Could not expand /lost+found */
811#define PR_3_CANT_EXPAND_LPF 0x030007
812
813/* Could not reconnect inode */
814#define PR_3_CANT_RECONNECT 0x030008
815
816/* Error while trying to find /lost+found */
817#define PR_3_ERR_FIND_LPF 0x030009
818
819/* Error in ext2fs_new_block while creating /lost+found */
820#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A
821
822/* Error in ext2fs_new_inode while creating /lost+found */
823#define PR_3_ERR_LPF_NEW_INODE 0x03000B
824
825/* Error in ext2fs_new_dir_block while creating /lost+found */
826#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C
827
828/* Error while writing directory block for /lost+found */
829#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D
830
831/* Error while adjusting inode count */
832#define PR_3_ADJUST_INODE 0x03000E
833
834/* Couldn't fix parent directory -- error */
835#define PR_3_FIX_PARENT_ERR 0x03000F
836
837/* Couldn't fix parent directory -- couldn't find it */
838#define PR_3_FIX_PARENT_NOFIND 0x030010
839
840/* Error allocating inode bitmap */
841#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011
842
843/* Error creating root directory */
844#define PR_3_CREATE_ROOT_ERROR 0x030012
845
846/* Error creating lost and found directory */
847#define PR_3_CREATE_LPF_ERROR 0x030013
848
849/* Root inode is not directory; aborting */
850#define PR_3_ROOT_NOT_DIR_ABORT 0x030014
851
852/* Cannot proceed without a root inode. */
853#define PR_3_NO_ROOT_INODE_ABORT 0x030015
854
855/* Internal error: couldn't find dir_info */
856#define PR_3_NO_DIRINFO 0x030016
857
858/* Lost+found is not a directory */
859#define PR_3_LPF_NOTDIR 0x030017
860
861/*
862 * Pass 3a --- rehashing diretories
863 */
864/* Pass 3a: Reindexing directories */
865#define PR_3A_PASS_HEADER 0x031000
866
867/* Error iterating over directories */
868#define PR_3A_OPTIMIZE_ITER 0x031001
869
870/* Error rehash directory */
871#define PR_3A_OPTIMIZE_DIR_ERR 0x031002
872
873/* Rehashing dir header */
874#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003
875
876/* Rehashing directory %d */
877#define PR_3A_OPTIMIZE_DIR 0x031004
878
879/* Rehashing dir end */
880#define PR_3A_OPTIMIZE_DIR_END 0x031005
881
882/*
883 * Pass 4 errors
884 */
885
886/* Pass 4: Checking reference counts */
887#define PR_4_PASS_HEADER 0x040000
888
889/* Unattached zero-length inode */
890#define PR_4_ZERO_LEN_INODE 0x040001
891
892/* Unattached inode */
893#define PR_4_UNATTACHED_INODE 0x040002
894
895/* Inode ref count wrong */
896#define PR_4_BAD_REF_COUNT 0x040003
897
898/* Inconsistent inode count information cached */
899#define PR_4_INCONSISTENT_COUNT 0x040004
900
901/*
902 * Pass 5 errors
903 */
904
905/* Pass 5: Checking group summary information */
906#define PR_5_PASS_HEADER 0x050000
907
908/* Padding at end of inode bitmap is not set. */
909#define PR_5_INODE_BMAP_PADDING 0x050001
910
911/* Padding at end of block bitmap is not set. */
912#define PR_5_BLOCK_BMAP_PADDING 0x050002
913
914/* Block bitmap differences header */
915#define PR_5_BLOCK_BITMAP_HEADER 0x050003
916
917/* Block not used, but marked in bitmap */
918#define PR_5_BLOCK_UNUSED 0x050004
919
920/* Block used, but not marked used in bitmap */
921#define PR_5_BLOCK_USED 0x050005
922
923/* Block bitmap differences end */
924#define PR_5_BLOCK_BITMAP_END 0x050006
925
926/* Inode bitmap differences header */
927#define PR_5_INODE_BITMAP_HEADER 0x050007
928
929/* Inode not used, but marked in bitmap */
930#define PR_5_INODE_UNUSED 0x050008
931
932/* Inode used, but not marked used in bitmap */
933#define PR_5_INODE_USED 0x050009
934
935/* Inode bitmap differences end */
936#define PR_5_INODE_BITMAP_END 0x05000A
937
938/* Free inodes count for group wrong */
939#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B
940
941/* Directories count for group wrong */
942#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C
943
944/* Free inodes count wrong */
945#define PR_5_FREE_INODE_COUNT 0x05000D
946
947/* Free blocks count for group wrong */
948#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E
949
950/* Free blocks count wrong */
951#define PR_5_FREE_BLOCK_COUNT 0x05000F
952
953/* Programming error: bitmap endpoints don't match */
954#define PR_5_BMAP_ENDPOINTS 0x050010
955
956/* Internal error: fudging end of bitmap */
957#define PR_5_FUDGE_BITMAP_ERROR 0x050011
958
959/* Error copying in replacement inode bitmap */
960#define PR_5_COPY_IBITMAP_ERROR 0x050012
961
962/* Error copying in replacement block bitmap */
963#define PR_5_COPY_BBITMAP_ERROR 0x050013
964
965/* Block range not used, but marked in bitmap */
966#define PR_5_BLOCK_RANGE_UNUSED 0x050014
967
968/* Block range used, but not marked used in bitmap */
969#define PR_5_BLOCK_RANGE_USED 0x050015
970
971/* Inode range not used, but marked in bitmap */
972#define PR_5_INODE_RANGE_UNUSED 0x050016
973
974/* Inode rangeused, but not marked used in bitmap */
975#define PR_5_INODE_RANGE_USED 0x050017
976
977/*
978 * Function declarations
979 */
980static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
981static int end_problem_latch(e2fsck_t ctx, int mask);
982static int set_latch_flags(int mask, int setflags, int clearflags);
983static void clear_problem_context(struct problem_context *ctx);
984
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000985/*
986 * Dictionary Abstract Data Type
987 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
988 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000989 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
990 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000991 */
992
993#ifndef DICT_H
994#define DICT_H
995
996/*
997 * Blurb for inclusion into C++ translation units
998 */
999
1000typedef unsigned long dictcount_t;
1001#define DICTCOUNT_T_MAX ULONG_MAX
1002
1003/*
1004 * The dictionary is implemented as a red-black tree
1005 */
1006
1007typedef enum { dnode_red, dnode_black } dnode_color_t;
1008
1009typedef struct dnode_t {
1010 struct dnode_t *dict_left;
1011 struct dnode_t *dict_right;
1012 struct dnode_t *dict_parent;
1013 dnode_color_t dict_color;
1014 const void *dict_key;
1015 void *dict_data;
1016} dnode_t;
1017
1018typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001019typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001020
1021typedef struct dict_t {
1022 dnode_t dict_nilnode;
1023 dictcount_t dict_nodecount;
1024 dictcount_t dict_maxcount;
1025 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001026 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001027 int dict_dupes;
1028} dict_t;
1029
1030typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
1031
1032typedef struct dict_load_t {
1033 dict_t *dict_dictptr;
1034 dnode_t dict_nilnode;
1035} dict_load_t;
1036
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001037#define dict_count(D) ((D)->dict_nodecount)
1038#define dnode_get(N) ((N)->dict_data)
1039#define dnode_getkey(N) ((N)->dict_key)
1040
1041#endif
1042
1043/*
1044 * Compatibility header file for e2fsck which should be included
1045 * instead of linux/jfs.h
1046 *
1047 * Copyright (C) 2000 Stephen C. Tweedie
1048 */
1049
1050/*
1051 * Pull in the definition of the e2fsck context structure
1052 */
1053
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001054struct buffer_head {
1055 char b_data[8192];
1056 e2fsck_t b_ctx;
1057 io_channel b_io;
1058 int b_size;
1059 blk_t b_blocknr;
1060 int b_dirty;
1061 int b_uptodate;
1062 int b_err;
1063};
1064
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001065
1066#define K_DEV_FS 1
1067#define K_DEV_JOURNAL 2
1068
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001069#define lock_buffer(bh) do {} while(0)
1070#define unlock_buffer(bh) do {} while(0)
1071#define buffer_req(bh) 1
1072#define do_readahead(journal, start) do {} while(0)
1073
1074static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
1075
1076typedef struct {
1077 int object_length;
1078} kmem_cache_t;
1079
1080#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001081
1082/*
1083 * We use the standard libext2fs portability tricks for inline
1084 * functions.
1085 */
1086
1087static _INLINE_ kmem_cache_t * do_cache_create(int len)
1088{
1089 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001090
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001091 new_cache = malloc(sizeof(*new_cache));
1092 if (new_cache)
1093 new_cache->object_length = len;
1094 return new_cache;
1095}
1096
1097static _INLINE_ void do_cache_destroy(kmem_cache_t *cache)
1098{
1099 free(cache);
1100}
1101
1102/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001103 * badblocks.c --- replace/append bad blocks to the bad block inode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001104 */
1105
1106static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
1107 void *priv_data);
1108
1109
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001110static void invalid_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t blk)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001111{
1112 printf(_("Bad block %u out of range; ignored.\n"), blk);
1113 return;
1114}
1115
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001116static void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001117 int replace_bad_blocks)
1118{
1119 ext2_filsys fs = ctx->fs;
1120 errcode_t retval;
1121 badblocks_list bb_list = 0;
1122 FILE *f;
1123 char buf[1024];
1124
1125 e2fsck_read_bitmaps(ctx);
1126
1127 /*
1128 * Make sure the bad block inode is sane. If there are any
1129 * illegal blocks, clear them.
1130 */
1131 retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
1132 check_bb_inode_blocks, 0);
1133 if (retval) {
1134 com_err("ext2fs_block_iterate", retval,
1135 _("while sanity checking the bad blocks inode"));
1136 goto fatal;
1137 }
1138
1139 /*
1140 * If we're appending to the bad blocks inode, read in the
1141 * current bad blocks.
1142 */
1143 if (!replace_bad_blocks) {
1144 retval = ext2fs_read_bb_inode(fs, &bb_list);
1145 if (retval) {
1146 com_err("ext2fs_read_bb_inode", retval,
1147 _("while reading the bad blocks inode"));
1148 goto fatal;
1149 }
1150 }
1151
1152 /*
1153 * Now read in the bad blocks from the file; if
1154 * bad_blocks_file is null, then try to run the badblocks
1155 * command.
1156 */
1157 if (bad_blocks_file) {
1158 f = fopen(bad_blocks_file, "r");
1159 if (!f) {
1160 com_err("read_bad_blocks_file", errno,
1161 _("while trying to open %s"), bad_blocks_file);
1162 goto fatal;
1163 }
1164 } else {
1165 sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
1166 (ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
1167 (ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
1168 fs->device_name, fs->super->s_blocks_count);
1169 f = popen(buf, "r");
1170 if (!f) {
1171 com_err("read_bad_blocks_file", errno,
1172 _("while trying popen '%s'"), buf);
1173 goto fatal;
1174 }
1175 }
1176 retval = ext2fs_read_bb_FILE(fs, f, &bb_list, invalid_block);
1177 if (bad_blocks_file)
1178 fclose(f);
1179 else
1180 pclose(f);
1181 if (retval) {
1182 com_err("ext2fs_read_bb_FILE", retval,
1183 _("while reading in list of bad blocks from file"));
1184 goto fatal;
1185 }
1186
1187 /*
1188 * Finally, update the bad blocks from the bad_block_map
1189 */
1190 retval = ext2fs_update_bb_inode(fs, bb_list);
1191 if (retval) {
1192 com_err("ext2fs_update_bb_inode", retval,
1193 _("while updating bad block inode"));
1194 goto fatal;
1195 }
1196
1197 ext2fs_badblocks_list_free(bb_list);
1198 return;
1199
1200fatal:
1201 ctx->flags |= E2F_FLAG_ABORT;
1202 return;
1203
1204}
1205
1206static int check_bb_inode_blocks(ext2_filsys fs,
1207 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001208 int blockcnt FSCK_ATTR((unused)),
1209 void *priv_data FSCK_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001210{
1211 if (!*block_nr)
1212 return 0;
1213
1214 /*
1215 * If the block number is outrageous, clear it and ignore it.
1216 */
1217 if (*block_nr >= fs->super->s_blocks_count ||
1218 *block_nr < fs->super->s_first_data_block) {
1219 printf(_("Warning illegal block %u found in bad block inode. Cleared.\n"), *block_nr);
1220 *block_nr = 0;
1221 return BLOCK_CHANGED;
1222 }
1223
1224 return 0;
1225}
1226
1227/*
1228 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001229 */
1230
1231
1232/*
1233 * These macros provide short convenient names for structure members,
1234 * which are embellished with dict_ prefixes so that they are
1235 * properly confined to the documented namespace. It's legal for a
1236 * program which uses dict to define, for instance, a macro called ``parent''.
1237 * Such a macro would interfere with the dnode_t struct definition.
1238 * In general, highly portable and reusable C modules which expose their
1239 * structures need to confine structure member names to well-defined spaces.
1240 * The resulting identifiers aren't necessarily convenient to use, nor
1241 * readable, in the implementation, however!
1242 */
1243
1244#define left dict_left
1245#define right dict_right
1246#define parent dict_parent
1247#define color dict_color
1248#define key dict_key
1249#define data dict_data
1250
1251#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001252#define maxcount dict_maxcount
1253#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001254#define dupes dict_dupes
1255
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001256#define dict_root(D) ((D)->nilnode.left)
1257#define dict_nil(D) (&(D)->nilnode)
1258#define DICT_DEPTH_MAX 64
1259
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001260static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001261
1262/*
1263 * Perform a ``left rotation'' adjustment on the tree. The given node P and
1264 * its right child C are rearranged so that the P instead becomes the left
1265 * child of C. The left subtree of C is inherited as the new right subtree
1266 * for P. The ordering of the keys within the tree is thus preserved.
1267 */
1268
1269static void rotate_left(dnode_t *upper)
1270{
1271 dnode_t *lower, *lowleft, *upparent;
1272
1273 lower = upper->right;
1274 upper->right = lowleft = lower->left;
1275 lowleft->parent = upper;
1276
1277 lower->parent = upparent = upper->parent;
1278
1279 /* don't need to check for root node here because root->parent is
1280 the sentinel nil node, and root->parent->left points back to root */
1281
1282 if (upper == upparent->left) {
1283 upparent->left = lower;
1284 } else {
1285 assert (upper == upparent->right);
1286 upparent->right = lower;
1287 }
1288
1289 lower->left = upper;
1290 upper->parent = lower;
1291}
1292
1293/*
1294 * This operation is the ``mirror'' image of rotate_left. It is
1295 * the same procedure, but with left and right interchanged.
1296 */
1297
1298static void rotate_right(dnode_t *upper)
1299{
1300 dnode_t *lower, *lowright, *upparent;
1301
1302 lower = upper->left;
1303 upper->left = lowright = lower->right;
1304 lowright->parent = upper;
1305
1306 lower->parent = upparent = upper->parent;
1307
1308 if (upper == upparent->right) {
1309 upparent->right = lower;
1310 } else {
1311 assert (upper == upparent->left);
1312 upparent->left = lower;
1313 }
1314
1315 lower->right = upper;
1316 upper->parent = lower;
1317}
1318
1319/*
1320 * Do a postorder traversal of the tree rooted at the specified
1321 * node and free everything under it. Used by dict_free().
1322 */
1323
1324static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
1325{
1326 if (node == nil)
1327 return;
1328 free_nodes(dict, node->left, nil);
1329 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001330 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001331}
1332
1333/*
1334 * Verify that the tree contains the given node. This is done by
1335 * traversing all of the nodes and comparing their pointers to the
1336 * given pointer. Returns 1 if the node is found, otherwise
1337 * returns zero. It is intended for debugging purposes.
1338 */
1339
1340static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
1341{
1342 if (root != nil) {
1343 return root == node
1344 || verify_dict_has_node(nil, root->left, node)
1345 || verify_dict_has_node(nil, root->right, node);
1346 }
1347 return 0;
1348}
1349
1350
1351/*
1352 * Select a different set of node allocator routines.
1353 */
1354
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001355static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001356{
1357 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001358 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001359}
1360
1361/*
1362 * Free all the nodes in the dictionary by using the dictionary's
1363 * installed free routine. The dictionary is emptied.
1364 */
1365
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001366static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001367{
1368 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
1369 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001370 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001371 dict->nilnode.left = &dict->nilnode;
1372 dict->nilnode.right = &dict->nilnode;
1373}
1374
1375/*
1376 * Initialize a user-supplied dictionary object.
1377 */
1378
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001379static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001380{
1381 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001382 dict->dict_freenode = dnode_free;
1383 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001384 dict->maxcount = maxcount;
1385 dict->nilnode.left = &dict->nilnode;
1386 dict->nilnode.right = &dict->nilnode;
1387 dict->nilnode.parent = &dict->nilnode;
1388 dict->nilnode.color = dnode_black;
1389 dict->dupes = 0;
1390 return dict;
1391}
1392
1393/*
1394 * Locate a node in the dictionary having the given key.
1395 * If the node is not found, a null a pointer is returned (rather than
1396 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
1397 * located node is returned.
1398 */
1399
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001400static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001401{
1402 dnode_t *root = dict_root(dict);
1403 dnode_t *nil = dict_nil(dict);
1404 dnode_t *saved;
1405 int result;
1406
1407 /* simple binary search adapted for trees that contain duplicate keys */
1408
1409 while (root != nil) {
1410 result = dict->compare(key, root->key);
1411 if (result < 0)
1412 root = root->left;
1413 else if (result > 0)
1414 root = root->right;
1415 else {
1416 if (!dict->dupes) { /* no duplicates, return match */
1417 return root;
1418 } else { /* could be dupes, find leftmost one */
1419 do {
1420 saved = root;
1421 root = root->left;
1422 while (root != nil && dict->compare(key, root->key))
1423 root = root->right;
1424 } while (root != nil);
1425 return saved;
1426 }
1427 }
1428 }
1429
1430 return NULL;
1431}
1432
1433/*
1434 * Insert a node into the dictionary. The node should have been
1435 * initialized with a data field. All other fields are ignored.
1436 * The behavior is undefined if the user attempts to insert into
1437 * a dictionary that is already full (for which the dict_isfull()
1438 * function returns true).
1439 */
1440
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001441static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001442{
1443 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
1444 dnode_t *parent = nil, *uncle, *grandpa;
1445 int result = -1;
1446
1447 node->key = key;
1448
1449 /* basic binary tree insert */
1450
1451 while (where != nil) {
1452 parent = where;
1453 result = dict->compare(key, where->key);
1454 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
1455 assert (dict->dupes || result != 0);
1456 if (result < 0)
1457 where = where->left;
1458 else
1459 where = where->right;
1460 }
1461
1462 assert (where == nil);
1463
1464 if (result < 0)
1465 parent->left = node;
1466 else
1467 parent->right = node;
1468
1469 node->parent = parent;
1470 node->left = nil;
1471 node->right = nil;
1472
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001473 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001474
1475 /* red black adjustments */
1476
1477 node->color = dnode_red;
1478
1479 while (parent->color == dnode_red) {
1480 grandpa = parent->parent;
1481 if (parent == grandpa->left) {
1482 uncle = grandpa->right;
1483 if (uncle->color == dnode_red) { /* red parent, red uncle */
1484 parent->color = dnode_black;
1485 uncle->color = dnode_black;
1486 grandpa->color = dnode_red;
1487 node = grandpa;
1488 parent = grandpa->parent;
1489 } else { /* red parent, black uncle */
1490 if (node == parent->right) {
1491 rotate_left(parent);
1492 parent = node;
1493 assert (grandpa == parent->parent);
1494 /* rotation between parent and child preserves grandpa */
1495 }
1496 parent->color = dnode_black;
1497 grandpa->color = dnode_red;
1498 rotate_right(grandpa);
1499 break;
1500 }
1501 } else { /* symmetric cases: parent == parent->parent->right */
1502 uncle = grandpa->left;
1503 if (uncle->color == dnode_red) {
1504 parent->color = dnode_black;
1505 uncle->color = dnode_black;
1506 grandpa->color = dnode_red;
1507 node = grandpa;
1508 parent = grandpa->parent;
1509 } else {
1510 if (node == parent->left) {
1511 rotate_right(parent);
1512 parent = node;
1513 assert (grandpa == parent->parent);
1514 }
1515 parent->color = dnode_black;
1516 grandpa->color = dnode_red;
1517 rotate_left(grandpa);
1518 break;
1519 }
1520 }
1521 }
1522
1523 dict_root(dict)->color = dnode_black;
1524
1525}
1526
1527/*
1528 * Allocate a node using the dictionary's allocator routine, give it
1529 * the data item.
1530 */
1531
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001532static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001533{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001534 dnode->data = data;
1535 dnode->parent = NULL;
1536 dnode->left = NULL;
1537 dnode->right = NULL;
1538 return dnode;
1539}
1540
1541static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
1542{
1543 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001544
1545 if (node) {
1546 dnode_init(node, data);
1547 dict_insert(dict, node, key);
1548 return 1;
1549 }
1550 return 0;
1551}
1552
1553/*
1554 * Return the node with the lowest (leftmost) key. If the dictionary is empty
1555 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
1556 */
1557
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001558static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001559{
1560 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
1561
1562 if (root != nil)
1563 while ((left = root->left) != nil)
1564 root = left;
1565
1566 return (root == nil) ? NULL : root;
1567}
1568
1569/*
1570 * Return the given node's successor node---the node which has the
1571 * next key in the the left to right ordering. If the node has
1572 * no successor, a null pointer is returned rather than a pointer to
1573 * the nil node.
1574 */
1575
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001576static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001577{
1578 dnode_t *nil = dict_nil(dict), *parent, *left;
1579
1580 if (curr->right != nil) {
1581 curr = curr->right;
1582 while ((left = curr->left) != nil)
1583 curr = left;
1584 return curr;
1585 }
1586
1587 parent = curr->parent;
1588
1589 while (parent != nil && curr == parent->right) {
1590 curr = parent;
1591 parent = curr->parent;
1592 }
1593
1594 return (parent == nil) ? NULL : parent;
1595}
1596
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001597
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001598static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001599{
1600 free(node);
1601}
1602
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001603
1604#undef left
1605#undef right
1606#undef parent
1607#undef color
1608#undef key
1609#undef data
1610
1611#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001612#undef maxcount
1613#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001614#undef dupes
1615
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001616
1617/*
1618 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001619 */
1620
1621/*
1622 * This subroutine is called during pass1 to create a directory info
1623 * entry. During pass1, the passed-in parent is 0; it will get filled
1624 * in during pass2.
1625 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001626static 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 +00001627{
1628 struct dir_info *dir;
1629 int i, j;
1630 ext2_ino_t num_dirs;
1631 errcode_t retval;
1632 unsigned long old_size;
1633
1634#if 0
1635 printf("add_dir_info for inode %lu...\n", ino);
1636#endif
1637 if (!ctx->dir_info) {
1638 ctx->dir_info_count = 0;
1639 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
1640 if (retval)
1641 num_dirs = 1024; /* Guess */
1642 ctx->dir_info_size = num_dirs + 10;
1643 ctx->dir_info = (struct dir_info *)
1644 e2fsck_allocate_memory(ctx, ctx->dir_info_size
1645 * sizeof (struct dir_info),
1646 "directory map");
1647 }
1648
1649 if (ctx->dir_info_count >= ctx->dir_info_size) {
1650 old_size = ctx->dir_info_size * sizeof(struct dir_info);
1651 ctx->dir_info_size += 10;
1652 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
1653 sizeof(struct dir_info),
1654 &ctx->dir_info);
1655 if (retval) {
1656 ctx->dir_info_size -= 10;
1657 return;
1658 }
1659 }
1660
1661 /*
1662 * Normally, add_dir_info is called with each inode in
1663 * sequential order; but once in a while (like when pass 3
1664 * needs to recreate the root directory or lost+found
1665 * directory) it is called out of order. In those cases, we
1666 * need to move the dir_info entries down to make room, since
1667 * the dir_info array needs to be sorted by inode number for
1668 * get_dir_info()'s sake.
1669 */
1670 if (ctx->dir_info_count &&
1671 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
1672 for (i = ctx->dir_info_count-1; i > 0; i--)
1673 if (ctx->dir_info[i-1].ino < ino)
1674 break;
1675 dir = &ctx->dir_info[i];
1676 if (dir->ino != ino)
1677 for (j = ctx->dir_info_count++; j > i; j--)
1678 ctx->dir_info[j] = ctx->dir_info[j-1];
1679 } else
1680 dir = &ctx->dir_info[ctx->dir_info_count++];
1681
1682 dir->ino = ino;
1683 dir->dotdot = parent;
1684 dir->parent = parent;
1685}
1686
1687/*
1688 * get_dir_info() --- given an inode number, try to find the directory
1689 * information entry for it.
1690 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001691static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001692{
1693 int low, high, mid;
1694
1695 low = 0;
1696 high = ctx->dir_info_count-1;
1697 if (!ctx->dir_info)
1698 return 0;
1699 if (ino == ctx->dir_info[low].ino)
1700 return &ctx->dir_info[low];
1701 if (ino == ctx->dir_info[high].ino)
1702 return &ctx->dir_info[high];
1703
1704 while (low < high) {
1705 mid = (low+high)/2;
1706 if (mid == low || mid == high)
1707 break;
1708 if (ino == ctx->dir_info[mid].ino)
1709 return &ctx->dir_info[mid];
1710 if (ino < ctx->dir_info[mid].ino)
1711 high = mid;
1712 else
1713 low = mid;
1714 }
1715 return 0;
1716}
1717
1718/*
1719 * Free the dir_info structure when it isn't needed any more.
1720 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001721static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001722{
Rob Landleye7c43b62006-03-01 16:39:45 +00001723 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001724 ctx->dir_info_size = 0;
1725 ctx->dir_info_count = 0;
1726}
1727
1728/*
1729 * Return the count of number of directories in the dir_info structure
1730 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001731static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001732{
1733 return ctx->dir_info_count;
1734}
1735
1736/*
1737 * A simple interator function
1738 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001739static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001740{
1741 if (*control >= ctx->dir_info_count)
1742 return 0;
1743
1744 return(ctx->dir_info + (*control)++);
1745}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001746
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001747/*
1748 * dirinfo.c --- maintains the directory information table for e2fsck.
1749 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001750 */
1751
1752#ifdef ENABLE_HTREE
1753
1754/*
1755 * This subroutine is called during pass1 to create a directory info
1756 * entry. During pass1, the passed-in parent is 0; it will get filled
1757 * in during pass2.
1758 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001759static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001760{
1761 struct dx_dir_info *dir;
1762 int i, j;
1763 errcode_t retval;
1764 unsigned long old_size;
1765
1766#if 0
1767 printf("add_dx_dir_info for inode %lu...\n", ino);
1768#endif
1769 if (!ctx->dx_dir_info) {
1770 ctx->dx_dir_info_count = 0;
1771 ctx->dx_dir_info_size = 100; /* Guess */
1772 ctx->dx_dir_info = (struct dx_dir_info *)
1773 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
1774 * sizeof (struct dx_dir_info),
1775 "directory map");
1776 }
1777
1778 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
1779 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
1780 ctx->dx_dir_info_size += 10;
1781 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
1782 sizeof(struct dx_dir_info),
1783 &ctx->dx_dir_info);
1784 if (retval) {
1785 ctx->dx_dir_info_size -= 10;
1786 return;
1787 }
1788 }
1789
1790 /*
1791 * Normally, add_dx_dir_info is called with each inode in
1792 * sequential order; but once in a while (like when pass 3
1793 * needs to recreate the root directory or lost+found
1794 * directory) it is called out of order. In those cases, we
1795 * need to move the dx_dir_info entries down to make room, since
1796 * the dx_dir_info array needs to be sorted by inode number for
1797 * get_dx_dir_info()'s sake.
1798 */
1799 if (ctx->dx_dir_info_count &&
1800 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
1801 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
1802 if (ctx->dx_dir_info[i-1].ino < ino)
1803 break;
1804 dir = &ctx->dx_dir_info[i];
1805 if (dir->ino != ino)
1806 for (j = ctx->dx_dir_info_count++; j > i; j--)
1807 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
1808 } else
1809 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
1810
1811 dir->ino = ino;
1812 dir->numblocks = num_blocks;
1813 dir->hashversion = 0;
1814 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
1815 * sizeof (struct dx_dirblock_info),
1816 "dx_block info array");
1817
1818}
1819
1820/*
1821 * get_dx_dir_info() --- given an inode number, try to find the directory
1822 * information entry for it.
1823 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001824static 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 +00001825{
1826 int low, high, mid;
1827
1828 low = 0;
1829 high = ctx->dx_dir_info_count-1;
1830 if (!ctx->dx_dir_info)
1831 return 0;
1832 if (ino == ctx->dx_dir_info[low].ino)
1833 return &ctx->dx_dir_info[low];
1834 if (ino == ctx->dx_dir_info[high].ino)
1835 return &ctx->dx_dir_info[high];
1836
1837 while (low < high) {
1838 mid = (low+high)/2;
1839 if (mid == low || mid == high)
1840 break;
1841 if (ino == ctx->dx_dir_info[mid].ino)
1842 return &ctx->dx_dir_info[mid];
1843 if (ino < ctx->dx_dir_info[mid].ino)
1844 high = mid;
1845 else
1846 low = mid;
1847 }
1848 return 0;
1849}
1850
1851/*
1852 * Free the dx_dir_info structure when it isn't needed any more.
1853 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001854static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001855{
1856 int i;
1857 struct dx_dir_info *dir;
1858
1859 if (ctx->dx_dir_info) {
1860 dir = ctx->dx_dir_info;
1861 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +00001862 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001863 }
1864 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001865 }
1866 ctx->dx_dir_info_size = 0;
1867 ctx->dx_dir_info_count = 0;
1868}
1869
1870/*
1871 * A simple interator function
1872 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001873static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001874{
1875 if (*control >= ctx->dx_dir_info_count)
1876 return 0;
1877
1878 return(ctx->dx_dir_info + (*control)++);
1879}
1880
1881#endif /* ENABLE_HTREE */
1882/*
1883 * e2fsck.c - a consistency checker for the new extended file system.
1884 *
Mike Frysinger51a43b42005-09-24 07:11:16 +00001885 */
1886
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001887/*
1888 * This function allocates an e2fsck context
1889 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001890static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001891{
1892 e2fsck_t context;
1893 errcode_t retval;
1894
1895 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
1896 if (retval)
1897 return retval;
1898
1899 memset(context, 0, sizeof(struct e2fsck_struct));
1900
1901 context->process_inode_size = 256;
1902 context->ext_attr_ver = 2;
1903
1904 *ret = context;
1905 return 0;
1906}
1907
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001908struct ea_refcount_el {
1909 blk_t ea_blk;
1910 int ea_count;
1911};
1912
1913struct ea_refcount {
1914 blk_t count;
1915 blk_t size;
1916 blk_t cursor;
1917 struct ea_refcount_el *list;
1918};
1919
1920static void ea_refcount_free(ext2_refcount_t refcount)
1921{
1922 if (!refcount)
1923 return;
1924
Rob Landleye7c43b62006-03-01 16:39:45 +00001925 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001926 ext2fs_free_mem(&refcount);
1927}
1928
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001929/*
1930 * This function resets an e2fsck context; it is called when e2fsck
1931 * needs to be restarted.
1932 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001933static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001934{
1935 ctx->flags = 0;
1936 ctx->lost_and_found = 0;
1937 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00001938 ext2fs_free_inode_bitmap(ctx->inode_used_map);
1939 ctx->inode_used_map = 0;
1940 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
1941 ctx->inode_dir_map = 0;
1942 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
1943 ctx->inode_reg_map = 0;
1944 ext2fs_free_block_bitmap(ctx->block_found_map);
1945 ctx->block_found_map = 0;
1946 ext2fs_free_icount(ctx->inode_link_info);
1947 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001948 if (ctx->journal_io) {
1949 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1950 io_channel_close(ctx->journal_io);
1951 ctx->journal_io = 0;
1952 }
Rob Landleye7c43b62006-03-01 16:39:45 +00001953 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001954 ext2fs_free_dblist(ctx->fs->dblist);
1955 ctx->fs->dblist = 0;
1956 }
1957 e2fsck_free_dir_info(ctx);
1958#ifdef ENABLE_HTREE
1959 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +00001960#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001961 ea_refcount_free(ctx->refcount);
1962 ctx->refcount = 0;
1963 ea_refcount_free(ctx->refcount_extra);
1964 ctx->refcount_extra = 0;
1965 ext2fs_free_block_bitmap(ctx->block_dup_map);
1966 ctx->block_dup_map = 0;
1967 ext2fs_free_block_bitmap(ctx->block_ea_map);
1968 ctx->block_ea_map = 0;
1969 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
1970 ctx->inode_bb_map = 0;
1971 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
1972 ctx->inode_bad_map = 0;
1973 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
1974 ctx->inode_imagic_map = 0;
1975 ext2fs_u32_list_free(ctx->dirs_to_hash);
1976 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001977
1978 /*
1979 * Clear the array of invalid meta-data flags
1980 */
Rob Landleye7c43b62006-03-01 16:39:45 +00001981 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
1982 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
1983 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001984
1985 /* Clear statistic counters */
1986 ctx->fs_directory_count = 0;
1987 ctx->fs_regular_count = 0;
1988 ctx->fs_blockdev_count = 0;
1989 ctx->fs_chardev_count = 0;
1990 ctx->fs_links_count = 0;
1991 ctx->fs_symlinks_count = 0;
1992 ctx->fs_fast_symlinks_count = 0;
1993 ctx->fs_fifo_count = 0;
1994 ctx->fs_total_count = 0;
1995 ctx->fs_badblocks_count = 0;
1996 ctx->fs_sockets_count = 0;
1997 ctx->fs_ind_count = 0;
1998 ctx->fs_dind_count = 0;
1999 ctx->fs_tind_count = 0;
2000 ctx->fs_fragmented = 0;
2001 ctx->large_files = 0;
2002
2003 /* Reset the superblock to the user's requested value */
2004 ctx->superblock = ctx->use_superblock;
2005
2006 return 0;
2007}
2008
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002009static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002010{
2011 if (!ctx)
2012 return;
2013
2014 e2fsck_reset_context(ctx);
2015 if (ctx->blkid)
2016 blkid_put_cache(ctx->blkid);
2017
2018 ext2fs_free_mem(&ctx);
2019}
2020
2021/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002022 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002023 */
2024
2025/*
2026 * The strategy we use for keeping track of EA refcounts is as
2027 * follows. We keep a sorted array of first EA blocks and its
2028 * reference counts. Once the refcount has dropped to zero, it is
2029 * removed from the array to save memory space. Once the EA block is
2030 * checked, its bit is set in the block_ea_map bitmap.
2031 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002032
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002033
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002034static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002035{
2036 ext2_refcount_t refcount;
2037 errcode_t retval;
2038 size_t bytes;
2039
2040 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
2041 if (retval)
2042 return retval;
2043 memset(refcount, 0, sizeof(struct ea_refcount));
2044
2045 if (!size)
2046 size = 500;
2047 refcount->size = size;
2048 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
2049#ifdef DEBUG
2050 printf("Refcount allocated %d entries, %d bytes.\n",
2051 refcount->size, bytes);
2052#endif
2053 retval = ext2fs_get_mem(bytes, &refcount->list);
2054 if (retval)
2055 goto errout;
2056 memset(refcount->list, 0, bytes);
2057
2058 refcount->count = 0;
2059 refcount->cursor = 0;
2060
2061 *ret = refcount;
2062 return 0;
2063
2064errout:
2065 ea_refcount_free(refcount);
2066 return(retval);
2067}
2068
2069/*
2070 * collapse_refcount() --- go through the refcount array, and get rid
2071 * of any count == zero entries
2072 */
2073static void refcount_collapse(ext2_refcount_t refcount)
2074{
2075 unsigned int i, j;
2076 struct ea_refcount_el *list;
2077
2078 list = refcount->list;
2079 for (i = 0, j = 0; i < refcount->count; i++) {
2080 if (list[i].ea_count) {
2081 if (i != j)
2082 list[j] = list[i];
2083 j++;
2084 }
2085 }
2086#if defined(DEBUG) || defined(TEST_PROGRAM)
2087 printf("Refcount_collapse: size was %d, now %d\n",
2088 refcount->count, j);
2089#endif
2090 refcount->count = j;
2091}
2092
2093
2094/*
2095 * insert_refcount_el() --- Insert a new entry into the sorted list at a
2096 * specified position.
2097 */
2098static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
2099 blk_t blk, int pos)
2100{
2101 struct ea_refcount_el *el;
2102 errcode_t retval;
2103 blk_t new_size = 0;
2104 int num;
2105
2106 if (refcount->count >= refcount->size) {
2107 new_size = refcount->size + 100;
2108#ifdef DEBUG
2109 printf("Reallocating refcount %d entries...\n", new_size);
2110#endif
2111 retval = ext2fs_resize_mem((size_t) refcount->size *
2112 sizeof(struct ea_refcount_el),
2113 (size_t) new_size *
2114 sizeof(struct ea_refcount_el),
2115 &refcount->list);
2116 if (retval)
2117 return 0;
2118 refcount->size = new_size;
2119 }
2120 num = (int) refcount->count - pos;
2121 if (num < 0)
2122 return 0; /* should never happen */
2123 if (num) {
2124 memmove(&refcount->list[pos+1], &refcount->list[pos],
2125 sizeof(struct ea_refcount_el) * num);
2126 }
2127 refcount->count++;
2128 el = &refcount->list[pos];
2129 el->ea_count = 0;
2130 el->ea_blk = blk;
2131 return el;
2132}
2133
2134
2135/*
2136 * get_refcount_el() --- given an block number, try to find refcount
2137 * information in the sorted list. If the create flag is set,
2138 * and we can't find an entry, create one in the sorted list.
2139 */
2140static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
2141 blk_t blk, int create)
2142{
2143 float range;
2144 int low, high, mid;
2145 blk_t lowval, highval;
2146
2147 if (!refcount || !refcount->list)
2148 return 0;
2149retry:
2150 low = 0;
2151 high = (int) refcount->count-1;
2152 if (create && ((refcount->count == 0) ||
2153 (blk > refcount->list[high].ea_blk))) {
2154 if (refcount->count >= refcount->size)
2155 refcount_collapse(refcount);
2156
2157 return insert_refcount_el(refcount, blk,
2158 (unsigned) refcount->count);
2159 }
2160 if (refcount->count == 0)
2161 return 0;
2162
2163 if (refcount->cursor >= refcount->count)
2164 refcount->cursor = 0;
2165 if (blk == refcount->list[refcount->cursor].ea_blk)
2166 return &refcount->list[refcount->cursor++];
2167#ifdef DEBUG
2168 printf("Non-cursor get_refcount_el: %u\n", blk);
2169#endif
2170 while (low <= high) {
2171#if 0
2172 mid = (low+high)/2;
Mike Frysinger51a43b42005-09-24 07:11:16 +00002173#else
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002174 if (low == high)
2175 mid = low;
2176 else {
2177 /* Interpolate for efficiency */
2178 lowval = refcount->list[low].ea_blk;
2179 highval = refcount->list[high].ea_blk;
2180
2181 if (blk < lowval)
2182 range = 0;
2183 else if (blk > highval)
2184 range = 1;
2185 else
2186 range = ((float) (blk - lowval)) /
2187 (highval - lowval);
2188 mid = low + ((int) (range * (high-low)));
2189 }
Mike Frysinger51a43b42005-09-24 07:11:16 +00002190#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002191 if (blk == refcount->list[mid].ea_blk) {
2192 refcount->cursor = mid+1;
2193 return &refcount->list[mid];
2194 }
2195 if (blk < refcount->list[mid].ea_blk)
2196 high = mid-1;
2197 else
2198 low = mid+1;
2199 }
2200 /*
2201 * If we need to create a new entry, it should be right at
2202 * low (where high will be left at low-1).
2203 */
2204 if (create) {
2205 if (refcount->count >= refcount->size) {
2206 refcount_collapse(refcount);
2207 if (refcount->count < refcount->size)
2208 goto retry;
2209 }
2210 return insert_refcount_el(refcount, blk, low);
2211 }
2212 return 0;
2213}
2214
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002215static errcode_t
2216ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002217{
2218 struct ea_refcount_el *el;
2219
2220 el = get_refcount_el(refcount, blk, 1);
2221 if (!el)
2222 return EXT2_ET_NO_MEMORY;
2223 el->ea_count++;
2224
2225 if (ret)
2226 *ret = el->ea_count;
2227 return 0;
2228}
2229
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002230static errcode_t
2231ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002232{
2233 struct ea_refcount_el *el;
2234
2235 el = get_refcount_el(refcount, blk, 0);
2236 if (!el || el->ea_count == 0)
2237 return EXT2_ET_INVALID_ARGUMENT;
2238
2239 el->ea_count--;
2240
2241 if (ret)
2242 *ret = el->ea_count;
2243 return 0;
2244}
2245
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002246static errcode_t
2247ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002248{
2249 struct ea_refcount_el *el;
2250
2251 /*
2252 * Get the refcount element
2253 */
2254 el = get_refcount_el(refcount, blk, count ? 1 : 0);
2255 if (!el)
2256 return count ? EXT2_ET_NO_MEMORY : 0;
2257 el->ea_count = count;
2258 return 0;
2259}
2260
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002261static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002262{
2263 refcount->cursor = 0;
2264}
2265
2266
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002267static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002268{
2269 struct ea_refcount_el *list;
2270
2271 while (1) {
2272 if (refcount->cursor >= refcount->count)
2273 return 0;
2274 list = refcount->list;
2275 if (list[refcount->cursor].ea_count) {
2276 if (ret)
2277 *ret = list[refcount->cursor].ea_count;
2278 return list[refcount->cursor++].ea_blk;
2279 }
2280 refcount->cursor++;
2281 }
2282}
2283
2284
2285/*
2286 * ehandler.c --- handle bad block errors which come up during the
2287 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002288 */
2289
2290
2291static const char *operation;
2292
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002293static errcode_t
2294e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00002295 void *data, size_t size FSCK_ATTR((unused)),
2296 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002297{
2298 int i;
2299 char *p;
2300 ext2_filsys fs = (ext2_filsys) channel->app_data;
2301 e2fsck_t ctx;
2302
2303 ctx = (e2fsck_t) fs->priv_data;
2304
2305 /*
2306 * If more than one block was read, try reading each block
2307 * separately. We could use the actual bytes read to figure
2308 * out where to start, but we don't bother.
2309 */
2310 if (count > 1) {
2311 p = (char *) data;
2312 for (i=0; i < count; i++, p += channel->block_size, block++) {
2313 error = io_channel_read_blk(channel, block,
2314 1, p);
2315 if (error)
2316 return error;
2317 }
2318 return 0;
2319 }
2320 if (operation)
2321 printf(_("Error reading block %lu (%s) while %s. "), block,
2322 error_message(error), operation);
2323 else
2324 printf(_("Error reading block %lu (%s). "), block,
2325 error_message(error));
2326 preenhalt(ctx);
2327 if (ask(ctx, _("Ignore error"), 1)) {
2328 if (ask(ctx, _("Force rewrite"), 1))
2329 io_channel_write_blk(channel, block, 1, data);
2330 return 0;
2331 }
2332
2333 return error;
2334}
2335
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002336static errcode_t
2337e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00002338 const void *data, size_t size FSCK_ATTR((unused)),
2339 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002340{
2341 int i;
2342 const char *p;
2343 ext2_filsys fs = (ext2_filsys) channel->app_data;
2344 e2fsck_t ctx;
2345
2346 ctx = (e2fsck_t) fs->priv_data;
2347
2348 /*
2349 * If more than one block was written, try writing each block
2350 * separately. We could use the actual bytes read to figure
2351 * out where to start, but we don't bother.
2352 */
2353 if (count > 1) {
2354 p = (const char *) data;
2355 for (i=0; i < count; i++, p += channel->block_size, block++) {
2356 error = io_channel_write_blk(channel, block,
2357 1, p);
2358 if (error)
2359 return error;
2360 }
2361 return 0;
2362 }
2363
2364 if (operation)
2365 printf(_("Error writing block %lu (%s) while %s. "), block,
2366 error_message(error), operation);
2367 else
2368 printf(_("Error writing block %lu (%s). "), block,
2369 error_message(error));
2370 preenhalt(ctx);
2371 if (ask(ctx, _("Ignore error"), 1))
2372 return 0;
2373
2374 return error;
2375}
2376
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002377static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002378{
2379 const char *ret = operation;
2380
2381 operation = op;
2382 return ret;
2383}
2384
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002385static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002386{
2387 channel->read_error = e2fsck_handle_read_error;
2388 channel->write_error = e2fsck_handle_write_error;
2389}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002390
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002391/*
2392 * journal.c --- code for handling the "ext3" journal
2393 *
2394 * Copyright (C) 2000 Andreas Dilger
2395 * Copyright (C) 2000 Theodore Ts'o
2396 *
2397 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
2398 * Copyright (C) 1999 Red Hat Software
2399 *
2400 * This file may be redistributed under the terms of the
2401 * GNU General Public License version 2 or at your discretion
2402 * any later version.
2403 */
2404
2405#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
2406
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002407/*
2408 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
2409 * This creates a larger static binary, and a smaller binary using
2410 * shared libraries. It's also probably slightly less CPU-efficient,
2411 * which is why it's not on by default. But, it's a good way of
2412 * testing the functions in inode_io.c and fileio.c.
2413 */
2414#undef USE_INODE_IO
2415
2416/* Kernel compatibility functions for handling the journal. These allow us
2417 * to use the recovery.c file virtually unchanged from the kernel, so we
2418 * don't have to do much to keep kernel and user recovery in sync.
2419 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002420static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002421{
2422#ifdef USE_INODE_IO
2423 *phys = block;
2424 return 0;
2425#else
2426 struct inode *inode = journal->j_inode;
2427 errcode_t retval;
2428 blk_t pblk;
2429
2430 if (!inode) {
2431 *phys = block;
2432 return 0;
2433 }
2434
2435 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
2436 &inode->i_ext2, NULL, 0, block, &pblk);
2437 *phys = pblk;
2438 return (retval);
2439#endif
2440}
2441
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002442static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002443{
2444 struct buffer_head *bh;
2445
2446 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
2447 if (!bh)
2448 return NULL;
2449
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002450 bh->b_ctx = kdev->k_ctx;
2451 if (kdev->k_dev == K_DEV_FS)
2452 bh->b_io = kdev->k_ctx->fs->io;
2453 else
2454 bh->b_io = kdev->k_ctx->journal_io;
2455 bh->b_size = blocksize;
2456 bh->b_blocknr = blocknr;
2457
2458 return bh;
2459}
2460
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002461static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002462{
2463 io_channel io;
2464
2465 if (kdev->k_dev == K_DEV_FS)
2466 io = kdev->k_ctx->fs->io;
2467 else
2468 io = kdev->k_ctx->journal_io;
2469
2470 io_channel_flush(io);
2471}
2472
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002473static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002474{
2475 int retval;
2476 struct buffer_head *bh;
2477
2478 for (; nr > 0; --nr) {
2479 bh = *bhp++;
2480 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002481 retval = io_channel_read_blk(bh->b_io,
2482 bh->b_blocknr,
2483 1, bh->b_data);
2484 if (retval) {
2485 com_err(bh->b_ctx->device_name, retval,
2486 "while reading block %lu\n",
2487 (unsigned long) bh->b_blocknr);
2488 bh->b_err = retval;
2489 continue;
2490 }
2491 bh->b_uptodate = 1;
2492 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002493 retval = io_channel_write_blk(bh->b_io,
2494 bh->b_blocknr,
2495 1, bh->b_data);
2496 if (retval) {
2497 com_err(bh->b_ctx->device_name, retval,
2498 "while writing block %lu\n",
2499 (unsigned long) bh->b_blocknr);
2500 bh->b_err = retval;
2501 continue;
2502 }
2503 bh->b_dirty = 0;
2504 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002505 }
2506 }
2507}
2508
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002509static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002510{
2511 bh->b_dirty = 1;
2512}
2513
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002514static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002515{
2516 bh->b_dirty = 0;
2517}
2518
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002519static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002520{
2521 if (bh->b_dirty)
2522 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002523 ext2fs_free_mem(&bh);
2524}
2525
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002526static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002527{
2528 return bh->b_uptodate;
2529}
2530
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002531static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002532{
2533 bh->b_uptodate = val;
2534}
2535
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002536static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002537{
2538 if (!bh->b_uptodate)
2539 ll_rw_block(READ, 1, &bh);
2540}
2541
2542
2543static void e2fsck_clear_recover(e2fsck_t ctx, int error)
2544{
2545 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
2546
2547 /* if we had an error doing journal recovery, we need a full fsck */
2548 if (error)
2549 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
2550 ext2fs_mark_super_dirty(ctx->fs);
2551}
2552
2553static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
2554{
2555 struct ext2_super_block *sb = ctx->fs->super;
2556 struct ext2_super_block jsuper;
2557 struct problem_context pctx;
2558 struct buffer_head *bh;
2559 struct inode *j_inode = NULL;
2560 struct kdev_s *dev_fs = NULL, *dev_journal;
2561 const char *journal_name = 0;
2562 journal_t *journal = NULL;
2563 errcode_t retval = 0;
2564 io_manager io_ptr = 0;
2565 unsigned long start = 0;
2566 blk_t blk;
2567 int ext_journal = 0;
2568 int tried_backup_jnl = 0;
2569 int i;
2570
2571 clear_problem_context(&pctx);
2572
2573 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
2574 if (!journal) {
2575 return EXT2_ET_NO_MEMORY;
2576 }
2577
2578 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
2579 if (!dev_fs) {
2580 retval = EXT2_ET_NO_MEMORY;
2581 goto errout;
2582 }
2583 dev_journal = dev_fs+1;
2584
2585 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
2586 dev_fs->k_dev = K_DEV_FS;
2587 dev_journal->k_dev = K_DEV_JOURNAL;
2588
2589 journal->j_dev = dev_journal;
2590 journal->j_fs_dev = dev_fs;
2591 journal->j_inode = NULL;
2592 journal->j_blocksize = ctx->fs->blocksize;
2593
2594 if (uuid_is_null(sb->s_journal_uuid)) {
2595 if (!sb->s_journal_inum)
2596 return EXT2_ET_BAD_INODE_NUM;
2597 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
2598 "journal inode");
2599 if (!j_inode) {
2600 retval = EXT2_ET_NO_MEMORY;
2601 goto errout;
2602 }
2603
2604 j_inode->i_ctx = ctx;
2605 j_inode->i_ino = sb->s_journal_inum;
2606
2607 if ((retval = ext2fs_read_inode(ctx->fs,
2608 sb->s_journal_inum,
2609 &j_inode->i_ext2))) {
2610 try_backup_journal:
2611 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
2612 tried_backup_jnl)
2613 goto errout;
2614 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
2615 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
2616 EXT2_N_BLOCKS*4);
2617 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
2618 j_inode->i_ext2.i_links_count = 1;
2619 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
2620 tried_backup_jnl++;
2621 }
2622 if (!j_inode->i_ext2.i_links_count ||
2623 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
2624 retval = EXT2_ET_NO_JOURNAL;
2625 goto try_backup_journal;
2626 }
2627 if (j_inode->i_ext2.i_size / journal->j_blocksize <
2628 JFS_MIN_JOURNAL_BLOCKS) {
2629 retval = EXT2_ET_JOURNAL_TOO_SMALL;
2630 goto try_backup_journal;
2631 }
2632 for (i=0; i < EXT2_N_BLOCKS; i++) {
2633 blk = j_inode->i_ext2.i_block[i];
2634 if (!blk) {
2635 if (i < EXT2_NDIR_BLOCKS) {
2636 retval = EXT2_ET_JOURNAL_TOO_SMALL;
2637 goto try_backup_journal;
2638 }
2639 continue;
2640 }
2641 if (blk < sb->s_first_data_block ||
2642 blk >= sb->s_blocks_count) {
2643 retval = EXT2_ET_BAD_BLOCK_NUM;
2644 goto try_backup_journal;
2645 }
2646 }
2647 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
2648
2649#ifdef USE_INODE_IO
2650 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
2651 &j_inode->i_ext2,
2652 &journal_name);
2653 if (retval)
2654 goto errout;
2655
2656 io_ptr = inode_io_manager;
2657#else
2658 journal->j_inode = j_inode;
2659 ctx->journal_io = ctx->fs->io;
2660 if ((retval = journal_bmap(journal, 0, &start)) != 0)
2661 goto errout;
2662#endif
2663 } else {
2664 ext_journal = 1;
2665 if (!ctx->journal_name) {
2666 char uuid[37];
2667
2668 uuid_unparse(sb->s_journal_uuid, uuid);
2669 ctx->journal_name = blkid_get_devname(ctx->blkid,
2670 "UUID", uuid);
2671 if (!ctx->journal_name)
2672 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
2673 }
2674 journal_name = ctx->journal_name;
2675
2676 if (!journal_name) {
2677 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
2678 return EXT2_ET_LOAD_EXT_JOURNAL;
2679 }
2680
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002681 io_ptr = unix_io_manager;
2682 }
2683
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002684#ifndef USE_INODE_IO
2685 if (ext_journal)
2686#endif
2687 retval = io_ptr->open(journal_name, IO_FLAG_RW,
2688 &ctx->journal_io);
2689 if (retval)
2690 goto errout;
2691
2692 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
2693
2694 if (ext_journal) {
2695 if (ctx->fs->blocksize == 1024)
2696 start = 1;
2697 bh = getblk(dev_journal, start, ctx->fs->blocksize);
2698 if (!bh) {
2699 retval = EXT2_ET_NO_MEMORY;
2700 goto errout;
2701 }
2702 ll_rw_block(READ, 1, &bh);
2703 if ((retval = bh->b_err) != 0)
2704 goto errout;
2705 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
2706 sizeof(jsuper));
2707 brelse(bh);
2708#ifdef EXT2FS_ENABLE_SWAPFS
2709 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
2710 ext2fs_swap_super(&jsuper);
2711#endif
2712 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
2713 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
2714 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
2715 retval = EXT2_ET_LOAD_EXT_JOURNAL;
2716 goto errout;
2717 }
2718 /* Make sure the journal UUID is correct */
2719 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
2720 sizeof(jsuper.s_uuid))) {
2721 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
2722 retval = EXT2_ET_LOAD_EXT_JOURNAL;
2723 goto errout;
2724 }
2725
2726 journal->j_maxlen = jsuper.s_blocks_count;
2727 start++;
2728 }
2729
2730 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
2731 retval = EXT2_ET_NO_MEMORY;
2732 goto errout;
2733 }
2734
2735 journal->j_sb_buffer = bh;
2736 journal->j_superblock = (journal_superblock_t *)bh->b_data;
2737
2738#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00002739 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002740#endif
2741
2742 *ret_journal = journal;
2743 return 0;
2744
2745errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00002746 ext2fs_free_mem(&dev_fs);
2747 ext2fs_free_mem(&j_inode);
2748 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002749 return retval;
2750
2751}
2752
2753static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
2754 struct problem_context *pctx)
2755{
2756 struct ext2_super_block *sb = ctx->fs->super;
2757 int recover = ctx->fs->super->s_feature_incompat &
2758 EXT3_FEATURE_INCOMPAT_RECOVER;
2759 int has_journal = ctx->fs->super->s_feature_compat &
2760 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2761
2762 if (has_journal || sb->s_journal_inum) {
2763 /* The journal inode is bogus, remove and force full fsck */
2764 pctx->ino = sb->s_journal_inum;
2765 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
2766 if (has_journal && sb->s_journal_inum)
2767 printf("*** ext3 journal has been deleted - "
2768 "filesystem is now ext2 only ***\n\n");
2769 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2770 sb->s_journal_inum = 0;
2771 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
2772 e2fsck_clear_recover(ctx, 1);
2773 return 0;
2774 }
2775 return EXT2_ET_BAD_INODE_NUM;
2776 } else if (recover) {
2777 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
2778 e2fsck_clear_recover(ctx, 1);
2779 return 0;
2780 }
2781 return EXT2_ET_UNSUPP_FEATURE;
2782 }
2783 return 0;
2784}
2785
2786#define V1_SB_SIZE 0x0024
2787static void clear_v2_journal_fields(journal_t *journal)
2788{
2789 e2fsck_t ctx = journal->j_dev->k_ctx;
2790 struct problem_context pctx;
2791
2792 clear_problem_context(&pctx);
2793
2794 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
2795 return;
2796
2797 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
2798 ctx->fs->blocksize-V1_SB_SIZE);
2799 mark_buffer_dirty(journal->j_sb_buffer);
2800}
2801
2802
2803static errcode_t e2fsck_journal_load(journal_t *journal)
2804{
2805 e2fsck_t ctx = journal->j_dev->k_ctx;
2806 journal_superblock_t *jsb;
2807 struct buffer_head *jbh = journal->j_sb_buffer;
2808 struct problem_context pctx;
2809
2810 clear_problem_context(&pctx);
2811
2812 ll_rw_block(READ, 1, &jbh);
2813 if (jbh->b_err) {
2814 com_err(ctx->device_name, jbh->b_err,
2815 _("reading journal superblock\n"));
2816 return jbh->b_err;
2817 }
2818
2819 jsb = journal->j_superblock;
2820 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
2821 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
2822 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2823
2824 switch (ntohl(jsb->s_header.h_blocktype)) {
2825 case JFS_SUPERBLOCK_V1:
2826 journal->j_format_version = 1;
2827 if (jsb->s_feature_compat ||
2828 jsb->s_feature_incompat ||
2829 jsb->s_feature_ro_compat ||
2830 jsb->s_nr_users)
2831 clear_v2_journal_fields(journal);
2832 break;
2833
2834 case JFS_SUPERBLOCK_V2:
2835 journal->j_format_version = 2;
2836 if (ntohl(jsb->s_nr_users) > 1 &&
2837 uuid_is_null(ctx->fs->super->s_journal_uuid))
2838 clear_v2_journal_fields(journal);
2839 if (ntohl(jsb->s_nr_users) > 1) {
2840 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
2841 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
2842 }
2843 break;
2844
2845 /*
2846 * These should never appear in a journal super block, so if
2847 * they do, the journal is badly corrupted.
2848 */
2849 case JFS_DESCRIPTOR_BLOCK:
2850 case JFS_COMMIT_BLOCK:
2851 case JFS_REVOKE_BLOCK:
2852 return EXT2_ET_CORRUPT_SUPERBLOCK;
2853
2854 /* If we don't understand the superblock major type, but there
2855 * is a magic number, then it is likely to be a new format we
2856 * just don't understand, so leave it alone. */
2857 default:
2858 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
2859 }
2860
2861 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
2862 return EXT2_ET_UNSUPP_FEATURE;
2863
2864 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
2865 return EXT2_ET_RO_UNSUPP_FEATURE;
2866
2867 /* We have now checked whether we know enough about the journal
2868 * format to be able to proceed safely, so any other checks that
2869 * fail we should attempt to recover from. */
2870 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
2871 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
2872 _("%s: no valid journal superblock found\n"),
2873 ctx->device_name);
2874 return EXT2_ET_CORRUPT_SUPERBLOCK;
2875 }
2876
2877 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
2878 journal->j_maxlen = ntohl(jsb->s_maxlen);
2879 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
2880 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
2881 _("%s: journal too short\n"),
2882 ctx->device_name);
2883 return EXT2_ET_CORRUPT_SUPERBLOCK;
2884 }
2885
2886 journal->j_tail_sequence = ntohl(jsb->s_sequence);
2887 journal->j_transaction_sequence = journal->j_tail_sequence;
2888 journal->j_tail = ntohl(jsb->s_start);
2889 journal->j_first = ntohl(jsb->s_first);
2890 journal->j_last = ntohl(jsb->s_maxlen);
2891
2892 return 0;
2893}
2894
2895static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
2896 journal_t *journal)
2897{
2898 char *p;
2899 union {
2900 uuid_t uuid;
2901 __u32 val[4];
2902 } u;
2903 __u32 new_seq = 0;
2904 int i;
2905
2906 /* Leave a valid existing V1 superblock signature alone.
2907 * Anything unrecognisable we overwrite with a new V2
2908 * signature. */
2909
2910 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
2911 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
2912 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
2913 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
2914 }
2915
2916 /* Zero out everything else beyond the superblock header */
2917
2918 p = ((char *) jsb) + sizeof(journal_header_t);
2919 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
2920
2921 jsb->s_blocksize = htonl(ctx->fs->blocksize);
2922 jsb->s_maxlen = htonl(journal->j_maxlen);
2923 jsb->s_first = htonl(1);
2924
2925 /* Initialize the journal sequence number so that there is "no"
2926 * chance we will find old "valid" transactions in the journal.
2927 * This avoids the need to zero the whole journal (slow to do,
2928 * and risky when we are just recovering the filesystem).
2929 */
2930 uuid_generate(u.uuid);
2931 for (i = 0; i < 4; i ++)
2932 new_seq ^= u.val[i];
2933 jsb->s_sequence = htonl(new_seq);
2934
2935 mark_buffer_dirty(journal->j_sb_buffer);
2936 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
2937}
2938
2939static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
2940 journal_t *journal,
2941 struct problem_context *pctx)
2942{
2943 struct ext2_super_block *sb = ctx->fs->super;
2944 int recover = ctx->fs->super->s_feature_incompat &
2945 EXT3_FEATURE_INCOMPAT_RECOVER;
2946
2947 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
2948 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
2949 e2fsck_journal_reset_super(ctx, journal->j_superblock,
2950 journal);
2951 journal->j_transaction_sequence = 1;
2952 e2fsck_clear_recover(ctx, recover);
2953 return 0;
2954 }
2955 return EXT2_ET_CORRUPT_SUPERBLOCK;
2956 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
2957 return EXT2_ET_CORRUPT_SUPERBLOCK;
2958
2959 return 0;
2960}
2961
2962static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
2963 int reset, int drop)
2964{
2965 journal_superblock_t *jsb;
2966
2967 if (drop)
2968 mark_buffer_clean(journal->j_sb_buffer);
2969 else if (!(ctx->options & E2F_OPT_READONLY)) {
2970 jsb = journal->j_superblock;
2971 jsb->s_sequence = htonl(journal->j_transaction_sequence);
2972 if (reset)
2973 jsb->s_start = 0; /* this marks the journal as empty */
2974 mark_buffer_dirty(journal->j_sb_buffer);
2975 }
2976 brelse(journal->j_sb_buffer);
2977
2978 if (ctx->journal_io) {
2979 if (ctx->fs && ctx->fs->io != ctx->journal_io)
2980 io_channel_close(ctx->journal_io);
2981 ctx->journal_io = 0;
2982 }
2983
2984#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00002985 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002986#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00002987 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002988 ext2fs_free_mem(&journal);
2989}
2990
2991/*
2992 * This function makes sure that the superblock fields regarding the
2993 * journal are consistent.
2994 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002995static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002996{
2997 struct ext2_super_block *sb = ctx->fs->super;
2998 journal_t *journal;
2999 int recover = ctx->fs->super->s_feature_incompat &
3000 EXT3_FEATURE_INCOMPAT_RECOVER;
3001 struct problem_context pctx;
3002 problem_t problem;
3003 int reset = 0, force_fsck = 0;
3004 int retval;
3005
3006 /* If we don't have any journal features, don't do anything more */
3007 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
3008 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
3009 uuid_is_null(sb->s_journal_uuid))
3010 return 0;
3011
3012 clear_problem_context(&pctx);
3013 pctx.num = sb->s_journal_inum;
3014
3015 retval = e2fsck_get_journal(ctx, &journal);
3016 if (retval) {
3017 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
3018 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
3019 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
3020 (retval == EXT2_ET_NO_JOURNAL))
3021 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
3022 return retval;
3023 }
3024
3025 retval = e2fsck_journal_load(journal);
3026 if (retval) {
3027 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
3028 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
3029 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
3030 &pctx))) ||
3031 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
3032 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
3033 &pctx))) ||
3034 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
3035 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
3036 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
3037 &pctx);
3038 e2fsck_journal_release(ctx, journal, 0, 1);
3039 return retval;
3040 }
3041
3042 /*
3043 * We want to make the flags consistent here. We will not leave with
3044 * needs_recovery set but has_journal clear. We can't get in a loop
3045 * with -y, -n, or -p, only if a user isn't making up their mind.
3046 */
3047no_has_journal:
3048 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
3049 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
3050 pctx.str = "inode";
3051 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
3052 if (recover &&
3053 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
3054 goto no_has_journal;
3055 /*
3056 * Need a full fsck if we are releasing a
3057 * journal stored on a reserved inode.
3058 */
3059 force_fsck = recover ||
3060 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
3061 /* Clear all of the journal fields */
3062 sb->s_journal_inum = 0;
3063 sb->s_journal_dev = 0;
3064 memset(sb->s_journal_uuid, 0,
3065 sizeof(sb->s_journal_uuid));
3066 e2fsck_clear_recover(ctx, force_fsck);
3067 } else if (!(ctx->options & E2F_OPT_READONLY)) {
3068 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
3069 ext2fs_mark_super_dirty(ctx->fs);
3070 }
3071 }
3072
3073 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
3074 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
3075 journal->j_superblock->s_start != 0) {
3076 /* Print status information */
3077 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
3078 if (ctx->superblock)
3079 problem = PR_0_JOURNAL_RUN_DEFAULT;
3080 else
3081 problem = PR_0_JOURNAL_RUN;
3082 if (fix_problem(ctx, problem, &pctx)) {
3083 ctx->options |= E2F_OPT_FORCE;
3084 sb->s_feature_incompat |=
3085 EXT3_FEATURE_INCOMPAT_RECOVER;
3086 ext2fs_mark_super_dirty(ctx->fs);
3087 } else if (fix_problem(ctx,
3088 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
3089 reset = 1;
3090 sb->s_state &= ~EXT2_VALID_FS;
3091 ext2fs_mark_super_dirty(ctx->fs);
3092 }
3093 /*
3094 * If the user answers no to the above question, we
3095 * ignore the fact that journal apparently has data;
3096 * accidentally replaying over valid data would be far
3097 * worse than skipping a questionable recovery.
3098 *
3099 * XXX should we abort with a fatal error here? What
3100 * will the ext3 kernel code do if a filesystem with
3101 * !NEEDS_RECOVERY but with a non-zero
3102 * journal->j_superblock->s_start is mounted?
3103 */
3104 }
3105
3106 e2fsck_journal_release(ctx, journal, reset, 0);
3107 return retval;
3108}
3109
3110static errcode_t recover_ext3_journal(e2fsck_t ctx)
3111{
3112 journal_t *journal;
3113 int retval;
3114
3115 journal_init_revoke_caches();
3116 retval = e2fsck_get_journal(ctx, &journal);
3117 if (retval)
3118 return retval;
3119
3120 retval = e2fsck_journal_load(journal);
3121 if (retval)
3122 goto errout;
3123
3124 retval = journal_init_revoke(journal, 1024);
3125 if (retval)
3126 goto errout;
3127
3128 retval = -journal_recover(journal);
3129 if (retval)
3130 goto errout;
3131
3132 if (journal->j_superblock->s_errno) {
3133 ctx->fs->super->s_state |= EXT2_ERROR_FS;
3134 ext2fs_mark_super_dirty(ctx->fs);
3135 journal->j_superblock->s_errno = 0;
3136 mark_buffer_dirty(journal->j_sb_buffer);
3137 }
3138
3139errout:
3140 journal_destroy_revoke(journal);
3141 journal_destroy_revoke_caches();
3142 e2fsck_journal_release(ctx, journal, 1, 0);
3143 return retval;
3144}
3145
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003146static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003147{
3148 io_manager io_ptr = ctx->fs->io->manager;
3149 int blocksize = ctx->fs->blocksize;
3150 errcode_t retval, recover_retval;
3151
3152 printf(_("%s: recovering journal\n"), ctx->device_name);
3153 if (ctx->options & E2F_OPT_READONLY) {
3154 printf(_("%s: won't do journal recovery while read-only\n"),
3155 ctx->device_name);
3156 return EXT2_ET_FILE_RO;
3157 }
3158
3159 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
3160 ext2fs_flush(ctx->fs); /* Force out any modifications */
3161
3162 recover_retval = recover_ext3_journal(ctx);
3163
3164 /*
3165 * Reload the filesystem context to get up-to-date data from disk
3166 * because journal recovery will change the filesystem under us.
3167 */
3168 ext2fs_close(ctx->fs);
3169 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
3170 ctx->superblock, blocksize, io_ptr,
3171 &ctx->fs);
3172
3173 if (retval) {
3174 com_err(ctx->program_name, retval,
3175 _("while trying to re-open %s"),
3176 ctx->device_name);
3177 fatal_error(ctx, 0);
3178 }
3179 ctx->fs->priv_data = ctx;
3180
3181 /* Set the superblock flags */
3182 e2fsck_clear_recover(ctx, recover_retval);
3183 return recover_retval;
3184}
3185
3186/*
3187 * This function will move the journal inode from a visible file in
3188 * the filesystem directory hierarchy to the reserved inode if necessary.
3189 */
3190static const char * const journal_names[] = {
3191 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
3192
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003193static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003194{
3195 struct ext2_super_block *sb = ctx->fs->super;
3196 struct problem_context pctx;
3197 struct ext2_inode inode;
3198 ext2_filsys fs = ctx->fs;
3199 ext2_ino_t ino;
3200 errcode_t retval;
3201 const char * const * cpp;
3202 int group, mount_flags;
3203
3204 clear_problem_context(&pctx);
3205
3206 /*
3207 * If the filesystem is opened read-only, or there is no
3208 * journal, then do nothing.
3209 */
3210 if ((ctx->options & E2F_OPT_READONLY) ||
3211 (sb->s_journal_inum == 0) ||
3212 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
3213 return;
3214
3215 /*
3216 * Read in the journal inode
3217 */
3218 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
3219 return;
3220
3221 /*
3222 * If it's necessary to backup the journal inode, do so.
3223 */
3224 if ((sb->s_jnl_backup_type == 0) ||
3225 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
3226 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
3227 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
3228 memcpy(sb->s_jnl_blocks, inode.i_block,
3229 EXT2_N_BLOCKS*4);
3230 sb->s_jnl_blocks[16] = inode.i_size;
3231 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
3232 ext2fs_mark_super_dirty(fs);
3233 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
3234 }
3235 }
3236
3237 /*
3238 * If the journal is already the hidden inode, then do nothing
3239 */
3240 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
3241 return;
3242
3243 /*
3244 * The journal inode had better have only one link and not be readable.
3245 */
3246 if (inode.i_links_count != 1)
3247 return;
3248
3249 /*
3250 * If the filesystem is mounted, or we can't tell whether
3251 * or not it's mounted, do nothing.
3252 */
3253 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
3254 if (retval || (mount_flags & EXT2_MF_MOUNTED))
3255 return;
3256
3257 /*
3258 * If we can't find the name of the journal inode, then do
3259 * nothing.
3260 */
3261 for (cpp = journal_names; *cpp; cpp++) {
3262 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
3263 strlen(*cpp), 0, &ino);
3264 if ((retval == 0) && (ino == sb->s_journal_inum))
3265 break;
3266 }
3267 if (*cpp == 0)
3268 return;
3269
3270 /* We need the inode bitmap to be loaded */
3271 retval = ext2fs_read_bitmaps(fs);
3272 if (retval)
3273 return;
3274
3275 pctx.str = *cpp;
3276 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
3277 return;
3278
3279 /*
3280 * OK, we've done all the checks, let's actually move the
3281 * journal inode. Errors at this point mean we need to force
3282 * an ext2 filesystem check.
3283 */
3284 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
3285 goto err_out;
3286 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
3287 goto err_out;
3288 sb->s_journal_inum = EXT2_JOURNAL_INO;
3289 ext2fs_mark_super_dirty(fs);
3290 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
3291 inode.i_links_count = 0;
3292 inode.i_dtime = time(0);
3293 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
3294 goto err_out;
3295
3296 group = ext2fs_group_of_ino(fs, ino);
3297 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
3298 ext2fs_mark_ib_dirty(fs);
3299 fs->group_desc[group].bg_free_inodes_count++;
3300 fs->super->s_free_inodes_count++;
3301 return;
3302
3303err_out:
3304 pctx.errcode = retval;
3305 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
3306 fs->super->s_state &= ~EXT2_VALID_FS;
3307 ext2fs_mark_super_dirty(fs);
3308 return;
3309}
3310
3311/*
3312 * message.c --- print e2fsck messages (with compression)
3313 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003314 * print_e2fsck_message() prints a message to the user, using
3315 * compression techniques and expansions of abbreviations.
3316 *
3317 * The following % expansions are supported:
3318 *
3319 * %b <blk> block number
3320 * %B <blkcount> integer
3321 * %c <blk2> block number
3322 * %Di <dirent>->ino inode number
3323 * %Dn <dirent>->name string
3324 * %Dr <dirent>->rec_len
3325 * %Dl <dirent>->name_len
3326 * %Dt <dirent>->filetype
3327 * %d <dir> inode number
3328 * %g <group> integer
3329 * %i <ino> inode number
3330 * %Is <inode> -> i_size
3331 * %IS <inode> -> i_extra_isize
3332 * %Ib <inode> -> i_blocks
3333 * %Il <inode> -> i_links_count
3334 * %Im <inode> -> i_mode
3335 * %IM <inode> -> i_mtime
3336 * %IF <inode> -> i_faddr
3337 * %If <inode> -> i_file_acl
3338 * %Id <inode> -> i_dir_acl
3339 * %Iu <inode> -> i_uid
3340 * %Ig <inode> -> i_gid
3341 * %j <ino2> inode number
3342 * %m <com_err error message>
3343 * %N <num>
3344 * %p ext2fs_get_pathname of directory <ino>
3345 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
3346 * the containing directory. (If dirent is NULL
3347 * then return the pathname of directory <ino2>)
3348 * %q ext2fs_get_pathname of directory <dir>
3349 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
3350 * the containing directory.
3351 * %s <str> miscellaneous string
3352 * %S backup superblock
3353 * %X <num> hexadecimal format
3354 *
3355 * The following '@' expansions are supported:
3356 *
3357 * @a extended attribute
3358 * @A error allocating
3359 * @b block
3360 * @B bitmap
3361 * @c compress
3362 * @C conflicts with some other fs block
3363 * @D deleted
3364 * @d directory
3365 * @e entry
3366 * @E Entry '%Dn' in %p (%i)
3367 * @f filesystem
3368 * @F for @i %i (%Q) is
3369 * @g group
3370 * @h HTREE directory inode
3371 * @i inode
3372 * @I illegal
3373 * @j journal
3374 * @l lost+found
3375 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00003376 * @m multiply-claimed
3377 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003378 * @o orphaned
3379 * @p problem in
3380 * @r root inode
3381 * @s should be
3382 * @S superblock
3383 * @u unattached
3384 * @v device
3385 * @z zero-length
3386 */
3387
3388
3389/*
3390 * This structure defines the abbreviations used by the text strings
3391 * below. The first character in the string is the index letter. An
3392 * abbreviation of the form '@<i>' is expanded by looking up the index
3393 * letter <i> in the table below.
3394 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003395static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003396 N_("aextended attribute"),
3397 N_("Aerror allocating"),
3398 N_("bblock"),
3399 N_("Bbitmap"),
3400 N_("ccompress"),
3401 N_("Cconflicts with some other fs @b"),
3402 N_("iinode"),
3403 N_("Iillegal"),
3404 N_("jjournal"),
3405 N_("Ddeleted"),
3406 N_("ddirectory"),
3407 N_("eentry"),
3408 N_("E@e '%Dn' in %p (%i)"),
3409 N_("ffilesystem"),
3410 N_("Ffor @i %i (%Q) is"),
3411 N_("ggroup"),
3412 N_("hHTREE @d @i"),
3413 N_("llost+found"),
3414 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00003415 N_("mmultiply-claimed"),
3416 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003417 N_("oorphaned"),
3418 N_("pproblem in"),
3419 N_("rroot @i"),
3420 N_("sshould be"),
3421 N_("Ssuper@b"),
3422 N_("uunattached"),
3423 N_("vdevice"),
3424 N_("zzero-length"),
3425 "@@",
3426 0
3427 };
3428
3429/*
3430 * Give more user friendly names to the "special" inodes.
3431 */
3432#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003433static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003434{
3435 N_("<The NULL inode>"), /* 0 */
3436 N_("<The bad blocks inode>"), /* 1 */
3437 "/", /* 2 */
3438 N_("<The ACL index inode>"), /* 3 */
3439 N_("<The ACL data inode>"), /* 4 */
3440 N_("<The boot loader inode>"), /* 5 */
3441 N_("<The undelete directory inode>"), /* 6 */
3442 N_("<The group descriptor inode>"), /* 7 */
3443 N_("<The journal inode>"), /* 8 */
3444 N_("<Reserved inode 9>"), /* 9 */
3445 N_("<Reserved inode 10>"), /* 10 */
3446};
3447
3448/*
3449 * This function does "safe" printing. It will convert non-printable
3450 * ASCII characters using '^' and M- notation.
3451 */
3452static void safe_print(const char *cp, int len)
3453{
3454 unsigned char ch;
3455
3456 if (len < 0)
3457 len = strlen(cp);
3458
3459 while (len--) {
3460 ch = *cp++;
3461 if (ch > 128) {
3462 fputs("M-", stdout);
3463 ch -= 128;
3464 }
3465 if ((ch < 32) || (ch == 0x7f)) {
3466 fputc('^', stdout);
3467 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
3468 }
3469 fputc(ch, stdout);
3470 }
3471}
3472
3473
3474/*
3475 * This function prints a pathname, using the ext2fs_get_pathname
3476 * function
3477 */
3478static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
3479{
3480 errcode_t retval;
3481 char *path;
3482
3483 if (!dir && (ino < num_special_inodes)) {
3484 fputs(_(special_inode_name[ino]), stdout);
3485 return;
3486 }
3487
3488 retval = ext2fs_get_pathname(fs, dir, ino, &path);
3489 if (retval)
3490 fputs("???", stdout);
3491 else {
3492 safe_print(path, -1);
3493 ext2fs_free_mem(&path);
3494 }
3495}
3496
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003497static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
3498 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003499/*
3500 * This function handles the '@' expansion. We allow recursive
3501 * expansion; an @ expression can contain further '@' and '%'
3502 * expressions.
3503 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003504static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003505 struct problem_context *pctx,
3506 int *first)
3507{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003508 const char * const *cpp;
3509 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003510
3511 /* Search for the abbreviation */
3512 for (cpp = abbrevs; *cpp; cpp++) {
3513 if (ch == *cpp[0])
3514 break;
3515 }
3516 if (*cpp) {
3517 str = _(*cpp) + 1;
3518 if (*first && islower(*str)) {
3519 *first = 0;
3520 fputc(toupper(*str++), stdout);
3521 }
3522 print_e2fsck_message(ctx, str, pctx, *first);
3523 } else
3524 printf("@%c", ch);
3525}
3526
3527/*
3528 * This function expands '%IX' expressions
3529 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003530static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003531 struct problem_context *ctx)
3532{
3533 struct ext2_inode *inode;
3534 struct ext2_inode_large *large_inode;
3535 char * time_str;
3536 time_t t;
3537 int do_gmt = -1;
3538
3539 if (!ctx || !ctx->inode)
3540 goto no_inode;
3541
3542 inode = ctx->inode;
3543 large_inode = (struct ext2_inode_large *) inode;
3544
3545 switch (ch) {
3546 case 's':
3547 if (LINUX_S_ISDIR(inode->i_mode))
3548 printf("%u", inode->i_size);
3549 else {
3550#ifdef EXT2_NO_64_TYPE
3551 if (inode->i_size_high)
3552 printf("0x%x%08x", inode->i_size_high,
3553 inode->i_size);
3554 else
3555 printf("%u", inode->i_size);
3556#else
3557 printf("%llu", (inode->i_size |
3558 ((__u64) inode->i_size_high << 32)));
3559#endif
3560 }
3561 break;
3562 case 'S':
3563 printf("%u", large_inode->i_extra_isize);
3564 break;
3565 case 'b':
3566 printf("%u", inode->i_blocks);
3567 break;
3568 case 'l':
3569 printf("%d", inode->i_links_count);
3570 break;
3571 case 'm':
3572 printf("0%o", inode->i_mode);
3573 break;
3574 case 'M':
3575 /* The diet libc doesn't respect the TZ environemnt variable */
3576 if (do_gmt == -1) {
3577 time_str = getenv("TZ");
3578 if (!time_str)
3579 time_str = "";
3580 do_gmt = !strcmp(time_str, "GMT");
3581 }
3582 t = inode->i_mtime;
3583 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
3584 printf("%.24s", time_str);
3585 break;
3586 case 'F':
3587 printf("%u", inode->i_faddr);
3588 break;
3589 case 'f':
3590 printf("%u", inode->i_file_acl);
3591 break;
3592 case 'd':
3593 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
3594 inode->i_dir_acl : 0));
3595 break;
3596 case 'u':
3597 printf("%d", (inode->i_uid |
3598 (inode->osd2.linux2.l_i_uid_high << 16)));
3599 break;
3600 case 'g':
3601 printf("%d", (inode->i_gid |
3602 (inode->osd2.linux2.l_i_gid_high << 16)));
3603 break;
3604 default:
3605 no_inode:
3606 printf("%%I%c", ch);
3607 break;
3608 }
3609}
3610
3611/*
3612 * This function expands '%dX' expressions
3613 */
3614static _INLINE_ void expand_dirent_expression(char ch,
3615 struct problem_context *ctx)
3616{
3617 struct ext2_dir_entry *dirent;
3618 int len;
3619
3620 if (!ctx || !ctx->dirent)
3621 goto no_dirent;
3622
3623 dirent = ctx->dirent;
3624
3625 switch (ch) {
3626 case 'i':
3627 printf("%u", dirent->inode);
3628 break;
3629 case 'n':
3630 len = dirent->name_len & 0xFF;
3631 if (len > EXT2_NAME_LEN)
3632 len = EXT2_NAME_LEN;
3633 if (len > dirent->rec_len)
3634 len = dirent->rec_len;
3635 safe_print(dirent->name, len);
3636 break;
3637 case 'r':
3638 printf("%u", dirent->rec_len);
3639 break;
3640 case 'l':
3641 printf("%u", dirent->name_len & 0xFF);
3642 break;
3643 case 't':
3644 printf("%u", dirent->name_len >> 8);
3645 break;
3646 default:
3647 no_dirent:
3648 printf("%%D%c", ch);
3649 break;
3650 }
3651}
3652
3653static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
3654 struct problem_context *ctx)
3655{
3656 if (!ctx)
3657 goto no_context;
3658
3659 switch (ch) {
3660 case '%':
3661 fputc('%', stdout);
3662 break;
3663 case 'b':
3664 printf("%u", ctx->blk);
3665 break;
3666 case 'B':
3667#ifdef EXT2_NO_64_TYPE
3668 printf("%d", ctx->blkcount);
3669#else
3670 printf("%lld", ctx->blkcount);
3671#endif
3672 break;
3673 case 'c':
3674 printf("%u", ctx->blk2);
3675 break;
3676 case 'd':
3677 printf("%u", ctx->dir);
3678 break;
3679 case 'g':
3680 printf("%d", ctx->group);
3681 break;
3682 case 'i':
3683 printf("%u", ctx->ino);
3684 break;
3685 case 'j':
3686 printf("%u", ctx->ino2);
3687 break;
3688 case 'm':
3689 printf("%s", error_message(ctx->errcode));
3690 break;
3691 case 'N':
3692#ifdef EXT2_NO_64_TYPE
3693 printf("%u", ctx->num);
3694#else
3695 printf("%llu", ctx->num);
3696#endif
3697 break;
3698 case 'p':
3699 print_pathname(fs, ctx->ino, 0);
3700 break;
3701 case 'P':
3702 print_pathname(fs, ctx->ino2,
3703 ctx->dirent ? ctx->dirent->inode : 0);
3704 break;
3705 case 'q':
3706 print_pathname(fs, ctx->dir, 0);
3707 break;
3708 case 'Q':
3709 print_pathname(fs, ctx->dir, ctx->ino);
3710 break;
3711 case 'S':
3712 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
3713 break;
3714 case 's':
3715 printf("%s", ctx->str ? ctx->str : "NULL");
3716 break;
3717 case 'X':
3718#ifdef EXT2_NO_64_TYPE
3719 printf("0x%x", ctx->num);
3720#else
3721 printf("0x%llx", ctx->num);
3722#endif
3723 break;
3724 default:
3725 no_context:
3726 printf("%%%c", ch);
3727 break;
3728 }
3729}
3730
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003731
3732static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003733 struct problem_context *pctx, int first)
3734{
3735 ext2_filsys fs = ctx->fs;
3736 const char * cp;
3737 int i;
3738
3739 e2fsck_clear_progbar(ctx);
3740 for (cp = msg; *cp; cp++) {
3741 if (cp[0] == '@') {
3742 cp++;
3743 expand_at_expression(ctx, *cp, pctx, &first);
3744 } else if (cp[0] == '%' && cp[1] == 'I') {
3745 cp += 2;
3746 expand_inode_expression(*cp, pctx);
3747 } else if (cp[0] == '%' && cp[1] == 'D') {
3748 cp += 2;
3749 expand_dirent_expression(*cp, pctx);
3750 } else if ((cp[0] == '%')) {
3751 cp++;
3752 expand_percent_expression(fs, *cp, pctx);
3753 } else {
3754 for (i=0; cp[i]; i++)
3755 if ((cp[i] == '@') || cp[i] == '%')
3756 break;
3757 printf("%.*s", i, cp);
3758 cp += i-1;
3759 }
3760 first = 0;
3761 }
3762}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003763
3764
3765/*
3766 * region.c --- code which manages allocations within a region.
3767 */
3768
3769struct region_el {
3770 region_addr_t start;
3771 region_addr_t end;
3772 struct region_el *next;
3773};
3774
3775struct region_struct {
3776 region_addr_t min;
3777 region_addr_t max;
3778 struct region_el *allocated;
3779};
3780
3781static region_t region_create(region_addr_t min, region_addr_t max)
3782{
3783 region_t region;
3784
3785 region = malloc(sizeof(struct region_struct));
3786 if (!region)
3787 return NULL;
3788 memset(region, 0, sizeof(struct region_struct));
3789 region->min = min;
3790 region->max = max;
3791 return region;
3792}
3793
3794static void region_free(region_t region)
3795{
3796 struct region_el *r, *next;
3797
3798 for (r = region->allocated; r; r = next) {
3799 next = r->next;
3800 free(r);
3801 }
3802 memset(region, 0, sizeof(struct region_struct));
3803 free(region);
3804}
3805
3806static int region_allocate(region_t region, region_addr_t start, int n)
3807{
3808 struct region_el *r, *new_region, *prev, *next;
3809 region_addr_t end;
3810
3811 end = start+n;
3812 if ((start < region->min) || (end > region->max))
3813 return -1;
3814 if (n == 0)
3815 return 1;
3816
3817 /*
3818 * Search through the linked list. If we find that it
3819 * conflicts witih something that's already allocated, return
3820 * 1; if we can find an existing region which we can grow, do
3821 * so. Otherwise, stop when we find the appropriate place
3822 * insert a new region element into the linked list.
3823 */
3824 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
3825 if (((start >= r->start) && (start < r->end)) ||
3826 ((end > r->start) && (end <= r->end)) ||
3827 ((start <= r->start) && (end >= r->end)))
3828 return 1;
3829 if (end == r->start) {
3830 r->start = start;
3831 return 0;
3832 }
3833 if (start == r->end) {
3834 if ((next = r->next)) {
3835 if (end > next->start)
3836 return 1;
3837 if (end == next->start) {
3838 r->end = next->end;
3839 r->next = next->next;
3840 free(next);
3841 return 0;
3842 }
3843 }
3844 r->end = end;
3845 return 0;
3846 }
3847 if (start < r->start)
3848 break;
3849 }
3850 /*
3851 * Insert a new region element structure into the linked list
3852 */
3853 new_region = malloc(sizeof(struct region_el));
3854 if (!new_region)
3855 return -1;
3856 new_region->start = start;
3857 new_region->end = start + n;
3858 new_region->next = r;
3859 if (prev)
3860 prev->next = new_region;
3861 else
3862 region->allocated = new_region;
3863 return 0;
3864}
3865
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003866/*
3867 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
3868 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003869 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
3870 * and applies the following tests to each inode:
3871 *
3872 * - The mode field of the inode must be legal.
3873 * - The size and block count fields of the inode are correct.
3874 * - A data block must not be used by another inode
3875 *
3876 * Pass 1 also gathers the collects the following information:
3877 *
3878 * - A bitmap of which inodes are in use. (inode_used_map)
3879 * - A bitmap of which inodes are directories. (inode_dir_map)
3880 * - A bitmap of which inodes are regular files. (inode_reg_map)
3881 * - A bitmap of which inodes have bad fields. (inode_bad_map)
3882 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
3883 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
3884 * - A bitmap of which blocks are in use. (block_found_map)
3885 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
3886 * - The data blocks of the directory inodes. (dir_map)
3887 *
3888 * Pass 1 is designed to stash away enough information so that the
3889 * other passes should not need to read in the inode information
3890 * during the normal course of a filesystem check. (Althogh if an
3891 * inconsistency is detected, other passes may need to read in an
3892 * inode to fix it.)
3893 *
3894 * Note that pass 1B will be invoked if there are any duplicate blocks
3895 * found.
3896 */
3897
3898
3899static int process_block(ext2_filsys fs, blk_t *blocknr,
3900 e2_blkcnt_t blockcnt, blk_t ref_blk,
3901 int ref_offset, void *priv_data);
3902static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
3903 e2_blkcnt_t blockcnt, blk_t ref_blk,
3904 int ref_offset, void *priv_data);
3905static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
3906 char *block_buf);
3907static void mark_table_blocks(e2fsck_t ctx);
3908static void alloc_bb_map(e2fsck_t ctx);
3909static void alloc_imagic_map(e2fsck_t ctx);
3910static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
3911static void handle_fs_bad_blocks(e2fsck_t ctx);
3912static void process_inodes(e2fsck_t ctx, char *block_buf);
3913static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
3914static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
3915 dgrp_t group, void * priv_data);
3916static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3917 char *block_buf, int adjust_sign);
3918/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
3919
3920static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
3921 struct ext2_inode * inode, int bufsize,
3922 const char *proc);
3923
3924struct process_block_struct_1 {
3925 ext2_ino_t ino;
3926 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
3927 fragmented:1, compressed:1, bbcheck:1;
3928 blk_t num_blocks;
3929 blk_t max_blocks;
3930 e2_blkcnt_t last_block;
3931 int num_illegal_blocks;
3932 blk_t previous_block;
3933 struct ext2_inode *inode;
3934 struct problem_context *pctx;
3935 ext2fs_block_bitmap fs_meta_blocks;
3936 e2fsck_t ctx;
3937};
3938
3939struct process_inode_block {
3940 ext2_ino_t ino;
3941 struct ext2_inode inode;
3942};
3943
3944struct scan_callback_struct {
3945 e2fsck_t ctx;
3946 char *block_buf;
3947};
3948
3949/*
3950 * For the inodes to process list.
3951 */
3952static struct process_inode_block *inodes_to_process;
3953static int process_inode_count;
3954
3955static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
3956 EXT2_MIN_BLOCK_LOG_SIZE + 1];
3957
3958/*
3959 * Free all memory allocated by pass1 in preparation for restarting
3960 * things.
3961 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003962static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003963{
3964 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003965}
3966
3967/*
3968 * Check to make sure a device inode is real. Returns 1 if the device
3969 * checks out, 0 if not.
3970 *
3971 * Note: this routine is now also used to check FIFO's and Sockets,
3972 * since they have the same requirement; the i_block fields should be
3973 * zero.
3974 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003975static int
3976e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003977{
3978 int i;
3979
3980 /*
3981 * If i_blocks is non-zero, or the index flag is set, then
3982 * this is a bogus device/fifo/socket
3983 */
3984 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
3985 (inode->i_flags & EXT2_INDEX_FL))
3986 return 0;
3987
3988 /*
3989 * We should be able to do the test below all the time, but
3990 * because the kernel doesn't forcibly clear the device
3991 * inode's additional i_block fields, there are some rare
3992 * occasions when a legitimate device inode will have non-zero
3993 * additional i_block fields. So for now, we only complain
3994 * when the immutable flag is set, which should never happen
3995 * for devices. (And that's when the problem is caused, since
3996 * you can't set or clear immutable flags for devices.) Once
3997 * the kernel has been fixed we can change this...
3998 */
3999 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
4000 for (i=4; i < EXT2_N_BLOCKS; i++)
4001 if (inode->i_block[i])
4002 return 0;
4003 }
4004 return 1;
4005}
4006
4007/*
4008 * Check to make sure a symlink inode is real. Returns 1 if the symlink
4009 * checks out, 0 if not.
4010 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004011static int
4012e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004013{
4014 unsigned int len;
4015 int i;
4016 blk_t blocks;
4017
4018 if ((inode->i_size_high || inode->i_size == 0) ||
4019 (inode->i_flags & EXT2_INDEX_FL))
4020 return 0;
4021
4022 blocks = ext2fs_inode_data_blocks(fs, inode);
4023 if (blocks) {
4024 if ((inode->i_size >= fs->blocksize) ||
4025 (blocks != fs->blocksize >> 9) ||
4026 (inode->i_block[0] < fs->super->s_first_data_block) ||
4027 (inode->i_block[0] >= fs->super->s_blocks_count))
4028 return 0;
4029
4030 for (i = 1; i < EXT2_N_BLOCKS; i++)
4031 if (inode->i_block[i])
4032 return 0;
4033
4034 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
4035 return 0;
4036
4037 len = strnlen(buf, fs->blocksize);
4038 if (len == fs->blocksize)
4039 return 0;
4040 } else {
4041 if (inode->i_size >= sizeof(inode->i_block))
4042 return 0;
4043
4044 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
4045 if (len == sizeof(inode->i_block))
4046 return 0;
4047 }
4048 if (len != inode->i_size)
4049 return 0;
4050 return 1;
4051}
4052
4053/*
4054 * If the immutable (or append-only) flag is set on the inode, offer
4055 * to clear it.
4056 */
4057#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
4058static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
4059{
4060 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
4061 return;
4062
4063 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
4064 return;
4065
4066 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
4067 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
4068}
4069
4070/*
4071 * If device, fifo or socket, check size is zero -- if not offer to
4072 * clear it
4073 */
4074static void check_size(e2fsck_t ctx, struct problem_context *pctx)
4075{
4076 struct ext2_inode *inode = pctx->inode;
4077
4078 if ((inode->i_size == 0) && (inode->i_size_high == 0))
4079 return;
4080
4081 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
4082 return;
4083
4084 inode->i_size = 0;
4085 inode->i_size_high = 0;
4086 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
4087}
4088
4089static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
4090{
4091 struct ext2_super_block *sb = ctx->fs->super;
4092 struct ext2_inode_large *inode;
4093 struct ext2_ext_attr_entry *entry;
4094 char *start, *end;
4095 int storage_size, remain, offs;
4096 int problem = 0;
4097
4098 inode = (struct ext2_inode_large *) pctx->inode;
4099 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
4100 inode->i_extra_isize;
4101 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
4102 inode->i_extra_isize + sizeof(__u32);
4103 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
4104 entry = (struct ext2_ext_attr_entry *) start;
4105
4106 /* scan all entry's headers first */
4107
4108 /* take finish entry 0UL into account */
4109 remain = storage_size - sizeof(__u32);
4110 offs = end - start;
4111
4112 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
4113
4114 /* header eats this space */
4115 remain -= sizeof(struct ext2_ext_attr_entry);
4116
4117 /* is attribute name valid? */
4118 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
4119 pctx->num = entry->e_name_len;
4120 problem = PR_1_ATTR_NAME_LEN;
4121 goto fix;
4122 }
4123
4124 /* attribute len eats this space */
4125 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
4126
4127 /* check value size */
4128 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
4129 pctx->num = entry->e_value_size;
4130 problem = PR_1_ATTR_VALUE_SIZE;
4131 goto fix;
4132 }
4133
4134 /* check value placement */
4135 if (entry->e_value_offs +
4136 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
4137 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
4138 pctx->num = entry->e_value_offs;
4139 problem = PR_1_ATTR_VALUE_OFFSET;
4140 goto fix;
4141 }
4142
4143 /* e_value_block must be 0 in inode's ea */
4144 if (entry->e_value_block != 0) {
4145 pctx->num = entry->e_value_block;
4146 problem = PR_1_ATTR_VALUE_BLOCK;
4147 goto fix;
4148 }
4149
4150 /* e_hash must be 0 in inode's ea */
4151 if (entry->e_hash != 0) {
4152 pctx->num = entry->e_hash;
4153 problem = PR_1_ATTR_HASH;
4154 goto fix;
4155 }
4156
4157 remain -= entry->e_value_size;
4158 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
4159
4160 entry = EXT2_EXT_ATTR_NEXT(entry);
4161 }
4162fix:
4163 /*
4164 * it seems like a corruption. it's very unlikely we could repair
4165 * EA(s) in automatic fashion -bzzz
4166 */
4167#if 0
4168 problem = PR_1_ATTR_HASH;
4169#endif
4170 if (problem == 0 || !fix_problem(ctx, problem, pctx))
4171 return;
4172
4173 /* simple remove all possible EA(s) */
4174 *((__u32 *)start) = 0UL;
4175 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
4176 EXT2_INODE_SIZE(sb), "pass1");
4177}
4178
4179static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
4180{
4181 struct ext2_super_block *sb = ctx->fs->super;
4182 struct ext2_inode_large *inode;
4183 __u32 *eamagic;
4184 int min, max;
4185
4186 inode = (struct ext2_inode_large *) pctx->inode;
4187 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
4188 /* this isn't large inode. so, nothing to check */
4189 return;
4190 }
4191
4192#if 0
4193 printf("inode #%u, i_extra_size %d\n", pctx->ino,
4194 inode->i_extra_isize);
4195#endif
4196 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
4197 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
4198 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
4199 /*
4200 * For now we will allow i_extra_isize to be 0, but really
4201 * implementations should never allow i_extra_isize to be 0
4202 */
4203 if (inode->i_extra_isize &&
4204 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
4205 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
4206 return;
4207 inode->i_extra_isize = min;
4208 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
4209 EXT2_INODE_SIZE(sb), "pass1");
4210 return;
4211 }
4212
4213 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
4214 inode->i_extra_isize);
4215 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
4216 /* it seems inode has an extended attribute(s) in body */
4217 check_ea_in_inode(ctx, pctx);
4218 }
4219}
4220
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004221static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004222{
4223 int i;
4224 __u64 max_sizes;
4225 ext2_filsys fs = ctx->fs;
4226 ext2_ino_t ino;
4227 struct ext2_inode *inode;
4228 ext2_inode_scan scan;
4229 char *block_buf;
4230#ifdef RESOURCE_TRACK
4231 struct resource_track rtrack;
4232#endif
4233 unsigned char frag, fsize;
4234 struct problem_context pctx;
4235 struct scan_callback_struct scan_struct;
4236 struct ext2_super_block *sb = ctx->fs->super;
4237 int imagic_fs;
4238 int busted_fs_time = 0;
4239 int inode_size;
4240
4241#ifdef RESOURCE_TRACK
4242 init_resource_track(&rtrack);
4243#endif
4244 clear_problem_context(&pctx);
4245
4246 if (!(ctx->options & E2F_OPT_PREEN))
4247 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
4248
4249 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4250 !(ctx->options & E2F_OPT_NO)) {
4251 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
4252 ctx->dirs_to_hash = 0;
4253 }
4254
4255#ifdef MTRACE
4256 mtrace_print("Pass 1");
4257#endif
4258
4259#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
4260
4261 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
4262 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
4263 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
4264 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
4265 max_sizes = (max_sizes * (1UL << i)) - 1;
4266 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
4267 }
4268#undef EXT2_BPP
4269
4270 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
4271
4272 /*
4273 * Allocate bitmaps structures
4274 */
4275 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
4276 &ctx->inode_used_map);
4277 if (pctx.errcode) {
4278 pctx.num = 1;
4279 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4280 ctx->flags |= E2F_FLAG_ABORT;
4281 return;
4282 }
4283 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4284 _("directory inode map"), &ctx->inode_dir_map);
4285 if (pctx.errcode) {
4286 pctx.num = 2;
4287 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4288 ctx->flags |= E2F_FLAG_ABORT;
4289 return;
4290 }
4291 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4292 _("regular file inode map"), &ctx->inode_reg_map);
4293 if (pctx.errcode) {
4294 pctx.num = 6;
4295 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4296 ctx->flags |= E2F_FLAG_ABORT;
4297 return;
4298 }
4299 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
4300 &ctx->block_found_map);
4301 if (pctx.errcode) {
4302 pctx.num = 1;
4303 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
4304 ctx->flags |= E2F_FLAG_ABORT;
4305 return;
4306 }
4307 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
4308 &ctx->inode_link_info);
4309 if (pctx.errcode) {
4310 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
4311 ctx->flags |= E2F_FLAG_ABORT;
4312 return;
4313 }
4314 inode_size = EXT2_INODE_SIZE(fs->super);
4315 inode = (struct ext2_inode *)
4316 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
4317
4318 inodes_to_process = (struct process_inode_block *)
4319 e2fsck_allocate_memory(ctx,
4320 (ctx->process_inode_size *
4321 sizeof(struct process_inode_block)),
4322 "array of inodes to process");
4323 process_inode_count = 0;
4324
4325 pctx.errcode = ext2fs_init_dblist(fs, 0);
4326 if (pctx.errcode) {
4327 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
4328 ctx->flags |= E2F_FLAG_ABORT;
4329 return;
4330 }
4331
4332 /*
4333 * If the last orphan field is set, clear it, since the pass1
4334 * processing will automatically find and clear the orphans.
4335 * In the future, we may want to try using the last_orphan
4336 * linked list ourselves, but for now, we clear it so that the
4337 * ext3 mount code won't get confused.
4338 */
4339 if (!(ctx->options & E2F_OPT_READONLY)) {
4340 if (fs->super->s_last_orphan) {
4341 fs->super->s_last_orphan = 0;
4342 ext2fs_mark_super_dirty(fs);
4343 }
4344 }
4345
4346 mark_table_blocks(ctx);
4347 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
4348 "block interate buffer");
4349 e2fsck_use_inode_shortcuts(ctx, 1);
4350 ehandler_operation(_("doing inode scan"));
4351 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4352 &scan);
4353 if (pctx.errcode) {
4354 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
4355 ctx->flags |= E2F_FLAG_ABORT;
4356 return;
4357 }
4358 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
4359 ctx->stashed_inode = inode;
4360 scan_struct.ctx = ctx;
4361 scan_struct.block_buf = block_buf;
4362 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
4363 if (ctx->progress)
4364 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
4365 return;
Mike Frysinger874af852006-03-08 07:03:27 +00004366 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
4367 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004368 busted_fs_time = 1;
4369
4370 while (1) {
4371 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
4372 inode, inode_size);
4373 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4374 return;
4375 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
4376 if (!ctx->inode_bb_map)
4377 alloc_bb_map(ctx);
4378 ext2fs_mark_inode_bitmap(ctx->inode_bb_map, ino);
4379 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4380 continue;
4381 }
4382 if (pctx.errcode) {
4383 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
4384 ctx->flags |= E2F_FLAG_ABORT;
4385 return;
4386 }
4387 if (!ino)
4388 break;
4389 pctx.ino = ino;
4390 pctx.inode = inode;
4391 ctx->stashed_ino = ino;
4392 if (inode->i_links_count) {
4393 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
4394 ino, inode->i_links_count);
4395 if (pctx.errcode) {
4396 pctx.num = inode->i_links_count;
4397 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
4398 ctx->flags |= E2F_FLAG_ABORT;
4399 return;
4400 }
4401 }
4402 if (ino == EXT2_BAD_INO) {
4403 struct process_block_struct_1 pb;
4404
4405 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
4406 &pb.fs_meta_blocks);
4407 if (pctx.errcode) {
4408 pctx.num = 4;
4409 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
4410 ctx->flags |= E2F_FLAG_ABORT;
4411 return;
4412 }
4413 pb.ino = EXT2_BAD_INO;
4414 pb.num_blocks = pb.last_block = 0;
4415 pb.num_illegal_blocks = 0;
4416 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
4417 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
4418 pb.inode = inode;
4419 pb.pctx = &pctx;
4420 pb.ctx = ctx;
4421 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
4422 block_buf, process_bad_block, &pb);
4423 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
4424 if (pctx.errcode) {
4425 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
4426 ctx->flags |= E2F_FLAG_ABORT;
4427 return;
4428 }
4429 if (pb.bbcheck)
4430 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
4431 ctx->flags |= E2F_FLAG_ABORT;
4432 return;
4433 }
4434 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4435 clear_problem_context(&pctx);
4436 continue;
4437 } else if (ino == EXT2_ROOT_INO) {
4438 /*
4439 * Make sure the root inode is a directory; if
4440 * not, offer to clear it. It will be
4441 * regnerated in pass #3.
4442 */
4443 if (!LINUX_S_ISDIR(inode->i_mode)) {
4444 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
4445 inode->i_dtime = time(0);
4446 inode->i_links_count = 0;
4447 ext2fs_icount_store(ctx->inode_link_info,
4448 ino, 0);
4449 e2fsck_write_inode(ctx, ino, inode,
4450 "pass1");
4451 }
4452
4453 }
4454 /*
4455 * If dtime is set, offer to clear it. mke2fs
4456 * version 0.2b created filesystems with the
4457 * dtime field set for the root and lost+found
4458 * directories. We won't worry about
4459 * /lost+found, since that can be regenerated
4460 * easily. But we will fix the root directory
4461 * as a special case.
4462 */
4463 if (inode->i_dtime && inode->i_links_count) {
4464 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
4465 inode->i_dtime = 0;
4466 e2fsck_write_inode(ctx, ino, inode,
4467 "pass1");
4468 }
4469 }
4470 } else if (ino == EXT2_JOURNAL_INO) {
4471 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4472 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
4473 if (!LINUX_S_ISREG(inode->i_mode) &&
4474 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
4475 &pctx)) {
4476 inode->i_mode = LINUX_S_IFREG;
4477 e2fsck_write_inode(ctx, ino, inode,
4478 "pass1");
4479 }
4480 check_blocks(ctx, &pctx, block_buf);
4481 continue;
4482 }
4483 if ((inode->i_links_count || inode->i_blocks ||
4484 inode->i_blocks || inode->i_block[0]) &&
4485 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
4486 &pctx)) {
4487 memset(inode, 0, inode_size);
4488 ext2fs_icount_store(ctx->inode_link_info,
4489 ino, 0);
4490 e2fsck_write_inode_full(ctx, ino, inode,
4491 inode_size, "pass1");
4492 }
4493 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
4494 int problem = 0;
4495
4496 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4497 if (ino == EXT2_BOOT_LOADER_INO) {
4498 if (LINUX_S_ISDIR(inode->i_mode))
4499 problem = PR_1_RESERVED_BAD_MODE;
4500 } else if (ino == EXT2_RESIZE_INO) {
4501 if (inode->i_mode &&
4502 !LINUX_S_ISREG(inode->i_mode))
4503 problem = PR_1_RESERVED_BAD_MODE;
4504 } else {
4505 if (inode->i_mode != 0)
4506 problem = PR_1_RESERVED_BAD_MODE;
4507 }
4508 if (problem) {
4509 if (fix_problem(ctx, problem, &pctx)) {
4510 inode->i_mode = 0;
4511 e2fsck_write_inode(ctx, ino, inode,
4512 "pass1");
4513 }
4514 }
4515 check_blocks(ctx, &pctx, block_buf);
4516 continue;
4517 }
4518 /*
4519 * Check for inodes who might have been part of the
4520 * orphaned list linked list. They should have gotten
4521 * dealt with by now, unless the list had somehow been
4522 * corrupted.
4523 *
4524 * FIXME: In the future, inodes which are still in use
4525 * (and which are therefore) pending truncation should
4526 * be handled specially. Right now we just clear the
4527 * dtime field, and the normal e2fsck handling of
4528 * inodes where i_size and the inode blocks are
4529 * inconsistent is to fix i_size, instead of releasing
4530 * the extra blocks. This won't catch the inodes that
4531 * was at the end of the orphan list, but it's better
4532 * than nothing. The right answer is that there
4533 * shouldn't be any bugs in the orphan list handling. :-)
4534 */
4535 if (inode->i_dtime && !busted_fs_time &&
4536 inode->i_dtime < ctx->fs->super->s_inodes_count) {
4537 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
4538 inode->i_dtime = inode->i_links_count ?
4539 0 : time(0);
4540 e2fsck_write_inode(ctx, ino, inode,
4541 "pass1");
4542 }
4543 }
4544
4545 /*
4546 * This code assumes that deleted inodes have
4547 * i_links_count set to 0.
4548 */
4549 if (!inode->i_links_count) {
4550 if (!inode->i_dtime && inode->i_mode) {
4551 if (fix_problem(ctx,
4552 PR_1_ZERO_DTIME, &pctx)) {
4553 inode->i_dtime = time(0);
4554 e2fsck_write_inode(ctx, ino, inode,
4555 "pass1");
4556 }
4557 }
4558 continue;
4559 }
4560 /*
4561 * n.b. 0.3c ext2fs code didn't clear i_links_count for
4562 * deleted files. Oops.
4563 *
4564 * Since all new ext2 implementations get this right,
4565 * we now assume that the case of non-zero
4566 * i_links_count and non-zero dtime means that we
4567 * should keep the file, not delete it.
4568 *
4569 */
4570 if (inode->i_dtime) {
4571 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
4572 inode->i_dtime = 0;
4573 e2fsck_write_inode(ctx, ino, inode, "pass1");
4574 }
4575 }
4576
4577 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
4578 switch (fs->super->s_creator_os) {
4579 case EXT2_OS_LINUX:
4580 frag = inode->osd2.linux2.l_i_frag;
4581 fsize = inode->osd2.linux2.l_i_fsize;
4582 break;
4583 case EXT2_OS_HURD:
4584 frag = inode->osd2.hurd2.h_i_frag;
4585 fsize = inode->osd2.hurd2.h_i_fsize;
4586 break;
4587 case EXT2_OS_MASIX:
4588 frag = inode->osd2.masix2.m_i_frag;
4589 fsize = inode->osd2.masix2.m_i_fsize;
4590 break;
4591 default:
4592 frag = fsize = 0;
4593 }
4594
4595 if (inode->i_faddr || frag || fsize ||
4596 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
4597 mark_inode_bad(ctx, ino);
4598 if (inode->i_flags & EXT2_IMAGIC_FL) {
4599 if (imagic_fs) {
4600 if (!ctx->inode_imagic_map)
4601 alloc_imagic_map(ctx);
4602 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
4603 ino);
4604 } else {
4605 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
4606 inode->i_flags &= ~EXT2_IMAGIC_FL;
4607 e2fsck_write_inode(ctx, ino,
4608 inode, "pass1");
4609 }
4610 }
4611 }
4612
4613 check_inode_extra_space(ctx, &pctx);
4614
4615 if (LINUX_S_ISDIR(inode->i_mode)) {
4616 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
4617 e2fsck_add_dir_info(ctx, ino, 0);
4618 ctx->fs_directory_count++;
4619 } else if (LINUX_S_ISREG (inode->i_mode)) {
4620 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
4621 ctx->fs_regular_count++;
4622 } else if (LINUX_S_ISCHR (inode->i_mode) &&
4623 e2fsck_pass1_check_device_inode(fs, inode)) {
4624 check_immutable(ctx, &pctx);
4625 check_size(ctx, &pctx);
4626 ctx->fs_chardev_count++;
4627 } else if (LINUX_S_ISBLK (inode->i_mode) &&
4628 e2fsck_pass1_check_device_inode(fs, inode)) {
4629 check_immutable(ctx, &pctx);
4630 check_size(ctx, &pctx);
4631 ctx->fs_blockdev_count++;
4632 } else if (LINUX_S_ISLNK (inode->i_mode) &&
4633 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
4634 check_immutable(ctx, &pctx);
4635 ctx->fs_symlinks_count++;
4636 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
4637 ctx->fs_fast_symlinks_count++;
4638 check_blocks(ctx, &pctx, block_buf);
4639 continue;
4640 }
4641 }
4642 else if (LINUX_S_ISFIFO (inode->i_mode) &&
4643 e2fsck_pass1_check_device_inode(fs, inode)) {
4644 check_immutable(ctx, &pctx);
4645 check_size(ctx, &pctx);
4646 ctx->fs_fifo_count++;
4647 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
4648 e2fsck_pass1_check_device_inode(fs, inode)) {
4649 check_immutable(ctx, &pctx);
4650 check_size(ctx, &pctx);
4651 ctx->fs_sockets_count++;
4652 } else
4653 mark_inode_bad(ctx, ino);
4654 if (inode->i_block[EXT2_IND_BLOCK])
4655 ctx->fs_ind_count++;
4656 if (inode->i_block[EXT2_DIND_BLOCK])
4657 ctx->fs_dind_count++;
4658 if (inode->i_block[EXT2_TIND_BLOCK])
4659 ctx->fs_tind_count++;
4660 if (inode->i_block[EXT2_IND_BLOCK] ||
4661 inode->i_block[EXT2_DIND_BLOCK] ||
4662 inode->i_block[EXT2_TIND_BLOCK] ||
4663 inode->i_file_acl) {
4664 inodes_to_process[process_inode_count].ino = ino;
4665 inodes_to_process[process_inode_count].inode = *inode;
4666 process_inode_count++;
4667 } else
4668 check_blocks(ctx, &pctx, block_buf);
4669
4670 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4671 return;
4672
4673 if (process_inode_count >= ctx->process_inode_size) {
4674 process_inodes(ctx, block_buf);
4675
4676 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4677 return;
4678 }
4679 }
4680 process_inodes(ctx, block_buf);
4681 ext2fs_close_inode_scan(scan);
4682 ehandler_operation(0);
4683
4684 /*
4685 * If any extended attribute blocks' reference counts need to
4686 * be adjusted, either up (ctx->refcount_extra), or down
4687 * (ctx->refcount), then fix them.
4688 */
4689 if (ctx->refcount) {
4690 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
4691 ea_refcount_free(ctx->refcount);
4692 ctx->refcount = 0;
4693 }
4694 if (ctx->refcount_extra) {
4695 adjust_extattr_refcount(ctx, ctx->refcount_extra,
4696 block_buf, +1);
4697 ea_refcount_free(ctx->refcount_extra);
4698 ctx->refcount_extra = 0;
4699 }
4700
4701 if (ctx->invalid_bitmaps)
4702 handle_fs_bad_blocks(ctx);
4703
4704 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00004705 ext2fs_free_block_bitmap(ctx->block_ea_map);
4706 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004707
4708 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
4709 ext2fs_block_bitmap save_bmap;
4710
4711 save_bmap = fs->block_map;
4712 fs->block_map = ctx->block_found_map;
4713 clear_problem_context(&pctx);
4714 pctx.errcode = ext2fs_create_resize_inode(fs);
4715 if (pctx.errcode) {
4716 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
4717 /* Should never get here */
4718 ctx->flags |= E2F_FLAG_ABORT;
4719 return;
4720 }
Mike Frysinger874af852006-03-08 07:03:27 +00004721 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
4722 "recreate inode");
4723 inode->i_mtime = time(0);
4724 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
4725 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004726 fs->block_map = save_bmap;
4727 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
4728 }
4729
4730 if (ctx->flags & E2F_FLAG_RESTART) {
4731 /*
4732 * Only the master copy of the superblock and block
4733 * group descriptors are going to be written during a
4734 * restart, so set the superblock to be used to be the
4735 * master superblock.
4736 */
4737 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004738 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004739 goto endit;
4740 }
4741
4742 if (ctx->block_dup_map) {
4743 if (ctx->options & E2F_OPT_PREEN) {
4744 clear_problem_context(&pctx);
4745 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
4746 }
4747 e2fsck_pass1_dupblocks(ctx, block_buf);
4748 }
4749 ext2fs_free_mem(&inodes_to_process);
4750endit:
4751 e2fsck_use_inode_shortcuts(ctx, 0);
4752
4753 ext2fs_free_mem(&block_buf);
4754 ext2fs_free_mem(&inode);
4755
4756#ifdef RESOURCE_TRACK
4757 if (ctx->options & E2F_OPT_TIME2) {
4758 e2fsck_clear_progbar(ctx);
4759 print_resource_track(_("Pass 1"), &rtrack);
4760 }
4761#endif
4762}
4763
4764/*
4765 * When the inode_scan routines call this callback at the end of the
4766 * glock group, call process_inodes.
4767 */
4768static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004769 ext2_inode_scan scan FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004770 dgrp_t group, void * priv_data)
4771{
4772 struct scan_callback_struct *scan_struct;
4773 e2fsck_t ctx;
4774
4775 scan_struct = (struct scan_callback_struct *) priv_data;
4776 ctx = scan_struct->ctx;
4777
4778 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
4779
4780 if (ctx->progress)
4781 if ((ctx->progress)(ctx, 1, group+1,
4782 ctx->fs->group_desc_count))
4783 return EXT2_ET_CANCEL_REQUESTED;
4784
4785 return 0;
4786}
4787
4788/*
4789 * Process the inodes in the "inodes to process" list.
4790 */
4791static void process_inodes(e2fsck_t ctx, char *block_buf)
4792{
4793 int i;
4794 struct ext2_inode *old_stashed_inode;
4795 ext2_ino_t old_stashed_ino;
4796 const char *old_operation;
4797 char buf[80];
4798 struct problem_context pctx;
4799
4800#if 0
4801 printf("begin process_inodes: ");
4802#endif
4803 if (process_inode_count == 0)
4804 return;
4805 old_operation = ehandler_operation(0);
4806 old_stashed_inode = ctx->stashed_inode;
4807 old_stashed_ino = ctx->stashed_ino;
4808 qsort(inodes_to_process, process_inode_count,
4809 sizeof(struct process_inode_block), process_inode_cmp);
4810 clear_problem_context(&pctx);
4811 for (i=0; i < process_inode_count; i++) {
4812 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
4813 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
4814
4815#if 0
4816 printf("%u ", pctx.ino);
4817#endif
4818 sprintf(buf, _("reading indirect blocks of inode %u"),
4819 pctx.ino);
4820 ehandler_operation(buf);
4821 check_blocks(ctx, &pctx, block_buf);
4822 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4823 break;
4824 }
4825 ctx->stashed_inode = old_stashed_inode;
4826 ctx->stashed_ino = old_stashed_ino;
4827 process_inode_count = 0;
4828#if 0
4829 printf("end process inodes\n");
4830#endif
4831 ehandler_operation(old_operation);
4832}
4833
4834static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
4835{
4836 const struct process_inode_block *ib_a =
4837 (const struct process_inode_block *) a;
4838 const struct process_inode_block *ib_b =
4839 (const struct process_inode_block *) b;
4840 int ret;
4841
4842 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
4843 ib_b->inode.i_block[EXT2_IND_BLOCK]);
4844 if (ret == 0)
4845 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
4846 return ret;
4847}
4848
4849/*
4850 * Mark an inode as being bad in some what
4851 */
4852static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
4853{
4854 struct problem_context pctx;
4855
4856 if (!ctx->inode_bad_map) {
4857 clear_problem_context(&pctx);
4858
4859 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4860 _("bad inode map"), &ctx->inode_bad_map);
4861 if (pctx.errcode) {
4862 pctx.num = 3;
4863 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4864 /* Should never get here */
4865 ctx->flags |= E2F_FLAG_ABORT;
4866 return;
4867 }
4868 }
4869 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
4870}
4871
4872
4873/*
4874 * This procedure will allocate the inode "bb" (badblock) map table
4875 */
4876static void alloc_bb_map(e2fsck_t ctx)
4877{
4878 struct problem_context pctx;
4879
4880 clear_problem_context(&pctx);
4881 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4882 _("inode in bad block map"),
4883 &ctx->inode_bb_map);
4884 if (pctx.errcode) {
4885 pctx.num = 4;
4886 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4887 /* Should never get here */
4888 ctx->flags |= E2F_FLAG_ABORT;
4889 return;
4890 }
4891}
4892
4893/*
4894 * This procedure will allocate the inode imagic table
4895 */
4896static void alloc_imagic_map(e2fsck_t ctx)
4897{
4898 struct problem_context pctx;
4899
4900 clear_problem_context(&pctx);
4901 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
4902 _("imagic inode map"),
4903 &ctx->inode_imagic_map);
4904 if (pctx.errcode) {
4905 pctx.num = 5;
4906 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
4907 /* Should never get here */
4908 ctx->flags |= E2F_FLAG_ABORT;
4909 return;
4910 }
4911}
4912
4913/*
4914 * Marks a block as in use, setting the dup_map if it's been set
4915 * already. Called by process_block and process_bad_block.
4916 *
4917 * WARNING: Assumes checks have already been done to make sure block
4918 * is valid. This is true in both process_block and process_bad_block.
4919 */
4920static _INLINE_ void mark_block_used(e2fsck_t ctx, blk_t block)
4921{
4922 struct problem_context pctx;
4923
4924 clear_problem_context(&pctx);
4925
4926 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
4927 if (!ctx->block_dup_map) {
4928 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
4929 _("multiply claimed block map"),
4930 &ctx->block_dup_map);
4931 if (pctx.errcode) {
4932 pctx.num = 3;
4933 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
4934 &pctx);
4935 /* Should never get here */
4936 ctx->flags |= E2F_FLAG_ABORT;
4937 return;
4938 }
4939 }
4940 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
4941 } else {
4942 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
4943 }
4944}
4945
4946/*
4947 * Adjust the extended attribute block's reference counts at the end
4948 * of pass 1, either by subtracting out references for EA blocks that
4949 * are still referenced in ctx->refcount, or by adding references for
4950 * EA blocks that had extra references as accounted for in
4951 * ctx->refcount_extra.
4952 */
4953static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
4954 char *block_buf, int adjust_sign)
4955{
4956 struct ext2_ext_attr_header *header;
4957 struct problem_context pctx;
4958 ext2_filsys fs = ctx->fs;
4959 blk_t blk;
4960 __u32 should_be;
4961 int count;
4962
4963 clear_problem_context(&pctx);
4964
4965 ea_refcount_intr_begin(refcount);
4966 while (1) {
4967 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
4968 break;
4969 pctx.blk = blk;
4970 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4971 if (pctx.errcode) {
4972 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
4973 return;
4974 }
4975 header = (struct ext2_ext_attr_header *) block_buf;
4976 pctx.blkcount = header->h_refcount;
4977 should_be = header->h_refcount + adjust_sign * count;
4978 pctx.num = should_be;
4979 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
4980 header->h_refcount = should_be;
4981 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
4982 block_buf);
4983 if (pctx.errcode) {
4984 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
4985 continue;
4986 }
4987 }
4988 }
4989}
4990
4991/*
4992 * Handle processing the extended attribute blocks
4993 */
4994static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
4995 char *block_buf)
4996{
4997 ext2_filsys fs = ctx->fs;
4998 ext2_ino_t ino = pctx->ino;
4999 struct ext2_inode *inode = pctx->inode;
5000 blk_t blk;
5001 char * end;
5002 struct ext2_ext_attr_header *header;
5003 struct ext2_ext_attr_entry *entry;
5004 int count;
5005 region_t region;
5006
5007 blk = inode->i_file_acl;
5008 if (blk == 0)
5009 return 0;
5010
5011 /*
5012 * If the Extended attribute flag isn't set, then a non-zero
5013 * file acl means that the inode is corrupted.
5014 *
5015 * Or if the extended attribute block is an invalid block,
5016 * then the inode is also corrupted.
5017 */
5018 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
5019 (blk < fs->super->s_first_data_block) ||
5020 (blk >= fs->super->s_blocks_count)) {
5021 mark_inode_bad(ctx, ino);
5022 return 0;
5023 }
5024
5025 /* If ea bitmap hasn't been allocated, create it */
5026 if (!ctx->block_ea_map) {
5027 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
5028 _("ext attr block map"),
5029 &ctx->block_ea_map);
5030 if (pctx->errcode) {
5031 pctx->num = 2;
5032 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
5033 ctx->flags |= E2F_FLAG_ABORT;
5034 return 0;
5035 }
5036 }
5037
5038 /* Create the EA refcount structure if necessary */
5039 if (!ctx->refcount) {
5040 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
5041 if (pctx->errcode) {
5042 pctx->num = 1;
5043 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
5044 ctx->flags |= E2F_FLAG_ABORT;
5045 return 0;
5046 }
5047 }
5048
5049#if 0
5050 /* Debugging text */
5051 printf("Inode %u has EA block %u\n", ino, blk);
5052#endif
5053
5054 /* Have we seen this EA block before? */
5055 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
5056 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
5057 return 1;
5058 /* Ooops, this EA was referenced more than it stated */
5059 if (!ctx->refcount_extra) {
5060 pctx->errcode = ea_refcount_create(0,
5061 &ctx->refcount_extra);
5062 if (pctx->errcode) {
5063 pctx->num = 2;
5064 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
5065 ctx->flags |= E2F_FLAG_ABORT;
5066 return 0;
5067 }
5068 }
5069 ea_refcount_increment(ctx->refcount_extra, blk, 0);
5070 return 1;
5071 }
5072
5073 /*
5074 * OK, we haven't seen this EA block yet. So we need to
5075 * validate it
5076 */
5077 pctx->blk = blk;
5078 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
5079 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
5080 goto clear_extattr;
5081 header = (struct ext2_ext_attr_header *) block_buf;
5082 pctx->blk = inode->i_file_acl;
5083 if (((ctx->ext_attr_ver == 1) &&
5084 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
5085 ((ctx->ext_attr_ver == 2) &&
5086 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
5087 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
5088 goto clear_extattr;
5089 }
5090
5091 if (header->h_blocks != 1) {
5092 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
5093 goto clear_extattr;
5094 }
5095
5096 region = region_create(0, fs->blocksize);
5097 if (!region) {
5098 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
5099 ctx->flags |= E2F_FLAG_ABORT;
5100 return 0;
5101 }
5102 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
5103 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5104 goto clear_extattr;
5105 }
5106
5107 entry = (struct ext2_ext_attr_entry *)(header+1);
5108 end = block_buf + fs->blocksize;
5109 while ((char *)entry < end && *(__u32 *)entry) {
5110 if (region_allocate(region, (char *)entry - (char *)header,
5111 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
5112 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5113 goto clear_extattr;
5114 }
5115 if ((ctx->ext_attr_ver == 1 &&
5116 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
5117 (ctx->ext_attr_ver == 2 &&
5118 entry->e_name_index == 0)) {
5119 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
5120 goto clear_extattr;
5121 }
5122 if (entry->e_value_block != 0) {
5123 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
5124 goto clear_extattr;
5125 }
5126 if (entry->e_value_size &&
5127 region_allocate(region, entry->e_value_offs,
5128 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
5129 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5130 goto clear_extattr;
5131 }
5132 entry = EXT2_EXT_ATTR_NEXT(entry);
5133 }
5134 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
5135 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
5136 goto clear_extattr;
5137 }
5138 region_free(region);
5139
5140 count = header->h_refcount - 1;
5141 if (count)
5142 ea_refcount_store(ctx->refcount, blk, count);
5143 mark_block_used(ctx, blk);
5144 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
5145
5146 return 1;
5147
5148clear_extattr:
5149 inode->i_file_acl = 0;
5150 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
5151 return 0;
5152}
5153
5154/* Returns 1 if bad htree, 0 if OK */
5155static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005156 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005157 struct ext2_inode *inode,
5158 char *block_buf)
5159{
5160 struct ext2_dx_root_info *root;
5161 ext2_filsys fs = ctx->fs;
5162 errcode_t retval;
5163 blk_t blk;
5164
5165 if ((!LINUX_S_ISDIR(inode->i_mode) &&
5166 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
5167 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
5168 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
5169 return 1;
5170
5171 blk = inode->i_block[0];
5172 if (((blk == 0) ||
5173 (blk < fs->super->s_first_data_block) ||
5174 (blk >= fs->super->s_blocks_count)) &&
5175 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5176 return 1;
5177
5178 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
5179 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5180 return 1;
5181
5182 /* XXX should check that beginning matches a directory */
5183 root = (struct ext2_dx_root_info *) (block_buf + 24);
5184
5185 if ((root->reserved_zero || root->info_length < 8) &&
5186 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
5187 return 1;
5188
5189 pctx->num = root->hash_version;
5190 if ((root->hash_version != EXT2_HASH_LEGACY) &&
5191 (root->hash_version != EXT2_HASH_HALF_MD4) &&
5192 (root->hash_version != EXT2_HASH_TEA) &&
5193 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
5194 return 1;
5195
5196 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
5197 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
5198 return 1;
5199
5200 pctx->num = root->indirect_levels;
5201 if ((root->indirect_levels > 1) &&
5202 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
5203 return 1;
5204
5205 return 0;
5206}
5207
5208/*
5209 * This subroutine is called on each inode to account for all of the
5210 * blocks used by that inode.
5211 */
5212static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
5213 char *block_buf)
5214{
5215 ext2_filsys fs = ctx->fs;
5216 struct process_block_struct_1 pb;
5217 ext2_ino_t ino = pctx->ino;
5218 struct ext2_inode *inode = pctx->inode;
5219 int bad_size = 0;
5220 int dirty_inode = 0;
5221 __u64 size;
5222
5223 pb.ino = ino;
5224 pb.num_blocks = 0;
5225 pb.last_block = -1;
5226 pb.num_illegal_blocks = 0;
5227 pb.suppress = 0; pb.clear = 0;
5228 pb.fragmented = 0;
5229 pb.compressed = 0;
5230 pb.previous_block = 0;
5231 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
5232 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
5233 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
5234 pb.inode = inode;
5235 pb.pctx = pctx;
5236 pb.ctx = ctx;
5237 pctx->ino = ino;
5238 pctx->errcode = 0;
5239
5240 if (inode->i_flags & EXT2_COMPRBLK_FL) {
5241 if (fs->super->s_feature_incompat &
5242 EXT2_FEATURE_INCOMPAT_COMPRESSION)
5243 pb.compressed = 1;
5244 else {
5245 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
5246 inode->i_flags &= ~EXT2_COMPRBLK_FL;
5247 dirty_inode++;
5248 }
5249 }
5250 }
5251
5252 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
5253 pb.num_blocks++;
5254
5255 if (ext2fs_inode_has_valid_blocks(inode))
5256 pctx->errcode = ext2fs_block_iterate2(fs, ino,
5257 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
5258 block_buf, process_block, &pb);
5259 end_problem_latch(ctx, PR_LATCH_BLOCK);
5260 end_problem_latch(ctx, PR_LATCH_TOOBIG);
5261 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5262 goto out;
5263 if (pctx->errcode)
5264 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
5265
5266 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
5267 ctx->fs_fragmented++;
5268
5269 if (pb.clear) {
5270 inode->i_links_count = 0;
5271 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
5272 inode->i_dtime = time(0);
5273 dirty_inode++;
5274 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5275 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
5276 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5277 /*
5278 * The inode was probably partially accounted for
5279 * before processing was aborted, so we need to
5280 * restart the pass 1 scan.
5281 */
5282 ctx->flags |= E2F_FLAG_RESTART;
5283 goto out;
5284 }
5285
5286 if (inode->i_flags & EXT2_INDEX_FL) {
5287 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
5288 inode->i_flags &= ~EXT2_INDEX_FL;
5289 dirty_inode++;
5290 } else {
5291#ifdef ENABLE_HTREE
5292 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
5293#endif
5294 }
5295 }
5296 if (ctx->dirs_to_hash && pb.is_dir &&
5297 !(inode->i_flags & EXT2_INDEX_FL) &&
5298 ((inode->i_size / fs->blocksize) >= 3))
5299 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
5300
5301 if (!pb.num_blocks && pb.is_dir) {
5302 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
5303 inode->i_links_count = 0;
5304 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
5305 inode->i_dtime = time(0);
5306 dirty_inode++;
5307 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5308 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
5309 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5310 ctx->fs_directory_count--;
5311 goto out;
5312 }
5313 }
5314
5315 pb.num_blocks *= (fs->blocksize / 512);
5316#if 0
5317 printf("inode %u, i_size = %lu, last_block = %lld, i_blocks=%lu, num_blocks = %lu\n",
5318 ino, inode->i_size, pb.last_block, inode->i_blocks,
5319 pb.num_blocks);
5320#endif
5321 if (pb.is_dir) {
5322 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
5323 if (nblock > (pb.last_block + 1))
5324 bad_size = 1;
5325 else if (nblock < (pb.last_block + 1)) {
5326 if (((pb.last_block + 1) - nblock) >
5327 fs->super->s_prealloc_dir_blocks)
5328 bad_size = 2;
5329 }
5330 } else {
5331 size = EXT2_I_SIZE(inode);
5332 if ((pb.last_block >= 0) &&
5333 (size < (__u64) pb.last_block * fs->blocksize))
5334 bad_size = 3;
5335 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
5336 bad_size = 4;
5337 }
5338 /* i_size for symlinks is checked elsewhere */
5339 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
5340 pctx->num = (pb.last_block+1) * fs->blocksize;
5341 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
5342 inode->i_size = pctx->num;
5343 if (!LINUX_S_ISDIR(inode->i_mode))
5344 inode->i_size_high = pctx->num >> 32;
5345 dirty_inode++;
5346 }
5347 pctx->num = 0;
5348 }
5349 if (LINUX_S_ISREG(inode->i_mode) &&
5350 (inode->i_size_high || inode->i_size & 0x80000000UL))
5351 ctx->large_files++;
5352 if (pb.num_blocks != inode->i_blocks) {
5353 pctx->num = pb.num_blocks;
5354 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
5355 inode->i_blocks = pb.num_blocks;
5356 dirty_inode++;
5357 }
5358 pctx->num = 0;
5359 }
5360out:
5361 if (dirty_inode)
5362 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
5363}
5364
5365#if 0
5366/*
5367 * Helper function called by process block when an illegal block is
5368 * found. It returns a description about why the block is illegal
5369 */
5370static char *describe_illegal_block(ext2_filsys fs, blk_t block)
5371{
5372 blk_t super;
5373 int i;
5374 static char problem[80];
5375
5376 super = fs->super->s_first_data_block;
5377 strcpy(problem, "PROGRAMMING ERROR: Unknown reason for illegal block");
5378 if (block < super) {
5379 sprintf(problem, "< FIRSTBLOCK (%u)", super);
5380 return(problem);
5381 } else if (block >= fs->super->s_blocks_count) {
5382 sprintf(problem, "> BLOCKS (%u)", fs->super->s_blocks_count);
5383 return(problem);
5384 }
5385 for (i = 0; i < fs->group_desc_count; i++) {
5386 if (block == super) {
5387 sprintf(problem, "is the superblock in group %d", i);
5388 break;
5389 }
5390 if (block > super &&
5391 block <= (super + fs->desc_blocks)) {
5392 sprintf(problem, "is in the group descriptors "
5393 "of group %d", i);
5394 break;
5395 }
5396 if (block == fs->group_desc[i].bg_block_bitmap) {
5397 sprintf(problem, "is the block bitmap of group %d", i);
5398 break;
5399 }
5400 if (block == fs->group_desc[i].bg_inode_bitmap) {
5401 sprintf(problem, "is the inode bitmap of group %d", i);
5402 break;
5403 }
5404 if (block >= fs->group_desc[i].bg_inode_table &&
5405 (block < fs->group_desc[i].bg_inode_table
5406 + fs->inode_blocks_per_group)) {
5407 sprintf(problem, "is in the inode table of group %d",
5408 i);
5409 break;
5410 }
5411 super += fs->super->s_blocks_per_group;
5412 }
5413 return(problem);
5414}
5415#endif
5416
5417/*
5418 * This is a helper function for check_blocks().
5419 */
5420static int process_block(ext2_filsys fs,
5421 blk_t *block_nr,
5422 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005423 blk_t ref_block FSCK_ATTR((unused)),
5424 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005425 void *priv_data)
5426{
5427 struct process_block_struct_1 *p;
5428 struct problem_context *pctx;
5429 blk_t blk = *block_nr;
5430 int ret_code = 0;
5431 int problem = 0;
5432 e2fsck_t ctx;
5433
5434 p = (struct process_block_struct_1 *) priv_data;
5435 pctx = p->pctx;
5436 ctx = p->ctx;
5437
5438 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
5439 /* todo: Check that the comprblk_fl is high, that the
5440 blkaddr pattern looks right (all non-holes up to
5441 first EXT2FS_COMPRESSED_BLKADDR, then all
5442 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
5443 that the feature_incompat bit is high, and that the
5444 inode is a regular file. If we're doing a "full
5445 check" (a concept introduced to e2fsck by e2compr,
5446 meaning that we look at data blocks as well as
5447 metadata) then call some library routine that
5448 checks the compressed data. I'll have to think
5449 about this, because one particularly important
5450 problem to be able to fix is to recalculate the
5451 cluster size if necessary. I think that perhaps
5452 we'd better do most/all e2compr-specific checks
5453 separately, after the non-e2compr checks. If not
5454 doing a full check, it may be useful to test that
5455 the personality is linux; e.g. if it isn't then
5456 perhaps this really is just an illegal block. */
5457 return 0;
5458 }
5459
5460 if (blk == 0) {
5461 if (p->is_dir == 0) {
5462 /*
5463 * Should never happen, since only directories
5464 * get called with BLOCK_FLAG_HOLE
5465 */
5466#if DEBUG_E2FSCK
5467 printf("process_block() called with blk == 0, "
5468 "blockcnt=%d, inode %lu???\n",
5469 blockcnt, p->ino);
5470#endif
5471 return 0;
5472 }
5473 if (blockcnt < 0)
5474 return 0;
5475 if (blockcnt * fs->blocksize < p->inode->i_size) {
5476#if 0
5477 printf("Missing block (#%d) in directory inode %lu!\n",
5478 blockcnt, p->ino);
5479#endif
5480 goto mark_dir;
5481 }
5482 return 0;
5483 }
5484
5485#if 0
5486 printf("Process_block, inode %lu, block %u, #%d\n", p->ino, blk,
5487 blockcnt);
5488#endif
5489
5490 /*
5491 * Simplistic fragmentation check. We merely require that the
5492 * file be contiguous. (Which can never be true for really
5493 * big files that are greater than a block group.)
5494 */
5495 if (!HOLE_BLKADDR(p->previous_block)) {
5496 if (p->previous_block+1 != blk)
5497 p->fragmented = 1;
5498 }
5499 p->previous_block = blk;
5500
5501 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
5502 problem = PR_1_TOOBIG_DIR;
5503 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
5504 problem = PR_1_TOOBIG_REG;
5505 if (!p->is_dir && !p->is_reg && blockcnt > 0)
5506 problem = PR_1_TOOBIG_SYMLINK;
5507
5508 if (blk < fs->super->s_first_data_block ||
5509 blk >= fs->super->s_blocks_count)
5510 problem = PR_1_ILLEGAL_BLOCK_NUM;
5511
5512 if (problem) {
5513 p->num_illegal_blocks++;
5514 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
5515 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
5516 p->clear = 1;
5517 return BLOCK_ABORT;
5518 }
5519 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
5520 p->suppress = 1;
5521 set_latch_flags(PR_LATCH_BLOCK,
5522 PRL_SUPPRESS, 0);
5523 }
5524 }
5525 pctx->blk = blk;
5526 pctx->blkcount = blockcnt;
5527 if (fix_problem(ctx, problem, pctx)) {
5528 blk = *block_nr = 0;
5529 ret_code = BLOCK_CHANGED;
5530 goto mark_dir;
5531 } else
5532 return 0;
5533 }
5534
5535 if (p->ino == EXT2_RESIZE_INO) {
5536 /*
5537 * The resize inode has already be sanity checked
5538 * during pass #0 (the superblock checks). All we
5539 * have to do is mark the double indirect block as
5540 * being in use; all of the other blocks are handled
5541 * by mark_table_blocks()).
5542 */
5543 if (blockcnt == BLOCK_COUNT_DIND)
5544 mark_block_used(ctx, blk);
5545 } else
5546 mark_block_used(ctx, blk);
5547 p->num_blocks++;
5548 if (blockcnt >= 0)
5549 p->last_block = blockcnt;
5550mark_dir:
5551 if (p->is_dir && (blockcnt >= 0)) {
5552 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
5553 blk, blockcnt);
5554 if (pctx->errcode) {
5555 pctx->blk = blk;
5556 pctx->num = blockcnt;
5557 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
5558 /* Should never get here */
5559 ctx->flags |= E2F_FLAG_ABORT;
5560 return BLOCK_ABORT;
5561 }
5562 }
5563 return ret_code;
5564}
5565
5566static int process_bad_block(ext2_filsys fs,
5567 blk_t *block_nr,
5568 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005569 blk_t ref_block FSCK_ATTR((unused)),
5570 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005571 void *priv_data)
5572{
5573 struct process_block_struct_1 *p;
5574 blk_t blk = *block_nr;
5575 blk_t first_block;
5576 dgrp_t i;
5577 struct problem_context *pctx;
5578 e2fsck_t ctx;
5579
5580 /*
5581 * Note: This function processes blocks for the bad blocks
5582 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
5583 */
5584
5585 if (!blk)
5586 return 0;
5587
5588 p = (struct process_block_struct_1 *) priv_data;
5589 ctx = p->ctx;
5590 pctx = p->pctx;
5591
5592 pctx->ino = EXT2_BAD_INO;
5593 pctx->blk = blk;
5594 pctx->blkcount = blockcnt;
5595
5596 if ((blk < fs->super->s_first_data_block) ||
5597 (blk >= fs->super->s_blocks_count)) {
5598 if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
5599 *block_nr = 0;
5600 return BLOCK_CHANGED;
5601 } else
5602 return 0;
5603 }
5604
5605 if (blockcnt < 0) {
5606 if (ext2fs_test_block_bitmap(p->fs_meta_blocks, blk)) {
5607 p->bbcheck = 1;
5608 if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
5609 *block_nr = 0;
5610 return BLOCK_CHANGED;
5611 }
5612 } else if (ext2fs_test_block_bitmap(ctx->block_found_map,
5613 blk)) {
5614 p->bbcheck = 1;
5615 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
5616 pctx)) {
5617 *block_nr = 0;
5618 return BLOCK_CHANGED;
5619 }
5620 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5621 return BLOCK_ABORT;
5622 } else
5623 mark_block_used(ctx, blk);
5624 return 0;
5625 }
5626#if 0
5627 printf ("DEBUG: Marking %u as bad.\n", blk);
5628#endif
5629 ctx->fs_badblocks_count++;
5630 /*
5631 * If the block is not used, then mark it as used and return.
5632 * If it is already marked as found, this must mean that
5633 * there's an overlap between the filesystem table blocks
5634 * (bitmaps and inode table) and the bad block list.
5635 */
5636 if (!ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
5637 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
5638 return 0;
5639 }
5640 /*
5641 * Try to find the where the filesystem block was used...
5642 */
5643 first_block = fs->super->s_first_data_block;
5644
5645 for (i = 0; i < fs->group_desc_count; i++ ) {
5646 pctx->group = i;
5647 pctx->blk = blk;
5648 if (!ext2fs_bg_has_super(fs, i))
5649 goto skip_super;
5650 if (blk == first_block) {
5651 if (i == 0) {
5652 if (fix_problem(ctx,
5653 PR_1_BAD_PRIMARY_SUPERBLOCK,
5654 pctx)) {
5655 *block_nr = 0;
5656 return BLOCK_CHANGED;
5657 }
5658 return 0;
5659 }
5660 fix_problem(ctx, PR_1_BAD_SUPERBLOCK, pctx);
5661 return 0;
5662 }
5663 if ((blk > first_block) &&
5664 (blk <= first_block + fs->desc_blocks)) {
5665 if (i == 0) {
5666 pctx->blk = *block_nr;
5667 if (fix_problem(ctx,
5668 PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, pctx)) {
5669 *block_nr = 0;
5670 return BLOCK_CHANGED;
5671 }
5672 return 0;
5673 }
5674 fix_problem(ctx, PR_1_BAD_GROUP_DESCRIPTORS, pctx);
5675 return 0;
5676 }
5677 skip_super:
5678 if (blk == fs->group_desc[i].bg_block_bitmap) {
5679 if (fix_problem(ctx, PR_1_BB_BAD_BLOCK, pctx)) {
5680 ctx->invalid_block_bitmap_flag[i]++;
5681 ctx->invalid_bitmaps++;
5682 }
5683 return 0;
5684 }
5685 if (blk == fs->group_desc[i].bg_inode_bitmap) {
5686 if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
5687 ctx->invalid_inode_bitmap_flag[i]++;
5688 ctx->invalid_bitmaps++;
5689 }
5690 return 0;
5691 }
5692 if ((blk >= fs->group_desc[i].bg_inode_table) &&
5693 (blk < (fs->group_desc[i].bg_inode_table +
5694 fs->inode_blocks_per_group))) {
5695 /*
5696 * If there are bad blocks in the inode table,
5697 * the inode scan code will try to do
5698 * something reasonable automatically.
5699 */
5700 return 0;
5701 }
5702 first_block += fs->super->s_blocks_per_group;
5703 }
5704 /*
5705 * If we've gotten to this point, then the only
5706 * possibility is that the bad block inode meta data
5707 * is using a bad block.
5708 */
5709 if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
5710 (blk == p->inode->i_block[EXT2_DIND_BLOCK]) ||
5711 (blk == p->inode->i_block[EXT2_TIND_BLOCK])) {
5712 p->bbcheck = 1;
5713 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK, pctx)) {
5714 *block_nr = 0;
5715 return BLOCK_CHANGED;
5716 }
5717 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5718 return BLOCK_ABORT;
5719 return 0;
5720 }
5721
5722 pctx->group = -1;
5723
5724 /* Warn user that the block wasn't claimed */
5725 fix_problem(ctx, PR_1_PROGERR_CLAIMED_BLOCK, pctx);
5726
5727 return 0;
5728}
5729
5730static void new_table_block(e2fsck_t ctx, blk_t first_block, int group,
5731 const char *name, int num, blk_t *new_block)
5732{
5733 ext2_filsys fs = ctx->fs;
5734 blk_t old_block = *new_block;
5735 int i;
5736 char *buf;
5737 struct problem_context pctx;
5738
5739 clear_problem_context(&pctx);
5740
5741 pctx.group = group;
5742 pctx.blk = old_block;
5743 pctx.str = name;
5744
5745 pctx.errcode = ext2fs_get_free_blocks(fs, first_block,
5746 first_block + fs->super->s_blocks_per_group,
5747 num, ctx->block_found_map, new_block);
5748 if (pctx.errcode) {
5749 pctx.num = num;
5750 fix_problem(ctx, PR_1_RELOC_BLOCK_ALLOCATE, &pctx);
5751 ext2fs_unmark_valid(fs);
5752 return;
5753 }
5754 pctx.errcode = ext2fs_get_mem(fs->blocksize, &buf);
5755 if (pctx.errcode) {
5756 fix_problem(ctx, PR_1_RELOC_MEMORY_ALLOCATE, &pctx);
5757 ext2fs_unmark_valid(fs);
5758 return;
5759 }
5760 ext2fs_mark_super_dirty(fs);
5761 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
5762 pctx.blk2 = *new_block;
5763 fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
5764 PR_1_RELOC_TO), &pctx);
5765 pctx.blk2 = 0;
5766 for (i = 0; i < num; i++) {
5767 pctx.blk = i;
5768 ext2fs_mark_block_bitmap(ctx->block_found_map, (*new_block)+i);
5769 if (old_block) {
5770 pctx.errcode = io_channel_read_blk(fs->io,
5771 old_block + i, 1, buf);
5772 if (pctx.errcode)
5773 fix_problem(ctx, PR_1_RELOC_READ_ERR, &pctx);
5774 } else
5775 memset(buf, 0, fs->blocksize);
5776
5777 pctx.blk = (*new_block) + i;
5778 pctx.errcode = io_channel_write_blk(fs->io, pctx.blk,
5779 1, buf);
5780 if (pctx.errcode)
5781 fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
5782 }
5783 ext2fs_free_mem(&buf);
5784}
5785
5786/*
5787 * This routine gets called at the end of pass 1 if bad blocks are
5788 * detected in the superblock, group descriptors, inode_bitmaps, or
5789 * block bitmaps. At this point, all of the blocks have been mapped
5790 * out, so we can try to allocate new block(s) to replace the bad
5791 * blocks.
5792 */
5793static void handle_fs_bad_blocks(e2fsck_t ctx)
5794{
5795 ext2_filsys fs = ctx->fs;
5796 dgrp_t i;
5797 int first_block = fs->super->s_first_data_block;
5798
5799 for (i = 0; i < fs->group_desc_count; i++) {
5800 if (ctx->invalid_block_bitmap_flag[i]) {
5801 new_table_block(ctx, first_block, i, _("block bitmap"),
5802 1, &fs->group_desc[i].bg_block_bitmap);
5803 }
5804 if (ctx->invalid_inode_bitmap_flag[i]) {
5805 new_table_block(ctx, first_block, i, _("inode bitmap"),
5806 1, &fs->group_desc[i].bg_inode_bitmap);
5807 }
5808 if (ctx->invalid_inode_table_flag[i]) {
5809 new_table_block(ctx, first_block, i, _("inode table"),
5810 fs->inode_blocks_per_group,
5811 &fs->group_desc[i].bg_inode_table);
5812 ctx->flags |= E2F_FLAG_RESTART;
5813 }
5814 first_block += fs->super->s_blocks_per_group;
5815 }
5816 ctx->invalid_bitmaps = 0;
5817}
5818
5819/*
5820 * This routine marks all blocks which are used by the superblock,
5821 * group descriptors, inode bitmaps, and block bitmaps.
5822 */
5823static void mark_table_blocks(e2fsck_t ctx)
5824{
5825 ext2_filsys fs = ctx->fs;
5826 blk_t block, b;
5827 dgrp_t i;
5828 int j;
5829 struct problem_context pctx;
5830
5831 clear_problem_context(&pctx);
5832
5833 block = fs->super->s_first_data_block;
5834 for (i = 0; i < fs->group_desc_count; i++) {
5835 pctx.group = i;
5836
5837 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
5838
5839 /*
5840 * Mark the blocks used for the inode table
5841 */
5842 if (fs->group_desc[i].bg_inode_table) {
5843 for (j = 0, b = fs->group_desc[i].bg_inode_table;
5844 j < fs->inode_blocks_per_group;
5845 j++, b++) {
5846 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5847 b)) {
5848 pctx.blk = b;
5849 if (fix_problem(ctx,
5850 PR_1_ITABLE_CONFLICT, &pctx)) {
5851 ctx->invalid_inode_table_flag[i]++;
5852 ctx->invalid_bitmaps++;
5853 }
5854 } else {
5855 ext2fs_mark_block_bitmap(ctx->block_found_map,
5856 b);
5857 }
5858 }
5859 }
5860
5861 /*
5862 * Mark block used for the block bitmap
5863 */
5864 if (fs->group_desc[i].bg_block_bitmap) {
5865 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5866 fs->group_desc[i].bg_block_bitmap)) {
5867 pctx.blk = fs->group_desc[i].bg_block_bitmap;
5868 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
5869 ctx->invalid_block_bitmap_flag[i]++;
5870 ctx->invalid_bitmaps++;
5871 }
5872 } else {
5873 ext2fs_mark_block_bitmap(ctx->block_found_map,
5874 fs->group_desc[i].bg_block_bitmap);
5875 }
5876
5877 }
5878 /*
5879 * Mark block used for the inode bitmap
5880 */
5881 if (fs->group_desc[i].bg_inode_bitmap) {
5882 if (ext2fs_test_block_bitmap(ctx->block_found_map,
5883 fs->group_desc[i].bg_inode_bitmap)) {
5884 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
5885 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
5886 ctx->invalid_inode_bitmap_flag[i]++;
5887 ctx->invalid_bitmaps++;
5888 }
5889 } else {
5890 ext2fs_mark_block_bitmap(ctx->block_found_map,
5891 fs->group_desc[i].bg_inode_bitmap);
5892 }
5893 }
5894 block += fs->super->s_blocks_per_group;
5895 }
5896}
5897
5898/*
5899 * Thes subroutines short circuits ext2fs_get_blocks and
5900 * ext2fs_check_directory; we use them since we already have the inode
5901 * structure, so there's no point in letting the ext2fs library read
5902 * the inode again.
5903 */
5904static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
5905 blk_t *blocks)
5906{
5907 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5908 int i;
5909
5910 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5911 return EXT2_ET_CALLBACK_NOTHANDLED;
5912
5913 for (i=0; i < EXT2_N_BLOCKS; i++)
5914 blocks[i] = ctx->stashed_inode->i_block[i];
5915 return 0;
5916}
5917
5918static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
5919 struct ext2_inode *inode)
5920{
5921 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5922
5923 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5924 return EXT2_ET_CALLBACK_NOTHANDLED;
5925 *inode = *ctx->stashed_inode;
5926 return 0;
5927}
5928
5929static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
5930 struct ext2_inode *inode)
5931{
5932 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5933
5934 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
5935 *ctx->stashed_inode = *inode;
5936 return EXT2_ET_CALLBACK_NOTHANDLED;
5937}
5938
5939static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
5940{
5941 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
5942
5943 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
5944 return EXT2_ET_CALLBACK_NOTHANDLED;
5945
5946 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
5947 return EXT2_ET_NO_DIRECTORY;
5948 return 0;
5949}
5950
5951void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
5952{
5953 ext2_filsys fs = ctx->fs;
5954
5955 if (bool) {
5956 fs->get_blocks = pass1_get_blocks;
5957 fs->check_directory = pass1_check_directory;
5958 fs->read_inode = pass1_read_inode;
5959 fs->write_inode = pass1_write_inode;
5960 ctx->stashed_ino = 0;
5961 } else {
5962 fs->get_blocks = 0;
5963 fs->check_directory = 0;
5964 fs->read_inode = 0;
5965 fs->write_inode = 0;
5966 }
5967}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005968
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005969/*
5970 * pass1b.c --- Pass #1b of e2fsck
5971 *
5972 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
5973 * only invoked if pass 1 discovered blocks which are in use by more
5974 * than one inode.
5975 *
5976 * Pass1B scans the data blocks of all the inodes again, generating a
5977 * complete list of duplicate blocks and which inodes have claimed
5978 * them.
5979 *
5980 * Pass1C does a tree-traversal of the filesystem, to determine the
5981 * parent directories of these inodes. This step is necessary so that
5982 * e2fsck can print out the pathnames of affected inodes.
5983 *
5984 * Pass1D is a reconciliation pass. For each inode with duplicate
5985 * blocks, the user is prompted if s/he would like to clone the file
5986 * (so that the file gets a fresh copy of the duplicated blocks) or
5987 * simply to delete the file.
5988 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005989 */
5990
5991
5992/* Needed for architectures where sizeof(int) != sizeof(void *) */
5993#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
5994#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
5995
5996/* Define an extension to the ext2 library's block count information */
5997#define BLOCK_COUNT_EXTATTR (-5)
5998
5999struct block_el {
6000 blk_t block;
6001 struct block_el *next;
6002};
6003
6004struct inode_el {
6005 ext2_ino_t inode;
6006 struct inode_el *next;
6007};
6008
6009struct dup_block {
6010 int num_bad;
6011 struct inode_el *inode_list;
6012};
6013
6014/*
6015 * This structure stores information about a particular inode which
6016 * is sharing blocks with other inodes. This information is collected
6017 * to display to the user, so that the user knows what files he or she
6018 * is dealing with, when trying to decide how to resolve the conflict
6019 * of multiply-claimed blocks.
6020 */
6021struct dup_inode {
6022 ext2_ino_t dir;
6023 int num_dupblocks;
6024 struct ext2_inode inode;
6025 struct block_el *block_list;
6026};
6027
6028static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
6029 e2_blkcnt_t blockcnt, blk_t ref_blk,
6030 int ref_offset, void *priv_data);
6031static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
6032 struct dup_inode *dp, char *block_buf);
6033static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
6034 struct dup_inode *dp, char* block_buf);
6035static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
6036
6037static void pass1b(e2fsck_t ctx, char *block_buf);
6038static void pass1c(e2fsck_t ctx, char *block_buf);
6039static void pass1d(e2fsck_t ctx, char *block_buf);
6040
6041static int dup_inode_count = 0;
6042
6043static dict_t blk_dict, ino_dict;
6044
6045static ext2fs_inode_bitmap inode_dup_map;
6046
6047static int dict_int_cmp(const void *a, const void *b)
6048{
6049 intptr_t ia, ib;
6050
6051 ia = (intptr_t)a;
6052 ib = (intptr_t)b;
6053
6054 return (ia-ib);
6055}
6056
6057/*
6058 * Add a duplicate block record
6059 */
6060static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
6061 struct ext2_inode *inode)
6062{
6063 dnode_t *n;
6064 struct dup_block *db;
6065 struct dup_inode *di;
6066 struct block_el *blk_el;
6067 struct inode_el *ino_el;
6068
6069 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
6070 if (n)
6071 db = (struct dup_block *) dnode_get(n);
6072 else {
6073 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
6074 sizeof(struct dup_block), "duplicate block header");
6075 db->num_bad = 0;
6076 db->inode_list = 0;
6077 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
6078 }
6079 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
6080 sizeof(struct inode_el), "inode element");
6081 ino_el->inode = ino;
6082 ino_el->next = db->inode_list;
6083 db->inode_list = ino_el;
6084 db->num_bad++;
6085
6086 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
6087 if (n)
6088 di = (struct dup_inode *) dnode_get(n);
6089 else {
6090 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
6091 sizeof(struct dup_inode), "duplicate inode header");
6092 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
6093 di->num_dupblocks = 0;
6094 di->block_list = 0;
6095 di->inode = *inode;
6096 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
6097 }
6098 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
6099 sizeof(struct block_el), "block element");
6100 blk_el->block = blk;
6101 blk_el->next = di->block_list;
6102 di->block_list = blk_el;
6103 di->num_dupblocks++;
6104}
6105
6106/*
6107 * Free a duplicate inode record
6108 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006109static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006110{
6111 struct dup_inode *di;
6112 struct block_el *p, *next;
6113
6114 di = (struct dup_inode *) dnode_get(node);
6115 for (p = di->block_list; p; p = next) {
6116 next = p->next;
6117 free(p);
6118 }
6119 free(node);
6120}
6121
6122/*
6123 * Free a duplicate block record
6124 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006125static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006126{
6127 struct dup_block *db;
6128 struct inode_el *p, *next;
6129
6130 db = (struct dup_block *) dnode_get(node);
6131 for (p = db->inode_list; p; p = next) {
6132 next = p->next;
6133 free(p);
6134 }
6135 free(node);
6136}
6137
6138
6139/*
6140 * Main procedure for handling duplicate blocks
6141 */
6142void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
6143{
6144 ext2_filsys fs = ctx->fs;
6145 struct problem_context pctx;
6146
6147 clear_problem_context(&pctx);
6148
6149 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
6150 _("multiply claimed inode map"), &inode_dup_map);
6151 if (pctx.errcode) {
6152 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
6153 ctx->flags |= E2F_FLAG_ABORT;
6154 return;
6155 }
6156
6157 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
6158 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006159 dict_set_allocator(&ino_dict, inode_dnode_free);
6160 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006161
6162 pass1b(ctx, block_buf);
6163 pass1c(ctx, block_buf);
6164 pass1d(ctx, block_buf);
6165
6166 /*
6167 * Time to free all of the accumulated data structures that we
6168 * don't need anymore.
6169 */
6170 dict_free_nodes(&ino_dict);
6171 dict_free_nodes(&blk_dict);
6172}
6173
6174/*
6175 * Scan the inodes looking for inodes that contain duplicate blocks.
6176 */
6177struct process_block_struct_1b {
6178 e2fsck_t ctx;
6179 ext2_ino_t ino;
6180 int dup_blocks;
6181 struct ext2_inode *inode;
6182 struct problem_context *pctx;
6183};
6184
6185static void pass1b(e2fsck_t ctx, char *block_buf)
6186{
6187 ext2_filsys fs = ctx->fs;
6188 ext2_ino_t ino;
6189 struct ext2_inode inode;
6190 ext2_inode_scan scan;
6191 struct process_block_struct_1b pb;
6192 struct problem_context pctx;
6193
6194 clear_problem_context(&pctx);
6195
6196 if (!(ctx->options & E2F_OPT_PREEN))
6197 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
6198 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
6199 &scan);
6200 if (pctx.errcode) {
6201 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
6202 ctx->flags |= E2F_FLAG_ABORT;
6203 return;
6204 }
6205 ctx->stashed_inode = &inode;
6206 pb.ctx = ctx;
6207 pb.pctx = &pctx;
6208 pctx.str = "pass1b";
6209 while (1) {
6210 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
6211 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
6212 continue;
6213 if (pctx.errcode) {
6214 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
6215 ctx->flags |= E2F_FLAG_ABORT;
6216 return;
6217 }
6218 if (!ino)
6219 break;
6220 pctx.ino = ctx->stashed_ino = ino;
6221 if ((ino != EXT2_BAD_INO) &&
6222 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
6223 continue;
6224
6225 pb.ino = ino;
6226 pb.dup_blocks = 0;
6227 pb.inode = &inode;
6228
6229 if (ext2fs_inode_has_valid_blocks(&inode) ||
6230 (ino == EXT2_BAD_INO))
6231 pctx.errcode = ext2fs_block_iterate2(fs, ino,
6232 0, block_buf, process_pass1b_block, &pb);
6233 if (inode.i_file_acl)
6234 process_pass1b_block(fs, &inode.i_file_acl,
6235 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
6236 if (pb.dup_blocks) {
6237 end_problem_latch(ctx, PR_LATCH_DBLOCK);
6238 if (ino >= EXT2_FIRST_INODE(fs->super) ||
6239 ino == EXT2_ROOT_INO)
6240 dup_inode_count++;
6241 }
6242 if (pctx.errcode)
6243 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6244 }
6245 ext2fs_close_inode_scan(scan);
6246 e2fsck_use_inode_shortcuts(ctx, 0);
6247}
6248
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006249static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006250 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006251 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6252 blk_t ref_blk FSCK_ATTR((unused)),
6253 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006254 void *priv_data)
6255{
6256 struct process_block_struct_1b *p;
6257 e2fsck_t ctx;
6258
6259 if (HOLE_BLKADDR(*block_nr))
6260 return 0;
6261 p = (struct process_block_struct_1b *) priv_data;
6262 ctx = p->ctx;
6263
6264 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
6265 return 0;
6266
6267 /* OK, this is a duplicate block */
6268 if (p->ino != EXT2_BAD_INO) {
6269 p->pctx->blk = *block_nr;
6270 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
6271 }
6272 p->dup_blocks++;
6273 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
6274
6275 add_dupe(ctx, p->ino, *block_nr, p->inode);
6276
6277 return 0;
6278}
6279
6280/*
6281 * Pass 1c: Scan directories for inodes with duplicate blocks. This
6282 * is used so that we can print pathnames when prompting the user for
6283 * what to do.
6284 */
6285struct search_dir_struct {
6286 int count;
6287 ext2_ino_t first_inode;
6288 ext2_ino_t max_inode;
6289};
6290
6291static int search_dirent_proc(ext2_ino_t dir, int entry,
6292 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006293 int offset FSCK_ATTR((unused)),
6294 int blocksize FSCK_ATTR((unused)),
6295 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006296 void *priv_data)
6297{
6298 struct search_dir_struct *sd;
6299 struct dup_inode *p;
6300 dnode_t *n;
6301
6302 sd = (struct search_dir_struct *) priv_data;
6303
6304 if (dirent->inode > sd->max_inode)
6305 /* Should abort this inode, but not everything */
6306 return 0;
6307
6308 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
6309 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
6310 return 0;
6311
6312 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
6313 if (!n)
6314 return 0;
6315 p = (struct dup_inode *) dnode_get(n);
6316 p->dir = dir;
6317 sd->count--;
6318
6319 return(sd->count ? 0 : DIRENT_ABORT);
6320}
6321
6322
6323static void pass1c(e2fsck_t ctx, char *block_buf)
6324{
6325 ext2_filsys fs = ctx->fs;
6326 struct search_dir_struct sd;
6327 struct problem_context pctx;
6328
6329 clear_problem_context(&pctx);
6330
6331 if (!(ctx->options & E2F_OPT_PREEN))
6332 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
6333
6334 /*
6335 * Search through all directories to translate inodes to names
6336 * (by searching for the containing directory for that inode.)
6337 */
6338 sd.count = dup_inode_count;
6339 sd.first_inode = EXT2_FIRST_INODE(fs->super);
6340 sd.max_inode = fs->super->s_inodes_count;
6341 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
6342 search_dirent_proc, &sd);
6343}
6344
6345static void pass1d(e2fsck_t ctx, char *block_buf)
6346{
6347 ext2_filsys fs = ctx->fs;
6348 struct dup_inode *p, *t;
6349 struct dup_block *q;
6350 ext2_ino_t *shared, ino;
6351 int shared_len;
6352 int i;
6353 int file_ok;
6354 int meta_data = 0;
6355 struct problem_context pctx;
6356 dnode_t *n, *m;
6357 struct block_el *s;
6358 struct inode_el *r;
6359
6360 clear_problem_context(&pctx);
6361
6362 if (!(ctx->options & E2F_OPT_PREEN))
6363 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
6364 e2fsck_read_bitmaps(ctx);
6365
6366 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
6367 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
6368 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
6369 sizeof(ext2_ino_t) * dict_count(&ino_dict),
6370 "Shared inode list");
6371 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
6372 p = (struct dup_inode *) dnode_get(n);
6373 shared_len = 0;
6374 file_ok = 1;
6375 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00006376 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006377 continue;
6378
6379 /*
6380 * Find all of the inodes which share blocks with this
6381 * one. First we find all of the duplicate blocks
6382 * belonging to this inode, and then search each block
6383 * get the list of inodes, and merge them together.
6384 */
6385 for (s = p->block_list; s; s = s->next) {
6386 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
6387 if (!m)
6388 continue; /* Should never happen... */
6389 q = (struct dup_block *) dnode_get(m);
6390 if (q->num_bad > 1)
6391 file_ok = 0;
6392 if (check_if_fs_block(ctx, s->block)) {
6393 file_ok = 0;
6394 meta_data = 1;
6395 }
6396
6397 /*
6398 * Add all inodes used by this block to the
6399 * shared[] --- which is a unique list, so
6400 * if an inode is already in shared[], don't
6401 * add it again.
6402 */
6403 for (r = q->inode_list; r; r = r->next) {
6404 if (r->inode == ino)
6405 continue;
6406 for (i = 0; i < shared_len; i++)
6407 if (shared[i] == r->inode)
6408 break;
6409 if (i == shared_len) {
6410 shared[shared_len++] = r->inode;
6411 }
6412 }
6413 }
6414
6415 /*
6416 * Report the inode that we are working on
6417 */
6418 pctx.inode = &p->inode;
6419 pctx.ino = ino;
6420 pctx.dir = p->dir;
6421 pctx.blkcount = p->num_dupblocks;
6422 pctx.num = meta_data ? shared_len+1 : shared_len;
6423 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
6424 pctx.blkcount = 0;
6425 pctx.num = 0;
6426
6427 if (meta_data)
6428 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
6429
6430 for (i = 0; i < shared_len; i++) {
6431 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
6432 if (!m)
6433 continue; /* should never happen */
6434 t = (struct dup_inode *) dnode_get(m);
6435 /*
6436 * Report the inode that we are sharing with
6437 */
6438 pctx.inode = &t->inode;
6439 pctx.ino = shared[i];
6440 pctx.dir = t->dir;
6441 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
6442 }
6443 if (file_ok) {
6444 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
6445 continue;
6446 }
6447 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
6448 pctx.errcode = clone_file(ctx, ino, p, block_buf);
6449 if (pctx.errcode)
6450 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
6451 else
6452 continue;
6453 }
6454 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
6455 delete_file(ctx, ino, p, block_buf);
6456 else
6457 ext2fs_unmark_valid(fs);
6458 }
6459 ext2fs_free_mem(&shared);
6460}
6461
6462/*
6463 * Drop the refcount on the dup_block structure, and clear the entry
6464 * in the block_dup_map if appropriate.
6465 */
6466static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
6467{
6468 p->num_bad--;
6469 if (p->num_bad <= 0 ||
6470 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
6471 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
6472}
6473
6474static int delete_file_block(ext2_filsys fs,
6475 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006476 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6477 blk_t ref_block FSCK_ATTR((unused)),
6478 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006479 void *priv_data)
6480{
6481 struct process_block_struct_1b *pb;
6482 struct dup_block *p;
6483 dnode_t *n;
6484 e2fsck_t ctx;
6485
6486 pb = (struct process_block_struct_1b *) priv_data;
6487 ctx = pb->ctx;
6488
6489 if (HOLE_BLKADDR(*block_nr))
6490 return 0;
6491
6492 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
6493 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
6494 if (n) {
6495 p = (struct dup_block *) dnode_get(n);
6496 decrement_badcount(ctx, *block_nr, p);
6497 } else
6498 com_err("delete_file_block", 0,
6499 _("internal error; can't find dup_blk for %d\n"),
6500 *block_nr);
6501 } else {
6502 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6503 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6504 }
6505
6506 return 0;
6507}
6508
6509static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
6510 struct dup_inode *dp, char* block_buf)
6511{
6512 ext2_filsys fs = ctx->fs;
6513 struct process_block_struct_1b pb;
6514 struct ext2_inode inode;
6515 struct problem_context pctx;
6516 unsigned int count;
6517
6518 clear_problem_context(&pctx);
6519 pctx.ino = pb.ino = ino;
6520 pb.dup_blocks = dp->num_dupblocks;
6521 pb.ctx = ctx;
6522 pctx.str = "delete_file";
6523
6524 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
6525 if (ext2fs_inode_has_valid_blocks(&inode))
6526 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6527 delete_file_block, &pb);
6528 if (pctx.errcode)
6529 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6530 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6531 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6532 if (ctx->inode_bad_map)
6533 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6534 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6535
6536 /* Inode may have changed by block_iterate, so reread it */
6537 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
6538 inode.i_links_count = 0;
6539 inode.i_dtime = time(0);
6540 if (inode.i_file_acl &&
6541 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6542 count = 1;
6543 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6544 block_buf, -1, &count);
6545 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6546 pctx.errcode = 0;
6547 count = 1;
6548 }
6549 if (pctx.errcode) {
6550 pctx.blk = inode.i_file_acl;
6551 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
6552 }
6553 /*
6554 * If the count is zero, then arrange to have the
6555 * block deleted. If the block is in the block_dup_map,
6556 * also call delete_file_block since it will take care
6557 * of keeping the accounting straight.
6558 */
6559 if ((count == 0) ||
6560 ext2fs_test_block_bitmap(ctx->block_dup_map,
6561 inode.i_file_acl))
6562 delete_file_block(fs, &inode.i_file_acl,
6563 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
6564 }
6565 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
6566}
6567
6568struct clone_struct {
6569 errcode_t errcode;
6570 ext2_ino_t dir;
6571 char *buf;
6572 e2fsck_t ctx;
6573};
6574
6575static int clone_file_block(ext2_filsys fs,
6576 blk_t *block_nr,
6577 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006578 blk_t ref_block FSCK_ATTR((unused)),
6579 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006580 void *priv_data)
6581{
6582 struct dup_block *p;
6583 blk_t new_block;
6584 errcode_t retval;
6585 struct clone_struct *cs = (struct clone_struct *) priv_data;
6586 dnode_t *n;
6587 e2fsck_t ctx;
6588
6589 ctx = cs->ctx;
6590
6591 if (HOLE_BLKADDR(*block_nr))
6592 return 0;
6593
6594 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
6595 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
6596 if (n) {
6597 p = (struct dup_block *) dnode_get(n);
6598 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
6599 &new_block);
6600 if (retval) {
6601 cs->errcode = retval;
6602 return BLOCK_ABORT;
6603 }
6604 if (cs->dir && (blockcnt >= 0)) {
6605 retval = ext2fs_set_dir_block(fs->dblist,
6606 cs->dir, new_block, blockcnt);
6607 if (retval) {
6608 cs->errcode = retval;
6609 return BLOCK_ABORT;
6610 }
6611 }
6612#if 0
6613 printf("Cloning block %u to %u\n", *block_nr,
6614 new_block);
6615#endif
6616 retval = io_channel_read_blk(fs->io, *block_nr, 1,
6617 cs->buf);
6618 if (retval) {
6619 cs->errcode = retval;
6620 return BLOCK_ABORT;
6621 }
6622 retval = io_channel_write_blk(fs->io, new_block, 1,
6623 cs->buf);
6624 if (retval) {
6625 cs->errcode = retval;
6626 return BLOCK_ABORT;
6627 }
6628 decrement_badcount(ctx, *block_nr, p);
6629 *block_nr = new_block;
6630 ext2fs_mark_block_bitmap(ctx->block_found_map,
6631 new_block);
6632 ext2fs_mark_block_bitmap(fs->block_map, new_block);
6633 return BLOCK_CHANGED;
6634 } else
6635 com_err("clone_file_block", 0,
6636 _("internal error; can't find dup_blk for %d\n"),
6637 *block_nr);
6638 }
6639 return 0;
6640}
6641
6642static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
6643 struct dup_inode *dp, char* block_buf)
6644{
6645 ext2_filsys fs = ctx->fs;
6646 errcode_t retval;
6647 struct clone_struct cs;
6648 struct problem_context pctx;
6649 blk_t blk;
6650 dnode_t *n;
6651 struct inode_el *ino_el;
6652 struct dup_block *db;
6653 struct dup_inode *di;
6654
6655 clear_problem_context(&pctx);
6656 cs.errcode = 0;
6657 cs.dir = 0;
6658 cs.ctx = ctx;
6659 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
6660 if (retval)
6661 return retval;
6662
6663 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
6664 cs.dir = ino;
6665
6666 pctx.ino = ino;
6667 pctx.str = "clone_file";
6668 if (ext2fs_inode_has_valid_blocks(&dp->inode))
6669 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6670 clone_file_block, &cs);
6671 ext2fs_mark_bb_dirty(fs);
6672 if (pctx.errcode) {
6673 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
6674 retval = pctx.errcode;
6675 goto errout;
6676 }
6677 if (cs.errcode) {
6678 com_err("clone_file", cs.errcode,
6679 _("returned from clone_file_block"));
6680 retval = cs.errcode;
6681 goto errout;
6682 }
6683 /* The inode may have changed on disk, so we have to re-read it */
6684 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
6685 blk = dp->inode.i_file_acl;
6686 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
6687 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
6688 BLOCK_CHANGED)) {
6689 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
6690 /*
6691 * If we cloned the EA block, find all other inodes
6692 * which refered to that EA block, and modify
6693 * them to point to the new EA block.
6694 */
6695 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
6696 db = (struct dup_block *) dnode_get(n);
6697 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
6698 if (ino_el->inode == ino)
6699 continue;
6700 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
6701 di = (struct dup_inode *) dnode_get(n);
6702 if (di->inode.i_file_acl == blk) {
6703 di->inode.i_file_acl = dp->inode.i_file_acl;
6704 e2fsck_write_inode(ctx, ino_el->inode,
6705 &di->inode, "clone file EA");
6706 decrement_badcount(ctx, blk, db);
6707 }
6708 }
6709 }
6710 retval = 0;
6711errout:
6712 ext2fs_free_mem(&cs.buf);
6713 return retval;
6714}
6715
6716/*
6717 * This routine returns 1 if a block overlaps with one of the superblocks,
6718 * group descriptors, inode bitmaps, or block bitmaps.
6719 */
6720static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
6721{
6722 ext2_filsys fs = ctx->fs;
6723 blk_t block;
6724 dgrp_t i;
6725
6726 block = fs->super->s_first_data_block;
6727 for (i = 0; i < fs->group_desc_count; i++) {
6728
6729 /* Check superblocks/block group descriptros */
6730 if (ext2fs_bg_has_super(fs, i)) {
6731 if (test_block >= block &&
6732 (test_block <= block + fs->desc_blocks))
6733 return 1;
6734 }
6735
6736 /* Check the inode table */
6737 if ((fs->group_desc[i].bg_inode_table) &&
6738 (test_block >= fs->group_desc[i].bg_inode_table) &&
6739 (test_block < (fs->group_desc[i].bg_inode_table +
6740 fs->inode_blocks_per_group)))
6741 return 1;
6742
6743 /* Check the bitmap blocks */
6744 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
6745 (test_block == fs->group_desc[i].bg_inode_bitmap))
6746 return 1;
6747
6748 block += fs->super->s_blocks_per_group;
6749 }
6750 return 0;
6751}
6752/*
6753 * pass2.c --- check directory structure
6754 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006755 * Pass 2 of e2fsck iterates through all active directory inodes, and
6756 * applies to following tests to each directory entry in the directory
6757 * blocks in the inodes:
6758 *
6759 * - The length of the directory entry (rec_len) should be at
6760 * least 8 bytes, and no more than the remaining space
6761 * left in the directory block.
6762 * - The length of the name in the directory entry (name_len)
6763 * should be less than (rec_len - 8).
6764 * - The inode number in the directory entry should be within
6765 * legal bounds.
6766 * - The inode number should refer to a in-use inode.
6767 * - The first entry should be '.', and its inode should be
6768 * the inode of the directory.
6769 * - The second entry should be '..'.
6770 *
6771 * To minimize disk seek time, the directory blocks are processed in
6772 * sorted order of block numbers.
6773 *
6774 * Pass 2 also collects the following information:
6775 * - The inode numbers of the subdirectories for each directory.
6776 *
6777 * Pass 2 relies on the following information from previous passes:
6778 * - The directory information collected in pass 1.
6779 * - The inode_used_map bitmap
6780 * - The inode_bad_map bitmap
6781 * - The inode_dir_map bitmap
6782 *
6783 * Pass 2 frees the following data structures
6784 * - The inode_bad_map bitmap
6785 * - The inode_reg_map bitmap
6786 */
6787
6788/* #define DX_DEBUG */
6789
6790/*
6791 * Keeps track of how many times an inode is referenced.
6792 */
6793static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
6794static int check_dir_block(ext2_filsys fs,
6795 struct ext2_db_entry *dir_blocks_info,
6796 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006797static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
6798 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006799static int update_dir_block(ext2_filsys fs,
6800 blk_t *block_nr,
6801 e2_blkcnt_t blockcnt,
6802 blk_t ref_block,
6803 int ref_offset,
6804 void *priv_data);
6805static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
6806static int htree_depth(struct dx_dir_info *dx_dir,
6807 struct dx_dirblock_info *dx_db);
6808static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b);
6809
6810struct check_dir_struct {
6811 char *buf;
6812 struct problem_context pctx;
6813 int count, max;
6814 e2fsck_t ctx;
6815};
6816
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006817static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006818{
6819 struct ext2_super_block *sb = ctx->fs->super;
6820 struct problem_context pctx;
6821 ext2_filsys fs = ctx->fs;
6822 char *buf;
6823#ifdef RESOURCE_TRACK
6824 struct resource_track rtrack;
6825#endif
6826 struct dir_info *dir;
6827 struct check_dir_struct cd;
6828 struct dx_dir_info *dx_dir;
6829 struct dx_dirblock_info *dx_db, *dx_parent;
6830 int b;
6831 int i, depth;
6832 problem_t code;
6833 int bad_dir;
6834
6835#ifdef RESOURCE_TRACK
6836 init_resource_track(&rtrack);
6837#endif
6838
6839 clear_problem_context(&cd.pctx);
6840
6841#ifdef MTRACE
6842 mtrace_print("Pass 2");
6843#endif
6844
6845 if (!(ctx->options & E2F_OPT_PREEN))
6846 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
6847
6848 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
6849 0, ctx->inode_link_info,
6850 &ctx->inode_count);
6851 if (cd.pctx.errcode) {
6852 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
6853 ctx->flags |= E2F_FLAG_ABORT;
6854 return;
6855 }
6856 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
6857 "directory scan buffer");
6858
6859 /*
6860 * Set up the parent pointer for the root directory, if
6861 * present. (If the root directory is not present, we will
6862 * create it in pass 3.)
6863 */
6864 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
6865 if (dir)
6866 dir->parent = EXT2_ROOT_INO;
6867
6868 cd.buf = buf;
6869 cd.ctx = ctx;
6870 cd.count = 1;
6871 cd.max = ext2fs_dblist_count(fs->dblist);
6872
6873 if (ctx->progress)
6874 (void) (ctx->progress)(ctx, 2, 0, cd.max);
6875
6876 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
6877 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
6878
6879 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
6880 &cd);
6881 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6882 return;
6883 if (cd.pctx.errcode) {
6884 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
6885 ctx->flags |= E2F_FLAG_ABORT;
6886 return;
6887 }
6888
6889#ifdef ENABLE_HTREE
6890 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
6891 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6892 return;
6893 if (dx_dir->numblocks == 0)
6894 continue;
6895 clear_problem_context(&pctx);
6896 bad_dir = 0;
6897 pctx.dir = dx_dir->ino;
6898 dx_db = dx_dir->dx_block;
6899 if (dx_db->flags & DX_FLAG_REFERENCED)
6900 dx_db->flags |= DX_FLAG_DUP_REF;
6901 else
6902 dx_db->flags |= DX_FLAG_REFERENCED;
6903 /*
6904 * Find all of the first and last leaf blocks, and
6905 * update their parent's min and max hash values
6906 */
6907 for (b=0, dx_db = dx_dir->dx_block;
6908 b < dx_dir->numblocks;
6909 b++, dx_db++) {
6910 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
6911 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
6912 continue;
6913 dx_parent = &dx_dir->dx_block[dx_db->parent];
6914 /*
6915 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
6916 */
6917 if (dx_db->flags & DX_FLAG_FIRST)
6918 dx_parent->min_hash = dx_db->min_hash;
6919 /*
6920 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
6921 */
6922 if (dx_db->flags & DX_FLAG_LAST)
6923 dx_parent->max_hash = dx_db->max_hash;
6924 }
6925
6926 for (b=0, dx_db = dx_dir->dx_block;
6927 b < dx_dir->numblocks;
6928 b++, dx_db++) {
6929 pctx.blkcount = b;
6930 pctx.group = dx_db->parent;
6931 code = 0;
6932 if (!(dx_db->flags & DX_FLAG_FIRST) &&
6933 (dx_db->min_hash < dx_db->node_min_hash)) {
6934 pctx.blk = dx_db->min_hash;
6935 pctx.blk2 = dx_db->node_min_hash;
6936 code = PR_2_HTREE_MIN_HASH;
6937 fix_problem(ctx, code, &pctx);
6938 bad_dir++;
6939 }
6940 if (dx_db->type == DX_DIRBLOCK_LEAF) {
6941 depth = htree_depth(dx_dir, dx_db);
6942 if (depth != dx_dir->depth) {
6943 code = PR_2_HTREE_BAD_DEPTH;
6944 fix_problem(ctx, code, &pctx);
6945 bad_dir++;
6946 }
6947 }
6948 /*
6949 * This test doesn't apply for the root block
6950 * at block #0
6951 */
6952 if (b &&
6953 (dx_db->max_hash > dx_db->node_max_hash)) {
6954 pctx.blk = dx_db->max_hash;
6955 pctx.blk2 = dx_db->node_max_hash;
6956 code = PR_2_HTREE_MAX_HASH;
6957 fix_problem(ctx, code, &pctx);
6958 bad_dir++;
6959 }
6960 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
6961 code = PR_2_HTREE_NOTREF;
6962 fix_problem(ctx, code, &pctx);
6963 bad_dir++;
6964 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
6965 code = PR_2_HTREE_DUPREF;
6966 fix_problem(ctx, code, &pctx);
6967 bad_dir++;
6968 }
6969 if (code == 0)
6970 continue;
6971 }
6972 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
6973 clear_htree(ctx, dx_dir->ino);
6974 dx_dir->numblocks = 0;
6975 }
6976 }
6977#endif
6978 ext2fs_free_mem(&buf);
6979 ext2fs_free_dblist(fs->dblist);
6980
Rob Landleye7c43b62006-03-01 16:39:45 +00006981 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
6982 ctx->inode_bad_map = 0;
6983 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
6984 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006985
6986 clear_problem_context(&pctx);
6987 if (ctx->large_files) {
6988 if (!(sb->s_feature_ro_compat &
6989 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
6990 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
6991 sb->s_feature_ro_compat |=
6992 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
6993 ext2fs_mark_super_dirty(fs);
6994 }
6995 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
6996 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
6997 ext2fs_update_dynamic_rev(fs);
6998 ext2fs_mark_super_dirty(fs);
6999 }
7000 } else if (!ctx->large_files &&
7001 (sb->s_feature_ro_compat &
7002 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
7003 if (fs->flags & EXT2_FLAG_RW) {
7004 sb->s_feature_ro_compat &=
7005 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
7006 ext2fs_mark_super_dirty(fs);
7007 }
7008 }
7009
7010#ifdef RESOURCE_TRACK
7011 if (ctx->options & E2F_OPT_TIME2) {
7012 e2fsck_clear_progbar(ctx);
7013 print_resource_track(_("Pass 2"), &rtrack);
7014 }
7015#endif
7016}
7017
7018#define MAX_DEPTH 32000
7019static int htree_depth(struct dx_dir_info *dx_dir,
7020 struct dx_dirblock_info *dx_db)
7021{
7022 int depth = 0;
7023
7024 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
7025 dx_db = &dx_dir->dx_block[dx_db->parent];
7026 depth++;
7027 }
7028 return depth;
7029}
7030
7031static int dict_de_cmp(const void *a, const void *b)
7032{
7033 const struct ext2_dir_entry *de_a, *de_b;
7034 int a_len, b_len;
7035
7036 de_a = (const struct ext2_dir_entry *) a;
7037 a_len = de_a->name_len & 0xFF;
7038 de_b = (const struct ext2_dir_entry *) b;
7039 b_len = de_b->name_len & 0xFF;
7040
7041 if (a_len != b_len)
7042 return (a_len - b_len);
7043
7044 return strncmp(de_a->name, de_b->name, a_len);
7045}
7046
7047/*
7048 * This is special sort function that makes sure that directory blocks
7049 * with a dirblock of zero are sorted to the beginning of the list.
7050 * This guarantees that the root node of the htree directories are
7051 * processed first, so we know what hash version to use.
7052 */
7053static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b)
7054{
7055 const struct ext2_db_entry *db_a =
7056 (const struct ext2_db_entry *) a;
7057 const struct ext2_db_entry *db_b =
7058 (const struct ext2_db_entry *) b;
7059
7060 if (db_a->blockcnt && !db_b->blockcnt)
7061 return 1;
7062
7063 if (!db_a->blockcnt && db_b->blockcnt)
7064 return -1;
7065
7066 if (db_a->blk != db_b->blk)
7067 return (int) (db_a->blk - db_b->blk);
7068
7069 if (db_a->ino != db_b->ino)
7070 return (int) (db_a->ino - db_b->ino);
7071
7072 return (int) (db_a->blockcnt - db_b->blockcnt);
7073}
7074
7075
7076/*
7077 * Make sure the first entry in the directory is '.', and that the
7078 * directory entry is sane.
7079 */
7080static int check_dot(e2fsck_t ctx,
7081 struct ext2_dir_entry *dirent,
7082 ext2_ino_t ino, struct problem_context *pctx)
7083{
7084 struct ext2_dir_entry *nextdir;
7085 int status = 0;
7086 int created = 0;
7087 int new_len;
7088 int problem = 0;
7089
7090 if (!dirent->inode)
7091 problem = PR_2_MISSING_DOT;
7092 else if (((dirent->name_len & 0xFF) != 1) ||
7093 (dirent->name[0] != '.'))
7094 problem = PR_2_1ST_NOT_DOT;
7095 else if (dirent->name[1] != '\0')
7096 problem = PR_2_DOT_NULL_TERM;
7097
7098 if (problem) {
7099 if (fix_problem(ctx, problem, pctx)) {
7100 if (dirent->rec_len < 12)
7101 dirent->rec_len = 12;
7102 dirent->inode = ino;
7103 dirent->name_len = 1;
7104 dirent->name[0] = '.';
7105 dirent->name[1] = '\0';
7106 status = 1;
7107 created = 1;
7108 }
7109 }
7110 if (dirent->inode != ino) {
7111 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
7112 dirent->inode = ino;
7113 status = 1;
7114 }
7115 }
7116 if (dirent->rec_len > 12) {
7117 new_len = dirent->rec_len - 12;
7118 if (new_len > 12) {
7119 if (created ||
7120 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
7121 nextdir = (struct ext2_dir_entry *)
7122 ((char *) dirent + 12);
7123 dirent->rec_len = 12;
7124 nextdir->rec_len = new_len;
7125 nextdir->inode = 0;
7126 nextdir->name_len = 0;
7127 status = 1;
7128 }
7129 }
7130 }
7131 return status;
7132}
7133
7134/*
7135 * Make sure the second entry in the directory is '..', and that the
7136 * directory entry is sane. We do not check the inode number of '..'
7137 * here; this gets done in pass 3.
7138 */
7139static int check_dotdot(e2fsck_t ctx,
7140 struct ext2_dir_entry *dirent,
7141 struct dir_info *dir, struct problem_context *pctx)
7142{
7143 int problem = 0;
7144
7145 if (!dirent->inode)
7146 problem = PR_2_MISSING_DOT_DOT;
7147 else if (((dirent->name_len & 0xFF) != 2) ||
7148 (dirent->name[0] != '.') ||
7149 (dirent->name[1] != '.'))
7150 problem = PR_2_2ND_NOT_DOT_DOT;
7151 else if (dirent->name[2] != '\0')
7152 problem = PR_2_DOT_DOT_NULL_TERM;
7153
7154 if (problem) {
7155 if (fix_problem(ctx, problem, pctx)) {
7156 if (dirent->rec_len < 12)
7157 dirent->rec_len = 12;
7158 /*
7159 * Note: we don't have the parent inode just
7160 * yet, so we will fill it in with the root
7161 * inode. This will get fixed in pass 3.
7162 */
7163 dirent->inode = EXT2_ROOT_INO;
7164 dirent->name_len = 2;
7165 dirent->name[0] = '.';
7166 dirent->name[1] = '.';
7167 dirent->name[2] = '\0';
7168 return 1;
7169 }
7170 return 0;
7171 }
7172 dir->dotdot = dirent->inode;
7173 return 0;
7174}
7175
7176/*
7177 * Check to make sure a directory entry doesn't contain any illegal
7178 * characters.
7179 */
7180static int check_name(e2fsck_t ctx,
7181 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007182 struct problem_context *pctx)
7183{
7184 int i;
7185 int fixup = -1;
7186 int ret = 0;
7187
7188 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
7189 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
7190 if (fixup < 0) {
7191 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
7192 }
7193 if (fixup) {
7194 dirent->name[i] = '.';
7195 ret = 1;
7196 }
7197 }
7198 }
7199 return ret;
7200}
7201
7202/*
7203 * Check the directory filetype (if present)
7204 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007205
7206/*
7207 * Given a mode, return the ext2 file type
7208 */
7209static int ext2_file_type(unsigned int mode)
7210{
7211 if (LINUX_S_ISREG(mode))
7212 return EXT2_FT_REG_FILE;
7213
7214 if (LINUX_S_ISDIR(mode))
7215 return EXT2_FT_DIR;
7216
7217 if (LINUX_S_ISCHR(mode))
7218 return EXT2_FT_CHRDEV;
7219
7220 if (LINUX_S_ISBLK(mode))
7221 return EXT2_FT_BLKDEV;
7222
7223 if (LINUX_S_ISLNK(mode))
7224 return EXT2_FT_SYMLINK;
7225
7226 if (LINUX_S_ISFIFO(mode))
7227 return EXT2_FT_FIFO;
7228
7229 if (LINUX_S_ISSOCK(mode))
7230 return EXT2_FT_SOCK;
7231
7232 return 0;
7233}
7234
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007235static _INLINE_ int check_filetype(e2fsck_t ctx,
7236 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007237 struct problem_context *pctx)
7238{
7239 int filetype = dirent->name_len >> 8;
7240 int should_be = EXT2_FT_UNKNOWN;
7241 struct ext2_inode inode;
7242
7243 if (!(ctx->fs->super->s_feature_incompat &
7244 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
7245 if (filetype == 0 ||
7246 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
7247 return 0;
7248 dirent->name_len = dirent->name_len & 0xFF;
7249 return 1;
7250 }
7251
7252 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
7253 should_be = EXT2_FT_DIR;
7254 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
7255 dirent->inode)) {
7256 should_be = EXT2_FT_REG_FILE;
7257 } else if (ctx->inode_bad_map &&
7258 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
7259 dirent->inode))
7260 should_be = 0;
7261 else {
7262 e2fsck_read_inode(ctx, dirent->inode, &inode,
7263 "check_filetype");
7264 should_be = ext2_file_type(inode.i_mode);
7265 }
7266 if (filetype == should_be)
7267 return 0;
7268 pctx->num = should_be;
7269
7270 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
7271 pctx) == 0)
7272 return 0;
7273
7274 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
7275 return 1;
7276}
7277
7278#ifdef ENABLE_HTREE
7279static void parse_int_node(ext2_filsys fs,
7280 struct ext2_db_entry *db,
7281 struct check_dir_struct *cd,
7282 struct dx_dir_info *dx_dir,
7283 char *block_buf)
7284{
7285 struct ext2_dx_root_info *root;
7286 struct ext2_dx_entry *ent;
7287 struct ext2_dx_countlimit *limit;
7288 struct dx_dirblock_info *dx_db;
7289 int i, expect_limit, count;
7290 blk_t blk;
7291 ext2_dirhash_t min_hash = 0xffffffff;
7292 ext2_dirhash_t max_hash = 0;
7293 ext2_dirhash_t hash = 0, prev_hash;
7294
7295 if (db->blockcnt == 0) {
7296 root = (struct ext2_dx_root_info *) (block_buf + 24);
7297
7298#ifdef DX_DEBUG
7299 printf("Root node dump:\n");
7300 printf("\t Reserved zero: %d\n", root->reserved_zero);
7301 printf("\t Hash Version: %d\n", root->hash_version);
7302 printf("\t Info length: %d\n", root->info_length);
7303 printf("\t Indirect levels: %d\n", root->indirect_levels);
7304 printf("\t Flags: %d\n", root->unused_flags);
7305#endif
7306
7307 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
7308 } else {
7309 ent = (struct ext2_dx_entry *) (block_buf+8);
7310 }
7311 limit = (struct ext2_dx_countlimit *) ent;
7312
7313#ifdef DX_DEBUG
7314 printf("Number of entries (count): %d\n",
7315 ext2fs_le16_to_cpu(limit->count));
7316 printf("Number of entries (limit): %d\n",
7317 ext2fs_le16_to_cpu(limit->limit));
7318#endif
7319
7320 count = ext2fs_le16_to_cpu(limit->count);
7321 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
7322 sizeof(struct ext2_dx_entry);
7323 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
7324 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
7325 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
7326 goto clear_and_exit;
7327 }
7328 if (count > expect_limit) {
7329 cd->pctx.num = count;
7330 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
7331 goto clear_and_exit;
7332 count = expect_limit;
7333 }
7334
7335 for (i=0; i < count; i++) {
7336 prev_hash = hash;
7337 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
7338#ifdef DX_DEBUG
7339 printf("Entry #%d: Hash 0x%08x, block %d\n", i,
7340 hash, ext2fs_le32_to_cpu(ent[i].block));
7341#endif
7342 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
7343 /* Check to make sure the block is valid */
7344 if (blk > (blk_t) dx_dir->numblocks) {
7345 cd->pctx.blk = blk;
7346 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
7347 &cd->pctx))
7348 goto clear_and_exit;
7349 }
7350 if (hash < prev_hash &&
7351 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
7352 goto clear_and_exit;
7353 dx_db = &dx_dir->dx_block[blk];
7354 if (dx_db->flags & DX_FLAG_REFERENCED) {
7355 dx_db->flags |= DX_FLAG_DUP_REF;
7356 } else {
7357 dx_db->flags |= DX_FLAG_REFERENCED;
7358 dx_db->parent = db->blockcnt;
7359 }
7360 if (hash < min_hash)
7361 min_hash = hash;
7362 if (hash > max_hash)
7363 max_hash = hash;
7364 dx_db->node_min_hash = hash;
7365 if ((i+1) < count)
7366 dx_db->node_max_hash =
7367 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
7368 else {
7369 dx_db->node_max_hash = 0xfffffffe;
7370 dx_db->flags |= DX_FLAG_LAST;
7371 }
7372 if (i == 0)
7373 dx_db->flags |= DX_FLAG_FIRST;
7374 }
7375#ifdef DX_DEBUG
7376 printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n",
7377 db->blockcnt, min_hash, max_hash);
7378#endif
7379 dx_db = &dx_dir->dx_block[db->blockcnt];
7380 dx_db->min_hash = min_hash;
7381 dx_db->max_hash = max_hash;
7382 return;
7383
7384clear_and_exit:
7385 clear_htree(cd->ctx, cd->pctx.ino);
7386 dx_dir->numblocks = 0;
7387}
7388#endif /* ENABLE_HTREE */
7389
7390/*
7391 * Given a busted directory, try to salvage it somehow.
7392 *
7393 */
7394static void salvage_directory(ext2_filsys fs,
7395 struct ext2_dir_entry *dirent,
7396 struct ext2_dir_entry *prev,
7397 unsigned int *offset)
7398{
7399 char *cp = (char *) dirent;
7400 int left = fs->blocksize - *offset - dirent->rec_len;
7401 int name_len = dirent->name_len & 0xFF;
7402
7403 /*
7404 * Special case of directory entry of size 8: copy what's left
7405 * of the directory block up to cover up the invalid hole.
7406 */
7407 if ((left >= 12) && (dirent->rec_len == 8)) {
7408 memmove(cp, cp+8, left);
7409 memset(cp + left, 0, 8);
7410 return;
7411 }
7412 /*
7413 * If the directory entry overruns the end of the directory
7414 * block, and the name is small enough to fit, then adjust the
7415 * record length.
7416 */
7417 if ((left < 0) &&
7418 (name_len + 8 <= dirent->rec_len + left) &&
7419 dirent->inode <= fs->super->s_inodes_count &&
7420 strnlen(dirent->name, name_len) == name_len) {
7421 dirent->rec_len += left;
7422 return;
7423 }
7424 /*
7425 * If the directory entry is a multiple of four, so it is
7426 * valid, let the previous directory entry absorb the invalid
7427 * one.
7428 */
7429 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
7430 prev->rec_len += dirent->rec_len;
7431 *offset += dirent->rec_len;
7432 return;
7433 }
7434 /*
7435 * Default salvage method --- kill all of the directory
7436 * entries for the rest of the block. We will either try to
7437 * absorb it into the previous directory entry, or create a
7438 * new empty directory entry the rest of the directory block.
7439 */
7440 if (prev) {
7441 prev->rec_len += fs->blocksize - *offset;
7442 *offset = fs->blocksize;
7443 } else {
7444 dirent->rec_len = fs->blocksize - *offset;
7445 dirent->name_len = 0;
7446 dirent->inode = 0;
7447 }
7448}
7449
7450static int check_dir_block(ext2_filsys fs,
7451 struct ext2_db_entry *db,
7452 void *priv_data)
7453{
7454 struct dir_info *subdir, *dir;
7455 struct dx_dir_info *dx_dir;
7456#ifdef ENABLE_HTREE
7457 struct dx_dirblock_info *dx_db = 0;
7458#endif /* ENABLE_HTREE */
7459 struct ext2_dir_entry *dirent, *prev;
7460 ext2_dirhash_t hash;
7461 unsigned int offset = 0;
7462 int dir_modified = 0;
7463 int dot_state;
7464 blk_t block_nr = db->blk;
7465 ext2_ino_t ino = db->ino;
7466 __u16 links;
7467 struct check_dir_struct *cd;
7468 char *buf;
7469 e2fsck_t ctx;
7470 int problem;
7471 struct ext2_dx_root_info *root;
7472 struct ext2_dx_countlimit *limit;
7473 static dict_t de_dict;
7474 struct problem_context pctx;
7475 int dups_found = 0;
7476
7477 cd = (struct check_dir_struct *) priv_data;
7478 buf = cd->buf;
7479 ctx = cd->ctx;
7480
7481 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7482 return DIRENT_ABORT;
7483
7484 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
7485 return DIRENT_ABORT;
7486
7487 /*
7488 * Make sure the inode is still in use (could have been
7489 * deleted in the duplicate/bad blocks pass.
7490 */
7491 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
7492 return 0;
7493
7494 cd->pctx.ino = ino;
7495 cd->pctx.blk = block_nr;
7496 cd->pctx.blkcount = db->blockcnt;
7497 cd->pctx.ino2 = 0;
7498 cd->pctx.dirent = 0;
7499 cd->pctx.num = 0;
7500
7501 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007502 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007503 return 0;
7504 block_nr = db->blk;
7505 }
7506
7507 if (db->blockcnt)
7508 dot_state = 2;
7509 else
7510 dot_state = 0;
7511
7512 if (ctx->dirs_to_hash &&
7513 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
7514 dups_found++;
7515
7516#if 0
7517 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
7518 db->blockcnt, ino);
7519#endif
7520
7521 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
7522 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
7523 cd->pctx.errcode = 0; /* We'll handle this ourselves */
7524 if (cd->pctx.errcode) {
7525 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
7526 ctx->flags |= E2F_FLAG_ABORT;
7527 return DIRENT_ABORT;
7528 }
7529 memset(buf, 0, fs->blocksize);
7530 }
7531#ifdef ENABLE_HTREE
7532 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
7533 if (dx_dir && dx_dir->numblocks) {
7534 if (db->blockcnt >= dx_dir->numblocks) {
7535 printf("XXX should never happen!!!\n");
7536 abort();
7537 }
7538 dx_db = &dx_dir->dx_block[db->blockcnt];
7539 dx_db->type = DX_DIRBLOCK_LEAF;
7540 dx_db->phys = block_nr;
7541 dx_db->min_hash = ~0;
7542 dx_db->max_hash = 0;
7543
7544 dirent = (struct ext2_dir_entry *) buf;
7545 limit = (struct ext2_dx_countlimit *) (buf+8);
7546 if (db->blockcnt == 0) {
7547 root = (struct ext2_dx_root_info *) (buf + 24);
7548 dx_db->type = DX_DIRBLOCK_ROOT;
7549 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
7550 if ((root->reserved_zero ||
7551 root->info_length < 8 ||
7552 root->indirect_levels > 1) &&
7553 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
7554 clear_htree(ctx, ino);
7555 dx_dir->numblocks = 0;
7556 dx_db = 0;
7557 }
7558 dx_dir->hashversion = root->hash_version;
7559 dx_dir->depth = root->indirect_levels + 1;
7560 } else if ((dirent->inode == 0) &&
7561 (dirent->rec_len == fs->blocksize) &&
7562 (dirent->name_len == 0) &&
7563 (ext2fs_le16_to_cpu(limit->limit) ==
7564 ((fs->blocksize-8) /
7565 sizeof(struct ext2_dx_entry))))
7566 dx_db->type = DX_DIRBLOCK_NODE;
7567 }
7568#endif /* ENABLE_HTREE */
7569
7570 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
7571 prev = 0;
7572 do {
7573 problem = 0;
7574 dirent = (struct ext2_dir_entry *) (buf + offset);
7575 cd->pctx.dirent = dirent;
7576 cd->pctx.num = offset;
7577 if (((offset + dirent->rec_len) > fs->blocksize) ||
7578 (dirent->rec_len < 12) ||
7579 ((dirent->rec_len % 4) != 0) ||
7580 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
7581 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
7582 salvage_directory(fs, dirent, prev, &offset);
7583 dir_modified++;
7584 continue;
7585 } else
7586 goto abort_free_dict;
7587 }
7588 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
7589 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
7590 dirent->name_len = EXT2_NAME_LEN;
7591 dir_modified++;
7592 }
7593 }
7594
7595 if (dot_state == 0) {
7596 if (check_dot(ctx, dirent, ino, &cd->pctx))
7597 dir_modified++;
7598 } else if (dot_state == 1) {
7599 dir = e2fsck_get_dir_info(ctx, ino);
7600 if (!dir) {
7601 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
7602 goto abort_free_dict;
7603 }
7604 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
7605 dir_modified++;
7606 } else if (dirent->inode == ino) {
7607 problem = PR_2_LINK_DOT;
7608 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
7609 dirent->inode = 0;
7610 dir_modified++;
7611 goto next;
7612 }
7613 }
7614 if (!dirent->inode)
7615 goto next;
7616
7617 /*
7618 * Make sure the inode listed is a legal one.
7619 */
7620 if (((dirent->inode != EXT2_ROOT_INO) &&
7621 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
7622 (dirent->inode > fs->super->s_inodes_count)) {
7623 problem = PR_2_BAD_INO;
7624 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
7625 dirent->inode))) {
7626 /*
7627 * If the inode is unused, offer to clear it.
7628 */
7629 problem = PR_2_UNUSED_INODE;
7630 } else if (ctx->inode_bb_map &&
7631 (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
7632 dirent->inode))) {
7633 /*
7634 * If the inode is in a bad block, offer to
7635 * clear it.
7636 */
7637 problem = PR_2_BB_INODE;
7638 } else if ((dot_state > 1) &&
7639 ((dirent->name_len & 0xFF) == 1) &&
7640 (dirent->name[0] == '.')) {
7641 /*
7642 * If there's a '.' entry in anything other
7643 * than the first directory entry, it's a
7644 * duplicate entry that should be removed.
7645 */
7646 problem = PR_2_DUP_DOT;
7647 } else if ((dot_state > 1) &&
7648 ((dirent->name_len & 0xFF) == 2) &&
7649 (dirent->name[0] == '.') &&
7650 (dirent->name[1] == '.')) {
7651 /*
7652 * If there's a '..' entry in anything other
7653 * than the second directory entry, it's a
7654 * duplicate entry that should be removed.
7655 */
7656 problem = PR_2_DUP_DOT_DOT;
7657 } else if ((dot_state > 1) &&
7658 (dirent->inode == EXT2_ROOT_INO)) {
7659 /*
7660 * Don't allow links to the root directory.
7661 * We check this specially to make sure we
7662 * catch this error case even if the root
7663 * directory hasn't been created yet.
7664 */
7665 problem = PR_2_LINK_ROOT;
7666 } else if ((dot_state > 1) &&
7667 (dirent->name_len & 0xFF) == 0) {
7668 /*
7669 * Don't allow zero-length directory names.
7670 */
7671 problem = PR_2_NULL_NAME;
7672 }
7673
7674 if (problem) {
7675 if (fix_problem(ctx, problem, &cd->pctx)) {
7676 dirent->inode = 0;
7677 dir_modified++;
7678 goto next;
7679 } else {
7680 ext2fs_unmark_valid(fs);
7681 if (problem == PR_2_BAD_INO)
7682 goto next;
7683 }
7684 }
7685
7686 /*
7687 * If the inode was marked as having bad fields in
7688 * pass1, process it and offer to fix/clear it.
7689 * (We wait until now so that we can display the
7690 * pathname to the user.)
7691 */
7692 if (ctx->inode_bad_map &&
7693 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
7694 dirent->inode)) {
7695 if (e2fsck_process_bad_inode(ctx, ino,
7696 dirent->inode,
7697 buf + fs->blocksize)) {
7698 dirent->inode = 0;
7699 dir_modified++;
7700 goto next;
7701 }
7702 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7703 return DIRENT_ABORT;
7704 }
7705
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007706 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007707 dir_modified++;
7708
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007709 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007710 dir_modified++;
7711
7712#ifdef ENABLE_HTREE
7713 if (dx_db) {
7714 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
7715 (dirent->name_len & 0xFF),
7716 fs->super->s_hash_seed, &hash, 0);
7717 if (hash < dx_db->min_hash)
7718 dx_db->min_hash = hash;
7719 if (hash > dx_db->max_hash)
7720 dx_db->max_hash = hash;
7721 }
7722#endif
7723
7724 /*
7725 * If this is a directory, then mark its parent in its
7726 * dir_info structure. If the parent field is already
7727 * filled in, then this directory has more than one
7728 * hard link. We assume the first link is correct,
7729 * and ask the user if he/she wants to clear this one.
7730 */
7731 if ((dot_state > 1) &&
7732 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
7733 dirent->inode))) {
7734 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
7735 if (!subdir) {
7736 cd->pctx.ino = dirent->inode;
7737 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
7738 goto abort_free_dict;
7739 }
7740 if (subdir->parent) {
7741 cd->pctx.ino2 = subdir->parent;
7742 if (fix_problem(ctx, PR_2_LINK_DIR,
7743 &cd->pctx)) {
7744 dirent->inode = 0;
7745 dir_modified++;
7746 goto next;
7747 }
7748 cd->pctx.ino2 = 0;
7749 } else
7750 subdir->parent = ino;
7751 }
7752
7753 if (dups_found) {
7754 ;
7755 } else if (dict_lookup(&de_dict, dirent)) {
7756 clear_problem_context(&pctx);
7757 pctx.ino = ino;
7758 pctx.dirent = dirent;
7759 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
7760 if (!ctx->dirs_to_hash)
7761 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
7762 if (ctx->dirs_to_hash)
7763 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
7764 dups_found++;
7765 } else
7766 dict_alloc_insert(&de_dict, dirent, dirent);
7767
7768 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
7769 &links);
7770 if (links > 1)
7771 ctx->fs_links_count++;
7772 ctx->fs_total_count++;
7773 next:
7774 prev = dirent;
7775 offset += dirent->rec_len;
7776 dot_state++;
7777 } while (offset < fs->blocksize);
7778#if 0
7779 printf("\n");
7780#endif
7781#ifdef ENABLE_HTREE
7782 if (dx_db) {
7783#ifdef DX_DEBUG
7784 printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
7785 db->blockcnt, dx_db->type,
7786 dx_db->min_hash, dx_db->max_hash);
7787#endif
7788 cd->pctx.dir = cd->pctx.ino;
7789 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
7790 (dx_db->type == DX_DIRBLOCK_NODE))
7791 parse_int_node(fs, db, cd, dx_dir, buf);
7792 }
7793#endif /* ENABLE_HTREE */
7794 if (offset != fs->blocksize) {
7795 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
7796 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
7797 dirent->rec_len = cd->pctx.num;
7798 dir_modified++;
7799 }
7800 }
7801 if (dir_modified) {
7802 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
7803 if (cd->pctx.errcode) {
7804 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
7805 &cd->pctx))
7806 goto abort_free_dict;
7807 }
7808 ext2fs_mark_changed(fs);
7809 }
7810 dict_free_nodes(&de_dict);
7811 return 0;
7812abort_free_dict:
7813 dict_free_nodes(&de_dict);
7814 ctx->flags |= E2F_FLAG_ABORT;
7815 return DIRENT_ABORT;
7816}
7817
7818/*
7819 * This function is called to deallocate a block, and is an interator
7820 * functioned called by deallocate inode via ext2fs_iterate_block().
7821 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007822static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007823 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
7824 blk_t ref_block FSCK_ATTR((unused)),
7825 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007826 void *priv_data)
7827{
7828 e2fsck_t ctx = (e2fsck_t) priv_data;
7829
7830 if (HOLE_BLKADDR(*block_nr))
7831 return 0;
7832 if ((*block_nr < fs->super->s_first_data_block) ||
7833 (*block_nr >= fs->super->s_blocks_count))
7834 return 0;
7835 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
7836 ext2fs_block_alloc_stats(fs, *block_nr, -1);
7837 return 0;
7838}
7839
7840/*
7841 * This fuction deallocates an inode
7842 */
7843static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
7844{
7845 ext2_filsys fs = ctx->fs;
7846 struct ext2_inode inode;
7847 struct problem_context pctx;
7848 __u32 count;
7849
7850 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
7851 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
7852 inode.i_links_count = 0;
7853 inode.i_dtime = time(0);
7854 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
7855 clear_problem_context(&pctx);
7856 pctx.ino = ino;
7857
7858 /*
7859 * Fix up the bitmaps...
7860 */
7861 e2fsck_read_bitmaps(ctx);
7862 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
7863 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
7864 if (ctx->inode_bad_map)
7865 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
7866 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
7867
7868 if (inode.i_file_acl &&
7869 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
7870 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
7871 block_buf, -1, &count);
7872 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
7873 pctx.errcode = 0;
7874 count = 1;
7875 }
7876 if (pctx.errcode) {
7877 pctx.blk = inode.i_file_acl;
7878 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
7879 ctx->flags |= E2F_FLAG_ABORT;
7880 return;
7881 }
7882 if (count == 0) {
7883 ext2fs_unmark_block_bitmap(ctx->block_found_map,
7884 inode.i_file_acl);
7885 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
7886 }
7887 inode.i_file_acl = 0;
7888 }
7889
7890 if (!ext2fs_inode_has_valid_blocks(&inode))
7891 return;
7892
7893 if (LINUX_S_ISREG(inode.i_mode) &&
7894 (inode.i_size_high || inode.i_size & 0x80000000UL))
7895 ctx->large_files--;
7896
7897 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
7898 deallocate_inode_block, ctx);
7899 if (pctx.errcode) {
7900 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
7901 ctx->flags |= E2F_FLAG_ABORT;
7902 return;
7903 }
7904}
7905
7906/*
7907 * This fuction clears the htree flag on an inode
7908 */
7909static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
7910{
7911 struct ext2_inode inode;
7912
7913 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
7914 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
7915 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
7916 if (ctx->dirs_to_hash)
7917 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
7918}
7919
7920
7921static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
7922 ext2_ino_t ino, char *buf)
7923{
7924 ext2_filsys fs = ctx->fs;
7925 struct ext2_inode inode;
7926 int inode_modified = 0;
7927 int not_fixed = 0;
7928 unsigned char *frag, *fsize;
7929 struct problem_context pctx;
7930 int problem = 0;
7931
7932 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
7933
7934 clear_problem_context(&pctx);
7935 pctx.ino = ino;
7936 pctx.dir = dir;
7937 pctx.inode = &inode;
7938
7939 if (inode.i_file_acl &&
7940 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
7941 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
7942 inode.i_file_acl = 0;
7943#ifdef EXT2FS_ENABLE_SWAPFS
7944 /*
7945 * This is a special kludge to deal with long symlinks
7946 * on big endian systems. i_blocks had already been
7947 * decremented earlier in pass 1, but since i_file_acl
7948 * hadn't yet been cleared, ext2fs_read_inode()
7949 * assumed that the file was short symlink and would
7950 * not have byte swapped i_block[0]. Hence, we have
7951 * to byte-swap it here.
7952 */
7953 if (LINUX_S_ISLNK(inode.i_mode) &&
7954 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
7955 (inode.i_blocks == fs->blocksize >> 9))
7956 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
7957#endif
7958 inode_modified++;
7959 } else
7960 not_fixed++;
7961
7962 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
7963 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
7964 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
7965 !(LINUX_S_ISSOCK(inode.i_mode)))
7966 problem = PR_2_BAD_MODE;
7967 else if (LINUX_S_ISCHR(inode.i_mode)
7968 && !e2fsck_pass1_check_device_inode(fs, &inode))
7969 problem = PR_2_BAD_CHAR_DEV;
7970 else if (LINUX_S_ISBLK(inode.i_mode)
7971 && !e2fsck_pass1_check_device_inode(fs, &inode))
7972 problem = PR_2_BAD_BLOCK_DEV;
7973 else if (LINUX_S_ISFIFO(inode.i_mode)
7974 && !e2fsck_pass1_check_device_inode(fs, &inode))
7975 problem = PR_2_BAD_FIFO;
7976 else if (LINUX_S_ISSOCK(inode.i_mode)
7977 && !e2fsck_pass1_check_device_inode(fs, &inode))
7978 problem = PR_2_BAD_SOCKET;
7979 else if (LINUX_S_ISLNK(inode.i_mode)
7980 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
7981 problem = PR_2_INVALID_SYMLINK;
7982 }
7983
7984 if (problem) {
7985 if (fix_problem(ctx, problem, &pctx)) {
7986 deallocate_inode(ctx, ino, 0);
7987 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7988 return 0;
7989 return 1;
7990 } else
7991 not_fixed++;
7992 problem = 0;
7993 }
7994
7995 if (inode.i_faddr) {
7996 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
7997 inode.i_faddr = 0;
7998 inode_modified++;
7999 } else
8000 not_fixed++;
8001 }
8002
8003 switch (fs->super->s_creator_os) {
8004 case EXT2_OS_LINUX:
8005 frag = &inode.osd2.linux2.l_i_frag;
8006 fsize = &inode.osd2.linux2.l_i_fsize;
8007 break;
8008 case EXT2_OS_HURD:
8009 frag = &inode.osd2.hurd2.h_i_frag;
8010 fsize = &inode.osd2.hurd2.h_i_fsize;
8011 break;
8012 case EXT2_OS_MASIX:
8013 frag = &inode.osd2.masix2.m_i_frag;
8014 fsize = &inode.osd2.masix2.m_i_fsize;
8015 break;
8016 default:
8017 frag = fsize = 0;
8018 }
8019 if (frag && *frag) {
8020 pctx.num = *frag;
8021 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
8022 *frag = 0;
8023 inode_modified++;
8024 } else
8025 not_fixed++;
8026 pctx.num = 0;
8027 }
8028 if (fsize && *fsize) {
8029 pctx.num = *fsize;
8030 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
8031 *fsize = 0;
8032 inode_modified++;
8033 } else
8034 not_fixed++;
8035 pctx.num = 0;
8036 }
8037
8038 if (inode.i_file_acl &&
8039 ((inode.i_file_acl < fs->super->s_first_data_block) ||
8040 (inode.i_file_acl >= fs->super->s_blocks_count))) {
8041 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
8042 inode.i_file_acl = 0;
8043 inode_modified++;
8044 } else
8045 not_fixed++;
8046 }
8047 if (inode.i_dir_acl &&
8048 LINUX_S_ISDIR(inode.i_mode)) {
8049 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
8050 inode.i_dir_acl = 0;
8051 inode_modified++;
8052 } else
8053 not_fixed++;
8054 }
8055
8056 if (inode_modified)
8057 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
8058 if (!not_fixed)
8059 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
8060 return 0;
8061}
8062
8063
8064/*
8065 * allocate_dir_block --- this function allocates a new directory
8066 * block for a particular inode; this is done if a directory has
8067 * a "hole" in it, or if a directory has a illegal block number
8068 * that was zeroed out and now needs to be replaced.
8069 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008070static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008071 struct problem_context *pctx)
8072{
8073 ext2_filsys fs = ctx->fs;
8074 blk_t blk;
8075 char *block;
8076 struct ext2_inode inode;
8077
8078 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
8079 return 1;
8080
8081 /*
8082 * Read the inode and block bitmaps in; we'll be messing with
8083 * them.
8084 */
8085 e2fsck_read_bitmaps(ctx);
8086
8087 /*
8088 * First, find a free block
8089 */
8090 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
8091 if (pctx->errcode) {
8092 pctx->str = "ext2fs_new_block";
8093 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
8094 return 1;
8095 }
8096 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
8097 ext2fs_mark_block_bitmap(fs->block_map, blk);
8098 ext2fs_mark_bb_dirty(fs);
8099
8100 /*
8101 * Now let's create the actual data block for the inode
8102 */
8103 if (db->blockcnt)
8104 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
8105 else
8106 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
8107 EXT2_ROOT_INO, &block);
8108
8109 if (pctx->errcode) {
8110 pctx->str = "ext2fs_new_dir_block";
8111 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
8112 return 1;
8113 }
8114
8115 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
8116 ext2fs_free_mem(&block);
8117 if (pctx->errcode) {
8118 pctx->str = "ext2fs_write_dir_block";
8119 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
8120 return 1;
8121 }
8122
8123 /*
8124 * Update the inode block count
8125 */
8126 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
8127 inode.i_blocks += fs->blocksize / 512;
8128 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
8129 inode.i_size = (db->blockcnt+1) * fs->blocksize;
8130 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
8131
8132 /*
8133 * Finally, update the block pointers for the inode
8134 */
8135 db->blk = blk;
8136 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
8137 0, update_dir_block, db);
8138 if (pctx->errcode) {
8139 pctx->str = "ext2fs_block_iterate";
8140 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
8141 return 1;
8142 }
8143
8144 return 0;
8145}
8146
8147/*
8148 * This is a helper function for allocate_dir_block().
8149 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008150static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008151 blk_t *block_nr,
8152 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008153 blk_t ref_block FSCK_ATTR((unused)),
8154 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008155 void *priv_data)
8156{
8157 struct ext2_db_entry *db;
8158
8159 db = (struct ext2_db_entry *) priv_data;
8160 if (db->blockcnt == (int) blockcnt) {
8161 *block_nr = db->blk;
8162 return BLOCK_CHANGED;
8163 }
8164 return 0;
8165}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008166
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008167/*
8168 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
8169 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008170 * Pass #3 assures that all directories are connected to the
8171 * filesystem tree, using the following algorithm:
8172 *
8173 * First, the root directory is checked to make sure it exists; if
8174 * not, e2fsck will offer to create a new one. It is then marked as
8175 * "done".
8176 *
8177 * Then, pass3 interates over all directory inodes; for each directory
8178 * it attempts to trace up the filesystem tree, using dirinfo.parent
8179 * until it reaches a directory which has been marked "done". If it
8180 * can not do so, then the directory must be disconnected, and e2fsck
8181 * will offer to reconnect it to /lost+found. While it is chasing
8182 * parent pointers up the filesystem tree, if pass3 sees a directory
8183 * twice, then it has detected a filesystem loop, and it will again
8184 * offer to reconnect the directory to /lost+found in to break the
8185 * filesystem loop.
8186 *
8187 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
8188 * reconnect inodes to /lost+found; this subroutine is also used by
8189 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
8190 * is responsible for creating /lost+found if it does not exist.
8191 *
8192 * Pass 3 frees the following data structures:
8193 * - The dirinfo directory information cache.
8194 */
8195
8196static void check_root(e2fsck_t ctx);
8197static int check_directory(e2fsck_t ctx, struct dir_info *dir,
8198 struct problem_context *pctx);
8199static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
8200
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008201static ext2fs_inode_bitmap inode_loop_detect;
8202static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008203
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008204static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008205{
8206 ext2_filsys fs = ctx->fs;
8207 int i;
8208#ifdef RESOURCE_TRACK
8209 struct resource_track rtrack;
8210#endif
8211 struct problem_context pctx;
8212 struct dir_info *dir;
8213 unsigned long maxdirs, count;
8214
8215#ifdef RESOURCE_TRACK
8216 init_resource_track(&rtrack);
8217#endif
8218
8219 clear_problem_context(&pctx);
8220
8221#ifdef MTRACE
8222 mtrace_print("Pass 3");
8223#endif
8224
8225 if (!(ctx->options & E2F_OPT_PREEN))
8226 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
8227
8228 /*
8229 * Allocate some bitmaps to do loop detection.
8230 */
8231 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
8232 &inode_done_map);
8233 if (pctx.errcode) {
8234 pctx.num = 2;
8235 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
8236 ctx->flags |= E2F_FLAG_ABORT;
8237 goto abort_exit;
8238 }
8239#ifdef RESOURCE_TRACK
8240 if (ctx->options & E2F_OPT_TIME) {
8241 e2fsck_clear_progbar(ctx);
8242 print_resource_track(_("Peak memory"), &ctx->global_rtrack);
8243 }
8244#endif
8245
8246 check_root(ctx);
8247 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8248 goto abort_exit;
8249
8250 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
8251
8252 maxdirs = e2fsck_get_num_dirinfo(ctx);
8253 count = 1;
8254
8255 if (ctx->progress)
8256 if ((ctx->progress)(ctx, 3, 0, maxdirs))
8257 goto abort_exit;
8258
8259 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
8260 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8261 goto abort_exit;
8262 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
8263 goto abort_exit;
8264 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
8265 if (check_directory(ctx, dir, &pctx))
8266 goto abort_exit;
8267 }
8268
8269 /*
8270 * Force the creation of /lost+found if not present
8271 */
8272 if ((ctx->flags & E2F_OPT_READONLY) == 0)
8273 e2fsck_get_lost_and_found(ctx, 1);
8274
8275 /*
8276 * If there are any directories that need to be indexed or
8277 * optimized, do it here.
8278 */
8279 e2fsck_rehash_directories(ctx);
8280
8281abort_exit:
8282 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00008283 ext2fs_free_inode_bitmap(inode_loop_detect);
8284 inode_loop_detect = 0;
8285 ext2fs_free_inode_bitmap(inode_done_map);
8286 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008287
8288#ifdef RESOURCE_TRACK
8289 if (ctx->options & E2F_OPT_TIME2) {
8290 e2fsck_clear_progbar(ctx);
8291 print_resource_track(_("Pass 3"), &rtrack);
8292 }
8293#endif
8294}
8295
8296/*
8297 * This makes sure the root inode is present; if not, we ask if the
8298 * user wants us to create it. Not creating it is a fatal error.
8299 */
8300static void check_root(e2fsck_t ctx)
8301{
8302 ext2_filsys fs = ctx->fs;
8303 blk_t blk;
8304 struct ext2_inode inode;
8305 char * block;
8306 struct problem_context pctx;
8307
8308 clear_problem_context(&pctx);
8309
8310 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
8311 /*
8312 * If the root inode is not a directory, die here. The
8313 * user must have answered 'no' in pass1 when we
8314 * offered to clear it.
8315 */
8316 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
8317 EXT2_ROOT_INO))) {
8318 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
8319 ctx->flags |= E2F_FLAG_ABORT;
8320 }
8321 return;
8322 }
8323
8324 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
8325 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
8326 ctx->flags |= E2F_FLAG_ABORT;
8327 return;
8328 }
8329
8330 e2fsck_read_bitmaps(ctx);
8331
8332 /*
8333 * First, find a free block
8334 */
8335 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
8336 if (pctx.errcode) {
8337 pctx.str = "ext2fs_new_block";
8338 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8339 ctx->flags |= E2F_FLAG_ABORT;
8340 return;
8341 }
8342 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
8343 ext2fs_mark_block_bitmap(fs->block_map, blk);
8344 ext2fs_mark_bb_dirty(fs);
8345
8346 /*
8347 * Now let's create the actual data block for the inode
8348 */
8349 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
8350 &block);
8351 if (pctx.errcode) {
8352 pctx.str = "ext2fs_new_dir_block";
8353 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8354 ctx->flags |= E2F_FLAG_ABORT;
8355 return;
8356 }
8357
8358 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
8359 if (pctx.errcode) {
8360 pctx.str = "ext2fs_write_dir_block";
8361 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8362 ctx->flags |= E2F_FLAG_ABORT;
8363 return;
8364 }
8365 ext2fs_free_mem(&block);
8366
8367 /*
8368 * Set up the inode structure
8369 */
8370 memset(&inode, 0, sizeof(inode));
8371 inode.i_mode = 040755;
8372 inode.i_size = fs->blocksize;
8373 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
8374 inode.i_links_count = 2;
8375 inode.i_blocks = fs->blocksize / 512;
8376 inode.i_block[0] = blk;
8377
8378 /*
8379 * Write out the inode.
8380 */
8381 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
8382 if (pctx.errcode) {
8383 pctx.str = "ext2fs_write_inode";
8384 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
8385 ctx->flags |= E2F_FLAG_ABORT;
8386 return;
8387 }
8388
8389 /*
8390 * Miscellaneous bookkeeping...
8391 */
8392 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
8393 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
8394 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
8395
8396 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
8397 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
8398 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
8399 ext2fs_mark_ib_dirty(fs);
8400}
8401
8402/*
8403 * This subroutine is responsible for making sure that a particular
8404 * directory is connected to the root; if it isn't we trace it up as
8405 * far as we can go, and then offer to connect the resulting parent to
8406 * the lost+found. We have to do loop detection; if we ever discover
8407 * a loop, we treat that as a disconnected directory and offer to
8408 * reparent it to lost+found.
8409 *
8410 * However, loop detection is expensive, because for very large
8411 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
8412 * is non-trivial. Loops in filesystems are also a rare error case,
8413 * and we shouldn't optimize for error cases. So we try two passes of
8414 * the algorithm. The first time, we ignore loop detection and merely
8415 * increment a counter; if the counter exceeds some extreme threshold,
8416 * then we try again with the loop detection bitmap enabled.
8417 */
8418static int check_directory(e2fsck_t ctx, struct dir_info *dir,
8419 struct problem_context *pctx)
8420{
8421 ext2_filsys fs = ctx->fs;
8422 struct dir_info *p = dir;
8423 int loop_pass = 0, parent_count = 0;
8424
8425 if (!p)
8426 return 0;
8427
8428 while (1) {
8429 /*
8430 * Mark this inode as being "done"; by the time we
8431 * return from this function, the inode we either be
8432 * verified as being connected to the directory tree,
8433 * or we will have offered to reconnect this to
8434 * lost+found.
8435 *
8436 * If it was marked done already, then we've reached a
8437 * parent we've already checked.
8438 */
8439 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
8440 break;
8441
8442 /*
8443 * If this directory doesn't have a parent, or we've
8444 * seen the parent once already, then offer to
8445 * reparent it to lost+found
8446 */
8447 if (!p->parent ||
8448 (loop_pass &&
8449 (ext2fs_test_inode_bitmap(inode_loop_detect,
8450 p->parent)))) {
8451 pctx->ino = p->ino;
8452 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
8453 if (e2fsck_reconnect_file(ctx, pctx->ino))
8454 ext2fs_unmark_valid(fs);
8455 else {
8456 p = e2fsck_get_dir_info(ctx, pctx->ino);
8457 p->parent = ctx->lost_and_found;
8458 fix_dotdot(ctx, p, ctx->lost_and_found);
8459 }
8460 }
8461 break;
8462 }
8463 p = e2fsck_get_dir_info(ctx, p->parent);
8464 if (!p) {
8465 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
8466 return 0;
8467 }
8468 if (loop_pass) {
8469 ext2fs_mark_inode_bitmap(inode_loop_detect,
8470 p->ino);
8471 } else if (parent_count++ > 2048) {
8472 /*
8473 * If we've run into a path depth that's
8474 * greater than 2048, try again with the inode
8475 * loop bitmap turned on and start from the
8476 * top.
8477 */
8478 loop_pass = 1;
8479 if (inode_loop_detect)
8480 ext2fs_clear_inode_bitmap(inode_loop_detect);
8481 else {
8482 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
8483 if (pctx->errcode) {
8484 pctx->num = 1;
8485 fix_problem(ctx,
8486 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
8487 ctx->flags |= E2F_FLAG_ABORT;
8488 return -1;
8489 }
8490 }
8491 p = dir;
8492 }
8493 }
8494
8495 /*
8496 * Make sure that .. and the parent directory are the same;
8497 * offer to fix it if not.
8498 */
8499 if (dir->parent != dir->dotdot) {
8500 pctx->ino = dir->ino;
8501 pctx->ino2 = dir->dotdot;
8502 pctx->dir = dir->parent;
8503 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
8504 fix_dotdot(ctx, dir, dir->parent);
8505 }
8506 return 0;
8507}
8508
8509/*
8510 * This routine gets the lost_and_found inode, making it a directory
8511 * if necessary
8512 */
8513ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
8514{
8515 ext2_filsys fs = ctx->fs;
8516 ext2_ino_t ino;
8517 blk_t blk;
8518 errcode_t retval;
8519 struct ext2_inode inode;
8520 char * block;
8521 static const char name[] = "lost+found";
8522 struct problem_context pctx;
8523 struct dir_info *dirinfo;
8524
8525 if (ctx->lost_and_found)
8526 return ctx->lost_and_found;
8527
8528 clear_problem_context(&pctx);
8529
8530 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
8531 sizeof(name)-1, 0, &ino);
8532 if (retval && !fix)
8533 return 0;
8534 if (!retval) {
8535 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
8536 ctx->lost_and_found = ino;
8537 return ino;
8538 }
8539
8540 /* Lost+found isn't a directory! */
8541 if (!fix)
8542 return 0;
8543 pctx.ino = ino;
8544 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
8545 return 0;
8546
8547 /* OK, unlink the old /lost+found file. */
8548 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
8549 if (pctx.errcode) {
8550 pctx.str = "ext2fs_unlink";
8551 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8552 return 0;
8553 }
8554 dirinfo = e2fsck_get_dir_info(ctx, ino);
8555 if (dirinfo)
8556 dirinfo->parent = 0;
8557 e2fsck_adjust_inode_count(ctx, ino, -1);
8558 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
8559 pctx.errcode = retval;
8560 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
8561 }
8562 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
8563 return 0;
8564
8565 /*
8566 * Read the inode and block bitmaps in; we'll be messing with
8567 * them.
8568 */
8569 e2fsck_read_bitmaps(ctx);
8570
8571 /*
8572 * First, find a free block
8573 */
8574 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
8575 if (retval) {
8576 pctx.errcode = retval;
8577 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
8578 return 0;
8579 }
8580 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
8581 ext2fs_block_alloc_stats(fs, blk, +1);
8582
8583 /*
8584 * Next find a free inode.
8585 */
8586 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
8587 ctx->inode_used_map, &ino);
8588 if (retval) {
8589 pctx.errcode = retval;
8590 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
8591 return 0;
8592 }
8593 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
8594 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
8595 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
8596
8597 /*
8598 * Now let's create the actual data block for the inode
8599 */
8600 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
8601 if (retval) {
8602 pctx.errcode = retval;
8603 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
8604 return 0;
8605 }
8606
8607 retval = ext2fs_write_dir_block(fs, blk, block);
8608 ext2fs_free_mem(&block);
8609 if (retval) {
8610 pctx.errcode = retval;
8611 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
8612 return 0;
8613 }
8614
8615 /*
8616 * Set up the inode structure
8617 */
8618 memset(&inode, 0, sizeof(inode));
8619 inode.i_mode = 040700;
8620 inode.i_size = fs->blocksize;
8621 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
8622 inode.i_links_count = 2;
8623 inode.i_blocks = fs->blocksize / 512;
8624 inode.i_block[0] = blk;
8625
8626 /*
8627 * Next, write out the inode.
8628 */
8629 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
8630 if (pctx.errcode) {
8631 pctx.str = "ext2fs_write_inode";
8632 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8633 return 0;
8634 }
8635 /*
8636 * Finally, create the directory link
8637 */
8638 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
8639 if (pctx.errcode) {
8640 pctx.str = "ext2fs_link";
8641 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
8642 return 0;
8643 }
8644
8645 /*
8646 * Miscellaneous bookkeeping that needs to be kept straight.
8647 */
8648 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
8649 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
8650 ext2fs_icount_store(ctx->inode_count, ino, 2);
8651 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
8652 ctx->lost_and_found = ino;
8653#if 0
8654 printf("/lost+found created; inode #%lu\n", ino);
8655#endif
8656 return ino;
8657}
8658
8659/*
8660 * This routine will connect a file to lost+found
8661 */
8662int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
8663{
8664 ext2_filsys fs = ctx->fs;
8665 errcode_t retval;
8666 char name[80];
8667 struct problem_context pctx;
8668 struct ext2_inode inode;
8669 int file_type = 0;
8670
8671 clear_problem_context(&pctx);
8672 pctx.ino = ino;
8673
8674 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
8675 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
8676 ctx->bad_lost_and_found++;
8677 }
8678 if (ctx->bad_lost_and_found) {
8679 fix_problem(ctx, PR_3_NO_LPF, &pctx);
8680 return 1;
8681 }
8682
8683 sprintf(name, "#%u", ino);
8684 if (ext2fs_read_inode(fs, ino, &inode) == 0)
8685 file_type = ext2_file_type(inode.i_mode);
8686 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
8687 if (retval == EXT2_ET_DIR_NO_SPACE) {
8688 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
8689 return 1;
8690 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
8691 1, 0);
8692 if (retval) {
8693 pctx.errcode = retval;
8694 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
8695 return 1;
8696 }
8697 retval = ext2fs_link(fs, ctx->lost_and_found, name,
8698 ino, file_type);
8699 }
8700 if (retval) {
8701 pctx.errcode = retval;
8702 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
8703 return 1;
8704 }
8705 e2fsck_adjust_inode_count(ctx, ino, 1);
8706
8707 return 0;
8708}
8709
8710/*
8711 * Utility routine to adjust the inode counts on an inode.
8712 */
8713errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
8714{
8715 ext2_filsys fs = ctx->fs;
8716 errcode_t retval;
8717 struct ext2_inode inode;
8718
8719 if (!ino)
8720 return 0;
8721
8722 retval = ext2fs_read_inode(fs, ino, &inode);
8723 if (retval)
8724 return retval;
8725
8726#if 0
8727 printf("Adjusting link count for inode %lu by %d (from %d)\n", ino, adj,
8728 inode.i_links_count);
8729#endif
8730
8731 if (adj == 1) {
8732 ext2fs_icount_increment(ctx->inode_count, ino, 0);
8733 if (inode.i_links_count == (__u16) ~0)
8734 return 0;
8735 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
8736 inode.i_links_count++;
8737 } else if (adj == -1) {
8738 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
8739 if (inode.i_links_count == 0)
8740 return 0;
8741 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
8742 inode.i_links_count--;
8743 }
8744
8745 retval = ext2fs_write_inode(fs, ino, &inode);
8746 if (retval)
8747 return retval;
8748
8749 return 0;
8750}
8751
8752/*
8753 * Fix parent --- this routine fixes up the parent of a directory.
8754 */
8755struct fix_dotdot_struct {
8756 ext2_filsys fs;
8757 ext2_ino_t parent;
8758 int done;
8759 e2fsck_t ctx;
8760};
8761
8762static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008763 int offset FSCK_ATTR((unused)),
8764 int blocksize FSCK_ATTR((unused)),
8765 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008766 void *priv_data)
8767{
8768 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
8769 errcode_t retval;
8770 struct problem_context pctx;
8771
8772 if ((dirent->name_len & 0xFF) != 2)
8773 return 0;
8774 if (strncmp(dirent->name, "..", 2))
8775 return 0;
8776
8777 clear_problem_context(&pctx);
8778
8779 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
8780 if (retval) {
8781 pctx.errcode = retval;
8782 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
8783 }
8784 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
8785 if (retval) {
8786 pctx.errcode = retval;
8787 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
8788 }
8789 dirent->inode = fp->parent;
8790
8791 fp->done++;
8792 return DIRENT_ABORT | DIRENT_CHANGED;
8793}
8794
8795static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
8796{
8797 ext2_filsys fs = ctx->fs;
8798 errcode_t retval;
8799 struct fix_dotdot_struct fp;
8800 struct problem_context pctx;
8801
8802 fp.fs = fs;
8803 fp.parent = parent;
8804 fp.done = 0;
8805 fp.ctx = ctx;
8806
8807#if 0
8808 printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent);
8809#endif
8810
8811 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
8812 0, fix_dotdot_proc, &fp);
8813 if (retval || !fp.done) {
8814 clear_problem_context(&pctx);
8815 pctx.ino = dir->ino;
8816 pctx.errcode = retval;
8817 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
8818 PR_3_FIX_PARENT_NOFIND, &pctx);
8819 ext2fs_unmark_valid(fs);
8820 }
8821 dir->dotdot = parent;
8822
8823 return;
8824}
8825
8826/*
8827 * These routines are responsible for expanding a /lost+found if it is
8828 * too small.
8829 */
8830
8831struct expand_dir_struct {
8832 int num;
8833 int guaranteed_size;
8834 int newblocks;
8835 int last_block;
8836 errcode_t err;
8837 e2fsck_t ctx;
8838};
8839
8840static int expand_dir_proc(ext2_filsys fs,
8841 blk_t *blocknr,
8842 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00008843 blk_t ref_block FSCK_ATTR((unused)),
8844 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008845 void *priv_data)
8846{
8847 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
8848 blk_t new_blk;
8849 static blk_t last_blk = 0;
8850 char *block;
8851 errcode_t retval;
8852 e2fsck_t ctx;
8853
8854 ctx = es->ctx;
8855
8856 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
8857 return BLOCK_ABORT;
8858
8859 if (blockcnt > 0)
8860 es->last_block = blockcnt;
8861 if (*blocknr) {
8862 last_blk = *blocknr;
8863 return 0;
8864 }
8865 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
8866 &new_blk);
8867 if (retval) {
8868 es->err = retval;
8869 return BLOCK_ABORT;
8870 }
8871 if (blockcnt > 0) {
8872 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
8873 if (retval) {
8874 es->err = retval;
8875 return BLOCK_ABORT;
8876 }
8877 es->num--;
8878 retval = ext2fs_write_dir_block(fs, new_blk, block);
8879 } else {
8880 retval = ext2fs_get_mem(fs->blocksize, &block);
8881 if (retval) {
8882 es->err = retval;
8883 return BLOCK_ABORT;
8884 }
8885 memset(block, 0, fs->blocksize);
8886 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
8887 }
8888 if (retval) {
8889 es->err = retval;
8890 return BLOCK_ABORT;
8891 }
8892 ext2fs_free_mem(&block);
8893 *blocknr = new_blk;
8894 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
8895 ext2fs_block_alloc_stats(fs, new_blk, +1);
8896 es->newblocks++;
8897
8898 if (es->num == 0)
8899 return (BLOCK_CHANGED | BLOCK_ABORT);
8900 else
8901 return BLOCK_CHANGED;
8902}
8903
8904errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
8905 int num, int guaranteed_size)
8906{
8907 ext2_filsys fs = ctx->fs;
8908 errcode_t retval;
8909 struct expand_dir_struct es;
8910 struct ext2_inode inode;
8911
8912 if (!(fs->flags & EXT2_FLAG_RW))
8913 return EXT2_ET_RO_FILSYS;
8914
8915 /*
8916 * Read the inode and block bitmaps in; we'll be messing with
8917 * them.
8918 */
8919 e2fsck_read_bitmaps(ctx);
8920
8921 retval = ext2fs_check_directory(fs, dir);
8922 if (retval)
8923 return retval;
8924
8925 es.num = num;
8926 es.guaranteed_size = guaranteed_size;
8927 es.last_block = 0;
8928 es.err = 0;
8929 es.newblocks = 0;
8930 es.ctx = ctx;
8931
8932 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
8933 0, expand_dir_proc, &es);
8934
8935 if (es.err)
8936 return es.err;
8937
8938 /*
8939 * Update the size and block count fields in the inode.
8940 */
8941 retval = ext2fs_read_inode(fs, dir, &inode);
8942 if (retval)
8943 return retval;
8944
8945 inode.i_size = (es.last_block + 1) * fs->blocksize;
8946 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
8947
8948 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
8949
8950 return 0;
8951}
8952
8953/*
8954 * pass4.c -- pass #4 of e2fsck: Check reference counts
8955 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008956 * Pass 4 frees the following data structures:
8957 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
8958 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
8959 */
8960
8961/*
8962 * This routine is called when an inode is not connected to the
8963 * directory tree.
8964 *
8965 * This subroutine returns 1 then the caller shouldn't bother with the
8966 * rest of the pass 4 tests.
8967 */
8968static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
8969{
8970 ext2_filsys fs = ctx->fs;
8971 struct ext2_inode inode;
8972 struct problem_context pctx;
8973
8974 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
8975 clear_problem_context(&pctx);
8976 pctx.ino = i;
8977 pctx.inode = &inode;
8978
8979 /*
8980 * Offer to delete any zero-length files that does not have
8981 * blocks. If there is an EA block, it might have useful
8982 * information, so we won't prompt to delete it, but let it be
8983 * reconnected to lost+found.
8984 */
8985 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
8986 LINUX_S_ISDIR(inode.i_mode))) {
8987 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
8988 ext2fs_icount_store(ctx->inode_link_info, i, 0);
8989 inode.i_links_count = 0;
8990 inode.i_dtime = time(0);
8991 e2fsck_write_inode(ctx, i, &inode,
8992 "disconnect_inode");
8993 /*
8994 * Fix up the bitmaps...
8995 */
8996 e2fsck_read_bitmaps(ctx);
8997 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
8998 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
8999 ext2fs_inode_alloc_stats2(fs, i, -1,
9000 LINUX_S_ISDIR(inode.i_mode));
9001 return 0;
9002 }
9003 }
9004
9005 /*
9006 * Prompt to reconnect.
9007 */
9008 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
9009 if (e2fsck_reconnect_file(ctx, i))
9010 ext2fs_unmark_valid(fs);
9011 } else {
9012 /*
9013 * If we don't attach the inode, then skip the
9014 * i_links_test since there's no point in trying to
9015 * force i_links_count to zero.
9016 */
9017 ext2fs_unmark_valid(fs);
9018 return 1;
9019 }
9020 return 0;
9021}
9022
9023
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009024static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009025{
9026 ext2_filsys fs = ctx->fs;
9027 ext2_ino_t i;
9028 struct ext2_inode inode;
9029#ifdef RESOURCE_TRACK
9030 struct resource_track rtrack;
9031#endif
9032 struct problem_context pctx;
9033 __u16 link_count, link_counted;
9034 char *buf = 0;
9035 int group, maxgroup;
9036
9037#ifdef RESOURCE_TRACK
9038 init_resource_track(&rtrack);
9039#endif
9040
9041#ifdef MTRACE
9042 mtrace_print("Pass 4");
9043#endif
9044
9045 clear_problem_context(&pctx);
9046
9047 if (!(ctx->options & E2F_OPT_PREEN))
9048 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
9049
9050 group = 0;
9051 maxgroup = fs->group_desc_count;
9052 if (ctx->progress)
9053 if ((ctx->progress)(ctx, 4, 0, maxgroup))
9054 return;
9055
9056 for (i=1; i <= fs->super->s_inodes_count; i++) {
9057 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9058 return;
9059 if ((i % fs->super->s_inodes_per_group) == 0) {
9060 group++;
9061 if (ctx->progress)
9062 if ((ctx->progress)(ctx, 4, group, maxgroup))
9063 return;
9064 }
9065 if (i == EXT2_BAD_INO ||
9066 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
9067 continue;
9068 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
9069 (ctx->inode_imagic_map &&
9070 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) ||
9071 (ctx->inode_bb_map &&
9072 ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
9073 continue;
9074 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
9075 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
9076 if (link_counted == 0) {
9077 if (!buf)
9078 buf = e2fsck_allocate_memory(ctx,
9079 fs->blocksize, "bad_inode buffer");
9080 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
9081 continue;
9082 if (disconnect_inode(ctx, i))
9083 continue;
9084 ext2fs_icount_fetch(ctx->inode_link_info, i,
9085 &link_count);
9086 ext2fs_icount_fetch(ctx->inode_count, i,
9087 &link_counted);
9088 }
9089 if (link_counted != link_count) {
9090 e2fsck_read_inode(ctx, i, &inode, "pass4");
9091 pctx.ino = i;
9092 pctx.inode = &inode;
9093 if (link_count != inode.i_links_count) {
9094 pctx.num = link_count;
9095 fix_problem(ctx,
9096 PR_4_INCONSISTENT_COUNT, &pctx);
9097 }
9098 pctx.num = link_counted;
9099 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
9100 inode.i_links_count = link_counted;
9101 e2fsck_write_inode(ctx, i, &inode, "pass4");
9102 }
9103 }
9104 }
9105 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
9106 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
9107 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
9108 ctx->inode_bb_map = 0;
9109 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
9110 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00009111 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009112#ifdef RESOURCE_TRACK
9113 if (ctx->options & E2F_OPT_TIME2) {
9114 e2fsck_clear_progbar(ctx);
9115 print_resource_track(_("Pass 4"), &rtrack);
9116 }
9117#endif
9118}
9119
9120/*
9121 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009122 */
9123
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009124#define NO_BLK ((blk_t) -1)
9125
9126static void print_bitmap_problem(e2fsck_t ctx, int problem,
9127 struct problem_context *pctx)
9128{
9129 switch (problem) {
9130 case PR_5_BLOCK_UNUSED:
9131 if (pctx->blk == pctx->blk2)
9132 pctx->blk2 = 0;
9133 else
9134 problem = PR_5_BLOCK_RANGE_UNUSED;
9135 break;
9136 case PR_5_BLOCK_USED:
9137 if (pctx->blk == pctx->blk2)
9138 pctx->blk2 = 0;
9139 else
9140 problem = PR_5_BLOCK_RANGE_USED;
9141 break;
9142 case PR_5_INODE_UNUSED:
9143 if (pctx->ino == pctx->ino2)
9144 pctx->ino2 = 0;
9145 else
9146 problem = PR_5_INODE_RANGE_UNUSED;
9147 break;
9148 case PR_5_INODE_USED:
9149 if (pctx->ino == pctx->ino2)
9150 pctx->ino2 = 0;
9151 else
9152 problem = PR_5_INODE_RANGE_USED;
9153 break;
9154 }
9155 fix_problem(ctx, problem, pctx);
9156 pctx->blk = pctx->blk2 = NO_BLK;
9157 pctx->ino = pctx->ino2 = 0;
9158}
9159
9160static void check_block_bitmaps(e2fsck_t ctx)
9161{
9162 ext2_filsys fs = ctx->fs;
9163 blk_t i;
9164 int *free_array;
9165 int group = 0;
9166 unsigned int blocks = 0;
9167 unsigned int free_blocks = 0;
9168 int group_free = 0;
9169 int actual, bitmap;
9170 struct problem_context pctx;
9171 int problem, save_problem, fixit, had_problem;
9172 errcode_t retval;
9173
9174 clear_problem_context(&pctx);
9175 free_array = (int *) e2fsck_allocate_memory(ctx,
9176 fs->group_desc_count * sizeof(int), "free block count array");
9177
9178 if ((fs->super->s_first_data_block <
9179 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
9180 (fs->super->s_blocks_count-1 >
9181 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
9182 pctx.num = 1;
9183 pctx.blk = fs->super->s_first_data_block;
9184 pctx.blk2 = fs->super->s_blocks_count -1;
9185 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
9186 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
9187 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9188
9189 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9190 return;
9191 }
9192
9193 if ((fs->super->s_first_data_block <
9194 ext2fs_get_block_bitmap_start(fs->block_map)) ||
9195 (fs->super->s_blocks_count-1 >
9196 ext2fs_get_block_bitmap_end(fs->block_map))) {
9197 pctx.num = 2;
9198 pctx.blk = fs->super->s_first_data_block;
9199 pctx.blk2 = fs->super->s_blocks_count -1;
9200 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
9201 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
9202 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9203
9204 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9205 return;
9206 }
9207
9208redo_counts:
9209 had_problem = 0;
9210 save_problem = 0;
9211 pctx.blk = pctx.blk2 = NO_BLK;
9212 for (i = fs->super->s_first_data_block;
9213 i < fs->super->s_blocks_count;
9214 i++) {
9215 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
9216 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
9217
9218 if (actual == bitmap)
9219 goto do_counts;
9220
9221 if (!actual && bitmap) {
9222 /*
9223 * Block not used, but marked in use in the bitmap.
9224 */
9225 problem = PR_5_BLOCK_UNUSED;
9226 } else {
9227 /*
9228 * Block used, but not marked in use in the bitmap.
9229 */
9230 problem = PR_5_BLOCK_USED;
9231 }
9232 if (pctx.blk == NO_BLK) {
9233 pctx.blk = pctx.blk2 = i;
9234 save_problem = problem;
9235 } else {
9236 if ((problem == save_problem) &&
9237 (pctx.blk2 == i-1))
9238 pctx.blk2++;
9239 else {
9240 print_bitmap_problem(ctx, save_problem, &pctx);
9241 pctx.blk = pctx.blk2 = i;
9242 save_problem = problem;
9243 }
9244 }
9245 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
9246 had_problem++;
9247
9248 do_counts:
9249 if (!bitmap) {
9250 group_free++;
9251 free_blocks++;
9252 }
9253 blocks ++;
9254 if ((blocks == fs->super->s_blocks_per_group) ||
9255 (i == fs->super->s_blocks_count-1)) {
9256 free_array[group] = group_free;
9257 group ++;
9258 blocks = 0;
9259 group_free = 0;
9260 if (ctx->progress)
9261 if ((ctx->progress)(ctx, 5, group,
9262 fs->group_desc_count*2))
9263 return;
9264 }
9265 }
9266 if (pctx.blk != NO_BLK)
9267 print_bitmap_problem(ctx, save_problem, &pctx);
9268 if (had_problem)
9269 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
9270 else
9271 fixit = -1;
9272 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
9273
9274 if (fixit == 1) {
9275 ext2fs_free_block_bitmap(fs->block_map);
9276 retval = ext2fs_copy_bitmap(ctx->block_found_map,
9277 &fs->block_map);
9278 if (retval) {
9279 clear_problem_context(&pctx);
9280 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
9281 ctx->flags |= E2F_FLAG_ABORT;
9282 return;
9283 }
9284 ext2fs_set_bitmap_padding(fs->block_map);
9285 ext2fs_mark_bb_dirty(fs);
9286
9287 /* Redo the counts */
9288 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
9289 memset(free_array, 0, fs->group_desc_count * sizeof(int));
9290 goto redo_counts;
9291 } else if (fixit == 0)
9292 ext2fs_unmark_valid(fs);
9293
9294 for (i = 0; i < fs->group_desc_count; i++) {
9295 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
9296 pctx.group = i;
9297 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
9298 pctx.blk2 = free_array[i];
9299
9300 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
9301 &pctx)) {
9302 fs->group_desc[i].bg_free_blocks_count =
9303 free_array[i];
9304 ext2fs_mark_super_dirty(fs);
9305 } else
9306 ext2fs_unmark_valid(fs);
9307 }
9308 }
9309 if (free_blocks != fs->super->s_free_blocks_count) {
9310 pctx.group = 0;
9311 pctx.blk = fs->super->s_free_blocks_count;
9312 pctx.blk2 = free_blocks;
9313
9314 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
9315 fs->super->s_free_blocks_count = free_blocks;
9316 ext2fs_mark_super_dirty(fs);
9317 } else
9318 ext2fs_unmark_valid(fs);
9319 }
9320 ext2fs_free_mem(&free_array);
9321}
9322
9323static void check_inode_bitmaps(e2fsck_t ctx)
9324{
9325 ext2_filsys fs = ctx->fs;
9326 ext2_ino_t i;
9327 unsigned int free_inodes = 0;
9328 int group_free = 0;
9329 int dirs_count = 0;
9330 int group = 0;
9331 unsigned int inodes = 0;
9332 int *free_array;
9333 int *dir_array;
9334 int actual, bitmap;
9335 errcode_t retval;
9336 struct problem_context pctx;
9337 int problem, save_problem, fixit, had_problem;
9338
9339 clear_problem_context(&pctx);
9340 free_array = (int *) e2fsck_allocate_memory(ctx,
9341 fs->group_desc_count * sizeof(int), "free inode count array");
9342
9343 dir_array = (int *) e2fsck_allocate_memory(ctx,
9344 fs->group_desc_count * sizeof(int), "directory count array");
9345
9346 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
9347 (fs->super->s_inodes_count >
9348 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
9349 pctx.num = 3;
9350 pctx.blk = 1;
9351 pctx.blk2 = fs->super->s_inodes_count;
9352 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
9353 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
9354 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9355
9356 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9357 return;
9358 }
9359 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
9360 (fs->super->s_inodes_count >
9361 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
9362 pctx.num = 4;
9363 pctx.blk = 1;
9364 pctx.blk2 = fs->super->s_inodes_count;
9365 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
9366 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
9367 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
9368
9369 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9370 return;
9371 }
9372
9373redo_counts:
9374 had_problem = 0;
9375 save_problem = 0;
9376 pctx.ino = pctx.ino2 = 0;
9377 for (i = 1; i <= fs->super->s_inodes_count; i++) {
9378 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
9379 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
9380
9381 if (actual == bitmap)
9382 goto do_counts;
9383
9384 if (!actual && bitmap) {
9385 /*
9386 * Inode wasn't used, but marked in bitmap
9387 */
9388 problem = PR_5_INODE_UNUSED;
9389 } else /* if (actual && !bitmap) */ {
9390 /*
9391 * Inode used, but not in bitmap
9392 */
9393 problem = PR_5_INODE_USED;
9394 }
9395 if (pctx.ino == 0) {
9396 pctx.ino = pctx.ino2 = i;
9397 save_problem = problem;
9398 } else {
9399 if ((problem == save_problem) &&
9400 (pctx.ino2 == i-1))
9401 pctx.ino2++;
9402 else {
9403 print_bitmap_problem(ctx, save_problem, &pctx);
9404 pctx.ino = pctx.ino2 = i;
9405 save_problem = problem;
9406 }
9407 }
9408 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
9409 had_problem++;
9410
9411do_counts:
9412 if (!bitmap) {
9413 group_free++;
9414 free_inodes++;
9415 } else {
9416 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
9417 dirs_count++;
9418 }
9419 inodes++;
9420 if ((inodes == fs->super->s_inodes_per_group) ||
9421 (i == fs->super->s_inodes_count)) {
9422 free_array[group] = group_free;
9423 dir_array[group] = dirs_count;
9424 group ++;
9425 inodes = 0;
9426 group_free = 0;
9427 dirs_count = 0;
9428 if (ctx->progress)
9429 if ((ctx->progress)(ctx, 5,
9430 group + fs->group_desc_count,
9431 fs->group_desc_count*2))
9432 return;
9433 }
9434 }
9435 if (pctx.ino)
9436 print_bitmap_problem(ctx, save_problem, &pctx);
9437
9438 if (had_problem)
9439 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
9440 else
9441 fixit = -1;
9442 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
9443
9444 if (fixit == 1) {
9445 ext2fs_free_inode_bitmap(fs->inode_map);
9446 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
9447 &fs->inode_map);
9448 if (retval) {
9449 clear_problem_context(&pctx);
9450 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
9451 ctx->flags |= E2F_FLAG_ABORT;
9452 return;
9453 }
9454 ext2fs_set_bitmap_padding(fs->inode_map);
9455 ext2fs_mark_ib_dirty(fs);
9456
9457 /* redo counts */
9458 inodes = 0; free_inodes = 0; group_free = 0;
9459 dirs_count = 0; group = 0;
9460 memset(free_array, 0, fs->group_desc_count * sizeof(int));
9461 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
9462 goto redo_counts;
9463 } else if (fixit == 0)
9464 ext2fs_unmark_valid(fs);
9465
9466 for (i = 0; i < fs->group_desc_count; i++) {
9467 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
9468 pctx.group = i;
9469 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
9470 pctx.ino2 = free_array[i];
9471 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
9472 &pctx)) {
9473 fs->group_desc[i].bg_free_inodes_count =
9474 free_array[i];
9475 ext2fs_mark_super_dirty(fs);
9476 } else
9477 ext2fs_unmark_valid(fs);
9478 }
9479 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
9480 pctx.group = i;
9481 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
9482 pctx.ino2 = dir_array[i];
9483
9484 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
9485 &pctx)) {
9486 fs->group_desc[i].bg_used_dirs_count =
9487 dir_array[i];
9488 ext2fs_mark_super_dirty(fs);
9489 } else
9490 ext2fs_unmark_valid(fs);
9491 }
9492 }
9493 if (free_inodes != fs->super->s_free_inodes_count) {
9494 pctx.group = -1;
9495 pctx.ino = fs->super->s_free_inodes_count;
9496 pctx.ino2 = free_inodes;
9497
9498 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
9499 fs->super->s_free_inodes_count = free_inodes;
9500 ext2fs_mark_super_dirty(fs);
9501 } else
9502 ext2fs_unmark_valid(fs);
9503 }
9504 ext2fs_free_mem(&free_array);
9505 ext2fs_free_mem(&dir_array);
9506}
9507
9508static void check_inode_end(e2fsck_t ctx)
9509{
9510 ext2_filsys fs = ctx->fs;
9511 ext2_ino_t end, save_inodes_count, i;
9512 struct problem_context pctx;
9513
9514 clear_problem_context(&pctx);
9515
9516 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
9517 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
9518 &save_inodes_count);
9519 if (pctx.errcode) {
9520 pctx.num = 1;
9521 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9522 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9523 return;
9524 }
9525 if (save_inodes_count == end)
9526 return;
9527
9528 for (i = save_inodes_count + 1; i <= end; i++) {
9529 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
9530 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
9531 for (i = save_inodes_count + 1; i <= end; i++)
9532 ext2fs_mark_inode_bitmap(fs->inode_map,
9533 i);
9534 ext2fs_mark_ib_dirty(fs);
9535 } else
9536 ext2fs_unmark_valid(fs);
9537 break;
9538 }
9539 }
9540
9541 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
9542 save_inodes_count, 0);
9543 if (pctx.errcode) {
9544 pctx.num = 2;
9545 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9546 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9547 return;
9548 }
9549}
9550
9551static void check_block_end(e2fsck_t ctx)
9552{
9553 ext2_filsys fs = ctx->fs;
9554 blk_t end, save_blocks_count, i;
9555 struct problem_context pctx;
9556
9557 clear_problem_context(&pctx);
9558
9559 end = fs->block_map->start +
9560 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
9561 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
9562 &save_blocks_count);
9563 if (pctx.errcode) {
9564 pctx.num = 3;
9565 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9566 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9567 return;
9568 }
9569 if (save_blocks_count == end)
9570 return;
9571
9572 for (i = save_blocks_count + 1; i <= end; i++) {
9573 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
9574 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
9575 for (i = save_blocks_count + 1; i <= end; i++)
9576 ext2fs_mark_block_bitmap(fs->block_map,
9577 i);
9578 ext2fs_mark_bb_dirty(fs);
9579 } else
9580 ext2fs_unmark_valid(fs);
9581 break;
9582 }
9583 }
9584
9585 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
9586 save_blocks_count, 0);
9587 if (pctx.errcode) {
9588 pctx.num = 4;
9589 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
9590 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
9591 return;
9592 }
9593}
9594
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009595static void e2fsck_pass5(e2fsck_t ctx)
9596{
9597#ifdef RESOURCE_TRACK
9598 struct resource_track rtrack;
9599#endif
9600 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009601
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009602#ifdef MTRACE
9603 mtrace_print("Pass 5");
9604#endif
9605
9606#ifdef RESOURCE_TRACK
9607 init_resource_track(&rtrack);
9608#endif
9609
9610 clear_problem_context(&pctx);
9611
9612 if (!(ctx->options & E2F_OPT_PREEN))
9613 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
9614
9615 if (ctx->progress)
9616 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
9617 return;
9618
9619 e2fsck_read_bitmaps(ctx);
9620
9621 check_block_bitmaps(ctx);
9622 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9623 return;
9624 check_inode_bitmaps(ctx);
9625 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9626 return;
9627 check_inode_end(ctx);
9628 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9629 return;
9630 check_block_end(ctx);
9631 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
9632 return;
9633
9634 ext2fs_free_inode_bitmap(ctx->inode_used_map);
9635 ctx->inode_used_map = 0;
9636 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
9637 ctx->inode_dir_map = 0;
9638 ext2fs_free_block_bitmap(ctx->block_found_map);
9639 ctx->block_found_map = 0;
9640
9641#ifdef RESOURCE_TRACK
9642 if (ctx->options & E2F_OPT_TIME2) {
9643 e2fsck_clear_progbar(ctx);
9644 print_resource_track(_("Pass 5"), &rtrack);
9645 }
9646#endif
9647}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009648
9649/*
9650 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009651 */
9652
9653#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
9654#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
9655#define PR_NO_DEFAULT 0x000004 /* Default to no */
9656#define PR_MSG_ONLY 0x000008 /* Print message only */
9657
9658/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
9659
9660#define PR_FATAL 0x001000 /* Fatal error */
9661#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
9662 /* ask another */
9663#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
9664#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
9665#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
9666#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
9667#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
9668
9669
9670#define PROMPT_NONE 0
9671#define PROMPT_FIX 1
9672#define PROMPT_CLEAR 2
9673#define PROMPT_RELOCATE 3
9674#define PROMPT_ALLOCATE 4
9675#define PROMPT_EXPAND 5
9676#define PROMPT_CONNECT 6
9677#define PROMPT_CREATE 7
9678#define PROMPT_SALVAGE 8
9679#define PROMPT_TRUNCATE 9
9680#define PROMPT_CLEAR_INODE 10
9681#define PROMPT_ABORT 11
9682#define PROMPT_SPLIT 12
9683#define PROMPT_CONTINUE 13
9684#define PROMPT_CLONE 14
9685#define PROMPT_DELETE 15
9686#define PROMPT_SUPPRESS 16
9687#define PROMPT_UNLINK 17
9688#define PROMPT_CLEAR_HTREE 18
9689#define PROMPT_RECREATE 19
9690#define PROMPT_NULL 20
9691
9692struct e2fsck_problem {
9693 problem_t e2p_code;
9694 const char * e2p_description;
9695 char prompt;
9696 int flags;
9697 problem_t second_code;
9698};
9699
9700struct latch_descr {
9701 int latch_code;
9702 problem_t question;
9703 problem_t end_message;
9704 int flags;
9705};
9706
9707/*
9708 * These are the prompts which are used to ask the user if they want
9709 * to fix a problem.
9710 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009711static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009712 N_("(no prompt)"), /* 0 */
9713 N_("Fix"), /* 1 */
9714 N_("Clear"), /* 2 */
9715 N_("Relocate"), /* 3 */
9716 N_("Allocate"), /* 4 */
9717 N_("Expand"), /* 5 */
9718 N_("Connect to /lost+found"), /* 6 */
9719 N_("Create"), /* 7 */
9720 N_("Salvage"), /* 8 */
9721 N_("Truncate"), /* 9 */
9722 N_("Clear inode"), /* 10 */
9723 N_("Abort"), /* 11 */
9724 N_("Split"), /* 12 */
9725 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00009726 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009727 N_("Delete file"), /* 15 */
9728 N_("Suppress messages"),/* 16 */
9729 N_("Unlink"), /* 17 */
9730 N_("Clear HTree index"),/* 18 */
9731 N_("Recreate"), /* 19 */
9732 "", /* 20 */
9733};
9734
9735/*
9736 * These messages are printed when we are preen mode and we will be
9737 * automatically fixing the problem.
9738 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009739static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009740 N_("(NONE)"), /* 0 */
9741 N_("FIXED"), /* 1 */
9742 N_("CLEARED"), /* 2 */
9743 N_("RELOCATED"), /* 3 */
9744 N_("ALLOCATED"), /* 4 */
9745 N_("EXPANDED"), /* 5 */
9746 N_("RECONNECTED"), /* 6 */
9747 N_("CREATED"), /* 7 */
9748 N_("SALVAGED"), /* 8 */
9749 N_("TRUNCATED"), /* 9 */
9750 N_("INODE CLEARED"), /* 10 */
9751 N_("ABORTED"), /* 11 */
9752 N_("SPLIT"), /* 12 */
9753 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00009754 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009755 N_("FILE DELETED"), /* 15 */
9756 N_("SUPPRESSED"), /* 16 */
9757 N_("UNLINKED"), /* 17 */
9758 N_("HTREE INDEX CLEARED"),/* 18 */
9759 N_("WILL RECREATE"), /* 19 */
9760 "", /* 20 */
9761};
9762
9763static const struct e2fsck_problem problem_table[] = {
9764
9765 /* Pre-Pass 1 errors */
9766
9767 /* Block bitmap not in group */
9768 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
9769 PROMPT_RELOCATE, PR_LATCH_RELOC },
9770
9771 /* Inode bitmap not in group */
9772 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
9773 PROMPT_RELOCATE, PR_LATCH_RELOC },
9774
9775 /* Inode table not in group */
9776 { PR_0_ITABLE_NOT_GROUP,
9777 N_("@i table for @g %g is not in @g. (@b %b)\n"
9778 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
9779 PROMPT_RELOCATE, PR_LATCH_RELOC },
9780
9781 /* Superblock corrupt */
9782 { PR_0_SB_CORRUPT,
9783 N_("\nThe @S could not be read or does not describe a correct ext2\n"
9784 "@f. If the @v is valid and it really contains an ext2\n"
9785 "@f (and not swap or ufs or something else), then the @S\n"
9786 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
9787 " e2fsck -b %S <@v>\n\n"),
9788 PROMPT_NONE, PR_FATAL },
9789
9790 /* Filesystem size is wrong */
9791 { PR_0_FS_SIZE_WRONG,
9792 N_("The @f size (according to the @S) is %b @bs\n"
9793 "The physical size of the @v is %c @bs\n"
9794 "Either the @S or the partition table is likely to be corrupt!\n"),
9795 PROMPT_ABORT, 0 },
9796
9797 /* Fragments not supported */
9798 { PR_0_NO_FRAGMENTS,
9799 N_("@S @b_size = %b, fragsize = %c.\n"
9800 "This version of e2fsck does not support fragment sizes different\n"
9801 "from the @b size.\n"),
9802 PROMPT_NONE, PR_FATAL },
9803
9804 /* Bad blocks_per_group */
9805 { PR_0_BLOCKS_PER_GROUP,
9806 N_("@S @bs_per_group = %b, should have been %c\n"),
9807 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9808
9809 /* Bad first_data_block */
9810 { PR_0_FIRST_DATA_BLOCK,
9811 N_("@S first_data_@b = %b, should have been %c\n"),
9812 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9813
9814 /* Adding UUID to filesystem */
9815 { PR_0_ADD_UUID,
9816 N_("@f did not have a UUID; generating one.\n\n"),
9817 PROMPT_NONE, 0 },
9818
9819 /* Relocate hint */
9820 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00009821 N_("Note: if several inode or block bitmap blocks or part\n"
9822 "of the inode table require relocation, you may wish to try\n"
9823 "running e2fsck with the '-b %S' option first. The problem\n"
9824 "may lie only with the primary block group descriptors, and\n"
9825 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009826 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
9827
9828 /* Miscellaneous superblock corruption */
9829 { PR_0_MISC_CORRUPT_SUPER,
9830 N_("Corruption found in @S. (%s = %N).\n"),
9831 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
9832
9833 /* Error determing physical device size of filesystem */
9834 { PR_0_GETSIZE_ERROR,
9835 N_("Error determining size of the physical @v: %m\n"),
9836 PROMPT_NONE, PR_FATAL },
9837
9838 /* Inode count in superblock is incorrect */
9839 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00009840 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009841 PROMPT_FIX, 0 },
9842
9843 { PR_0_HURD_CLEAR_FILETYPE,
9844 N_("The Hurd does not support the filetype feature.\n"),
9845 PROMPT_CLEAR, 0 },
9846
9847 /* Journal inode is invalid */
9848 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009849 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009850 PROMPT_CLEAR, PR_PREEN_OK },
9851
9852 /* The external journal has (unsupported) multiple filesystems */
9853 { PR_0_JOURNAL_UNSUPP_MULTIFS,
9854 N_("External @j has multiple @f users (unsupported).\n"),
9855 PROMPT_NONE, PR_FATAL },
9856
9857 /* Can't find external journal */
9858 { PR_0_CANT_FIND_JOURNAL,
9859 N_("Can't find external @j\n"),
9860 PROMPT_NONE, PR_FATAL },
9861
9862 /* External journal has bad superblock */
9863 { PR_0_EXT_JOURNAL_BAD_SUPER,
9864 N_("External @j has bad @S\n"),
9865 PROMPT_NONE, PR_FATAL },
9866
9867 /* Superblock has a bad journal UUID */
9868 { PR_0_JOURNAL_BAD_UUID,
9869 N_("External @j does not support this @f\n"),
9870 PROMPT_NONE, PR_FATAL },
9871
9872 /* Journal has an unknown superblock type */
9873 { PR_0_JOURNAL_UNSUPP_SUPER,
9874 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
9875 "It is likely that your copy of e2fsck is old and/or doesn't "
9876 "support this @j format.\n"
9877 "It is also possible the @j @S is corrupt.\n"),
9878 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
9879
9880 /* Journal superblock is corrupt */
9881 { PR_0_JOURNAL_BAD_SUPER,
9882 N_("Ext3 @j @S is corrupt.\n"),
9883 PROMPT_FIX, PR_PREEN_OK },
9884
9885 /* Superblock flag should be cleared */
9886 { PR_0_JOURNAL_HAS_JOURNAL,
9887 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
9888 PROMPT_CLEAR, PR_PREEN_OK },
9889
9890 /* Superblock flag is incorrect */
9891 { PR_0_JOURNAL_RECOVER_SET,
9892 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
9893 PROMPT_CLEAR, PR_PREEN_OK },
9894
9895 /* Journal has data, but recovery flag is clear */
9896 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009897 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009898 PROMPT_NONE, 0 },
9899
9900 /* Ask if we should clear the journal */
9901 { PR_0_JOURNAL_RESET_JOURNAL,
9902 N_("Clear @j"),
9903 PROMPT_NULL, PR_PREEN_NOMSG },
9904
9905 /* Ask if we should run the journal anyway */
9906 { PR_0_JOURNAL_RUN,
9907 N_("Run @j anyway"),
9908 PROMPT_NULL, 0 },
9909
9910 /* Run the journal by default */
9911 { PR_0_JOURNAL_RUN_DEFAULT,
9912 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
9913 PROMPT_NONE, 0 },
9914
9915 /* Clearing orphan inode */
9916 { PR_0_ORPHAN_CLEAR_INODE,
9917 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
9918 PROMPT_NONE, 0 },
9919
9920 /* Illegal block found in orphaned inode */
9921 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
9922 N_("@I @b #%B (%b) found in @o @i %i.\n"),
9923 PROMPT_NONE, 0 },
9924
9925 /* Already cleared block found in orphaned inode */
9926 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
9927 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
9928 PROMPT_NONE, 0 },
9929
9930 /* Illegal orphan inode in superblock */
9931 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
9932 N_("@I @o @i %i in @S.\n"),
9933 PROMPT_NONE, 0 },
9934
9935 /* Illegal inode in orphaned inode list */
9936 { PR_0_ORPHAN_ILLEGAL_INODE,
9937 N_("@I @i %i in @o @i list.\n"),
9938 PROMPT_NONE, 0 },
9939
9940 /* Filesystem revision is 0, but feature flags are set */
9941 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00009942 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009943 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
9944
9945 /* Journal superblock has an unknown read-only feature flag set */
9946 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
9947 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
9948 PROMPT_ABORT, 0 },
9949
9950 /* Journal superblock has an unknown incompatible feature flag set */
9951 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
9952 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
9953 PROMPT_ABORT, 0 },
9954
9955 /* Journal has unsupported version number */
9956 { PR_0_JOURNAL_UNSUPP_VERSION,
9957 N_("@j version not supported by this e2fsck.\n"),
9958 PROMPT_ABORT, 0 },
9959
9960 /* Moving journal to hidden file */
9961 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00009962 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009963 PROMPT_NONE, 0 },
9964
9965 /* Error moving journal to hidden file */
9966 { PR_0_ERR_MOVE_JOURNAL,
9967 N_("Error moving @j: %m\n\n"),
9968 PROMPT_NONE, 0 },
9969
9970 /* Clearing V2 journal superblock */
9971 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00009972 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009973 "Clearing fields beyond the V1 @j @S...\n\n"),
9974 PROMPT_NONE, 0 },
9975
9976 /* Backup journal inode blocks */
9977 { PR_0_BACKUP_JNL,
9978 N_("Backing up @j @i @b information.\n\n"),
9979 PROMPT_NONE, 0 },
9980
9981 /* Reserved blocks w/o resize_inode */
9982 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
9983 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
9984 "is %N; @s zero. "),
9985 PROMPT_FIX, 0 },
9986
9987 /* Resize_inode not enabled, but resize inode is non-zero */
9988 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009989 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009990 PROMPT_CLEAR, 0 },
9991
9992 /* Resize inode invalid */
9993 { PR_0_RESIZE_INODE_INVALID,
9994 N_("Resize @i not valid. "),
9995 PROMPT_RECREATE, 0 },
9996
9997 /* Pass 1 errors */
9998
9999 /* Pass 1: Checking inodes, blocks, and sizes */
10000 { PR_1_PASS_HEADER,
10001 N_("Pass 1: Checking @is, @bs, and sizes\n"),
10002 PROMPT_NONE, 0 },
10003
10004 /* Root directory is not an inode */
10005 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
10006 PROMPT_CLEAR, 0 },
10007
10008 /* Root directory has dtime set */
10009 { PR_1_ROOT_DTIME,
10010 N_("@r has dtime set (probably due to old mke2fs). "),
10011 PROMPT_FIX, PR_PREEN_OK },
10012
10013 /* Reserved inode has bad mode */
10014 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010015 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010016 PROMPT_CLEAR, PR_PREEN_OK },
10017
10018 /* Deleted inode has zero dtime */
10019 { PR_1_ZERO_DTIME,
10020 N_("@D @i %i has zero dtime. "),
10021 PROMPT_FIX, PR_PREEN_OK },
10022
10023 /* Inode in use, but dtime set */
10024 { PR_1_SET_DTIME,
10025 N_("@i %i is in use, but has dtime set. "),
10026 PROMPT_FIX, PR_PREEN_OK },
10027
10028 /* Zero-length directory */
10029 { PR_1_ZERO_LENGTH_DIR,
10030 N_("@i %i is a @z @d. "),
10031 PROMPT_CLEAR, PR_PREEN_OK },
10032
10033 /* Block bitmap conflicts with some other fs block */
10034 { PR_1_BB_CONFLICT,
10035 N_("@g %g's @b @B at %b @C.\n"),
10036 PROMPT_RELOCATE, 0 },
10037
10038 /* Inode bitmap conflicts with some other fs block */
10039 { PR_1_IB_CONFLICT,
10040 N_("@g %g's @i @B at %b @C.\n"),
10041 PROMPT_RELOCATE, 0 },
10042
10043 /* Inode table conflicts with some other fs block */
10044 { PR_1_ITABLE_CONFLICT,
10045 N_("@g %g's @i table at %b @C.\n"),
10046 PROMPT_RELOCATE, 0 },
10047
10048 /* Block bitmap is on a bad block */
10049 { PR_1_BB_BAD_BLOCK,
10050 N_("@g %g's @b @B (%b) is bad. "),
10051 PROMPT_RELOCATE, 0 },
10052
10053 /* Inode bitmap is on a bad block */
10054 { PR_1_IB_BAD_BLOCK,
10055 N_("@g %g's @i @B (%b) is bad. "),
10056 PROMPT_RELOCATE, 0 },
10057
10058 /* Inode has incorrect i_size */
10059 { PR_1_BAD_I_SIZE,
10060 N_("@i %i, i_size is %Is, @s %N. "),
10061 PROMPT_FIX, PR_PREEN_OK },
10062
10063 /* Inode has incorrect i_blocks */
10064 { PR_1_BAD_I_BLOCKS,
10065 N_("@i %i, i_@bs is %Ib, @s %N. "),
10066 PROMPT_FIX, PR_PREEN_OK },
10067
10068 /* Illegal blocknumber in inode */
10069 { PR_1_ILLEGAL_BLOCK_NUM,
10070 N_("@I @b #%B (%b) in @i %i. "),
10071 PROMPT_CLEAR, PR_LATCH_BLOCK },
10072
10073 /* Block number overlaps fs metadata */
10074 { PR_1_BLOCK_OVERLAPS_METADATA,
10075 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
10076 PROMPT_CLEAR, PR_LATCH_BLOCK },
10077
10078 /* Inode has illegal blocks (latch question) */
10079 { PR_1_INODE_BLOCK_LATCH,
10080 N_("@i %i has illegal @b(s). "),
10081 PROMPT_CLEAR, 0 },
10082
10083 /* Too many bad blocks in inode */
10084 { PR_1_TOO_MANY_BAD_BLOCKS,
10085 N_("Too many illegal @bs in @i %i.\n"),
10086 PROMPT_CLEAR_INODE, PR_NO_OK },
10087
10088 /* Illegal block number in bad block inode */
10089 { PR_1_BB_ILLEGAL_BLOCK_NUM,
10090 N_("@I @b #%B (%b) in bad @b @i. "),
10091 PROMPT_CLEAR, PR_LATCH_BBLOCK },
10092
10093 /* Bad block inode has illegal blocks (latch question) */
10094 { PR_1_INODE_BBLOCK_LATCH,
10095 N_("Bad @b @i has illegal @b(s). "),
10096 PROMPT_CLEAR, 0 },
10097
10098 /* Duplicate or bad blocks in use! */
10099 { PR_1_DUP_BLOCKS_PREENSTOP,
10100 N_("Duplicate or bad @b in use!\n"),
10101 PROMPT_NONE, 0 },
10102
10103 /* Bad block used as bad block indirect block */
10104 { PR_1_BBINODE_BAD_METABLOCK,
10105 N_("Bad @b %b used as bad @b @i indirect @b. "),
10106 PROMPT_CLEAR, PR_LATCH_BBLOCK },
10107
10108 /* Inconsistency can't be fixed prompt */
10109 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
10110 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
10111 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
10112 "in the @f.\n"),
10113 PROMPT_CONTINUE, PR_PREEN_NOMSG },
10114
10115 /* Bad primary block */
10116 { PR_1_BAD_PRIMARY_BLOCK,
10117 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
10118 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
10119
10120 /* Bad primary block prompt */
10121 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +000010122 N_("You can remove this @b from the bad @b list and hope\n"
10123 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010124 PROMPT_CLEAR, PR_PREEN_NOMSG },
10125
10126 /* Bad primary superblock */
10127 { PR_1_BAD_PRIMARY_SUPERBLOCK,
10128 N_("The primary @S (%b) is on the bad @b list.\n"),
10129 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
10130
10131 /* Bad primary block group descriptors */
10132 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
10133 N_("Block %b in the primary @g descriptors "
10134 "is on the bad @b list\n"),
10135 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
10136
10137 /* Bad superblock in group */
10138 { PR_1_BAD_SUPERBLOCK,
10139 N_("Warning: Group %g's @S (%b) is bad.\n"),
10140 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
10141
10142 /* Bad block group descriptors in group */
10143 { PR_1_BAD_GROUP_DESCRIPTORS,
10144 N_("Warning: Group %g's copy of the @g descriptors has a bad "
10145 "@b (%b).\n"),
10146 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
10147
10148 /* Block claimed for no reason */
10149 { PR_1_PROGERR_CLAIMED_BLOCK,
10150 N_("Programming error? @b #%b claimed for no reason in "
10151 "process_bad_@b.\n"),
10152 PROMPT_NONE, PR_PREEN_OK },
10153
10154 /* Error allocating blocks for relocating metadata */
10155 { PR_1_RELOC_BLOCK_ALLOCATE,
10156 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
10157 PROMPT_NONE, PR_PREEN_OK },
10158
10159 /* Error allocating block buffer during relocation process */
10160 { PR_1_RELOC_MEMORY_ALLOCATE,
10161 N_("@A @b buffer for relocating %s\n"),
10162 PROMPT_NONE, PR_PREEN_OK },
10163
10164 /* Relocating metadata group information from X to Y */
10165 { PR_1_RELOC_FROM_TO,
10166 N_("Relocating @g %g's %s from %b to %c...\n"),
10167 PROMPT_NONE, PR_PREEN_OK },
10168
10169 /* Relocating metatdata group information to X */
10170 { PR_1_RELOC_TO,
10171 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
10172 PROMPT_NONE, PR_PREEN_OK },
10173
10174 /* Block read error during relocation process */
10175 { PR_1_RELOC_READ_ERR,
10176 N_("Warning: could not read @b %b of %s: %m\n"),
10177 PROMPT_NONE, PR_PREEN_OK },
10178
10179 /* Block write error during relocation process */
10180 { PR_1_RELOC_WRITE_ERR,
10181 N_("Warning: could not write @b %b for %s: %m\n"),
10182 PROMPT_NONE, PR_PREEN_OK },
10183
10184 /* Error allocating inode bitmap */
10185 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010186 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010187 PROMPT_NONE, PR_FATAL },
10188
10189 /* Error allocating block bitmap */
10190 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010191 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010192 PROMPT_NONE, PR_FATAL },
10193
10194 /* Error allocating icount structure */
10195 { PR_1_ALLOCATE_ICOUNT,
10196 N_("@A icount link information: %m\n"),
10197 PROMPT_NONE, PR_FATAL },
10198
10199 /* Error allocating dbcount */
10200 { PR_1_ALLOCATE_DBCOUNT,
10201 N_("@A @d @b array: %m\n"),
10202 PROMPT_NONE, PR_FATAL },
10203
10204 /* Error while scanning inodes */
10205 { PR_1_ISCAN_ERROR,
10206 N_("Error while scanning @is (%i): %m\n"),
10207 PROMPT_NONE, PR_FATAL },
10208
10209 /* Error while iterating over blocks */
10210 { PR_1_BLOCK_ITERATE,
10211 N_("Error while iterating over @bs in @i %i: %m\n"),
10212 PROMPT_NONE, PR_FATAL },
10213
10214 /* Error while storing inode count information */
10215 { PR_1_ICOUNT_STORE,
10216 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
10217 PROMPT_NONE, PR_FATAL },
10218
10219 /* Error while storing directory block information */
10220 { PR_1_ADD_DBLOCK,
10221 N_("Error storing @d @b information "
10222 "(@i=%i, @b=%b, num=%N): %m\n"),
10223 PROMPT_NONE, PR_FATAL },
10224
10225 /* Error while reading inode (for clearing) */
10226 { PR_1_READ_INODE,
10227 N_("Error reading @i %i: %m\n"),
10228 PROMPT_NONE, PR_FATAL },
10229
10230 /* Suppress messages prompt */
10231 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
10232
10233 /* Imagic flag set on an inode when filesystem doesn't support it */
10234 { PR_1_SET_IMAGIC,
10235 N_("@i %i has imagic flag set. "),
10236 PROMPT_CLEAR, 0 },
10237
10238 /* Immutable flag set on a device or socket inode */
10239 { PR_1_SET_IMMUTABLE,
10240 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
10241 "or append-only flag set. "),
10242 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
10243
10244 /* Compression flag set on an inode when filesystem doesn't support it */
10245 { PR_1_COMPR_SET,
10246 N_("@i %i has @cion flag set on @f without @cion support. "),
10247 PROMPT_CLEAR, 0 },
10248
10249 /* Non-zero size for device, fifo or socket inode */
10250 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010251 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010252 PROMPT_FIX, PR_PREEN_OK },
10253
10254 /* Filesystem revision is 0, but feature flags are set */
10255 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +000010256 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010257 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
10258
10259 /* Journal inode is not in use, but contains data */
10260 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +000010261 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010262 PROMPT_CLEAR, PR_PREEN_OK },
10263
10264 /* Journal has bad mode */
10265 { PR_1_JOURNAL_BAD_MODE,
10266 N_("@j is not regular file. "),
10267 PROMPT_FIX, PR_PREEN_OK },
10268
10269 /* Deal with inodes that were part of orphan linked list */
10270 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +000010271 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010272 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
10273
10274 /* Deal with inodes that were part of corrupted orphan linked
10275 list (latch question) */
10276 { PR_1_ORPHAN_LIST_REFUGEES,
10277 N_("@is that were part of a corrupted orphan linked list found. "),
10278 PROMPT_FIX, 0 },
10279
10280 /* Error allocating refcount structure */
10281 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010282 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010283 PROMPT_NONE, PR_FATAL },
10284
10285 /* Error reading extended attribute block */
10286 { PR_1_READ_EA_BLOCK,
10287 N_("Error reading @a @b %b for @i %i. "),
10288 PROMPT_CLEAR, 0 },
10289
10290 /* Invalid extended attribute block */
10291 { PR_1_BAD_EA_BLOCK,
10292 N_("@i %i has a bad @a @b %b. "),
10293 PROMPT_CLEAR, 0 },
10294
10295 /* Error reading Extended Attribute block while fixing refcount */
10296 { PR_1_EXTATTR_READ_ABORT,
10297 N_("Error reading @a @b %b (%m). "),
10298 PROMPT_ABORT, 0 },
10299
10300 /* Extended attribute reference count incorrect */
10301 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010302 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010303 PROMPT_FIX, 0 },
10304
10305 /* Error writing Extended Attribute block while fixing refcount */
10306 { PR_1_EXTATTR_WRITE,
10307 N_("Error writing @a @b %b (%m). "),
10308 PROMPT_ABORT, 0 },
10309
10310 /* Multiple EA blocks not supported */
10311 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +000010312 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010313 PROMPT_CLEAR, 0},
10314
10315 /* Error allocating EA region allocation structure */
10316 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +000010317 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010318 PROMPT_ABORT, 0},
10319
10320 /* Error EA allocation collision */
10321 { PR_1_EA_ALLOC_COLLISION,
10322 N_("@a @b %b is corrupt (allocation collision). "),
10323 PROMPT_CLEAR, 0},
10324
10325 /* Bad extended attribute name */
10326 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +000010327 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010328 PROMPT_CLEAR, 0},
10329
10330 /* Bad extended attribute value */
10331 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +000010332 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010333 PROMPT_CLEAR, 0},
10334
10335 /* Inode too big (latch question) */
10336 { PR_1_INODE_TOOBIG,
10337 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
10338
10339 /* Directory too big */
10340 { PR_1_TOOBIG_DIR,
10341 N_("@b #%B (%b) causes @d to be too big. "),
10342 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10343
10344 /* Regular file too big */
10345 { PR_1_TOOBIG_REG,
10346 N_("@b #%B (%b) causes file to be too big. "),
10347 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10348
10349 /* Symlink too big */
10350 { PR_1_TOOBIG_SYMLINK,
10351 N_("@b #%B (%b) causes symlink to be too big. "),
10352 PROMPT_CLEAR, PR_LATCH_TOOBIG },
10353
10354 /* INDEX_FL flag set on a non-HTREE filesystem */
10355 { PR_1_HTREE_SET,
10356 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
10357 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10358
10359 /* INDEX_FL flag set on a non-directory */
10360 { PR_1_HTREE_NODIR,
10361 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
10362 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10363
10364 /* Invalid root node in HTREE directory */
10365 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010366 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010367 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10368
10369 /* Unsupported hash version in HTREE directory */
10370 { PR_1_HTREE_HASHV,
10371 N_("@h %i has an unsupported hash version (%N)\n"),
10372 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10373
10374 /* Incompatible flag in HTREE root node */
10375 { PR_1_HTREE_INCOMPAT,
10376 N_("@h %i uses an incompatible htree root node flag.\n"),
10377 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10378
10379 /* HTREE too deep */
10380 { PR_1_HTREE_DEPTH,
10381 N_("@h %i has a tree depth (%N) which is too big\n"),
10382 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10383
10384 /* Bad block has indirect block that conflicts with filesystem block */
10385 { PR_1_BB_FS_BLOCK,
10386 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
10387 "@f metadata. "),
10388 PROMPT_CLEAR, PR_LATCH_BBLOCK },
10389
10390 /* Resize inode failed */
10391 { PR_1_RESIZE_INODE_CREATE,
10392 N_("Resize @i (re)creation failed: %m."),
10393 PROMPT_ABORT, 0 },
10394
10395 /* invalid inode->i_extra_isize */
10396 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010397 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010398 PROMPT_FIX, PR_PREEN_OK },
10399
10400 /* invalid ea entry->e_name_len */
10401 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +000010402 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010403 PROMPT_CLEAR, PR_PREEN_OK },
10404
10405 /* invalid ea entry->e_value_size */
10406 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +000010407 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010408 PROMPT_CLEAR, PR_PREEN_OK },
10409
10410 /* invalid ea entry->e_value_offs */
10411 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +000010412 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010413 PROMPT_CLEAR, PR_PREEN_OK },
10414
10415 /* invalid ea entry->e_value_block */
10416 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +000010417 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 +000010418 PROMPT_CLEAR, PR_PREEN_OK },
10419
10420 /* invalid ea entry->e_hash */
10421 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +000010422 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 +000010423 PROMPT_CLEAR, PR_PREEN_OK },
10424
10425 /* Pass 1b errors */
10426
10427 /* Pass 1B: Rescan for duplicate/bad blocks */
10428 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010429 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
10430 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010431 PROMPT_NONE, 0 },
10432
10433 /* Duplicate/bad block(s) header */
10434 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010435 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010436 PROMPT_NONE, 0 },
10437
10438 /* Duplicate/bad block(s) in inode */
10439 { PR_1B_DUP_BLOCK,
10440 " %b",
10441 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
10442
10443 /* Duplicate/bad block(s) end */
10444 { PR_1B_DUP_BLOCK_END,
10445 "\n",
10446 PROMPT_NONE, PR_PREEN_NOHDR },
10447
10448 /* Error while scanning inodes */
10449 { PR_1B_ISCAN_ERROR,
10450 N_("Error while scanning inodes (%i): %m\n"),
10451 PROMPT_NONE, PR_FATAL },
10452
10453 /* Error allocating inode bitmap */
10454 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000010455 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010456 PROMPT_NONE, PR_FATAL },
10457
10458 /* Error while iterating over blocks */
10459 { PR_1B_BLOCK_ITERATE,
10460 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
10461 PROMPT_NONE, 0 },
10462
10463 /* Error adjusting EA refcount */
10464 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010465 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010466 PROMPT_NONE, 0 },
10467
10468
Mike Frysinger874af852006-03-08 07:03:27 +000010469 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010470 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010471 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010472 PROMPT_NONE, 0 },
10473
10474
Mike Frysinger874af852006-03-08 07:03:27 +000010475 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010476 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +000010477 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010478 PROMPT_NONE, 0 },
10479
10480 /* File has duplicate blocks */
10481 { PR_1D_DUP_FILE,
10482 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +000010483 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010484 PROMPT_NONE, 0 },
10485
10486 /* List of files sharing duplicate blocks */
10487 { PR_1D_DUP_FILE_LIST,
10488 N_("\t%Q (@i #%i, mod time %IM)\n"),
10489 PROMPT_NONE, 0 },
10490
10491 /* File sharing blocks with filesystem metadata */
10492 { PR_1D_SHARE_METADATA,
10493 N_("\t<@f metadata>\n"),
10494 PROMPT_NONE, 0 },
10495
10496 /* Report of how many duplicate/bad inodes */
10497 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +000010498 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010499 PROMPT_NONE, 0 },
10500
10501 /* Duplicated blocks already reassigned or cloned. */
10502 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +000010503 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010504 PROMPT_NONE, 0 },
10505
10506 /* Clone duplicate/bad blocks? */
10507 { PR_1D_CLONE_QUESTION,
10508 "", PROMPT_CLONE, PR_NO_OK },
10509
10510 /* Delete file? */
10511 { PR_1D_DELETE_QUESTION,
10512 "", PROMPT_DELETE, 0 },
10513
10514 /* Couldn't clone file (error) */
10515 { PR_1D_CLONE_ERROR,
10516 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
10517
10518 /* Pass 2 errors */
10519
10520 /* Pass 2: Checking directory structure */
10521 { PR_2_PASS_HEADER,
10522 N_("Pass 2: Checking @d structure\n"),
10523 PROMPT_NONE, 0 },
10524
10525 /* Bad inode number for '.' */
10526 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010527 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010528 PROMPT_FIX, 0 },
10529
10530 /* Directory entry has bad inode number */
10531 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +000010532 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010533 PROMPT_CLEAR, 0 },
10534
10535 /* Directory entry has deleted or unused inode */
10536 { PR_2_UNUSED_INODE,
10537 N_("@E has @D/unused @i %Di. "),
10538 PROMPT_CLEAR, PR_PREEN_OK },
10539
10540 /* Directry entry is link to '.' */
10541 { PR_2_LINK_DOT,
10542 N_("@E @L to '.' "),
10543 PROMPT_CLEAR, 0 },
10544
10545 /* Directory entry points to inode now located in a bad block */
10546 { PR_2_BB_INODE,
10547 N_("@E points to @i (%Di) located in a bad @b.\n"),
10548 PROMPT_CLEAR, 0 },
10549
10550 /* Directory entry contains a link to a directory */
10551 { PR_2_LINK_DIR,
10552 N_("@E @L to @d %P (%Di).\n"),
10553 PROMPT_CLEAR, 0 },
10554
10555 /* Directory entry contains a link to the root directry */
10556 { PR_2_LINK_ROOT,
10557 N_("@E @L to the @r.\n"),
10558 PROMPT_CLEAR, 0 },
10559
10560 /* Directory entry has illegal characters in its name */
10561 { PR_2_BAD_NAME,
10562 N_("@E has illegal characters in its name.\n"),
10563 PROMPT_FIX, 0 },
10564
10565 /* Missing '.' in directory inode */
10566 { PR_2_MISSING_DOT,
10567 N_("Missing '.' in @d @i %i.\n"),
10568 PROMPT_FIX, 0 },
10569
10570 /* Missing '..' in directory inode */
10571 { PR_2_MISSING_DOT_DOT,
10572 N_("Missing '..' in @d @i %i.\n"),
10573 PROMPT_FIX, 0 },
10574
10575 /* First entry in directory inode doesn't contain '.' */
10576 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010577 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010578 PROMPT_FIX, 0 },
10579
10580 /* Second entry in directory inode doesn't contain '..' */
10581 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010582 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010583 PROMPT_FIX, 0 },
10584
10585 /* i_faddr should be zero */
10586 { PR_2_FADDR_ZERO,
10587 N_("i_faddr @F %IF, @s zero.\n"),
10588 PROMPT_CLEAR, 0 },
10589
10590 /* i_file_acl should be zero */
10591 { PR_2_FILE_ACL_ZERO,
10592 N_("i_file_acl @F %If, @s zero.\n"),
10593 PROMPT_CLEAR, 0 },
10594
10595 /* i_dir_acl should be zero */
10596 { PR_2_DIR_ACL_ZERO,
10597 N_("i_dir_acl @F %Id, @s zero.\n"),
10598 PROMPT_CLEAR, 0 },
10599
10600 /* i_frag should be zero */
10601 { PR_2_FRAG_ZERO,
10602 N_("i_frag @F %N, @s zero.\n"),
10603 PROMPT_CLEAR, 0 },
10604
10605 /* i_fsize should be zero */
10606 { PR_2_FSIZE_ZERO,
10607 N_("i_fsize @F %N, @s zero.\n"),
10608 PROMPT_CLEAR, 0 },
10609
10610 /* inode has bad mode */
10611 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010612 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010613 PROMPT_CLEAR, 0 },
10614
10615 /* directory corrupted */
10616 { PR_2_DIR_CORRUPTED,
10617 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
10618 PROMPT_SALVAGE, 0 },
10619
10620 /* filename too long */
10621 { PR_2_FILENAME_LONG,
10622 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
10623 PROMPT_TRUNCATE, 0 },
10624
10625 /* Directory inode has a missing block (hole) */
10626 { PR_2_DIRECTORY_HOLE,
10627 N_("@d @i %i has an unallocated @b #%B. "),
10628 PROMPT_ALLOCATE, 0 },
10629
10630 /* '.' is not NULL terminated */
10631 { PR_2_DOT_NULL_TERM,
10632 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
10633 PROMPT_FIX, 0 },
10634
10635 /* '..' is not NULL terminated */
10636 { PR_2_DOT_DOT_NULL_TERM,
10637 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
10638 PROMPT_FIX, 0 },
10639
10640 /* Illegal character device inode */
10641 { PR_2_BAD_CHAR_DEV,
10642 N_("@i %i (%Q) is an @I character @v.\n"),
10643 PROMPT_CLEAR, 0 },
10644
10645 /* Illegal block device inode */
10646 { PR_2_BAD_BLOCK_DEV,
10647 N_("@i %i (%Q) is an @I @b @v.\n"),
10648 PROMPT_CLEAR, 0 },
10649
10650 /* Duplicate '.' entry */
10651 { PR_2_DUP_DOT,
10652 N_("@E is duplicate '.' @e.\n"),
10653 PROMPT_FIX, 0 },
10654
10655 /* Duplicate '..' entry */
10656 { PR_2_DUP_DOT_DOT,
10657 N_("@E is duplicate '..' @e.\n"),
10658 PROMPT_FIX, 0 },
10659
10660 /* Internal error: couldn't find dir_info */
10661 { PR_2_NO_DIRINFO,
10662 N_("Internal error: couldn't find dir_info for %i.\n"),
10663 PROMPT_NONE, PR_FATAL },
10664
10665 /* Final rec_len is wrong */
10666 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +000010667 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010668 PROMPT_FIX, 0 },
10669
10670 /* Error allocating icount structure */
10671 { PR_2_ALLOCATE_ICOUNT,
10672 N_("@A icount structure: %m\n"),
10673 PROMPT_NONE, PR_FATAL },
10674
10675 /* Error iterating over directory blocks */
10676 { PR_2_DBLIST_ITERATE,
10677 N_("Error iterating over @d @bs: %m\n"),
10678 PROMPT_NONE, PR_FATAL },
10679
10680 /* Error reading directory block */
10681 { PR_2_READ_DIRBLOCK,
10682 N_("Error reading @d @b %b (@i %i): %m\n"),
10683 PROMPT_CONTINUE, 0 },
10684
10685 /* Error writing directory block */
10686 { PR_2_WRITE_DIRBLOCK,
10687 N_("Error writing @d @b %b (@i %i): %m\n"),
10688 PROMPT_CONTINUE, 0 },
10689
10690 /* Error allocating new directory block */
10691 { PR_2_ALLOC_DIRBOCK,
10692 N_("@A new @d @b for @i %i (%s): %m\n"),
10693 PROMPT_NONE, 0 },
10694
10695 /* Error deallocating inode */
10696 { PR_2_DEALLOC_INODE,
10697 N_("Error deallocating @i %i: %m\n"),
10698 PROMPT_NONE, PR_FATAL },
10699
10700 /* Directory entry for '.' is big. Split? */
10701 { PR_2_SPLIT_DOT,
10702 N_("@d @e for '.' is big. "),
10703 PROMPT_SPLIT, PR_NO_OK },
10704
10705 /* Illegal FIFO inode */
10706 { PR_2_BAD_FIFO,
10707 N_("@i %i (%Q) is an @I FIFO.\n"),
10708 PROMPT_CLEAR, 0 },
10709
10710 /* Illegal socket inode */
10711 { PR_2_BAD_SOCKET,
10712 N_("@i %i (%Q) is an @I socket.\n"),
10713 PROMPT_CLEAR, 0 },
10714
10715 /* Directory filetype not set */
10716 { PR_2_SET_FILETYPE,
10717 N_("Setting filetype for @E to %N.\n"),
10718 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
10719
10720 /* Directory filetype incorrect */
10721 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +000010722 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010723 PROMPT_FIX, 0 },
10724
10725 /* Directory filetype set on filesystem */
10726 { PR_2_CLEAR_FILETYPE,
10727 N_("@E has filetype set.\n"),
10728 PROMPT_CLEAR, PR_PREEN_OK },
10729
10730 /* Directory filename is null */
10731 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +000010732 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010733 PROMPT_CLEAR, 0 },
10734
10735 /* Invalid symlink */
10736 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +000010737 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010738 PROMPT_CLEAR, 0 },
10739
10740 /* i_file_acl (extended attribute block) is bad */
10741 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +000010742 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010743 PROMPT_CLEAR, 0 },
10744
10745 /* Filesystem contains large files, but has no such flag in sb */
10746 { PR_2_FEATURE_LARGE_FILES,
10747 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
10748 PROMPT_FIX, 0 },
10749
10750 /* Node in HTREE directory not referenced */
10751 { PR_2_HTREE_NOTREF,
10752 N_("@p @h %d: node (%B) not referenced\n"),
10753 PROMPT_NONE, 0 },
10754
10755 /* Node in HTREE directory referenced twice */
10756 { PR_2_HTREE_DUPREF,
10757 N_("@p @h %d: node (%B) referenced twice\n"),
10758 PROMPT_NONE, 0 },
10759
10760 /* Node in HTREE directory has bad min hash */
10761 { PR_2_HTREE_MIN_HASH,
10762 N_("@p @h %d: node (%B) has bad min hash\n"),
10763 PROMPT_NONE, 0 },
10764
10765 /* Node in HTREE directory has bad max hash */
10766 { PR_2_HTREE_MAX_HASH,
10767 N_("@p @h %d: node (%B) has bad max hash\n"),
10768 PROMPT_NONE, 0 },
10769
10770 /* Clear invalid HTREE directory */
10771 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +000010772 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010773
10774 /* Bad block in htree interior node */
10775 { PR_2_HTREE_BADBLK,
10776 N_("@p @h %d (%q): bad @b number %b.\n"),
10777 PROMPT_CLEAR_HTREE, 0 },
10778
10779 /* Error adjusting EA refcount */
10780 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010781 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010782 PROMPT_NONE, PR_FATAL },
10783
10784 /* Invalid HTREE root node */
10785 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +000010786 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010787 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10788
10789 /* Invalid HTREE limit */
10790 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +000010791 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010792 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10793
10794 /* Invalid HTREE count */
10795 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +000010796 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010797 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10798
10799 /* HTREE interior node has out-of-order hashes in table */
10800 { PR_2_HTREE_HASH_ORDER,
10801 N_("@p @h %d: node (%B) has an unordered hash table\n"),
10802 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
10803
Mike Frysinger874af852006-03-08 07:03:27 +000010804 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010805 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +000010806 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010807 PROMPT_NONE, 0 },
10808
10809 /* Duplicate directory entry found */
10810 { PR_2_DUPLICATE_DIRENT,
10811 N_("Duplicate @E found. "),
10812 PROMPT_CLEAR, 0 },
10813
10814 /* Non-unique filename found */
10815 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
10816 N_("@E has a non-unique filename.\nRename to %s"),
10817 PROMPT_NULL, 0 },
10818
10819 /* Duplicate directory entry found */
10820 { PR_2_REPORT_DUP_DIRENT,
10821 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
10822 PROMPT_NONE, 0 },
10823
10824 /* Pass 3 errors */
10825
10826 /* Pass 3: Checking directory connectivity */
10827 { PR_3_PASS_HEADER,
10828 N_("Pass 3: Checking @d connectivity\n"),
10829 PROMPT_NONE, 0 },
10830
10831 /* Root inode not allocated */
10832 { PR_3_NO_ROOT_INODE,
10833 N_("@r not allocated. "),
10834 PROMPT_ALLOCATE, 0 },
10835
10836 /* No room in lost+found */
10837 { PR_3_EXPAND_LF_DIR,
10838 N_("No room in @l @d. "),
10839 PROMPT_EXPAND, 0 },
10840
10841 /* Unconnected directory inode */
10842 { PR_3_UNCONNECTED_DIR,
10843 N_("Unconnected @d @i %i (%p)\n"),
10844 PROMPT_CONNECT, 0 },
10845
10846 /* /lost+found not found */
10847 { PR_3_NO_LF_DIR,
10848 N_("/@l not found. "),
10849 PROMPT_CREATE, PR_PREEN_OK },
10850
10851 /* .. entry is incorrect */
10852 { PR_3_BAD_DOT_DOT,
10853 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
10854 PROMPT_FIX, 0 },
10855
10856 /* Bad or non-existent /lost+found. Cannot reconnect */
10857 { PR_3_NO_LPF,
10858 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
10859 PROMPT_NONE, 0 },
10860
10861 /* Could not expand /lost+found */
10862 { PR_3_CANT_EXPAND_LPF,
10863 N_("Could not expand /@l: %m\n"),
10864 PROMPT_NONE, 0 },
10865
10866 /* Could not reconnect inode */
10867 { PR_3_CANT_RECONNECT,
10868 N_("Could not reconnect %i: %m\n"),
10869 PROMPT_NONE, 0 },
10870
10871 /* Error while trying to find /lost+found */
10872 { PR_3_ERR_FIND_LPF,
10873 N_("Error while trying to find /@l: %m\n"),
10874 PROMPT_NONE, 0 },
10875
10876 /* Error in ext2fs_new_block while creating /lost+found */
10877 { PR_3_ERR_LPF_NEW_BLOCK,
10878 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
10879 PROMPT_NONE, 0 },
10880
10881 /* Error in ext2fs_new_inode while creating /lost+found */
10882 { PR_3_ERR_LPF_NEW_INODE,
10883 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
10884 PROMPT_NONE, 0 },
10885
10886 /* Error in ext2fs_new_dir_block while creating /lost+found */
10887 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
10888 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
10889 PROMPT_NONE, 0 },
10890
10891 /* Error while writing directory block for /lost+found */
10892 { PR_3_ERR_LPF_WRITE_BLOCK,
10893 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
10894 PROMPT_NONE, 0 },
10895
10896 /* Error while adjusting inode count */
10897 { PR_3_ADJUST_INODE,
10898 N_("Error while adjusting @i count on @i %i\n"),
10899 PROMPT_NONE, 0 },
10900
10901 /* Couldn't fix parent directory -- error */
10902 { PR_3_FIX_PARENT_ERR,
10903 N_("Couldn't fix parent of @i %i: %m\n\n"),
10904 PROMPT_NONE, 0 },
10905
10906 /* Couldn't fix parent directory -- couldn't find it */
10907 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +000010908 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 +000010909 PROMPT_NONE, 0 },
10910
10911 /* Error allocating inode bitmap */
10912 { PR_3_ALLOCATE_IBITMAP_ERROR,
10913 N_("@A @i @B (%N): %m\n"),
10914 PROMPT_NONE, PR_FATAL },
10915
10916 /* Error creating root directory */
10917 { PR_3_CREATE_ROOT_ERROR,
10918 N_("Error creating root @d (%s): %m\n"),
10919 PROMPT_NONE, PR_FATAL },
10920
10921 /* Error creating lost and found directory */
10922 { PR_3_CREATE_LPF_ERROR,
10923 N_("Error creating /@l @d (%s): %m\n"),
10924 PROMPT_NONE, PR_FATAL },
10925
10926 /* Root inode is not directory; aborting */
10927 { PR_3_ROOT_NOT_DIR_ABORT,
10928 N_("@r is not a @d; aborting.\n"),
10929 PROMPT_NONE, PR_FATAL },
10930
10931 /* Cannot proceed without a root inode. */
10932 { PR_3_NO_ROOT_INODE_ABORT,
10933 N_("Cannot proceed without a @r.\n"),
10934 PROMPT_NONE, PR_FATAL },
10935
10936 /* Internal error: couldn't find dir_info */
10937 { PR_3_NO_DIRINFO,
10938 N_("Internal error: couldn't find dir_info for %i.\n"),
10939 PROMPT_NONE, PR_FATAL },
10940
10941 /* Lost+found not a directory */
10942 { PR_3_LPF_NOTDIR,
10943 N_("/@l is not a @d (ino=%i)\n"),
10944 PROMPT_UNLINK, 0 },
10945
10946 /* Pass 3A Directory Optimization */
10947
10948 /* Pass 3A: Optimizing directories */
10949 { PR_3A_PASS_HEADER,
10950 N_("Pass 3A: Optimizing directories\n"),
10951 PROMPT_NONE, PR_PREEN_NOMSG },
10952
10953 /* Error iterating over directories */
10954 { PR_3A_OPTIMIZE_ITER,
10955 N_("Failed to create dirs_to_hash iterator: %m"),
10956 PROMPT_NONE, 0 },
10957
10958 /* Error rehash directory */
10959 { PR_3A_OPTIMIZE_DIR_ERR,
10960 N_("Failed to optimize directory %q (%d): %m"),
10961 PROMPT_NONE, 0 },
10962
10963 /* Rehashing dir header */
10964 { PR_3A_OPTIMIZE_DIR_HEADER,
10965 N_("Optimizing directories: "),
10966 PROMPT_NONE, PR_MSG_ONLY },
10967
10968 /* Rehashing directory %d */
10969 { PR_3A_OPTIMIZE_DIR,
10970 " %d",
10971 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
10972
10973 /* Rehashing dir end */
10974 { PR_3A_OPTIMIZE_DIR_END,
10975 "\n",
10976 PROMPT_NONE, PR_PREEN_NOHDR },
10977
10978 /* Pass 4 errors */
10979
10980 /* Pass 4: Checking reference counts */
10981 { PR_4_PASS_HEADER,
10982 N_("Pass 4: Checking reference counts\n"),
10983 PROMPT_NONE, 0 },
10984
10985 /* Unattached zero-length inode */
10986 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010987 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010988 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
10989
10990 /* Unattached inode */
10991 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +000010992 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010993 PROMPT_CONNECT, 0 },
10994
10995 /* Inode ref count wrong */
10996 { PR_4_BAD_REF_COUNT,
10997 N_("@i %i ref count is %Il, @s %N. "),
10998 PROMPT_FIX, PR_PREEN_OK },
10999
11000 { PR_4_INCONSISTENT_COUNT,
11001 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
11002 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
11003 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +000011004 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011005 PROMPT_NONE, 0 },
11006
11007 /* Pass 5 errors */
11008
11009 /* Pass 5: Checking group summary information */
11010 { PR_5_PASS_HEADER,
11011 N_("Pass 5: Checking @g summary information\n"),
11012 PROMPT_NONE, 0 },
11013
11014 /* Padding at end of inode bitmap is not set. */
11015 { PR_5_INODE_BMAP_PADDING,
11016 N_("Padding at end of @i @B is not set. "),
11017 PROMPT_FIX, PR_PREEN_OK },
11018
11019 /* Padding at end of block bitmap is not set. */
11020 { PR_5_BLOCK_BMAP_PADDING,
11021 N_("Padding at end of @b @B is not set. "),
11022 PROMPT_FIX, PR_PREEN_OK },
11023
11024 /* Block bitmap differences header */
11025 { PR_5_BLOCK_BITMAP_HEADER,
11026 N_("@b @B differences: "),
11027 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
11028
11029 /* Block not used, but marked in bitmap */
11030 { PR_5_BLOCK_UNUSED,
11031 " -%b",
11032 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11033
11034 /* Block used, but not marked used in bitmap */
11035 { PR_5_BLOCK_USED,
11036 " +%b",
11037 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11038
11039 /* Block bitmap differences end */
11040 { PR_5_BLOCK_BITMAP_END,
11041 "\n",
11042 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11043
11044 /* Inode bitmap differences header */
11045 { PR_5_INODE_BITMAP_HEADER,
11046 N_("@i @B differences: "),
11047 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
11048
11049 /* Inode not used, but marked in bitmap */
11050 { PR_5_INODE_UNUSED,
11051 " -%i",
11052 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11053
11054 /* Inode used, but not marked used in bitmap */
11055 { PR_5_INODE_USED,
11056 " +%i",
11057 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11058
11059 /* Inode bitmap differences end */
11060 { PR_5_INODE_BITMAP_END,
11061 "\n",
11062 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11063
11064 /* Free inodes count for group wrong */
11065 { PR_5_FREE_INODE_COUNT_GROUP,
11066 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
11067 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11068
11069 /* Directories count for group wrong */
11070 { PR_5_FREE_DIR_COUNT_GROUP,
11071 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
11072 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11073
11074 /* Free inodes count wrong */
11075 { PR_5_FREE_INODE_COUNT,
11076 N_("Free @is count wrong (%i, counted=%j).\n"),
11077 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11078
11079 /* Free blocks count for group wrong */
11080 { PR_5_FREE_BLOCK_COUNT_GROUP,
11081 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
11082 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11083
11084 /* Free blocks count wrong */
11085 { PR_5_FREE_BLOCK_COUNT,
11086 N_("Free @bs count wrong (%b, counted=%c).\n"),
11087 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
11088
11089 /* Programming error: bitmap endpoints don't match */
11090 { PR_5_BMAP_ENDPOINTS,
11091 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
11092 "match calculated @B endpoints (%i, %j)\n"),
11093 PROMPT_NONE, PR_FATAL },
11094
11095 /* Internal error: fudging end of bitmap */
11096 { PR_5_FUDGE_BITMAP_ERROR,
11097 N_("Internal error: fudging end of bitmap (%N)\n"),
11098 PROMPT_NONE, PR_FATAL },
11099
11100 /* Error copying in replacement inode bitmap */
11101 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000011102 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011103 PROMPT_NONE, PR_FATAL },
11104
11105 /* Error copying in replacement block bitmap */
11106 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +000011107 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011108 PROMPT_NONE, PR_FATAL },
11109
11110 /* Block range not used, but marked in bitmap */
11111 { PR_5_BLOCK_RANGE_UNUSED,
11112 " -(%b--%c)",
11113 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11114
11115 /* Block range used, but not marked used in bitmap */
11116 { PR_5_BLOCK_RANGE_USED,
11117 " +(%b--%c)",
11118 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11119
11120 /* Inode range not used, but marked in bitmap */
11121 { PR_5_INODE_RANGE_UNUSED,
11122 " -(%i--%j)",
11123 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11124
11125 /* Inode range used, but not marked used in bitmap */
11126 { PR_5_INODE_RANGE_USED,
11127 " +(%i--%j)",
11128 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
11129
11130 { 0 }
11131};
11132
11133/*
11134 * This is the latch flags register. It allows several problems to be
11135 * "latched" together. This means that the user has to answer but one
11136 * question for the set of problems, and all of the associated
11137 * problems will be either fixed or not fixed.
11138 */
11139static struct latch_descr pr_latch_info[] = {
11140 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
11141 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
11142 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
11143 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
11144 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
11145 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
11146 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
11147 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
11148 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
11149 { -1, 0, 0 },
11150};
11151
11152static const struct e2fsck_problem *find_problem(problem_t code)
11153{
11154 int i;
11155
11156 for (i=0; problem_table[i].e2p_code; i++) {
11157 if (problem_table[i].e2p_code == code)
11158 return &problem_table[i];
11159 }
11160 return 0;
11161}
11162
11163static struct latch_descr *find_latch(int code)
11164{
11165 int i;
11166
11167 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
11168 if (pr_latch_info[i].latch_code == code)
11169 return &pr_latch_info[i];
11170 }
11171 return 0;
11172}
11173
11174int end_problem_latch(e2fsck_t ctx, int mask)
11175{
11176 struct latch_descr *ldesc;
11177 struct problem_context pctx;
11178 int answer = -1;
11179
11180 ldesc = find_latch(mask);
11181 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
11182 clear_problem_context(&pctx);
11183 answer = fix_problem(ctx, ldesc->end_message, &pctx);
11184 }
11185 ldesc->flags &= ~(PRL_VARIABLE);
11186 return answer;
11187}
11188
11189int set_latch_flags(int mask, int setflags, int clearflags)
11190{
11191 struct latch_descr *ldesc;
11192
11193 ldesc = find_latch(mask);
11194 if (!ldesc)
11195 return -1;
11196 ldesc->flags |= setflags;
11197 ldesc->flags &= ~clearflags;
11198 return 0;
11199}
11200
11201void clear_problem_context(struct problem_context *ctx)
11202{
11203 memset(ctx, 0, sizeof(struct problem_context));
11204 ctx->blkcount = -1;
11205 ctx->group = -1;
11206}
11207
11208int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
11209{
11210 ext2_filsys fs = ctx->fs;
11211 const struct e2fsck_problem *ptr;
11212 struct latch_descr *ldesc = 0;
11213 const char *message;
11214 int def_yn, answer, ans;
11215 int print_answer = 0;
11216 int suppress = 0;
11217
11218 ptr = find_problem(code);
11219 if (!ptr) {
11220 printf(_("Unhandled error code (0x%x)!\n"), code);
11221 return 0;
11222 }
11223 def_yn = 1;
11224 if ((ptr->flags & PR_NO_DEFAULT) ||
11225 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
11226 (ctx->options & E2F_OPT_NO))
11227 def_yn= 0;
11228
11229 /*
11230 * Do special latch processing. This is where we ask the
11231 * latch question, if it exists
11232 */
11233 if (ptr->flags & PR_LATCH_MASK) {
11234 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
11235 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
11236 ans = fix_problem(ctx, ldesc->question, pctx);
11237 if (ans == 1)
11238 ldesc->flags |= PRL_YES;
11239 if (ans == 0)
11240 ldesc->flags |= PRL_NO;
11241 ldesc->flags |= PRL_LATCHED;
11242 }
11243 if (ldesc->flags & PRL_SUPPRESS)
11244 suppress++;
11245 }
11246 if ((ptr->flags & PR_PREEN_NOMSG) &&
11247 (ctx->options & E2F_OPT_PREEN))
11248 suppress++;
11249 if ((ptr->flags & PR_NO_NOMSG) &&
11250 (ctx->options & E2F_OPT_NO))
11251 suppress++;
11252 if (!suppress) {
11253 message = ptr->e2p_description;
11254 if ((ctx->options & E2F_OPT_PREEN) &&
11255 !(ptr->flags & PR_PREEN_NOHDR)) {
11256 printf("%s: ", ctx->device_name ?
11257 ctx->device_name : ctx->filesystem_name);
11258 }
11259 if (*message)
11260 print_e2fsck_message(ctx, _(message), pctx, 1);
11261 }
11262 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
11263 preenhalt(ctx);
11264
11265 if (ptr->flags & PR_FATAL)
11266 fatal_error(ctx, 0);
11267
11268 if (ptr->prompt == PROMPT_NONE) {
11269 if (ptr->flags & PR_NOCOLLATE)
11270 answer = -1;
11271 else
11272 answer = def_yn;
11273 } else {
11274 if (ctx->options & E2F_OPT_PREEN) {
11275 answer = def_yn;
11276 if (!(ptr->flags & PR_PREEN_NOMSG))
11277 print_answer = 1;
11278 } else if ((ptr->flags & PR_LATCH_MASK) &&
11279 (ldesc->flags & (PRL_YES | PRL_NO))) {
11280 if (!suppress)
11281 print_answer = 1;
11282 if (ldesc->flags & PRL_YES)
11283 answer = 1;
11284 else
11285 answer = 0;
11286 } else
11287 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
11288 if (!answer && !(ptr->flags & PR_NO_OK))
11289 ext2fs_unmark_valid(fs);
11290
11291 if (print_answer)
11292 printf("%s.\n", answer ?
11293 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
11294
11295 }
11296
11297 if ((ptr->prompt == PROMPT_ABORT) && answer)
11298 fatal_error(ctx, 0);
11299
11300 if (ptr->flags & PR_AFTER_CODE)
11301 answer = fix_problem(ctx, ptr->second_code, pctx);
11302
11303 return answer;
11304}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011305
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011306/*
11307 * linux/fs/recovery.c
11308 *
11309 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011310 */
11311
11312/*
11313 * Maintain information about the progress of the recovery job, so that
11314 * the different passes can carry information between them.
11315 */
11316struct recovery_info
11317{
11318 tid_t start_transaction;
11319 tid_t end_transaction;
11320
11321 int nr_replays;
11322 int nr_revokes;
11323 int nr_revoke_hits;
11324};
11325
11326enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
11327static int do_one_pass(journal_t *journal,
11328 struct recovery_info *info, enum passtype pass);
11329static int scan_revoke_records(journal_t *, struct buffer_head *,
11330 tid_t, struct recovery_info *);
11331
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011332/*
11333 * Read a block from the journal
11334 */
11335
11336static int jread(struct buffer_head **bhp, journal_t *journal,
11337 unsigned int offset)
11338{
11339 int err;
11340 unsigned long blocknr;
11341 struct buffer_head *bh;
11342
11343 *bhp = NULL;
11344
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011345 err = journal_bmap(journal, offset, &blocknr);
11346
11347 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +000011348 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011349 return err;
11350 }
11351
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011352 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011353 if (!bh)
11354 return -ENOMEM;
11355
11356 if (!buffer_uptodate(bh)) {
11357 /* If this is a brand new buffer, start readahead.
11358 Otherwise, we assume we are already reading it. */
11359 if (!buffer_req(bh))
11360 do_readahead(journal, offset);
11361 wait_on_buffer(bh);
11362 }
11363
11364 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +000011365 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011366 brelse(bh);
11367 return -EIO;
11368 }
11369
11370 *bhp = bh;
11371 return 0;
11372}
11373
11374
11375/*
11376 * Count the number of in-use tags in a journal descriptor block.
11377 */
11378
11379static int count_tags(struct buffer_head *bh, int size)
11380{
11381 char * tagp;
11382 journal_block_tag_t * tag;
11383 int nr = 0;
11384
11385 tagp = &bh->b_data[sizeof(journal_header_t)];
11386
11387 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
11388 tag = (journal_block_tag_t *) tagp;
11389
11390 nr++;
11391 tagp += sizeof(journal_block_tag_t);
11392 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
11393 tagp += 16;
11394
11395 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
11396 break;
11397 }
11398
11399 return nr;
11400}
11401
11402
11403/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000011404#define wrap(journal, var) \
11405do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011406 if (var >= (journal)->j_last) \
11407 var -= ((journal)->j_last - (journal)->j_first); \
11408} while (0)
11409
11410/**
11411 * int journal_recover(journal_t *journal) - recovers a on-disk journal
11412 * @journal: the journal to recover
11413 *
11414 * The primary function for recovering the log contents when mounting a
11415 * journaled device.
11416 *
11417 * Recovery is done in three passes. In the first pass, we look for the
11418 * end of the log. In the second, we assemble the list of revoke
11419 * blocks. In the third and final pass, we replay any un-revoked blocks
11420 * in the log.
11421 */
11422int journal_recover(journal_t *journal)
11423{
11424 int err;
11425 journal_superblock_t * sb;
11426
11427 struct recovery_info info;
11428
11429 memset(&info, 0, sizeof(info));
11430 sb = journal->j_superblock;
11431
11432 /*
11433 * The journal superblock's s_start field (the current log head)
11434 * is always zero if, and only if, the journal was cleanly
11435 * unmounted.
11436 */
11437
11438 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011439 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
11440 return 0;
11441 }
11442
11443 err = do_one_pass(journal, &info, PASS_SCAN);
11444 if (!err)
11445 err = do_one_pass(journal, &info, PASS_REVOKE);
11446 if (!err)
11447 err = do_one_pass(journal, &info, PASS_REPLAY);
11448
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011449 /* Restart the log at the next transaction ID, thus invalidating
11450 * any existing commit records in the log. */
11451 journal->j_transaction_sequence = ++info.end_transaction;
11452
11453 journal_clear_revoke(journal);
11454 sync_blockdev(journal->j_fs_dev);
11455 return err;
11456}
11457
11458static int do_one_pass(journal_t *journal,
11459 struct recovery_info *info, enum passtype pass)
11460{
11461 unsigned int first_commit_ID, next_commit_ID;
11462 unsigned long next_log_block;
11463 int err, success = 0;
11464 journal_superblock_t * sb;
11465 journal_header_t * tmp;
11466 struct buffer_head * bh;
11467 unsigned int sequence;
11468 int blocktype;
11469
11470 /* Precompute the maximum metadata descriptors in a descriptor block */
11471 int MAX_BLOCKS_PER_DESC;
11472 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
11473 / sizeof(journal_block_tag_t));
11474
11475 /*
11476 * First thing is to establish what we expect to find in the log
11477 * (in terms of transaction IDs), and where (in terms of log
11478 * block offsets): query the superblock.
11479 */
11480
11481 sb = journal->j_superblock;
11482 next_commit_ID = ntohl(sb->s_sequence);
11483 next_log_block = ntohl(sb->s_start);
11484
11485 first_commit_ID = next_commit_ID;
11486 if (pass == PASS_SCAN)
11487 info->start_transaction = first_commit_ID;
11488
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011489 /*
11490 * Now we walk through the log, transaction by transaction,
11491 * making sure that each transaction has a commit block in the
11492 * expected place. Each complete transaction gets replayed back
11493 * into the main filesystem.
11494 */
11495
11496 while (1) {
11497 int flags;
11498 char * tagp;
11499 journal_block_tag_t * tag;
11500 struct buffer_head * obh;
11501 struct buffer_head * nbh;
11502
11503 /* If we already know where to stop the log traversal,
11504 * check right now that we haven't gone past the end of
11505 * the log. */
11506
11507 if (pass != PASS_SCAN)
11508 if (tid_geq(next_commit_ID, info->end_transaction))
11509 break;
11510
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011511 /* Skip over each chunk of the transaction looking
11512 * either the next descriptor block or the final commit
11513 * record. */
11514
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011515 err = jread(&bh, journal, next_log_block);
11516 if (err)
11517 goto failed;
11518
11519 next_log_block++;
11520 wrap(journal, next_log_block);
11521
11522 /* What kind of buffer is it?
11523 *
11524 * If it is a descriptor block, check that it has the
11525 * expected sequence number. Otherwise, we're all done
11526 * here. */
11527
11528 tmp = (journal_header_t *)bh->b_data;
11529
11530 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
11531 brelse(bh);
11532 break;
11533 }
11534
11535 blocktype = ntohl(tmp->h_blocktype);
11536 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011537
11538 if (sequence != next_commit_ID) {
11539 brelse(bh);
11540 break;
11541 }
11542
11543 /* OK, we have a valid descriptor block which matches
11544 * all of the sequence number checks. What are we going
11545 * to do with it? That depends on the pass... */
11546
11547 switch(blocktype) {
11548 case JFS_DESCRIPTOR_BLOCK:
11549 /* If it is a valid descriptor block, replay it
11550 * in pass REPLAY; otherwise, just skip over the
11551 * blocks it describes. */
11552 if (pass != PASS_REPLAY) {
11553 next_log_block +=
11554 count_tags(bh, journal->j_blocksize);
11555 wrap(journal, next_log_block);
11556 brelse(bh);
11557 continue;
11558 }
11559
11560 /* A descriptor block: we can now write all of
11561 * the data blocks. Yay, useful work is finally
11562 * getting done here! */
11563
11564 tagp = &bh->b_data[sizeof(journal_header_t)];
11565 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
11566 <= journal->j_blocksize) {
11567 unsigned long io_block;
11568
11569 tag = (journal_block_tag_t *) tagp;
11570 flags = ntohl(tag->t_flags);
11571
11572 io_block = next_log_block++;
11573 wrap(journal, next_log_block);
11574 err = jread(&obh, journal, io_block);
11575 if (err) {
11576 /* Recover what we can, but
11577 * report failure at the end. */
11578 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000011579 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011580 "block %ld in log\n",
11581 err, io_block);
11582 } else {
11583 unsigned long blocknr;
11584
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011585 blocknr = ntohl(tag->t_blocknr);
11586
11587 /* If the block has been
11588 * revoked, then we're all done
11589 * here. */
11590 if (journal_test_revoke
11591 (journal, blocknr,
11592 next_commit_ID)) {
11593 brelse(obh);
11594 ++info->nr_revoke_hits;
11595 goto skip_write;
11596 }
11597
11598 /* Find a buffer for the new
11599 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011600 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011601 blocknr,
11602 journal->j_blocksize);
11603 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000011604 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011605 "during recovery.\n");
11606 err = -ENOMEM;
11607 brelse(bh);
11608 brelse(obh);
11609 goto failed;
11610 }
11611
11612 lock_buffer(nbh);
11613 memcpy(nbh->b_data, obh->b_data,
11614 journal->j_blocksize);
11615 if (flags & JFS_FLAG_ESCAPE) {
11616 *((unsigned int *)bh->b_data) =
11617 htonl(JFS_MAGIC_NUMBER);
11618 }
11619
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011620 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011621 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011622 ++info->nr_replays;
11623 /* ll_rw_block(WRITE, 1, &nbh); */
11624 unlock_buffer(nbh);
11625 brelse(obh);
11626 brelse(nbh);
11627 }
11628
11629 skip_write:
11630 tagp += sizeof(journal_block_tag_t);
11631 if (!(flags & JFS_FLAG_SAME_UUID))
11632 tagp += 16;
11633
11634 if (flags & JFS_FLAG_LAST_TAG)
11635 break;
11636 }
11637
11638 brelse(bh);
11639 continue;
11640
11641 case JFS_COMMIT_BLOCK:
11642 /* Found an expected commit block: not much to
11643 * do other than move on to the next sequence
11644 * number. */
11645 brelse(bh);
11646 next_commit_ID++;
11647 continue;
11648
11649 case JFS_REVOKE_BLOCK:
11650 /* If we aren't in the REVOKE pass, then we can
11651 * just skip over this block. */
11652 if (pass != PASS_REVOKE) {
11653 brelse(bh);
11654 continue;
11655 }
11656
11657 err = scan_revoke_records(journal, bh,
11658 next_commit_ID, info);
11659 brelse(bh);
11660 if (err)
11661 goto failed;
11662 continue;
11663
11664 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011665 goto done;
11666 }
11667 }
11668
11669 done:
11670 /*
11671 * We broke out of the log scan loop: either we came to the
11672 * known end of the log or we found an unexpected block in the
11673 * log. If the latter happened, then we know that the "current"
11674 * transaction marks the end of the valid log.
11675 */
11676
11677 if (pass == PASS_SCAN)
11678 info->end_transaction = next_commit_ID;
11679 else {
11680 /* It's really bad news if different passes end up at
11681 * different places (but possible due to IO errors). */
11682 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000011683 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011684 "transaction %u, expected %u\n",
11685 pass, next_commit_ID, info->end_transaction);
11686 if (!success)
11687 success = -EIO;
11688 }
11689 }
11690
11691 return success;
11692
11693 failed:
11694 return err;
11695}
11696
11697
11698/* Scan a revoke record, marking all blocks mentioned as revoked. */
11699
11700static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
11701 tid_t sequence, struct recovery_info *info)
11702{
11703 journal_revoke_header_t *header;
11704 int offset, max;
11705
11706 header = (journal_revoke_header_t *) bh->b_data;
11707 offset = sizeof(journal_revoke_header_t);
11708 max = ntohl(header->r_count);
11709
11710 while (offset < max) {
11711 unsigned long blocknr;
11712 int err;
11713
11714 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
11715 offset += 4;
11716 err = journal_set_revoke(journal, blocknr, sequence);
11717 if (err)
11718 return err;
11719 ++info->nr_revokes;
11720 }
11721 return 0;
11722}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011723
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011724
11725/*
11726 * rehash.c --- rebuild hash tree directories
11727 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011728 * This algorithm is designed for simplicity of implementation and to
11729 * pack the directory as much as possible. It however requires twice
11730 * as much memory as the size of the directory. The maximum size
11731 * directory supported using a 4k blocksize is roughly a gigabyte, and
11732 * so there may very well be problems with machines that don't have
11733 * virtual memory, and obscenely large directories.
11734 *
11735 * An alternate algorithm which is much more disk intensive could be
11736 * written, and probably will need to be written in the future. The
11737 * design goals of such an algorithm are: (a) use (roughly) constant
11738 * amounts of memory, no matter how large the directory, (b) the
11739 * directory must be safe at all times, even if e2fsck is interrupted
11740 * in the middle, (c) we must use minimal amounts of extra disk
11741 * blocks. This pretty much requires an incremental approach, where
11742 * we are reading from one part of the directory, and inserting into
11743 * the front half. So the algorithm will have to keep track of a
11744 * moving block boundary between the new tree and the old tree, and
11745 * files will need to be moved from the old directory and inserted
11746 * into the new tree. If the new directory requires space which isn't
11747 * yet available, blocks from the beginning part of the old directory
11748 * may need to be moved to the end of the directory to make room for
11749 * the new tree:
11750 *
11751 * --------------------------------------------------------
11752 * | new tree | | old tree |
11753 * --------------------------------------------------------
11754 * ^ ptr ^ptr
11755 * tail new head old
11756 *
11757 * This is going to be a pain in the tuckus to implement, and will
11758 * require a lot more disk accesses. So I'm going to skip it for now;
11759 * it's only really going to be an issue for really, really big
11760 * filesystems (when we reach the level of tens of millions of files
11761 * in a single directory). It will probably be easier to simply
11762 * require that e2fsck use VM first.
11763 */
11764
11765struct fill_dir_struct {
11766 char *buf;
11767 struct ext2_inode *inode;
11768 int err;
11769 e2fsck_t ctx;
11770 struct hash_entry *harray;
11771 int max_array, num_array;
11772 int dir_size;
11773 int compress;
11774 ino_t parent;
11775};
11776
11777struct hash_entry {
11778 ext2_dirhash_t hash;
11779 ext2_dirhash_t minor_hash;
11780 struct ext2_dir_entry *dir;
11781};
11782
11783struct out_dir {
11784 int num;
11785 int max;
11786 char *buf;
11787 ext2_dirhash_t *hashes;
11788};
11789
11790static int fill_dir_block(ext2_filsys fs,
11791 blk_t *block_nr,
11792 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011793 blk_t ref_block FSCK_ATTR((unused)),
11794 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011795 void *priv_data)
11796{
11797 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
11798 struct hash_entry *new_array, *ent;
11799 struct ext2_dir_entry *dirent;
11800 char *dir;
11801 unsigned int offset, dir_offset;
11802
11803 if (blockcnt < 0)
11804 return 0;
11805
11806 offset = blockcnt * fs->blocksize;
11807 if (offset + fs->blocksize > fd->inode->i_size) {
11808 fd->err = EXT2_ET_DIR_CORRUPTED;
11809 return BLOCK_ABORT;
11810 }
11811 dir = (fd->buf+offset);
11812 if (HOLE_BLKADDR(*block_nr)) {
11813 memset(dir, 0, fs->blocksize);
11814 dirent = (struct ext2_dir_entry *) dir;
11815 dirent->rec_len = fs->blocksize;
11816 } else {
11817 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
11818 if (fd->err)
11819 return BLOCK_ABORT;
11820 }
11821 /* While the directory block is "hot", index it. */
11822 dir_offset = 0;
11823 while (dir_offset < fs->blocksize) {
11824 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
11825 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
11826 (dirent->rec_len < 8) ||
11827 ((dirent->rec_len % 4) != 0) ||
11828 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
11829 fd->err = EXT2_ET_DIR_CORRUPTED;
11830 return BLOCK_ABORT;
11831 }
11832 dir_offset += dirent->rec_len;
11833 if (dirent->inode == 0)
11834 continue;
11835 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
11836 (dirent->name[0] == '.'))
11837 continue;
11838 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
11839 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
11840 fd->parent = dirent->inode;
11841 continue;
11842 }
11843 if (fd->num_array >= fd->max_array) {
11844 new_array = realloc(fd->harray,
11845 sizeof(struct hash_entry) * (fd->max_array+500));
11846 if (!new_array) {
11847 fd->err = ENOMEM;
11848 return BLOCK_ABORT;
11849 }
11850 fd->harray = new_array;
11851 fd->max_array += 500;
11852 }
11853 ent = fd->harray + fd->num_array++;
11854 ent->dir = dirent;
11855 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
11856 if (fd->compress)
11857 ent->hash = ent->minor_hash = 0;
11858 else {
11859 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
11860 dirent->name,
11861 dirent->name_len & 0xFF,
11862 fs->super->s_hash_seed,
11863 &ent->hash, &ent->minor_hash);
11864 if (fd->err)
11865 return BLOCK_ABORT;
11866 }
11867 }
11868
11869 return 0;
11870}
11871
11872/* Used for sorting the hash entry */
11873static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b)
11874{
11875 const struct hash_entry *he_a = (const struct hash_entry *) a;
11876 const struct hash_entry *he_b = (const struct hash_entry *) b;
11877 int ret;
11878 int min_len;
11879
11880 min_len = he_a->dir->name_len;
11881 if (min_len > he_b->dir->name_len)
11882 min_len = he_b->dir->name_len;
11883
11884 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
11885 if (ret == 0) {
11886 if (he_a->dir->name_len > he_b->dir->name_len)
11887 ret = 1;
11888 else if (he_a->dir->name_len < he_b->dir->name_len)
11889 ret = -1;
11890 else
11891 ret = he_b->dir->inode - he_a->dir->inode;
11892 }
11893 return ret;
11894}
11895
11896/* Used for sorting the hash entry */
11897static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b)
11898{
11899 const struct hash_entry *he_a = (const struct hash_entry *) a;
11900 const struct hash_entry *he_b = (const struct hash_entry *) b;
11901 int ret;
11902
11903 if (he_a->hash > he_b->hash)
11904 ret = 1;
11905 else if (he_a->hash < he_b->hash)
11906 ret = -1;
11907 else {
11908 if (he_a->minor_hash > he_b->minor_hash)
11909 ret = 1;
11910 else if (he_a->minor_hash < he_b->minor_hash)
11911 ret = -1;
11912 else
11913 ret = name_cmp(a, b);
11914 }
11915 return ret;
11916}
11917
11918static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
11919 int blocks)
11920{
11921 void *new_mem;
11922
11923 if (outdir->max) {
11924 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
11925 if (!new_mem)
11926 return ENOMEM;
11927 outdir->buf = new_mem;
11928 new_mem = realloc(outdir->hashes,
11929 blocks * sizeof(ext2_dirhash_t));
11930 if (!new_mem)
11931 return ENOMEM;
11932 outdir->hashes = new_mem;
11933 } else {
11934 outdir->buf = malloc(blocks * fs->blocksize);
11935 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
11936 outdir->num = 0;
11937 }
11938 outdir->max = blocks;
11939 return 0;
11940}
11941
11942static void free_out_dir(struct out_dir *outdir)
11943{
Rob Landleye7c43b62006-03-01 16:39:45 +000011944 free(outdir->buf);
11945 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011946 outdir->max = 0;
11947 outdir->num =0;
11948}
11949
11950static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
11951 char ** ret)
11952{
11953 errcode_t retval;
11954
11955 if (outdir->num >= outdir->max) {
11956 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
11957 if (retval)
11958 return retval;
11959 }
11960 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
11961 memset(*ret, 0, fs->blocksize);
11962 return 0;
11963}
11964
11965/*
11966 * This function is used to make a unique filename. We do this by
11967 * appending ~0, and then incrementing the number. However, we cannot
11968 * expand the length of the filename beyond the padding available in
11969 * the directory entry.
11970 */
11971static void mutate_name(char *str, __u16 *len)
11972{
11973 int i;
11974 __u16 l = *len & 0xFF, h = *len & 0xff00;
11975
11976 /*
11977 * First check to see if it looks the name has been mutated
11978 * already
11979 */
11980 for (i = l-1; i > 0; i--) {
11981 if (!isdigit(str[i]))
11982 break;
11983 }
11984 if ((i == l-1) || (str[i] != '~')) {
11985 if (((l-1) & 3) < 2)
11986 l += 2;
11987 else
11988 l = (l+3) & ~3;
11989 str[l-2] = '~';
11990 str[l-1] = '0';
11991 *len = l | h;
11992 return;
11993 }
11994 for (i = l-1; i >= 0; i--) {
11995 if (isdigit(str[i])) {
11996 if (str[i] == '9')
11997 str[i] = '0';
11998 else {
11999 str[i]++;
12000 return;
12001 }
12002 continue;
12003 }
12004 if (i == 1) {
12005 if (str[0] == 'z')
12006 str[0] = 'A';
12007 else if (str[0] == 'Z') {
12008 str[0] = '~';
12009 str[1] = '0';
12010 } else
12011 str[0]++;
12012 } else if (i > 0) {
12013 str[i] = '1';
12014 str[i-1] = '~';
12015 } else {
12016 if (str[0] == '~')
12017 str[0] = 'a';
12018 else
12019 str[0]++;
12020 }
12021 break;
12022 }
12023}
12024
12025static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
12026 ext2_ino_t ino,
12027 struct fill_dir_struct *fd)
12028{
12029 struct problem_context pctx;
12030 struct hash_entry *ent, *prev;
12031 int i, j;
12032 int fixed = 0;
12033 char new_name[256];
12034 __u16 new_len;
12035
12036 clear_problem_context(&pctx);
12037 pctx.ino = ino;
12038
12039 for (i=1; i < fd->num_array; i++) {
12040 ent = fd->harray + i;
12041 prev = ent - 1;
12042 if (!ent->dir->inode ||
12043 ((ent->dir->name_len & 0xFF) !=
12044 (prev->dir->name_len & 0xFF)) ||
12045 (strncmp(ent->dir->name, prev->dir->name,
12046 ent->dir->name_len & 0xFF)))
12047 continue;
12048 pctx.dirent = ent->dir;
12049 if ((ent->dir->inode == prev->dir->inode) &&
12050 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
12051 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
12052 ent->dir->inode = 0;
12053 fixed++;
12054 continue;
12055 }
12056 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
12057 new_len = ent->dir->name_len;
12058 mutate_name(new_name, &new_len);
12059 for (j=0; j < fd->num_array; j++) {
12060 if ((i==j) ||
12061 ((ent->dir->name_len & 0xFF) !=
12062 (fd->harray[j].dir->name_len & 0xFF)) ||
12063 (strncmp(new_name, fd->harray[j].dir->name,
12064 new_len & 0xFF)))
12065 continue;
12066 mutate_name(new_name, &new_len);
12067
12068 j = -1;
12069 }
12070 new_name[new_len & 0xFF] = 0;
12071 pctx.str = new_name;
12072 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
12073 memcpy(ent->dir->name, new_name, new_len & 0xFF);
12074 ent->dir->name_len = new_len;
12075 ext2fs_dirhash(fs->super->s_def_hash_version,
12076 ent->dir->name,
12077 ent->dir->name_len & 0xFF,
12078 fs->super->s_hash_seed,
12079 &ent->hash, &ent->minor_hash);
12080 fixed++;
12081 }
12082 }
12083 return fixed;
12084}
12085
12086
12087static errcode_t copy_dir_entries(ext2_filsys fs,
12088 struct fill_dir_struct *fd,
12089 struct out_dir *outdir)
12090{
12091 errcode_t retval;
12092 char *block_start;
12093 struct hash_entry *ent;
12094 struct ext2_dir_entry *dirent;
12095 int i, rec_len, left;
12096 ext2_dirhash_t prev_hash;
12097 int offset;
12098
12099 outdir->max = 0;
12100 retval = alloc_size_dir(fs, outdir,
12101 (fd->dir_size / fs->blocksize) + 2);
12102 if (retval)
12103 return retval;
12104 outdir->num = fd->compress ? 0 : 1;
12105 offset = 0;
12106 outdir->hashes[0] = 0;
12107 prev_hash = 1;
12108 if ((retval = get_next_block(fs, outdir, &block_start)))
12109 return retval;
12110 dirent = (struct ext2_dir_entry *) block_start;
12111 left = fs->blocksize;
12112 for (i=0; i < fd->num_array; i++) {
12113 ent = fd->harray + i;
12114 if (ent->dir->inode == 0)
12115 continue;
12116 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
12117 if (rec_len > left) {
12118 if (left)
12119 dirent->rec_len += left;
12120 if ((retval = get_next_block(fs, outdir,
12121 &block_start)))
12122 return retval;
12123 offset = 0;
12124 }
12125 left = fs->blocksize - offset;
12126 dirent = (struct ext2_dir_entry *) (block_start + offset);
12127 if (offset == 0) {
12128 if (ent->hash == prev_hash)
12129 outdir->hashes[outdir->num-1] = ent->hash | 1;
12130 else
12131 outdir->hashes[outdir->num-1] = ent->hash;
12132 }
12133 dirent->inode = ent->dir->inode;
12134 dirent->name_len = ent->dir->name_len;
12135 dirent->rec_len = rec_len;
12136 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
12137 offset += rec_len;
12138 left -= rec_len;
12139 if (left < 12) {
12140 dirent->rec_len += left;
12141 offset += left;
12142 left = 0;
12143 }
12144 prev_hash = ent->hash;
12145 }
12146 if (left)
12147 dirent->rec_len += left;
12148
12149 return 0;
12150}
12151
12152
12153static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
12154 ext2_ino_t ino, ext2_ino_t parent)
12155{
12156 struct ext2_dir_entry *dir;
12157 struct ext2_dx_root_info *root;
12158 struct ext2_dx_countlimit *limits;
12159 int filetype = 0;
12160
12161 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
12162 filetype = EXT2_FT_DIR << 8;
12163
12164 memset(buf, 0, fs->blocksize);
12165 dir = (struct ext2_dir_entry *) buf;
12166 dir->inode = ino;
12167 dir->name[0] = '.';
12168 dir->name_len = 1 | filetype;
12169 dir->rec_len = 12;
12170 dir = (struct ext2_dir_entry *) (buf + 12);
12171 dir->inode = parent;
12172 dir->name[0] = '.';
12173 dir->name[1] = '.';
12174 dir->name_len = 2 | filetype;
12175 dir->rec_len = fs->blocksize - 12;
12176
12177 root = (struct ext2_dx_root_info *) (buf+24);
12178 root->reserved_zero = 0;
12179 root->hash_version = fs->super->s_def_hash_version;
12180 root->info_length = 8;
12181 root->indirect_levels = 0;
12182 root->unused_flags = 0;
12183
12184 limits = (struct ext2_dx_countlimit *) (buf+32);
12185 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
12186 limits->count = 0;
12187
12188 return root;
12189}
12190
12191
12192static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
12193{
12194 struct ext2_dir_entry *dir;
12195 struct ext2_dx_countlimit *limits;
12196
12197 memset(buf, 0, fs->blocksize);
12198 dir = (struct ext2_dir_entry *) buf;
12199 dir->inode = 0;
12200 dir->rec_len = fs->blocksize;
12201
12202 limits = (struct ext2_dx_countlimit *) (buf+8);
12203 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
12204 limits->count = 0;
12205
12206 return (struct ext2_dx_entry *) limits;
12207}
12208
12209/*
12210 * This function takes the leaf nodes which have been written in
12211 * outdir, and populates the root node and any necessary interior nodes.
12212 */
12213static errcode_t calculate_tree(ext2_filsys fs,
12214 struct out_dir *outdir,
12215 ext2_ino_t ino,
12216 ext2_ino_t parent)
12217{
12218 struct ext2_dx_root_info *root_info;
12219 struct ext2_dx_entry *root, *dx_ent = 0;
12220 struct ext2_dx_countlimit *root_limit, *limit;
12221 errcode_t retval;
12222 char * block_start;
12223 int i, c1, c2, nblks;
12224 int limit_offset, root_offset;
12225
12226 root_info = set_root_node(fs, outdir->buf, ino, parent);
12227 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
12228 root_info->info_length;
12229 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
12230 c1 = root_limit->limit;
12231 nblks = outdir->num;
12232
12233 /* Write out the pointer blocks */
12234 if (nblks-1 <= c1) {
12235 /* Just write out the root block, and we're done */
12236 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
12237 for (i=1; i < nblks; i++) {
12238 root->block = ext2fs_cpu_to_le32(i);
12239 if (i != 1)
12240 root->hash =
12241 ext2fs_cpu_to_le32(outdir->hashes[i]);
12242 root++;
12243 c1--;
12244 }
12245 } else {
12246 c2 = 0;
12247 limit = 0;
12248 root_info->indirect_levels = 1;
12249 for (i=1; i < nblks; i++) {
12250 if (c1 == 0)
12251 return ENOSPC;
12252 if (c2 == 0) {
12253 if (limit)
12254 limit->limit = limit->count =
12255 ext2fs_cpu_to_le16(limit->limit);
12256 root = (struct ext2_dx_entry *)
12257 (outdir->buf + root_offset);
12258 root->block = ext2fs_cpu_to_le32(outdir->num);
12259 if (i != 1)
12260 root->hash =
12261 ext2fs_cpu_to_le32(outdir->hashes[i]);
12262 if ((retval = get_next_block(fs, outdir,
12263 &block_start)))
12264 return retval;
12265 dx_ent = set_int_node(fs, block_start);
12266 limit = (struct ext2_dx_countlimit *) dx_ent;
12267 c2 = limit->limit;
12268 root_offset += sizeof(struct ext2_dx_entry);
12269 c1--;
12270 }
12271 dx_ent->block = ext2fs_cpu_to_le32(i);
12272 if (c2 != limit->limit)
12273 dx_ent->hash =
12274 ext2fs_cpu_to_le32(outdir->hashes[i]);
12275 dx_ent++;
12276 c2--;
12277 }
12278 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
12279 limit->limit = ext2fs_cpu_to_le16(limit->limit);
12280 }
12281 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
12282 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
12283 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
12284
12285 return 0;
12286}
12287
12288struct write_dir_struct {
12289 struct out_dir *outdir;
12290 errcode_t err;
12291 e2fsck_t ctx;
12292 int cleared;
12293};
12294
12295/*
12296 * Helper function which writes out a directory block.
12297 */
12298static int write_dir_block(ext2_filsys fs,
12299 blk_t *block_nr,
12300 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012301 blk_t ref_block FSCK_ATTR((unused)),
12302 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012303 void *priv_data)
12304{
12305 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
12306 blk_t blk;
12307 char *dir;
12308
12309 if (*block_nr == 0)
12310 return 0;
12311 if (blockcnt >= wd->outdir->num) {
12312 e2fsck_read_bitmaps(wd->ctx);
12313 blk = *block_nr;
12314 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
12315 ext2fs_block_alloc_stats(fs, blk, -1);
12316 *block_nr = 0;
12317 wd->cleared++;
12318 return BLOCK_CHANGED;
12319 }
12320 if (blockcnt < 0)
12321 return 0;
12322
12323 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
12324 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
12325 if (wd->err)
12326 return BLOCK_ABORT;
12327 return 0;
12328}
12329
12330static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
12331 struct out_dir *outdir,
12332 ext2_ino_t ino, int compress)
12333{
12334 struct write_dir_struct wd;
12335 errcode_t retval;
12336 struct ext2_inode inode;
12337
12338 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
12339 if (retval)
12340 return retval;
12341
12342 wd.outdir = outdir;
12343 wd.err = 0;
12344 wd.ctx = ctx;
12345 wd.cleared = 0;
12346
12347 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
12348 write_dir_block, &wd);
12349 if (retval)
12350 return retval;
12351 if (wd.err)
12352 return wd.err;
12353
12354 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
12355 if (compress)
12356 inode.i_flags &= ~EXT2_INDEX_FL;
12357 else
12358 inode.i_flags |= EXT2_INDEX_FL;
12359 inode.i_size = outdir->num * fs->blocksize;
12360 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
12361 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
12362
12363 return 0;
12364}
12365
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012366static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012367{
12368 ext2_filsys fs = ctx->fs;
12369 errcode_t retval;
12370 struct ext2_inode inode;
12371 char *dir_buf = 0;
12372 struct fill_dir_struct fd;
12373 struct out_dir outdir;
12374
12375 outdir.max = outdir.num = 0;
12376 outdir.buf = 0;
12377 outdir.hashes = 0;
12378 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
12379
12380 retval = ENOMEM;
12381 fd.harray = 0;
12382 dir_buf = malloc(inode.i_size);
12383 if (!dir_buf)
12384 goto errout;
12385
12386 fd.max_array = inode.i_size / 32;
12387 fd.num_array = 0;
12388 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
12389 if (!fd.harray)
12390 goto errout;
12391
12392 fd.ctx = ctx;
12393 fd.buf = dir_buf;
12394 fd.inode = &inode;
12395 fd.err = 0;
12396 fd.dir_size = 0;
12397 fd.compress = 0;
12398 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
12399 (inode.i_size / fs->blocksize) < 2)
12400 fd.compress = 1;
12401 fd.parent = 0;
12402
12403 /* Read in the entire directory into memory */
12404 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
12405 fill_dir_block, &fd);
12406 if (fd.err) {
12407 retval = fd.err;
12408 goto errout;
12409 }
12410
12411#if 0
12412 printf("%d entries (%d bytes) found in inode %d\n",
12413 fd.num_array, fd.dir_size, ino);
12414#endif
12415
12416 /* Sort the list */
12417resort:
12418 if (fd.compress)
12419 qsort(fd.harray+2, fd.num_array-2,
12420 sizeof(struct hash_entry), name_cmp);
12421 else
12422 qsort(fd.harray, fd.num_array,
12423 sizeof(struct hash_entry), hash_cmp);
12424
12425 /*
12426 * Look for duplicates
12427 */
12428 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
12429 goto resort;
12430
12431 if (ctx->options & E2F_OPT_NO) {
12432 retval = 0;
12433 goto errout;
12434 }
12435
12436 /*
12437 * Copy the directory entries. In a htree directory these
12438 * will become the leaf nodes.
12439 */
12440 retval = copy_dir_entries(fs, &fd, &outdir);
12441 if (retval)
12442 goto errout;
12443
12444 free(dir_buf); dir_buf = 0;
12445
12446 if (!fd.compress) {
12447 /* Calculate the interior nodes */
12448 retval = calculate_tree(fs, &outdir, ino, fd.parent);
12449 if (retval)
12450 goto errout;
12451 }
12452
12453 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012454
12455errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000012456 free(dir_buf);
12457 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012458
12459 free_out_dir(&outdir);
12460 return retval;
12461}
12462
12463void e2fsck_rehash_directories(e2fsck_t ctx)
12464{
12465 struct problem_context pctx;
12466#ifdef RESOURCE_TRACK
12467 struct resource_track rtrack;
12468#endif
12469 struct dir_info *dir;
12470 ext2_u32_iterate iter;
12471 ext2_ino_t ino;
12472 errcode_t retval;
12473 int i, cur, max, all_dirs, dir_index, first = 1;
12474
12475#ifdef RESOURCE_TRACK
12476 init_resource_track(&rtrack);
12477#endif
12478
12479 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
12480
12481 if (!ctx->dirs_to_hash && !all_dirs)
12482 return;
12483
12484 e2fsck_get_lost_and_found(ctx, 0);
12485
12486 clear_problem_context(&pctx);
12487
12488 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
12489 cur = 0;
12490 if (all_dirs) {
12491 i = 0;
12492 max = e2fsck_get_num_dirinfo(ctx);
12493 } else {
12494 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
12495 &iter);
12496 if (retval) {
12497 pctx.errcode = retval;
12498 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
12499 return;
12500 }
12501 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
12502 }
12503 while (1) {
12504 if (all_dirs) {
12505 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
12506 break;
12507 ino = dir->ino;
12508 } else {
12509 if (!ext2fs_u32_list_iterate(iter, &ino))
12510 break;
12511 }
12512 if (ino == ctx->lost_and_found)
12513 continue;
12514 pctx.dir = ino;
12515 if (first) {
12516 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
12517 first = 0;
12518 }
12519#if 0
12520 fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx);
12521#endif
12522 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
12523 if (pctx.errcode) {
12524 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
12525 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
12526 }
12527 if (ctx->progress && !ctx->progress_fd)
12528 e2fsck_simple_progress(ctx, "Rebuilding directory",
12529 100.0 * (float) (++cur) / (float) max, ino);
12530 }
12531 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
12532 if (!all_dirs)
12533 ext2fs_u32_list_iterate_end(iter);
12534
Rob Landleye7c43b62006-03-01 16:39:45 +000012535 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012536 ctx->dirs_to_hash = 0;
12537
12538#ifdef RESOURCE_TRACK
12539 if (ctx->options & E2F_OPT_TIME2) {
12540 e2fsck_clear_progbar(ctx);
12541 print_resource_track("Pass 3A", &rtrack);
12542 }
12543#endif
12544}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012545
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012546/*
12547 * linux/fs/revoke.c
12548 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012549 * Journal revoke routines for the generic filesystem journaling code;
12550 * part of the ext2fs journaling system.
12551 *
12552 * Revoke is the mechanism used to prevent old log records for deleted
12553 * metadata from being replayed on top of newer data using the same
12554 * blocks. The revoke mechanism is used in two separate places:
12555 *
12556 * + Commit: during commit we write the entire list of the current
12557 * transaction's revoked blocks to the journal
12558 *
12559 * + Recovery: during recovery we record the transaction ID of all
12560 * revoked blocks. If there are multiple revoke records in the log
12561 * for a single block, only the last one counts, and if there is a log
12562 * entry for a block beyond the last revoke, then that log entry still
12563 * gets replayed.
12564 *
12565 * We can get interactions between revokes and new log data within a
12566 * single transaction:
12567 *
12568 * Block is revoked and then journaled:
12569 * The desired end result is the journaling of the new block, so we
12570 * cancel the revoke before the transaction commits.
12571 *
12572 * Block is journaled and then revoked:
12573 * The revoke must take precedence over the write of the block, so we
12574 * need either to cancel the journal entry or to write the revoke
12575 * later in the log than the log block. In this case, we choose the
12576 * latter: journaling a block cancels any revoke record for that block
12577 * in the current transaction, so any revoke for that block in the
12578 * transaction must have happened after the block was journaled and so
12579 * the revoke must take precedence.
12580 *
12581 * Block is revoked and then written as data:
12582 * The data write is allowed to succeed, but the revoke is _not_
12583 * cancelled. We still need to prevent old log records from
12584 * overwriting the new data. We don't even need to clear the revoke
12585 * bit here.
12586 *
12587 * Revoke information on buffers is a tri-state value:
12588 *
12589 * RevokeValid clear: no cached revoke status, need to look it up
12590 * RevokeValid set, Revoked clear:
12591 * buffer has not been revoked, and cancel_revoke
12592 * need do nothing.
12593 * RevokeValid set, Revoked set:
12594 * buffer has been revoked.
12595 */
12596
12597static kmem_cache_t *revoke_record_cache;
12598static kmem_cache_t *revoke_table_cache;
12599
12600/* Each revoke record represents one single revoked block. During
12601 journal replay, this involves recording the transaction ID of the
12602 last transaction to revoke this block. */
12603
12604struct jbd_revoke_record_s
12605{
12606 struct list_head hash;
12607 tid_t sequence; /* Used for recovery only */
12608 unsigned long blocknr;
12609};
12610
12611
12612/* The revoke table is just a simple hash table of revoke records. */
12613struct jbd_revoke_table_s
12614{
12615 /* It is conceivable that we might want a larger hash table
12616 * for recovery. Must be a power of two. */
12617 int hash_size;
12618 int hash_shift;
12619 struct list_head *hash_table;
12620};
12621
12622
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012623/* Utility functions to maintain the revoke table */
12624
12625/* Borrowed from buffer.c: this is a tried and tested block hash function */
12626static inline int hash(journal_t *journal, unsigned long block)
12627{
12628 struct jbd_revoke_table_s *table = journal->j_revoke;
12629 int hash_shift = table->hash_shift;
12630
12631 return ((block << (hash_shift - 6)) ^
12632 (block >> 13) ^
12633 (block << (hash_shift - 12))) & (table->hash_size - 1);
12634}
12635
12636static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
12637 tid_t seq)
12638{
12639 struct list_head *hash_list;
12640 struct jbd_revoke_record_s *record;
12641
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012642 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
12643 if (!record)
12644 goto oom;
12645
12646 record->sequence = seq;
12647 record->blocknr = blocknr;
12648 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
12649 list_add(&record->hash, hash_list);
12650 return 0;
12651
12652oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012653 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012654}
12655
12656/* Find a revoke record in the journal's hash table. */
12657
12658static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
12659 unsigned long blocknr)
12660{
12661 struct list_head *hash_list;
12662 struct jbd_revoke_record_s *record;
12663
12664 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
12665
12666 record = (struct jbd_revoke_record_s *) hash_list->next;
12667 while (&(record->hash) != hash_list) {
12668 if (record->blocknr == blocknr)
12669 return record;
12670 record = (struct jbd_revoke_record_s *) record->hash.next;
12671 }
12672 return NULL;
12673}
12674
12675int journal_init_revoke_caches(void)
12676{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012677 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012678 if (revoke_record_cache == 0)
12679 return -ENOMEM;
12680
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012681 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012682 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012683 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012684 revoke_record_cache = NULL;
12685 return -ENOMEM;
12686 }
12687 return 0;
12688}
12689
12690void journal_destroy_revoke_caches(void)
12691{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012692 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012693 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012694 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012695 revoke_table_cache = 0;
12696}
12697
12698/* Initialise the revoke table for a given journal to a given size. */
12699
12700int journal_init_revoke(journal_t *journal, int hash_size)
12701{
12702 int shift, tmp;
12703
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012704 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
12705 if (!journal->j_revoke)
12706 return -ENOMEM;
12707
12708 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012709 journal->j_revoke->hash_size = hash_size;
12710
12711 shift = 0;
12712 tmp = hash_size;
12713 while((tmp >>= 1UL) != 0UL)
12714 shift++;
12715 journal->j_revoke->hash_shift = shift;
12716
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012717 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012718 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012719 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012720 journal->j_revoke = NULL;
12721 return -ENOMEM;
12722 }
12723
12724 for (tmp = 0; tmp < hash_size; tmp++)
12725 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
12726
12727 return 0;
12728}
12729
12730/* Destoy a journal's revoke table. The table must already be empty! */
12731
12732void journal_destroy_revoke(journal_t *journal)
12733{
12734 struct jbd_revoke_table_s *table;
12735 struct list_head *hash_list;
12736 int i;
12737
12738 table = journal->j_revoke;
12739 if (!table)
12740 return;
12741
12742 for (i=0; i<table->hash_size; i++) {
12743 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012744 }
12745
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012746 free(table->hash_table);
12747 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012748 journal->j_revoke = NULL;
12749}
12750
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012751/*
12752 * Revoke support for recovery.
12753 *
12754 * Recovery needs to be able to:
12755 *
12756 * record all revoke records, including the tid of the latest instance
12757 * of each revoke in the journal
12758 *
12759 * check whether a given block in a given transaction should be replayed
12760 * (ie. has not been revoked by a revoke record in that or a subsequent
12761 * transaction)
12762 *
12763 * empty the revoke table after recovery.
12764 */
12765
12766/*
12767 * First, setting revoke records. We create a new revoke record for
12768 * every block ever revoked in the log as we scan it for recovery, and
12769 * we update the existing records if we find multiple revokes for a
12770 * single block.
12771 */
12772
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012773int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012774 tid_t sequence)
12775{
12776 struct jbd_revoke_record_s *record;
12777
12778 record = find_revoke_record(journal, blocknr);
12779 if (record) {
12780 /* If we have multiple occurences, only record the
12781 * latest sequence number in the hashed record */
12782 if (tid_gt(sequence, record->sequence))
12783 record->sequence = sequence;
12784 return 0;
12785 }
12786 return insert_revoke_hash(journal, blocknr, sequence);
12787}
12788
12789/*
12790 * Test revoke records. For a given block referenced in the log, has
12791 * that block been revoked? A revoke record with a given transaction
12792 * sequence number revokes all blocks in that transaction and earlier
12793 * ones, but later transactions still need replayed.
12794 */
12795
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012796int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012797 tid_t sequence)
12798{
12799 struct jbd_revoke_record_s *record;
12800
12801 record = find_revoke_record(journal, blocknr);
12802 if (!record)
12803 return 0;
12804 if (tid_gt(sequence, record->sequence))
12805 return 0;
12806 return 1;
12807}
12808
12809/*
12810 * Finally, once recovery is over, we need to clear the revoke table so
12811 * that it can be reused by the running filesystem.
12812 */
12813
12814void journal_clear_revoke(journal_t *journal)
12815{
12816 int i;
12817 struct list_head *hash_list;
12818 struct jbd_revoke_record_s *record;
12819 struct jbd_revoke_table_s *revoke_var;
12820
12821 revoke_var = journal->j_revoke;
12822
12823 for (i = 0; i < revoke_var->hash_size; i++) {
12824 hash_list = &revoke_var->hash_table[i];
12825 while (!list_empty(hash_list)) {
12826 record = (struct jbd_revoke_record_s*) hash_list->next;
12827 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012828 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012829 }
12830 }
12831}
12832
12833/*
12834 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012835 */
12836
12837#define MIN_CHECK 1
12838#define MAX_CHECK 2
12839
12840static void check_super_value(e2fsck_t ctx, const char *descr,
12841 unsigned long value, int flags,
12842 unsigned long min_val, unsigned long max_val)
12843{
12844 struct problem_context pctx;
12845
12846 if (((flags & MIN_CHECK) && (value < min_val)) ||
12847 ((flags & MAX_CHECK) && (value > max_val))) {
12848 clear_problem_context(&pctx);
12849 pctx.num = value;
12850 pctx.str = descr;
12851 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
12852 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
12853 }
12854}
12855
12856/*
12857 * This routine may get stubbed out in special compilations of the
12858 * e2fsck code..
12859 */
12860#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012861static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012862{
12863 return (ext2fs_get_device_size(ctx->filesystem_name,
12864 EXT2_BLOCK_SIZE(ctx->fs->super),
12865 &ctx->num_blocks));
12866}
12867#endif
12868
12869/*
12870 * helper function to release an inode
12871 */
12872struct process_block_struct {
12873 e2fsck_t ctx;
12874 char *buf;
12875 struct problem_context *pctx;
12876 int truncating;
12877 int truncate_offset;
12878 e2_blkcnt_t truncate_block;
12879 int truncated_blocks;
12880 int abort;
12881 errcode_t errcode;
12882};
12883
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012884static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012885 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012886 blk_t ref_blk FSCK_ATTR((unused)),
12887 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012888 void *priv_data)
12889{
12890 struct process_block_struct *pb;
12891 e2fsck_t ctx;
12892 struct problem_context *pctx;
12893 blk_t blk = *block_nr;
12894 int retval = 0;
12895
12896 pb = (struct process_block_struct *) priv_data;
12897 ctx = pb->ctx;
12898 pctx = pb->pctx;
12899
12900 pctx->blk = blk;
12901 pctx->blkcount = blockcnt;
12902
12903 if (HOLE_BLKADDR(blk))
12904 return 0;
12905
12906 if ((blk < fs->super->s_first_data_block) ||
12907 (blk >= fs->super->s_blocks_count)) {
12908 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
12909 return_abort:
12910 pb->abort = 1;
12911 return BLOCK_ABORT;
12912 }
12913
12914 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
12915 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
12916 goto return_abort;
12917 }
12918
12919 /*
12920 * If we are deleting an orphan, then we leave the fields alone.
12921 * If we are truncating an orphan, then update the inode fields
12922 * and clean up any partial block data.
12923 */
12924 if (pb->truncating) {
12925 /*
12926 * We only remove indirect blocks if they are
12927 * completely empty.
12928 */
12929 if (blockcnt < 0) {
12930 int i, limit;
12931 blk_t *bp;
12932
12933 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
12934 pb->buf);
12935 if (pb->errcode)
12936 goto return_abort;
12937
12938 limit = fs->blocksize >> 2;
12939 for (i = 0, bp = (blk_t *) pb->buf;
12940 i < limit; i++, bp++)
12941 if (*bp)
12942 return 0;
12943 }
12944 /*
12945 * We don't remove direct blocks until we've reached
12946 * the truncation block.
12947 */
12948 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
12949 return 0;
12950 /*
12951 * If part of the last block needs truncating, we do
12952 * it here.
12953 */
12954 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
12955 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
12956 pb->buf);
12957 if (pb->errcode)
12958 goto return_abort;
12959 memset(pb->buf + pb->truncate_offset, 0,
12960 fs->blocksize - pb->truncate_offset);
12961 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
12962 pb->buf);
12963 if (pb->errcode)
12964 goto return_abort;
12965 }
12966 pb->truncated_blocks++;
12967 *block_nr = 0;
12968 retval |= BLOCK_CHANGED;
12969 }
12970
12971 ext2fs_block_alloc_stats(fs, blk, -1);
12972 return retval;
12973}
12974
12975/*
12976 * This function releases an inode. Returns 1 if an inconsistency was
12977 * found. If the inode has a link count, then it is being truncated and
12978 * not deleted.
12979 */
12980static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
12981 struct ext2_inode *inode, char *block_buf,
12982 struct problem_context *pctx)
12983{
12984 struct process_block_struct pb;
12985 ext2_filsys fs = ctx->fs;
12986 errcode_t retval;
12987 __u32 count;
12988
12989 if (!ext2fs_inode_has_valid_blocks(inode))
12990 return 0;
12991
12992 pb.buf = block_buf + 3 * ctx->fs->blocksize;
12993 pb.ctx = ctx;
12994 pb.abort = 0;
12995 pb.errcode = 0;
12996 pb.pctx = pctx;
12997 if (inode->i_links_count) {
12998 pb.truncating = 1;
12999 pb.truncate_block = (e2_blkcnt_t)
13000 ((((long long)inode->i_size_high << 32) +
13001 inode->i_size + fs->blocksize - 1) /
13002 fs->blocksize);
13003 pb.truncate_offset = inode->i_size % fs->blocksize;
13004 } else {
13005 pb.truncating = 0;
13006 pb.truncate_block = 0;
13007 pb.truncate_offset = 0;
13008 }
13009 pb.truncated_blocks = 0;
13010 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
13011 block_buf, release_inode_block, &pb);
13012 if (retval) {
13013 com_err("release_inode_blocks", retval,
13014 _("while calling ext2fs_block_iterate for inode %d"),
13015 ino);
13016 return 1;
13017 }
13018 if (pb.abort)
13019 return 1;
13020
13021 /* Refresh the inode since ext2fs_block_iterate may have changed it */
13022 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
13023
13024 if (pb.truncated_blocks)
13025 inode->i_blocks -= pb.truncated_blocks *
13026 (fs->blocksize / 512);
13027
13028 if (inode->i_file_acl) {
13029 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
13030 block_buf, -1, &count);
13031 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
13032 retval = 0;
13033 count = 1;
13034 }
13035 if (retval) {
13036 com_err("release_inode_blocks", retval,
13037 _("while calling ext2fs_adjust_ea_refocunt for inode %d"),
13038 ino);
13039 return 1;
13040 }
13041 if (count == 0)
13042 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
13043 inode->i_file_acl = 0;
13044 }
13045 return 0;
13046}
13047
13048/*
13049 * This function releases all of the orphan inodes. It returns 1 if
13050 * it hit some error, and 0 on success.
13051 */
13052static int release_orphan_inodes(e2fsck_t ctx)
13053{
13054 ext2_filsys fs = ctx->fs;
13055 ext2_ino_t ino, next_ino;
13056 struct ext2_inode inode;
13057 struct problem_context pctx;
13058 char *block_buf;
13059
13060 if ((ino = fs->super->s_last_orphan) == 0)
13061 return 0;
13062
13063 /*
13064 * Win or lose, we won't be using the head of the orphan inode
13065 * list again.
13066 */
13067 fs->super->s_last_orphan = 0;
13068 ext2fs_mark_super_dirty(fs);
13069
13070 /*
13071 * If the filesystem contains errors, don't run the orphan
13072 * list, since the orphan list can't be trusted; and we're
13073 * going to be running a full e2fsck run anyway...
13074 */
13075 if (fs->super->s_state & EXT2_ERROR_FS)
13076 return 0;
13077
13078 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
13079 (ino > fs->super->s_inodes_count)) {
13080 clear_problem_context(&pctx);
13081 pctx.ino = ino;
13082 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
13083 return 1;
13084 }
13085
13086 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
13087 "block iterate buffer");
13088 e2fsck_read_bitmaps(ctx);
13089
13090 while (ino) {
13091 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
13092 clear_problem_context(&pctx);
13093 pctx.ino = ino;
13094 pctx.inode = &inode;
13095 pctx.str = inode.i_links_count ? _("Truncating") :
13096 _("Clearing");
13097
13098 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
13099
13100 next_ino = inode.i_dtime;
13101 if (next_ino &&
13102 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
13103 (next_ino > fs->super->s_inodes_count))) {
13104 pctx.ino = next_ino;
13105 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
13106 goto return_abort;
13107 }
13108
13109 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
13110 goto return_abort;
13111
13112 if (!inode.i_links_count) {
13113 ext2fs_inode_alloc_stats2(fs, ino, -1,
13114 LINUX_S_ISDIR(inode.i_mode));
13115 inode.i_dtime = time(0);
13116 } else {
13117 inode.i_dtime = 0;
13118 }
13119 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
13120 ino = next_ino;
13121 }
13122 ext2fs_free_mem(&block_buf);
13123 return 0;
13124return_abort:
13125 ext2fs_free_mem(&block_buf);
13126 return 1;
13127}
13128
13129/*
13130 * Check the resize inode to make sure it is sane. We check both for
13131 * the case where on-line resizing is not enabled (in which case the
13132 * resize inode should be cleared) as well as the case where on-line
13133 * resizing is enabled.
13134 */
13135static void check_resize_inode(e2fsck_t ctx)
13136{
13137 ext2_filsys fs = ctx->fs;
13138 struct ext2_inode inode;
13139 struct problem_context pctx;
13140 int i, j, gdt_off, ind_off;
13141 blk_t blk, pblk, expect;
13142 __u32 *dind_buf = 0, *ind_buf;
13143 errcode_t retval;
13144
13145 clear_problem_context(&pctx);
13146
13147 /*
13148 * If the resize inode feature isn't set, then
13149 * s_reserved_gdt_blocks must be zero.
13150 */
13151 if (!(fs->super->s_feature_compat &
13152 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
13153 if (fs->super->s_reserved_gdt_blocks) {
13154 pctx.num = fs->super->s_reserved_gdt_blocks;
13155 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
13156 &pctx)) {
13157 fs->super->s_reserved_gdt_blocks = 0;
13158 ext2fs_mark_super_dirty(fs);
13159 }
13160 }
13161 }
13162
Mike Frysinger874af852006-03-08 07:03:27 +000013163 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013164 pctx.ino = EXT2_RESIZE_INO;
13165 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
13166 if (retval) {
13167 if (fs->super->s_feature_compat &
13168 EXT2_FEATURE_COMPAT_RESIZE_INODE)
13169 ctx->flags |= E2F_FLAG_RESIZE_INODE;
13170 return;
13171 }
13172
13173 /*
13174 * If the resize inode feature isn't set, check to make sure
13175 * the resize inode is cleared; then we're done.
13176 */
13177 if (!(fs->super->s_feature_compat &
13178 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
13179 for (i=0; i < EXT2_N_BLOCKS; i++) {
13180 if (inode.i_block[i])
13181 break;
13182 }
13183 if ((i < EXT2_N_BLOCKS) &&
13184 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
13185 memset(&inode, 0, sizeof(inode));
13186 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
13187 "clear_resize");
13188 }
13189 return;
13190 }
13191
13192 /*
13193 * The resize inode feature is enabled; check to make sure the
13194 * only block in use is the double indirect block
13195 */
13196 blk = inode.i_block[EXT2_DIND_BLOCK];
13197 for (i=0; i < EXT2_N_BLOCKS; i++) {
13198 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
13199 break;
13200 }
13201 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
13202 !(inode.i_mode & LINUX_S_IFREG) ||
13203 (blk < fs->super->s_first_data_block ||
13204 blk >= fs->super->s_blocks_count)) {
13205 resize_inode_invalid:
13206 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
13207 memset(&inode, 0, sizeof(inode));
13208 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
13209 "clear_resize");
13210 ctx->flags |= E2F_FLAG_RESIZE_INODE;
13211 }
13212 if (!(ctx->options & E2F_OPT_READONLY)) {
13213 fs->super->s_state &= ~EXT2_VALID_FS;
13214 ext2fs_mark_super_dirty(fs);
13215 }
13216 goto cleanup;
13217 }
13218 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
13219 "resize dind buffer");
13220 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
13221
13222 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
13223 if (retval)
13224 goto resize_inode_invalid;
13225
13226 gdt_off = fs->desc_blocks;
13227 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
13228 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
13229 i++, gdt_off++, pblk++) {
13230 gdt_off %= fs->blocksize/4;
13231 if (dind_buf[gdt_off] != pblk)
13232 goto resize_inode_invalid;
13233 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
13234 if (retval)
13235 goto resize_inode_invalid;
13236 ind_off = 0;
13237 for (j = 1; j < fs->group_desc_count; j++) {
13238 if (!ext2fs_bg_has_super(fs, j))
13239 continue;
13240 expect = pblk + (j * fs->super->s_blocks_per_group);
13241 if (ind_buf[ind_off] != expect)
13242 goto resize_inode_invalid;
13243 ind_off++;
13244 }
13245 }
13246
13247cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000013248 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013249
13250 }
13251
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013252static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013253{
13254 ext2_filsys fs = ctx->fs;
13255 blk_t first_block, last_block;
13256 struct ext2_super_block *sb = fs->super;
13257 struct ext2_group_desc *gd;
13258 blk_t blocks_per_group = fs->super->s_blocks_per_group;
13259 blk_t bpg_max;
13260 int inodes_per_block;
13261 int ipg_max;
13262 int inode_size;
13263 dgrp_t i;
13264 blk_t should_be;
13265 struct problem_context pctx;
13266 __u32 free_blocks = 0, free_inodes = 0;
13267
13268 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
13269 ipg_max = inodes_per_block * (blocks_per_group - 4);
13270 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
13271 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
13272 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
13273 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
13274 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
13275
13276 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
13277 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
13278 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
13279 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
13280 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
13281 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
13282
13283 clear_problem_context(&pctx);
13284
13285 /*
13286 * Verify the super block constants...
13287 */
13288 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
13289 MIN_CHECK, 1, 0);
13290 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
13291 MIN_CHECK, 1, 0);
13292 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
13293 MAX_CHECK, 0, sb->s_blocks_count);
13294 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
13295 MIN_CHECK | MAX_CHECK, 0,
13296 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
13297 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
13298 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
13299 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
13300 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
13301 bpg_max);
13302 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
13303 MIN_CHECK | MAX_CHECK, 8, bpg_max);
13304 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
13305 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
13306 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
13307 MAX_CHECK, 0, sb->s_blocks_count / 2);
13308 check_super_value(ctx, "reserved_gdt_blocks",
13309 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
13310 fs->blocksize/4);
13311 inode_size = EXT2_INODE_SIZE(sb);
13312 check_super_value(ctx, "inode_size",
13313 inode_size, MIN_CHECK | MAX_CHECK,
13314 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
13315 if (inode_size & (inode_size - 1)) {
13316 pctx.num = inode_size;
13317 pctx.str = "inode_size";
13318 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
13319 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
13320 return;
13321 }
13322
13323 if (!ctx->num_blocks) {
13324 pctx.errcode = e2fsck_get_device_size(ctx);
13325 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
13326 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
13327 ctx->flags |= E2F_FLAG_ABORT;
13328 return;
13329 }
13330 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
13331 (ctx->num_blocks < sb->s_blocks_count)) {
13332 pctx.blk = sb->s_blocks_count;
13333 pctx.blk2 = ctx->num_blocks;
13334 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
13335 ctx->flags |= E2F_FLAG_ABORT;
13336 return;
13337 }
13338 }
13339 }
13340
13341 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
13342 pctx.blk = EXT2_BLOCK_SIZE(sb);
13343 pctx.blk2 = EXT2_FRAG_SIZE(sb);
13344 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
13345 ctx->flags |= E2F_FLAG_ABORT;
13346 return;
13347 }
13348
13349 should_be = sb->s_frags_per_group >>
13350 (sb->s_log_block_size - sb->s_log_frag_size);
13351 if (sb->s_blocks_per_group != should_be) {
13352 pctx.blk = sb->s_blocks_per_group;
13353 pctx.blk2 = should_be;
13354 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
13355 ctx->flags |= E2F_FLAG_ABORT;
13356 return;
13357 }
13358
13359 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
13360 if (sb->s_first_data_block != should_be) {
13361 pctx.blk = sb->s_first_data_block;
13362 pctx.blk2 = should_be;
13363 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
13364 ctx->flags |= E2F_FLAG_ABORT;
13365 return;
13366 }
13367
13368 should_be = sb->s_inodes_per_group * fs->group_desc_count;
13369 if (sb->s_inodes_count != should_be) {
13370 pctx.ino = sb->s_inodes_count;
13371 pctx.ino2 = should_be;
13372 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
13373 sb->s_inodes_count = should_be;
13374 ext2fs_mark_super_dirty(fs);
13375 }
13376 }
13377
13378 /*
13379 * Verify the group descriptors....
13380 */
13381 first_block = sb->s_first_data_block;
13382 last_block = first_block + blocks_per_group;
13383
13384 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
13385 pctx.group = i;
13386
13387 if (i == fs->group_desc_count - 1)
13388 last_block = sb->s_blocks_count;
13389 if ((gd->bg_block_bitmap < first_block) ||
13390 (gd->bg_block_bitmap >= last_block)) {
13391 pctx.blk = gd->bg_block_bitmap;
13392 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
13393 gd->bg_block_bitmap = 0;
13394 }
13395 if (gd->bg_block_bitmap == 0) {
13396 ctx->invalid_block_bitmap_flag[i]++;
13397 ctx->invalid_bitmaps++;
13398 }
13399 if ((gd->bg_inode_bitmap < first_block) ||
13400 (gd->bg_inode_bitmap >= last_block)) {
13401 pctx.blk = gd->bg_inode_bitmap;
13402 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
13403 gd->bg_inode_bitmap = 0;
13404 }
13405 if (gd->bg_inode_bitmap == 0) {
13406 ctx->invalid_inode_bitmap_flag[i]++;
13407 ctx->invalid_bitmaps++;
13408 }
13409 if ((gd->bg_inode_table < first_block) ||
13410 ((gd->bg_inode_table +
13411 fs->inode_blocks_per_group - 1) >= last_block)) {
13412 pctx.blk = gd->bg_inode_table;
13413 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
13414 gd->bg_inode_table = 0;
13415 }
13416 if (gd->bg_inode_table == 0) {
13417 ctx->invalid_inode_table_flag[i]++;
13418 ctx->invalid_bitmaps++;
13419 }
13420 free_blocks += gd->bg_free_blocks_count;
13421 free_inodes += gd->bg_free_inodes_count;
13422 first_block += sb->s_blocks_per_group;
13423 last_block += sb->s_blocks_per_group;
13424
13425 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
13426 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
13427 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
13428 ext2fs_unmark_valid(fs);
13429
13430 }
13431
13432 /*
13433 * Update the global counts from the block group counts. This
13434 * is needed for an experimental patch which eliminates
13435 * locking the entire filesystem when allocating blocks or
13436 * inodes; if the filesystem is not unmounted cleanly, the
13437 * global counts may not be accurate.
13438 */
13439 if ((free_blocks != sb->s_free_blocks_count) ||
13440 (free_inodes != sb->s_free_inodes_count)) {
13441 if (ctx->options & E2F_OPT_READONLY)
13442 ext2fs_unmark_valid(fs);
13443 else {
13444 sb->s_free_blocks_count = free_blocks;
13445 sb->s_free_inodes_count = free_inodes;
13446 ext2fs_mark_super_dirty(fs);
13447 }
13448 }
13449
13450 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
13451 (sb->s_free_inodes_count > sb->s_inodes_count))
13452 ext2fs_unmark_valid(fs);
13453
13454
13455 /*
13456 * If we have invalid bitmaps, set the error state of the
13457 * filesystem.
13458 */
13459 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
13460 sb->s_state &= ~EXT2_VALID_FS;
13461 ext2fs_mark_super_dirty(fs);
13462 }
13463
13464 clear_problem_context(&pctx);
13465
13466#ifndef EXT2_SKIP_UUID
13467 /*
13468 * If the UUID field isn't assigned, assign it.
13469 */
13470 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
13471 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
13472 uuid_generate(sb->s_uuid);
13473 ext2fs_mark_super_dirty(fs);
13474 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
13475 }
13476 }
13477#endif
13478
13479 /*
13480 * For the Hurd, check to see if the filetype option is set,
13481 * since it doesn't support it.
13482 */
13483 if (!(ctx->options & E2F_OPT_READONLY) &&
13484 fs->super->s_creator_os == EXT2_OS_HURD &&
13485 (fs->super->s_feature_incompat &
13486 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
13487 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
13488 fs->super->s_feature_incompat &=
13489 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
13490 ext2fs_mark_super_dirty(fs);
13491
13492 }
13493 }
13494
13495 /*
13496 * If we have any of the compatibility flags set, we need to have a
13497 * revision 1 filesystem. Most kernels will not check the flags on
13498 * a rev 0 filesystem and we may have corruption issues because of
13499 * the incompatible changes to the filesystem.
13500 */
13501 if (!(ctx->options & E2F_OPT_READONLY) &&
13502 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
13503 (fs->super->s_feature_compat ||
13504 fs->super->s_feature_ro_compat ||
13505 fs->super->s_feature_incompat) &&
13506 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
13507 ext2fs_update_dynamic_rev(fs);
13508 ext2fs_mark_super_dirty(fs);
13509 }
13510
13511 check_resize_inode(ctx);
13512
13513 /*
13514 * Clean up any orphan inodes, if present.
13515 */
13516 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
13517 fs->super->s_state &= ~EXT2_VALID_FS;
13518 ext2fs_mark_super_dirty(fs);
13519 }
13520
13521 /*
13522 * Move the ext3 journal file, if necessary.
13523 */
13524 e2fsck_move_ext3_journal(ctx);
13525 return;
13526}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013527
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013528/*
13529 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013530 */
13531
13532#ifdef ENABLE_SWAPFS
13533
13534struct swap_block_struct {
13535 ext2_ino_t ino;
13536 int isdir;
13537 errcode_t errcode;
13538 char *dir_buf;
13539 struct ext2_inode *inode;
13540};
13541
13542/*
13543 * This is a helper function for block_iterate. We mark all of the
13544 * indirect and direct blocks as changed, so that block_iterate will
13545 * write them out.
13546 */
13547static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
13548 void *priv_data)
13549{
13550 errcode_t retval;
13551
13552 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
13553
13554 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
13555 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
13556 if (retval) {
13557 sb->errcode = retval;
13558 return BLOCK_ABORT;
13559 }
13560 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
13561 if (retval) {
13562 sb->errcode = retval;
13563 return BLOCK_ABORT;
13564 }
13565 }
13566 if (blockcnt >= 0) {
13567 if (blockcnt < EXT2_NDIR_BLOCKS)
13568 return 0;
13569 return BLOCK_CHANGED;
13570 }
13571 if (blockcnt == BLOCK_COUNT_IND) {
13572 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
13573 return 0;
13574 return BLOCK_CHANGED;
13575 }
13576 if (blockcnt == BLOCK_COUNT_DIND) {
13577 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
13578 return 0;
13579 return BLOCK_CHANGED;
13580 }
13581 if (blockcnt == BLOCK_COUNT_TIND) {
13582 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
13583 return 0;
13584 return BLOCK_CHANGED;
13585 }
13586 return BLOCK_CHANGED;
13587}
13588
13589/*
13590 * This function is responsible for byte-swapping all of the indirect,
13591 * block pointers. It is also responsible for byte-swapping directories.
13592 */
13593static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
13594 struct ext2_inode *inode)
13595{
13596 errcode_t retval;
13597 struct swap_block_struct sb;
13598
13599 sb.ino = ino;
13600 sb.inode = inode;
13601 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
13602 sb.errcode = 0;
13603 sb.isdir = 0;
13604 if (LINUX_S_ISDIR(inode->i_mode))
13605 sb.isdir = 1;
13606
13607 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
13608 swap_block, &sb);
13609 if (retval) {
13610 com_err("swap_inode_blocks", retval,
13611 _("while calling ext2fs_block_iterate"));
13612 ctx->flags |= E2F_FLAG_ABORT;
13613 return;
13614 }
13615 if (sb.errcode) {
13616 com_err("swap_inode_blocks", sb.errcode,
13617 _("while calling iterator function"));
13618 ctx->flags |= E2F_FLAG_ABORT;
13619 return;
13620 }
13621}
13622
13623static void swap_inodes(e2fsck_t ctx)
13624{
13625 ext2_filsys fs = ctx->fs;
13626 dgrp_t group;
13627 unsigned int i;
13628 ext2_ino_t ino = 1;
13629 char *buf, *block_buf;
13630 errcode_t retval;
13631 struct ext2_inode * inode;
13632
13633 e2fsck_use_inode_shortcuts(ctx, 1);
13634
13635 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
13636 &buf);
13637 if (retval) {
13638 com_err("swap_inodes", retval,
13639 _("while allocating inode buffer"));
13640 ctx->flags |= E2F_FLAG_ABORT;
13641 return;
13642 }
13643 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
13644 "block interate buffer");
13645 for (group = 0; group < fs->group_desc_count; group++) {
13646 retval = io_channel_read_blk(fs->io,
13647 fs->group_desc[group].bg_inode_table,
13648 fs->inode_blocks_per_group, buf);
13649 if (retval) {
13650 com_err("swap_inodes", retval,
13651 _("while reading inode table (group %d)"),
13652 group);
13653 ctx->flags |= E2F_FLAG_ABORT;
13654 return;
13655 }
13656 inode = (struct ext2_inode *) buf;
13657 for (i=0; i < fs->super->s_inodes_per_group;
13658 i++, ino++, inode++) {
13659 ctx->stashed_ino = ino;
13660 ctx->stashed_inode = inode;
13661
13662 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
13663 ext2fs_swap_inode(fs, inode, inode, 0);
13664
13665 /*
13666 * Skip deleted files.
13667 */
13668 if (inode->i_links_count == 0)
13669 continue;
13670
13671 if (LINUX_S_ISDIR(inode->i_mode) ||
13672 ((inode->i_block[EXT2_IND_BLOCK] ||
13673 inode->i_block[EXT2_DIND_BLOCK] ||
13674 inode->i_block[EXT2_TIND_BLOCK]) &&
13675 ext2fs_inode_has_valid_blocks(inode)))
13676 swap_inode_blocks(ctx, ino, block_buf, inode);
13677
13678 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
13679 return;
13680
13681 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
13682 ext2fs_swap_inode(fs, inode, inode, 1);
13683 }
13684 retval = io_channel_write_blk(fs->io,
13685 fs->group_desc[group].bg_inode_table,
13686 fs->inode_blocks_per_group, buf);
13687 if (retval) {
13688 com_err("swap_inodes", retval,
13689 _("while writing inode table (group %d)"),
13690 group);
13691 ctx->flags |= E2F_FLAG_ABORT;
13692 return;
13693 }
13694 }
13695 ext2fs_free_mem(&buf);
13696 ext2fs_free_mem(&block_buf);
13697 e2fsck_use_inode_shortcuts(ctx, 0);
13698 ext2fs_flush_icache(fs);
13699}
13700
13701#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
13702/*
13703 * On the PowerPC, the big-endian variant of the ext2 filesystem
13704 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
13705 * of each word. Thus a bitmap with only bit 0 set would be, as
13706 * a string of bytes, 00 00 00 01 00 ...
13707 * To cope with this, we byte-reverse each word of a bitmap if
13708 * we have a big-endian filesystem, that is, if we are *not*
13709 * byte-swapping other word-sized numbers.
13710 */
13711#define EXT2_BIG_ENDIAN_BITMAPS
13712#endif
13713
13714#ifdef EXT2_BIG_ENDIAN_BITMAPS
13715static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
13716{
13717 __u32 *p = (__u32 *) bmap->bitmap;
13718 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
13719
13720 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
13721 *p = ext2fs_swab32(*p);
13722}
13723#endif
13724
13725
13726#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013727static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013728{
13729 ext2_filsys fs = ctx->fs;
13730#ifdef RESOURCE_TRACK
13731 struct resource_track rtrack;
13732
13733 init_resource_track(&rtrack);
13734#endif
13735
13736 if (!(ctx->options & E2F_OPT_PREEN))
13737 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
13738
13739#ifdef MTRACE
13740 mtrace_print("Byte swap");
13741#endif
13742
13743 if (fs->super->s_mnt_count) {
13744 fprintf(stderr, _("%s: the filesystem must be freshly "
13745 "checked using fsck\n"
13746 "and not mounted before trying to "
13747 "byte-swap it.\n"), ctx->device_name);
13748 ctx->flags |= E2F_FLAG_ABORT;
13749 return;
13750 }
13751 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
13752 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
13753 EXT2_FLAG_SWAP_BYTES_WRITE);
13754 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
13755 } else {
13756 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
13757 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
13758 }
13759 swap_inodes(ctx);
13760 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
13761 return;
13762 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
13763 fs->flags |= EXT2_FLAG_SWAP_BYTES;
13764 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
13765 EXT2_FLAG_SWAP_BYTES_WRITE);
13766
13767#ifdef EXT2_BIG_ENDIAN_BITMAPS
13768 e2fsck_read_bitmaps(ctx);
13769 ext2fs_swap_bitmap(fs->inode_map);
13770 ext2fs_swap_bitmap(fs->block_map);
13771 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
13772#endif
13773 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
13774 ext2fs_flush(fs);
13775 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13776
13777#ifdef RESOURCE_TRACK
13778 if (ctx->options & E2F_OPT_TIME2)
13779 print_resource_track(_("Byte swap"), &rtrack);
13780#endif
13781}
13782#endif /* ENABLE_SWAPFS */
13783
13784#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013785
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013786/*
13787 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013788 */
13789
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013790
13791#if 0
13792void fatal_error(e2fsck_t ctx, const char *msg)
13793{
13794 if (msg)
13795 fprintf (stderr, "e2fsck: %s\n", msg);
13796 if (ctx->fs && ctx->fs->io) {
13797 if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL)
13798 io_channel_flush(ctx->fs->io);
13799 else
13800 fprintf(stderr, "e2fsck: io manager magic bad!\n");
13801 }
13802 ctx->flags |= E2F_FLAG_ABORT;
13803 if (ctx->flags & E2F_FLAG_SETJMP_OK)
13804 longjmp(ctx->abort_loc, 1);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013805 exit(EXIT_ERROR);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013806}
13807#endif
13808
13809void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
13810 const char *description)
13811{
13812 void *ret;
13813 char buf[256];
13814
13815#ifdef DEBUG_ALLOCATE_MEMORY
13816 printf("Allocating %d bytes for %s...\n", size, description);
13817#endif
13818 ret = malloc(size);
13819 if (!ret) {
13820 sprintf(buf, "Can't allocate %s\n", description);
13821 fatal_error(ctx, buf);
13822 }
13823 memset(ret, 0, size);
13824 return ret;
13825}
13826
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013827static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013828{
13829 char *ret;
13830
13831 if (!str)
13832 return NULL;
13833 if (!len)
13834 len = strlen(str);
13835 ret = malloc(len+1);
13836 if (ret) {
13837 strncpy(ret, str, len);
13838 ret[len] = 0;
13839 }
13840 return ret;
13841}
13842
13843#ifndef HAVE_CONIO_H
13844static int read_a_char(void)
13845{
13846 char c;
13847 int r;
13848 int fail = 0;
13849
13850 while(1) {
13851 if (e2fsck_global_ctx &&
13852 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
13853 return 3;
13854 }
13855 r = read(0, &c, 1);
13856 if (r == 1)
13857 return c;
13858 if (fail++ > 100)
13859 break;
13860 }
13861 return EOF;
13862}
13863#endif
13864
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013865static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013866{
13867 int c;
13868 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013869 static const char short_yes[] = "yY";
13870 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013871
13872#ifdef HAVE_TERMIOS_H
13873 struct termios termios, tmp;
13874
13875 tcgetattr (0, &termios);
13876 tmp = termios;
13877 tmp.c_lflag &= ~(ICANON | ECHO);
13878 tmp.c_cc[VMIN] = 1;
13879 tmp.c_cc[VTIME] = 0;
13880 tcsetattr (0, TCSANOW, &tmp);
13881#endif
13882
13883 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013884 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013885 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013886 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013887 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013888 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013889 printf("%s%s? ", string, defstr);
13890 while (1) {
13891 fflush (stdout);
13892 if ((c = read_a_char()) == EOF)
13893 break;
13894 if (c == 3) {
13895#ifdef HAVE_TERMIOS_H
13896 tcsetattr (0, TCSANOW, &termios);
13897#endif
13898 if (e2fsck_global_ctx &&
13899 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
13900 puts("\n");
13901 longjmp(e2fsck_global_ctx->abort_loc, 1);
13902 }
13903 puts(_("cancelled!\n"));
13904 return 0;
13905 }
13906 if (strchr(short_yes, (char) c)) {
13907 def = 1;
13908 break;
13909 }
13910 else if (strchr(short_no, (char) c)) {
13911 def = 0;
13912 break;
13913 }
13914 else if ((c == ' ' || c == '\n') && (def != -1))
13915 break;
13916 }
13917 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013918 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013919 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013920 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013921#ifdef HAVE_TERMIOS_H
13922 tcsetattr (0, TCSANOW, &termios);
13923#endif
13924 return def;
13925}
13926
13927int ask (e2fsck_t ctx, const char * string, int def)
13928{
13929 if (ctx->options & E2F_OPT_NO) {
13930 printf (_("%s? no\n\n"), string);
13931 return 0;
13932 }
13933 if (ctx->options & E2F_OPT_YES) {
13934 printf (_("%s? yes\n\n"), string);
13935 return 1;
13936 }
13937 if (ctx->options & E2F_OPT_PREEN) {
13938 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
13939 return def;
13940 }
13941 return ask_yn(string, def);
13942}
13943
13944void e2fsck_read_bitmaps(e2fsck_t ctx)
13945{
13946 ext2_filsys fs = ctx->fs;
13947 errcode_t retval;
13948
13949 if (ctx->invalid_bitmaps) {
13950 com_err(ctx->program_name, 0,
13951 _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
13952 ctx->device_name);
13953 fatal_error(ctx, 0);
13954 }
13955
13956 ehandler_operation(_("reading inode and block bitmaps"));
13957 retval = ext2fs_read_bitmaps(fs);
13958 ehandler_operation(0);
13959 if (retval) {
13960 com_err(ctx->program_name, retval,
13961 _("while retrying to read bitmaps for %s"),
13962 ctx->device_name);
13963 fatal_error(ctx, 0);
13964 }
13965}
13966
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013967static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013968{
13969 ext2_filsys fs = ctx->fs;
13970 errcode_t retval;
13971
13972 if (ext2fs_test_bb_dirty(fs)) {
13973 ehandler_operation(_("writing block bitmaps"));
13974 retval = ext2fs_write_block_bitmap(fs);
13975 ehandler_operation(0);
13976 if (retval) {
13977 com_err(ctx->program_name, retval,
13978 _("while retrying to write block bitmaps for %s"),
13979 ctx->device_name);
13980 fatal_error(ctx, 0);
13981 }
13982 }
13983
13984 if (ext2fs_test_ib_dirty(fs)) {
13985 ehandler_operation(_("writing inode bitmaps"));
13986 retval = ext2fs_write_inode_bitmap(fs);
13987 ehandler_operation(0);
13988 if (retval) {
13989 com_err(ctx->program_name, retval,
13990 _("while retrying to write inode bitmaps for %s"),
13991 ctx->device_name);
13992 fatal_error(ctx, 0);
13993 }
13994 }
13995}
13996
13997void preenhalt(e2fsck_t ctx)
13998{
13999 ext2_filsys fs = ctx->fs;
14000
14001 if (!(ctx->options & E2F_OPT_PREEN))
14002 return;
14003 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
14004 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
14005 ctx->device_name);
14006 if (fs != NULL) {
14007 fs->super->s_state |= EXT2_ERROR_FS;
14008 ext2fs_mark_super_dirty(fs);
14009 ext2fs_close(fs);
14010 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014011 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014012}
14013
14014#ifdef RESOURCE_TRACK
14015void init_resource_track(struct resource_track *track)
14016{
14017#ifdef HAVE_GETRUSAGE
14018 struct rusage r;
14019#endif
14020
14021 track->brk_start = sbrk(0);
14022 gettimeofday(&track->time_start, 0);
14023#ifdef HAVE_GETRUSAGE
14024#ifdef sun
14025 memset(&r, 0, sizeof(struct rusage));
14026#endif
14027 getrusage(RUSAGE_SELF, &r);
14028 track->user_start = r.ru_utime;
14029 track->system_start = r.ru_stime;
14030#else
14031 track->user_start.tv_sec = track->user_start.tv_usec = 0;
14032 track->system_start.tv_sec = track->system_start.tv_usec = 0;
14033#endif
14034}
14035
14036static _INLINE_ float timeval_subtract(struct timeval *tv1,
14037 struct timeval *tv2)
14038{
14039 return ((tv1->tv_sec - tv2->tv_sec) +
14040 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
14041}
14042
14043void print_resource_track(const char *desc, struct resource_track *track)
14044{
14045#ifdef HAVE_GETRUSAGE
14046 struct rusage r;
14047#endif
14048#ifdef HAVE_MALLINFO
14049 struct mallinfo malloc_info;
14050#endif
14051 struct timeval time_end;
14052
14053 gettimeofday(&time_end, 0);
14054
14055 if (desc)
14056 printf("%s: ", desc);
14057
14058#ifdef HAVE_MALLINFO
14059#define kbytes(x) (((x) + 1023) / 1024)
14060
14061 malloc_info = mallinfo();
14062 printf(_("Memory used: %dk/%dk (%dk/%dk), "),
14063 kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd),
14064 kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
14065#else
14066 printf(_("Memory used: %d, "),
14067 (int) (((char *) sbrk(0)) - ((char *) track->brk_start)));
14068#endif
14069#ifdef HAVE_GETRUSAGE
14070 getrusage(RUSAGE_SELF, &r);
14071
14072 printf(_("time: %5.2f/%5.2f/%5.2f\n"),
14073 timeval_subtract(&time_end, &track->time_start),
14074 timeval_subtract(&r.ru_utime, &track->user_start),
14075 timeval_subtract(&r.ru_stime, &track->system_start));
14076#else
14077 printf(_("elapsed time: %6.3f\n"),
14078 timeval_subtract(&time_end, &track->time_start));
14079#endif
14080}
14081#endif /* RESOURCE_TRACK */
14082
14083void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
14084 struct ext2_inode * inode, const char *proc)
14085{
14086 int retval;
14087
14088 retval = ext2fs_read_inode(ctx->fs, ino, inode);
14089 if (retval) {
14090 com_err("ext2fs_read_inode", retval,
14091 _("while reading inode %ld in %s"), ino, proc);
14092 fatal_error(ctx, 0);
14093 }
14094}
14095
14096extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
14097 struct ext2_inode * inode, int bufsize,
14098 const char *proc)
14099{
14100 int retval;
14101
14102 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
14103 if (retval) {
14104 com_err("ext2fs_write_inode", retval,
14105 _("while writing inode %ld in %s"), ino, proc);
14106 fatal_error(ctx, 0);
14107 }
14108}
14109
14110extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
14111 struct ext2_inode * inode, const char *proc)
14112{
14113 int retval;
14114
14115 retval = ext2fs_write_inode(ctx->fs, ino, inode);
14116 if (retval) {
14117 com_err("ext2fs_write_inode", retval,
14118 _("while writing inode %ld in %s"), ino, proc);
14119 fatal_error(ctx, 0);
14120 }
14121}
14122
14123#ifdef MTRACE
14124void mtrace_print(char *mesg)
14125{
14126 FILE *malloc_get_mallstream();
14127 FILE *f = malloc_get_mallstream();
14128
14129 if (f)
14130 fprintf(f, "============= %s\n", mesg);
14131}
14132#endif
14133
14134blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
14135 io_manager manager)
14136{
14137 struct ext2_super_block *sb;
14138 io_channel io = NULL;
14139 void *buf = NULL;
14140 int blocksize;
14141 blk_t superblock, ret_sb = 8193;
14142
14143 if (fs && fs->super) {
14144 ret_sb = (fs->super->s_blocks_per_group +
14145 fs->super->s_first_data_block);
14146 if (ctx) {
14147 ctx->superblock = ret_sb;
14148 ctx->blocksize = fs->blocksize;
14149 }
14150 return ret_sb;
14151 }
14152
14153 if (ctx) {
14154 if (ctx->blocksize) {
14155 ret_sb = ctx->blocksize * 8;
14156 if (ctx->blocksize == 1024)
14157 ret_sb++;
14158 ctx->superblock = ret_sb;
14159 return ret_sb;
14160 }
14161 ctx->superblock = ret_sb;
14162 ctx->blocksize = 1024;
14163 }
14164
14165 if (!name || !manager)
14166 goto cleanup;
14167
14168 if (manager->open(name, 0, &io) != 0)
14169 goto cleanup;
14170
14171 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
14172 goto cleanup;
14173 sb = (struct ext2_super_block *) buf;
14174
14175 for (blocksize = EXT2_MIN_BLOCK_SIZE;
14176 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
14177 superblock = blocksize*8;
14178 if (blocksize == 1024)
14179 superblock++;
14180 io_channel_set_blksize(io, blocksize);
14181 if (io_channel_read_blk(io, superblock,
14182 -SUPERBLOCK_SIZE, buf))
14183 continue;
14184#ifdef EXT2FS_ENABLE_SWAPFS
14185 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
14186 ext2fs_swap_super(sb);
14187#endif
14188 if (sb->s_magic == EXT2_SUPER_MAGIC) {
14189 ret_sb = superblock;
14190 if (ctx) {
14191 ctx->superblock = superblock;
14192 ctx->blocksize = blocksize;
14193 }
14194 break;
14195 }
14196 }
14197
14198cleanup:
14199 if (io)
14200 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000014201 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014202 return (ret_sb);
14203}
14204
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014205
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014206/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014207 * This function runs through the e2fsck passes and calls them all,
14208 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014209 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014210typedef void (*pass_t)(e2fsck_t ctx);
14211
14212static const pass_t e2fsck_passes[] = {
14213 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
14214 e2fsck_pass5, 0 };
14215
14216#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
14217
14218static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014219{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014220 int i;
14221 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014222
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014223 if (setjmp(ctx->abort_loc)) {
14224 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
14225 return (ctx->flags & E2F_FLAG_RUN_RETURN);
14226 }
14227 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014228
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014229 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
14230 if (ctx->flags & E2F_FLAG_RUN_RETURN)
14231 break;
14232 e2fsck_pass(ctx);
14233 if (ctx->progress)
14234 (void) (ctx->progress)(ctx, 0, 0, 0);
14235 }
14236 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014237
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014238 if (ctx->flags & E2F_FLAG_RUN_RETURN)
14239 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014240 return 0;
14241}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014242
14243
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014244/*
14245 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014246 */
14247
14248
Mike Frysinger51a43b42005-09-24 07:11:16 +000014249/* Command line options */
14250static int swapfs;
14251#ifdef ENABLE_SWAPFS
14252static int normalize_swapfs;
14253#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014254static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000014255static int show_version_only;
14256static int verbose;
14257
14258static int replace_bad_blocks;
14259static int keep_bad_blocks;
14260static char *bad_blocks_file;
14261
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014262#ifdef __CONFIG_JBD_DEBUG__E2FS /* Enabled by configure --enable-jfs-debug */
Mike Frysinger51a43b42005-09-24 07:11:16 +000014263int journal_enable_debug = -1;
14264#endif
14265
14266#if 0
14267static void usage(e2fsck_t ctx)
14268{
14269 fprintf(stderr,
14270 _("Usage: %s [-panyrcdfvstDFSV] [-b superblock] [-B blocksize]\n"
14271 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
Mike Frysinger874af852006-03-08 07:03:27 +000014272 "\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
Mike Frysinger51a43b42005-09-24 07:11:16 +000014273 "\t\t[-E extended-options] device\n"),
14274 ctx->program_name);
14275
14276 fprintf(stderr, _("\nEmergency help:\n"
14277 " -p Automatic repair (no questions)\n"
14278 " -n Make no changes to the filesystem\n"
14279 " -y Assume \"yes\" to all questions\n"
14280 " -c Check for bad blocks and add them to the badblock list\n"
14281 " -f Force checking even if filesystem is marked clean\n"));
14282 fprintf(stderr, _(""
14283 " -v Be verbose\n"
14284 " -b superblock Use alternative superblock\n"
14285 " -B blocksize Force blocksize when looking for superblock\n"
Mike Frysinger874af852006-03-08 07:03:27 +000014286 " -j external_journal Set location of the external journal\n"
Mike Frysinger51a43b42005-09-24 07:11:16 +000014287 " -l bad_blocks_file Add to badblocks list\n"
14288 " -L bad_blocks_file Set badblocks list\n"
14289 ));
14290
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014291 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014292}
14293#endif
14294
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014295#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
14296
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014297static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014298{
14299 ext2_filsys fs = ctx->fs;
14300 int inodes, inodes_used, blocks, blocks_used;
14301 int dir_links;
14302 int num_files, num_links;
14303 int frag_percent;
14304
14305 dir_links = 2 * ctx->fs_directory_count - 1;
14306 num_files = ctx->fs_total_count - dir_links;
14307 num_links = ctx->fs_links_count - dir_links;
14308 inodes = fs->super->s_inodes_count;
14309 inodes_used = (fs->super->s_inodes_count -
14310 fs->super->s_free_inodes_count);
14311 blocks = fs->super->s_blocks_count;
14312 blocks_used = (fs->super->s_blocks_count -
14313 fs->super->s_free_blocks_count);
14314
14315 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
14316 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014317
Mike Frysinger51a43b42005-09-24 07:11:16 +000014318 if (!verbose) {
14319 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
14320 ctx->device_name, inodes_used, inodes,
14321 frag_percent / 10, frag_percent % 10,
14322 blocks_used, blocks);
14323 return;
14324 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014325 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
14326 100 * inodes_used / inodes);
14327 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
14328 P_E2("", "s", ctx->fs_fragmented),
14329 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014330 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
14331 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014332 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
14333 (int) ((long long) 100 * blocks_used / blocks));
14334 printf ("%8d bad block%s\n", P_E2("", "s", ctx->fs_badblocks_count));
14335 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
14336 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
14337 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
14338 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
14339 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
14340 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
14341 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
14342 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
14343 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
14344 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
14345 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000014346}
14347
14348static void check_mount(e2fsck_t ctx)
14349{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014350 errcode_t retval;
14351 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014352
14353 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
14354 &ctx->mount_flags);
14355 if (retval) {
14356 com_err("ext2fs_check_if_mount", retval,
14357 _("while determining whether %s is mounted."),
14358 ctx->filesystem_name);
14359 return;
14360 }
14361
14362 /*
14363 * If the filesystem isn't mounted, or it's the root filesystem
14364 * and it's mounted read-only, then everything's fine.
14365 */
14366 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
14367 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
14368 (ctx->mount_flags & EXT2_MF_READONLY)))
14369 return;
14370
14371 if (ctx->options & E2F_OPT_READONLY) {
14372 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
14373 return;
14374 }
14375
14376 printf(_("%s is mounted. "), ctx->filesystem_name);
14377 if (!ctx->interactive)
14378 fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
14379 printf(_("\n\n\007\007\007\007WARNING!!! "
14380 "Running e2fsck on a mounted filesystem may cause\n"
14381 "SEVERE filesystem damage.\007\007\007\n\n"));
14382 cont = ask_yn(_("Do you really want to continue"), -1);
14383 if (!cont) {
14384 printf (_("check aborted.\n"));
14385 exit (0);
14386 }
14387 return;
14388}
14389
14390static int is_on_batt(void)
14391{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014392 FILE *f;
14393 DIR *d;
14394 char tmp[80], tmp2[80], fname[80];
14395 unsigned int acflag;
14396 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014397
14398 f = fopen("/proc/apm", "r");
14399 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014400 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014401 acflag = 1;
14402 fclose(f);
14403 return (acflag != 1);
14404 }
14405 d = opendir("/proc/acpi/ac_adapter");
14406 if (d) {
14407 while ((de=readdir(d)) != NULL) {
14408 if (!strncmp(".", de->d_name, 1))
14409 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014410 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000014411 de->d_name);
14412 f = fopen(fname, "r");
14413 if (!f)
14414 continue;
14415 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
14416 tmp[0] = 0;
14417 fclose(f);
14418 if (strncmp(tmp, "off-line", 8) == 0) {
14419 closedir(d);
14420 return 1;
14421 }
14422 }
14423 closedir(d);
14424 }
14425 return 0;
14426}
14427
14428/*
14429 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014430 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000014431 * message explaining why a check is being forced.
14432 */
14433static void check_if_skip(e2fsck_t ctx)
14434{
14435 ext2_filsys fs = ctx->fs;
14436 const char *reason = NULL;
14437 unsigned int reason_arg = 0;
14438 long next_check;
14439 int batt = is_on_batt();
14440 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014441
Mike Frysinger51a43b42005-09-24 07:11:16 +000014442 if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
14443 cflag || swapfs)
14444 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014445
Mike Frysinger51a43b42005-09-24 07:11:16 +000014446 if ((fs->super->s_state & EXT2_ERROR_FS) ||
14447 !ext2fs_test_valid(fs))
14448 reason = _(" contains a file system with errors");
14449 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
14450 reason = _(" was not cleanly unmounted");
14451 else if ((fs->super->s_max_mnt_count > 0) &&
14452 (fs->super->s_mnt_count >=
14453 (unsigned) fs->super->s_max_mnt_count)) {
14454 reason = _(" has been mounted %u times without being checked");
14455 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014456 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000014457 (unsigned) fs->super->s_max_mnt_count*2))
14458 reason = 0;
14459 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014460 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000014461 fs->super->s_checkinterval)) {
14462 reason = _(" has gone %u days without being checked");
14463 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014464 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000014465 fs->super->s_checkinterval*2))
14466 reason = 0;
14467 }
14468 if (reason) {
14469 fputs(ctx->device_name, stdout);
14470 printf(reason, reason_arg);
14471 fputs(_(", check forced.\n"), stdout);
14472 return;
14473 }
14474 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
14475 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
14476 fs->super->s_inodes_count,
14477 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
14478 fs->super->s_blocks_count);
14479 next_check = 100000;
14480 if (fs->super->s_max_mnt_count > 0) {
14481 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014482 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014483 next_check = 1;
14484 }
14485 if (fs->super->s_checkinterval &&
14486 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
14487 next_check = 1;
14488 if (next_check <= 5) {
14489 if (next_check == 1)
14490 fputs(_(" (check after next mount)"), stdout);
14491 else
14492 printf(_(" (check in %ld mounts)"), next_check);
14493 }
14494 fputc('\n', stdout);
14495 ext2fs_close(fs);
14496 ctx->fs = NULL;
14497 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014498 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014499}
14500
14501/*
14502 * For completion notice
14503 */
14504struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014505 int max_pass;
14506 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000014507};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014508static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000014509 5, { 0, 70, 90, 92, 95, 100 }
14510};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014511
Mike Frysinger51a43b42005-09-24 07:11:16 +000014512static char bar[128], spaces[128];
14513
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014514static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014515 int max)
14516{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014517 float percent;
14518
Mike Frysinger51a43b42005-09-24 07:11:16 +000014519 if (pass <= 0)
14520 return 0.0;
14521 if (pass > tbl->max_pass || max == 0)
14522 return 100.0;
14523 percent = ((float) curr) / ((float) max);
14524 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
14525 + tbl->table[pass-1]);
14526}
14527
Rob Landleydfba7412006-03-06 20:47:33 +000014528void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014529{
14530 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
14531 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014532
Mike Frysinger51a43b42005-09-24 07:11:16 +000014533 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
14534 ctx->stop_meta);
14535 fflush(stdout);
14536 ctx->flags &= ~E2F_FLAG_PROG_BAR;
14537}
14538
14539int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
14540 unsigned int dpynum)
14541{
14542 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014543 int i;
14544 unsigned int tick;
14545 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014546 int dpywidth;
14547 int fixed_percent;
14548
14549 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
14550 return 0;
14551
14552 /*
14553 * Calculate the new progress position. If the
14554 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014555 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000014556 */
14557 fixed_percent = (int) ((10 * percent) + 0.5);
14558 if (ctx->progress_last_percent == fixed_percent)
14559 return 0;
14560 ctx->progress_last_percent = fixed_percent;
14561
14562 /*
14563 * If we've already updated the spinner once within
14564 * the last 1/8th of a second, no point doing it
14565 * again.
14566 */
14567 gettimeofday(&tv, NULL);
14568 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
14569 if ((tick == ctx->progress_last_time) &&
14570 (fixed_percent != 0) && (fixed_percent != 1000))
14571 return 0;
14572 ctx->progress_last_time = tick;
14573
14574 /*
14575 * Advance the spinner, and note that the progress bar
14576 * will be on the screen
14577 */
14578 ctx->progress_pos = (ctx->progress_pos+1) & 3;
14579 ctx->flags |= E2F_FLAG_PROG_BAR;
14580
14581 dpywidth = 66 - strlen(label);
14582 dpywidth = 8 * (dpywidth / 8);
14583 if (dpynum)
14584 dpywidth -= 8;
14585
14586 i = ((percent * dpywidth) + 50) / 100;
14587 printf("%s%s: |%s%s", ctx->start_meta, label,
14588 bar + (sizeof(bar) - (i+1)),
14589 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
14590 if (fixed_percent == 1000)
14591 fputc('|', stdout);
14592 else
14593 fputc(spinner[ctx->progress_pos & 3], stdout);
14594 printf(" %4.1f%% ", percent);
14595 if (dpynum)
14596 printf("%u\r", dpynum);
14597 else
14598 fputs(" \r", stdout);
14599 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014600
Mike Frysinger51a43b42005-09-24 07:11:16 +000014601 if (fixed_percent == 1000)
14602 e2fsck_clear_progbar(ctx);
14603 fflush(stdout);
14604
14605 return 0;
14606}
14607
14608static int e2fsck_update_progress(e2fsck_t ctx, int pass,
14609 unsigned long cur, unsigned long max)
14610{
14611 char buf[80];
14612 float percent;
14613
14614 if (pass == 0)
14615 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014616
Mike Frysinger51a43b42005-09-24 07:11:16 +000014617 if (ctx->progress_fd) {
14618 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
14619 write(ctx->progress_fd, buf, strlen(buf));
14620 } else {
14621 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
14622 e2fsck_simple_progress(ctx, ctx->device_name,
14623 percent, 0);
14624 }
14625 return 0;
14626}
14627
Mike Frysinger51a43b42005-09-24 07:11:16 +000014628static void reserve_stdio_fds(void)
14629{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014630 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014631
14632 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000014633 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014634 if (fd > 2)
14635 break;
14636 if (fd < 0) {
14637 fprintf(stderr, _("ERROR: Couldn't open "
14638 "/dev/null (%s)\n"),
14639 strerror(errno));
14640 break;
14641 }
14642 }
14643 close(fd);
14644}
14645
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014646static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014647{
14648 e2fsck_t ctx = e2fsck_global_ctx;
14649
14650 if (!ctx)
14651 return;
14652
14653 ctx->progress = e2fsck_update_progress;
14654 ctx->progress_fd = 0;
14655}
14656
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014657static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014658{
14659 e2fsck_t ctx = e2fsck_global_ctx;
14660
14661 if (!ctx)
14662 return;
14663
14664 e2fsck_clear_progbar(ctx);
14665 ctx->progress = 0;
14666}
14667
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000014668static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000014669{
14670 e2fsck_t ctx = e2fsck_global_ctx;
14671
14672 if (!ctx)
14673 exit(FSCK_CANCELED);
14674
14675 ctx->flags |= E2F_FLAG_CANCEL;
14676}
Mike Frysinger51a43b42005-09-24 07:11:16 +000014677
14678static void parse_extended_opts(e2fsck_t ctx, const char *opts)
14679{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014680 char *buf, *token, *next, *p, *arg;
14681 int ea_ver;
14682 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014683
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014684 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014685 for (token = buf; token && *token; token = next) {
14686 p = strchr(token, ',');
14687 next = 0;
14688 if (p) {
14689 *p = 0;
14690 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014691 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014692 arg = strchr(token, '=');
14693 if (arg) {
14694 *arg = 0;
14695 arg++;
14696 }
14697 if (strcmp(token, "ea_ver") == 0) {
14698 if (!arg) {
14699 extended_usage++;
14700 continue;
14701 }
14702 ea_ver = strtoul(arg, &p, 0);
14703 if (*p ||
14704 ((ea_ver != 1) && (ea_ver != 2))) {
14705 fprintf(stderr,
14706 _("Invalid EA version.\n"));
14707 extended_usage++;
14708 continue;
14709 }
14710 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000014711 } else {
14712 fprintf(stderr, _("Unknown extended option: %s\n"),
14713 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014714 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000014715 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014716 }
14717 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014718 bb_error_msg_and_die(
14719 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000014720 "and may take an argument which\n"
14721 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000014722 "Valid extended options are:\n"
14723 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000014724 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014725}
Mike Frysinger51a43b42005-09-24 07:11:16 +000014726
14727
14728static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
14729{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014730 int flush = 0;
14731 int c, fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014732#ifdef MTRACE
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014733 extern void *mallwatch;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014734#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014735 e2fsck_t ctx;
14736 errcode_t retval;
14737 struct sigaction sa;
14738 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014739
14740 retval = e2fsck_allocate_context(&ctx);
14741 if (retval)
14742 return retval;
14743
14744 *ret_ctx = ctx;
14745
14746 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
14747 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
14748 if (isatty(0) && isatty(1)) {
14749 ctx->interactive = 1;
14750 } else {
14751 ctx->start_meta[0] = '\001';
14752 ctx->stop_meta[0] = '\002';
14753 }
14754 memset(bar, '=', sizeof(bar)-1);
14755 memset(spaces, ' ', sizeof(spaces)-1);
14756 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014757
Mike Frysinger51a43b42005-09-24 07:11:16 +000014758 if (argc && *argv)
14759 ctx->program_name = *argv;
14760 else
14761 ctx->program_name = "e2fsck";
14762 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
14763 switch (c) {
14764 case 'C':
14765 ctx->progress = e2fsck_update_progress;
14766 ctx->progress_fd = atoi(optarg);
14767 if (!ctx->progress_fd)
14768 break;
14769 /* Validate the file descriptor to avoid disasters */
14770 fd = dup(ctx->progress_fd);
14771 if (fd < 0) {
14772 fprintf(stderr,
14773 _("Error validating file descriptor %d: %s\n"),
14774 ctx->progress_fd,
14775 error_message(errno));
14776 fatal_error(ctx,
14777 _("Invalid completion information file descriptor"));
14778 } else
14779 close(fd);
14780 break;
14781 case 'D':
14782 ctx->options |= E2F_OPT_COMPRESS_DIRS;
14783 break;
14784 case 'E':
14785 extended_opts = optarg;
14786 break;
14787 case 'p':
14788 case 'a':
14789 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
14790 conflict_opt:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014791 fatal_error(ctx,
Mike Frysinger51a43b42005-09-24 07:11:16 +000014792 _("Only one the options -p/-a, -n or -y may be specified."));
14793 }
14794 ctx->options |= E2F_OPT_PREEN;
14795 break;
14796 case 'n':
14797 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
14798 goto conflict_opt;
14799 ctx->options |= E2F_OPT_NO;
14800 break;
14801 case 'y':
14802 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
14803 goto conflict_opt;
14804 ctx->options |= E2F_OPT_YES;
14805 break;
14806 case 't':
14807#ifdef RESOURCE_TRACK
14808 if (ctx->options & E2F_OPT_TIME)
14809 ctx->options |= E2F_OPT_TIME2;
14810 else
14811 ctx->options |= E2F_OPT_TIME;
14812#else
14813 fprintf(stderr, _("The -t option is not "
14814 "supported on this version of e2fsck.\n"));
14815#endif
14816 break;
14817 case 'c':
14818 if (cflag++)
14819 ctx->options |= E2F_OPT_WRITECHECK;
14820 ctx->options |= E2F_OPT_CHECKBLOCKS;
14821 break;
14822 case 'r':
14823 /* What we do by default, anyway! */
14824 break;
14825 case 'b':
14826 ctx->use_superblock = atoi(optarg);
14827 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
14828 break;
14829 case 'B':
14830 ctx->blocksize = atoi(optarg);
14831 break;
14832 case 'I':
14833 ctx->inode_buffer_blocks = atoi(optarg);
14834 break;
14835 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014836 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014837 break;
14838 case 'P':
14839 ctx->process_inode_size = atoi(optarg);
14840 break;
14841 case 'L':
14842 replace_bad_blocks++;
14843 case 'l':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014844 bad_blocks_file = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014845 break;
14846 case 'd':
14847 ctx->options |= E2F_OPT_DEBUG;
14848 break;
14849 case 'f':
14850 ctx->options |= E2F_OPT_FORCE;
14851 break;
14852 case 'F':
14853 flush = 1;
14854 break;
14855 case 'v':
14856 verbose = 1;
14857 break;
14858 case 'V':
14859 show_version_only = 1;
14860 break;
14861#ifdef MTRACE
14862 case 'M':
14863 mallwatch = (void *) strtol(optarg, NULL, 0);
14864 break;
14865#endif
14866 case 'N':
14867 ctx->device_name = optarg;
14868 break;
14869#ifdef ENABLE_SWAPFS
14870 case 's':
14871 normalize_swapfs = 1;
14872 case 'S':
14873 swapfs = 1;
14874 break;
14875#else
14876 case 's':
14877 case 'S':
14878 fprintf(stderr, _("Byte-swapping filesystems "
14879 "not compiled in this version "
14880 "of e2fsck\n"));
14881 exit(1);
14882#endif
14883 case 'k':
14884 keep_bad_blocks++;
14885 break;
14886 default:
14887 usage();
14888 }
14889 if (show_version_only)
14890 return 0;
14891 if (optind != argc - 1)
14892 usage();
14893 if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
14894 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
14895 ctx->options |= E2F_OPT_READONLY;
14896 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014897 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000014898 *ctx->io_options++ = 0;
14899 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
14900 if (!ctx->filesystem_name) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014901 com_err(ctx->program_name, 0, _("Unable to resolve '%s'"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000014902 argv[optind]);
14903 fatal_error(ctx, 0);
14904 }
14905 if (extended_opts)
14906 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014907
Mike Frysinger51a43b42005-09-24 07:11:16 +000014908 if (flush) {
14909 fd = open(ctx->filesystem_name, O_RDONLY, 0);
14910 if (fd < 0) {
14911 com_err("open", errno,
14912 _("while opening %s for flushing"),
14913 ctx->filesystem_name);
14914 fatal_error(ctx, 0);
14915 }
14916 if ((retval = ext2fs_sync_device(fd, 1))) {
14917 com_err("ext2fs_sync_device", retval,
14918 _("while trying to flush %s"),
14919 ctx->filesystem_name);
14920 fatal_error(ctx, 0);
14921 }
14922 close(fd);
14923 }
14924#ifdef ENABLE_SWAPFS
14925 if (swapfs) {
14926 if (cflag || bad_blocks_file) {
14927 fprintf(stderr, _("Incompatible options not "
14928 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014929 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014930 }
14931 }
14932#endif
14933 if (cflag && bad_blocks_file) {
14934 fprintf(stderr, _("The -c and the -l/-L options may "
14935 "not be both used at the same time.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014936 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014937 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000014938 /*
14939 * Set up signal action
14940 */
14941 memset(&sa, 0, sizeof(struct sigaction));
14942 sa.sa_handler = signal_cancel;
14943 sigaction(SIGINT, &sa, 0);
14944 sigaction(SIGTERM, &sa, 0);
14945#ifdef SA_RESTART
14946 sa.sa_flags = SA_RESTART;
14947#endif
14948 e2fsck_global_ctx = ctx;
14949 sa.sa_handler = signal_progress_on;
14950 sigaction(SIGUSR1, &sa, 0);
14951 sa.sa_handler = signal_progress_off;
14952 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000014953
14954 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014955 if (cflag)
14956 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000014957#ifdef __CONFIG_JBD_DEBUG__E2FS
14958 if (getenv("E2FSCK_JBD_DEBUG"))
14959 journal_enable_debug = atoi(getenv("E2FSCK_JBD_DEBUG"));
14960#endif
14961 return 0;
14962}
14963
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014964static const char my_ver_string[] = E2FSPROGS_VERSION;
14965static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014966
Mike Frysinger51a43b42005-09-24 07:11:16 +000014967int e2fsck_main (int argc, char *argv[])
14968{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000014969 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000014970 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014971 ext2_filsys fs = 0;
14972 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014973 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014974 const char *lib_ver_date;
14975 int my_ver, lib_ver;
14976 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000014977 struct problem_context pctx;
14978 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000014979
Mike Frysinger51a43b42005-09-24 07:11:16 +000014980 clear_problem_context(&pctx);
14981#ifdef MTRACE
14982 mtrace();
14983#endif
14984#ifdef MCHECK
14985 mcheck(0);
14986#endif
14987#ifdef ENABLE_NLS
14988 setlocale(LC_MESSAGES, "");
14989 setlocale(LC_CTYPE, "");
14990 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
14991 textdomain(NLS_CAT_NAME);
14992#endif
14993 my_ver = ext2fs_parse_version_string(my_ver_string);
14994 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
14995 if (my_ver > lib_ver) {
14996 fprintf( stderr, _("Error: ext2fs library version "
14997 "out of date!\n"));
14998 show_version_only++;
14999 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015000
Mike Frysinger51a43b42005-09-24 07:11:16 +000015001 retval = PRS(argc, argv, &ctx);
15002 if (retval) {
15003 com_err("e2fsck", retval,
15004 _("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015005 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000015006 }
15007 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015008
Mike Frysinger51a43b42005-09-24 07:11:16 +000015009#ifdef RESOURCE_TRACK
15010 init_resource_track(&ctx->global_rtrack);
15011#endif
15012
15013 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
15014 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
15015 my_ver_date);
15016
15017 if (show_version_only) {
15018 fprintf(stderr, _("\tUsing %s, %s\n"),
15019 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015020 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000015021 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015022
Mike Frysinger51a43b42005-09-24 07:11:16 +000015023 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015024
Mike Frysinger51a43b42005-09-24 07:11:16 +000015025 if (!(ctx->options & E2F_OPT_PREEN) &&
15026 !(ctx->options & E2F_OPT_NO) &&
15027 !(ctx->options & E2F_OPT_YES)) {
15028 if (!ctx->interactive)
15029 fatal_error(ctx,
15030 _("need terminal for interactive repairs"));
15031 }
15032 ctx->superblock = ctx->use_superblock;
15033restart:
15034#ifdef CONFIG_TESTIO_DEBUG
15035 io_ptr = test_io_manager;
15036 test_io_backing_manager = unix_io_manager;
15037#else
15038 io_ptr = unix_io_manager;
15039#endif
15040 flags = 0;
15041 if ((ctx->options & E2F_OPT_READONLY) == 0)
15042 flags |= EXT2_FLAG_RW;
15043
15044 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015045 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000015046 flags, ctx->superblock, ctx->blocksize,
15047 io_ptr, &fs);
15048 } else if (ctx->superblock) {
15049 int blocksize;
15050 for (blocksize = EXT2_MIN_BLOCK_SIZE;
15051 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015052 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000015053 ctx->io_options, flags,
15054 ctx->superblock, blocksize,
15055 io_ptr, &fs);
15056 if (!retval)
15057 break;
15058 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015059 } else
15060 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000015061 flags, 0, 0, io_ptr, &fs);
15062 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
15063 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
15064 ((retval == EXT2_ET_BAD_MAGIC) ||
15065 ((retval == 0) && ext2fs_check_desc(fs)))) {
15066 if (!fs || (fs->group_desc_count > 1)) {
15067 printf(_("%s trying backup blocks...\n"),
15068 retval ? _("Couldn't find ext2 superblock,") :
15069 _("Group descriptors look bad..."));
15070 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
15071 if (fs)
15072 ext2fs_close(fs);
15073 goto restart;
15074 }
15075 }
15076 if (retval) {
15077 com_err(ctx->program_name, retval, _("while trying to open %s"),
15078 ctx->filesystem_name);
15079 if (retval == EXT2_ET_REV_TOO_HIGH) {
15080 printf(_("The filesystem revision is apparently "
15081 "too high for this version of e2fsck.\n"
15082 "(Or the filesystem superblock "
15083 "is corrupt)\n\n"));
15084 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
15085 } else if (retval == EXT2_ET_SHORT_READ)
15086 printf(_("Could this be a zero-length partition?\n"));
15087 else if ((retval == EPERM) || (retval == EACCES))
15088 printf(_("You must have %s access to the "
15089 "filesystem or be root\n"),
15090 (ctx->options & E2F_OPT_READONLY) ?
15091 "r/o" : "r/w");
15092 else if (retval == ENXIO)
15093 printf(_("Possibly non-existent or swap device?\n"));
15094#ifdef EROFS
15095 else if (retval == EROFS)
15096 printf(_("Disk write-protected; use the -n option "
15097 "to do a read-only\n"
15098 "check of the device.\n"));
15099#endif
15100 else
15101 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
15102 fatal_error(ctx, 0);
15103 }
15104 ctx->fs = fs;
15105 fs->priv_data = ctx;
15106 sb = fs->super;
15107 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
15108 com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
15109 _("while trying to open %s"),
15110 ctx->filesystem_name);
15111 get_newer:
15112 fatal_error(ctx, _("Get a newer version of e2fsck!"));
15113 }
15114
15115 /*
15116 * Set the device name, which is used whenever we print error
15117 * or informational messages to the user.
15118 */
15119 if (ctx->device_name == 0 &&
15120 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000015121 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000015122 sizeof(sb->s_volume_name));
15123 }
15124 if (ctx->device_name == 0)
15125 ctx->device_name = ctx->filesystem_name;
15126
15127 /*
15128 * Make sure the ext3 superblock fields are consistent.
15129 */
15130 retval = e2fsck_check_ext3_journal(ctx);
15131 if (retval) {
15132 com_err(ctx->program_name, retval,
15133 _("while checking ext3 journal for %s"),
15134 ctx->device_name);
15135 fatal_error(ctx, 0);
15136 }
15137
15138 /*
15139 * Check to see if we need to do ext3-style recovery. If so,
15140 * do it, and then restart the fsck.
15141 */
15142 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
15143 if (ctx->options & E2F_OPT_READONLY) {
15144 printf(_("Warning: skipping journal recovery "
15145 "because doing a read-only filesystem "
15146 "check.\n"));
15147 io_channel_flush(ctx->fs->io);
15148 } else {
15149 if (ctx->flags & E2F_FLAG_RESTARTED) {
15150 /*
15151 * Whoops, we attempted to run the
15152 * journal twice. This should never
15153 * happen, unless the hardware or
15154 * device driver is being bogus.
15155 */
15156 com_err(ctx->program_name, 0,
15157 _("unable to set superblock flags on %s\n"), ctx->device_name);
15158 fatal_error(ctx, 0);
15159 }
15160 retval = e2fsck_run_ext3_journal(ctx);
15161 if (retval) {
15162 com_err(ctx->program_name, retval,
15163 _("while recovering ext3 journal of %s"),
15164 ctx->device_name);
15165 fatal_error(ctx, 0);
15166 }
15167 ext2fs_close(ctx->fs);
15168 ctx->fs = 0;
15169 ctx->flags |= E2F_FLAG_RESTARTED;
15170 goto restart;
15171 }
15172 }
15173
15174 /*
15175 * Check for compatibility with the feature sets. We need to
15176 * be more stringent than ext2fs_open().
15177 */
15178 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
15179 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
15180 com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
15181 "(%s)", ctx->device_name);
15182 goto get_newer;
15183 }
15184 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
15185 com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
15186 "(%s)", ctx->device_name);
15187 goto get_newer;
15188 }
15189#ifdef ENABLE_COMPRESSION
15190 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
15191 com_err(ctx->program_name, 0,
15192 _("Warning: compression support is experimental.\n"));
15193#endif
15194#ifndef ENABLE_HTREE
15195 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
15196 com_err(ctx->program_name, 0,
15197 _("E2fsck not compiled with HTREE support,\n\t"
15198 "but filesystem %s has HTREE directories.\n"),
15199 ctx->device_name);
15200 goto get_newer;
15201 }
15202#endif
15203
15204 /*
15205 * If the user specified a specific superblock, presumably the
15206 * master superblock has been trashed. So we mark the
15207 * superblock as dirty, so it can be written out.
15208 */
15209 if (ctx->superblock &&
15210 !(ctx->options & E2F_OPT_READONLY))
15211 ext2fs_mark_super_dirty(fs);
15212
15213 /*
15214 * We only update the master superblock because (a) paranoia;
15215 * we don't want to corrupt the backup superblocks, and (b) we
15216 * don't need to update the mount count and last checked
15217 * fields in the backup superblock (the kernel doesn't
15218 * update the backup superblocks anyway).
15219 */
15220 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
15221
15222 ehandler_init(fs->io);
15223
15224 if (ctx->superblock)
15225 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
15226 ext2fs_mark_valid(fs);
15227 check_super_block(ctx);
15228 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
15229 fatal_error(ctx, 0);
15230 check_if_skip(ctx);
15231 if (bad_blocks_file)
15232 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
15233 else if (cflag)
15234 read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
15235 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
15236 fatal_error(ctx, 0);
15237#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000015238
15239#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000015240#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000015241#else
Rob Landley8b606342006-01-24 02:38:28 +000015242#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000015243#endif
15244
15245
Mike Frysinger51a43b42005-09-24 07:11:16 +000015246 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000015247 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000015248 fprintf(stderr, _("%s: Filesystem byte order "
15249 "already normalized.\n"), ctx->device_name);
15250 fatal_error(ctx, 0);
15251 }
15252 }
15253 if (swapfs) {
15254 swap_filesys(ctx);
15255 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
15256 fatal_error(ctx, 0);
15257 }
15258#endif
15259
15260 /*
15261 * Mark the system as valid, 'til proven otherwise
15262 */
15263 ext2fs_mark_valid(fs);
15264
15265 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
15266 if (retval) {
15267 com_err(ctx->program_name, retval,
15268 _("while reading bad blocks inode"));
15269 preenhalt(ctx);
15270 printf(_("This doesn't bode well,"
15271 " but we'll try to go on...\n"));
15272 }
15273
15274 run_result = e2fsck_run(ctx);
15275 e2fsck_clear_progbar(ctx);
15276 if (run_result == E2F_FLAG_RESTART) {
15277 printf(_("Restarting e2fsck from the beginning...\n"));
15278 retval = e2fsck_reset_context(ctx);
15279 if (retval) {
15280 com_err(ctx->program_name, retval,
15281 _("while resetting context"));
15282 fatal_error(ctx, 0);
15283 }
15284 ext2fs_close(fs);
15285 goto restart;
15286 }
15287 if (run_result & E2F_FLAG_CANCEL) {
15288 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
15289 ctx->device_name : ctx->filesystem_name);
15290 exit_value |= FSCK_CANCELED;
15291 }
15292 if (run_result & E2F_FLAG_ABORT)
15293 fatal_error(ctx, _("aborted"));
15294
15295#ifdef MTRACE
15296 mtrace_print("Cleanup");
15297#endif
15298 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015299 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000015300 if (!(ctx->options & E2F_OPT_PREEN))
15301 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
15302 ctx->device_name);
15303 if (ctx->mount_flags & EXT2_MF_ISROOT) {
15304 printf(_("%s: ***** REBOOT LINUX *****\n"),
15305 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015306 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000015307 }
15308 }
15309 if (!ext2fs_test_valid(fs)) {
15310 printf(_("\n%s: ********** WARNING: Filesystem still has "
15311 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015312 exit_value |= EXIT_UNCORRECTED;
15313 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000015314 }
15315 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015316 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000015317 else {
15318 show_stats(ctx);
15319 if (!(ctx->options & E2F_OPT_READONLY)) {
15320 if (ext2fs_test_valid(fs)) {
15321 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000015322 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000015323 sb->s_state = EXT2_VALID_FS;
15324 } else
15325 sb->s_state &= ~EXT2_VALID_FS;
15326 sb->s_mnt_count = 0;
15327 sb->s_lastcheck = time(NULL);
15328 ext2fs_mark_super_dirty(fs);
15329 }
15330 }
15331
15332 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015333
Mike Frysinger51a43b42005-09-24 07:11:16 +000015334 ext2fs_close(fs);
15335 ctx->fs = NULL;
15336 free(ctx->filesystem_name);
15337 free(ctx->journal_name);
15338 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000015339
Mike Frysinger51a43b42005-09-24 07:11:16 +000015340#ifdef RESOURCE_TRACK
15341 if (ctx->options & E2F_OPT_TIME)
15342 print_resource_track(NULL, &ctx->global_rtrack);
15343#endif
15344
15345 return exit_value;
15346}