aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2005-09-24 07:11:16 +0000
committerMike Frysinger <vapier@gentoo.org>2005-09-24 07:11:16 +0000
commit51a43b47fefaea46b00a74180a7d0b39022e6d11 (patch)
treeb099fd873abe7e76231ee24fe373dda750c002cf
parentbfe773f471eee7711e19f13df1385208e61c5082 (diff)
downloadbusybox-w32-51a43b47fefaea46b00a74180a7d0b39022e6d11.tar.gz
busybox-w32-51a43b47fefaea46b00a74180a7d0b39022e6d11.tar.bz2
busybox-w32-51a43b47fefaea46b00a74180a7d0b39022e6d11.zip
import the very fat e2fsck/fsck applets
-rw-r--r--e2fsprogs/Config.in17
-rw-r--r--e2fsprogs/Makefile.in27
-rw-r--r--e2fsprogs/e2fsbb.h30
-rw-r--r--e2fsprogs/e2fsck.c1179
-rw-r--r--e2fsprogs/e2fsck/badblocks.c136
-rw-r--r--e2fsprogs/e2fsck/dict.c1519
-rw-r--r--e2fsprogs/e2fsck/dict.h144
-rw-r--r--e2fsprogs/e2fsck/dirinfo.c137
-rw-r--r--e2fsprogs/e2fsck/dx_dirinfo.c150
-rw-r--r--e2fsprogs/e2fsck/e2fsck.c202
-rw-r--r--e2fsprogs/e2fsck/e2fsck.h446
-rw-r--r--e2fsprogs/e2fsck/ea_refcount.c479
-rw-r--r--e2fsprogs/e2fsck/ehandler.c124
-rw-r--r--e2fsprogs/e2fsck/jfs_user.h120
-rw-r--r--e2fsprogs/e2fsck/journal.c960
-rw-r--r--e2fsprogs/e2fsck/message.c466
-rw-r--r--e2fsprogs/e2fsck/pass1.c2122
-rw-r--r--e2fsprogs/e2fsck/pass1b.c805
-rw-r--r--e2fsprogs/e2fsck/pass2.c1414
-rw-r--r--e2fsprogs/e2fsck/pass3.c804
-rw-r--r--e2fsprogs/e2fsck/pass4.c178
-rw-r--r--e2fsprogs/e2fsck/pass5.c547
-rw-r--r--e2fsprogs/e2fsck/problem.c1655
-rw-r--r--e2fsprogs/e2fsck/problem.h897
-rw-r--r--e2fsprogs/e2fsck/problemP.h42
-rw-r--r--e2fsprogs/e2fsck/recovery.c586
-rw-r--r--e2fsprogs/e2fsck/region.c204
-rw-r--r--e2fsprogs/e2fsck/rehash.c840
-rw-r--r--e2fsprogs/e2fsck/revoke.c640
-rw-r--r--e2fsprogs/e2fsck/super.c713
-rw-r--r--e2fsprogs/e2fsck/swapfs.c269
-rw-r--r--e2fsprogs/e2fsck/util.c503
-rw-r--r--e2fsprogs/fsck.c1302
-rw-r--r--e2fsprogs/fsck.h59
-rw-r--r--include/applets.h10
-rw-r--r--include/usage.h34
36 files changed, 19738 insertions, 22 deletions
diff --git a/e2fsprogs/Config.in b/e2fsprogs/Config.in
index 4a564df60..225e5aaaf 100644
--- a/e2fsprogs/Config.in
+++ b/e2fsprogs/Config.in
@@ -11,6 +11,23 @@ config CONFIG_CHATTR
11 help 11 help
12 chattr changes the file attributes on a second extended file system. 12 chattr changes the file attributes on a second extended file system.
13 13
14config CONFIG_E2FSCK
15 bool "e2fsck"
16 default n
17 help
18 e2fsck is used to check Linux second extended file systems (ext2fs).
19 e2fsck also supports ext2 filesystems countaining a journal (ext3).
20 The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
21 provided.
22
23config CONFIG_FSCK
24 bool "fsck"
25 default n
26 help
27 fsck is used to check and optionally repair one or more filesystems.
28 In actuality, fsck is simply a front-end for the various file system
29 checkers (fsck.fstype) available under Linux.
30
14config CONFIG_LSATTR 31config CONFIG_LSATTR
15 bool "lsattr" 32 bool "lsattr"
16 default n 33 default n
diff --git a/e2fsprogs/Makefile.in b/e2fsprogs/Makefile.in
index cd3584975..03a98149e 100644
--- a/e2fsprogs/Makefile.in
+++ b/e2fsprogs/Makefile.in
@@ -6,17 +6,22 @@
6 6
7E2FSPROGS_AR:=e2fsprogs.a 7E2FSPROGS_AR:=e2fsprogs.a
8 8
9E2FSPROGS_DIR:=$(top_builddir)/e2fsprogs/ 9E2FSPROGS_DIR:=$(top_builddir)/e2fsprogs
10 10
11srcdir=$(top_srcdir)/e2fsprogs 11E2FSPROGS_CFLAGS := -I$(E2FSPROGS_DIR) -include $(E2FSPROGS_DIR)/e2fsbb.h
12
13E2FSPROGS_CFLAGS := -I$(srcdir) -include $(srcdir)/e2fsbb.h
14 12
15BLKID_SRC := cache.c dev.c devname.c devno.c blkid_getsize.c \ 13BLKID_SRC := cache.c dev.c devname.c devno.c blkid_getsize.c \
16 probe.c read.c resolve.c save.c tag.c resolve.c 14 probe.c read.c resolve.c save.c tag.c resolve.c
17BLKID_SRCS := $(patsubst %,blkid/%, $(BLKID_SRC)) 15BLKID_SRCS := $(patsubst %,blkid/%, $(BLKID_SRC))
18BLKID_OBJS := $(patsubst %.c,%.o, $(BLKID_SRCS)) 16BLKID_OBJS := $(patsubst %.c,%.o, $(BLKID_SRCS))
19 17
18E2FSCK_SRC := badblocks.c dict.c dirinfo.c dx_dirinfo.c e2fsck.c \
19 ea_refcount.c ehandler.c journal.c message.c pass1.c pass1b.c \
20 pass2.c pass3.c pass4.c pass5.c problem.c recovery.c region.c \
21 rehash.c revoke.c super.c swapfs.c util.c
22E2FSCK_SRCS := $(patsubst %,e2fsck/%, $(E2FSCK_SRC))
23E2FSCK_OBJS := $(patsubst %.c,%.o, $(E2FSCK_SRCS))
24
20E2P_SRC := fgetsetflags.c fgetsetversion.c pf.c iod.c mntopts.c \ 25E2P_SRC := fgetsetflags.c fgetsetversion.c pf.c iod.c mntopts.c \
21 feature.c ls.c uuid.c pe.c ostype.c ps.c hashstr.c \ 26 feature.c ls.c uuid.c pe.c ostype.c ps.c hashstr.c \
22 parse_num.c 27 parse_num.c
@@ -29,7 +34,9 @@ EXT2FS_SRC := gen_bitmap.c bitops.c ismounted.c mkjournal.c unix_io.c \
29 openfs.c io_manager.c finddev.c read_bb.c alloc.c badblocks.c \ 34 openfs.c io_manager.c finddev.c read_bb.c alloc.c badblocks.c \
30 getsize.c getsectsize.c alloc_tables.c read_bb_file.c mkdir.c \ 35 getsize.c getsectsize.c alloc_tables.c read_bb_file.c mkdir.c \
31 bb_inode.c newdir.c alloc_sb.c lookup.c dirblock.c expanddir.c \ 36 bb_inode.c newdir.c alloc_sb.c lookup.c dirblock.c expanddir.c \
32 dir_iterate.c link.c res_gdt.c 37 dir_iterate.c link.c res_gdt.c icount.c get_pathname.c dblist.c \
38 dirhash.c version.c flushb.c unlink.c check_desc.c valid_blk.c \
39 ext_attr.c bmap.c dblist_dir.c
33EXT2FS_SRCS := $(patsubst %,ext2fs/%, $(EXT2FS_SRC)) 40EXT2FS_SRCS := $(patsubst %,ext2fs/%, $(EXT2FS_SRC))
34EXT2FS_OBJS := $(patsubst %.c,%.o, $(EXT2FS_SRCS)) 41EXT2FS_OBJS := $(patsubst %.c,%.o, $(EXT2FS_SRCS))
35 42
@@ -40,15 +47,17 @@ UUID_OBJS := $(patsubst %.c,%.o, $(UUID_SRCS))
40 47
41E2FSPROGS-:= 48E2FSPROGS-:=
42E2FSPROGS-$(CONFIG_CHATTR) += chattr.o $(E2P_OBJS) 49E2FSPROGS-$(CONFIG_CHATTR) += chattr.o $(E2P_OBJS)
50E2FSPROGS-$(CONFIG_E2FSCK) += e2fsck.o $(BLKID_OBJS) $(E2FSCK_OBJS)
51E2FSPROGS-$(CONFIG_FSCK) += fsck.o base_device.o
43E2FSPROGS-$(CONFIG_LSATTR) += lsattr.o $(E2P_OBJS) 52E2FSPROGS-$(CONFIG_LSATTR) += lsattr.o $(E2P_OBJS)
44E2FSPROGS-$(CONFIG_MKE2FS) += mke2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS) 53E2FSPROGS-$(CONFIG_MKE2FS) += mke2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
45E2FSPROGS-$(CONFIG_TUNE2FS) += tune2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS) 54E2FSPROGS-$(CONFIG_TUNE2FS) += tune2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
46 55
47libraries-y+=$(E2FSPROGS_DIR)$(E2FSPROGS_AR) 56libraries-y+=$(E2FSPROGS_DIR)/$(E2FSPROGS_AR)
48 57
49 58
50$(E2FSPROGS_DIR)$(E2FSPROGS_AR): $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y)) 59$(E2FSPROGS_DIR)/$(E2FSPROGS_AR): $(patsubst %,$(E2FSPROGS_DIR)/%, $(E2FSPROGS-y))
51 $(AR) $(ARFLAGS) $@ $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y)) 60 $(AR) $(ARFLAGS) $@ $(patsubst %,$(E2FSPROGS_DIR)/%, $(E2FSPROGS-y))
52 61
53$(E2FSPROGS_DIR)%.o: $(srcdir)/%.c 62$(E2FSPROGS_DIR)/%.o: $(E2FSPROGS_DIR)/%.c
54 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(E2FSPROGS_CFLAGS) -c -o $@ $< 63 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(E2FSPROGS_CFLAGS) -c -o $@ $<
diff --git a/e2fsprogs/e2fsbb.h b/e2fsprogs/e2fsbb.h
index f728125ad..45079df81 100644
--- a/e2fsprogs/e2fsbb.h
+++ b/e2fsprogs/e2fsbb.h
@@ -32,29 +32,33 @@ typedef long errcode_t;
32#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural)) 32#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
33 33
34/* misc crap */ 34/* misc crap */
35#define fatal_error(msg, err) bb_error_msg_and_die(msg) 35#define fatal_error(err, msg) bb_error_msg_and_die(msg)
36#define usage() bb_show_usage() 36#define usage() bb_show_usage()
37#define perror(msg) bb_perror_msg(msg) 37#define perror(msg) bb_perror_msg(msg)
38 38
39/* header defines */ 39/* header defines */
40#define ENABLE_HTREE 1
41#define HAVE_DIRENT_H 1
40#define HAVE_ERRNO_H 1 42#define HAVE_ERRNO_H 1
41#define HAVE_UNISTD_H 1
42#define HAVE_EXT2_IOCTLS 1 43#define HAVE_EXT2_IOCTLS 1
44#define HAVE_GETOPT_H 1
45#define HAVE_INTTYPES_H 1
46#define HAVE_LINUX_FD_H 1
47#define HAVE_MALLOC_H 1
48#define HAVE_MNTENT_H 1
49#define HAVE_NETINET_IN_H 1
50#define HAVE_NET_IF_H 1
51#define HAVE_SETJMP_H 1
52#define HAVE_SIGNAL_H 1
43#define HAVE_STDLIB_H 1 53#define HAVE_STDLIB_H 1
44#define HAVE_SYS_STAT_H 1
45#define HAVE_SYS_TYPES_H 1
46#define HAVE_UNISTD_H 1
47#define HAVE_SYS_IOCTL_H 1 54#define HAVE_SYS_IOCTL_H 1
55#define HAVE_SYS_MOUNT_H 1
48#define HAVE_SYS_QUEUE_H 1 56#define HAVE_SYS_QUEUE_H 1
49#define HAVE_SYS_SOCKET_H 1
50#define HAVE_NETINET_IN_H 1
51#define HAVE_INTTYPES_H 1
52#define HAVE_NET_IF_H 1
53#define HAVE_GETOPT_H 1
54#define HAVE_SYS_RESOURCE_H 1 57#define HAVE_SYS_RESOURCE_H 1
55#define HAVE_SYS_MOUNT_H 1 58#define HAVE_SYS_SOCKET_H 1
59#define HAVE_SYS_STAT_H 1
56#define HAVE_SYS_TIME_H 1 60#define HAVE_SYS_TIME_H 1
57#define HAVE_LINUX_FD_H 1 61#define HAVE_SYS_TYPES_H 1
58#define HAVE_MNTENT_H 1 62#define HAVE_UNISTD_H 1
59 63
60#endif /* __E2FSBB_H__ */ 64#endif /* __E2FSBB_H__ */
diff --git a/e2fsprogs/e2fsck.c b/e2fsprogs/e2fsck.c
new file mode 100644
index 000000000..f1adc8549
--- /dev/null
+++ b/e2fsprogs/e2fsck.c
@@ -0,0 +1,1179 @@
1/*
2 * unix.c - The unix-specific code for e2fsck
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <fcntl.h>
16#include <ctype.h>
17#include <time.h>
18#ifdef HAVE_SIGNAL_H
19#include <signal.h>
20#endif
21#ifdef HAVE_GETOPT_H
22#include <getopt.h>
23#else
24extern char *optarg;
25extern int optind;
26#endif
27#include <unistd.h>
28#ifdef HAVE_ERRNO_H
29#include <errno.h>
30#endif
31#ifdef HAVE_MNTENT_H
32#include <mntent.h>
33#endif
34#ifdef HAVE_SYS_IOCTL_H
35#include <sys/ioctl.h>
36#endif
37#ifdef HAVE_MALLOC_H
38#include <malloc.h>
39#endif
40#ifdef HAVE_SYS_TYPES_H
41#include <sys/types.h>
42#endif
43#ifdef HAVE_DIRENT_H
44#include <dirent.h>
45#endif
46
47#include "e2fsbb.h"
48#include "et/com_err.h"
49#include "e2fsck/e2fsck.h"
50#include "e2fsck/problem.h"
51//#include "../version.h"
52
53/* Command line options */
54static int swapfs;
55#ifdef ENABLE_SWAPFS
56static int normalize_swapfs;
57#endif
58static int cflag; /* check disk */
59static int show_version_only;
60static int verbose;
61
62static int replace_bad_blocks;
63static int keep_bad_blocks;
64static char *bad_blocks_file;
65
66e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
67
68#ifdef __CONFIG_JBD_DEBUG__E2FS /* Enabled by configure --enable-jfs-debug */
69int journal_enable_debug = -1;
70#endif
71
72#if 0
73static void usage(e2fsck_t ctx)
74{
75 fprintf(stderr,
76 _("Usage: %s [-panyrcdfvstDFSV] [-b superblock] [-B blocksize]\n"
77 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
78 "\t\t[-l|-L bad_blocks_file] [-C fd] [-j ext-journal]\n"
79 "\t\t[-E extended-options] device\n"),
80 ctx->program_name);
81
82 fprintf(stderr, _("\nEmergency help:\n"
83 " -p Automatic repair (no questions)\n"
84 " -n Make no changes to the filesystem\n"
85 " -y Assume \"yes\" to all questions\n"
86 " -c Check for bad blocks and add them to the badblock list\n"
87 " -f Force checking even if filesystem is marked clean\n"));
88 fprintf(stderr, _(""
89 " -v Be verbose\n"
90 " -b superblock Use alternative superblock\n"
91 " -B blocksize Force blocksize when looking for superblock\n"
92 " -j external-journal Set location of the external journal\n"
93 " -l bad_blocks_file Add to badblocks list\n"
94 " -L bad_blocks_file Set badblocks list\n"
95 ));
96
97 exit(FSCK_USAGE);
98}
99#endif
100
101static void show_stats(e2fsck_t ctx)
102{
103 ext2_filsys fs = ctx->fs;
104 int inodes, inodes_used, blocks, blocks_used;
105 int dir_links;
106 int num_files, num_links;
107 int frag_percent;
108
109 dir_links = 2 * ctx->fs_directory_count - 1;
110 num_files = ctx->fs_total_count - dir_links;
111 num_links = ctx->fs_links_count - dir_links;
112 inodes = fs->super->s_inodes_count;
113 inodes_used = (fs->super->s_inodes_count -
114 fs->super->s_free_inodes_count);
115 blocks = fs->super->s_blocks_count;
116 blocks_used = (fs->super->s_blocks_count -
117 fs->super->s_free_blocks_count);
118
119 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
120 frag_percent = (frag_percent + 5) / 10;
121
122 if (!verbose) {
123 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
124 ctx->device_name, inodes_used, inodes,
125 frag_percent / 10, frag_percent % 10,
126 blocks_used, blocks);
127 return;
128 }
129 printf (P_("\n%8d inode used (%d%%)\n", "\n%8d inodes used (%d%%)\n",
130 inodes_used), inodes_used, 100 * inodes_used / inodes);
131 printf (P_("%8d non-contiguous inode (%0d.%d%%)\n",
132 "%8d non-contiguous inodes (%0d.%d%%)\n",
133 ctx->fs_fragmented),
134 ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
135 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
136 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
137 printf (P_("%8d block used (%d%%)\n", "%8d blocks used (%d%%)\n",
138 blocks_used),
139 blocks_used, (int) ((long long) 100 * blocks_used / blocks));
140 printf (P_("%8d bad block\n", "%8d bad blocks\n",
141 ctx->fs_badblocks_count), ctx->fs_badblocks_count);
142 printf (P_("%8d large file\n", "%8d large files\n",
143 ctx->large_files), ctx->large_files);
144 printf (P_("\n%8d regular file\n", "\n%8d regular files\n",
145 ctx->fs_regular_count), ctx->fs_regular_count);
146 printf (P_("%8d directory\n", "%8d directories\n",
147 ctx->fs_directory_count), ctx->fs_directory_count);
148 printf (P_("%8d character device file\n",
149 "%8d character device files\n", ctx->fs_chardev_count),
150 ctx->fs_chardev_count);
151 printf (P_("%8d block device file\n", "%8d block device files\n",
152 ctx->fs_blockdev_count), ctx->fs_blockdev_count);
153 printf (P_("%8d fifo\n", "%8d fifos\n", ctx->fs_fifo_count),
154 ctx->fs_fifo_count);
155 printf (P_("%8d link\n", "%8d links\n",
156 ctx->fs_links_count - dir_links),
157 ctx->fs_links_count - dir_links);
158 printf (P_("%8d symbolic link", "%8d symbolic links",
159 ctx->fs_symlinks_count), ctx->fs_symlinks_count);
160 printf (P_(" (%d fast symbolic link)\n", " (%d fast symbolic links)\n",
161 ctx->fs_fast_symlinks_count), ctx->fs_fast_symlinks_count);
162 printf (P_("%8d socket\n", "%8d sockets\n", ctx->fs_sockets_count),
163 ctx->fs_sockets_count);
164 printf ("--------\n");
165 printf (P_("%8d file\n", "%8d files\n",
166 ctx->fs_total_count - dir_links),
167 ctx->fs_total_count - dir_links);
168}
169
170static void check_mount(e2fsck_t ctx)
171{
172 errcode_t retval;
173 int cont;
174
175 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
176 &ctx->mount_flags);
177 if (retval) {
178 com_err("ext2fs_check_if_mount", retval,
179 _("while determining whether %s is mounted."),
180 ctx->filesystem_name);
181 return;
182 }
183
184 /*
185 * If the filesystem isn't mounted, or it's the root filesystem
186 * and it's mounted read-only, then everything's fine.
187 */
188 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
189 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
190 (ctx->mount_flags & EXT2_MF_READONLY)))
191 return;
192
193 if (ctx->options & E2F_OPT_READONLY) {
194 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
195 return;
196 }
197
198 printf(_("%s is mounted. "), ctx->filesystem_name);
199 if (!ctx->interactive)
200 fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
201 printf(_("\n\n\007\007\007\007WARNING!!! "
202 "Running e2fsck on a mounted filesystem may cause\n"
203 "SEVERE filesystem damage.\007\007\007\n\n"));
204 cont = ask_yn(_("Do you really want to continue"), -1);
205 if (!cont) {
206 printf (_("check aborted.\n"));
207 exit (0);
208 }
209 return;
210}
211
212static int is_on_batt(void)
213{
214 FILE *f;
215 DIR *d;
216 char tmp[80], tmp2[80], fname[80];
217 unsigned int acflag;
218 struct dirent* de;
219
220 f = fopen("/proc/apm", "r");
221 if (f) {
222 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
223 acflag = 1;
224 fclose(f);
225 return (acflag != 1);
226 }
227 d = opendir("/proc/acpi/ac_adapter");
228 if (d) {
229 while ((de=readdir(d)) != NULL) {
230 if (!strncmp(".", de->d_name, 1))
231 continue;
232 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
233 de->d_name);
234 f = fopen(fname, "r");
235 if (!f)
236 continue;
237 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
238 tmp[0] = 0;
239 fclose(f);
240 if (strncmp(tmp, "off-line", 8) == 0) {
241 closedir(d);
242 return 1;
243 }
244 }
245 closedir(d);
246 }
247 return 0;
248}
249
250/*
251 * This routine checks to see if a filesystem can be skipped; if so,
252 * it will exit with E2FSCK_OK. Under some conditions it will print a
253 * message explaining why a check is being forced.
254 */
255static void check_if_skip(e2fsck_t ctx)
256{
257 ext2_filsys fs = ctx->fs;
258 const char *reason = NULL;
259 unsigned int reason_arg = 0;
260 long next_check;
261 int batt = is_on_batt();
262 time_t now = time(0);
263
264 if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
265 cflag || swapfs)
266 return;
267
268 if ((fs->super->s_state & EXT2_ERROR_FS) ||
269 !ext2fs_test_valid(fs))
270 reason = _(" contains a file system with errors");
271 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
272 reason = _(" was not cleanly unmounted");
273 else if ((fs->super->s_max_mnt_count > 0) &&
274 (fs->super->s_mnt_count >=
275 (unsigned) fs->super->s_max_mnt_count)) {
276 reason = _(" has been mounted %u times without being checked");
277 reason_arg = fs->super->s_mnt_count;
278 if (batt && (fs->super->s_mnt_count <
279 (unsigned) fs->super->s_max_mnt_count*2))
280 reason = 0;
281 } else if (fs->super->s_checkinterval &&
282 ((now - fs->super->s_lastcheck) >=
283 fs->super->s_checkinterval)) {
284 reason = _(" has gone %u days without being checked");
285 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
286 if (batt && ((now - fs->super->s_lastcheck) <
287 fs->super->s_checkinterval*2))
288 reason = 0;
289 }
290 if (reason) {
291 fputs(ctx->device_name, stdout);
292 printf(reason, reason_arg);
293 fputs(_(", check forced.\n"), stdout);
294 return;
295 }
296 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
297 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
298 fs->super->s_inodes_count,
299 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
300 fs->super->s_blocks_count);
301 next_check = 100000;
302 if (fs->super->s_max_mnt_count > 0) {
303 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
304 if (next_check <= 0)
305 next_check = 1;
306 }
307 if (fs->super->s_checkinterval &&
308 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
309 next_check = 1;
310 if (next_check <= 5) {
311 if (next_check == 1)
312 fputs(_(" (check after next mount)"), stdout);
313 else
314 printf(_(" (check in %ld mounts)"), next_check);
315 }
316 fputc('\n', stdout);
317 ext2fs_close(fs);
318 ctx->fs = NULL;
319 e2fsck_free_context(ctx);
320 exit(FSCK_OK);
321}
322
323/*
324 * For completion notice
325 */
326struct percent_tbl {
327 int max_pass;
328 int table[32];
329};
330struct percent_tbl e2fsck_tbl = {
331 5, { 0, 70, 90, 92, 95, 100 }
332};
333static char bar[128], spaces[128];
334
335static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
336 int max)
337{
338 float percent;
339
340 if (pass <= 0)
341 return 0.0;
342 if (pass > tbl->max_pass || max == 0)
343 return 100.0;
344 percent = ((float) curr) / ((float) max);
345 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
346 + tbl->table[pass-1]);
347}
348
349extern void e2fsck_clear_progbar(e2fsck_t ctx)
350{
351 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
352 return;
353
354 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
355 ctx->stop_meta);
356 fflush(stdout);
357 ctx->flags &= ~E2F_FLAG_PROG_BAR;
358}
359
360int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
361 unsigned int dpynum)
362{
363 static const char spinner[] = "\\|/-";
364 int i;
365 unsigned int tick;
366 struct timeval tv;
367 int dpywidth;
368 int fixed_percent;
369
370 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
371 return 0;
372
373 /*
374 * Calculate the new progress position. If the
375 * percentage hasn't changed, then we skip out right
376 * away.
377 */
378 fixed_percent = (int) ((10 * percent) + 0.5);
379 if (ctx->progress_last_percent == fixed_percent)
380 return 0;
381 ctx->progress_last_percent = fixed_percent;
382
383 /*
384 * If we've already updated the spinner once within
385 * the last 1/8th of a second, no point doing it
386 * again.
387 */
388 gettimeofday(&tv, NULL);
389 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
390 if ((tick == ctx->progress_last_time) &&
391 (fixed_percent != 0) && (fixed_percent != 1000))
392 return 0;
393 ctx->progress_last_time = tick;
394
395 /*
396 * Advance the spinner, and note that the progress bar
397 * will be on the screen
398 */
399 ctx->progress_pos = (ctx->progress_pos+1) & 3;
400 ctx->flags |= E2F_FLAG_PROG_BAR;
401
402 dpywidth = 66 - strlen(label);
403 dpywidth = 8 * (dpywidth / 8);
404 if (dpynum)
405 dpywidth -= 8;
406
407 i = ((percent * dpywidth) + 50) / 100;
408 printf("%s%s: |%s%s", ctx->start_meta, label,
409 bar + (sizeof(bar) - (i+1)),
410 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
411 if (fixed_percent == 1000)
412 fputc('|', stdout);
413 else
414 fputc(spinner[ctx->progress_pos & 3], stdout);
415 printf(" %4.1f%% ", percent);
416 if (dpynum)
417 printf("%u\r", dpynum);
418 else
419 fputs(" \r", stdout);
420 fputs(ctx->stop_meta, stdout);
421
422 if (fixed_percent == 1000)
423 e2fsck_clear_progbar(ctx);
424 fflush(stdout);
425
426 return 0;
427}
428
429static int e2fsck_update_progress(e2fsck_t ctx, int pass,
430 unsigned long cur, unsigned long max)
431{
432 char buf[80];
433 float percent;
434
435 if (pass == 0)
436 return 0;
437
438 if (ctx->progress_fd) {
439 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
440 write(ctx->progress_fd, buf, strlen(buf));
441 } else {
442 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
443 e2fsck_simple_progress(ctx, ctx->device_name,
444 percent, 0);
445 }
446 return 0;
447}
448
449#define PATH_SET "PATH=/sbin"
450
451static void reserve_stdio_fds(void)
452{
453 int fd;
454
455 while (1) {
456 fd = open("/dev/null", O_RDWR);
457 if (fd > 2)
458 break;
459 if (fd < 0) {
460 fprintf(stderr, _("ERROR: Couldn't open "
461 "/dev/null (%s)\n"),
462 strerror(errno));
463 break;
464 }
465 }
466 close(fd);
467}
468
469#ifdef HAVE_SIGNAL_H
470static void signal_progress_on(int sig EXT2FS_ATTR((unused)))
471{
472 e2fsck_t ctx = e2fsck_global_ctx;
473
474 if (!ctx)
475 return;
476
477 ctx->progress = e2fsck_update_progress;
478 ctx->progress_fd = 0;
479}
480
481static void signal_progress_off(int sig EXT2FS_ATTR((unused)))
482{
483 e2fsck_t ctx = e2fsck_global_ctx;
484
485 if (!ctx)
486 return;
487
488 e2fsck_clear_progbar(ctx);
489 ctx->progress = 0;
490}
491
492static void signal_cancel(int sig EXT2FS_ATTR((unused)))
493{
494 e2fsck_t ctx = e2fsck_global_ctx;
495
496 if (!ctx)
497 exit(FSCK_CANCELED);
498
499 ctx->flags |= E2F_FLAG_CANCEL;
500}
501#endif
502
503static void parse_extended_opts(e2fsck_t ctx, const char *opts)
504{
505 char *buf, *token, *next, *p, *arg;
506 int ea_ver;
507 int extended_usage = 0;
508
509 buf = string_copy(ctx, opts, 0);
510 for (token = buf; token && *token; token = next) {
511 p = strchr(token, ',');
512 next = 0;
513 if (p) {
514 *p = 0;
515 next = p+1;
516 }
517 arg = strchr(token, '=');
518 if (arg) {
519 *arg = 0;
520 arg++;
521 }
522 if (strcmp(token, "ea_ver") == 0) {
523 if (!arg) {
524 extended_usage++;
525 continue;
526 }
527 ea_ver = strtoul(arg, &p, 0);
528 if (*p ||
529 ((ea_ver != 1) && (ea_ver != 2))) {
530 fprintf(stderr,
531 _("Invalid EA version.\n"));
532 extended_usage++;
533 continue;
534 }
535 ctx->ext_attr_ver = ea_ver;
536 } else
537 extended_usage++;
538 }
539 if (extended_usage) {
540 fprintf(stderr, _("Extended options are separated by commas, "
541 "and may take an argument which\n"
542 "is set off by an equals ('=') sign. "
543 "Valid raid options are:\n"
544 "\tea_ver=<ea_version (1 or 2)\n\n"));
545 exit(1);
546 }
547}
548
549
550static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
551{
552 int flush = 0;
553 int c, fd;
554#ifdef MTRACE
555 extern void *mallwatch;
556#endif
557 e2fsck_t ctx;
558 errcode_t retval;
559#ifdef HAVE_SIGNAL_H
560 struct sigaction sa;
561#endif
562 char *extended_opts = 0;
563
564 retval = e2fsck_allocate_context(&ctx);
565 if (retval)
566 return retval;
567
568 *ret_ctx = ctx;
569
570 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
571 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
572 if (isatty(0) && isatty(1)) {
573 ctx->interactive = 1;
574 } else {
575 ctx->start_meta[0] = '\001';
576 ctx->stop_meta[0] = '\002';
577 }
578 memset(bar, '=', sizeof(bar)-1);
579 memset(spaces, ' ', sizeof(spaces)-1);
580 blkid_get_cache(&ctx->blkid, NULL);
581
582 if (argc && *argv)
583 ctx->program_name = *argv;
584 else
585 ctx->program_name = "e2fsck";
586 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
587 switch (c) {
588 case 'C':
589 ctx->progress = e2fsck_update_progress;
590 ctx->progress_fd = atoi(optarg);
591 if (!ctx->progress_fd)
592 break;
593 /* Validate the file descriptor to avoid disasters */
594 fd = dup(ctx->progress_fd);
595 if (fd < 0) {
596 fprintf(stderr,
597 _("Error validating file descriptor %d: %s\n"),
598 ctx->progress_fd,
599 error_message(errno));
600 fatal_error(ctx,
601 _("Invalid completion information file descriptor"));
602 } else
603 close(fd);
604 break;
605 case 'D':
606 ctx->options |= E2F_OPT_COMPRESS_DIRS;
607 break;
608 case 'E':
609 extended_opts = optarg;
610 break;
611 case 'p':
612 case 'a':
613 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
614 conflict_opt:
615 fatal_error(ctx,
616 _("Only one the options -p/-a, -n or -y may be specified."));
617 }
618 ctx->options |= E2F_OPT_PREEN;
619 break;
620 case 'n':
621 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
622 goto conflict_opt;
623 ctx->options |= E2F_OPT_NO;
624 break;
625 case 'y':
626 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
627 goto conflict_opt;
628 ctx->options |= E2F_OPT_YES;
629 break;
630 case 't':
631#ifdef RESOURCE_TRACK
632 if (ctx->options & E2F_OPT_TIME)
633 ctx->options |= E2F_OPT_TIME2;
634 else
635 ctx->options |= E2F_OPT_TIME;
636#else
637 fprintf(stderr, _("The -t option is not "
638 "supported on this version of e2fsck.\n"));
639#endif
640 break;
641 case 'c':
642 if (cflag++)
643 ctx->options |= E2F_OPT_WRITECHECK;
644 ctx->options |= E2F_OPT_CHECKBLOCKS;
645 break;
646 case 'r':
647 /* What we do by default, anyway! */
648 break;
649 case 'b':
650 ctx->use_superblock = atoi(optarg);
651 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
652 break;
653 case 'B':
654 ctx->blocksize = atoi(optarg);
655 break;
656 case 'I':
657 ctx->inode_buffer_blocks = atoi(optarg);
658 break;
659 case 'j':
660 ctx->journal_name = string_copy(ctx, optarg, 0);
661 break;
662 case 'P':
663 ctx->process_inode_size = atoi(optarg);
664 break;
665 case 'L':
666 replace_bad_blocks++;
667 case 'l':
668 bad_blocks_file = string_copy(ctx, optarg, 0);
669 break;
670 case 'd':
671 ctx->options |= E2F_OPT_DEBUG;
672 break;
673 case 'f':
674 ctx->options |= E2F_OPT_FORCE;
675 break;
676 case 'F':
677 flush = 1;
678 break;
679 case 'v':
680 verbose = 1;
681 break;
682 case 'V':
683 show_version_only = 1;
684 break;
685#ifdef MTRACE
686 case 'M':
687 mallwatch = (void *) strtol(optarg, NULL, 0);
688 break;
689#endif
690 case 'N':
691 ctx->device_name = optarg;
692 break;
693#ifdef ENABLE_SWAPFS
694 case 's':
695 normalize_swapfs = 1;
696 case 'S':
697 swapfs = 1;
698 break;
699#else
700 case 's':
701 case 'S':
702 fprintf(stderr, _("Byte-swapping filesystems "
703 "not compiled in this version "
704 "of e2fsck\n"));
705 exit(1);
706#endif
707 case 'k':
708 keep_bad_blocks++;
709 break;
710 default:
711 usage();
712 }
713 if (show_version_only)
714 return 0;
715 if (optind != argc - 1)
716 usage();
717 if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
718 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
719 ctx->options |= E2F_OPT_READONLY;
720 ctx->io_options = strchr(argv[optind], '?');
721 if (ctx->io_options)
722 *ctx->io_options++ = 0;
723 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
724 if (!ctx->filesystem_name) {
725 com_err(ctx->program_name, 0, _("Unable to resolve '%s'"),
726 argv[optind]);
727 fatal_error(ctx, 0);
728 }
729 if (extended_opts)
730 parse_extended_opts(ctx, extended_opts);
731
732 if (flush) {
733 fd = open(ctx->filesystem_name, O_RDONLY, 0);
734 if (fd < 0) {
735 com_err("open", errno,
736 _("while opening %s for flushing"),
737 ctx->filesystem_name);
738 fatal_error(ctx, 0);
739 }
740 if ((retval = ext2fs_sync_device(fd, 1))) {
741 com_err("ext2fs_sync_device", retval,
742 _("while trying to flush %s"),
743 ctx->filesystem_name);
744 fatal_error(ctx, 0);
745 }
746 close(fd);
747 }
748#ifdef ENABLE_SWAPFS
749 if (swapfs) {
750 if (cflag || bad_blocks_file) {
751 fprintf(stderr, _("Incompatible options not "
752 "allowed when byte-swapping.\n"));
753 exit(FSCK_USAGE);
754 }
755 }
756#endif
757 if (cflag && bad_blocks_file) {
758 fprintf(stderr, _("The -c and the -l/-L options may "
759 "not be both used at the same time.\n"));
760 exit(FSCK_USAGE);
761 }
762#ifdef HAVE_SIGNAL_H
763 /*
764 * Set up signal action
765 */
766 memset(&sa, 0, sizeof(struct sigaction));
767 sa.sa_handler = signal_cancel;
768 sigaction(SIGINT, &sa, 0);
769 sigaction(SIGTERM, &sa, 0);
770#ifdef SA_RESTART
771 sa.sa_flags = SA_RESTART;
772#endif
773 e2fsck_global_ctx = ctx;
774 sa.sa_handler = signal_progress_on;
775 sigaction(SIGUSR1, &sa, 0);
776 sa.sa_handler = signal_progress_off;
777 sigaction(SIGUSR2, &sa, 0);
778#endif
779
780 /* Update our PATH to include /sbin if we need to run badblocks */
781 if (cflag) {
782 char *oldpath = getenv("PATH");
783 if (oldpath) {
784 char *newpath;
785
786 newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
787 strlen (oldpath));
788 if (!newpath)
789 fatal_error(ctx, "Couldn't malloc() newpath");
790 strcpy (newpath, PATH_SET);
791 strcat (newpath, ":");
792 strcat (newpath, oldpath);
793 putenv (newpath);
794 } else
795 putenv (PATH_SET);
796 }
797#ifdef __CONFIG_JBD_DEBUG__E2FS
798 if (getenv("E2FSCK_JBD_DEBUG"))
799 journal_enable_debug = atoi(getenv("E2FSCK_JBD_DEBUG"));
800#endif
801 return 0;
802}
803
804static const char *my_ver_string = E2FSPROGS_VERSION;
805static const char *my_ver_date = E2FSPROGS_DATE;
806
807int e2fsck_main (int argc, char *argv[])
808{
809 errcode_t retval = 0;
810 int exit_value = FSCK_OK;
811 ext2_filsys fs = 0;
812 io_manager io_ptr;
813 struct ext2_super_block *sb;
814 const char *lib_ver_date;
815 int my_ver, lib_ver;
816 e2fsck_t ctx;
817 struct problem_context pctx;
818 int flags, run_result;
819
820 clear_problem_context(&pctx);
821#ifdef MTRACE
822 mtrace();
823#endif
824#ifdef MCHECK
825 mcheck(0);
826#endif
827#ifdef ENABLE_NLS
828 setlocale(LC_MESSAGES, "");
829 setlocale(LC_CTYPE, "");
830 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
831 textdomain(NLS_CAT_NAME);
832#endif
833 my_ver = ext2fs_parse_version_string(my_ver_string);
834 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
835 if (my_ver > lib_ver) {
836 fprintf( stderr, _("Error: ext2fs library version "
837 "out of date!\n"));
838 show_version_only++;
839 }
840
841 retval = PRS(argc, argv, &ctx);
842 if (retval) {
843 com_err("e2fsck", retval,
844 _("while trying to initialize program"));
845 exit(FSCK_ERROR);
846 }
847 reserve_stdio_fds();
848
849#ifdef RESOURCE_TRACK
850 init_resource_track(&ctx->global_rtrack);
851#endif
852
853 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
854 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
855 my_ver_date);
856
857 if (show_version_only) {
858 fprintf(stderr, _("\tUsing %s, %s\n"),
859 error_message(EXT2_ET_BASE), lib_ver_date);
860 exit(FSCK_OK);
861 }
862
863 check_mount(ctx);
864
865 if (!(ctx->options & E2F_OPT_PREEN) &&
866 !(ctx->options & E2F_OPT_NO) &&
867 !(ctx->options & E2F_OPT_YES)) {
868 if (!ctx->interactive)
869 fatal_error(ctx,
870 _("need terminal for interactive repairs"));
871 }
872 ctx->superblock = ctx->use_superblock;
873restart:
874#ifdef CONFIG_TESTIO_DEBUG
875 io_ptr = test_io_manager;
876 test_io_backing_manager = unix_io_manager;
877#else
878 io_ptr = unix_io_manager;
879#endif
880 flags = 0;
881 if ((ctx->options & E2F_OPT_READONLY) == 0)
882 flags |= EXT2_FLAG_RW;
883
884 if (ctx->superblock && ctx->blocksize) {
885 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
886 flags, ctx->superblock, ctx->blocksize,
887 io_ptr, &fs);
888 } else if (ctx->superblock) {
889 int blocksize;
890 for (blocksize = EXT2_MIN_BLOCK_SIZE;
891 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
892 retval = ext2fs_open2(ctx->filesystem_name,
893 ctx->io_options, flags,
894 ctx->superblock, blocksize,
895 io_ptr, &fs);
896 if (!retval)
897 break;
898 }
899 } else
900 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
901 flags, 0, 0, io_ptr, &fs);
902 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
903 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
904 ((retval == EXT2_ET_BAD_MAGIC) ||
905 ((retval == 0) && ext2fs_check_desc(fs)))) {
906 if (!fs || (fs->group_desc_count > 1)) {
907 printf(_("%s trying backup blocks...\n"),
908 retval ? _("Couldn't find ext2 superblock,") :
909 _("Group descriptors look bad..."));
910 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
911 if (fs)
912 ext2fs_close(fs);
913 goto restart;
914 }
915 }
916 if (retval) {
917 com_err(ctx->program_name, retval, _("while trying to open %s"),
918 ctx->filesystem_name);
919 if (retval == EXT2_ET_REV_TOO_HIGH) {
920 printf(_("The filesystem revision is apparently "
921 "too high for this version of e2fsck.\n"
922 "(Or the filesystem superblock "
923 "is corrupt)\n\n"));
924 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
925 } else if (retval == EXT2_ET_SHORT_READ)
926 printf(_("Could this be a zero-length partition?\n"));
927 else if ((retval == EPERM) || (retval == EACCES))
928 printf(_("You must have %s access to the "
929 "filesystem or be root\n"),
930 (ctx->options & E2F_OPT_READONLY) ?
931 "r/o" : "r/w");
932 else if (retval == ENXIO)
933 printf(_("Possibly non-existent or swap device?\n"));
934#ifdef EROFS
935 else if (retval == EROFS)
936 printf(_("Disk write-protected; use the -n option "
937 "to do a read-only\n"
938 "check of the device.\n"));
939#endif
940 else
941 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
942 fatal_error(ctx, 0);
943 }
944 ctx->fs = fs;
945 fs->priv_data = ctx;
946 sb = fs->super;
947 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
948 com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
949 _("while trying to open %s"),
950 ctx->filesystem_name);
951 get_newer:
952 fatal_error(ctx, _("Get a newer version of e2fsck!"));
953 }
954
955 /*
956 * Set the device name, which is used whenever we print error
957 * or informational messages to the user.
958 */
959 if (ctx->device_name == 0 &&
960 (sb->s_volume_name[0] != 0)) {
961 ctx->device_name = string_copy(ctx, sb->s_volume_name,
962 sizeof(sb->s_volume_name));
963 }
964 if (ctx->device_name == 0)
965 ctx->device_name = ctx->filesystem_name;
966
967 /*
968 * Make sure the ext3 superblock fields are consistent.
969 */
970 retval = e2fsck_check_ext3_journal(ctx);
971 if (retval) {
972 com_err(ctx->program_name, retval,
973 _("while checking ext3 journal for %s"),
974 ctx->device_name);
975 fatal_error(ctx, 0);
976 }
977
978 /*
979 * Check to see if we need to do ext3-style recovery. If so,
980 * do it, and then restart the fsck.
981 */
982 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
983 if (ctx->options & E2F_OPT_READONLY) {
984 printf(_("Warning: skipping journal recovery "
985 "because doing a read-only filesystem "
986 "check.\n"));
987 io_channel_flush(ctx->fs->io);
988 } else {
989 if (ctx->flags & E2F_FLAG_RESTARTED) {
990 /*
991 * Whoops, we attempted to run the
992 * journal twice. This should never
993 * happen, unless the hardware or
994 * device driver is being bogus.
995 */
996 com_err(ctx->program_name, 0,
997 _("unable to set superblock flags on %s\n"), ctx->device_name);
998 fatal_error(ctx, 0);
999 }
1000 retval = e2fsck_run_ext3_journal(ctx);
1001 if (retval) {
1002 com_err(ctx->program_name, retval,
1003 _("while recovering ext3 journal of %s"),
1004 ctx->device_name);
1005 fatal_error(ctx, 0);
1006 }
1007 ext2fs_close(ctx->fs);
1008 ctx->fs = 0;
1009 ctx->flags |= E2F_FLAG_RESTARTED;
1010 goto restart;
1011 }
1012 }
1013
1014 /*
1015 * Check for compatibility with the feature sets. We need to
1016 * be more stringent than ext2fs_open().
1017 */
1018 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
1019 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
1020 com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
1021 "(%s)", ctx->device_name);
1022 goto get_newer;
1023 }
1024 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
1025 com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
1026 "(%s)", ctx->device_name);
1027 goto get_newer;
1028 }
1029#ifdef ENABLE_COMPRESSION
1030 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
1031 com_err(ctx->program_name, 0,
1032 _("Warning: compression support is experimental.\n"));
1033#endif
1034#ifndef ENABLE_HTREE
1035 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
1036 com_err(ctx->program_name, 0,
1037 _("E2fsck not compiled with HTREE support,\n\t"
1038 "but filesystem %s has HTREE directories.\n"),
1039 ctx->device_name);
1040 goto get_newer;
1041 }
1042#endif
1043
1044 /*
1045 * If the user specified a specific superblock, presumably the
1046 * master superblock has been trashed. So we mark the
1047 * superblock as dirty, so it can be written out.
1048 */
1049 if (ctx->superblock &&
1050 !(ctx->options & E2F_OPT_READONLY))
1051 ext2fs_mark_super_dirty(fs);
1052
1053 /*
1054 * We only update the master superblock because (a) paranoia;
1055 * we don't want to corrupt the backup superblocks, and (b) we
1056 * don't need to update the mount count and last checked
1057 * fields in the backup superblock (the kernel doesn't
1058 * update the backup superblocks anyway).
1059 */
1060 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
1061
1062 ehandler_init(fs->io);
1063
1064 if (ctx->superblock)
1065 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
1066 ext2fs_mark_valid(fs);
1067 check_super_block(ctx);
1068 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1069 fatal_error(ctx, 0);
1070 check_if_skip(ctx);
1071 if (bad_blocks_file)
1072 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
1073 else if (cflag)
1074 read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
1075 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1076 fatal_error(ctx, 0);
1077#ifdef ENABLE_SWAPFS
1078 if (normalize_swapfs) {
1079 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
1080 ext2fs_native_flag()) {
1081 fprintf(stderr, _("%s: Filesystem byte order "
1082 "already normalized.\n"), ctx->device_name);
1083 fatal_error(ctx, 0);
1084 }
1085 }
1086 if (swapfs) {
1087 swap_filesys(ctx);
1088 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1089 fatal_error(ctx, 0);
1090 }
1091#endif
1092
1093 /*
1094 * Mark the system as valid, 'til proven otherwise
1095 */
1096 ext2fs_mark_valid(fs);
1097
1098 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
1099 if (retval) {
1100 com_err(ctx->program_name, retval,
1101 _("while reading bad blocks inode"));
1102 preenhalt(ctx);
1103 printf(_("This doesn't bode well,"
1104 " but we'll try to go on...\n"));
1105 }
1106
1107 run_result = e2fsck_run(ctx);
1108 e2fsck_clear_progbar(ctx);
1109 if (run_result == E2F_FLAG_RESTART) {
1110 printf(_("Restarting e2fsck from the beginning...\n"));
1111 retval = e2fsck_reset_context(ctx);
1112 if (retval) {
1113 com_err(ctx->program_name, retval,
1114 _("while resetting context"));
1115 fatal_error(ctx, 0);
1116 }
1117 ext2fs_close(fs);
1118 goto restart;
1119 }
1120 if (run_result & E2F_FLAG_CANCEL) {
1121 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
1122 ctx->device_name : ctx->filesystem_name);
1123 exit_value |= FSCK_CANCELED;
1124 }
1125 if (run_result & E2F_FLAG_ABORT)
1126 fatal_error(ctx, _("aborted"));
1127
1128#ifdef MTRACE
1129 mtrace_print("Cleanup");
1130#endif
1131 if (ext2fs_test_changed(fs)) {
1132 exit_value |= FSCK_NONDESTRUCT;
1133 if (!(ctx->options & E2F_OPT_PREEN))
1134 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
1135 ctx->device_name);
1136 if (ctx->mount_flags & EXT2_MF_ISROOT) {
1137 printf(_("%s: ***** REBOOT LINUX *****\n"),
1138 ctx->device_name);
1139 exit_value |= FSCK_REBOOT;
1140 }
1141 }
1142 if (!ext2fs_test_valid(fs)) {
1143 printf(_("\n%s: ********** WARNING: Filesystem still has "
1144 "errors **********\n\n"), ctx->device_name);
1145 exit_value |= FSCK_UNCORRECTED;
1146 exit_value &= ~FSCK_NONDESTRUCT;
1147 }
1148 if (exit_value & FSCK_CANCELED)
1149 exit_value &= ~FSCK_NONDESTRUCT;
1150 else {
1151 show_stats(ctx);
1152 if (!(ctx->options & E2F_OPT_READONLY)) {
1153 if (ext2fs_test_valid(fs)) {
1154 if (!(sb->s_state & EXT2_VALID_FS))
1155 exit_value |= FSCK_NONDESTRUCT;
1156 sb->s_state = EXT2_VALID_FS;
1157 } else
1158 sb->s_state &= ~EXT2_VALID_FS;
1159 sb->s_mnt_count = 0;
1160 sb->s_lastcheck = time(NULL);
1161 ext2fs_mark_super_dirty(fs);
1162 }
1163 }
1164
1165 e2fsck_write_bitmaps(ctx);
1166
1167 ext2fs_close(fs);
1168 ctx->fs = NULL;
1169 free(ctx->filesystem_name);
1170 free(ctx->journal_name);
1171 e2fsck_free_context(ctx);
1172
1173#ifdef RESOURCE_TRACK
1174 if (ctx->options & E2F_OPT_TIME)
1175 print_resource_track(NULL, &ctx->global_rtrack);
1176#endif
1177
1178 return exit_value;
1179}
diff --git a/e2fsprogs/e2fsck/badblocks.c b/e2fsprogs/e2fsck/badblocks.c
new file mode 100644
index 000000000..09ef3ab1b
--- /dev/null
+++ b/e2fsprogs/e2fsck/badblocks.c
@@ -0,0 +1,136 @@
1/*
2 * badblocks.c --- replace/append bad blocks to the bad block inode
3 *
4 * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be
5 * redistributed under the terms of the GNU Public License.
6 */
7
8#include <time.h>
9#ifdef HAVE_ERRNO_H
10#include <errno.h>
11#endif
12
13#include <et/com_err.h>
14#include "e2fsck.h"
15
16static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
17 void *priv_data);
18
19
20static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
21{
22 printf(_("Bad block %u out of range; ignored.\n"), blk);
23 return;
24}
25
26void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
27 int replace_bad_blocks)
28{
29 ext2_filsys fs = ctx->fs;
30 errcode_t retval;
31 badblocks_list bb_list = 0;
32 FILE *f;
33 char buf[1024];
34
35 e2fsck_read_bitmaps(ctx);
36
37 /*
38 * Make sure the bad block inode is sane. If there are any
39 * illegal blocks, clear them.
40 */
41 retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
42 check_bb_inode_blocks, 0);
43 if (retval) {
44 com_err("ext2fs_block_iterate", retval,
45 _("while sanity checking the bad blocks inode"));
46 goto fatal;
47 }
48
49 /*
50 * If we're appending to the bad blocks inode, read in the
51 * current bad blocks.
52 */
53 if (!replace_bad_blocks) {
54 retval = ext2fs_read_bb_inode(fs, &bb_list);
55 if (retval) {
56 com_err("ext2fs_read_bb_inode", retval,
57 _("while reading the bad blocks inode"));
58 goto fatal;
59 }
60 }
61
62 /*
63 * Now read in the bad blocks from the file; if
64 * bad_blocks_file is null, then try to run the badblocks
65 * command.
66 */
67 if (bad_blocks_file) {
68 f = fopen(bad_blocks_file, "r");
69 if (!f) {
70 com_err("read_bad_blocks_file", errno,
71 _("while trying to open %s"), bad_blocks_file);
72 goto fatal;
73 }
74 } else {
75 sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
76 (ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
77 (ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
78 fs->device_name, fs->super->s_blocks_count);
79 f = popen(buf, "r");
80 if (!f) {
81 com_err("read_bad_blocks_file", errno,
82 _("while trying popen '%s'"), buf);
83 goto fatal;
84 }
85 }
86 retval = ext2fs_read_bb_FILE(fs, f, &bb_list, invalid_block);
87 if (bad_blocks_file)
88 fclose(f);
89 else
90 pclose(f);
91 if (retval) {
92 com_err("ext2fs_read_bb_FILE", retval,
93 _("while reading in list of bad blocks from file"));
94 goto fatal;
95 }
96
97 /*
98 * Finally, update the bad blocks from the bad_block_map
99 */
100 retval = ext2fs_update_bb_inode(fs, bb_list);
101 if (retval) {
102 com_err("ext2fs_update_bb_inode", retval,
103 _("while updating bad block inode"));
104 goto fatal;
105 }
106
107 ext2fs_badblocks_list_free(bb_list);
108 return;
109
110fatal:
111 ctx->flags |= E2F_FLAG_ABORT;
112 return;
113
114}
115
116static int check_bb_inode_blocks(ext2_filsys fs,
117 blk_t *block_nr,
118 int blockcnt EXT2FS_ATTR((unused)),
119 void *priv_data EXT2FS_ATTR((unused)))
120{
121 if (!*block_nr)
122 return 0;
123
124 /*
125 * If the block number is outrageous, clear it and ignore it.
126 */
127 if (*block_nr >= fs->super->s_blocks_count ||
128 *block_nr < fs->super->s_first_data_block) {
129 printf(_("Warning illegal block %u found in bad block inode. Cleared.\n"), *block_nr);
130 *block_nr = 0;
131 return BLOCK_CHANGED;
132 }
133
134 return 0;
135}
136
diff --git a/e2fsprogs/e2fsck/dict.c b/e2fsprogs/e2fsck/dict.c
new file mode 100644
index 000000000..6aabc1b9e
--- /dev/null
+++ b/e2fsprogs/e2fsck/dict.c
@@ -0,0 +1,1519 @@
1/*
2 * Dictionary Abstract Data Type
3 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
4 *
5 * Free Software License:
6 *
7 * All rights are reserved by the author, with the following exceptions:
8 * Permission is granted to freely reproduce and distribute this software,
9 * possibly in exchange for a fee, provided that this copyright notice appears
10 * intact. Permission is also granted to adapt this software to produce
11 * derivative works, as long as the modified versions carry this copyright
12 * notice and additional notices stating that the work has been modified.
13 * This source code may be translated into executable form and incorporated
14 * into proprietary software; there is no requirement for such software to
15 * contain a copyright notice related to this source.
16 *
17 * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $
18 * $Name: kazlib_1_20 $
19 */
20
21#ifdef __GNUC__
22#define EXT2FS_ATTR(x) __attribute__(x)
23#else
24#define EXT2FS_ATTR(x)
25#endif
26
27#include <stdlib.h>
28#include <stddef.h>
29#include <assert.h>
30#define DICT_IMPLEMENTATION
31#include "dict.h"
32
33#ifdef KAZLIB_RCSID
34static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $";
35#endif
36
37/*
38 * These macros provide short convenient names for structure members,
39 * which are embellished with dict_ prefixes so that they are
40 * properly confined to the documented namespace. It's legal for a
41 * program which uses dict to define, for instance, a macro called ``parent''.
42 * Such a macro would interfere with the dnode_t struct definition.
43 * In general, highly portable and reusable C modules which expose their
44 * structures need to confine structure member names to well-defined spaces.
45 * The resulting identifiers aren't necessarily convenient to use, nor
46 * readable, in the implementation, however!
47 */
48
49#define left dict_left
50#define right dict_right
51#define parent dict_parent
52#define color dict_color
53#define key dict_key
54#define data dict_data
55
56#define nilnode dict_nilnode
57#define nodecount dict_nodecount
58#define maxcount dict_maxcount
59#define compare dict_compare
60#define allocnode dict_allocnode
61#define freenode dict_freenode
62#define context dict_context
63#define dupes dict_dupes
64
65#define dictptr dict_dictptr
66
67#define dict_root(D) ((D)->nilnode.left)
68#define dict_nil(D) (&(D)->nilnode)
69#define DICT_DEPTH_MAX 64
70
71static dnode_t *dnode_alloc(void *context);
72static void dnode_free(dnode_t *node, void *context);
73
74/*
75 * Perform a ``left rotation'' adjustment on the tree. The given node P and
76 * its right child C are rearranged so that the P instead becomes the left
77 * child of C. The left subtree of C is inherited as the new right subtree
78 * for P. The ordering of the keys within the tree is thus preserved.
79 */
80
81static void rotate_left(dnode_t *upper)
82{
83 dnode_t *lower, *lowleft, *upparent;
84
85 lower = upper->right;
86 upper->right = lowleft = lower->left;
87 lowleft->parent = upper;
88
89 lower->parent = upparent = upper->parent;
90
91 /* don't need to check for root node here because root->parent is
92 the sentinel nil node, and root->parent->left points back to root */
93
94 if (upper == upparent->left) {
95 upparent->left = lower;
96 } else {
97 assert (upper == upparent->right);
98 upparent->right = lower;
99 }
100
101 lower->left = upper;
102 upper->parent = lower;
103}
104
105/*
106 * This operation is the ``mirror'' image of rotate_left. It is
107 * the same procedure, but with left and right interchanged.
108 */
109
110static void rotate_right(dnode_t *upper)
111{
112 dnode_t *lower, *lowright, *upparent;
113
114 lower = upper->left;
115 upper->left = lowright = lower->right;
116 lowright->parent = upper;
117
118 lower->parent = upparent = upper->parent;
119
120 if (upper == upparent->right) {
121 upparent->right = lower;
122 } else {
123 assert (upper == upparent->left);
124 upparent->left = lower;
125 }
126
127 lower->right = upper;
128 upper->parent = lower;
129}
130
131/*
132 * Do a postorder traversal of the tree rooted at the specified
133 * node and free everything under it. Used by dict_free().
134 */
135
136static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
137{
138 if (node == nil)
139 return;
140 free_nodes(dict, node->left, nil);
141 free_nodes(dict, node->right, nil);
142 dict->freenode(node, dict->context);
143}
144
145/*
146 * This procedure performs a verification that the given subtree is a binary
147 * search tree. It performs an inorder traversal of the tree using the
148 * dict_next() successor function, verifying that the key of each node is
149 * strictly lower than that of its successor, if duplicates are not allowed,
150 * or lower or equal if duplicates are allowed. This function is used for
151 * debugging purposes.
152 */
153#ifndef NDEBUG
154static int verify_bintree(dict_t *dict)
155{
156 dnode_t *first, *next;
157
158 first = dict_first(dict);
159
160 if (dict->dupes) {
161 while (first && (next = dict_next(dict, first))) {
162 if (dict->compare(first->key, next->key) > 0)
163 return 0;
164 first = next;
165 }
166 } else {
167 while (first && (next = dict_next(dict, first))) {
168 if (dict->compare(first->key, next->key) >= 0)
169 return 0;
170 first = next;
171 }
172 }
173 return 1;
174}
175
176/*
177 * This function recursively verifies that the given binary subtree satisfies
178 * three of the red black properties. It checks that every red node has only
179 * black children. It makes sure that each node is either red or black. And it
180 * checks that every path has the same count of black nodes from root to leaf.
181 * It returns the blackheight of the given subtree; this allows blackheights to
182 * be computed recursively and compared for left and right siblings for
183 * mismatches. It does not check for every nil node being black, because there
184 * is only one sentinel nil node. The return value of this function is the
185 * black height of the subtree rooted at the node ``root'', or zero if the
186 * subtree is not red-black.
187 */
188
189static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
190{
191 unsigned height_left, height_right;
192
193 if (root != nil) {
194 height_left = verify_redblack(nil, root->left);
195 height_right = verify_redblack(nil, root->right);
196 if (height_left == 0 || height_right == 0)
197 return 0;
198 if (height_left != height_right)
199 return 0;
200 if (root->color == dnode_red) {
201 if (root->left->color != dnode_black)
202 return 0;
203 if (root->right->color != dnode_black)
204 return 0;
205 return height_left;
206 }
207 if (root->color != dnode_black)
208 return 0;
209 return height_left + 1;
210 }
211 return 1;
212}
213
214/*
215 * Compute the actual count of nodes by traversing the tree and
216 * return it. This could be compared against the stored count to
217 * detect a mismatch.
218 */
219
220static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
221{
222 if (root == nil)
223 return 0;
224 else
225 return 1 + verify_node_count(nil, root->left)
226 + verify_node_count(nil, root->right);
227}
228#endif
229
230/*
231 * Verify that the tree contains the given node. This is done by
232 * traversing all of the nodes and comparing their pointers to the
233 * given pointer. Returns 1 if the node is found, otherwise
234 * returns zero. It is intended for debugging purposes.
235 */
236
237static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
238{
239 if (root != nil) {
240 return root == node
241 || verify_dict_has_node(nil, root->left, node)
242 || verify_dict_has_node(nil, root->right, node);
243 }
244 return 0;
245}
246
247
248#ifdef E2FSCK_NOTUSED
249/*
250 * Dynamically allocate and initialize a dictionary object.
251 */
252
253dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
254{
255 dict_t *new = malloc(sizeof *new);
256
257 if (new) {
258 new->compare = comp;
259 new->allocnode = dnode_alloc;
260 new->freenode = dnode_free;
261 new->context = NULL;
262 new->nodecount = 0;
263 new->maxcount = maxcount;
264 new->nilnode.left = &new->nilnode;
265 new->nilnode.right = &new->nilnode;
266 new->nilnode.parent = &new->nilnode;
267 new->nilnode.color = dnode_black;
268 new->dupes = 0;
269 }
270 return new;
271}
272#endif /* E2FSCK_NOTUSED */
273
274/*
275 * Select a different set of node allocator routines.
276 */
277
278void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
279 dnode_free_t fr, void *context)
280{
281 assert (dict_count(dict) == 0);
282 assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
283
284 dict->allocnode = al ? al : dnode_alloc;
285 dict->freenode = fr ? fr : dnode_free;
286 dict->context = context;
287}
288
289#ifdef E2FSCK_NOTUSED
290/*
291 * Free a dynamically allocated dictionary object. Removing the nodes
292 * from the tree before deleting it is required.
293 */
294
295void dict_destroy(dict_t *dict)
296{
297 assert (dict_isempty(dict));
298 free(dict);
299}
300#endif
301
302/*
303 * Free all the nodes in the dictionary by using the dictionary's
304 * installed free routine. The dictionary is emptied.
305 */
306
307void dict_free_nodes(dict_t *dict)
308{
309 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
310 free_nodes(dict, root, nil);
311 dict->nodecount = 0;
312 dict->nilnode.left = &dict->nilnode;
313 dict->nilnode.right = &dict->nilnode;
314}
315
316#ifdef E2FSCK_NOTUSED
317/*
318 * Obsolescent function, equivalent to dict_free_nodes
319 */
320void dict_free(dict_t *dict)
321{
322#ifdef KAZLIB_OBSOLESCENT_DEBUG
323 assert ("call to obsolescent function dict_free()" && 0);
324#endif
325 dict_free_nodes(dict);
326}
327#endif
328
329/*
330 * Initialize a user-supplied dictionary object.
331 */
332
333dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
334{
335 dict->compare = comp;
336 dict->allocnode = dnode_alloc;
337 dict->freenode = dnode_free;
338 dict->context = NULL;
339 dict->nodecount = 0;
340 dict->maxcount = maxcount;
341 dict->nilnode.left = &dict->nilnode;
342 dict->nilnode.right = &dict->nilnode;
343 dict->nilnode.parent = &dict->nilnode;
344 dict->nilnode.color = dnode_black;
345 dict->dupes = 0;
346 return dict;
347}
348
349#ifdef E2FSCK_NOTUSED
350/*
351 * Initialize a dictionary in the likeness of another dictionary
352 */
353
354void dict_init_like(dict_t *dict, const dict_t *template)
355{
356 dict->compare = template->compare;
357 dict->allocnode = template->allocnode;
358 dict->freenode = template->freenode;
359 dict->context = template->context;
360 dict->nodecount = 0;
361 dict->maxcount = template->maxcount;
362 dict->nilnode.left = &dict->nilnode;
363 dict->nilnode.right = &dict->nilnode;
364 dict->nilnode.parent = &dict->nilnode;
365 dict->nilnode.color = dnode_black;
366 dict->dupes = template->dupes;
367
368 assert (dict_similar(dict, template));
369}
370
371/*
372 * Remove all nodes from the dictionary (without freeing them in any way).
373 */
374
375static void dict_clear(dict_t *dict)
376{
377 dict->nodecount = 0;
378 dict->nilnode.left = &dict->nilnode;
379 dict->nilnode.right = &dict->nilnode;
380 dict->nilnode.parent = &dict->nilnode;
381 assert (dict->nilnode.color == dnode_black);
382}
383
384
385/*
386 * Verify the integrity of the dictionary structure. This is provided for
387 * debugging purposes, and should be placed in assert statements. Just because
388 * this function succeeds doesn't mean that the tree is not corrupt. Certain
389 * corruptions in the tree may simply cause undefined behavior.
390 */
391
392int dict_verify(dict_t *dict)
393{
394#ifndef NDEBUG
395 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
396
397 /* check that the sentinel node and root node are black */
398 if (root->color != dnode_black)
399 return 0;
400 if (nil->color != dnode_black)
401 return 0;
402 if (nil->right != nil)
403 return 0;
404 /* nil->left is the root node; check that its parent pointer is nil */
405 if (nil->left->parent != nil)
406 return 0;
407 /* perform a weak test that the tree is a binary search tree */
408 if (!verify_bintree(dict))
409 return 0;
410 /* verify that the tree is a red-black tree */
411 if (!verify_redblack(nil, root))
412 return 0;
413 if (verify_node_count(nil, root) != dict_count(dict))
414 return 0;
415#endif
416 return 1;
417}
418
419/*
420 * Determine whether two dictionaries are similar: have the same comparison and
421 * allocator functions, and same status as to whether duplicates are allowed.
422 */
423
424int dict_similar(const dict_t *left, const dict_t *right)
425{
426 if (left->compare != right->compare)
427 return 0;
428
429 if (left->allocnode != right->allocnode)
430 return 0;
431
432 if (left->freenode != right->freenode)
433 return 0;
434
435 if (left->context != right->context)
436 return 0;
437
438 if (left->dupes != right->dupes)
439 return 0;
440
441 return 1;
442}
443#endif /* E2FSCK_NOTUSED */
444
445/*
446 * Locate a node in the dictionary having the given key.
447 * If the node is not found, a null a pointer is returned (rather than
448 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
449 * located node is returned.
450 */
451
452dnode_t *dict_lookup(dict_t *dict, const void *key)
453{
454 dnode_t *root = dict_root(dict);
455 dnode_t *nil = dict_nil(dict);
456 dnode_t *saved;
457 int result;
458
459 /* simple binary search adapted for trees that contain duplicate keys */
460
461 while (root != nil) {
462 result = dict->compare(key, root->key);
463 if (result < 0)
464 root = root->left;
465 else if (result > 0)
466 root = root->right;
467 else {
468 if (!dict->dupes) { /* no duplicates, return match */
469 return root;
470 } else { /* could be dupes, find leftmost one */
471 do {
472 saved = root;
473 root = root->left;
474 while (root != nil && dict->compare(key, root->key))
475 root = root->right;
476 } while (root != nil);
477 return saved;
478 }
479 }
480 }
481
482 return NULL;
483}
484
485#ifdef E2FSCK_NOTUSED
486/*
487 * Look for the node corresponding to the lowest key that is equal to or
488 * greater than the given key. If there is no such node, return null.
489 */
490
491dnode_t *dict_lower_bound(dict_t *dict, const void *key)
492{
493 dnode_t *root = dict_root(dict);
494 dnode_t *nil = dict_nil(dict);
495 dnode_t *tentative = 0;
496
497 while (root != nil) {
498 int result = dict->compare(key, root->key);
499
500 if (result > 0) {
501 root = root->right;
502 } else if (result < 0) {
503 tentative = root;
504 root = root->left;
505 } else {
506 if (!dict->dupes) {
507 return root;
508 } else {
509 tentative = root;
510 root = root->left;
511 }
512 }
513 }
514
515 return tentative;
516}
517
518/*
519 * Look for the node corresponding to the greatest key that is equal to or
520 * lower than the given key. If there is no such node, return null.
521 */
522
523dnode_t *dict_upper_bound(dict_t *dict, const void *key)
524{
525 dnode_t *root = dict_root(dict);
526 dnode_t *nil = dict_nil(dict);
527 dnode_t *tentative = 0;
528
529 while (root != nil) {
530 int result = dict->compare(key, root->key);
531
532 if (result < 0) {
533 root = root->left;
534 } else if (result > 0) {
535 tentative = root;
536 root = root->right;
537 } else {
538 if (!dict->dupes) {
539 return root;
540 } else {
541 tentative = root;
542 root = root->right;
543 }
544 }
545 }
546
547 return tentative;
548}
549#endif
550
551/*
552 * Insert a node into the dictionary. The node should have been
553 * initialized with a data field. All other fields are ignored.
554 * The behavior is undefined if the user attempts to insert into
555 * a dictionary that is already full (for which the dict_isfull()
556 * function returns true).
557 */
558
559void dict_insert(dict_t *dict, dnode_t *node, const void *key)
560{
561 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
562 dnode_t *parent = nil, *uncle, *grandpa;
563 int result = -1;
564
565 node->key = key;
566
567 assert (!dict_isfull(dict));
568 assert (!dict_contains(dict, node));
569 assert (!dnode_is_in_a_dict(node));
570
571 /* basic binary tree insert */
572
573 while (where != nil) {
574 parent = where;
575 result = dict->compare(key, where->key);
576 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
577 assert (dict->dupes || result != 0);
578 if (result < 0)
579 where = where->left;
580 else
581 where = where->right;
582 }
583
584 assert (where == nil);
585
586 if (result < 0)
587 parent->left = node;
588 else
589 parent->right = node;
590
591 node->parent = parent;
592 node->left = nil;
593 node->right = nil;
594
595 dict->nodecount++;
596
597 /* red black adjustments */
598
599 node->color = dnode_red;
600
601 while (parent->color == dnode_red) {
602 grandpa = parent->parent;
603 if (parent == grandpa->left) {
604 uncle = grandpa->right;
605 if (uncle->color == dnode_red) { /* red parent, red uncle */
606 parent->color = dnode_black;
607 uncle->color = dnode_black;
608 grandpa->color = dnode_red;
609 node = grandpa;
610 parent = grandpa->parent;
611 } else { /* red parent, black uncle */
612 if (node == parent->right) {
613 rotate_left(parent);
614 parent = node;
615 assert (grandpa == parent->parent);
616 /* rotation between parent and child preserves grandpa */
617 }
618 parent->color = dnode_black;
619 grandpa->color = dnode_red;
620 rotate_right(grandpa);
621 break;
622 }
623 } else { /* symmetric cases: parent == parent->parent->right */
624 uncle = grandpa->left;
625 if (uncle->color == dnode_red) {
626 parent->color = dnode_black;
627 uncle->color = dnode_black;
628 grandpa->color = dnode_red;
629 node = grandpa;
630 parent = grandpa->parent;
631 } else {
632 if (node == parent->left) {
633 rotate_right(parent);
634 parent = node;
635 assert (grandpa == parent->parent);
636 }
637 parent->color = dnode_black;
638 grandpa->color = dnode_red;
639 rotate_left(grandpa);
640 break;
641 }
642 }
643 }
644
645 dict_root(dict)->color = dnode_black;
646
647 assert (dict_verify(dict));
648}
649
650#ifdef E2FSCK_NOTUSED
651/*
652 * Delete the given node from the dictionary. If the given node does not belong
653 * to the given dictionary, undefined behavior results. A pointer to the
654 * deleted node is returned.
655 */
656
657dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
658{
659 dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
660
661 /* basic deletion */
662
663 assert (!dict_isempty(dict));
664 assert (dict_contains(dict, delete));
665
666 /*
667 * If the node being deleted has two children, then we replace it with its
668 * successor (i.e. the leftmost node in the right subtree.) By doing this,
669 * we avoid the traditional algorithm under which the successor's key and
670 * value *only* move to the deleted node and the successor is spliced out
671 * from the tree. We cannot use this approach because the user may hold
672 * pointers to the successor, or nodes may be inextricably tied to some
673 * other structures by way of embedding, etc. So we must splice out the
674 * node we are given, not some other node, and must not move contents from
675 * one node to another behind the user's back.
676 */
677
678 if (delete->left != nil && delete->right != nil) {
679 dnode_t *next = dict_next(dict, delete);
680 dnode_t *nextparent = next->parent;
681 dnode_color_t nextcolor = next->color;
682
683 assert (next != nil);
684 assert (next->parent != nil);
685 assert (next->left == nil);
686
687 /*
688 * First, splice out the successor from the tree completely, by
689 * moving up its right child into its place.
690 */
691
692 child = next->right;
693 child->parent = nextparent;
694
695 if (nextparent->left == next) {
696 nextparent->left = child;
697 } else {
698 assert (nextparent->right == next);
699 nextparent->right = child;
700 }
701
702 /*
703 * Now that the successor has been extricated from the tree, install it
704 * in place of the node that we want deleted.
705 */
706
707 next->parent = delparent;
708 next->left = delete->left;
709 next->right = delete->right;
710 next->left->parent = next;
711 next->right->parent = next;
712 next->color = delete->color;
713 delete->color = nextcolor;
714
715 if (delparent->left == delete) {
716 delparent->left = next;
717 } else {
718 assert (delparent->right == delete);
719 delparent->right = next;
720 }
721
722 } else {
723 assert (delete != nil);
724 assert (delete->left == nil || delete->right == nil);
725
726 child = (delete->left != nil) ? delete->left : delete->right;
727
728 child->parent = delparent = delete->parent;
729
730 if (delete == delparent->left) {
731 delparent->left = child;
732 } else {
733 assert (delete == delparent->right);
734 delparent->right = child;
735 }
736 }
737
738 delete->parent = NULL;
739 delete->right = NULL;
740 delete->left = NULL;
741
742 dict->nodecount--;
743
744 assert (verify_bintree(dict));
745
746 /* red-black adjustments */
747
748 if (delete->color == dnode_black) {
749 dnode_t *parent, *sister;
750
751 dict_root(dict)->color = dnode_red;
752
753 while (child->color == dnode_black) {
754 parent = child->parent;
755 if (child == parent->left) {
756 sister = parent->right;
757 assert (sister != nil);
758 if (sister->color == dnode_red) {
759 sister->color = dnode_black;
760 parent->color = dnode_red;
761 rotate_left(parent);
762 sister = parent->right;
763 assert (sister != nil);
764 }
765 if (sister->left->color == dnode_black
766 && sister->right->color == dnode_black) {
767 sister->color = dnode_red;
768 child = parent;
769 } else {
770 if (sister->right->color == dnode_black) {
771 assert (sister->left->color == dnode_red);
772 sister->left->color = dnode_black;
773 sister->color = dnode_red;
774 rotate_right(sister);
775 sister = parent->right;
776 assert (sister != nil);
777 }
778 sister->color = parent->color;
779 sister->right->color = dnode_black;
780 parent->color = dnode_black;
781 rotate_left(parent);
782 break;
783 }
784 } else { /* symmetric case: child == child->parent->right */
785 assert (child == parent->right);
786 sister = parent->left;
787 assert (sister != nil);
788 if (sister->color == dnode_red) {
789 sister->color = dnode_black;
790 parent->color = dnode_red;
791 rotate_right(parent);
792 sister = parent->left;
793 assert (sister != nil);
794 }
795 if (sister->right->color == dnode_black
796 && sister->left->color == dnode_black) {
797 sister->color = dnode_red;
798 child = parent;
799 } else {
800 if (sister->left->color == dnode_black) {
801 assert (sister->right->color == dnode_red);
802 sister->right->color = dnode_black;
803 sister->color = dnode_red;
804 rotate_left(sister);
805 sister = parent->left;
806 assert (sister != nil);
807 }
808 sister->color = parent->color;
809 sister->left->color = dnode_black;
810 parent->color = dnode_black;
811 rotate_right(parent);
812 break;
813 }
814 }
815 }
816
817 child->color = dnode_black;
818 dict_root(dict)->color = dnode_black;
819 }
820
821 assert (dict_verify(dict));
822
823 return delete;
824}
825#endif /* E2FSCK_NOTUSED */
826
827/*
828 * Allocate a node using the dictionary's allocator routine, give it
829 * the data item.
830 */
831
832int dict_alloc_insert(dict_t *dict, const void *key, void *data)
833{
834 dnode_t *node = dict->allocnode(dict->context);
835
836 if (node) {
837 dnode_init(node, data);
838 dict_insert(dict, node, key);
839 return 1;
840 }
841 return 0;
842}
843
844#ifdef E2FSCK_NOTUSED
845void dict_delete_free(dict_t *dict, dnode_t *node)
846{
847 dict_delete(dict, node);
848 dict->freenode(node, dict->context);
849}
850#endif
851
852/*
853 * Return the node with the lowest (leftmost) key. If the dictionary is empty
854 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
855 */
856
857dnode_t *dict_first(dict_t *dict)
858{
859 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
860
861 if (root != nil)
862 while ((left = root->left) != nil)
863 root = left;
864
865 return (root == nil) ? NULL : root;
866}
867
868/*
869 * Return the node with the highest (rightmost) key. If the dictionary is empty
870 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
871 */
872
873dnode_t *dict_last(dict_t *dict)
874{
875 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
876
877 if (root != nil)
878 while ((right = root->right) != nil)
879 root = right;
880
881 return (root == nil) ? NULL : root;
882}
883
884/*
885 * Return the given node's successor node---the node which has the
886 * next key in the the left to right ordering. If the node has
887 * no successor, a null pointer is returned rather than a pointer to
888 * the nil node.
889 */
890
891dnode_t *dict_next(dict_t *dict, dnode_t *curr)
892{
893 dnode_t *nil = dict_nil(dict), *parent, *left;
894
895 if (curr->right != nil) {
896 curr = curr->right;
897 while ((left = curr->left) != nil)
898 curr = left;
899 return curr;
900 }
901
902 parent = curr->parent;
903
904 while (parent != nil && curr == parent->right) {
905 curr = parent;
906 parent = curr->parent;
907 }
908
909 return (parent == nil) ? NULL : parent;
910}
911
912/*
913 * Return the given node's predecessor, in the key order.
914 * The nil sentinel node is returned if there is no predecessor.
915 */
916
917dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
918{
919 dnode_t *nil = dict_nil(dict), *parent, *right;
920
921 if (curr->left != nil) {
922 curr = curr->left;
923 while ((right = curr->right) != nil)
924 curr = right;
925 return curr;
926 }
927
928 parent = curr->parent;
929
930 while (parent != nil && curr == parent->left) {
931 curr = parent;
932 parent = curr->parent;
933 }
934
935 return (parent == nil) ? NULL : parent;
936}
937
938void dict_allow_dupes(dict_t *dict)
939{
940 dict->dupes = 1;
941}
942
943#undef dict_count
944#undef dict_isempty
945#undef dict_isfull
946#undef dnode_get
947#undef dnode_put
948#undef dnode_getkey
949
950dictcount_t dict_count(dict_t *dict)
951{
952 return dict->nodecount;
953}
954
955int dict_isempty(dict_t *dict)
956{
957 return dict->nodecount == 0;
958}
959
960int dict_isfull(dict_t *dict)
961{
962 return dict->nodecount == dict->maxcount;
963}
964
965int dict_contains(dict_t *dict, dnode_t *node)
966{
967 return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
968}
969
970static dnode_t *dnode_alloc(void *context EXT2FS_ATTR((unused)))
971{
972 return malloc(sizeof *dnode_alloc(NULL));
973}
974
975static void dnode_free(dnode_t *node, void *context EXT2FS_ATTR((unused)))
976{
977 free(node);
978}
979
980dnode_t *dnode_create(void *data)
981{
982 dnode_t *new = malloc(sizeof *new);
983 if (new) {
984 new->data = data;
985 new->parent = NULL;
986 new->left = NULL;
987 new->right = NULL;
988 }
989 return new;
990}
991
992dnode_t *dnode_init(dnode_t *dnode, void *data)
993{
994 dnode->data = data;
995 dnode->parent = NULL;
996 dnode->left = NULL;
997 dnode->right = NULL;
998 return dnode;
999}
1000
1001void dnode_destroy(dnode_t *dnode)
1002{
1003 assert (!dnode_is_in_a_dict(dnode));
1004 free(dnode);
1005}
1006
1007void *dnode_get(dnode_t *dnode)
1008{
1009 return dnode->data;
1010}
1011
1012const void *dnode_getkey(dnode_t *dnode)
1013{
1014 return dnode->key;
1015}
1016
1017#ifdef E2FSCK_NOTUSED
1018void dnode_put(dnode_t *dnode, void *data)
1019{
1020 dnode->data = data;
1021}
1022
1023int dnode_is_in_a_dict(dnode_t *dnode)
1024{
1025 return (dnode->parent && dnode->left && dnode->right);
1026}
1027
1028void dict_process(dict_t *dict, void *context, dnode_process_t function)
1029{
1030 dnode_t *node = dict_first(dict), *next;
1031
1032 while (node != NULL) {
1033 /* check for callback function deleting */
1034 /* the next node from under us */
1035 assert (dict_contains(dict, node));
1036 next = dict_next(dict, node);
1037 function(dict, node, context);
1038 node = next;
1039 }
1040}
1041
1042static void load_begin_internal(dict_load_t *load, dict_t *dict)
1043{
1044 load->dictptr = dict;
1045 load->nilnode.left = &load->nilnode;
1046 load->nilnode.right = &load->nilnode;
1047}
1048
1049void dict_load_begin(dict_load_t *load, dict_t *dict)
1050{
1051 assert (dict_isempty(dict));
1052 load_begin_internal(load, dict);
1053}
1054
1055void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
1056{
1057 dict_t *dict = load->dictptr;
1058 dnode_t *nil = &load->nilnode;
1059
1060 assert (!dnode_is_in_a_dict(newnode));
1061 assert (dict->nodecount < dict->maxcount);
1062
1063#ifndef NDEBUG
1064 if (dict->nodecount > 0) {
1065 if (dict->dupes)
1066 assert (dict->compare(nil->left->key, key) <= 0);
1067 else
1068 assert (dict->compare(nil->left->key, key) < 0);
1069 }
1070#endif
1071
1072 newnode->key = key;
1073 nil->right->left = newnode;
1074 nil->right = newnode;
1075 newnode->left = nil;
1076 dict->nodecount++;
1077}
1078
1079void dict_load_end(dict_load_t *load)
1080{
1081 dict_t *dict = load->dictptr;
1082 dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
1083 dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
1084 dnode_t *complete = 0;
1085 dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
1086 dictcount_t botrowcount;
1087 unsigned baselevel = 0, level = 0, i;
1088
1089 assert (dnode_red == 0 && dnode_black == 1);
1090
1091 while (fullcount >= nodecount && fullcount)
1092 fullcount >>= 1;
1093
1094 botrowcount = nodecount - fullcount;
1095
1096 for (curr = loadnil->left; curr != loadnil; curr = next) {
1097 next = curr->left;
1098
1099 if (complete == NULL && botrowcount-- == 0) {
1100 assert (baselevel == 0);
1101 assert (level == 0);
1102 baselevel = level = 1;
1103 complete = tree[0];
1104
1105 if (complete != 0) {
1106 tree[0] = 0;
1107 complete->right = dictnil;
1108 while (tree[level] != 0) {
1109 tree[level]->right = complete;
1110 complete->parent = tree[level];
1111 complete = tree[level];
1112 tree[level++] = 0;
1113 }
1114 }
1115 }
1116
1117 if (complete == NULL) {
1118 curr->left = dictnil;
1119 curr->right = dictnil;
1120 curr->color = level % 2;
1121 complete = curr;
1122
1123 assert (level == baselevel);
1124 while (tree[level] != 0) {
1125 tree[level]->right = complete;
1126 complete->parent = tree[level];
1127 complete = tree[level];
1128 tree[level++] = 0;
1129 }
1130 } else {
1131 curr->left = complete;
1132 curr->color = (level + 1) % 2;
1133 complete->parent = curr;
1134 tree[level] = curr;
1135 complete = 0;
1136 level = baselevel;
1137 }
1138 }
1139
1140 if (complete == NULL)
1141 complete = dictnil;
1142
1143 for (i = 0; i < DICT_DEPTH_MAX; i++) {
1144 if (tree[i] != 0) {
1145 tree[i]->right = complete;
1146 complete->parent = tree[i];
1147 complete = tree[i];
1148 }
1149 }
1150
1151 dictnil->color = dnode_black;
1152 dictnil->right = dictnil;
1153 complete->parent = dictnil;
1154 complete->color = dnode_black;
1155 dict_root(dict) = complete;
1156
1157 assert (dict_verify(dict));
1158}
1159
1160void dict_merge(dict_t *dest, dict_t *source)
1161{
1162 dict_load_t load;
1163 dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
1164
1165 assert (dict_similar(dest, source));
1166
1167 if (source == dest)
1168 return;
1169
1170 dest->nodecount = 0;
1171 load_begin_internal(&load, dest);
1172
1173 for (;;) {
1174 if (leftnode != NULL && rightnode != NULL) {
1175 if (dest->compare(leftnode->key, rightnode->key) < 0)
1176 goto copyleft;
1177 else
1178 goto copyright;
1179 } else if (leftnode != NULL) {
1180 goto copyleft;
1181 } else if (rightnode != NULL) {
1182 goto copyright;
1183 } else {
1184 assert (leftnode == NULL && rightnode == NULL);
1185 break;
1186 }
1187
1188 copyleft:
1189 {
1190 dnode_t *next = dict_next(dest, leftnode);
1191#ifndef NDEBUG
1192 leftnode->left = NULL; /* suppress assertion in dict_load_next */
1193#endif
1194 dict_load_next(&load, leftnode, leftnode->key);
1195 leftnode = next;
1196 continue;
1197 }
1198
1199 copyright:
1200 {
1201 dnode_t *next = dict_next(source, rightnode);
1202#ifndef NDEBUG
1203 rightnode->left = NULL;
1204#endif
1205 dict_load_next(&load, rightnode, rightnode->key);
1206 rightnode = next;
1207 continue;
1208 }
1209 }
1210
1211 dict_clear(source);
1212 dict_load_end(&load);
1213}
1214#endif /* E2FSCK_NOTUSED */
1215
1216#ifdef KAZLIB_TEST_MAIN
1217
1218#include <stdio.h>
1219#include <string.h>
1220#include <ctype.h>
1221#include <stdarg.h>
1222
1223typedef char input_t[256];
1224
1225static int tokenize(char *string, ...)
1226{
1227 char **tokptr;
1228 va_list arglist;
1229 int tokcount = 0;
1230
1231 va_start(arglist, string);
1232 tokptr = va_arg(arglist, char **);
1233 while (tokptr) {
1234 while (*string && isspace((unsigned char) *string))
1235 string++;
1236 if (!*string)
1237 break;
1238 *tokptr = string;
1239 while (*string && !isspace((unsigned char) *string))
1240 string++;
1241 tokptr = va_arg(arglist, char **);
1242 tokcount++;
1243 if (!*string)
1244 break;
1245 *string++ = 0;
1246 }
1247 va_end(arglist);
1248
1249 return tokcount;
1250}
1251
1252static int comparef(const void *key1, const void *key2)
1253{
1254 return strcmp(key1, key2);
1255}
1256
1257static char *dupstring(char *str)
1258{
1259 int sz = strlen(str) + 1;
1260 char *new = malloc(sz);
1261 if (new)
1262 memcpy(new, str, sz);
1263 return new;
1264}
1265
1266static dnode_t *new_node(void *c)
1267{
1268 static dnode_t few[5];
1269 static int count;
1270
1271 if (count < 5)
1272 return few + count++;
1273
1274 return NULL;
1275}
1276
1277static void del_node(dnode_t *n, void *c)
1278{
1279}
1280
1281static int prompt = 0;
1282
1283static void construct(dict_t *d)
1284{
1285 input_t in;
1286 int done = 0;
1287 dict_load_t dl;
1288 dnode_t *dn;
1289 char *tok1, *tok2, *val;
1290 const char *key;
1291 char *help =
1292 "p turn prompt on\n"
1293 "q finish construction\n"
1294 "a <key> <val> add new entry\n";
1295
1296 if (!dict_isempty(d))
1297 puts("warning: dictionary not empty!");
1298
1299 dict_load_begin(&dl, d);
1300
1301 while (!done) {
1302 if (prompt)
1303 putchar('>');
1304 fflush(stdout);
1305
1306 if (!fgets(in, sizeof(input_t), stdin))
1307 break;
1308
1309 switch (in[0]) {
1310 case '?':
1311 puts(help);
1312 break;
1313 case 'p':
1314 prompt = 1;
1315 break;
1316 case 'q':
1317 done = 1;
1318 break;
1319 case 'a':
1320 if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1321 puts("what?");
1322 break;
1323 }
1324 key = dupstring(tok1);
1325 val = dupstring(tok2);
1326 dn = dnode_create(val);
1327
1328 if (!key || !val || !dn) {
1329 puts("out of memory");
1330 free((void *) key);
1331 free(val);
1332 if (dn)
1333 dnode_destroy(dn);
1334 }
1335
1336 dict_load_next(&dl, dn, key);
1337 break;
1338 default:
1339 putchar('?');
1340 putchar('\n');
1341 break;
1342 }
1343 }
1344
1345 dict_load_end(&dl);
1346}
1347
1348int main(void)
1349{
1350 input_t in;
1351 dict_t darray[10];
1352 dict_t *d = &darray[0];
1353 dnode_t *dn;
1354 int i;
1355 char *tok1, *tok2, *val;
1356 const char *key;
1357
1358 char *help =
1359 "a <key> <val> add value to dictionary\n"
1360 "d <key> delete value from dictionary\n"
1361 "l <key> lookup value in dictionary\n"
1362 "( <key> lookup lower bound\n"
1363 ") <key> lookup upper bound\n"
1364 "# <num> switch to alternate dictionary (0-9)\n"
1365 "j <num> <num> merge two dictionaries\n"
1366 "f free the whole dictionary\n"
1367 "k allow duplicate keys\n"
1368 "c show number of entries\n"
1369 "t dump whole dictionary in sort order\n"
1370 "m make dictionary out of sorted items\n"
1371 "p turn prompt on\n"
1372 "s switch to non-functioning allocator\n"
1373 "q quit";
1374
1375 for (i = 0; i < sizeof darray / sizeof *darray; i++)
1376 dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
1377
1378 for (;;) {
1379 if (prompt)
1380 putchar('>');
1381 fflush(stdout);
1382
1383 if (!fgets(in, sizeof(input_t), stdin))
1384 break;
1385
1386 switch(in[0]) {
1387 case '?':
1388 puts(help);
1389 break;
1390 case 'a':
1391 if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1392 puts("what?");
1393 break;
1394 }
1395 key = dupstring(tok1);
1396 val = dupstring(tok2);
1397
1398 if (!key || !val) {
1399 puts("out of memory");
1400 free((void *) key);
1401 free(val);
1402 }
1403
1404 if (!dict_alloc_insert(d, key, val)) {
1405 puts("dict_alloc_insert failed");
1406 free((void *) key);
1407 free(val);
1408 break;
1409 }
1410 break;
1411 case 'd':
1412 if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1413 puts("what?");
1414 break;
1415 }
1416 dn = dict_lookup(d, tok1);
1417 if (!dn) {
1418 puts("dict_lookup failed");
1419 break;
1420 }
1421 val = dnode_get(dn);
1422 key = dnode_getkey(dn);
1423 dict_delete_free(d, dn);
1424
1425 free(val);
1426 free((void *) key);
1427 break;
1428 case 'f':
1429 dict_free(d);
1430 break;
1431 case 'l':
1432 case '(':
1433 case ')':
1434 if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1435 puts("what?");
1436 break;
1437 }
1438 dn = 0;
1439 switch (in[0]) {
1440 case 'l':
1441 dn = dict_lookup(d, tok1);
1442 break;
1443 case '(':
1444 dn = dict_lower_bound(d, tok1);
1445 break;
1446 case ')':
1447 dn = dict_upper_bound(d, tok1);
1448 break;
1449 }
1450 if (!dn) {
1451 puts("lookup failed");
1452 break;
1453 }
1454 val = dnode_get(dn);
1455 puts(val);
1456 break;
1457 case 'm':
1458 construct(d);
1459 break;
1460 case 'k':
1461 dict_allow_dupes(d);
1462 break;
1463 case 'c':
1464 printf("%lu\n", (unsigned long) dict_count(d));
1465 break;
1466 case 't':
1467 for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
1468 printf("%s\t%s\n", (char *) dnode_getkey(dn),
1469 (char *) dnode_get(dn));
1470 }
1471 break;
1472 case 'q':
1473 exit(0);
1474 break;
1475 case '\0':
1476 break;
1477 case 'p':
1478 prompt = 1;
1479 break;
1480 case 's':
1481 dict_set_allocator(d, new_node, del_node, NULL);
1482 break;
1483 case '#':
1484 if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1485 puts("what?");
1486 break;
1487 } else {
1488 int dictnum = atoi(tok1);
1489 if (dictnum < 0 || dictnum > 9) {
1490 puts("invalid number");
1491 break;
1492 }
1493 d = &darray[dictnum];
1494 }
1495 break;
1496 case 'j':
1497 if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1498 puts("what?");
1499 break;
1500 } else {
1501 int dict1 = atoi(tok1), dict2 = atoi(tok2);
1502 if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
1503 puts("invalid number");
1504 break;
1505 }
1506 dict_merge(&darray[dict1], &darray[dict2]);
1507 }
1508 break;
1509 default:
1510 putchar('?');
1511 putchar('\n');
1512 break;
1513 }
1514 }
1515
1516 return 0;
1517}
1518
1519#endif
diff --git a/e2fsprogs/e2fsck/dict.h b/e2fsprogs/e2fsck/dict.h
new file mode 100644
index 000000000..838079d6c
--- /dev/null
+++ b/e2fsprogs/e2fsck/dict.h
@@ -0,0 +1,144 @@
1/*
2 * Dictionary Abstract Data Type
3 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
4 *
5 * Free Software License:
6 *
7 * All rights are reserved by the author, with the following exceptions:
8 * Permission is granted to freely reproduce and distribute this software,
9 * possibly in exchange for a fee, provided that this copyright notice appears
10 * intact. Permission is also granted to adapt this software to produce
11 * derivative works, as long as the modified versions carry this copyright
12 * notice and additional notices stating that the work has been modified.
13 * This source code may be translated into executable form and incorporated
14 * into proprietary software; there is no requirement for such software to
15 * contain a copyright notice related to this source.
16 *
17 * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
18 * $Name: kazlib_1_20 $
19 */
20
21#ifndef DICT_H
22#define DICT_H
23
24#include <limits.h>
25#ifdef KAZLIB_SIDEEFFECT_DEBUG
26#include "sfx.h"
27#endif
28
29/*
30 * Blurb for inclusion into C++ translation units
31 */
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
37typedef unsigned long dictcount_t;
38#define DICTCOUNT_T_MAX ULONG_MAX
39
40/*
41 * The dictionary is implemented as a red-black tree
42 */
43
44typedef enum { dnode_red, dnode_black } dnode_color_t;
45
46typedef struct dnode_t {
47#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
48 struct dnode_t *dict_left;
49 struct dnode_t *dict_right;
50 struct dnode_t *dict_parent;
51 dnode_color_t dict_color;
52 const void *dict_key;
53 void *dict_data;
54#else
55 int dict_dummy;
56#endif
57} dnode_t;
58
59typedef int (*dict_comp_t)(const void *, const void *);
60typedef dnode_t *(*dnode_alloc_t)(void *);
61typedef void (*dnode_free_t)(dnode_t *, void *);
62
63typedef struct dict_t {
64#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
65 dnode_t dict_nilnode;
66 dictcount_t dict_nodecount;
67 dictcount_t dict_maxcount;
68 dict_comp_t dict_compare;
69 dnode_alloc_t dict_allocnode;
70 dnode_free_t dict_freenode;
71 void *dict_context;
72 int dict_dupes;
73#else
74 int dict_dummmy;
75#endif
76} dict_t;
77
78typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
79
80typedef struct dict_load_t {
81#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
82 dict_t *dict_dictptr;
83 dnode_t dict_nilnode;
84#else
85 int dict_dummmy;
86#endif
87} dict_load_t;
88
89extern dict_t *dict_create(dictcount_t, dict_comp_t);
90extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
91extern void dict_destroy(dict_t *);
92extern void dict_free_nodes(dict_t *);
93extern void dict_free(dict_t *);
94extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
95extern void dict_init_like(dict_t *, const dict_t *);
96extern int dict_verify(dict_t *);
97extern int dict_similar(const dict_t *, const dict_t *);
98extern dnode_t *dict_lookup(dict_t *, const void *);
99extern dnode_t *dict_lower_bound(dict_t *, const void *);
100extern dnode_t *dict_upper_bound(dict_t *, const void *);
101extern void dict_insert(dict_t *, dnode_t *, const void *);
102extern dnode_t *dict_delete(dict_t *, dnode_t *);
103extern int dict_alloc_insert(dict_t *, const void *, void *);
104extern void dict_delete_free(dict_t *, dnode_t *);
105extern dnode_t *dict_first(dict_t *);
106extern dnode_t *dict_last(dict_t *);
107extern dnode_t *dict_next(dict_t *, dnode_t *);
108extern dnode_t *dict_prev(dict_t *, dnode_t *);
109extern dictcount_t dict_count(dict_t *);
110extern int dict_isempty(dict_t *);
111extern int dict_isfull(dict_t *);
112extern int dict_contains(dict_t *, dnode_t *);
113extern void dict_allow_dupes(dict_t *);
114extern int dnode_is_in_a_dict(dnode_t *);
115extern dnode_t *dnode_create(void *);
116extern dnode_t *dnode_init(dnode_t *, void *);
117extern void dnode_destroy(dnode_t *);
118extern void *dnode_get(dnode_t *);
119extern const void *dnode_getkey(dnode_t *);
120extern void dnode_put(dnode_t *, void *);
121extern void dict_process(dict_t *, void *, dnode_process_t);
122extern void dict_load_begin(dict_load_t *, dict_t *);
123extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
124extern void dict_load_end(dict_load_t *);
125extern void dict_merge(dict_t *, dict_t *);
126
127#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
128#ifdef KAZLIB_SIDEEFFECT_DEBUG
129#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
130#else
131#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
132#endif
133#define dict_count(D) ((D)->dict_nodecount)
134#define dict_isempty(D) ((D)->dict_nodecount == 0)
135#define dnode_get(N) ((N)->dict_data)
136#define dnode_getkey(N) ((N)->dict_key)
137#define dnode_put(N, X) ((N)->dict_data = (X))
138#endif
139
140#ifdef __cplusplus
141}
142#endif
143
144#endif
diff --git a/e2fsprogs/e2fsck/dirinfo.c b/e2fsprogs/e2fsck/dirinfo.c
new file mode 100644
index 000000000..516c46135
--- /dev/null
+++ b/e2fsprogs/e2fsck/dirinfo.c
@@ -0,0 +1,137 @@
1/*
2 * dirinfo.c --- maintains the directory information table for e2fsck.
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include "e2fsck.h"
9
10/*
11 * This subroutine is called during pass1 to create a directory info
12 * entry. During pass1, the passed-in parent is 0; it will get filled
13 * in during pass2.
14 */
15void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
16{
17 struct dir_info *dir;
18 int i, j;
19 ext2_ino_t num_dirs;
20 errcode_t retval;
21 unsigned long old_size;
22
23#if 0
24 printf("add_dir_info for inode %lu...\n", ino);
25#endif
26 if (!ctx->dir_info) {
27 ctx->dir_info_count = 0;
28 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
29 if (retval)
30 num_dirs = 1024; /* Guess */
31 ctx->dir_info_size = num_dirs + 10;
32 ctx->dir_info = (struct dir_info *)
33 e2fsck_allocate_memory(ctx, ctx->dir_info_size
34 * sizeof (struct dir_info),
35 "directory map");
36 }
37
38 if (ctx->dir_info_count >= ctx->dir_info_size) {
39 old_size = ctx->dir_info_size * sizeof(struct dir_info);
40 ctx->dir_info_size += 10;
41 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
42 sizeof(struct dir_info),
43 &ctx->dir_info);
44 if (retval) {
45 ctx->dir_info_size -= 10;
46 return;
47 }
48 }
49
50 /*
51 * Normally, add_dir_info is called with each inode in
52 * sequential order; but once in a while (like when pass 3
53 * needs to recreate the root directory or lost+found
54 * directory) it is called out of order. In those cases, we
55 * need to move the dir_info entries down to make room, since
56 * the dir_info array needs to be sorted by inode number for
57 * get_dir_info()'s sake.
58 */
59 if (ctx->dir_info_count &&
60 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
61 for (i = ctx->dir_info_count-1; i > 0; i--)
62 if (ctx->dir_info[i-1].ino < ino)
63 break;
64 dir = &ctx->dir_info[i];
65 if (dir->ino != ino)
66 for (j = ctx->dir_info_count++; j > i; j--)
67 ctx->dir_info[j] = ctx->dir_info[j-1];
68 } else
69 dir = &ctx->dir_info[ctx->dir_info_count++];
70
71 dir->ino = ino;
72 dir->dotdot = parent;
73 dir->parent = parent;
74}
75
76/*
77 * get_dir_info() --- given an inode number, try to find the directory
78 * information entry for it.
79 */
80struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
81{
82 int low, high, mid;
83
84 low = 0;
85 high = ctx->dir_info_count-1;
86 if (!ctx->dir_info)
87 return 0;
88 if (ino == ctx->dir_info[low].ino)
89 return &ctx->dir_info[low];
90 if (ino == ctx->dir_info[high].ino)
91 return &ctx->dir_info[high];
92
93 while (low < high) {
94 mid = (low+high)/2;
95 if (mid == low || mid == high)
96 break;
97 if (ino == ctx->dir_info[mid].ino)
98 return &ctx->dir_info[mid];
99 if (ino < ctx->dir_info[mid].ino)
100 high = mid;
101 else
102 low = mid;
103 }
104 return 0;
105}
106
107/*
108 * Free the dir_info structure when it isn't needed any more.
109 */
110void e2fsck_free_dir_info(e2fsck_t ctx)
111{
112 if (ctx->dir_info) {
113 ext2fs_free_mem(&ctx->dir_info);
114 ctx->dir_info = 0;
115 }
116 ctx->dir_info_size = 0;
117 ctx->dir_info_count = 0;
118}
119
120/*
121 * Return the count of number of directories in the dir_info structure
122 */
123int e2fsck_get_num_dirinfo(e2fsck_t ctx)
124{
125 return ctx->dir_info_count;
126}
127
128/*
129 * A simple interator function
130 */
131struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
132{
133 if (*control >= ctx->dir_info_count)
134 return 0;
135
136 return(ctx->dir_info + (*control)++);
137}
diff --git a/e2fsprogs/e2fsck/dx_dirinfo.c b/e2fsprogs/e2fsck/dx_dirinfo.c
new file mode 100644
index 000000000..322f51679
--- /dev/null
+++ b/e2fsprogs/e2fsck/dx_dirinfo.c
@@ -0,0 +1,150 @@
1/*
2 * dirinfo.c --- maintains the directory information table for e2fsck.
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include "e2fsck.h"
9#ifdef ENABLE_HTREE
10
11/*
12 * This subroutine is called during pass1 to create a directory info
13 * entry. During pass1, the passed-in parent is 0; it will get filled
14 * in during pass2.
15 */
16void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
17{
18 struct dx_dir_info *dir;
19 int i, j;
20 errcode_t retval;
21 unsigned long old_size;
22
23#if 0
24 printf("add_dx_dir_info for inode %lu...\n", ino);
25#endif
26 if (!ctx->dx_dir_info) {
27 ctx->dx_dir_info_count = 0;
28 ctx->dx_dir_info_size = 100; /* Guess */
29 ctx->dx_dir_info = (struct dx_dir_info *)
30 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
31 * sizeof (struct dx_dir_info),
32 "directory map");
33 }
34
35 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
36 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
37 ctx->dx_dir_info_size += 10;
38 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
39 sizeof(struct dx_dir_info),
40 &ctx->dx_dir_info);
41 if (retval) {
42 ctx->dx_dir_info_size -= 10;
43 return;
44 }
45 }
46
47 /*
48 * Normally, add_dx_dir_info is called with each inode in
49 * sequential order; but once in a while (like when pass 3
50 * needs to recreate the root directory or lost+found
51 * directory) it is called out of order. In those cases, we
52 * need to move the dx_dir_info entries down to make room, since
53 * the dx_dir_info array needs to be sorted by inode number for
54 * get_dx_dir_info()'s sake.
55 */
56 if (ctx->dx_dir_info_count &&
57 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
58 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
59 if (ctx->dx_dir_info[i-1].ino < ino)
60 break;
61 dir = &ctx->dx_dir_info[i];
62 if (dir->ino != ino)
63 for (j = ctx->dx_dir_info_count++; j > i; j--)
64 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
65 } else
66 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
67
68 dir->ino = ino;
69 dir->numblocks = num_blocks;
70 dir->hashversion = 0;
71 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
72 * sizeof (struct dx_dirblock_info),
73 "dx_block info array");
74
75}
76
77/*
78 * get_dx_dir_info() --- given an inode number, try to find the directory
79 * information entry for it.
80 */
81struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
82{
83 int low, high, mid;
84
85 low = 0;
86 high = ctx->dx_dir_info_count-1;
87 if (!ctx->dx_dir_info)
88 return 0;
89 if (ino == ctx->dx_dir_info[low].ino)
90 return &ctx->dx_dir_info[low];
91 if (ino == ctx->dx_dir_info[high].ino)
92 return &ctx->dx_dir_info[high];
93
94 while (low < high) {
95 mid = (low+high)/2;
96 if (mid == low || mid == high)
97 break;
98 if (ino == ctx->dx_dir_info[mid].ino)
99 return &ctx->dx_dir_info[mid];
100 if (ino < ctx->dx_dir_info[mid].ino)
101 high = mid;
102 else
103 low = mid;
104 }
105 return 0;
106}
107
108/*
109 * Free the dx_dir_info structure when it isn't needed any more.
110 */
111void e2fsck_free_dx_dir_info(e2fsck_t ctx)
112{
113 int i;
114 struct dx_dir_info *dir;
115
116 if (ctx->dx_dir_info) {
117 dir = ctx->dx_dir_info;
118 for (i=0; i < ctx->dx_dir_info_count; i++) {
119 if (dir->dx_block) {
120 ext2fs_free_mem(&dir->dx_block);
121 dir->dx_block = 0;
122 }
123 }
124 ext2fs_free_mem(&ctx->dx_dir_info);
125 ctx->dx_dir_info = 0;
126 }
127 ctx->dx_dir_info_size = 0;
128 ctx->dx_dir_info_count = 0;
129}
130
131/*
132 * Return the count of number of directories in the dx_dir_info structure
133 */
134int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx)
135{
136 return ctx->dx_dir_info_count;
137}
138
139/*
140 * A simple interator function
141 */
142struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
143{
144 if (*control >= ctx->dx_dir_info_count)
145 return 0;
146
147 return(ctx->dx_dir_info + (*control)++);
148}
149
150#endif /* ENABLE_HTREE */
diff --git a/e2fsprogs/e2fsck/e2fsck.c b/e2fsprogs/e2fsck/e2fsck.c
new file mode 100644
index 000000000..2a84c5171
--- /dev/null
+++ b/e2fsprogs/e2fsck/e2fsck.c
@@ -0,0 +1,202 @@
1/*
2 * e2fsck.c - a consistency checker for the new extended file system.
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <errno.h>
13
14#include "e2fsck.h"
15#include "problem.h"
16
17/*
18 * This function allocates an e2fsck context
19 */
20errcode_t e2fsck_allocate_context(e2fsck_t *ret)
21{
22 e2fsck_t context;
23 errcode_t retval;
24
25 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
26 if (retval)
27 return retval;
28
29 memset(context, 0, sizeof(struct e2fsck_struct));
30
31 context->process_inode_size = 256;
32 context->ext_attr_ver = 2;
33
34 *ret = context;
35 return 0;
36}
37
38/*
39 * This function resets an e2fsck context; it is called when e2fsck
40 * needs to be restarted.
41 */
42errcode_t e2fsck_reset_context(e2fsck_t ctx)
43{
44 ctx->flags = 0;
45 ctx->lost_and_found = 0;
46 ctx->bad_lost_and_found = 0;
47 if (ctx->inode_used_map) {
48 ext2fs_free_inode_bitmap(ctx->inode_used_map);
49 ctx->inode_used_map = 0;
50 }
51 if (ctx->inode_dir_map) {
52 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
53 ctx->inode_dir_map = 0;
54 }
55 if (ctx->inode_reg_map) {
56 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
57 ctx->inode_reg_map = 0;
58 }
59 if (ctx->block_found_map) {
60 ext2fs_free_block_bitmap(ctx->block_found_map);
61 ctx->block_found_map = 0;
62 }
63 if (ctx->inode_link_info) {
64 ext2fs_free_icount(ctx->inode_link_info);
65 ctx->inode_link_info = 0;
66 }
67 if (ctx->journal_io) {
68 if (ctx->fs && ctx->fs->io != ctx->journal_io)
69 io_channel_close(ctx->journal_io);
70 ctx->journal_io = 0;
71 }
72 if (ctx->fs && ctx->fs->dblist) {
73 ext2fs_free_dblist(ctx->fs->dblist);
74 ctx->fs->dblist = 0;
75 }
76 e2fsck_free_dir_info(ctx);
77#ifdef ENABLE_HTREE
78 e2fsck_free_dx_dir_info(ctx);
79#endif
80 if (ctx->refcount) {
81 ea_refcount_free(ctx->refcount);
82 ctx->refcount = 0;
83 }
84 if (ctx->refcount_extra) {
85 ea_refcount_free(ctx->refcount_extra);
86 ctx->refcount_extra = 0;
87 }
88 if (ctx->block_dup_map) {
89 ext2fs_free_block_bitmap(ctx->block_dup_map);
90 ctx->block_dup_map = 0;
91 }
92 if (ctx->block_ea_map) {
93 ext2fs_free_block_bitmap(ctx->block_ea_map);
94 ctx->block_ea_map = 0;
95 }
96 if (ctx->inode_bb_map) {
97 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
98 ctx->inode_bb_map = 0;
99 }
100 if (ctx->inode_bad_map) {
101 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
102 ctx->inode_bad_map = 0;
103 }
104 if (ctx->inode_imagic_map) {
105 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
106 ctx->inode_imagic_map = 0;
107 }
108 if (ctx->dirs_to_hash) {
109 ext2fs_u32_list_free(ctx->dirs_to_hash);
110 ctx->dirs_to_hash = 0;
111 }
112
113 /*
114 * Clear the array of invalid meta-data flags
115 */
116 if (ctx->invalid_inode_bitmap_flag) {
117 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
118 ctx->invalid_inode_bitmap_flag = 0;
119 }
120 if (ctx->invalid_block_bitmap_flag) {
121 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
122 ctx->invalid_block_bitmap_flag = 0;
123 }
124 if (ctx->invalid_inode_table_flag) {
125 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
126 ctx->invalid_inode_table_flag = 0;
127 }
128
129 /* Clear statistic counters */
130 ctx->fs_directory_count = 0;
131 ctx->fs_regular_count = 0;
132 ctx->fs_blockdev_count = 0;
133 ctx->fs_chardev_count = 0;
134 ctx->fs_links_count = 0;
135 ctx->fs_symlinks_count = 0;
136 ctx->fs_fast_symlinks_count = 0;
137 ctx->fs_fifo_count = 0;
138 ctx->fs_total_count = 0;
139 ctx->fs_badblocks_count = 0;
140 ctx->fs_sockets_count = 0;
141 ctx->fs_ind_count = 0;
142 ctx->fs_dind_count = 0;
143 ctx->fs_tind_count = 0;
144 ctx->fs_fragmented = 0;
145 ctx->large_files = 0;
146
147 /* Reset the superblock to the user's requested value */
148 ctx->superblock = ctx->use_superblock;
149
150 return 0;
151}
152
153void e2fsck_free_context(e2fsck_t ctx)
154{
155 if (!ctx)
156 return;
157
158 e2fsck_reset_context(ctx);
159 if (ctx->blkid)
160 blkid_put_cache(ctx->blkid);
161
162 ext2fs_free_mem(&ctx);
163}
164
165/*
166 * This function runs through the e2fsck passes and calls them all,
167 * returning restart, abort, or cancel as necessary...
168 */
169typedef void (*pass_t)(e2fsck_t ctx);
170
171pass_t e2fsck_passes[] = {
172 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
173 e2fsck_pass5, 0 };
174
175#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
176
177int e2fsck_run(e2fsck_t ctx)
178{
179 int i;
180 pass_t e2fsck_pass;
181
182#ifdef HAVE_SETJMP_H
183 if (setjmp(ctx->abort_loc)) {
184 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
185 return (ctx->flags & E2F_FLAG_RUN_RETURN);
186 }
187 ctx->flags |= E2F_FLAG_SETJMP_OK;
188#endif
189
190 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
191 if (ctx->flags & E2F_FLAG_RUN_RETURN)
192 break;
193 e2fsck_pass(ctx);
194 if (ctx->progress)
195 (void) (ctx->progress)(ctx, 0, 0, 0);
196 }
197 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
198
199 if (ctx->flags & E2F_FLAG_RUN_RETURN)
200 return (ctx->flags & E2F_FLAG_RUN_RETURN);
201 return 0;
202}
diff --git a/e2fsprogs/e2fsck/e2fsck.h b/e2fsprogs/e2fsck/e2fsck.h
new file mode 100644
index 000000000..489903d7c
--- /dev/null
+++ b/e2fsprogs/e2fsck/e2fsck.h
@@ -0,0 +1,446 @@
1/*
2 * e2fsck.h
3 *
4 * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be
5 * redistributed under the terms of the GNU Public License.
6 *
7 */
8
9#include <stdio.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14#include <stdlib.h>
15#include <time.h>
16#ifdef HAVE_SYS_TYPES_H
17#include <sys/types.h>
18#endif
19#ifdef HAVE_SYS_TIME_H
20#include <sys/time.h>
21#endif
22#ifdef HAVE_SETJMP_H
23#include <setjmp.h>
24#endif
25
26#if EXT2_FLAT_INCLUDES
27#include "ext2_fs.h"
28#include "ext2fs.h"
29#include "blkid.h"
30#else
31#include "ext2fs/ext2_fs.h"
32#include "ext2fs/ext2fs.h"
33#include "blkid/blkid.h"
34#endif
35
36/*
37 * Exit codes used by fsck-type programs
38 */
39#define FSCK_OK 0 /* No errors */
40#define FSCK_NONDESTRUCT 1 /* File system errors corrected */
41#define FSCK_REBOOT 2 /* System should be rebooted */
42#define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */
43#define FSCK_ERROR 8 /* Operational error */
44#define FSCK_USAGE 16 /* Usage or syntax error */
45#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
46#define FSCK_LIBRARY 128 /* Shared library error */
47
48/*
49 * The last ext2fs revision level that this version of e2fsck is able to
50 * support
51 */
52#define E2FSCK_CURRENT_REV 1
53
54/*
55 * The directory information structure; stores directory information
56 * collected in earlier passes, to avoid disk i/o in fetching the
57 * directory information.
58 */
59struct dir_info {
60 ext2_ino_t ino; /* Inode number */
61 ext2_ino_t dotdot; /* Parent according to '..' */
62 ext2_ino_t parent; /* Parent according to treewalk */
63};
64
65
66/*
67 * The indexed directory information structure; stores information for
68 * directories which contain a hash tree index.
69 */
70struct dx_dir_info {
71 ext2_ino_t ino; /* Inode number */
72 int numblocks; /* number of blocks */
73 int hashversion;
74 short depth; /* depth of tree */
75 struct dx_dirblock_info *dx_block; /* Array of size numblocks */
76};
77
78#define DX_DIRBLOCK_ROOT 1
79#define DX_DIRBLOCK_LEAF 2
80#define DX_DIRBLOCK_NODE 3
81#define DX_DIRBLOCK_CORRUPT 4
82#define DX_DIRBLOCK_CLEARED 8
83
84struct dx_dirblock_info {
85 int type;
86 blk_t phys;
87 int flags;
88 blk_t parent;
89 ext2_dirhash_t min_hash;
90 ext2_dirhash_t max_hash;
91 ext2_dirhash_t node_min_hash;
92 ext2_dirhash_t node_max_hash;
93};
94
95#define DX_FLAG_REFERENCED 1
96#define DX_FLAG_DUP_REF 2
97#define DX_FLAG_FIRST 4
98#define DX_FLAG_LAST 8
99
100#ifdef RESOURCE_TRACK
101/*
102 * This structure is used for keeping track of how much resources have
103 * been used for a particular pass of e2fsck.
104 */
105struct resource_track {
106 struct timeval time_start;
107 struct timeval user_start;
108 struct timeval system_start;
109 void *brk_start;
110};
111#endif
112
113/*
114 * E2fsck options
115 */
116#define E2F_OPT_READONLY 0x0001
117#define E2F_OPT_PREEN 0x0002
118#define E2F_OPT_YES 0x0004
119#define E2F_OPT_NO 0x0008
120#define E2F_OPT_TIME 0x0010
121#define E2F_OPT_TIME2 0x0020
122#define E2F_OPT_CHECKBLOCKS 0x0040
123#define E2F_OPT_DEBUG 0x0080
124#define E2F_OPT_FORCE 0x0100
125#define E2F_OPT_WRITECHECK 0x0200
126#define E2F_OPT_COMPRESS_DIRS 0x0400
127
128/*
129 * E2fsck flags
130 */
131#define E2F_FLAG_ABORT 0x0001 /* Abort signaled */
132#define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */
133#define E2F_FLAG_SIGNAL_MASK 0x0003
134#define E2F_FLAG_RESTART 0x0004 /* Restart signaled */
135
136#define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */
137
138#define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */
139#define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */
140#define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */
141#define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly
142 * specified by the user */
143#define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */
144#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */
145
146/*
147 * Defines for indicating the e2fsck pass number
148 */
149#define E2F_PASS_1 1
150#define E2F_PASS_2 2
151#define E2F_PASS_3 3
152#define E2F_PASS_4 4
153#define E2F_PASS_5 5
154#define E2F_PASS_1B 6
155
156/*
157 * Define the extended attribute refcount structure
158 */
159typedef struct ea_refcount *ext2_refcount_t;
160
161/*
162 * This is the global e2fsck structure.
163 */
164typedef struct e2fsck_struct *e2fsck_t;
165
166struct e2fsck_struct {
167 ext2_filsys fs;
168 const char *program_name;
169 char *filesystem_name;
170 char *device_name;
171 char *io_options;
172 int flags; /* E2fsck internal flags */
173 int options;
174 blk_t use_superblock; /* sb requested by user */
175 blk_t superblock; /* sb used to open fs */
176 int blocksize; /* blocksize */
177 blk_t num_blocks; /* Total number of blocks */
178 int mount_flags;
179 blkid_cache blkid; /* blkid cache */
180
181#ifdef HAVE_SETJMP_H
182 jmp_buf abort_loc;
183#endif
184 unsigned long abort_code;
185
186 int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
187 unsigned long max);
188
189 ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
190 ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
191 ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
192 ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
193 ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
194 ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
195
196 ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
197 ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
198 ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
199
200 /*
201 * Inode count arrays
202 */
203 ext2_icount_t inode_count;
204 ext2_icount_t inode_link_info;
205
206 ext2_refcount_t refcount;
207 ext2_refcount_t refcount_extra;
208
209 /*
210 * Array of flags indicating whether an inode bitmap, block
211 * bitmap, or inode table is invalid
212 */
213 int *invalid_inode_bitmap_flag;
214 int *invalid_block_bitmap_flag;
215 int *invalid_inode_table_flag;
216 int invalid_bitmaps; /* There are invalid bitmaps/itable */
217
218 /*
219 * Block buffer
220 */
221 char *block_buf;
222
223 /*
224 * For pass1_check_directory and pass1_get_blocks
225 */
226 ext2_ino_t stashed_ino;
227 struct ext2_inode *stashed_inode;
228
229 /*
230 * Location of the lost and found directory
231 */
232 ext2_ino_t lost_and_found;
233 int bad_lost_and_found;
234
235 /*
236 * Directory information
237 */
238 int dir_info_count;
239 int dir_info_size;
240 struct dir_info *dir_info;
241
242 /*
243 * Indexed directory information
244 */
245 int dx_dir_info_count;
246 int dx_dir_info_size;
247 struct dx_dir_info *dx_dir_info;
248
249 /*
250 * Directories to hash
251 */
252 ext2_u32_list dirs_to_hash;
253
254 /*
255 * Tuning parameters
256 */
257 int process_inode_size;
258 int inode_buffer_blocks;
259
260 /*
261 * ext3 journal support
262 */
263 io_channel journal_io;
264 char *journal_name;
265
266#ifdef RESOURCE_TRACK
267 /*
268 * For timing purposes
269 */
270 struct resource_track global_rtrack;
271#endif
272
273 /*
274 * How we display the progress update (for unix)
275 */
276 int progress_fd;
277 int progress_pos;
278 int progress_last_percent;
279 unsigned int progress_last_time;
280 int interactive; /* Are we connected directly to a tty? */
281 char start_meta[2], stop_meta[2];
282
283 /* File counts */
284 int fs_directory_count;
285 int fs_regular_count;
286 int fs_blockdev_count;
287 int fs_chardev_count;
288 int fs_links_count;
289 int fs_symlinks_count;
290 int fs_fast_symlinks_count;
291 int fs_fifo_count;
292 int fs_total_count;
293 int fs_badblocks_count;
294 int fs_sockets_count;
295 int fs_ind_count;
296 int fs_dind_count;
297 int fs_tind_count;
298 int fs_fragmented;
299 int large_files;
300 int fs_ext_attr_inodes;
301 int fs_ext_attr_blocks;
302
303 int ext_attr_ver;
304
305 /*
306 * For the use of callers of the e2fsck functions; not used by
307 * e2fsck functions themselves.
308 */
309 void *priv_data;
310};
311
312/* Used by the region allocation code */
313typedef __u32 region_addr_t;
314typedef struct region_struct *region_t;
315
316#ifndef HAVE_STRNLEN
317#define strnlen(str, x) e2fsck_strnlen((str),(x))
318extern int e2fsck_strnlen(const char * s, int count);
319#endif
320
321/*
322 * Procedure declarations
323 */
324
325extern void e2fsck_pass1(e2fsck_t ctx);
326extern void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
327extern void e2fsck_pass2(e2fsck_t ctx);
328extern void e2fsck_pass3(e2fsck_t ctx);
329extern void e2fsck_pass4(e2fsck_t ctx);
330extern void e2fsck_pass5(e2fsck_t ctx);
331
332/* e2fsck.c */
333extern errcode_t e2fsck_allocate_context(e2fsck_t *ret);
334extern errcode_t e2fsck_reset_context(e2fsck_t ctx);
335extern void e2fsck_free_context(e2fsck_t ctx);
336extern int e2fsck_run(e2fsck_t ctx);
337
338
339/* badblock.c */
340extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
341 int replace_bad_blocks);
342
343/* dirinfo.c */
344extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
345extern struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino);
346extern void e2fsck_free_dir_info(e2fsck_t ctx);
347extern int e2fsck_get_num_dirinfo(e2fsck_t ctx);
348extern struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control);
349
350/* dx_dirinfo.c */
351extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
352extern struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino);
353extern void e2fsck_free_dx_dir_info(e2fsck_t ctx);
354extern int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx);
355extern struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control);
356
357/* ea_refcount.c */
358extern errcode_t ea_refcount_create(int size, ext2_refcount_t *ret);
359extern void ea_refcount_free(ext2_refcount_t refcount);
360extern errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk,
361 int *ret);
362extern errcode_t ea_refcount_increment(ext2_refcount_t refcount,
363 blk_t blk, int *ret);
364extern errcode_t ea_refcount_decrement(ext2_refcount_t refcount,
365 blk_t blk, int *ret);
366extern errcode_t ea_refcount_store(ext2_refcount_t refcount,
367 blk_t blk, int count);
368extern blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount);
369extern void ea_refcount_intr_begin(ext2_refcount_t refcount);
370extern blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret);
371
372/* ehandler.c */
373extern const char *ehandler_operation(const char *op);
374extern void ehandler_init(io_channel channel);
375
376/* journal.c */
377extern int e2fsck_check_ext3_journal(e2fsck_t ctx);
378extern int e2fsck_run_ext3_journal(e2fsck_t ctx);
379extern void e2fsck_move_ext3_journal(e2fsck_t ctx);
380
381/* pass1.c */
382extern void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
383extern int e2fsck_pass1_check_device_inode(ext2_filsys fs,
384 struct ext2_inode *inode);
385extern int e2fsck_pass1_check_symlink(ext2_filsys fs,
386 struct ext2_inode *inode, char *buf);
387
388/* pass2.c */
389extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
390 ext2_ino_t ino, char *buf);
391
392/* pass3.c */
393extern int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
394extern errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
395 int num, int gauranteed_size);
396extern ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
397extern errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
398 int adj);
399
400
401/* region.c */
402extern region_t region_create(region_addr_t min, region_addr_t max);
403extern void region_free(region_t region);
404extern int region_allocate(region_t region, region_addr_t start, int n);
405
406/* rehash.c */
407errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
408void e2fsck_rehash_directories(e2fsck_t ctx);
409
410/* super.c */
411void check_super_block(e2fsck_t ctx);
412errcode_t e2fsck_get_device_size(e2fsck_t ctx);
413
414/* swapfs.c */
415void swap_filesys(e2fsck_t ctx);
416
417/* util.c */
418extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
419 const char *description);
420extern int ask(e2fsck_t ctx, const char * string, int def);
421extern int ask_yn(const char * string, int def);
422extern void e2fsck_read_bitmaps(e2fsck_t ctx);
423extern void e2fsck_write_bitmaps(e2fsck_t ctx);
424extern void preenhalt(e2fsck_t ctx);
425extern char *string_copy(e2fsck_t ctx, const char *str, int len);
426#ifdef RESOURCE_TRACK
427extern void print_resource_track(const char *desc,
428 struct resource_track *track);
429extern void init_resource_track(struct resource_track *track);
430#endif
431extern int inode_has_valid_blocks(struct ext2_inode *inode);
432extern void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
433 struct ext2_inode * inode, const char * proc);
434extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
435 struct ext2_inode * inode, const char * proc);
436#ifdef MTRACE
437extern void mtrace_print(char *mesg);
438#endif
439extern blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
440 const char *name, io_manager manager);
441extern int ext2_file_type(unsigned int mode);
442
443/* unix.c */
444extern void e2fsck_clear_progbar(e2fsck_t ctx);
445extern int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
446 float percent, unsigned int dpynum);
diff --git a/e2fsprogs/e2fsck/ea_refcount.c b/e2fsprogs/e2fsck/ea_refcount.c
new file mode 100644
index 000000000..6420c7280
--- /dev/null
+++ b/e2fsprogs/e2fsck/ea_refcount.c
@@ -0,0 +1,479 @@
1/*
2 * ea_refcount.c
3 *
4 * Copyright (C) 2001 Theodore Ts'o. This file may be
5 * redistributed under the terms of the GNU Public License.
6 */
7
8#if HAVE_UNISTD_H
9#include <unistd.h>
10#endif
11#include <string.h>
12#include <stdio.h>
13
14#include "e2fsck.h"
15
16/*
17 * The strategy we use for keeping track of EA refcounts is as
18 * follows. We keep a sorted array of first EA blocks and its
19 * reference counts. Once the refcount has dropped to zero, it is
20 * removed from the array to save memory space. Once the EA block is
21 * checked, its bit is set in the block_ea_map bitmap.
22 */
23struct ea_refcount_el {
24 blk_t ea_blk;
25 int ea_count;
26};
27
28struct ea_refcount {
29 blk_t count;
30 blk_t size;
31 blk_t cursor;
32 struct ea_refcount_el *list;
33};
34
35void ea_refcount_free(ext2_refcount_t refcount)
36{
37 if (!refcount)
38 return;
39
40 if (refcount->list)
41 ext2fs_free_mem(&refcount->list);
42 ext2fs_free_mem(&refcount);
43}
44
45errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
46{
47 ext2_refcount_t refcount;
48 errcode_t retval;
49 size_t bytes;
50
51 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
52 if (retval)
53 return retval;
54 memset(refcount, 0, sizeof(struct ea_refcount));
55
56 if (!size)
57 size = 500;
58 refcount->size = size;
59 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
60#ifdef DEBUG
61 printf("Refcount allocated %d entries, %d bytes.\n",
62 refcount->size, bytes);
63#endif
64 retval = ext2fs_get_mem(bytes, &refcount->list);
65 if (retval)
66 goto errout;
67 memset(refcount->list, 0, bytes);
68
69 refcount->count = 0;
70 refcount->cursor = 0;
71
72 *ret = refcount;
73 return 0;
74
75errout:
76 ea_refcount_free(refcount);
77 return(retval);
78}
79
80/*
81 * collapse_refcount() --- go through the refcount array, and get rid
82 * of any count == zero entries
83 */
84static void refcount_collapse(ext2_refcount_t refcount)
85{
86 unsigned int i, j;
87 struct ea_refcount_el *list;
88
89 list = refcount->list;
90 for (i = 0, j = 0; i < refcount->count; i++) {
91 if (list[i].ea_count) {
92 if (i != j)
93 list[j] = list[i];
94 j++;
95 }
96 }
97#if defined(DEBUG) || defined(TEST_PROGRAM)
98 printf("Refcount_collapse: size was %d, now %d\n",
99 refcount->count, j);
100#endif
101 refcount->count = j;
102}
103
104
105/*
106 * insert_refcount_el() --- Insert a new entry into the sorted list at a
107 * specified position.
108 */
109static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
110 blk_t blk, int pos)
111{
112 struct ea_refcount_el *el;
113 errcode_t retval;
114 blk_t new_size = 0;
115 int num;
116
117 if (refcount->count >= refcount->size) {
118 new_size = refcount->size + 100;
119#ifdef DEBUG
120 printf("Reallocating refcount %d entries...\n", new_size);
121#endif
122 retval = ext2fs_resize_mem((size_t) refcount->size *
123 sizeof(struct ea_refcount_el),
124 (size_t) new_size *
125 sizeof(struct ea_refcount_el),
126 &refcount->list);
127 if (retval)
128 return 0;
129 refcount->size = new_size;
130 }
131 num = (int) refcount->count - pos;
132 if (num < 0)
133 return 0; /* should never happen */
134 if (num) {
135 memmove(&refcount->list[pos+1], &refcount->list[pos],
136 sizeof(struct ea_refcount_el) * num);
137 }
138 refcount->count++;
139 el = &refcount->list[pos];
140 el->ea_count = 0;
141 el->ea_blk = blk;
142 return el;
143}
144
145
146/*
147 * get_refcount_el() --- given an block number, try to find refcount
148 * information in the sorted list. If the create flag is set,
149 * and we can't find an entry, create one in the sorted list.
150 */
151static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
152 blk_t blk, int create)
153{
154 float range;
155 int low, high, mid;
156 blk_t lowval, highval;
157
158 if (!refcount || !refcount->list)
159 return 0;
160retry:
161 low = 0;
162 high = (int) refcount->count-1;
163 if (create && ((refcount->count == 0) ||
164 (blk > refcount->list[high].ea_blk))) {
165 if (refcount->count >= refcount->size)
166 refcount_collapse(refcount);
167
168 return insert_refcount_el(refcount, blk,
169 (unsigned) refcount->count);
170 }
171 if (refcount->count == 0)
172 return 0;
173
174 if (refcount->cursor >= refcount->count)
175 refcount->cursor = 0;
176 if (blk == refcount->list[refcount->cursor].ea_blk)
177 return &refcount->list[refcount->cursor++];
178#ifdef DEBUG
179 printf("Non-cursor get_refcount_el: %u\n", blk);
180#endif
181 while (low <= high) {
182#if 0
183 mid = (low+high)/2;
184#else
185 if (low == high)
186 mid = low;
187 else {
188 /* Interpolate for efficiency */
189 lowval = refcount->list[low].ea_blk;
190 highval = refcount->list[high].ea_blk;
191
192 if (blk < lowval)
193 range = 0;
194 else if (blk > highval)
195 range = 1;
196 else
197 range = ((float) (blk - lowval)) /
198 (highval - lowval);
199 mid = low + ((int) (range * (high-low)));
200 }
201#endif
202 if (blk == refcount->list[mid].ea_blk) {
203 refcount->cursor = mid+1;
204 return &refcount->list[mid];
205 }
206 if (blk < refcount->list[mid].ea_blk)
207 high = mid-1;
208 else
209 low = mid+1;
210 }
211 /*
212 * If we need to create a new entry, it should be right at
213 * low (where high will be left at low-1).
214 */
215 if (create) {
216 if (refcount->count >= refcount->size) {
217 refcount_collapse(refcount);
218 if (refcount->count < refcount->size)
219 goto retry;
220 }
221 return insert_refcount_el(refcount, blk, low);
222 }
223 return 0;
224}
225
226errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk,
227 int *ret)
228{
229 struct ea_refcount_el *el;
230
231 el = get_refcount_el(refcount, blk, 0);
232 if (!el) {
233 *ret = 0;
234 return 0;
235 }
236 *ret = el->ea_count;
237 return 0;
238}
239
240errcode_t ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
241{
242 struct ea_refcount_el *el;
243
244 el = get_refcount_el(refcount, blk, 1);
245 if (!el)
246 return EXT2_ET_NO_MEMORY;
247 el->ea_count++;
248
249 if (ret)
250 *ret = el->ea_count;
251 return 0;
252}
253
254errcode_t ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
255{
256 struct ea_refcount_el *el;
257
258 el = get_refcount_el(refcount, blk, 0);
259 if (!el || el->ea_count == 0)
260 return EXT2_ET_INVALID_ARGUMENT;
261
262 el->ea_count--;
263
264 if (ret)
265 *ret = el->ea_count;
266 return 0;
267}
268
269errcode_t ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
270{
271 struct ea_refcount_el *el;
272
273 /*
274 * Get the refcount element
275 */
276 el = get_refcount_el(refcount, blk, count ? 1 : 0);
277 if (!el)
278 return count ? EXT2_ET_NO_MEMORY : 0;
279 el->ea_count = count;
280 return 0;
281}
282
283blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount)
284{
285 if (!refcount)
286 return 0;
287
288 return refcount->size;
289}
290
291void ea_refcount_intr_begin(ext2_refcount_t refcount)
292{
293 refcount->cursor = 0;
294}
295
296
297blk_t ea_refcount_intr_next(ext2_refcount_t refcount,
298 int *ret)
299{
300 struct ea_refcount_el *list;
301
302 while (1) {
303 if (refcount->cursor >= refcount->count)
304 return 0;
305 list = refcount->list;
306 if (list[refcount->cursor].ea_count) {
307 if (ret)
308 *ret = list[refcount->cursor].ea_count;
309 return list[refcount->cursor++].ea_blk;
310 }
311 refcount->cursor++;
312 }
313}
314
315
316#ifdef TEST_PROGRAM
317
318errcode_t ea_refcount_validate(ext2_refcount_t refcount, FILE *out)
319{
320 errcode_t ret = 0;
321 int i;
322 const char *bad = "bad refcount";
323
324 if (refcount->count > refcount->size) {
325 fprintf(out, "%s: count > size\n", bad);
326 return EXT2_ET_INVALID_ARGUMENT;
327 }
328 for (i=1; i < refcount->count; i++) {
329 if (refcount->list[i-1].ea_blk >= refcount->list[i].ea_blk) {
330 fprintf(out, "%s: list[%d].blk=%u, list[%d].blk=%u\n",
331 bad, i-1, refcount->list[i-1].ea_blk,
332 i, refcount->list[i].ea_blk);
333 ret = EXT2_ET_INVALID_ARGUMENT;
334 }
335 }
336 return ret;
337}
338
339#define BCODE_END 0
340#define BCODE_CREATE 1
341#define BCODE_FREE 2
342#define BCODE_STORE 3
343#define BCODE_INCR 4
344#define BCODE_DECR 5
345#define BCODE_FETCH 6
346#define BCODE_VALIDATE 7
347#define BCODE_LIST 8
348#define BCODE_COLLAPSE 9
349
350int bcode_program[] = {
351 BCODE_CREATE, 5,
352 BCODE_STORE, 3, 3,
353 BCODE_STORE, 4, 4,
354 BCODE_STORE, 1, 1,
355 BCODE_STORE, 8, 8,
356 BCODE_STORE, 2, 2,
357 BCODE_STORE, 4, 0,
358 BCODE_STORE, 2, 0,
359 BCODE_STORE, 6, 6,
360 BCODE_VALIDATE,
361 BCODE_STORE, 4, 4,
362 BCODE_STORE, 2, 2,
363 BCODE_FETCH, 1,
364 BCODE_FETCH, 2,
365 BCODE_INCR, 3,
366 BCODE_INCR, 3,
367 BCODE_DECR, 4,
368 BCODE_STORE, 4, 4,
369 BCODE_VALIDATE,
370 BCODE_STORE, 20, 20,
371 BCODE_STORE, 40, 40,
372 BCODE_STORE, 30, 30,
373 BCODE_STORE, 10, 10,
374 BCODE_DECR, 30,
375 BCODE_FETCH, 30,
376 BCODE_DECR, 2,
377 BCODE_DECR, 2,
378 BCODE_COLLAPSE,
379 BCODE_LIST,
380 BCODE_VALIDATE,
381 BCODE_FREE,
382 BCODE_END
383};
384
385int main(int argc, char **argv)
386{
387 int i = 0;
388 ext2_refcount_t refcount;
389 int size, arg;
390 blk_t blk;
391 errcode_t retval;
392
393 while (1) {
394 switch (bcode_program[i++]) {
395 case BCODE_END:
396 exit(0);
397 case BCODE_CREATE:
398 size = bcode_program[i++];
399 retval = ea_refcount_create(size, &refcount);
400 if (retval) {
401 com_err("ea_refcount_create",
402 retval, "");
403 exit(1);
404 } else
405 printf("Creating refcount with size %d\n",
406 size);
407 break;
408 case BCODE_FREE:
409 ea_refcount_free(refcount);
410 refcount = 0;
411 printf("Freeing refcount\n");
412 break;
413 case BCODE_STORE:
414 blk = (blk_t) bcode_program[i++];
415 arg = bcode_program[i++];
416 retval = ea_refcount_store(refcount, blk, arg);
417 printf("Storing blk %u with value %d\n", blk, arg);
418 if (retval)
419 com_err("ea_refcount_store", retval, "");
420 break;
421 case BCODE_FETCH:
422 blk = (blk_t) bcode_program[i++];
423 retval = ea_refcount_fetch(refcount, blk, &arg);
424 if (retval)
425 com_err("ea_refcount_fetch", retval, "");
426 else
427 printf("bcode_fetch(%u) returns %d\n",
428 blk, arg);
429 break;
430 case BCODE_INCR:
431 blk = (blk_t) bcode_program[i++];
432 retval = ea_refcount_increment(refcount, blk,
433 &arg);
434 if (retval)
435 com_err("ea_refcount_increment", retval,
436 "");
437 else
438 printf("bcode_increment(%u) returns %d\n",
439 blk, arg);
440 break;
441 case BCODE_DECR:
442 blk = (blk_t) bcode_program[i++];
443 retval = ea_refcount_decrement(refcount, blk,
444 &arg);
445 if (retval)
446 com_err("ea_refcount_decrement", retval,
447 "while decrementing blk %u", blk);
448 else
449 printf("bcode_decrement(%u) returns %d\n",
450 blk, arg);
451 break;
452 case BCODE_VALIDATE:
453 retval = ea_refcount_validate(refcount, stderr);
454 if (retval)
455 com_err("ea_refcount_validate",
456 retval, "");
457 else
458 printf("Refcount validation OK.\n");
459 break;
460 case BCODE_LIST:
461 ea_refcount_intr_begin(refcount);
462 while (1) {
463 blk = ea_refcount_intr_next(refcount,
464 &arg);
465 if (!blk)
466 break;
467 printf("\tblk=%u, count=%d\n", blk,
468 arg);
469 }
470 break;
471 case BCODE_COLLAPSE:
472 refcount_collapse(refcount);
473 break;
474 }
475
476 }
477}
478
479#endif
diff --git a/e2fsprogs/e2fsck/ehandler.c b/e2fsprogs/e2fsck/ehandler.c
new file mode 100644
index 000000000..16d6d4fa0
--- /dev/null
+++ b/e2fsprogs/e2fsck/ehandler.c
@@ -0,0 +1,124 @@
1/*
2 * ehandler.c --- handle bad block errors which come up during the
3 * course of an e2fsck session.
4 *
5 * Copyright (C) 1994 Theodore Ts'o. This file may be redistributed
6 * under the terms of the GNU Public License.
7 */
8
9#include <stdlib.h>
10#include <unistd.h>
11#include <string.h>
12#include <ctype.h>
13#include <termios.h>
14
15#include "e2fsck.h"
16
17#include <sys/time.h>
18#include <sys/resource.h>
19
20static const char *operation;
21
22static errcode_t e2fsck_handle_read_error(io_channel channel,
23 unsigned long block,
24 int count,
25 void *data,
26 size_t size EXT2FS_ATTR((unused)),
27 int actual EXT2FS_ATTR((unused)),
28 errcode_t error)
29{
30 int i;
31 char *p;
32 ext2_filsys fs = (ext2_filsys) channel->app_data;
33 e2fsck_t ctx;
34
35 ctx = (e2fsck_t) fs->priv_data;
36
37 /*
38 * If more than one block was read, try reading each block
39 * separately. We could use the actual bytes read to figure
40 * out where to start, but we don't bother.
41 */
42 if (count > 1) {
43 p = (char *) data;
44 for (i=0; i < count; i++, p += channel->block_size, block++) {
45 error = io_channel_read_blk(channel, block,
46 1, p);
47 if (error)
48 return error;
49 }
50 return 0;
51 }
52 if (operation)
53 printf(_("Error reading block %lu (%s) while %s. "), block,
54 error_message(error), operation);
55 else
56 printf(_("Error reading block %lu (%s). "), block,
57 error_message(error));
58 preenhalt(ctx);
59 if (ask(ctx, _("Ignore error"), 1)) {
60 if (ask(ctx, _("Force rewrite"), 1))
61 io_channel_write_blk(channel, block, 1, data);
62 return 0;
63 }
64
65 return error;
66}
67
68static errcode_t e2fsck_handle_write_error(io_channel channel,
69 unsigned long block,
70 int count,
71 const void *data,
72 size_t size EXT2FS_ATTR((unused)),
73 int actual EXT2FS_ATTR((unused)),
74 errcode_t error)
75{
76 int i;
77 const char *p;
78 ext2_filsys fs = (ext2_filsys) channel->app_data;
79 e2fsck_t ctx;
80
81 ctx = (e2fsck_t) fs->priv_data;
82
83 /*
84 * If more than one block was written, try writing each block
85 * separately. We could use the actual bytes read to figure
86 * out where to start, but we don't bother.
87 */
88 if (count > 1) {
89 p = (const char *) data;
90 for (i=0; i < count; i++, p += channel->block_size, block++) {
91 error = io_channel_write_blk(channel, block,
92 1, p);
93 if (error)
94 return error;
95 }
96 return 0;
97 }
98
99 if (operation)
100 printf(_("Error writing block %lu (%s) while %s. "), block,
101 error_message(error), operation);
102 else
103 printf(_("Error writing block %lu (%s). "), block,
104 error_message(error));
105 preenhalt(ctx);
106 if (ask(ctx, _("Ignore error"), 1))
107 return 0;
108
109 return error;
110}
111
112const char *ehandler_operation(const char *op)
113{
114 const char *ret = operation;
115
116 operation = op;
117 return ret;
118}
119
120void ehandler_init(io_channel channel)
121{
122 channel->read_error = e2fsck_handle_read_error;
123 channel->write_error = e2fsck_handle_write_error;
124}
diff --git a/e2fsprogs/e2fsck/jfs_user.h b/e2fsprogs/e2fsck/jfs_user.h
new file mode 100644
index 000000000..c38def3e5
--- /dev/null
+++ b/e2fsprogs/e2fsck/jfs_user.h
@@ -0,0 +1,120 @@
1/*
2 * Compatibility header file for e2fsck which should be included
3 * instead of linux/jfs.h
4 *
5 * Copyright (C) 2000 Stephen C. Tweedie
6 *
7 * This file may be redistributed under the terms of the
8 * GNU General Public License version 2 or at your discretion
9 * any later version.
10 */
11
12/*
13 * Pull in the definition of the e2fsck context structure
14 */
15#include "e2fsck.h"
16
17struct buffer_head {
18 char b_data[8192];
19 e2fsck_t b_ctx;
20 io_channel b_io;
21 int b_size;
22 blk_t b_blocknr;
23 int b_dirty;
24 int b_uptodate;
25 int b_err;
26};
27
28struct inode {
29 e2fsck_t i_ctx;
30 ext2_ino_t i_ino;
31 struct ext2_inode i_ext2;
32};
33
34struct kdev_s {
35 e2fsck_t k_ctx;
36 int k_dev;
37};
38
39#define K_DEV_FS 1
40#define K_DEV_JOURNAL 2
41
42typedef struct kdev_s *kdev_t;
43
44#define lock_buffer(bh) do {} while(0)
45#define unlock_buffer(bh) do {} while(0)
46#define buffer_req(bh) 1
47#define do_readahead(journal, start) do {} while(0)
48
49extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
50
51typedef struct {
52 int object_length;
53} kmem_cache_t;
54
55#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
56#define kmem_cache_free(cache,obj) free(obj)
57#define kmem_cache_create(name,len,a,b,c,d) do_cache_create(len)
58#define kmem_cache_destroy(cache) do_cache_destroy(cache)
59#define kmalloc(len,flags) malloc(len)
60#define kfree(p) free(p)
61
62/*
63 * We use the standard libext2fs portability tricks for inline
64 * functions.
65 */
66extern kmem_cache_t * do_cache_create(int len);
67extern void do_cache_destroy(kmem_cache_t *cache);
68
69#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
70#ifdef E2FSCK_INCLUDE_INLINE_FUNCS
71#define _INLINE_ extern
72#else
73#ifdef __GNUC__
74#define _INLINE_ extern __inline__
75#else /* For Watcom C */
76#define _INLINE_ extern inline
77#endif
78#endif
79
80_INLINE_ kmem_cache_t * do_cache_create(int len)
81{
82 kmem_cache_t *new_cache;
83 new_cache = malloc(sizeof(*new_cache));
84 if (new_cache)
85 new_cache->object_length = len;
86 return new_cache;
87}
88
89_INLINE_ void do_cache_destroy(kmem_cache_t *cache)
90{
91 free(cache);
92}
93#undef _INLINE_
94#endif
95
96#define __init
97
98/*
99 * Now pull in the real linux/jfs.h definitions.
100 */
101#include <ext2fs/kernel-jbd.h>
102
103/*
104 * Kernel compatibility functions are defined in journal.c
105 */
106int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys);
107struct buffer_head *getblk(kdev_t ctx, blk_t blocknr, int blocksize);
108void sync_blockdev(kdev_t kdev);
109void ll_rw_block(int rw, int dummy, struct buffer_head *bh[]);
110void mark_buffer_dirty(struct buffer_head *bh);
111void mark_buffer_uptodate(struct buffer_head *bh, int val);
112void brelse(struct buffer_head *bh);
113int buffer_uptodate(struct buffer_head *bh);
114void wait_on_buffer(struct buffer_head *bh);
115
116/*
117 * Define newer 2.5 interfaces
118 */
119#define __getblk(dev, blocknr, blocksize) getblk(dev, blocknr, blocksize)
120#define set_buffer_uptodate(bh) mark_buffer_uptodate(bh, 1)
diff --git a/e2fsprogs/e2fsck/journal.c b/e2fsprogs/e2fsck/journal.c
new file mode 100644
index 000000000..14e774d50
--- /dev/null
+++ b/e2fsprogs/e2fsck/journal.c
@@ -0,0 +1,960 @@
1/*
2 * journal.c --- code for handling the "ext3" journal
3 *
4 * Copyright (C) 2000 Andreas Dilger
5 * Copyright (C) 2000 Theodore Ts'o
6 *
7 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
8 * Copyright (C) 1999 Red Hat Software
9 *
10 * This file may be redistributed under the terms of the
11 * GNU General Public License version 2 or at your discretion
12 * any later version.
13 */
14
15#ifdef HAVE_SYS_MOUNT_H
16#include <sys/param.h>
17#include <sys/mount.h>
18#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
19#endif
20#ifdef HAVE_SYS_STAT_H
21#include <sys/stat.h>
22#endif
23
24#define E2FSCK_INCLUDE_INLINE_FUNCS
25#include "jfs_user.h"
26#include "problem.h"
27#include "uuid/uuid.h"
28
29#ifdef __CONFIG_JBD_DEBUG__E2FS /* Enabled by configure --enable-jfs-debug */
30static int bh_count = 0;
31#endif
32
33/*
34 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
35 * This creates a larger static binary, and a smaller binary using
36 * shared libraries. It's also probably slightly less CPU-efficient,
37 * which is why it's not on by default. But, it's a good way of
38 * testing the functions in inode_io.c and fileio.c.
39 */
40#undef USE_INODE_IO
41
42/* Kernel compatibility functions for handling the journal. These allow us
43 * to use the recovery.c file virtually unchanged from the kernel, so we
44 * don't have to do much to keep kernel and user recovery in sync.
45 */
46int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
47{
48#ifdef USE_INODE_IO
49 *phys = block;
50 return 0;
51#else
52 struct inode *inode = journal->j_inode;
53 errcode_t retval;
54 blk_t pblk;
55
56 if (!inode) {
57 *phys = block;
58 return 0;
59 }
60
61 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
62 &inode->i_ext2, NULL, 0, block, &pblk);
63 *phys = pblk;
64 return (retval);
65#endif
66}
67
68struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
69{
70 struct buffer_head *bh;
71
72 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
73 if (!bh)
74 return NULL;
75
76 jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n",
77 (unsigned long) blocknr, blocksize, ++bh_count);
78
79 bh->b_ctx = kdev->k_ctx;
80 if (kdev->k_dev == K_DEV_FS)
81 bh->b_io = kdev->k_ctx->fs->io;
82 else
83 bh->b_io = kdev->k_ctx->journal_io;
84 bh->b_size = blocksize;
85 bh->b_blocknr = blocknr;
86
87 return bh;
88}
89
90void sync_blockdev(kdev_t kdev)
91{
92 io_channel io;
93
94 if (kdev->k_dev == K_DEV_FS)
95 io = kdev->k_ctx->fs->io;
96 else
97 io = kdev->k_ctx->journal_io;
98
99 io_channel_flush(io);
100}
101
102void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
103{
104 int retval;
105 struct buffer_head *bh;
106
107 for (; nr > 0; --nr) {
108 bh = *bhp++;
109 if (rw == READ && !bh->b_uptodate) {
110 jfs_debug(3, "reading block %lu/%p\n",
111 (unsigned long) bh->b_blocknr, (void *) bh);
112 retval = io_channel_read_blk(bh->b_io,
113 bh->b_blocknr,
114 1, bh->b_data);
115 if (retval) {
116 com_err(bh->b_ctx->device_name, retval,
117 "while reading block %lu\n",
118 (unsigned long) bh->b_blocknr);
119 bh->b_err = retval;
120 continue;
121 }
122 bh->b_uptodate = 1;
123 } else if (rw == WRITE && bh->b_dirty) {
124 jfs_debug(3, "writing block %lu/%p\n",
125 (unsigned long) bh->b_blocknr, (void *) bh);
126 retval = io_channel_write_blk(bh->b_io,
127 bh->b_blocknr,
128 1, bh->b_data);
129 if (retval) {
130 com_err(bh->b_ctx->device_name, retval,
131 "while writing block %lu\n",
132 (unsigned long) bh->b_blocknr);
133 bh->b_err = retval;
134 continue;
135 }
136 bh->b_dirty = 0;
137 bh->b_uptodate = 1;
138 } else {
139 jfs_debug(3, "no-op %s for block %lu\n",
140 rw == READ ? "read" : "write",
141 (unsigned long) bh->b_blocknr);
142 }
143 }
144}
145
146void mark_buffer_dirty(struct buffer_head *bh)
147{
148 bh->b_dirty = 1;
149}
150
151static void mark_buffer_clean(struct buffer_head * bh)
152{
153 bh->b_dirty = 0;
154}
155
156void brelse(struct buffer_head *bh)
157{
158 if (bh->b_dirty)
159 ll_rw_block(WRITE, 1, &bh);
160 jfs_debug(3, "freeing block %lu/%p (total %d)\n",
161 (unsigned long) bh->b_blocknr, (void *) bh, --bh_count);
162 ext2fs_free_mem(&bh);
163}
164
165int buffer_uptodate(struct buffer_head *bh)
166{
167 return bh->b_uptodate;
168}
169
170void mark_buffer_uptodate(struct buffer_head *bh, int val)
171{
172 bh->b_uptodate = val;
173}
174
175void wait_on_buffer(struct buffer_head *bh)
176{
177 if (!bh->b_uptodate)
178 ll_rw_block(READ, 1, &bh);
179}
180
181
182static void e2fsck_clear_recover(e2fsck_t ctx, int error)
183{
184 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
185
186 /* if we had an error doing journal recovery, we need a full fsck */
187 if (error)
188 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
189 ext2fs_mark_super_dirty(ctx->fs);
190}
191
192static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
193{
194 struct ext2_super_block *sb = ctx->fs->super;
195 struct ext2_super_block jsuper;
196 struct problem_context pctx;
197 struct buffer_head *bh;
198 struct inode *j_inode = NULL;
199 struct kdev_s *dev_fs = NULL, *dev_journal;
200 const char *journal_name = 0;
201 journal_t *journal = NULL;
202 errcode_t retval = 0;
203 io_manager io_ptr = 0;
204 unsigned long start = 0;
205 blk_t blk;
206 int ext_journal = 0;
207 int tried_backup_jnl = 0;
208 int i;
209
210 clear_problem_context(&pctx);
211
212 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
213 if (!journal) {
214 return EXT2_ET_NO_MEMORY;
215 }
216
217 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
218 if (!dev_fs) {
219 retval = EXT2_ET_NO_MEMORY;
220 goto errout;
221 }
222 dev_journal = dev_fs+1;
223
224 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
225 dev_fs->k_dev = K_DEV_FS;
226 dev_journal->k_dev = K_DEV_JOURNAL;
227
228 journal->j_dev = dev_journal;
229 journal->j_fs_dev = dev_fs;
230 journal->j_inode = NULL;
231 journal->j_blocksize = ctx->fs->blocksize;
232
233 if (uuid_is_null(sb->s_journal_uuid)) {
234 if (!sb->s_journal_inum)
235 return EXT2_ET_BAD_INODE_NUM;
236 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
237 "journal inode");
238 if (!j_inode) {
239 retval = EXT2_ET_NO_MEMORY;
240 goto errout;
241 }
242
243 j_inode->i_ctx = ctx;
244 j_inode->i_ino = sb->s_journal_inum;
245
246 if ((retval = ext2fs_read_inode(ctx->fs,
247 sb->s_journal_inum,
248 &j_inode->i_ext2))) {
249 try_backup_journal:
250 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
251 tried_backup_jnl)
252 goto errout;
253 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
254 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
255 EXT2_N_BLOCKS*4);
256 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
257 j_inode->i_ext2.i_links_count = 1;
258 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
259 tried_backup_jnl++;
260 }
261 if (!j_inode->i_ext2.i_links_count ||
262 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
263 retval = EXT2_ET_NO_JOURNAL;
264 goto try_backup_journal;
265 }
266 if (j_inode->i_ext2.i_size / journal->j_blocksize <
267 JFS_MIN_JOURNAL_BLOCKS) {
268 retval = EXT2_ET_JOURNAL_TOO_SMALL;
269 goto try_backup_journal;
270 }
271 for (i=0; i < EXT2_N_BLOCKS; i++) {
272 blk = j_inode->i_ext2.i_block[i];
273 if (!blk) {
274 if (i < EXT2_NDIR_BLOCKS) {
275 retval = EXT2_ET_JOURNAL_TOO_SMALL;
276 goto try_backup_journal;
277 }
278 continue;
279 }
280 if (blk < sb->s_first_data_block ||
281 blk >= sb->s_blocks_count) {
282 retval = EXT2_ET_BAD_BLOCK_NUM;
283 goto try_backup_journal;
284 }
285 }
286 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
287
288#ifdef USE_INODE_IO
289 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
290 &j_inode->i_ext2,
291 &journal_name);
292 if (retval)
293 goto errout;
294
295 io_ptr = inode_io_manager;
296#else
297 journal->j_inode = j_inode;
298 ctx->journal_io = ctx->fs->io;
299 if ((retval = journal_bmap(journal, 0, &start)) != 0)
300 goto errout;
301#endif
302 } else {
303 ext_journal = 1;
304 if (!ctx->journal_name) {
305 char uuid[37];
306
307 uuid_unparse(sb->s_journal_uuid, uuid);
308 ctx->journal_name = blkid_get_devname(ctx->blkid,
309 "UUID", uuid);
310 if (!ctx->journal_name)
311 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
312 }
313 journal_name = ctx->journal_name;
314
315 if (!journal_name) {
316 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
317 return EXT2_ET_LOAD_EXT_JOURNAL;
318 }
319
320 jfs_debug(1, "Using journal file %s\n", journal_name);
321 io_ptr = unix_io_manager;
322 }
323
324#if 0
325 test_io_backing_manager = io_ptr;
326 io_ptr = test_io_manager;
327#endif
328#ifndef USE_INODE_IO
329 if (ext_journal)
330#endif
331 retval = io_ptr->open(journal_name, IO_FLAG_RW,
332 &ctx->journal_io);
333 if (retval)
334 goto errout;
335
336 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
337
338 if (ext_journal) {
339 if (ctx->fs->blocksize == 1024)
340 start = 1;
341 bh = getblk(dev_journal, start, ctx->fs->blocksize);
342 if (!bh) {
343 retval = EXT2_ET_NO_MEMORY;
344 goto errout;
345 }
346 ll_rw_block(READ, 1, &bh);
347 if ((retval = bh->b_err) != 0)
348 goto errout;
349 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
350 sizeof(jsuper));
351 brelse(bh);
352#ifdef EXT2FS_ENABLE_SWAPFS
353 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
354 ext2fs_swap_super(&jsuper);
355#endif
356 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
357 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
358 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
359 retval = EXT2_ET_LOAD_EXT_JOURNAL;
360 goto errout;
361 }
362 /* Make sure the journal UUID is correct */
363 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
364 sizeof(jsuper.s_uuid))) {
365 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
366 retval = EXT2_ET_LOAD_EXT_JOURNAL;
367 goto errout;
368 }
369
370 journal->j_maxlen = jsuper.s_blocks_count;
371 start++;
372 }
373
374 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
375 retval = EXT2_ET_NO_MEMORY;
376 goto errout;
377 }
378
379 journal->j_sb_buffer = bh;
380 journal->j_superblock = (journal_superblock_t *)bh->b_data;
381
382#ifdef USE_INODE_IO
383 if (j_inode)
384 ext2fs_free_mem(&j_inode);
385#endif
386
387 *ret_journal = journal;
388 return 0;
389
390errout:
391 if (dev_fs)
392 ext2fs_free_mem(&dev_fs);
393 if (j_inode)
394 ext2fs_free_mem(&j_inode);
395 if (journal)
396 ext2fs_free_mem(&journal);
397 return retval;
398
399}
400
401static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
402 struct problem_context *pctx)
403{
404 struct ext2_super_block *sb = ctx->fs->super;
405 int recover = ctx->fs->super->s_feature_incompat &
406 EXT3_FEATURE_INCOMPAT_RECOVER;
407 int has_journal = ctx->fs->super->s_feature_compat &
408 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
409
410 if (has_journal || sb->s_journal_inum) {
411 /* The journal inode is bogus, remove and force full fsck */
412 pctx->ino = sb->s_journal_inum;
413 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
414 if (has_journal && sb->s_journal_inum)
415 printf("*** ext3 journal has been deleted - "
416 "filesystem is now ext2 only ***\n\n");
417 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
418 sb->s_journal_inum = 0;
419 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
420 e2fsck_clear_recover(ctx, 1);
421 return 0;
422 }
423 return EXT2_ET_BAD_INODE_NUM;
424 } else if (recover) {
425 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
426 e2fsck_clear_recover(ctx, 1);
427 return 0;
428 }
429 return EXT2_ET_UNSUPP_FEATURE;
430 }
431 return 0;
432}
433
434#define V1_SB_SIZE 0x0024
435static void clear_v2_journal_fields(journal_t *journal)
436{
437 e2fsck_t ctx = journal->j_dev->k_ctx;
438 struct problem_context pctx;
439
440 clear_problem_context(&pctx);
441
442 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
443 return;
444
445 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
446 ctx->fs->blocksize-V1_SB_SIZE);
447 mark_buffer_dirty(journal->j_sb_buffer);
448}
449
450
451static errcode_t e2fsck_journal_load(journal_t *journal)
452{
453 e2fsck_t ctx = journal->j_dev->k_ctx;
454 journal_superblock_t *jsb;
455 struct buffer_head *jbh = journal->j_sb_buffer;
456 struct problem_context pctx;
457
458 clear_problem_context(&pctx);
459
460 ll_rw_block(READ, 1, &jbh);
461 if (jbh->b_err) {
462 com_err(ctx->device_name, jbh->b_err,
463 _("reading journal superblock\n"));
464 return jbh->b_err;
465 }
466
467 jsb = journal->j_superblock;
468 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
469 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
470 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
471
472 switch (ntohl(jsb->s_header.h_blocktype)) {
473 case JFS_SUPERBLOCK_V1:
474 journal->j_format_version = 1;
475 if (jsb->s_feature_compat ||
476 jsb->s_feature_incompat ||
477 jsb->s_feature_ro_compat ||
478 jsb->s_nr_users)
479 clear_v2_journal_fields(journal);
480 break;
481
482 case JFS_SUPERBLOCK_V2:
483 journal->j_format_version = 2;
484 if (ntohl(jsb->s_nr_users) > 1 &&
485 uuid_is_null(ctx->fs->super->s_journal_uuid))
486 clear_v2_journal_fields(journal);
487 if (ntohl(jsb->s_nr_users) > 1) {
488 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
489 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
490 }
491 break;
492
493 /*
494 * These should never appear in a journal super block, so if
495 * they do, the journal is badly corrupted.
496 */
497 case JFS_DESCRIPTOR_BLOCK:
498 case JFS_COMMIT_BLOCK:
499 case JFS_REVOKE_BLOCK:
500 return EXT2_ET_CORRUPT_SUPERBLOCK;
501
502 /* If we don't understand the superblock major type, but there
503 * is a magic number, then it is likely to be a new format we
504 * just don't understand, so leave it alone. */
505 default:
506 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
507 }
508
509 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
510 return EXT2_ET_UNSUPP_FEATURE;
511
512 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
513 return EXT2_ET_RO_UNSUPP_FEATURE;
514
515 /* We have now checked whether we know enough about the journal
516 * format to be able to proceed safely, so any other checks that
517 * fail we should attempt to recover from. */
518 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
519 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
520 _("%s: no valid journal superblock found\n"),
521 ctx->device_name);
522 return EXT2_ET_CORRUPT_SUPERBLOCK;
523 }
524
525 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
526 journal->j_maxlen = ntohl(jsb->s_maxlen);
527 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
528 com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
529 _("%s: journal too short\n"),
530 ctx->device_name);
531 return EXT2_ET_CORRUPT_SUPERBLOCK;
532 }
533
534 journal->j_tail_sequence = ntohl(jsb->s_sequence);
535 journal->j_transaction_sequence = journal->j_tail_sequence;
536 journal->j_tail = ntohl(jsb->s_start);
537 journal->j_first = ntohl(jsb->s_first);
538 journal->j_last = ntohl(jsb->s_maxlen);
539
540 return 0;
541}
542
543static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
544 journal_t *journal)
545{
546 char *p;
547 union {
548 uuid_t uuid;
549 __u32 val[4];
550 } u;
551 __u32 new_seq = 0;
552 int i;
553
554 /* Leave a valid existing V1 superblock signature alone.
555 * Anything unrecognisable we overwrite with a new V2
556 * signature. */
557
558 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
559 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
560 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
561 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
562 }
563
564 /* Zero out everything else beyond the superblock header */
565
566 p = ((char *) jsb) + sizeof(journal_header_t);
567 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
568
569 jsb->s_blocksize = htonl(ctx->fs->blocksize);
570 jsb->s_maxlen = htonl(journal->j_maxlen);
571 jsb->s_first = htonl(1);
572
573 /* Initialize the journal sequence number so that there is "no"
574 * chance we will find old "valid" transactions in the journal.
575 * This avoids the need to zero the whole journal (slow to do,
576 * and risky when we are just recovering the filesystem).
577 */
578 uuid_generate(u.uuid);
579 for (i = 0; i < 4; i ++)
580 new_seq ^= u.val[i];
581 jsb->s_sequence = htonl(new_seq);
582
583 mark_buffer_dirty(journal->j_sb_buffer);
584 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
585}
586
587static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
588 journal_t *journal,
589 struct problem_context *pctx)
590{
591 struct ext2_super_block *sb = ctx->fs->super;
592 int recover = ctx->fs->super->s_feature_incompat &
593 EXT3_FEATURE_INCOMPAT_RECOVER;
594
595 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
596 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
597 e2fsck_journal_reset_super(ctx, journal->j_superblock,
598 journal);
599 journal->j_transaction_sequence = 1;
600 e2fsck_clear_recover(ctx, recover);
601 return 0;
602 }
603 return EXT2_ET_CORRUPT_SUPERBLOCK;
604 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
605 return EXT2_ET_CORRUPT_SUPERBLOCK;
606
607 return 0;
608}
609
610static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
611 int reset, int drop)
612{
613 journal_superblock_t *jsb;
614
615 if (drop)
616 mark_buffer_clean(journal->j_sb_buffer);
617 else if (!(ctx->options & E2F_OPT_READONLY)) {
618 jsb = journal->j_superblock;
619 jsb->s_sequence = htonl(journal->j_transaction_sequence);
620 if (reset)
621 jsb->s_start = 0; /* this marks the journal as empty */
622 mark_buffer_dirty(journal->j_sb_buffer);
623 }
624 brelse(journal->j_sb_buffer);
625
626 if (ctx->journal_io) {
627 if (ctx->fs && ctx->fs->io != ctx->journal_io)
628 io_channel_close(ctx->journal_io);
629 ctx->journal_io = 0;
630 }
631
632#ifndef USE_INODE_IO
633 if (journal->j_inode)
634 ext2fs_free_mem(&journal->j_inode);
635#endif
636 if (journal->j_fs_dev)
637 ext2fs_free_mem(&journal->j_fs_dev);
638 ext2fs_free_mem(&journal);
639}
640
641/*
642 * This function makes sure that the superblock fields regarding the
643 * journal are consistent.
644 */
645int e2fsck_check_ext3_journal(e2fsck_t ctx)
646{
647 struct ext2_super_block *sb = ctx->fs->super;
648 journal_t *journal;
649 int recover = ctx->fs->super->s_feature_incompat &
650 EXT3_FEATURE_INCOMPAT_RECOVER;
651 struct problem_context pctx;
652 problem_t problem;
653 int reset = 0, force_fsck = 0;
654 int retval;
655
656 /* If we don't have any journal features, don't do anything more */
657 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
658 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
659 uuid_is_null(sb->s_journal_uuid))
660 return 0;
661
662 clear_problem_context(&pctx);
663 pctx.num = sb->s_journal_inum;
664
665 retval = e2fsck_get_journal(ctx, &journal);
666 if (retval) {
667 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
668 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
669 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
670 (retval == EXT2_ET_NO_JOURNAL))
671 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
672 return retval;
673 }
674
675 retval = e2fsck_journal_load(journal);
676 if (retval) {
677 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
678 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
679 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
680 &pctx))) ||
681 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
682 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
683 &pctx))) ||
684 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
685 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
686 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
687 &pctx);
688 e2fsck_journal_release(ctx, journal, 0, 1);
689 return retval;
690 }
691
692 /*
693 * We want to make the flags consistent here. We will not leave with
694 * needs_recovery set but has_journal clear. We can't get in a loop
695 * with -y, -n, or -p, only if a user isn't making up their mind.
696 */
697no_has_journal:
698 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
699 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
700 pctx.str = "inode";
701 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
702 if (recover &&
703 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
704 goto no_has_journal;
705 /*
706 * Need a full fsck if we are releasing a
707 * journal stored on a reserved inode.
708 */
709 force_fsck = recover ||
710 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
711 /* Clear all of the journal fields */
712 sb->s_journal_inum = 0;
713 sb->s_journal_dev = 0;
714 memset(sb->s_journal_uuid, 0,
715 sizeof(sb->s_journal_uuid));
716 e2fsck_clear_recover(ctx, force_fsck);
717 } else if (!(ctx->options & E2F_OPT_READONLY)) {
718 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
719 ext2fs_mark_super_dirty(ctx->fs);
720 }
721 }
722
723 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
724 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
725 journal->j_superblock->s_start != 0) {
726 /* Print status information */
727 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
728 if (ctx->superblock)
729 problem = PR_0_JOURNAL_RUN_DEFAULT;
730 else
731 problem = PR_0_JOURNAL_RUN;
732 if (fix_problem(ctx, problem, &pctx)) {
733 ctx->options |= E2F_OPT_FORCE;
734 sb->s_feature_incompat |=
735 EXT3_FEATURE_INCOMPAT_RECOVER;
736 ext2fs_mark_super_dirty(ctx->fs);
737 } else if (fix_problem(ctx,
738 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
739 reset = 1;
740 sb->s_state &= ~EXT2_VALID_FS;
741 ext2fs_mark_super_dirty(ctx->fs);
742 }
743 /*
744 * If the user answers no to the above question, we
745 * ignore the fact that journal apparently has data;
746 * accidentally replaying over valid data would be far
747 * worse than skipping a questionable recovery.
748 *
749 * XXX should we abort with a fatal error here? What
750 * will the ext3 kernel code do if a filesystem with
751 * !NEEDS_RECOVERY but with a non-zero
752 * journal->j_superblock->s_start is mounted?
753 */
754 }
755
756 e2fsck_journal_release(ctx, journal, reset, 0);
757 return retval;
758}
759
760static errcode_t recover_ext3_journal(e2fsck_t ctx)
761{
762 journal_t *journal;
763 int retval;
764
765 journal_init_revoke_caches();
766 retval = e2fsck_get_journal(ctx, &journal);
767 if (retval)
768 return retval;
769
770 retval = e2fsck_journal_load(journal);
771 if (retval)
772 goto errout;
773
774 retval = journal_init_revoke(journal, 1024);
775 if (retval)
776 goto errout;
777
778 retval = -journal_recover(journal);
779 if (retval)
780 goto errout;
781
782 if (journal->j_superblock->s_errno) {
783 ctx->fs->super->s_state |= EXT2_ERROR_FS;
784 ext2fs_mark_super_dirty(ctx->fs);
785 journal->j_superblock->s_errno = 0;
786 mark_buffer_dirty(journal->j_sb_buffer);
787 }
788
789errout:
790 journal_destroy_revoke(journal);
791 journal_destroy_revoke_caches();
792 e2fsck_journal_release(ctx, journal, 1, 0);
793 return retval;
794}
795
796int e2fsck_run_ext3_journal(e2fsck_t ctx)
797{
798 io_manager io_ptr = ctx->fs->io->manager;
799 int blocksize = ctx->fs->blocksize;
800 errcode_t retval, recover_retval;
801
802 printf(_("%s: recovering journal\n"), ctx->device_name);
803 if (ctx->options & E2F_OPT_READONLY) {
804 printf(_("%s: won't do journal recovery while read-only\n"),
805 ctx->device_name);
806 return EXT2_ET_FILE_RO;
807 }
808
809 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
810 ext2fs_flush(ctx->fs); /* Force out any modifications */
811
812 recover_retval = recover_ext3_journal(ctx);
813
814 /*
815 * Reload the filesystem context to get up-to-date data from disk
816 * because journal recovery will change the filesystem under us.
817 */
818 ext2fs_close(ctx->fs);
819 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
820 ctx->superblock, blocksize, io_ptr,
821 &ctx->fs);
822
823 if (retval) {
824 com_err(ctx->program_name, retval,
825 _("while trying to re-open %s"),
826 ctx->device_name);
827 fatal_error(ctx, 0);
828 }
829 ctx->fs->priv_data = ctx;
830
831 /* Set the superblock flags */
832 e2fsck_clear_recover(ctx, recover_retval);
833 return recover_retval;
834}
835
836/*
837 * This function will move the journal inode from a visible file in
838 * the filesystem directory hierarchy to the reserved inode if necessary.
839 */
840static const char * const journal_names[] = {
841 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
842
843void e2fsck_move_ext3_journal(e2fsck_t ctx)
844{
845 struct ext2_super_block *sb = ctx->fs->super;
846 struct problem_context pctx;
847 struct ext2_inode inode;
848 ext2_filsys fs = ctx->fs;
849 ext2_ino_t ino;
850 errcode_t retval;
851 const char * const * cpp;
852 int group, mount_flags;
853
854 clear_problem_context(&pctx);
855
856 /*
857 * If the filesystem is opened read-only, or there is no
858 * journal, then do nothing.
859 */
860 if ((ctx->options & E2F_OPT_READONLY) ||
861 (sb->s_journal_inum == 0) ||
862 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
863 return;
864
865 /*
866 * Read in the journal inode
867 */
868 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
869 return;
870
871 /*
872 * If it's necessary to backup the journal inode, do so.
873 */
874 if ((sb->s_jnl_backup_type == 0) ||
875 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
876 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
877 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
878 memcpy(sb->s_jnl_blocks, inode.i_block,
879 EXT2_N_BLOCKS*4);
880 sb->s_jnl_blocks[16] = inode.i_size;
881 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
882 ext2fs_mark_super_dirty(fs);
883 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
884 }
885 }
886
887 /*
888 * If the journal is already the hidden inode, then do nothing
889 */
890 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
891 return;
892
893 /*
894 * The journal inode had better have only one link and not be readable.
895 */
896 if (inode.i_links_count != 1)
897 return;
898
899 /*
900 * If the filesystem is mounted, or we can't tell whether
901 * or not it's mounted, do nothing.
902 */
903 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
904 if (retval || (mount_flags & EXT2_MF_MOUNTED))
905 return;
906
907 /*
908 * If we can't find the name of the journal inode, then do
909 * nothing.
910 */
911 for (cpp = journal_names; *cpp; cpp++) {
912 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
913 strlen(*cpp), 0, &ino);
914 if ((retval == 0) && (ino == sb->s_journal_inum))
915 break;
916 }
917 if (*cpp == 0)
918 return;
919
920 /* We need the inode bitmap to be loaded */
921 retval = ext2fs_read_bitmaps(fs);
922 if (retval)
923 return;
924
925 pctx.str = *cpp;
926 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
927 return;
928
929 /*
930 * OK, we've done all the checks, let's actually move the
931 * journal inode. Errors at this point mean we need to force
932 * an ext2 filesystem check.
933 */
934 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
935 goto err_out;
936 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
937 goto err_out;
938 sb->s_journal_inum = EXT2_JOURNAL_INO;
939 ext2fs_mark_super_dirty(fs);
940 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
941 inode.i_links_count = 0;
942 inode.i_dtime = time(0);
943 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
944 goto err_out;
945
946 group = ext2fs_group_of_ino(fs, ino);
947 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
948 ext2fs_mark_ib_dirty(fs);
949 fs->group_desc[group].bg_free_inodes_count++;
950 fs->super->s_free_inodes_count++;
951 return;
952
953err_out:
954 pctx.errcode = retval;
955 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
956 fs->super->s_state &= ~EXT2_VALID_FS;
957 ext2fs_mark_super_dirty(fs);
958 return;
959}
960
diff --git a/e2fsprogs/e2fsck/message.c b/e2fsprogs/e2fsck/message.c
new file mode 100644
index 000000000..b09ae8976
--- /dev/null
+++ b/e2fsprogs/e2fsck/message.c
@@ -0,0 +1,466 @@
1/*
2 * message.c --- print e2fsck messages (with compression)
3 *
4 * Copyright 1996, 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * print_e2fsck_message() prints a message to the user, using
12 * compression techniques and expansions of abbreviations.
13 *
14 * The following % expansions are supported:
15 *
16 * %b <blk> block number
17 * %B <blkcount> integer
18 * %c <blk2> block number
19 * %Di <dirent>->ino inode number
20 * %Dn <dirent>->name string
21 * %Dr <dirent>->rec_len
22 * %Dl <dirent>->name_len
23 * %Dt <dirent>->filetype
24 * %d <dir> inode number
25 * %g <group> integer
26 * %i <ino> inode number
27 * %Is <inode> -> i_size
28 * %IS <inode> -> i_extra_isize
29 * %Ib <inode> -> i_blocks
30 * %Il <inode> -> i_links_count
31 * %Im <inode> -> i_mode
32 * %IM <inode> -> i_mtime
33 * %IF <inode> -> i_faddr
34 * %If <inode> -> i_file_acl
35 * %Id <inode> -> i_dir_acl
36 * %Iu <inode> -> i_uid
37 * %Ig <inode> -> i_gid
38 * %j <ino2> inode number
39 * %m <com_err error message>
40 * %N <num>
41 * %p ext2fs_get_pathname of directory <ino>
42 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
43 * the containing directory. (If dirent is NULL
44 * then return the pathname of directory <ino2>)
45 * %q ext2fs_get_pathname of directory <dir>
46 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
47 * the containing directory.
48 * %s <str> miscellaneous string
49 * %S backup superblock
50 * %X <num> hexadecimal format
51 *
52 * The following '@' expansions are supported:
53 *
54 * @a extended attribute
55 * @A error allocating
56 * @b block
57 * @B bitmap
58 * @c compress
59 * @C conflicts with some other fs block
60 * @D deleted
61 * @d directory
62 * @e entry
63 * @E Entry '%Dn' in %p (%i)
64 * @f filesystem
65 * @F for @i %i (%Q) is
66 * @g group
67 * @h HTREE directory inode
68 * @i inode
69 * @I illegal
70 * @j journal
71 * @l lost+found
72 * @L is a link
73 * @o orphaned
74 * @p problem in
75 * @r root inode
76 * @s should be
77 * @S superblock
78 * @u unattached
79 * @v device
80 * @z zero-length
81 */
82
83#include <stdlib.h>
84#include <unistd.h>
85#include <string.h>
86#include <ctype.h>
87#include <termios.h>
88
89#include "e2fsck.h"
90
91#include "problem.h"
92
93#ifdef __GNUC__
94#define _INLINE_ __inline__
95#else
96#define _INLINE_
97#endif
98
99/*
100 * This structure defines the abbreviations used by the text strings
101 * below. The first character in the string is the index letter. An
102 * abbreviation of the form '@<i>' is expanded by looking up the index
103 * letter <i> in the table below.
104 */
105static const char *abbrevs[] = {
106 N_("aextended attribute"),
107 N_("Aerror allocating"),
108 N_("bblock"),
109 N_("Bbitmap"),
110 N_("ccompress"),
111 N_("Cconflicts with some other fs @b"),
112 N_("iinode"),
113 N_("Iillegal"),
114 N_("jjournal"),
115 N_("Ddeleted"),
116 N_("ddirectory"),
117 N_("eentry"),
118 N_("E@e '%Dn' in %p (%i)"),
119 N_("ffilesystem"),
120 N_("Ffor @i %i (%Q) is"),
121 N_("ggroup"),
122 N_("hHTREE @d @i"),
123 N_("llost+found"),
124 N_("Lis a link"),
125 N_("oorphaned"),
126 N_("pproblem in"),
127 N_("rroot @i"),
128 N_("sshould be"),
129 N_("Ssuper@b"),
130 N_("uunattached"),
131 N_("vdevice"),
132 N_("zzero-length"),
133 "@@",
134 0
135 };
136
137/*
138 * Give more user friendly names to the "special" inodes.
139 */
140#define num_special_inodes 11
141static const char *special_inode_name[] =
142{
143 N_("<The NULL inode>"), /* 0 */
144 N_("<The bad blocks inode>"), /* 1 */
145 "/", /* 2 */
146 N_("<The ACL index inode>"), /* 3 */
147 N_("<The ACL data inode>"), /* 4 */
148 N_("<The boot loader inode>"), /* 5 */
149 N_("<The undelete directory inode>"), /* 6 */
150 N_("<The group descriptor inode>"), /* 7 */
151 N_("<The journal inode>"), /* 8 */
152 N_("<Reserved inode 9>"), /* 9 */
153 N_("<Reserved inode 10>"), /* 10 */
154};
155
156/*
157 * This function does "safe" printing. It will convert non-printable
158 * ASCII characters using '^' and M- notation.
159 */
160static void safe_print(const char *cp, int len)
161{
162 unsigned char ch;
163
164 if (len < 0)
165 len = strlen(cp);
166
167 while (len--) {
168 ch = *cp++;
169 if (ch > 128) {
170 fputs("M-", stdout);
171 ch -= 128;
172 }
173 if ((ch < 32) || (ch == 0x7f)) {
174 fputc('^', stdout);
175 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
176 }
177 fputc(ch, stdout);
178 }
179}
180
181
182/*
183 * This function prints a pathname, using the ext2fs_get_pathname
184 * function
185 */
186static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
187{
188 errcode_t retval;
189 char *path;
190
191 if (!dir && (ino < num_special_inodes)) {
192 fputs(_(special_inode_name[ino]), stdout);
193 return;
194 }
195
196 retval = ext2fs_get_pathname(fs, dir, ino, &path);
197 if (retval)
198 fputs("???", stdout);
199 else {
200 safe_print(path, -1);
201 ext2fs_free_mem(&path);
202 }
203}
204
205/*
206 * This function handles the '@' expansion. We allow recursive
207 * expansion; an @ expression can contain further '@' and '%'
208 * expressions.
209 */
210static _INLINE_ void expand_at_expression(e2fsck_t ctx, char ch,
211 struct problem_context *pctx,
212 int *first)
213{
214 const char **cpp, *str;
215
216 /* Search for the abbreviation */
217 for (cpp = abbrevs; *cpp; cpp++) {
218 if (ch == *cpp[0])
219 break;
220 }
221 if (*cpp) {
222 str = _(*cpp) + 1;
223 if (*first && islower(*str)) {
224 *first = 0;
225 fputc(toupper(*str++), stdout);
226 }
227 print_e2fsck_message(ctx, str, pctx, *first);
228 } else
229 printf("@%c", ch);
230}
231
232/*
233 * This function expands '%IX' expressions
234 */
235static _INLINE_ void expand_inode_expression(char ch,
236 struct problem_context *ctx)
237{
238 struct ext2_inode *inode;
239 struct ext2_inode_large *large_inode;
240 char * time_str;
241 time_t t;
242 int do_gmt = -1;
243
244 if (!ctx || !ctx->inode)
245 goto no_inode;
246
247 inode = ctx->inode;
248 large_inode = (struct ext2_inode_large *) inode;
249
250 switch (ch) {
251 case 's':
252 if (LINUX_S_ISDIR(inode->i_mode))
253 printf("%u", inode->i_size);
254 else {
255#ifdef EXT2_NO_64_TYPE
256 if (inode->i_size_high)
257 printf("0x%x%08x", inode->i_size_high,
258 inode->i_size);
259 else
260 printf("%u", inode->i_size);
261#else
262 printf("%llu", (inode->i_size |
263 ((__u64) inode->i_size_high << 32)));
264#endif
265 }
266 break;
267 case 'S':
268 printf("%u", large_inode->i_extra_isize);
269 break;
270 case 'b':
271 printf("%u", inode->i_blocks);
272 break;
273 case 'l':
274 printf("%d", inode->i_links_count);
275 break;
276 case 'm':
277 printf("0%o", inode->i_mode);
278 break;
279 case 'M':
280 /* The diet libc doesn't respect the TZ environemnt variable */
281 if (do_gmt == -1) {
282 time_str = getenv("TZ");
283 if (!time_str)
284 time_str = "";
285 do_gmt = !strcmp(time_str, "GMT");
286 }
287 t = inode->i_mtime;
288 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
289 printf("%.24s", time_str);
290 break;
291 case 'F':
292 printf("%u", inode->i_faddr);
293 break;
294 case 'f':
295 printf("%u", inode->i_file_acl);
296 break;
297 case 'd':
298 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
299 inode->i_dir_acl : 0));
300 break;
301 case 'u':
302 printf("%d", (inode->i_uid |
303 (inode->osd2.linux2.l_i_uid_high << 16)));
304 break;
305 case 'g':
306 printf("%d", (inode->i_gid |
307 (inode->osd2.linux2.l_i_gid_high << 16)));
308 break;
309 default:
310 no_inode:
311 printf("%%I%c", ch);
312 break;
313 }
314}
315
316/*
317 * This function expands '%dX' expressions
318 */
319static _INLINE_ void expand_dirent_expression(char ch,
320 struct problem_context *ctx)
321{
322 struct ext2_dir_entry *dirent;
323 int len;
324
325 if (!ctx || !ctx->dirent)
326 goto no_dirent;
327
328 dirent = ctx->dirent;
329
330 switch (ch) {
331 case 'i':
332 printf("%u", dirent->inode);
333 break;
334 case 'n':
335 len = dirent->name_len & 0xFF;
336 if (len > EXT2_NAME_LEN)
337 len = EXT2_NAME_LEN;
338 if (len > dirent->rec_len)
339 len = dirent->rec_len;
340 safe_print(dirent->name, len);
341 break;
342 case 'r':
343 printf("%u", dirent->rec_len);
344 break;
345 case 'l':
346 printf("%u", dirent->name_len & 0xFF);
347 break;
348 case 't':
349 printf("%u", dirent->name_len >> 8);
350 break;
351 default:
352 no_dirent:
353 printf("%%D%c", ch);
354 break;
355 }
356}
357
358static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
359 struct problem_context *ctx)
360{
361 if (!ctx)
362 goto no_context;
363
364 switch (ch) {
365 case '%':
366 fputc('%', stdout);
367 break;
368 case 'b':
369 printf("%u", ctx->blk);
370 break;
371 case 'B':
372#ifdef EXT2_NO_64_TYPE
373 printf("%d", ctx->blkcount);
374#else
375 printf("%lld", ctx->blkcount);
376#endif
377 break;
378 case 'c':
379 printf("%u", ctx->blk2);
380 break;
381 case 'd':
382 printf("%u", ctx->dir);
383 break;
384 case 'g':
385 printf("%d", ctx->group);
386 break;
387 case 'i':
388 printf("%u", ctx->ino);
389 break;
390 case 'j':
391 printf("%u", ctx->ino2);
392 break;
393 case 'm':
394 printf("%s", error_message(ctx->errcode));
395 break;
396 case 'N':
397#ifdef EXT2_NO_64_TYPE
398 printf("%u", ctx->num);
399#else
400 printf("%llu", ctx->num);
401#endif
402 break;
403 case 'p':
404 print_pathname(fs, ctx->ino, 0);
405 break;
406 case 'P':
407 print_pathname(fs, ctx->ino2,
408 ctx->dirent ? ctx->dirent->inode : 0);
409 break;
410 case 'q':
411 print_pathname(fs, ctx->dir, 0);
412 break;
413 case 'Q':
414 print_pathname(fs, ctx->dir, ctx->ino);
415 break;
416 case 'S':
417 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
418 break;
419 case 's':
420 printf("%s", ctx->str ? ctx->str : "NULL");
421 break;
422 case 'X':
423#ifdef EXT2_NO_64_TYPE
424 printf("0x%x", ctx->num);
425#else
426 printf("0x%llx", ctx->num);
427#endif
428 break;
429 default:
430 no_context:
431 printf("%%%c", ch);
432 break;
433 }
434}
435
436void print_e2fsck_message(e2fsck_t ctx, const char *msg,
437 struct problem_context *pctx, int first)
438{
439 ext2_filsys fs = ctx->fs;
440 const char * cp;
441 int i;
442
443 e2fsck_clear_progbar(ctx);
444 for (cp = msg; *cp; cp++) {
445 if (cp[0] == '@') {
446 cp++;
447 expand_at_expression(ctx, *cp, pctx, &first);
448 } else if (cp[0] == '%' && cp[1] == 'I') {
449 cp += 2;
450 expand_inode_expression(*cp, pctx);
451 } else if (cp[0] == '%' && cp[1] == 'D') {
452 cp += 2;
453 expand_dirent_expression(*cp, pctx);
454 } else if ((cp[0] == '%')) {
455 cp++;
456 expand_percent_expression(fs, *cp, pctx);
457 } else {
458 for (i=0; cp[i]; i++)
459 if ((cp[i] == '@') || cp[i] == '%')
460 break;
461 printf("%.*s", i, cp);
462 cp += i-1;
463 }
464 first = 0;
465 }
466}
diff --git a/e2fsprogs/e2fsck/pass1.c b/e2fsprogs/e2fsck/pass1.c
new file mode 100644
index 000000000..8d01cae2b
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass1.c
@@ -0,0 +1,2122 @@
1/*
2 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
12 * and applies the following tests to each inode:
13 *
14 * - The mode field of the inode must be legal.
15 * - The size and block count fields of the inode are correct.
16 * - A data block must not be used by another inode
17 *
18 * Pass 1 also gathers the collects the following information:
19 *
20 * - A bitmap of which inodes are in use. (inode_used_map)
21 * - A bitmap of which inodes are directories. (inode_dir_map)
22 * - A bitmap of which inodes are regular files. (inode_reg_map)
23 * - A bitmap of which inodes have bad fields. (inode_bad_map)
24 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
25 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
26 * - A bitmap of which blocks are in use. (block_found_map)
27 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
28 * - The data blocks of the directory inodes. (dir_map)
29 *
30 * Pass 1 is designed to stash away enough information so that the
31 * other passes should not need to read in the inode information
32 * during the normal course of a filesystem check. (Althogh if an
33 * inconsistency is detected, other passes may need to read in an
34 * inode to fix it.)
35 *
36 * Note that pass 1B will be invoked if there are any duplicate blocks
37 * found.
38 */
39
40#define _GNU_SOURCE 1 /* get strnlen() */
41#include <string.h>
42#include <time.h>
43#ifdef HAVE_ERRNO_H
44#include <errno.h>
45#endif
46
47#include "e2fsck.h"
48#include <ext2fs/ext2_ext_attr.h>
49
50#include "problem.h"
51
52#ifdef NO_INLINE_FUNCS
53#define _INLINE_
54#else
55#define _INLINE_ inline
56#endif
57
58static int process_block(ext2_filsys fs, blk_t *blocknr,
59 e2_blkcnt_t blockcnt, blk_t ref_blk,
60 int ref_offset, void *priv_data);
61static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
62 e2_blkcnt_t blockcnt, blk_t ref_blk,
63 int ref_offset, void *priv_data);
64static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
65 char *block_buf);
66static void mark_table_blocks(e2fsck_t ctx);
67static void alloc_bb_map(e2fsck_t ctx);
68static void alloc_imagic_map(e2fsck_t ctx);
69static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
70static void handle_fs_bad_blocks(e2fsck_t ctx);
71static void process_inodes(e2fsck_t ctx, char *block_buf);
72static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
73static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
74 dgrp_t group, void * priv_data);
75static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
76 char *block_buf, int adjust_sign);
77/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
78
79extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
80 struct ext2_inode * inode, int bufsize,
81 const char *proc);
82
83struct process_block_struct {
84 ext2_ino_t ino;
85 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
86 fragmented:1, compressed:1, bbcheck:1;
87 blk_t num_blocks;
88 blk_t max_blocks;
89 e2_blkcnt_t last_block;
90 int num_illegal_blocks;
91 blk_t previous_block;
92 struct ext2_inode *inode;
93 struct problem_context *pctx;
94 ext2fs_block_bitmap fs_meta_blocks;
95 e2fsck_t ctx;
96};
97
98struct process_inode_block {
99 ext2_ino_t ino;
100 struct ext2_inode inode;
101};
102
103struct scan_callback_struct {
104 e2fsck_t ctx;
105 char *block_buf;
106};
107
108/*
109 * For the inodes to process list.
110 */
111static struct process_inode_block *inodes_to_process;
112static int process_inode_count;
113
114static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
115 EXT2_MIN_BLOCK_LOG_SIZE + 1];
116
117/*
118 * Free all memory allocated by pass1 in preparation for restarting
119 * things.
120 */
121static void unwind_pass1(ext2_filsys fs EXT2FS_ATTR((unused)))
122{
123 ext2fs_free_mem(&inodes_to_process);
124 inodes_to_process = 0;
125}
126
127/*
128 * Check to make sure a device inode is real. Returns 1 if the device
129 * checks out, 0 if not.
130 *
131 * Note: this routine is now also used to check FIFO's and Sockets,
132 * since they have the same requirement; the i_block fields should be
133 * zero.
134 */
135int e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
136{
137 int i;
138
139 /*
140 * If i_blocks is non-zero, or the index flag is set, then
141 * this is a bogus device/fifo/socket
142 */
143 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
144 (inode->i_flags & EXT2_INDEX_FL))
145 return 0;
146
147 /*
148 * We should be able to do the test below all the time, but
149 * because the kernel doesn't forcibly clear the device
150 * inode's additional i_block fields, there are some rare
151 * occasions when a legitimate device inode will have non-zero
152 * additional i_block fields. So for now, we only complain
153 * when the immutable flag is set, which should never happen
154 * for devices. (And that's when the problem is caused, since
155 * you can't set or clear immutable flags for devices.) Once
156 * the kernel has been fixed we can change this...
157 */
158 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
159 for (i=4; i < EXT2_N_BLOCKS; i++)
160 if (inode->i_block[i])
161 return 0;
162 }
163 return 1;
164}
165
166/*
167 * Check to make sure a symlink inode is real. Returns 1 if the symlink
168 * checks out, 0 if not.
169 */
170int e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode,
171 char *buf)
172{
173 unsigned int len;
174 int i;
175 blk_t blocks;
176
177 if ((inode->i_size_high || inode->i_size == 0) ||
178 (inode->i_flags & EXT2_INDEX_FL))
179 return 0;
180
181 blocks = ext2fs_inode_data_blocks(fs, inode);
182 if (blocks) {
183 if ((inode->i_size >= fs->blocksize) ||
184 (blocks != fs->blocksize >> 9) ||
185 (inode->i_block[0] < fs->super->s_first_data_block) ||
186 (inode->i_block[0] >= fs->super->s_blocks_count))
187 return 0;
188
189 for (i = 1; i < EXT2_N_BLOCKS; i++)
190 if (inode->i_block[i])
191 return 0;
192
193 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
194 return 0;
195
196 len = strnlen(buf, fs->blocksize);
197 if (len == fs->blocksize)
198 return 0;
199 } else {
200 if (inode->i_size >= sizeof(inode->i_block))
201 return 0;
202
203 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
204 if (len == sizeof(inode->i_block))
205 return 0;
206 }
207 if (len != inode->i_size)
208 return 0;
209 return 1;
210}
211
212/*
213 * If the immutable (or append-only) flag is set on the inode, offer
214 * to clear it.
215 */
216#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
217static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
218{
219 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
220 return;
221
222 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
223 return;
224
225 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
226 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
227}
228
229/*
230 * If device, fifo or socket, check size is zero -- if not offer to
231 * clear it
232 */
233static void check_size(e2fsck_t ctx, struct problem_context *pctx)
234{
235 struct ext2_inode *inode = pctx->inode;
236
237 if ((inode->i_size == 0) && (inode->i_size_high == 0))
238 return;
239
240 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
241 return;
242
243 inode->i_size = 0;
244 inode->i_size_high = 0;
245 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
246}
247
248static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
249{
250 struct ext2_super_block *sb = ctx->fs->super;
251 struct ext2_inode_large *inode;
252 struct ext2_ext_attr_entry *entry;
253 char *start, *end;
254 int storage_size, remain, offs;
255 int problem = 0;
256
257 inode = (struct ext2_inode_large *) pctx->inode;
258 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
259 inode->i_extra_isize;
260 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
261 inode->i_extra_isize + sizeof(__u32);
262 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
263 entry = (struct ext2_ext_attr_entry *) start;
264
265 /* scan all entry's headers first */
266
267 /* take finish entry 0UL into account */
268 remain = storage_size - sizeof(__u32);
269 offs = end - start;
270
271 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
272
273 /* header eats this space */
274 remain -= sizeof(struct ext2_ext_attr_entry);
275
276 /* is attribute name valid? */
277 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
278 pctx->num = entry->e_name_len;
279 problem = PR_1_ATTR_NAME_LEN;
280 goto fix;
281 }
282
283 /* attribute len eats this space */
284 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
285
286 /* check value size */
287 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
288 pctx->num = entry->e_value_size;
289 problem = PR_1_ATTR_VALUE_SIZE;
290 goto fix;
291 }
292
293 /* check value placement */
294 if (entry->e_value_offs +
295 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
296 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
297 pctx->num = entry->e_value_offs;
298 problem = PR_1_ATTR_VALUE_OFFSET;
299 goto fix;
300 }
301
302 /* e_value_block must be 0 in inode's ea */
303 if (entry->e_value_block != 0) {
304 pctx->num = entry->e_value_block;
305 problem = PR_1_ATTR_VALUE_BLOCK;
306 goto fix;
307 }
308
309 /* e_hash must be 0 in inode's ea */
310 if (entry->e_hash != 0) {
311 pctx->num = entry->e_hash;
312 problem = PR_1_ATTR_HASH;
313 goto fix;
314 }
315
316 remain -= entry->e_value_size;
317 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
318
319 entry = EXT2_EXT_ATTR_NEXT(entry);
320 }
321fix:
322 /*
323 * it seems like a corruption. it's very unlikely we could repair
324 * EA(s) in automatic fashion -bzzz
325 */
326#if 0
327 problem = PR_1_ATTR_HASH;
328#endif
329 if (problem == 0 || !fix_problem(ctx, problem, pctx))
330 return;
331
332 /* simple remove all possible EA(s) */
333 *((__u32 *)start) = 0UL;
334 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
335 EXT2_INODE_SIZE(sb), "pass1");
336}
337
338static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
339{
340 struct ext2_super_block *sb = ctx->fs->super;
341 struct ext2_inode_large *inode;
342 __u32 *eamagic;
343 int min, max;
344
345 inode = (struct ext2_inode_large *) pctx->inode;
346 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
347 /* this isn't large inode. so, nothing to check */
348 return;
349 }
350
351#if 0
352 printf("inode #%u, i_extra_size %d\n", pctx->ino,
353 inode->i_extra_isize);
354#endif
355 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
356 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
357 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
358 /*
359 * For now we will allow i_extra_isize to be 0, but really
360 * implementations should never allow i_extra_isize to be 0
361 */
362 if (inode->i_extra_isize &&
363 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
364 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
365 return;
366 inode->i_extra_isize = min;
367 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
368 EXT2_INODE_SIZE(sb), "pass1");
369 return;
370 }
371
372 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
373 inode->i_extra_isize);
374 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
375 /* it seems inode has an extended attribute(s) in body */
376 check_ea_in_inode(ctx, pctx);
377 }
378}
379
380void e2fsck_pass1(e2fsck_t ctx)
381{
382 int i;
383 __u64 max_sizes;
384 ext2_filsys fs = ctx->fs;
385 ext2_ino_t ino;
386 struct ext2_inode *inode;
387 ext2_inode_scan scan;
388 char *block_buf;
389#ifdef RESOURCE_TRACK
390 struct resource_track rtrack;
391#endif
392 unsigned char frag, fsize;
393 struct problem_context pctx;
394 struct scan_callback_struct scan_struct;
395 struct ext2_super_block *sb = ctx->fs->super;
396 int imagic_fs;
397 int busted_fs_time = 0;
398 int inode_size;
399
400#ifdef RESOURCE_TRACK
401 init_resource_track(&rtrack);
402#endif
403 clear_problem_context(&pctx);
404
405 if (!(ctx->options & E2F_OPT_PREEN))
406 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
407
408 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
409 !(ctx->options & E2F_OPT_NO)) {
410 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
411 ctx->dirs_to_hash = 0;
412 }
413
414#ifdef MTRACE
415 mtrace_print("Pass 1");
416#endif
417
418#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
419
420 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
421 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
422 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
423 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
424 max_sizes = (max_sizes * (1UL << i)) - 1;
425 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
426 }
427#undef EXT2_BPP
428
429 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
430
431 /*
432 * Allocate bitmaps structures
433 */
434 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
435 &ctx->inode_used_map);
436 if (pctx.errcode) {
437 pctx.num = 1;
438 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
439 ctx->flags |= E2F_FLAG_ABORT;
440 return;
441 }
442 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
443 _("directory inode map"), &ctx->inode_dir_map);
444 if (pctx.errcode) {
445 pctx.num = 2;
446 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
447 ctx->flags |= E2F_FLAG_ABORT;
448 return;
449 }
450 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
451 _("regular file inode map"), &ctx->inode_reg_map);
452 if (pctx.errcode) {
453 pctx.num = 6;
454 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
455 ctx->flags |= E2F_FLAG_ABORT;
456 return;
457 }
458 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
459 &ctx->block_found_map);
460 if (pctx.errcode) {
461 pctx.num = 1;
462 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
463 ctx->flags |= E2F_FLAG_ABORT;
464 return;
465 }
466 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
467 &ctx->inode_link_info);
468 if (pctx.errcode) {
469 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
470 ctx->flags |= E2F_FLAG_ABORT;
471 return;
472 }
473 inode_size = EXT2_INODE_SIZE(fs->super);
474 inode = (struct ext2_inode *)
475 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
476
477 inodes_to_process = (struct process_inode_block *)
478 e2fsck_allocate_memory(ctx,
479 (ctx->process_inode_size *
480 sizeof(struct process_inode_block)),
481 "array of inodes to process");
482 process_inode_count = 0;
483
484 pctx.errcode = ext2fs_init_dblist(fs, 0);
485 if (pctx.errcode) {
486 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
487 ctx->flags |= E2F_FLAG_ABORT;
488 return;
489 }
490
491 /*
492 * If the last orphan field is set, clear it, since the pass1
493 * processing will automatically find and clear the orphans.
494 * In the future, we may want to try using the last_orphan
495 * linked list ourselves, but for now, we clear it so that the
496 * ext3 mount code won't get confused.
497 */
498 if (!(ctx->options & E2F_OPT_READONLY)) {
499 if (fs->super->s_last_orphan) {
500 fs->super->s_last_orphan = 0;
501 ext2fs_mark_super_dirty(fs);
502 }
503 }
504
505 mark_table_blocks(ctx);
506 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
507 "block interate buffer");
508 e2fsck_use_inode_shortcuts(ctx, 1);
509 ehandler_operation(_("doing inode scan"));
510 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
511 &scan);
512 if (pctx.errcode) {
513 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
514 ctx->flags |= E2F_FLAG_ABORT;
515 return;
516 }
517 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
518 ctx->stashed_inode = inode;
519 scan_struct.ctx = ctx;
520 scan_struct.block_buf = block_buf;
521 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
522 if (ctx->progress)
523 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
524 return;
525 if (fs->super->s_wtime < fs->super->s_inodes_count)
526 busted_fs_time = 1;
527
528 while (1) {
529 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
530 inode, inode_size);
531 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
532 return;
533 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
534 if (!ctx->inode_bb_map)
535 alloc_bb_map(ctx);
536 ext2fs_mark_inode_bitmap(ctx->inode_bb_map, ino);
537 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
538 continue;
539 }
540 if (pctx.errcode) {
541 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
542 ctx->flags |= E2F_FLAG_ABORT;
543 return;
544 }
545 if (!ino)
546 break;
547 pctx.ino = ino;
548 pctx.inode = inode;
549 ctx->stashed_ino = ino;
550 if (inode->i_links_count) {
551 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
552 ino, inode->i_links_count);
553 if (pctx.errcode) {
554 pctx.num = inode->i_links_count;
555 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
556 ctx->flags |= E2F_FLAG_ABORT;
557 return;
558 }
559 }
560 if (ino == EXT2_BAD_INO) {
561 struct process_block_struct pb;
562
563 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
564 &pb.fs_meta_blocks);
565 if (pctx.errcode) {
566 pctx.num = 4;
567 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
568 ctx->flags |= E2F_FLAG_ABORT;
569 return;
570 }
571 pb.ino = EXT2_BAD_INO;
572 pb.num_blocks = pb.last_block = 0;
573 pb.num_illegal_blocks = 0;
574 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
575 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
576 pb.inode = inode;
577 pb.pctx = &pctx;
578 pb.ctx = ctx;
579 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
580 block_buf, process_bad_block, &pb);
581 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
582 if (pctx.errcode) {
583 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
584 ctx->flags |= E2F_FLAG_ABORT;
585 return;
586 }
587 if (pb.bbcheck)
588 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
589 ctx->flags |= E2F_FLAG_ABORT;
590 return;
591 }
592 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
593 clear_problem_context(&pctx);
594 continue;
595 } else if (ino == EXT2_ROOT_INO) {
596 /*
597 * Make sure the root inode is a directory; if
598 * not, offer to clear it. It will be
599 * regnerated in pass #3.
600 */
601 if (!LINUX_S_ISDIR(inode->i_mode)) {
602 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
603 inode->i_dtime = time(0);
604 inode->i_links_count = 0;
605 ext2fs_icount_store(ctx->inode_link_info,
606 ino, 0);
607 e2fsck_write_inode(ctx, ino, inode,
608 "pass1");
609 }
610
611 }
612 /*
613 * If dtime is set, offer to clear it. mke2fs
614 * version 0.2b created filesystems with the
615 * dtime field set for the root and lost+found
616 * directories. We won't worry about
617 * /lost+found, since that can be regenerated
618 * easily. But we will fix the root directory
619 * as a special case.
620 */
621 if (inode->i_dtime && inode->i_links_count) {
622 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
623 inode->i_dtime = 0;
624 e2fsck_write_inode(ctx, ino, inode,
625 "pass1");
626 }
627 }
628 } else if (ino == EXT2_JOURNAL_INO) {
629 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
630 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
631 if (!LINUX_S_ISREG(inode->i_mode) &&
632 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
633 &pctx)) {
634 inode->i_mode = LINUX_S_IFREG;
635 e2fsck_write_inode(ctx, ino, inode,
636 "pass1");
637 }
638 check_blocks(ctx, &pctx, block_buf);
639 continue;
640 }
641 if ((inode->i_links_count || inode->i_blocks ||
642 inode->i_blocks || inode->i_block[0]) &&
643 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
644 &pctx)) {
645 memset(inode, 0, inode_size);
646 ext2fs_icount_store(ctx->inode_link_info,
647 ino, 0);
648 e2fsck_write_inode_full(ctx, ino, inode,
649 inode_size, "pass1");
650 }
651 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
652 int problem = 0;
653
654 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
655 if (ino == EXT2_BOOT_LOADER_INO) {
656 if (LINUX_S_ISDIR(inode->i_mode))
657 problem = PR_1_RESERVED_BAD_MODE;
658 } else if (ino == EXT2_RESIZE_INO) {
659 if (inode->i_mode &&
660 !LINUX_S_ISREG(inode->i_mode))
661 problem = PR_1_RESERVED_BAD_MODE;
662 } else {
663 if (inode->i_mode != 0)
664 problem = PR_1_RESERVED_BAD_MODE;
665 }
666 if (problem) {
667 if (fix_problem(ctx, problem, &pctx)) {
668 inode->i_mode = 0;
669 e2fsck_write_inode(ctx, ino, inode,
670 "pass1");
671 }
672 }
673 check_blocks(ctx, &pctx, block_buf);
674 continue;
675 }
676 /*
677 * Check for inodes who might have been part of the
678 * orphaned list linked list. They should have gotten
679 * dealt with by now, unless the list had somehow been
680 * corrupted.
681 *
682 * FIXME: In the future, inodes which are still in use
683 * (and which are therefore) pending truncation should
684 * be handled specially. Right now we just clear the
685 * dtime field, and the normal e2fsck handling of
686 * inodes where i_size and the inode blocks are
687 * inconsistent is to fix i_size, instead of releasing
688 * the extra blocks. This won't catch the inodes that
689 * was at the end of the orphan list, but it's better
690 * than nothing. The right answer is that there
691 * shouldn't be any bugs in the orphan list handling. :-)
692 */
693 if (inode->i_dtime && !busted_fs_time &&
694 inode->i_dtime < ctx->fs->super->s_inodes_count) {
695 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
696 inode->i_dtime = inode->i_links_count ?
697 0 : time(0);
698 e2fsck_write_inode(ctx, ino, inode,
699 "pass1");
700 }
701 }
702
703 /*
704 * This code assumes that deleted inodes have
705 * i_links_count set to 0.
706 */
707 if (!inode->i_links_count) {
708 if (!inode->i_dtime && inode->i_mode) {
709 if (fix_problem(ctx,
710 PR_1_ZERO_DTIME, &pctx)) {
711 inode->i_dtime = time(0);
712 e2fsck_write_inode(ctx, ino, inode,
713 "pass1");
714 }
715 }
716 continue;
717 }
718 /*
719 * n.b. 0.3c ext2fs code didn't clear i_links_count for
720 * deleted files. Oops.
721 *
722 * Since all new ext2 implementations get this right,
723 * we now assume that the case of non-zero
724 * i_links_count and non-zero dtime means that we
725 * should keep the file, not delete it.
726 *
727 */
728 if (inode->i_dtime) {
729 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
730 inode->i_dtime = 0;
731 e2fsck_write_inode(ctx, ino, inode, "pass1");
732 }
733 }
734
735 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
736 switch (fs->super->s_creator_os) {
737 case EXT2_OS_LINUX:
738 frag = inode->osd2.linux2.l_i_frag;
739 fsize = inode->osd2.linux2.l_i_fsize;
740 break;
741 case EXT2_OS_HURD:
742 frag = inode->osd2.hurd2.h_i_frag;
743 fsize = inode->osd2.hurd2.h_i_fsize;
744 break;
745 case EXT2_OS_MASIX:
746 frag = inode->osd2.masix2.m_i_frag;
747 fsize = inode->osd2.masix2.m_i_fsize;
748 break;
749 default:
750 frag = fsize = 0;
751 }
752
753 if (inode->i_faddr || frag || fsize ||
754 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
755 mark_inode_bad(ctx, ino);
756 if (inode->i_flags & EXT2_IMAGIC_FL) {
757 if (imagic_fs) {
758 if (!ctx->inode_imagic_map)
759 alloc_imagic_map(ctx);
760 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
761 ino);
762 } else {
763 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
764 inode->i_flags &= ~EXT2_IMAGIC_FL;
765 e2fsck_write_inode(ctx, ino,
766 inode, "pass1");
767 }
768 }
769 }
770
771 check_inode_extra_space(ctx, &pctx);
772
773 if (LINUX_S_ISDIR(inode->i_mode)) {
774 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
775 e2fsck_add_dir_info(ctx, ino, 0);
776 ctx->fs_directory_count++;
777 } else if (LINUX_S_ISREG (inode->i_mode)) {
778 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
779 ctx->fs_regular_count++;
780 } else if (LINUX_S_ISCHR (inode->i_mode) &&
781 e2fsck_pass1_check_device_inode(fs, inode)) {
782 check_immutable(ctx, &pctx);
783 check_size(ctx, &pctx);
784 ctx->fs_chardev_count++;
785 } else if (LINUX_S_ISBLK (inode->i_mode) &&
786 e2fsck_pass1_check_device_inode(fs, inode)) {
787 check_immutable(ctx, &pctx);
788 check_size(ctx, &pctx);
789 ctx->fs_blockdev_count++;
790 } else if (LINUX_S_ISLNK (inode->i_mode) &&
791 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
792 check_immutable(ctx, &pctx);
793 ctx->fs_symlinks_count++;
794 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
795 ctx->fs_fast_symlinks_count++;
796 check_blocks(ctx, &pctx, block_buf);
797 continue;
798 }
799 }
800 else if (LINUX_S_ISFIFO (inode->i_mode) &&
801 e2fsck_pass1_check_device_inode(fs, inode)) {
802 check_immutable(ctx, &pctx);
803 check_size(ctx, &pctx);
804 ctx->fs_fifo_count++;
805 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
806 e2fsck_pass1_check_device_inode(fs, inode)) {
807 check_immutable(ctx, &pctx);
808 check_size(ctx, &pctx);
809 ctx->fs_sockets_count++;
810 } else
811 mark_inode_bad(ctx, ino);
812 if (inode->i_block[EXT2_IND_BLOCK])
813 ctx->fs_ind_count++;
814 if (inode->i_block[EXT2_DIND_BLOCK])
815 ctx->fs_dind_count++;
816 if (inode->i_block[EXT2_TIND_BLOCK])
817 ctx->fs_tind_count++;
818 if (inode->i_block[EXT2_IND_BLOCK] ||
819 inode->i_block[EXT2_DIND_BLOCK] ||
820 inode->i_block[EXT2_TIND_BLOCK] ||
821 inode->i_file_acl) {
822 inodes_to_process[process_inode_count].ino = ino;
823 inodes_to_process[process_inode_count].inode = *inode;
824 process_inode_count++;
825 } else
826 check_blocks(ctx, &pctx, block_buf);
827
828 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
829 return;
830
831 if (process_inode_count >= ctx->process_inode_size) {
832 process_inodes(ctx, block_buf);
833
834 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
835 return;
836 }
837 }
838 process_inodes(ctx, block_buf);
839 ext2fs_close_inode_scan(scan);
840 ehandler_operation(0);
841
842 /*
843 * If any extended attribute blocks' reference counts need to
844 * be adjusted, either up (ctx->refcount_extra), or down
845 * (ctx->refcount), then fix them.
846 */
847 if (ctx->refcount) {
848 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
849 ea_refcount_free(ctx->refcount);
850 ctx->refcount = 0;
851 }
852 if (ctx->refcount_extra) {
853 adjust_extattr_refcount(ctx, ctx->refcount_extra,
854 block_buf, +1);
855 ea_refcount_free(ctx->refcount_extra);
856 ctx->refcount_extra = 0;
857 }
858
859 if (ctx->invalid_bitmaps)
860 handle_fs_bad_blocks(ctx);
861
862 /* We don't need the block_ea_map any more */
863 if (ctx->block_ea_map) {
864 ext2fs_free_block_bitmap(ctx->block_ea_map);
865 ctx->block_ea_map = 0;
866 }
867
868 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
869 ext2fs_block_bitmap save_bmap;
870
871 save_bmap = fs->block_map;
872 fs->block_map = ctx->block_found_map;
873 clear_problem_context(&pctx);
874 pctx.errcode = ext2fs_create_resize_inode(fs);
875 if (pctx.errcode) {
876 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
877 /* Should never get here */
878 ctx->flags |= E2F_FLAG_ABORT;
879 return;
880 }
881 fs->block_map = save_bmap;
882 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
883 }
884
885 if (ctx->flags & E2F_FLAG_RESTART) {
886 /*
887 * Only the master copy of the superblock and block
888 * group descriptors are going to be written during a
889 * restart, so set the superblock to be used to be the
890 * master superblock.
891 */
892 ctx->use_superblock = 0;
893 unwind_pass1(fs);
894 goto endit;
895 }
896
897 if (ctx->block_dup_map) {
898 if (ctx->options & E2F_OPT_PREEN) {
899 clear_problem_context(&pctx);
900 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
901 }
902 e2fsck_pass1_dupblocks(ctx, block_buf);
903 }
904 ext2fs_free_mem(&inodes_to_process);
905endit:
906 e2fsck_use_inode_shortcuts(ctx, 0);
907
908 ext2fs_free_mem(&block_buf);
909 ext2fs_free_mem(&inode);
910
911#ifdef RESOURCE_TRACK
912 if (ctx->options & E2F_OPT_TIME2) {
913 e2fsck_clear_progbar(ctx);
914 print_resource_track(_("Pass 1"), &rtrack);
915 }
916#endif
917}
918
919/*
920 * When the inode_scan routines call this callback at the end of the
921 * glock group, call process_inodes.
922 */
923static errcode_t scan_callback(ext2_filsys fs,
924 ext2_inode_scan scan EXT2FS_ATTR((unused)),
925 dgrp_t group, void * priv_data)
926{
927 struct scan_callback_struct *scan_struct;
928 e2fsck_t ctx;
929
930 scan_struct = (struct scan_callback_struct *) priv_data;
931 ctx = scan_struct->ctx;
932
933 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
934
935 if (ctx->progress)
936 if ((ctx->progress)(ctx, 1, group+1,
937 ctx->fs->group_desc_count))
938 return EXT2_ET_CANCEL_REQUESTED;
939
940 return 0;
941}
942
943/*
944 * Process the inodes in the "inodes to process" list.
945 */
946static void process_inodes(e2fsck_t ctx, char *block_buf)
947{
948 int i;
949 struct ext2_inode *old_stashed_inode;
950 ext2_ino_t old_stashed_ino;
951 const char *old_operation;
952 char buf[80];
953 struct problem_context pctx;
954
955#if 0
956 printf("begin process_inodes: ");
957#endif
958 if (process_inode_count == 0)
959 return;
960 old_operation = ehandler_operation(0);
961 old_stashed_inode = ctx->stashed_inode;
962 old_stashed_ino = ctx->stashed_ino;
963 qsort(inodes_to_process, process_inode_count,
964 sizeof(struct process_inode_block), process_inode_cmp);
965 clear_problem_context(&pctx);
966 for (i=0; i < process_inode_count; i++) {
967 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
968 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
969
970#if 0
971 printf("%u ", pctx.ino);
972#endif
973 sprintf(buf, _("reading indirect blocks of inode %u"),
974 pctx.ino);
975 ehandler_operation(buf);
976 check_blocks(ctx, &pctx, block_buf);
977 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
978 break;
979 }
980 ctx->stashed_inode = old_stashed_inode;
981 ctx->stashed_ino = old_stashed_ino;
982 process_inode_count = 0;
983#if 0
984 printf("end process inodes\n");
985#endif
986 ehandler_operation(old_operation);
987}
988
989static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
990{
991 const struct process_inode_block *ib_a =
992 (const struct process_inode_block *) a;
993 const struct process_inode_block *ib_b =
994 (const struct process_inode_block *) b;
995 int ret;
996
997 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
998 ib_b->inode.i_block[EXT2_IND_BLOCK]);
999 if (ret == 0)
1000 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
1001 return ret;
1002}
1003
1004/*
1005 * Mark an inode as being bad in some what
1006 */
1007static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
1008{
1009 struct problem_context pctx;
1010
1011 if (!ctx->inode_bad_map) {
1012 clear_problem_context(&pctx);
1013
1014 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
1015 _("bad inode map"), &ctx->inode_bad_map);
1016 if (pctx.errcode) {
1017 pctx.num = 3;
1018 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
1019 /* Should never get here */
1020 ctx->flags |= E2F_FLAG_ABORT;
1021 return;
1022 }
1023 }
1024 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
1025}
1026
1027
1028/*
1029 * This procedure will allocate the inode "bb" (badblock) map table
1030 */
1031static void alloc_bb_map(e2fsck_t ctx)
1032{
1033 struct problem_context pctx;
1034
1035 clear_problem_context(&pctx);
1036 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
1037 _("inode in bad block map"),
1038 &ctx->inode_bb_map);
1039 if (pctx.errcode) {
1040 pctx.num = 4;
1041 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
1042 /* Should never get here */
1043 ctx->flags |= E2F_FLAG_ABORT;
1044 return;
1045 }
1046}
1047
1048/*
1049 * This procedure will allocate the inode imagic table
1050 */
1051static void alloc_imagic_map(e2fsck_t ctx)
1052{
1053 struct problem_context pctx;
1054
1055 clear_problem_context(&pctx);
1056 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
1057 _("imagic inode map"),
1058 &ctx->inode_imagic_map);
1059 if (pctx.errcode) {
1060 pctx.num = 5;
1061 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
1062 /* Should never get here */
1063 ctx->flags |= E2F_FLAG_ABORT;
1064 return;
1065 }
1066}
1067
1068/*
1069 * Marks a block as in use, setting the dup_map if it's been set
1070 * already. Called by process_block and process_bad_block.
1071 *
1072 * WARNING: Assumes checks have already been done to make sure block
1073 * is valid. This is true in both process_block and process_bad_block.
1074 */
1075static _INLINE_ void mark_block_used(e2fsck_t ctx, blk_t block)
1076{
1077 struct problem_context pctx;
1078
1079 clear_problem_context(&pctx);
1080
1081 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
1082 if (!ctx->block_dup_map) {
1083 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
1084 _("multiply claimed block map"),
1085 &ctx->block_dup_map);
1086 if (pctx.errcode) {
1087 pctx.num = 3;
1088 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
1089 &pctx);
1090 /* Should never get here */
1091 ctx->flags |= E2F_FLAG_ABORT;
1092 return;
1093 }
1094 }
1095 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
1096 } else {
1097 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
1098 }
1099}
1100
1101/*
1102 * Adjust the extended attribute block's reference counts at the end
1103 * of pass 1, either by subtracting out references for EA blocks that
1104 * are still referenced in ctx->refcount, or by adding references for
1105 * EA blocks that had extra references as accounted for in
1106 * ctx->refcount_extra.
1107 */
1108static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
1109 char *block_buf, int adjust_sign)
1110{
1111 struct ext2_ext_attr_header *header;
1112 struct problem_context pctx;
1113 ext2_filsys fs = ctx->fs;
1114 blk_t blk;
1115 __u32 should_be;
1116 int count;
1117
1118 clear_problem_context(&pctx);
1119
1120 ea_refcount_intr_begin(refcount);
1121 while (1) {
1122 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
1123 break;
1124 pctx.blk = blk;
1125 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
1126 if (pctx.errcode) {
1127 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
1128 return;
1129 }
1130 header = (struct ext2_ext_attr_header *) block_buf;
1131 pctx.blkcount = header->h_refcount;
1132 should_be = header->h_refcount + adjust_sign * count;
1133 pctx.num = should_be;
1134 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
1135 header->h_refcount = should_be;
1136 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
1137 block_buf);
1138 if (pctx.errcode) {
1139 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
1140 continue;
1141 }
1142 }
1143 }
1144}
1145
1146/*
1147 * Handle processing the extended attribute blocks
1148 */
1149static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
1150 char *block_buf)
1151{
1152 ext2_filsys fs = ctx->fs;
1153 ext2_ino_t ino = pctx->ino;
1154 struct ext2_inode *inode = pctx->inode;
1155 blk_t blk;
1156 char * end;
1157 struct ext2_ext_attr_header *header;
1158 struct ext2_ext_attr_entry *entry;
1159 int count;
1160 region_t region;
1161
1162 blk = inode->i_file_acl;
1163 if (blk == 0)
1164 return 0;
1165
1166 /*
1167 * If the Extended attribute flag isn't set, then a non-zero
1168 * file acl means that the inode is corrupted.
1169 *
1170 * Or if the extended attribute block is an invalid block,
1171 * then the inode is also corrupted.
1172 */
1173 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
1174 (blk < fs->super->s_first_data_block) ||
1175 (blk >= fs->super->s_blocks_count)) {
1176 mark_inode_bad(ctx, ino);
1177 return 0;
1178 }
1179
1180 /* If ea bitmap hasn't been allocated, create it */
1181 if (!ctx->block_ea_map) {
1182 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
1183 _("ext attr block map"),
1184 &ctx->block_ea_map);
1185 if (pctx->errcode) {
1186 pctx->num = 2;
1187 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
1188 ctx->flags |= E2F_FLAG_ABORT;
1189 return 0;
1190 }
1191 }
1192
1193 /* Create the EA refcount structure if necessary */
1194 if (!ctx->refcount) {
1195 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
1196 if (pctx->errcode) {
1197 pctx->num = 1;
1198 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
1199 ctx->flags |= E2F_FLAG_ABORT;
1200 return 0;
1201 }
1202 }
1203
1204#if 0
1205 /* Debugging text */
1206 printf("Inode %u has EA block %u\n", ino, blk);
1207#endif
1208
1209 /* Have we seen this EA block before? */
1210 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
1211 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
1212 return 1;
1213 /* Ooops, this EA was referenced more than it stated */
1214 if (!ctx->refcount_extra) {
1215 pctx->errcode = ea_refcount_create(0,
1216 &ctx->refcount_extra);
1217 if (pctx->errcode) {
1218 pctx->num = 2;
1219 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
1220 ctx->flags |= E2F_FLAG_ABORT;
1221 return 0;
1222 }
1223 }
1224 ea_refcount_increment(ctx->refcount_extra, blk, 0);
1225 return 1;
1226 }
1227
1228 /*
1229 * OK, we haven't seen this EA block yet. So we need to
1230 * validate it
1231 */
1232 pctx->blk = blk;
1233 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
1234 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
1235 goto clear_extattr;
1236 header = (struct ext2_ext_attr_header *) block_buf;
1237 pctx->blk = inode->i_file_acl;
1238 if (((ctx->ext_attr_ver == 1) &&
1239 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
1240 ((ctx->ext_attr_ver == 2) &&
1241 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
1242 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
1243 goto clear_extattr;
1244 }
1245
1246 if (header->h_blocks != 1) {
1247 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
1248 goto clear_extattr;
1249 }
1250
1251 region = region_create(0, fs->blocksize);
1252 if (!region) {
1253 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
1254 ctx->flags |= E2F_FLAG_ABORT;
1255 return 0;
1256 }
1257 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
1258 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
1259 goto clear_extattr;
1260 }
1261
1262 entry = (struct ext2_ext_attr_entry *)(header+1);
1263 end = block_buf + fs->blocksize;
1264 while ((char *)entry < end && *(__u32 *)entry) {
1265 if (region_allocate(region, (char *)entry - (char *)header,
1266 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
1267 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
1268 goto clear_extattr;
1269 }
1270 if ((ctx->ext_attr_ver == 1 &&
1271 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
1272 (ctx->ext_attr_ver == 2 &&
1273 entry->e_name_index == 0)) {
1274 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
1275 goto clear_extattr;
1276 }
1277 if (entry->e_value_block != 0) {
1278 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
1279 goto clear_extattr;
1280 }
1281 if (entry->e_value_size &&
1282 region_allocate(region, entry->e_value_offs,
1283 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
1284 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
1285 goto clear_extattr;
1286 }
1287 entry = EXT2_EXT_ATTR_NEXT(entry);
1288 }
1289 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
1290 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
1291 goto clear_extattr;
1292 }
1293 region_free(region);
1294
1295 count = header->h_refcount - 1;
1296 if (count)
1297 ea_refcount_store(ctx->refcount, blk, count);
1298 mark_block_used(ctx, blk);
1299 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
1300
1301 return 1;
1302
1303clear_extattr:
1304 inode->i_file_acl = 0;
1305 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
1306 return 0;
1307}
1308
1309/* Returns 1 if bad htree, 0 if OK */
1310static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
1311 ext2_ino_t ino EXT2FS_ATTR((unused)),
1312 struct ext2_inode *inode,
1313 char *block_buf)
1314{
1315 struct ext2_dx_root_info *root;
1316 ext2_filsys fs = ctx->fs;
1317 errcode_t retval;
1318 blk_t blk;
1319
1320 if ((!LINUX_S_ISDIR(inode->i_mode) &&
1321 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
1322 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
1323 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
1324 return 1;
1325
1326 blk = inode->i_block[0];
1327 if (((blk == 0) ||
1328 (blk < fs->super->s_first_data_block) ||
1329 (blk >= fs->super->s_blocks_count)) &&
1330 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
1331 return 1;
1332
1333 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
1334 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
1335 return 1;
1336
1337 /* XXX should check that beginning matches a directory */
1338 root = (struct ext2_dx_root_info *) (block_buf + 24);
1339
1340 if ((root->reserved_zero || root->info_length < 8) &&
1341 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
1342 return 1;
1343
1344 pctx->num = root->hash_version;
1345 if ((root->hash_version != EXT2_HASH_LEGACY) &&
1346 (root->hash_version != EXT2_HASH_HALF_MD4) &&
1347 (root->hash_version != EXT2_HASH_TEA) &&
1348 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
1349 return 1;
1350
1351 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
1352 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
1353 return 1;
1354
1355 pctx->num = root->indirect_levels;
1356 if ((root->indirect_levels > 1) &&
1357 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
1358 return 1;
1359
1360 return 0;
1361}
1362
1363/*
1364 * This subroutine is called on each inode to account for all of the
1365 * blocks used by that inode.
1366 */
1367static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
1368 char *block_buf)
1369{
1370 ext2_filsys fs = ctx->fs;
1371 struct process_block_struct pb;
1372 ext2_ino_t ino = pctx->ino;
1373 struct ext2_inode *inode = pctx->inode;
1374 int bad_size = 0;
1375 int dirty_inode = 0;
1376 __u64 size;
1377
1378 pb.ino = ino;
1379 pb.num_blocks = 0;
1380 pb.last_block = -1;
1381 pb.num_illegal_blocks = 0;
1382 pb.suppress = 0; pb.clear = 0;
1383 pb.fragmented = 0;
1384 pb.compressed = 0;
1385 pb.previous_block = 0;
1386 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
1387 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
1388 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
1389 pb.inode = inode;
1390 pb.pctx = pctx;
1391 pb.ctx = ctx;
1392 pctx->ino = ino;
1393 pctx->errcode = 0;
1394
1395 if (inode->i_flags & EXT2_COMPRBLK_FL) {
1396 if (fs->super->s_feature_incompat &
1397 EXT2_FEATURE_INCOMPAT_COMPRESSION)
1398 pb.compressed = 1;
1399 else {
1400 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
1401 inode->i_flags &= ~EXT2_COMPRBLK_FL;
1402 dirty_inode++;
1403 }
1404 }
1405 }
1406
1407 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
1408 pb.num_blocks++;
1409
1410 if (ext2fs_inode_has_valid_blocks(inode))
1411 pctx->errcode = ext2fs_block_iterate2(fs, ino,
1412 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
1413 block_buf, process_block, &pb);
1414 end_problem_latch(ctx, PR_LATCH_BLOCK);
1415 end_problem_latch(ctx, PR_LATCH_TOOBIG);
1416 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1417 goto out;
1418 if (pctx->errcode)
1419 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
1420
1421 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
1422 ctx->fs_fragmented++;
1423
1424 if (pb.clear) {
1425 inode->i_links_count = 0;
1426 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
1427 inode->i_dtime = time(0);
1428 dirty_inode++;
1429 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
1430 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
1431 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
1432 /*
1433 * The inode was probably partially accounted for
1434 * before processing was aborted, so we need to
1435 * restart the pass 1 scan.
1436 */
1437 ctx->flags |= E2F_FLAG_RESTART;
1438 goto out;
1439 }
1440
1441 if (inode->i_flags & EXT2_INDEX_FL) {
1442 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
1443 inode->i_flags &= ~EXT2_INDEX_FL;
1444 dirty_inode++;
1445 } else {
1446#ifdef ENABLE_HTREE
1447 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
1448#endif
1449 }
1450 }
1451 if (ctx->dirs_to_hash && pb.is_dir &&
1452 !(inode->i_flags & EXT2_INDEX_FL) &&
1453 ((inode->i_size / fs->blocksize) >= 3))
1454 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
1455
1456 if (!pb.num_blocks && pb.is_dir) {
1457 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
1458 inode->i_links_count = 0;
1459 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
1460 inode->i_dtime = time(0);
1461 dirty_inode++;
1462 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
1463 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
1464 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
1465 ctx->fs_directory_count--;
1466 goto out;
1467 }
1468 }
1469
1470 pb.num_blocks *= (fs->blocksize / 512);
1471#if 0
1472 printf("inode %u, i_size = %lu, last_block = %lld, i_blocks=%lu, num_blocks = %lu\n",
1473 ino, inode->i_size, pb.last_block, inode->i_blocks,
1474 pb.num_blocks);
1475#endif
1476 if (pb.is_dir) {
1477 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
1478 if (nblock > (pb.last_block + 1))
1479 bad_size = 1;
1480 else if (nblock < (pb.last_block + 1)) {
1481 if (((pb.last_block + 1) - nblock) >
1482 fs->super->s_prealloc_dir_blocks)
1483 bad_size = 2;
1484 }
1485 } else {
1486 size = EXT2_I_SIZE(inode);
1487 if ((pb.last_block >= 0) &&
1488 (size < (__u64) pb.last_block * fs->blocksize))
1489 bad_size = 3;
1490 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
1491 bad_size = 4;
1492 }
1493 /* i_size for symlinks is checked elsewhere */
1494 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
1495 pctx->num = (pb.last_block+1) * fs->blocksize;
1496 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
1497 inode->i_size = pctx->num;
1498 if (!LINUX_S_ISDIR(inode->i_mode))
1499 inode->i_size_high = pctx->num >> 32;
1500 dirty_inode++;
1501 }
1502 pctx->num = 0;
1503 }
1504 if (LINUX_S_ISREG(inode->i_mode) &&
1505 (inode->i_size_high || inode->i_size & 0x80000000UL))
1506 ctx->large_files++;
1507 if (pb.num_blocks != inode->i_blocks) {
1508 pctx->num = pb.num_blocks;
1509 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
1510 inode->i_blocks = pb.num_blocks;
1511 dirty_inode++;
1512 }
1513 pctx->num = 0;
1514 }
1515out:
1516 if (dirty_inode)
1517 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
1518}
1519
1520#if 0
1521/*
1522 * Helper function called by process block when an illegal block is
1523 * found. It returns a description about why the block is illegal
1524 */
1525static char *describe_illegal_block(ext2_filsys fs, blk_t block)
1526{
1527 blk_t super;
1528 int i;
1529 static char problem[80];
1530
1531 super = fs->super->s_first_data_block;
1532 strcpy(problem, "PROGRAMMING ERROR: Unknown reason for illegal block");
1533 if (block < super) {
1534 sprintf(problem, "< FIRSTBLOCK (%u)", super);
1535 return(problem);
1536 } else if (block >= fs->super->s_blocks_count) {
1537 sprintf(problem, "> BLOCKS (%u)", fs->super->s_blocks_count);
1538 return(problem);
1539 }
1540 for (i = 0; i < fs->group_desc_count; i++) {
1541 if (block == super) {
1542 sprintf(problem, "is the superblock in group %d", i);
1543 break;
1544 }
1545 if (block > super &&
1546 block <= (super + fs->desc_blocks)) {
1547 sprintf(problem, "is in the group descriptors "
1548 "of group %d", i);
1549 break;
1550 }
1551 if (block == fs->group_desc[i].bg_block_bitmap) {
1552 sprintf(problem, "is the block bitmap of group %d", i);
1553 break;
1554 }
1555 if (block == fs->group_desc[i].bg_inode_bitmap) {
1556 sprintf(problem, "is the inode bitmap of group %d", i);
1557 break;
1558 }
1559 if (block >= fs->group_desc[i].bg_inode_table &&
1560 (block < fs->group_desc[i].bg_inode_table
1561 + fs->inode_blocks_per_group)) {
1562 sprintf(problem, "is in the inode table of group %d",
1563 i);
1564 break;
1565 }
1566 super += fs->super->s_blocks_per_group;
1567 }
1568 return(problem);
1569}
1570#endif
1571
1572/*
1573 * This is a helper function for check_blocks().
1574 */
1575static int process_block(ext2_filsys fs,
1576 blk_t *block_nr,
1577 e2_blkcnt_t blockcnt,
1578 blk_t ref_block EXT2FS_ATTR((unused)),
1579 int ref_offset EXT2FS_ATTR((unused)),
1580 void *priv_data)
1581{
1582 struct process_block_struct *p;
1583 struct problem_context *pctx;
1584 blk_t blk = *block_nr;
1585 int ret_code = 0;
1586 int problem = 0;
1587 e2fsck_t ctx;
1588
1589 p = (struct process_block_struct *) priv_data;
1590 pctx = p->pctx;
1591 ctx = p->ctx;
1592
1593 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
1594 /* todo: Check that the comprblk_fl is high, that the
1595 blkaddr pattern looks right (all non-holes up to
1596 first EXT2FS_COMPRESSED_BLKADDR, then all
1597 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
1598 that the feature_incompat bit is high, and that the
1599 inode is a regular file. If we're doing a "full
1600 check" (a concept introduced to e2fsck by e2compr,
1601 meaning that we look at data blocks as well as
1602 metadata) then call some library routine that
1603 checks the compressed data. I'll have to think
1604 about this, because one particularly important
1605 problem to be able to fix is to recalculate the
1606 cluster size if necessary. I think that perhaps
1607 we'd better do most/all e2compr-specific checks
1608 separately, after the non-e2compr checks. If not
1609 doing a full check, it may be useful to test that
1610 the personality is linux; e.g. if it isn't then
1611 perhaps this really is just an illegal block. */
1612 return 0;
1613 }
1614
1615 if (blk == 0) {
1616 if (p->is_dir == 0) {
1617 /*
1618 * Should never happen, since only directories
1619 * get called with BLOCK_FLAG_HOLE
1620 */
1621#if DEBUG_E2FSCK
1622 printf("process_block() called with blk == 0, "
1623 "blockcnt=%d, inode %lu???\n",
1624 blockcnt, p->ino);
1625#endif
1626 return 0;
1627 }
1628 if (blockcnt < 0)
1629 return 0;
1630 if (blockcnt * fs->blocksize < p->inode->i_size) {
1631#if 0
1632 printf("Missing block (#%d) in directory inode %lu!\n",
1633 blockcnt, p->ino);
1634#endif
1635 goto mark_dir;
1636 }
1637 return 0;
1638 }
1639
1640#if 0
1641 printf("Process_block, inode %lu, block %u, #%d\n", p->ino, blk,
1642 blockcnt);
1643#endif
1644
1645 /*
1646 * Simplistic fragmentation check. We merely require that the
1647 * file be contiguous. (Which can never be true for really
1648 * big files that are greater than a block group.)
1649 */
1650 if (!HOLE_BLKADDR(p->previous_block)) {
1651 if (p->previous_block+1 != blk)
1652 p->fragmented = 1;
1653 }
1654 p->previous_block = blk;
1655
1656 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
1657 problem = PR_1_TOOBIG_DIR;
1658 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
1659 problem = PR_1_TOOBIG_REG;
1660 if (!p->is_dir && !p->is_reg && blockcnt > 0)
1661 problem = PR_1_TOOBIG_SYMLINK;
1662
1663 if (blk < fs->super->s_first_data_block ||
1664 blk >= fs->super->s_blocks_count)
1665 problem = PR_1_ILLEGAL_BLOCK_NUM;
1666
1667 if (problem) {
1668 p->num_illegal_blocks++;
1669 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
1670 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
1671 p->clear = 1;
1672 return BLOCK_ABORT;
1673 }
1674 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
1675 p->suppress = 1;
1676 set_latch_flags(PR_LATCH_BLOCK,
1677 PRL_SUPPRESS, 0);
1678 }
1679 }
1680 pctx->blk = blk;
1681 pctx->blkcount = blockcnt;
1682 if (fix_problem(ctx, problem, pctx)) {
1683 blk = *block_nr = 0;
1684 ret_code = BLOCK_CHANGED;
1685 goto mark_dir;
1686 } else
1687 return 0;
1688 }
1689
1690 if (p->ino == EXT2_RESIZE_INO) {
1691 /*
1692 * The resize inode has already be sanity checked
1693 * during pass #0 (the superblock checks). All we
1694 * have to do is mark the double indirect block as
1695 * being in use; all of the other blocks are handled
1696 * by mark_table_blocks()).
1697 */
1698 if (blockcnt == BLOCK_COUNT_DIND)
1699 mark_block_used(ctx, blk);
1700 } else
1701 mark_block_used(ctx, blk);
1702 p->num_blocks++;
1703 if (blockcnt >= 0)
1704 p->last_block = blockcnt;
1705mark_dir:
1706 if (p->is_dir && (blockcnt >= 0)) {
1707 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
1708 blk, blockcnt);
1709 if (pctx->errcode) {
1710 pctx->blk = blk;
1711 pctx->num = blockcnt;
1712 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
1713 /* Should never get here */
1714 ctx->flags |= E2F_FLAG_ABORT;
1715 return BLOCK_ABORT;
1716 }
1717 }
1718 return ret_code;
1719}
1720
1721static int process_bad_block(ext2_filsys fs,
1722 blk_t *block_nr,
1723 e2_blkcnt_t blockcnt,
1724 blk_t ref_block EXT2FS_ATTR((unused)),
1725 int ref_offset EXT2FS_ATTR((unused)),
1726 void *priv_data)
1727{
1728 struct process_block_struct *p;
1729 blk_t blk = *block_nr;
1730 blk_t first_block;
1731 dgrp_t i;
1732 struct problem_context *pctx;
1733 e2fsck_t ctx;
1734
1735 /*
1736 * Note: This function processes blocks for the bad blocks
1737 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
1738 */
1739
1740 if (!blk)
1741 return 0;
1742
1743 p = (struct process_block_struct *) priv_data;
1744 ctx = p->ctx;
1745 pctx = p->pctx;
1746
1747 pctx->ino = EXT2_BAD_INO;
1748 pctx->blk = blk;
1749 pctx->blkcount = blockcnt;
1750
1751 if ((blk < fs->super->s_first_data_block) ||
1752 (blk >= fs->super->s_blocks_count)) {
1753 if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
1754 *block_nr = 0;
1755 return BLOCK_CHANGED;
1756 } else
1757 return 0;
1758 }
1759
1760 if (blockcnt < 0) {
1761 if (ext2fs_test_block_bitmap(p->fs_meta_blocks, blk)) {
1762 p->bbcheck = 1;
1763 if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
1764 *block_nr = 0;
1765 return BLOCK_CHANGED;
1766 }
1767 } else if (ext2fs_test_block_bitmap(ctx->block_found_map,
1768 blk)) {
1769 p->bbcheck = 1;
1770 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
1771 pctx)) {
1772 *block_nr = 0;
1773 return BLOCK_CHANGED;
1774 }
1775 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1776 return BLOCK_ABORT;
1777 } else
1778 mark_block_used(ctx, blk);
1779 return 0;
1780 }
1781#if 0
1782 printf ("DEBUG: Marking %u as bad.\n", blk);
1783#endif
1784 ctx->fs_badblocks_count++;
1785 /*
1786 * If the block is not used, then mark it as used and return.
1787 * If it is already marked as found, this must mean that
1788 * there's an overlap between the filesystem table blocks
1789 * (bitmaps and inode table) and the bad block list.
1790 */
1791 if (!ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
1792 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
1793 return 0;
1794 }
1795 /*
1796 * Try to find the where the filesystem block was used...
1797 */
1798 first_block = fs->super->s_first_data_block;
1799
1800 for (i = 0; i < fs->group_desc_count; i++ ) {
1801 pctx->group = i;
1802 pctx->blk = blk;
1803 if (!ext2fs_bg_has_super(fs, i))
1804 goto skip_super;
1805 if (blk == first_block) {
1806 if (i == 0) {
1807 if (fix_problem(ctx,
1808 PR_1_BAD_PRIMARY_SUPERBLOCK,
1809 pctx)) {
1810 *block_nr = 0;
1811 return BLOCK_CHANGED;
1812 }
1813 return 0;
1814 }
1815 fix_problem(ctx, PR_1_BAD_SUPERBLOCK, pctx);
1816 return 0;
1817 }
1818 if ((blk > first_block) &&
1819 (blk <= first_block + fs->desc_blocks)) {
1820 if (i == 0) {
1821 pctx->blk = *block_nr;
1822 if (fix_problem(ctx,
1823 PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, pctx)) {
1824 *block_nr = 0;
1825 return BLOCK_CHANGED;
1826 }
1827 return 0;
1828 }
1829 fix_problem(ctx, PR_1_BAD_GROUP_DESCRIPTORS, pctx);
1830 return 0;
1831 }
1832 skip_super:
1833 if (blk == fs->group_desc[i].bg_block_bitmap) {
1834 if (fix_problem(ctx, PR_1_BB_BAD_BLOCK, pctx)) {
1835 ctx->invalid_block_bitmap_flag[i]++;
1836 ctx->invalid_bitmaps++;
1837 }
1838 return 0;
1839 }
1840 if (blk == fs->group_desc[i].bg_inode_bitmap) {
1841 if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
1842 ctx->invalid_inode_bitmap_flag[i]++;
1843 ctx->invalid_bitmaps++;
1844 }
1845 return 0;
1846 }
1847 if ((blk >= fs->group_desc[i].bg_inode_table) &&
1848 (blk < (fs->group_desc[i].bg_inode_table +
1849 fs->inode_blocks_per_group))) {
1850 /*
1851 * If there are bad blocks in the inode table,
1852 * the inode scan code will try to do
1853 * something reasonable automatically.
1854 */
1855 return 0;
1856 }
1857 first_block += fs->super->s_blocks_per_group;
1858 }
1859 /*
1860 * If we've gotten to this point, then the only
1861 * possibility is that the bad block inode meta data
1862 * is using a bad block.
1863 */
1864 if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
1865 (blk == p->inode->i_block[EXT2_DIND_BLOCK]) ||
1866 (blk == p->inode->i_block[EXT2_TIND_BLOCK])) {
1867 p->bbcheck = 1;
1868 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK, pctx)) {
1869 *block_nr = 0;
1870 return BLOCK_CHANGED;
1871 }
1872 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1873 return BLOCK_ABORT;
1874 return 0;
1875 }
1876
1877 pctx->group = -1;
1878
1879 /* Warn user that the block wasn't claimed */
1880 fix_problem(ctx, PR_1_PROGERR_CLAIMED_BLOCK, pctx);
1881
1882 return 0;
1883}
1884
1885static void new_table_block(e2fsck_t ctx, blk_t first_block, int group,
1886 const char *name, int num, blk_t *new_block)
1887{
1888 ext2_filsys fs = ctx->fs;
1889 blk_t old_block = *new_block;
1890 int i;
1891 char *buf;
1892 struct problem_context pctx;
1893
1894 clear_problem_context(&pctx);
1895
1896 pctx.group = group;
1897 pctx.blk = old_block;
1898 pctx.str = name;
1899
1900 pctx.errcode = ext2fs_get_free_blocks(fs, first_block,
1901 first_block + fs->super->s_blocks_per_group,
1902 num, ctx->block_found_map, new_block);
1903 if (pctx.errcode) {
1904 pctx.num = num;
1905 fix_problem(ctx, PR_1_RELOC_BLOCK_ALLOCATE, &pctx);
1906 ext2fs_unmark_valid(fs);
1907 return;
1908 }
1909 pctx.errcode = ext2fs_get_mem(fs->blocksize, &buf);
1910 if (pctx.errcode) {
1911 fix_problem(ctx, PR_1_RELOC_MEMORY_ALLOCATE, &pctx);
1912 ext2fs_unmark_valid(fs);
1913 return;
1914 }
1915 ext2fs_mark_super_dirty(fs);
1916 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
1917 pctx.blk2 = *new_block;
1918 fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
1919 PR_1_RELOC_TO), &pctx);
1920 pctx.blk2 = 0;
1921 for (i = 0; i < num; i++) {
1922 pctx.blk = i;
1923 ext2fs_mark_block_bitmap(ctx->block_found_map, (*new_block)+i);
1924 if (old_block) {
1925 pctx.errcode = io_channel_read_blk(fs->io,
1926 old_block + i, 1, buf);
1927 if (pctx.errcode)
1928 fix_problem(ctx, PR_1_RELOC_READ_ERR, &pctx);
1929 } else
1930 memset(buf, 0, fs->blocksize);
1931
1932 pctx.blk = (*new_block) + i;
1933 pctx.errcode = io_channel_write_blk(fs->io, pctx.blk,
1934 1, buf);
1935 if (pctx.errcode)
1936 fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
1937 }
1938 ext2fs_free_mem(&buf);
1939}
1940
1941/*
1942 * This routine gets called at the end of pass 1 if bad blocks are
1943 * detected in the superblock, group descriptors, inode_bitmaps, or
1944 * block bitmaps. At this point, all of the blocks have been mapped
1945 * out, so we can try to allocate new block(s) to replace the bad
1946 * blocks.
1947 */
1948static void handle_fs_bad_blocks(e2fsck_t ctx)
1949{
1950 ext2_filsys fs = ctx->fs;
1951 dgrp_t i;
1952 int first_block = fs->super->s_first_data_block;
1953
1954 for (i = 0; i < fs->group_desc_count; i++) {
1955 if (ctx->invalid_block_bitmap_flag[i]) {
1956 new_table_block(ctx, first_block, i, _("block bitmap"),
1957 1, &fs->group_desc[i].bg_block_bitmap);
1958 }
1959 if (ctx->invalid_inode_bitmap_flag[i]) {
1960 new_table_block(ctx, first_block, i, _("inode bitmap"),
1961 1, &fs->group_desc[i].bg_inode_bitmap);
1962 }
1963 if (ctx->invalid_inode_table_flag[i]) {
1964 new_table_block(ctx, first_block, i, _("inode table"),
1965 fs->inode_blocks_per_group,
1966 &fs->group_desc[i].bg_inode_table);
1967 ctx->flags |= E2F_FLAG_RESTART;
1968 }
1969 first_block += fs->super->s_blocks_per_group;
1970 }
1971 ctx->invalid_bitmaps = 0;
1972}
1973
1974/*
1975 * This routine marks all blocks which are used by the superblock,
1976 * group descriptors, inode bitmaps, and block bitmaps.
1977 */
1978static void mark_table_blocks(e2fsck_t ctx)
1979{
1980 ext2_filsys fs = ctx->fs;
1981 blk_t block, b;
1982 dgrp_t i;
1983 int j;
1984 struct problem_context pctx;
1985
1986 clear_problem_context(&pctx);
1987
1988 block = fs->super->s_first_data_block;
1989 for (i = 0; i < fs->group_desc_count; i++) {
1990 pctx.group = i;
1991
1992 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
1993
1994 /*
1995 * Mark the blocks used for the inode table
1996 */
1997 if (fs->group_desc[i].bg_inode_table) {
1998 for (j = 0, b = fs->group_desc[i].bg_inode_table;
1999 j < fs->inode_blocks_per_group;
2000 j++, b++) {
2001 if (ext2fs_test_block_bitmap(ctx->block_found_map,
2002 b)) {
2003 pctx.blk = b;
2004 if (fix_problem(ctx,
2005 PR_1_ITABLE_CONFLICT, &pctx)) {
2006 ctx->invalid_inode_table_flag[i]++;
2007 ctx->invalid_bitmaps++;
2008 }
2009 } else {
2010 ext2fs_mark_block_bitmap(ctx->block_found_map,
2011 b);
2012 }
2013 }
2014 }
2015
2016 /*
2017 * Mark block used for the block bitmap
2018 */
2019 if (fs->group_desc[i].bg_block_bitmap) {
2020 if (ext2fs_test_block_bitmap(ctx->block_found_map,
2021 fs->group_desc[i].bg_block_bitmap)) {
2022 pctx.blk = fs->group_desc[i].bg_block_bitmap;
2023 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
2024 ctx->invalid_block_bitmap_flag[i]++;
2025 ctx->invalid_bitmaps++;
2026 }
2027 } else {
2028 ext2fs_mark_block_bitmap(ctx->block_found_map,
2029 fs->group_desc[i].bg_block_bitmap);
2030 }
2031
2032 }
2033 /*
2034 * Mark block used for the inode bitmap
2035 */
2036 if (fs->group_desc[i].bg_inode_bitmap) {
2037 if (ext2fs_test_block_bitmap(ctx->block_found_map,
2038 fs->group_desc[i].bg_inode_bitmap)) {
2039 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
2040 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
2041 ctx->invalid_inode_bitmap_flag[i]++;
2042 ctx->invalid_bitmaps++;
2043 }
2044 } else {
2045 ext2fs_mark_block_bitmap(ctx->block_found_map,
2046 fs->group_desc[i].bg_inode_bitmap);
2047 }
2048 }
2049 block += fs->super->s_blocks_per_group;
2050 }
2051}
2052
2053/*
2054 * Thes subroutines short circuits ext2fs_get_blocks and
2055 * ext2fs_check_directory; we use them since we already have the inode
2056 * structure, so there's no point in letting the ext2fs library read
2057 * the inode again.
2058 */
2059static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
2060 blk_t *blocks)
2061{
2062 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
2063 int i;
2064
2065 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
2066 return EXT2_ET_CALLBACK_NOTHANDLED;
2067
2068 for (i=0; i < EXT2_N_BLOCKS; i++)
2069 blocks[i] = ctx->stashed_inode->i_block[i];
2070 return 0;
2071}
2072
2073static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
2074 struct ext2_inode *inode)
2075{
2076 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
2077
2078 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
2079 return EXT2_ET_CALLBACK_NOTHANDLED;
2080 *inode = *ctx->stashed_inode;
2081 return 0;
2082}
2083
2084static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
2085 struct ext2_inode *inode)
2086{
2087 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
2088
2089 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
2090 *ctx->stashed_inode = *inode;
2091 return EXT2_ET_CALLBACK_NOTHANDLED;
2092}
2093
2094static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
2095{
2096 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
2097
2098 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
2099 return EXT2_ET_CALLBACK_NOTHANDLED;
2100
2101 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
2102 return EXT2_ET_NO_DIRECTORY;
2103 return 0;
2104}
2105
2106void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
2107{
2108 ext2_filsys fs = ctx->fs;
2109
2110 if (bool) {
2111 fs->get_blocks = pass1_get_blocks;
2112 fs->check_directory = pass1_check_directory;
2113 fs->read_inode = pass1_read_inode;
2114 fs->write_inode = pass1_write_inode;
2115 ctx->stashed_ino = 0;
2116 } else {
2117 fs->get_blocks = 0;
2118 fs->check_directory = 0;
2119 fs->read_inode = 0;
2120 fs->write_inode = 0;
2121 }
2122}
diff --git a/e2fsprogs/e2fsck/pass1b.c b/e2fsprogs/e2fsck/pass1b.c
new file mode 100644
index 000000000..ff63807b7
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass1b.c
@@ -0,0 +1,805 @@
1/*
2 * pass1b.c --- Pass #1b of e2fsck
3 *
4 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
5 * only invoked if pass 1 discovered blocks which are in use by more
6 * than one inode.
7 *
8 * Pass1B scans the data blocks of all the inodes again, generating a
9 * complete list of duplicate blocks and which inodes have claimed
10 * them.
11 *
12 * Pass1C does a tree-traversal of the filesystem, to determine the
13 * parent directories of these inodes. This step is necessary so that
14 * e2fsck can print out the pathnames of affected inodes.
15 *
16 * Pass1D is a reconciliation pass. For each inode with duplicate
17 * blocks, the user is prompted if s/he would like to clone the file
18 * (so that the file gets a fresh copy of the duplicated blocks) or
19 * simply to delete the file.
20 *
21 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
22 *
23 * %Begin-Header%
24 * This file may be redistributed under the terms of the GNU Public
25 * License.
26 * %End-Header%
27 *
28 */
29
30#include <time.h>
31#ifdef HAVE_ERRNO_H
32#include <errno.h>
33#endif
34
35#ifdef HAVE_INTTYPES_H
36#include <inttypes.h>
37#endif
38
39/* Needed for architectures where sizeof(int) != sizeof(void *) */
40#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
41#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
42
43#include "e2fsck.h"
44
45#include "problem.h"
46#include "dict.h"
47
48/* Define an extension to the ext2 library's block count information */
49#define BLOCK_COUNT_EXTATTR (-5)
50
51struct block_el {
52 blk_t block;
53 struct block_el *next;
54};
55
56struct inode_el {
57 ext2_ino_t inode;
58 struct inode_el *next;
59};
60
61struct dup_block {
62 int num_bad;
63 struct inode_el *inode_list;
64};
65
66/*
67 * This structure stores information about a particular inode which
68 * is sharing blocks with other inodes. This information is collected
69 * to display to the user, so that the user knows what files he or she
70 * is dealing with, when trying to decide how to resolve the conflict
71 * of multiply-claimed blocks.
72 */
73struct dup_inode {
74 ext2_ino_t dir;
75 int num_dupblocks;
76 struct ext2_inode inode;
77 struct block_el *block_list;
78};
79
80static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
81 e2_blkcnt_t blockcnt, blk_t ref_blk,
82 int ref_offset, void *priv_data);
83static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
84 struct dup_inode *dp, char *block_buf);
85static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
86 struct dup_inode *dp, char* block_buf);
87static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
88
89static void pass1b(e2fsck_t ctx, char *block_buf);
90static void pass1c(e2fsck_t ctx, char *block_buf);
91static void pass1d(e2fsck_t ctx, char *block_buf);
92
93static int dup_inode_count = 0;
94
95static dict_t blk_dict, ino_dict;
96
97static ext2fs_inode_bitmap inode_dup_map;
98
99static int dict_int_cmp(const void *a, const void *b)
100{
101 intptr_t ia, ib;
102
103 ia = (intptr_t)a;
104 ib = (intptr_t)b;
105
106 return (ia-ib);
107}
108
109/*
110 * Add a duplicate block record
111 */
112static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
113 struct ext2_inode *inode)
114{
115 dnode_t *n;
116 struct dup_block *db;
117 struct dup_inode *di;
118 struct block_el *blk_el;
119 struct inode_el *ino_el;
120
121 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
122 if (n)
123 db = (struct dup_block *) dnode_get(n);
124 else {
125 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
126 sizeof(struct dup_block), "duplicate block header");
127 db->num_bad = 0;
128 db->inode_list = 0;
129 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
130 }
131 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
132 sizeof(struct inode_el), "inode element");
133 ino_el->inode = ino;
134 ino_el->next = db->inode_list;
135 db->inode_list = ino_el;
136 db->num_bad++;
137
138 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
139 if (n)
140 di = (struct dup_inode *) dnode_get(n);
141 else {
142 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
143 sizeof(struct dup_inode), "duplicate inode header");
144 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
145 di->num_dupblocks = 0;
146 di->block_list = 0;
147 di->inode = *inode;
148 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
149 }
150 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
151 sizeof(struct block_el), "block element");
152 blk_el->block = blk;
153 blk_el->next = di->block_list;
154 di->block_list = blk_el;
155 di->num_dupblocks++;
156}
157
158/*
159 * Free a duplicate inode record
160 */
161static void inode_dnode_free(dnode_t *node,
162 void *context EXT2FS_ATTR((unused)))
163{
164 struct dup_inode *di;
165 struct block_el *p, *next;
166
167 di = (struct dup_inode *) dnode_get(node);
168 for (p = di->block_list; p; p = next) {
169 next = p->next;
170 free(p);
171 }
172 free(node);
173}
174
175/*
176 * Free a duplicate block record
177 */
178static void block_dnode_free(dnode_t *node,
179 void *context EXT2FS_ATTR((unused)))
180{
181 struct dup_block *db;
182 struct inode_el *p, *next;
183
184 db = (struct dup_block *) dnode_get(node);
185 for (p = db->inode_list; p; p = next) {
186 next = p->next;
187 free(p);
188 }
189 free(node);
190}
191
192
193/*
194 * Main procedure for handling duplicate blocks
195 */
196void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
197{
198 ext2_filsys fs = ctx->fs;
199 struct problem_context pctx;
200
201 clear_problem_context(&pctx);
202
203 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
204 _("multiply claimed inode map"), &inode_dup_map);
205 if (pctx.errcode) {
206 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
207 ctx->flags |= E2F_FLAG_ABORT;
208 return;
209 }
210
211 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
212 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
213 dict_set_allocator(&ino_dict, NULL, inode_dnode_free, NULL);
214 dict_set_allocator(&blk_dict, NULL, block_dnode_free, NULL);
215
216 pass1b(ctx, block_buf);
217 pass1c(ctx, block_buf);
218 pass1d(ctx, block_buf);
219
220 /*
221 * Time to free all of the accumulated data structures that we
222 * don't need anymore.
223 */
224 dict_free_nodes(&ino_dict);
225 dict_free_nodes(&blk_dict);
226}
227
228/*
229 * Scan the inodes looking for inodes that contain duplicate blocks.
230 */
231struct process_block_struct {
232 e2fsck_t ctx;
233 ext2_ino_t ino;
234 int dup_blocks;
235 struct ext2_inode *inode;
236 struct problem_context *pctx;
237};
238
239static void pass1b(e2fsck_t ctx, char *block_buf)
240{
241 ext2_filsys fs = ctx->fs;
242 ext2_ino_t ino;
243 struct ext2_inode inode;
244 ext2_inode_scan scan;
245 struct process_block_struct pb;
246 struct problem_context pctx;
247
248 clear_problem_context(&pctx);
249
250 if (!(ctx->options & E2F_OPT_PREEN))
251 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
252 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
253 &scan);
254 if (pctx.errcode) {
255 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
256 ctx->flags |= E2F_FLAG_ABORT;
257 return;
258 }
259 ctx->stashed_inode = &inode;
260 pb.ctx = ctx;
261 pb.pctx = &pctx;
262 pctx.str = "pass1b";
263 while (1) {
264 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
265 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
266 continue;
267 if (pctx.errcode) {
268 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
269 ctx->flags |= E2F_FLAG_ABORT;
270 return;
271 }
272 if (!ino)
273 break;
274 pctx.ino = ctx->stashed_ino = ino;
275 if ((ino != EXT2_BAD_INO) &&
276 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
277 continue;
278
279 pb.ino = ino;
280 pb.dup_blocks = 0;
281 pb.inode = &inode;
282
283 if (ext2fs_inode_has_valid_blocks(&inode) ||
284 (ino == EXT2_BAD_INO))
285 pctx.errcode = ext2fs_block_iterate2(fs, ino,
286 0, block_buf, process_pass1b_block, &pb);
287 if (inode.i_file_acl)
288 process_pass1b_block(fs, &inode.i_file_acl,
289 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
290 if (pb.dup_blocks) {
291 end_problem_latch(ctx, PR_LATCH_DBLOCK);
292 if (ino >= EXT2_FIRST_INODE(fs->super) ||
293 ino == EXT2_ROOT_INO)
294 dup_inode_count++;
295 }
296 if (pctx.errcode)
297 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
298 }
299 ext2fs_close_inode_scan(scan);
300 e2fsck_use_inode_shortcuts(ctx, 0);
301}
302
303static int process_pass1b_block(ext2_filsys fs EXT2FS_ATTR((unused)),
304 blk_t *block_nr,
305 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
306 blk_t ref_blk EXT2FS_ATTR((unused)),
307 int ref_offset EXT2FS_ATTR((unused)),
308 void *priv_data)
309{
310 struct process_block_struct *p;
311 e2fsck_t ctx;
312
313 if (HOLE_BLKADDR(*block_nr))
314 return 0;
315 p = (struct process_block_struct *) priv_data;
316 ctx = p->ctx;
317
318 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
319 return 0;
320
321 /* OK, this is a duplicate block */
322 if (p->ino != EXT2_BAD_INO) {
323 p->pctx->blk = *block_nr;
324 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
325 }
326 p->dup_blocks++;
327 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
328
329 add_dupe(ctx, p->ino, *block_nr, p->inode);
330
331 return 0;
332}
333
334/*
335 * Pass 1c: Scan directories for inodes with duplicate blocks. This
336 * is used so that we can print pathnames when prompting the user for
337 * what to do.
338 */
339struct search_dir_struct {
340 int count;
341 ext2_ino_t first_inode;
342 ext2_ino_t max_inode;
343};
344
345static int search_dirent_proc(ext2_ino_t dir, int entry,
346 struct ext2_dir_entry *dirent,
347 int offset EXT2FS_ATTR((unused)),
348 int blocksize EXT2FS_ATTR((unused)),
349 char *buf EXT2FS_ATTR((unused)),
350 void *priv_data)
351{
352 struct search_dir_struct *sd;
353 struct dup_inode *p;
354 dnode_t *n;
355
356 sd = (struct search_dir_struct *) priv_data;
357
358 if (dirent->inode > sd->max_inode)
359 /* Should abort this inode, but not everything */
360 return 0;
361
362 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
363 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
364 return 0;
365
366 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
367 if (!n)
368 return 0;
369 p = (struct dup_inode *) dnode_get(n);
370 p->dir = dir;
371 sd->count--;
372
373 return(sd->count ? 0 : DIRENT_ABORT);
374}
375
376
377static void pass1c(e2fsck_t ctx, char *block_buf)
378{
379 ext2_filsys fs = ctx->fs;
380 struct search_dir_struct sd;
381 struct problem_context pctx;
382
383 clear_problem_context(&pctx);
384
385 if (!(ctx->options & E2F_OPT_PREEN))
386 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
387
388 /*
389 * Search through all directories to translate inodes to names
390 * (by searching for the containing directory for that inode.)
391 */
392 sd.count = dup_inode_count;
393 sd.first_inode = EXT2_FIRST_INODE(fs->super);
394 sd.max_inode = fs->super->s_inodes_count;
395 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
396 search_dirent_proc, &sd);
397}
398
399static void pass1d(e2fsck_t ctx, char *block_buf)
400{
401 ext2_filsys fs = ctx->fs;
402 struct dup_inode *p, *t;
403 struct dup_block *q;
404 ext2_ino_t *shared, ino;
405 int shared_len;
406 int i;
407 int file_ok;
408 int meta_data = 0;
409 struct problem_context pctx;
410 dnode_t *n, *m;
411 struct block_el *s;
412 struct inode_el *r;
413
414 clear_problem_context(&pctx);
415
416 if (!(ctx->options & E2F_OPT_PREEN))
417 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
418 e2fsck_read_bitmaps(ctx);
419
420 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
421 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
422 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
423 sizeof(ext2_ino_t) * dict_count(&ino_dict),
424 "Shared inode list");
425 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
426 p = (struct dup_inode *) dnode_get(n);
427 shared_len = 0;
428 file_ok = 1;
429 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
430 if (ino == EXT2_BAD_INO)
431 continue;
432
433 /*
434 * Find all of the inodes which share blocks with this
435 * one. First we find all of the duplicate blocks
436 * belonging to this inode, and then search each block
437 * get the list of inodes, and merge them together.
438 */
439 for (s = p->block_list; s; s = s->next) {
440 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
441 if (!m)
442 continue; /* Should never happen... */
443 q = (struct dup_block *) dnode_get(m);
444 if (q->num_bad > 1)
445 file_ok = 0;
446 if (check_if_fs_block(ctx, s->block)) {
447 file_ok = 0;
448 meta_data = 1;
449 }
450
451 /*
452 * Add all inodes used by this block to the
453 * shared[] --- which is a unique list, so
454 * if an inode is already in shared[], don't
455 * add it again.
456 */
457 for (r = q->inode_list; r; r = r->next) {
458 if (r->inode == ino)
459 continue;
460 for (i = 0; i < shared_len; i++)
461 if (shared[i] == r->inode)
462 break;
463 if (i == shared_len) {
464 shared[shared_len++] = r->inode;
465 }
466 }
467 }
468
469 /*
470 * Report the inode that we are working on
471 */
472 pctx.inode = &p->inode;
473 pctx.ino = ino;
474 pctx.dir = p->dir;
475 pctx.blkcount = p->num_dupblocks;
476 pctx.num = meta_data ? shared_len+1 : shared_len;
477 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
478 pctx.blkcount = 0;
479 pctx.num = 0;
480
481 if (meta_data)
482 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
483
484 for (i = 0; i < shared_len; i++) {
485 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
486 if (!m)
487 continue; /* should never happen */
488 t = (struct dup_inode *) dnode_get(m);
489 /*
490 * Report the inode that we are sharing with
491 */
492 pctx.inode = &t->inode;
493 pctx.ino = shared[i];
494 pctx.dir = t->dir;
495 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
496 }
497 if (file_ok) {
498 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
499 continue;
500 }
501 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
502 pctx.errcode = clone_file(ctx, ino, p, block_buf);
503 if (pctx.errcode)
504 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
505 else
506 continue;
507 }
508 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
509 delete_file(ctx, ino, p, block_buf);
510 else
511 ext2fs_unmark_valid(fs);
512 }
513 ext2fs_free_mem(&shared);
514}
515
516/*
517 * Drop the refcount on the dup_block structure, and clear the entry
518 * in the block_dup_map if appropriate.
519 */
520static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
521{
522 p->num_bad--;
523 if (p->num_bad <= 0 ||
524 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
525 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
526}
527
528static int delete_file_block(ext2_filsys fs,
529 blk_t *block_nr,
530 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
531 blk_t ref_block EXT2FS_ATTR((unused)),
532 int ref_offset EXT2FS_ATTR((unused)),
533 void *priv_data)
534{
535 struct process_block_struct *pb;
536 struct dup_block *p;
537 dnode_t *n;
538 e2fsck_t ctx;
539
540 pb = (struct process_block_struct *) priv_data;
541 ctx = pb->ctx;
542
543 if (HOLE_BLKADDR(*block_nr))
544 return 0;
545
546 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
547 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
548 if (n) {
549 p = (struct dup_block *) dnode_get(n);
550 decrement_badcount(ctx, *block_nr, p);
551 } else
552 com_err("delete_file_block", 0,
553 _("internal error; can't find dup_blk for %d\n"),
554 *block_nr);
555 } else {
556 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
557 ext2fs_block_alloc_stats(fs, *block_nr, -1);
558 }
559
560 return 0;
561}
562
563static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
564 struct dup_inode *dp, char* block_buf)
565{
566 ext2_filsys fs = ctx->fs;
567 struct process_block_struct pb;
568 struct ext2_inode inode;
569 struct problem_context pctx;
570 unsigned int count;
571
572 clear_problem_context(&pctx);
573 pctx.ino = pb.ino = ino;
574 pb.dup_blocks = dp->num_dupblocks;
575 pb.ctx = ctx;
576 pctx.str = "delete_file";
577
578 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
579 if (ext2fs_inode_has_valid_blocks(&inode))
580 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
581 delete_file_block, &pb);
582 if (pctx.errcode)
583 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
584 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
585 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
586 if (ctx->inode_bad_map)
587 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
588 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
589
590 /* Inode may have changed by block_iterate, so reread it */
591 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
592 inode.i_links_count = 0;
593 inode.i_dtime = time(0);
594 if (inode.i_file_acl &&
595 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
596 count = 1;
597 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
598 block_buf, -1, &count);
599 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
600 pctx.errcode = 0;
601 count = 1;
602 }
603 if (pctx.errcode) {
604 pctx.blk = inode.i_file_acl;
605 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
606 }
607 /*
608 * If the count is zero, then arrange to have the
609 * block deleted. If the block is in the block_dup_map,
610 * also call delete_file_block since it will take care
611 * of keeping the accounting straight.
612 */
613 if ((count == 0) ||
614 ext2fs_test_block_bitmap(ctx->block_dup_map,
615 inode.i_file_acl))
616 delete_file_block(fs, &inode.i_file_acl,
617 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
618 }
619 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
620}
621
622struct clone_struct {
623 errcode_t errcode;
624 ext2_ino_t dir;
625 char *buf;
626 e2fsck_t ctx;
627};
628
629static int clone_file_block(ext2_filsys fs,
630 blk_t *block_nr,
631 e2_blkcnt_t blockcnt,
632 blk_t ref_block EXT2FS_ATTR((unused)),
633 int ref_offset EXT2FS_ATTR((unused)),
634 void *priv_data)
635{
636 struct dup_block *p;
637 blk_t new_block;
638 errcode_t retval;
639 struct clone_struct *cs = (struct clone_struct *) priv_data;
640 dnode_t *n;
641 e2fsck_t ctx;
642
643 ctx = cs->ctx;
644
645 if (HOLE_BLKADDR(*block_nr))
646 return 0;
647
648 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
649 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
650 if (n) {
651 p = (struct dup_block *) dnode_get(n);
652 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
653 &new_block);
654 if (retval) {
655 cs->errcode = retval;
656 return BLOCK_ABORT;
657 }
658 if (cs->dir && (blockcnt >= 0)) {
659 retval = ext2fs_set_dir_block(fs->dblist,
660 cs->dir, new_block, blockcnt);
661 if (retval) {
662 cs->errcode = retval;
663 return BLOCK_ABORT;
664 }
665 }
666#if 0
667 printf("Cloning block %u to %u\n", *block_nr,
668 new_block);
669#endif
670 retval = io_channel_read_blk(fs->io, *block_nr, 1,
671 cs->buf);
672 if (retval) {
673 cs->errcode = retval;
674 return BLOCK_ABORT;
675 }
676 retval = io_channel_write_blk(fs->io, new_block, 1,
677 cs->buf);
678 if (retval) {
679 cs->errcode = retval;
680 return BLOCK_ABORT;
681 }
682 decrement_badcount(ctx, *block_nr, p);
683 *block_nr = new_block;
684 ext2fs_mark_block_bitmap(ctx->block_found_map,
685 new_block);
686 ext2fs_mark_block_bitmap(fs->block_map, new_block);
687 return BLOCK_CHANGED;
688 } else
689 com_err("clone_file_block", 0,
690 _("internal error; can't find dup_blk for %d\n"),
691 *block_nr);
692 }
693 return 0;
694}
695
696static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
697 struct dup_inode *dp, char* block_buf)
698{
699 ext2_filsys fs = ctx->fs;
700 errcode_t retval;
701 struct clone_struct cs;
702 struct problem_context pctx;
703 blk_t blk;
704 dnode_t *n;
705 struct inode_el *ino_el;
706 struct dup_block *db;
707 struct dup_inode *di;
708
709 clear_problem_context(&pctx);
710 cs.errcode = 0;
711 cs.dir = 0;
712 cs.ctx = ctx;
713 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
714 if (retval)
715 return retval;
716
717 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
718 cs.dir = ino;
719
720 pctx.ino = ino;
721 pctx.str = "clone_file";
722 if (ext2fs_inode_has_valid_blocks(&dp->inode))
723 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
724 clone_file_block, &cs);
725 ext2fs_mark_bb_dirty(fs);
726 if (pctx.errcode) {
727 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
728 retval = pctx.errcode;
729 goto errout;
730 }
731 if (cs.errcode) {
732 com_err("clone_file", cs.errcode,
733 _("returned from clone_file_block"));
734 retval = cs.errcode;
735 goto errout;
736 }
737 /* The inode may have changed on disk, so we have to re-read it */
738 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
739 blk = dp->inode.i_file_acl;
740 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
741 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
742 BLOCK_CHANGED)) {
743 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
744 /*
745 * If we cloned the EA block, find all other inodes
746 * which refered to that EA block, and modify
747 * them to point to the new EA block.
748 */
749 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
750 db = (struct dup_block *) dnode_get(n);
751 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
752 if (ino_el->inode == ino)
753 continue;
754 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
755 di = (struct dup_inode *) dnode_get(n);
756 if (di->inode.i_file_acl == blk) {
757 di->inode.i_file_acl = dp->inode.i_file_acl;
758 e2fsck_write_inode(ctx, ino_el->inode,
759 &di->inode, "clone file EA");
760 decrement_badcount(ctx, blk, db);
761 }
762 }
763 }
764 retval = 0;
765errout:
766 ext2fs_free_mem(&cs.buf);
767 return retval;
768}
769
770/*
771 * This routine returns 1 if a block overlaps with one of the superblocks,
772 * group descriptors, inode bitmaps, or block bitmaps.
773 */
774static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
775{
776 ext2_filsys fs = ctx->fs;
777 blk_t block;
778 dgrp_t i;
779
780 block = fs->super->s_first_data_block;
781 for (i = 0; i < fs->group_desc_count; i++) {
782
783 /* Check superblocks/block group descriptros */
784 if (ext2fs_bg_has_super(fs, i)) {
785 if (test_block >= block &&
786 (test_block <= block + fs->desc_blocks))
787 return 1;
788 }
789
790 /* Check the inode table */
791 if ((fs->group_desc[i].bg_inode_table) &&
792 (test_block >= fs->group_desc[i].bg_inode_table) &&
793 (test_block < (fs->group_desc[i].bg_inode_table +
794 fs->inode_blocks_per_group)))
795 return 1;
796
797 /* Check the bitmap blocks */
798 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
799 (test_block == fs->group_desc[i].bg_inode_bitmap))
800 return 1;
801
802 block += fs->super->s_blocks_per_group;
803 }
804 return 0;
805}
diff --git a/e2fsprogs/e2fsck/pass2.c b/e2fsprogs/e2fsck/pass2.c
new file mode 100644
index 000000000..69599fffc
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass2.c
@@ -0,0 +1,1414 @@
1/*
2 * pass2.c --- check directory structure
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * Pass 2 of e2fsck iterates through all active directory inodes, and
12 * applies to following tests to each directory entry in the directory
13 * blocks in the inodes:
14 *
15 * - The length of the directory entry (rec_len) should be at
16 * least 8 bytes, and no more than the remaining space
17 * left in the directory block.
18 * - The length of the name in the directory entry (name_len)
19 * should be less than (rec_len - 8).
20 * - The inode number in the directory entry should be within
21 * legal bounds.
22 * - The inode number should refer to a in-use inode.
23 * - The first entry should be '.', and its inode should be
24 * the inode of the directory.
25 * - The second entry should be '..'.
26 *
27 * To minimize disk seek time, the directory blocks are processed in
28 * sorted order of block numbers.
29 *
30 * Pass 2 also collects the following information:
31 * - The inode numbers of the subdirectories for each directory.
32 *
33 * Pass 2 relies on the following information from previous passes:
34 * - The directory information collected in pass 1.
35 * - The inode_used_map bitmap
36 * - The inode_bad_map bitmap
37 * - The inode_dir_map bitmap
38 *
39 * Pass 2 frees the following data structures
40 * - The inode_bad_map bitmap
41 * - The inode_reg_map bitmap
42 */
43
44#define _GNU_SOURCE 1 /* get strnlen() */
45#include <string.h>
46
47#include "e2fsck.h"
48#include "problem.h"
49#include "dict.h"
50
51#ifdef NO_INLINE_FUNCS
52#define _INLINE_
53#else
54#define _INLINE_ inline
55#endif
56
57/* #define DX_DEBUG */
58
59/*
60 * Keeps track of how many times an inode is referenced.
61 */
62static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
63static int check_dir_block(ext2_filsys fs,
64 struct ext2_db_entry *dir_blocks_info,
65 void *priv_data);
66static int allocate_dir_block(e2fsck_t ctx,
67 struct ext2_db_entry *dir_blocks_info,
68 char *buf, struct problem_context *pctx);
69static int update_dir_block(ext2_filsys fs,
70 blk_t *block_nr,
71 e2_blkcnt_t blockcnt,
72 blk_t ref_block,
73 int ref_offset,
74 void *priv_data);
75static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
76static int htree_depth(struct dx_dir_info *dx_dir,
77 struct dx_dirblock_info *dx_db);
78static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b);
79
80struct check_dir_struct {
81 char *buf;
82 struct problem_context pctx;
83 int count, max;
84 e2fsck_t ctx;
85};
86
87void e2fsck_pass2(e2fsck_t ctx)
88{
89 struct ext2_super_block *sb = ctx->fs->super;
90 struct problem_context pctx;
91 ext2_filsys fs = ctx->fs;
92 char *buf;
93#ifdef RESOURCE_TRACK
94 struct resource_track rtrack;
95#endif
96 struct dir_info *dir;
97 struct check_dir_struct cd;
98 struct dx_dir_info *dx_dir;
99 struct dx_dirblock_info *dx_db, *dx_parent;
100 int b;
101 int i, depth;
102 problem_t code;
103 int bad_dir;
104
105#ifdef RESOURCE_TRACK
106 init_resource_track(&rtrack);
107#endif
108
109 clear_problem_context(&cd.pctx);
110
111#ifdef MTRACE
112 mtrace_print("Pass 2");
113#endif
114
115 if (!(ctx->options & E2F_OPT_PREEN))
116 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
117
118 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
119 0, ctx->inode_link_info,
120 &ctx->inode_count);
121 if (cd.pctx.errcode) {
122 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
123 ctx->flags |= E2F_FLAG_ABORT;
124 return;
125 }
126 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
127 "directory scan buffer");
128
129 /*
130 * Set up the parent pointer for the root directory, if
131 * present. (If the root directory is not present, we will
132 * create it in pass 3.)
133 */
134 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
135 if (dir)
136 dir->parent = EXT2_ROOT_INO;
137
138 cd.buf = buf;
139 cd.ctx = ctx;
140 cd.count = 1;
141 cd.max = ext2fs_dblist_count(fs->dblist);
142
143 if (ctx->progress)
144 (void) (ctx->progress)(ctx, 2, 0, cd.max);
145
146 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
147 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
148
149 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
150 &cd);
151 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
152 return;
153 if (cd.pctx.errcode) {
154 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
155 ctx->flags |= E2F_FLAG_ABORT;
156 return;
157 }
158
159#ifdef ENABLE_HTREE
160 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
161 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
162 return;
163 if (dx_dir->numblocks == 0)
164 continue;
165 clear_problem_context(&pctx);
166 bad_dir = 0;
167 pctx.dir = dx_dir->ino;
168 dx_db = dx_dir->dx_block;
169 if (dx_db->flags & DX_FLAG_REFERENCED)
170 dx_db->flags |= DX_FLAG_DUP_REF;
171 else
172 dx_db->flags |= DX_FLAG_REFERENCED;
173 /*
174 * Find all of the first and last leaf blocks, and
175 * update their parent's min and max hash values
176 */
177 for (b=0, dx_db = dx_dir->dx_block;
178 b < dx_dir->numblocks;
179 b++, dx_db++) {
180 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
181 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
182 continue;
183 dx_parent = &dx_dir->dx_block[dx_db->parent];
184 /*
185 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
186 */
187 if (dx_db->flags & DX_FLAG_FIRST)
188 dx_parent->min_hash = dx_db->min_hash;
189 /*
190 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
191 */
192 if (dx_db->flags & DX_FLAG_LAST)
193 dx_parent->max_hash = dx_db->max_hash;
194 }
195
196 for (b=0, dx_db = dx_dir->dx_block;
197 b < dx_dir->numblocks;
198 b++, dx_db++) {
199 pctx.blkcount = b;
200 pctx.group = dx_db->parent;
201 code = 0;
202 if (!(dx_db->flags & DX_FLAG_FIRST) &&
203 (dx_db->min_hash < dx_db->node_min_hash)) {
204 pctx.blk = dx_db->min_hash;
205 pctx.blk2 = dx_db->node_min_hash;
206 code = PR_2_HTREE_MIN_HASH;
207 fix_problem(ctx, code, &pctx);
208 bad_dir++;
209 }
210 if (dx_db->type == DX_DIRBLOCK_LEAF) {
211 depth = htree_depth(dx_dir, dx_db);
212 if (depth != dx_dir->depth) {
213 code = PR_2_HTREE_BAD_DEPTH;
214 fix_problem(ctx, code, &pctx);
215 bad_dir++;
216 }
217 }
218 /*
219 * This test doesn't apply for the root block
220 * at block #0
221 */
222 if (b &&
223 (dx_db->max_hash > dx_db->node_max_hash)) {
224 pctx.blk = dx_db->max_hash;
225 pctx.blk2 = dx_db->node_max_hash;
226 code = PR_2_HTREE_MAX_HASH;
227 fix_problem(ctx, code, &pctx);
228 bad_dir++;
229 }
230 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
231 code = PR_2_HTREE_NOTREF;
232 fix_problem(ctx, code, &pctx);
233 bad_dir++;
234 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
235 code = PR_2_HTREE_DUPREF;
236 fix_problem(ctx, code, &pctx);
237 bad_dir++;
238 }
239 if (code == 0)
240 continue;
241 }
242 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
243 clear_htree(ctx, dx_dir->ino);
244 dx_dir->numblocks = 0;
245 }
246 }
247#endif
248 ext2fs_free_mem(&buf);
249 ext2fs_free_dblist(fs->dblist);
250
251 if (ctx->inode_bad_map) {
252 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
253 ctx->inode_bad_map = 0;
254 }
255 if (ctx->inode_reg_map) {
256 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
257 ctx->inode_reg_map = 0;
258 }
259
260 clear_problem_context(&pctx);
261 if (ctx->large_files) {
262 if (!(sb->s_feature_ro_compat &
263 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
264 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
265 sb->s_feature_ro_compat |=
266 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
267 ext2fs_mark_super_dirty(fs);
268 }
269 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
270 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
271 ext2fs_update_dynamic_rev(fs);
272 ext2fs_mark_super_dirty(fs);
273 }
274 } else if (!ctx->large_files &&
275 (sb->s_feature_ro_compat &
276 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
277 if (fs->flags & EXT2_FLAG_RW) {
278 sb->s_feature_ro_compat &=
279 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
280 ext2fs_mark_super_dirty(fs);
281 }
282 }
283
284#ifdef RESOURCE_TRACK
285 if (ctx->options & E2F_OPT_TIME2) {
286 e2fsck_clear_progbar(ctx);
287 print_resource_track(_("Pass 2"), &rtrack);
288 }
289#endif
290}
291
292#define MAX_DEPTH 32000
293static int htree_depth(struct dx_dir_info *dx_dir,
294 struct dx_dirblock_info *dx_db)
295{
296 int depth = 0;
297
298 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
299 dx_db = &dx_dir->dx_block[dx_db->parent];
300 depth++;
301 }
302 return depth;
303}
304
305static int dict_de_cmp(const void *a, const void *b)
306{
307 const struct ext2_dir_entry *de_a, *de_b;
308 int a_len, b_len;
309
310 de_a = (const struct ext2_dir_entry *) a;
311 a_len = de_a->name_len & 0xFF;
312 de_b = (const struct ext2_dir_entry *) b;
313 b_len = de_b->name_len & 0xFF;
314
315 if (a_len != b_len)
316 return (a_len - b_len);
317
318 return strncmp(de_a->name, de_b->name, a_len);
319}
320
321/*
322 * This is special sort function that makes sure that directory blocks
323 * with a dirblock of zero are sorted to the beginning of the list.
324 * This guarantees that the root node of the htree directories are
325 * processed first, so we know what hash version to use.
326 */
327static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b)
328{
329 const struct ext2_db_entry *db_a =
330 (const struct ext2_db_entry *) a;
331 const struct ext2_db_entry *db_b =
332 (const struct ext2_db_entry *) b;
333
334 if (db_a->blockcnt && !db_b->blockcnt)
335 return 1;
336
337 if (!db_a->blockcnt && db_b->blockcnt)
338 return -1;
339
340 if (db_a->blk != db_b->blk)
341 return (int) (db_a->blk - db_b->blk);
342
343 if (db_a->ino != db_b->ino)
344 return (int) (db_a->ino - db_b->ino);
345
346 return (int) (db_a->blockcnt - db_b->blockcnt);
347}
348
349
350/*
351 * Make sure the first entry in the directory is '.', and that the
352 * directory entry is sane.
353 */
354static int check_dot(e2fsck_t ctx,
355 struct ext2_dir_entry *dirent,
356 ext2_ino_t ino, struct problem_context *pctx)
357{
358 struct ext2_dir_entry *nextdir;
359 int status = 0;
360 int created = 0;
361 int new_len;
362 int problem = 0;
363
364 if (!dirent->inode)
365 problem = PR_2_MISSING_DOT;
366 else if (((dirent->name_len & 0xFF) != 1) ||
367 (dirent->name[0] != '.'))
368 problem = PR_2_1ST_NOT_DOT;
369 else if (dirent->name[1] != '\0')
370 problem = PR_2_DOT_NULL_TERM;
371
372 if (problem) {
373 if (fix_problem(ctx, problem, pctx)) {
374 if (dirent->rec_len < 12)
375 dirent->rec_len = 12;
376 dirent->inode = ino;
377 dirent->name_len = 1;
378 dirent->name[0] = '.';
379 dirent->name[1] = '\0';
380 status = 1;
381 created = 1;
382 }
383 }
384 if (dirent->inode != ino) {
385 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
386 dirent->inode = ino;
387 status = 1;
388 }
389 }
390 if (dirent->rec_len > 12) {
391 new_len = dirent->rec_len - 12;
392 if (new_len > 12) {
393 if (created ||
394 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
395 nextdir = (struct ext2_dir_entry *)
396 ((char *) dirent + 12);
397 dirent->rec_len = 12;
398 nextdir->rec_len = new_len;
399 nextdir->inode = 0;
400 nextdir->name_len = 0;
401 status = 1;
402 }
403 }
404 }
405 return status;
406}
407
408/*
409 * Make sure the second entry in the directory is '..', and that the
410 * directory entry is sane. We do not check the inode number of '..'
411 * here; this gets done in pass 3.
412 */
413static int check_dotdot(e2fsck_t ctx,
414 struct ext2_dir_entry *dirent,
415 struct dir_info *dir, struct problem_context *pctx)
416{
417 int problem = 0;
418
419 if (!dirent->inode)
420 problem = PR_2_MISSING_DOT_DOT;
421 else if (((dirent->name_len & 0xFF) != 2) ||
422 (dirent->name[0] != '.') ||
423 (dirent->name[1] != '.'))
424 problem = PR_2_2ND_NOT_DOT_DOT;
425 else if (dirent->name[2] != '\0')
426 problem = PR_2_DOT_DOT_NULL_TERM;
427
428 if (problem) {
429 if (fix_problem(ctx, problem, pctx)) {
430 if (dirent->rec_len < 12)
431 dirent->rec_len = 12;
432 /*
433 * Note: we don't have the parent inode just
434 * yet, so we will fill it in with the root
435 * inode. This will get fixed in pass 3.
436 */
437 dirent->inode = EXT2_ROOT_INO;
438 dirent->name_len = 2;
439 dirent->name[0] = '.';
440 dirent->name[1] = '.';
441 dirent->name[2] = '\0';
442 return 1;
443 }
444 return 0;
445 }
446 dir->dotdot = dirent->inode;
447 return 0;
448}
449
450/*
451 * Check to make sure a directory entry doesn't contain any illegal
452 * characters.
453 */
454static int check_name(e2fsck_t ctx,
455 struct ext2_dir_entry *dirent,
456 ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
457 struct problem_context *pctx)
458{
459 int i;
460 int fixup = -1;
461 int ret = 0;
462
463 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
464 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
465 if (fixup < 0) {
466 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
467 }
468 if (fixup) {
469 dirent->name[i] = '.';
470 ret = 1;
471 }
472 }
473 }
474 return ret;
475}
476
477/*
478 * Check the directory filetype (if present)
479 */
480static _INLINE_ int check_filetype(e2fsck_t ctx,
481 struct ext2_dir_entry *dirent,
482 ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
483 struct problem_context *pctx)
484{
485 int filetype = dirent->name_len >> 8;
486 int should_be = EXT2_FT_UNKNOWN;
487 struct ext2_inode inode;
488
489 if (!(ctx->fs->super->s_feature_incompat &
490 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
491 if (filetype == 0 ||
492 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
493 return 0;
494 dirent->name_len = dirent->name_len & 0xFF;
495 return 1;
496 }
497
498 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
499 should_be = EXT2_FT_DIR;
500 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
501 dirent->inode)) {
502 should_be = EXT2_FT_REG_FILE;
503 } else if (ctx->inode_bad_map &&
504 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
505 dirent->inode))
506 should_be = 0;
507 else {
508 e2fsck_read_inode(ctx, dirent->inode, &inode,
509 "check_filetype");
510 should_be = ext2_file_type(inode.i_mode);
511 }
512 if (filetype == should_be)
513 return 0;
514 pctx->num = should_be;
515
516 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
517 pctx) == 0)
518 return 0;
519
520 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
521 return 1;
522}
523
524#ifdef ENABLE_HTREE
525static void parse_int_node(ext2_filsys fs,
526 struct ext2_db_entry *db,
527 struct check_dir_struct *cd,
528 struct dx_dir_info *dx_dir,
529 char *block_buf)
530{
531 struct ext2_dx_root_info *root;
532 struct ext2_dx_entry *ent;
533 struct ext2_dx_countlimit *limit;
534 struct dx_dirblock_info *dx_db;
535 int i, expect_limit, count;
536 blk_t blk;
537 ext2_dirhash_t min_hash = 0xffffffff;
538 ext2_dirhash_t max_hash = 0;
539 ext2_dirhash_t hash = 0, prev_hash;
540
541 if (db->blockcnt == 0) {
542 root = (struct ext2_dx_root_info *) (block_buf + 24);
543
544#ifdef DX_DEBUG
545 printf("Root node dump:\n");
546 printf("\t Reserved zero: %d\n", root->reserved_zero);
547 printf("\t Hash Version: %d\n", root->hash_version);
548 printf("\t Info length: %d\n", root->info_length);
549 printf("\t Indirect levels: %d\n", root->indirect_levels);
550 printf("\t Flags: %d\n", root->unused_flags);
551#endif
552
553 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
554 } else {
555 ent = (struct ext2_dx_entry *) (block_buf+8);
556 }
557 limit = (struct ext2_dx_countlimit *) ent;
558
559#ifdef DX_DEBUG
560 printf("Number of entries (count): %d\n",
561 ext2fs_le16_to_cpu(limit->count));
562 printf("Number of entries (limit): %d\n",
563 ext2fs_le16_to_cpu(limit->limit));
564#endif
565
566 count = ext2fs_le16_to_cpu(limit->count);
567 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
568 sizeof(struct ext2_dx_entry);
569 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
570 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
571 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
572 goto clear_and_exit;
573 }
574 if (count > expect_limit) {
575 cd->pctx.num = count;
576 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
577 goto clear_and_exit;
578 count = expect_limit;
579 }
580
581 for (i=0; i < count; i++) {
582 prev_hash = hash;
583 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
584#ifdef DX_DEBUG
585 printf("Entry #%d: Hash 0x%08x, block %d\n", i,
586 hash, ext2fs_le32_to_cpu(ent[i].block));
587#endif
588 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
589 /* Check to make sure the block is valid */
590 if (blk > (blk_t) dx_dir->numblocks) {
591 cd->pctx.blk = blk;
592 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
593 &cd->pctx))
594 goto clear_and_exit;
595 }
596 if (hash < prev_hash &&
597 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
598 goto clear_and_exit;
599 dx_db = &dx_dir->dx_block[blk];
600 if (dx_db->flags & DX_FLAG_REFERENCED) {
601 dx_db->flags |= DX_FLAG_DUP_REF;
602 } else {
603 dx_db->flags |= DX_FLAG_REFERENCED;
604 dx_db->parent = db->blockcnt;
605 }
606 if (hash < min_hash)
607 min_hash = hash;
608 if (hash > max_hash)
609 max_hash = hash;
610 dx_db->node_min_hash = hash;
611 if ((i+1) < count)
612 dx_db->node_max_hash =
613 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
614 else {
615 dx_db->node_max_hash = 0xfffffffe;
616 dx_db->flags |= DX_FLAG_LAST;
617 }
618 if (i == 0)
619 dx_db->flags |= DX_FLAG_FIRST;
620 }
621#ifdef DX_DEBUG
622 printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n",
623 db->blockcnt, min_hash, max_hash);
624#endif
625 dx_db = &dx_dir->dx_block[db->blockcnt];
626 dx_db->min_hash = min_hash;
627 dx_db->max_hash = max_hash;
628 return;
629
630clear_and_exit:
631 clear_htree(cd->ctx, cd->pctx.ino);
632 dx_dir->numblocks = 0;
633}
634#endif /* ENABLE_HTREE */
635
636/*
637 * Given a busted directory, try to salvage it somehow.
638 *
639 */
640static void salvage_directory(ext2_filsys fs,
641 struct ext2_dir_entry *dirent,
642 struct ext2_dir_entry *prev,
643 unsigned int *offset)
644{
645 char *cp = (char *) dirent;
646 int left = fs->blocksize - *offset - dirent->rec_len;
647 int name_len = dirent->name_len & 0xFF;
648
649 /*
650 * Special case of directory entry of size 8: copy what's left
651 * of the directory block up to cover up the invalid hole.
652 */
653 if ((left >= 12) && (dirent->rec_len == 8)) {
654 memmove(cp, cp+8, left);
655 memset(cp + left, 0, 8);
656 return;
657 }
658 /*
659 * If the directory entry overruns the end of the directory
660 * block, and the name is small enough to fit, then adjust the
661 * record length.
662 */
663 if ((left < 0) &&
664 (name_len + 8 <= dirent->rec_len + left) &&
665 dirent->inode <= fs->super->s_inodes_count &&
666 strnlen(dirent->name, name_len) == name_len) {
667 dirent->rec_len += left;
668 return;
669 }
670 /*
671 * If the directory entry is a multiple of four, so it is
672 * valid, let the previous directory entry absorb the invalid
673 * one.
674 */
675 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
676 prev->rec_len += dirent->rec_len;
677 *offset += dirent->rec_len;
678 return;
679 }
680 /*
681 * Default salvage method --- kill all of the directory
682 * entries for the rest of the block. We will either try to
683 * absorb it into the previous directory entry, or create a
684 * new empty directory entry the rest of the directory block.
685 */
686 if (prev) {
687 prev->rec_len += fs->blocksize - *offset;
688 *offset = fs->blocksize;
689 } else {
690 dirent->rec_len = fs->blocksize - *offset;
691 dirent->name_len = 0;
692 dirent->inode = 0;
693 }
694}
695
696static int check_dir_block(ext2_filsys fs,
697 struct ext2_db_entry *db,
698 void *priv_data)
699{
700 struct dir_info *subdir, *dir;
701 struct dx_dir_info *dx_dir;
702#ifdef ENABLE_HTREE
703 struct dx_dirblock_info *dx_db = 0;
704#endif /* ENABLE_HTREE */
705 struct ext2_dir_entry *dirent, *prev;
706 ext2_dirhash_t hash;
707 unsigned int offset = 0;
708 int dir_modified = 0;
709 int dot_state;
710 blk_t block_nr = db->blk;
711 ext2_ino_t ino = db->ino;
712 __u16 links;
713 struct check_dir_struct *cd;
714 char *buf;
715 e2fsck_t ctx;
716 int problem;
717 struct ext2_dx_root_info *root;
718 struct ext2_dx_countlimit *limit;
719 static dict_t de_dict;
720 struct problem_context pctx;
721 int dups_found = 0;
722
723 cd = (struct check_dir_struct *) priv_data;
724 buf = cd->buf;
725 ctx = cd->ctx;
726
727 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
728 return DIRENT_ABORT;
729
730 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
731 return DIRENT_ABORT;
732
733 /*
734 * Make sure the inode is still in use (could have been
735 * deleted in the duplicate/bad blocks pass.
736 */
737 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
738 return 0;
739
740 cd->pctx.ino = ino;
741 cd->pctx.blk = block_nr;
742 cd->pctx.blkcount = db->blockcnt;
743 cd->pctx.ino2 = 0;
744 cd->pctx.dirent = 0;
745 cd->pctx.num = 0;
746
747 if (db->blk == 0) {
748 if (allocate_dir_block(ctx, db, buf, &cd->pctx))
749 return 0;
750 block_nr = db->blk;
751 }
752
753 if (db->blockcnt)
754 dot_state = 2;
755 else
756 dot_state = 0;
757
758 if (ctx->dirs_to_hash &&
759 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
760 dups_found++;
761
762#if 0
763 printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
764 db->blockcnt, ino);
765#endif
766
767 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
768 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
769 cd->pctx.errcode = 0; /* We'll handle this ourselves */
770 if (cd->pctx.errcode) {
771 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
772 ctx->flags |= E2F_FLAG_ABORT;
773 return DIRENT_ABORT;
774 }
775 memset(buf, 0, fs->blocksize);
776 }
777#ifdef ENABLE_HTREE
778 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
779 if (dx_dir && dx_dir->numblocks) {
780 if (db->blockcnt >= dx_dir->numblocks) {
781 printf("XXX should never happen!!!\n");
782 abort();
783 }
784 dx_db = &dx_dir->dx_block[db->blockcnt];
785 dx_db->type = DX_DIRBLOCK_LEAF;
786 dx_db->phys = block_nr;
787 dx_db->min_hash = ~0;
788 dx_db->max_hash = 0;
789
790 dirent = (struct ext2_dir_entry *) buf;
791 limit = (struct ext2_dx_countlimit *) (buf+8);
792 if (db->blockcnt == 0) {
793 root = (struct ext2_dx_root_info *) (buf + 24);
794 dx_db->type = DX_DIRBLOCK_ROOT;
795 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
796 if ((root->reserved_zero ||
797 root->info_length < 8 ||
798 root->indirect_levels > 1) &&
799 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
800 clear_htree(ctx, ino);
801 dx_dir->numblocks = 0;
802 dx_db = 0;
803 }
804 dx_dir->hashversion = root->hash_version;
805 dx_dir->depth = root->indirect_levels + 1;
806 } else if ((dirent->inode == 0) &&
807 (dirent->rec_len == fs->blocksize) &&
808 (dirent->name_len == 0) &&
809 (ext2fs_le16_to_cpu(limit->limit) ==
810 ((fs->blocksize-8) /
811 sizeof(struct ext2_dx_entry))))
812 dx_db->type = DX_DIRBLOCK_NODE;
813 }
814#endif /* ENABLE_HTREE */
815
816 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
817 prev = 0;
818 do {
819 problem = 0;
820 dirent = (struct ext2_dir_entry *) (buf + offset);
821 cd->pctx.dirent = dirent;
822 cd->pctx.num = offset;
823 if (((offset + dirent->rec_len) > fs->blocksize) ||
824 (dirent->rec_len < 12) ||
825 ((dirent->rec_len % 4) != 0) ||
826 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
827 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
828 salvage_directory(fs, dirent, prev, &offset);
829 dir_modified++;
830 continue;
831 } else
832 goto abort_free_dict;
833 }
834 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
835 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
836 dirent->name_len = EXT2_NAME_LEN;
837 dir_modified++;
838 }
839 }
840
841 if (dot_state == 0) {
842 if (check_dot(ctx, dirent, ino, &cd->pctx))
843 dir_modified++;
844 } else if (dot_state == 1) {
845 dir = e2fsck_get_dir_info(ctx, ino);
846 if (!dir) {
847 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
848 goto abort_free_dict;
849 }
850 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
851 dir_modified++;
852 } else if (dirent->inode == ino) {
853 problem = PR_2_LINK_DOT;
854 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
855 dirent->inode = 0;
856 dir_modified++;
857 goto next;
858 }
859 }
860 if (!dirent->inode)
861 goto next;
862
863 /*
864 * Make sure the inode listed is a legal one.
865 */
866 if (((dirent->inode != EXT2_ROOT_INO) &&
867 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
868 (dirent->inode > fs->super->s_inodes_count)) {
869 problem = PR_2_BAD_INO;
870 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
871 dirent->inode))) {
872 /*
873 * If the inode is unused, offer to clear it.
874 */
875 problem = PR_2_UNUSED_INODE;
876 } else if (ctx->inode_bb_map &&
877 (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
878 dirent->inode))) {
879 /*
880 * If the inode is in a bad block, offer to
881 * clear it.
882 */
883 problem = PR_2_BB_INODE;
884 } else if ((dot_state > 1) &&
885 ((dirent->name_len & 0xFF) == 1) &&
886 (dirent->name[0] == '.')) {
887 /*
888 * If there's a '.' entry in anything other
889 * than the first directory entry, it's a
890 * duplicate entry that should be removed.
891 */
892 problem = PR_2_DUP_DOT;
893 } else if ((dot_state > 1) &&
894 ((dirent->name_len & 0xFF) == 2) &&
895 (dirent->name[0] == '.') &&
896 (dirent->name[1] == '.')) {
897 /*
898 * If there's a '..' entry in anything other
899 * than the second directory entry, it's a
900 * duplicate entry that should be removed.
901 */
902 problem = PR_2_DUP_DOT_DOT;
903 } else if ((dot_state > 1) &&
904 (dirent->inode == EXT2_ROOT_INO)) {
905 /*
906 * Don't allow links to the root directory.
907 * We check this specially to make sure we
908 * catch this error case even if the root
909 * directory hasn't been created yet.
910 */
911 problem = PR_2_LINK_ROOT;
912 } else if ((dot_state > 1) &&
913 (dirent->name_len & 0xFF) == 0) {
914 /*
915 * Don't allow zero-length directory names.
916 */
917 problem = PR_2_NULL_NAME;
918 }
919
920 if (problem) {
921 if (fix_problem(ctx, problem, &cd->pctx)) {
922 dirent->inode = 0;
923 dir_modified++;
924 goto next;
925 } else {
926 ext2fs_unmark_valid(fs);
927 if (problem == PR_2_BAD_INO)
928 goto next;
929 }
930 }
931
932 /*
933 * If the inode was marked as having bad fields in
934 * pass1, process it and offer to fix/clear it.
935 * (We wait until now so that we can display the
936 * pathname to the user.)
937 */
938 if (ctx->inode_bad_map &&
939 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
940 dirent->inode)) {
941 if (e2fsck_process_bad_inode(ctx, ino,
942 dirent->inode,
943 buf + fs->blocksize)) {
944 dirent->inode = 0;
945 dir_modified++;
946 goto next;
947 }
948 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
949 return DIRENT_ABORT;
950 }
951
952 if (check_name(ctx, dirent, ino, &cd->pctx))
953 dir_modified++;
954
955 if (check_filetype(ctx, dirent, ino, &cd->pctx))
956 dir_modified++;
957
958#ifdef ENABLE_HTREE
959 if (dx_db) {
960 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
961 (dirent->name_len & 0xFF),
962 fs->super->s_hash_seed, &hash, 0);
963 if (hash < dx_db->min_hash)
964 dx_db->min_hash = hash;
965 if (hash > dx_db->max_hash)
966 dx_db->max_hash = hash;
967 }
968#endif
969
970 /*
971 * If this is a directory, then mark its parent in its
972 * dir_info structure. If the parent field is already
973 * filled in, then this directory has more than one
974 * hard link. We assume the first link is correct,
975 * and ask the user if he/she wants to clear this one.
976 */
977 if ((dot_state > 1) &&
978 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
979 dirent->inode))) {
980 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
981 if (!subdir) {
982 cd->pctx.ino = dirent->inode;
983 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
984 goto abort_free_dict;
985 }
986 if (subdir->parent) {
987 cd->pctx.ino2 = subdir->parent;
988 if (fix_problem(ctx, PR_2_LINK_DIR,
989 &cd->pctx)) {
990 dirent->inode = 0;
991 dir_modified++;
992 goto next;
993 }
994 cd->pctx.ino2 = 0;
995 } else
996 subdir->parent = ino;
997 }
998
999 if (dups_found) {
1000 ;
1001 } else if (dict_lookup(&de_dict, dirent)) {
1002 clear_problem_context(&pctx);
1003 pctx.ino = ino;
1004 pctx.dirent = dirent;
1005 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
1006 if (!ctx->dirs_to_hash)
1007 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
1008 if (ctx->dirs_to_hash)
1009 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
1010 dups_found++;
1011 } else
1012 dict_alloc_insert(&de_dict, dirent, dirent);
1013
1014 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
1015 &links);
1016 if (links > 1)
1017 ctx->fs_links_count++;
1018 ctx->fs_total_count++;
1019 next:
1020 prev = dirent;
1021 offset += dirent->rec_len;
1022 dot_state++;
1023 } while (offset < fs->blocksize);
1024#if 0
1025 printf("\n");
1026#endif
1027#ifdef ENABLE_HTREE
1028 if (dx_db) {
1029#ifdef DX_DEBUG
1030 printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
1031 db->blockcnt, dx_db->type,
1032 dx_db->min_hash, dx_db->max_hash);
1033#endif
1034 cd->pctx.dir = cd->pctx.ino;
1035 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
1036 (dx_db->type == DX_DIRBLOCK_NODE))
1037 parse_int_node(fs, db, cd, dx_dir, buf);
1038 }
1039#endif /* ENABLE_HTREE */
1040 if (offset != fs->blocksize) {
1041 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
1042 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
1043 dirent->rec_len = cd->pctx.num;
1044 dir_modified++;
1045 }
1046 }
1047 if (dir_modified) {
1048 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
1049 if (cd->pctx.errcode) {
1050 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
1051 &cd->pctx))
1052 goto abort_free_dict;
1053 }
1054 ext2fs_mark_changed(fs);
1055 }
1056 dict_free_nodes(&de_dict);
1057 return 0;
1058abort_free_dict:
1059 dict_free_nodes(&de_dict);
1060 ctx->flags |= E2F_FLAG_ABORT;
1061 return DIRENT_ABORT;
1062}
1063
1064/*
1065 * This function is called to deallocate a block, and is an interator
1066 * functioned called by deallocate inode via ext2fs_iterate_block().
1067 */
1068static int deallocate_inode_block(ext2_filsys fs,
1069 blk_t *block_nr,
1070 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
1071 blk_t ref_block EXT2FS_ATTR((unused)),
1072 int ref_offset EXT2FS_ATTR((unused)),
1073 void *priv_data)
1074{
1075 e2fsck_t ctx = (e2fsck_t) priv_data;
1076
1077 if (HOLE_BLKADDR(*block_nr))
1078 return 0;
1079 if ((*block_nr < fs->super->s_first_data_block) ||
1080 (*block_nr >= fs->super->s_blocks_count))
1081 return 0;
1082 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
1083 ext2fs_block_alloc_stats(fs, *block_nr, -1);
1084 return 0;
1085}
1086
1087/*
1088 * This fuction deallocates an inode
1089 */
1090static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
1091{
1092 ext2_filsys fs = ctx->fs;
1093 struct ext2_inode inode;
1094 struct problem_context pctx;
1095 __u32 count;
1096
1097 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
1098 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
1099 inode.i_links_count = 0;
1100 inode.i_dtime = time(0);
1101 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
1102 clear_problem_context(&pctx);
1103 pctx.ino = ino;
1104
1105 /*
1106 * Fix up the bitmaps...
1107 */
1108 e2fsck_read_bitmaps(ctx);
1109 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
1110 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
1111 if (ctx->inode_bad_map)
1112 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
1113 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
1114
1115 if (inode.i_file_acl &&
1116 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
1117 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
1118 block_buf, -1, &count);
1119 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
1120 pctx.errcode = 0;
1121 count = 1;
1122 }
1123 if (pctx.errcode) {
1124 pctx.blk = inode.i_file_acl;
1125 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
1126 ctx->flags |= E2F_FLAG_ABORT;
1127 return;
1128 }
1129 if (count == 0) {
1130 ext2fs_unmark_block_bitmap(ctx->block_found_map,
1131 inode.i_file_acl);
1132 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
1133 }
1134 inode.i_file_acl = 0;
1135 }
1136
1137 if (!ext2fs_inode_has_valid_blocks(&inode))
1138 return;
1139
1140 if (LINUX_S_ISREG(inode.i_mode) &&
1141 (inode.i_size_high || inode.i_size & 0x80000000UL))
1142 ctx->large_files--;
1143
1144 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
1145 deallocate_inode_block, ctx);
1146 if (pctx.errcode) {
1147 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
1148 ctx->flags |= E2F_FLAG_ABORT;
1149 return;
1150 }
1151}
1152
1153/*
1154 * This fuction clears the htree flag on an inode
1155 */
1156static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
1157{
1158 struct ext2_inode inode;
1159
1160 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
1161 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
1162 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
1163 if (ctx->dirs_to_hash)
1164 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
1165}
1166
1167
1168extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
1169 ext2_ino_t ino, char *buf)
1170{
1171 ext2_filsys fs = ctx->fs;
1172 struct ext2_inode inode;
1173 int inode_modified = 0;
1174 int not_fixed = 0;
1175 unsigned char *frag, *fsize;
1176 struct problem_context pctx;
1177 int problem = 0;
1178
1179 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
1180
1181 clear_problem_context(&pctx);
1182 pctx.ino = ino;
1183 pctx.dir = dir;
1184 pctx.inode = &inode;
1185
1186 if (inode.i_file_acl &&
1187 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
1188 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
1189 inode.i_file_acl = 0;
1190#ifdef EXT2FS_ENABLE_SWAPFS
1191 /*
1192 * This is a special kludge to deal with long symlinks
1193 * on big endian systems. i_blocks had already been
1194 * decremented earlier in pass 1, but since i_file_acl
1195 * hadn't yet been cleared, ext2fs_read_inode()
1196 * assumed that the file was short symlink and would
1197 * not have byte swapped i_block[0]. Hence, we have
1198 * to byte-swap it here.
1199 */
1200 if (LINUX_S_ISLNK(inode.i_mode) &&
1201 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
1202 (inode.i_blocks == fs->blocksize >> 9))
1203 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
1204#endif
1205 inode_modified++;
1206 } else
1207 not_fixed++;
1208
1209 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
1210 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
1211 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
1212 !(LINUX_S_ISSOCK(inode.i_mode)))
1213 problem = PR_2_BAD_MODE;
1214 else if (LINUX_S_ISCHR(inode.i_mode)
1215 && !e2fsck_pass1_check_device_inode(fs, &inode))
1216 problem = PR_2_BAD_CHAR_DEV;
1217 else if (LINUX_S_ISBLK(inode.i_mode)
1218 && !e2fsck_pass1_check_device_inode(fs, &inode))
1219 problem = PR_2_BAD_BLOCK_DEV;
1220 else if (LINUX_S_ISFIFO(inode.i_mode)
1221 && !e2fsck_pass1_check_device_inode(fs, &inode))
1222 problem = PR_2_BAD_FIFO;
1223 else if (LINUX_S_ISSOCK(inode.i_mode)
1224 && !e2fsck_pass1_check_device_inode(fs, &inode))
1225 problem = PR_2_BAD_SOCKET;
1226 else if (LINUX_S_ISLNK(inode.i_mode)
1227 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
1228 problem = PR_2_INVALID_SYMLINK;
1229 }
1230
1231 if (problem) {
1232 if (fix_problem(ctx, problem, &pctx)) {
1233 deallocate_inode(ctx, ino, 0);
1234 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
1235 return 0;
1236 return 1;
1237 } else
1238 not_fixed++;
1239 problem = 0;
1240 }
1241
1242 if (inode.i_faddr) {
1243 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
1244 inode.i_faddr = 0;
1245 inode_modified++;
1246 } else
1247 not_fixed++;
1248 }
1249
1250 switch (fs->super->s_creator_os) {
1251 case EXT2_OS_LINUX:
1252 frag = &inode.osd2.linux2.l_i_frag;
1253 fsize = &inode.osd2.linux2.l_i_fsize;
1254 break;
1255 case EXT2_OS_HURD:
1256 frag = &inode.osd2.hurd2.h_i_frag;
1257 fsize = &inode.osd2.hurd2.h_i_fsize;
1258 break;
1259 case EXT2_OS_MASIX:
1260 frag = &inode.osd2.masix2.m_i_frag;
1261 fsize = &inode.osd2.masix2.m_i_fsize;
1262 break;
1263 default:
1264 frag = fsize = 0;
1265 }
1266 if (frag && *frag) {
1267 pctx.num = *frag;
1268 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
1269 *frag = 0;
1270 inode_modified++;
1271 } else
1272 not_fixed++;
1273 pctx.num = 0;
1274 }
1275 if (fsize && *fsize) {
1276 pctx.num = *fsize;
1277 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
1278 *fsize = 0;
1279 inode_modified++;
1280 } else
1281 not_fixed++;
1282 pctx.num = 0;
1283 }
1284
1285 if (inode.i_file_acl &&
1286 ((inode.i_file_acl < fs->super->s_first_data_block) ||
1287 (inode.i_file_acl >= fs->super->s_blocks_count))) {
1288 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
1289 inode.i_file_acl = 0;
1290 inode_modified++;
1291 } else
1292 not_fixed++;
1293 }
1294 if (inode.i_dir_acl &&
1295 LINUX_S_ISDIR(inode.i_mode)) {
1296 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
1297 inode.i_dir_acl = 0;
1298 inode_modified++;
1299 } else
1300 not_fixed++;
1301 }
1302
1303 if (inode_modified)
1304 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
1305 if (!not_fixed)
1306 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
1307 return 0;
1308}
1309
1310
1311/*
1312 * allocate_dir_block --- this function allocates a new directory
1313 * block for a particular inode; this is done if a directory has
1314 * a "hole" in it, or if a directory has a illegal block number
1315 * that was zeroed out and now needs to be replaced.
1316 */
1317static int allocate_dir_block(e2fsck_t ctx,
1318 struct ext2_db_entry *db,
1319 char *buf EXT2FS_ATTR((unused)),
1320 struct problem_context *pctx)
1321{
1322 ext2_filsys fs = ctx->fs;
1323 blk_t blk;
1324 char *block;
1325 struct ext2_inode inode;
1326
1327 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
1328 return 1;
1329
1330 /*
1331 * Read the inode and block bitmaps in; we'll be messing with
1332 * them.
1333 */
1334 e2fsck_read_bitmaps(ctx);
1335
1336 /*
1337 * First, find a free block
1338 */
1339 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
1340 if (pctx->errcode) {
1341 pctx->str = "ext2fs_new_block";
1342 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1343 return 1;
1344 }
1345 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
1346 ext2fs_mark_block_bitmap(fs->block_map, blk);
1347 ext2fs_mark_bb_dirty(fs);
1348
1349 /*
1350 * Now let's create the actual data block for the inode
1351 */
1352 if (db->blockcnt)
1353 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
1354 else
1355 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
1356 EXT2_ROOT_INO, &block);
1357
1358 if (pctx->errcode) {
1359 pctx->str = "ext2fs_new_dir_block";
1360 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1361 return 1;
1362 }
1363
1364 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
1365 ext2fs_free_mem(&block);
1366 if (pctx->errcode) {
1367 pctx->str = "ext2fs_write_dir_block";
1368 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1369 return 1;
1370 }
1371
1372 /*
1373 * Update the inode block count
1374 */
1375 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
1376 inode.i_blocks += fs->blocksize / 512;
1377 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
1378 inode.i_size = (db->blockcnt+1) * fs->blocksize;
1379 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
1380
1381 /*
1382 * Finally, update the block pointers for the inode
1383 */
1384 db->blk = blk;
1385 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
1386 0, update_dir_block, db);
1387 if (pctx->errcode) {
1388 pctx->str = "ext2fs_block_iterate";
1389 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1390 return 1;
1391 }
1392
1393 return 0;
1394}
1395
1396/*
1397 * This is a helper function for allocate_dir_block().
1398 */
1399static int update_dir_block(ext2_filsys fs EXT2FS_ATTR((unused)),
1400 blk_t *block_nr,
1401 e2_blkcnt_t blockcnt,
1402 blk_t ref_block EXT2FS_ATTR((unused)),
1403 int ref_offset EXT2FS_ATTR((unused)),
1404 void *priv_data)
1405{
1406 struct ext2_db_entry *db;
1407
1408 db = (struct ext2_db_entry *) priv_data;
1409 if (db->blockcnt == (int) blockcnt) {
1410 *block_nr = db->blk;
1411 return BLOCK_CHANGED;
1412 }
1413 return 0;
1414}
diff --git a/e2fsprogs/e2fsck/pass3.c b/e2fsprogs/e2fsck/pass3.c
new file mode 100644
index 000000000..a92c8904c
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass3.c
@@ -0,0 +1,804 @@
1/*
2 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * Pass #3 assures that all directories are connected to the
12 * filesystem tree, using the following algorithm:
13 *
14 * First, the root directory is checked to make sure it exists; if
15 * not, e2fsck will offer to create a new one. It is then marked as
16 * "done".
17 *
18 * Then, pass3 interates over all directory inodes; for each directory
19 * it attempts to trace up the filesystem tree, using dirinfo.parent
20 * until it reaches a directory which has been marked "done". If it
21 * can not do so, then the directory must be disconnected, and e2fsck
22 * will offer to reconnect it to /lost+found. While it is chasing
23 * parent pointers up the filesystem tree, if pass3 sees a directory
24 * twice, then it has detected a filesystem loop, and it will again
25 * offer to reconnect the directory to /lost+found in to break the
26 * filesystem loop.
27 *
28 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
29 * reconnect inodes to /lost+found; this subroutine is also used by
30 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
31 * is responsible for creating /lost+found if it does not exist.
32 *
33 * Pass 3 frees the following data structures:
34 * - The dirinfo directory information cache.
35 */
36
37#ifdef HAVE_ERRNO_H
38#include <errno.h>
39#endif
40
41#include "e2fsck.h"
42#include "problem.h"
43
44static void check_root(e2fsck_t ctx);
45static int check_directory(e2fsck_t ctx, struct dir_info *dir,
46 struct problem_context *pctx);
47static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
48
49static ext2fs_inode_bitmap inode_loop_detect = 0;
50static ext2fs_inode_bitmap inode_done_map = 0;
51
52void e2fsck_pass3(e2fsck_t ctx)
53{
54 ext2_filsys fs = ctx->fs;
55 int i;
56#ifdef RESOURCE_TRACK
57 struct resource_track rtrack;
58#endif
59 struct problem_context pctx;
60 struct dir_info *dir;
61 unsigned long maxdirs, count;
62
63#ifdef RESOURCE_TRACK
64 init_resource_track(&rtrack);
65#endif
66
67 clear_problem_context(&pctx);
68
69#ifdef MTRACE
70 mtrace_print("Pass 3");
71#endif
72
73 if (!(ctx->options & E2F_OPT_PREEN))
74 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
75
76 /*
77 * Allocate some bitmaps to do loop detection.
78 */
79 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
80 &inode_done_map);
81 if (pctx.errcode) {
82 pctx.num = 2;
83 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
84 ctx->flags |= E2F_FLAG_ABORT;
85 goto abort_exit;
86 }
87#ifdef RESOURCE_TRACK
88 if (ctx->options & E2F_OPT_TIME) {
89 e2fsck_clear_progbar(ctx);
90 print_resource_track(_("Peak memory"), &ctx->global_rtrack);
91 }
92#endif
93
94 check_root(ctx);
95 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
96 goto abort_exit;
97
98 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
99
100 maxdirs = e2fsck_get_num_dirinfo(ctx);
101 count = 1;
102
103 if (ctx->progress)
104 if ((ctx->progress)(ctx, 3, 0, maxdirs))
105 goto abort_exit;
106
107 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
108 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
109 goto abort_exit;
110 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
111 goto abort_exit;
112 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
113 if (check_directory(ctx, dir, &pctx))
114 goto abort_exit;
115 }
116
117 /*
118 * Force the creation of /lost+found if not present
119 */
120 if ((ctx->flags & E2F_OPT_READONLY) == 0)
121 e2fsck_get_lost_and_found(ctx, 1);
122
123 /*
124 * If there are any directories that need to be indexed or
125 * optimized, do it here.
126 */
127 e2fsck_rehash_directories(ctx);
128
129abort_exit:
130 e2fsck_free_dir_info(ctx);
131 if (inode_loop_detect) {
132 ext2fs_free_inode_bitmap(inode_loop_detect);
133 inode_loop_detect = 0;
134 }
135 if (inode_done_map) {
136 ext2fs_free_inode_bitmap(inode_done_map);
137 inode_done_map = 0;
138 }
139
140#ifdef RESOURCE_TRACK
141 if (ctx->options & E2F_OPT_TIME2) {
142 e2fsck_clear_progbar(ctx);
143 print_resource_track(_("Pass 3"), &rtrack);
144 }
145#endif
146}
147
148/*
149 * This makes sure the root inode is present; if not, we ask if the
150 * user wants us to create it. Not creating it is a fatal error.
151 */
152static void check_root(e2fsck_t ctx)
153{
154 ext2_filsys fs = ctx->fs;
155 blk_t blk;
156 struct ext2_inode inode;
157 char * block;
158 struct problem_context pctx;
159
160 clear_problem_context(&pctx);
161
162 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
163 /*
164 * If the root inode is not a directory, die here. The
165 * user must have answered 'no' in pass1 when we
166 * offered to clear it.
167 */
168 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
169 EXT2_ROOT_INO))) {
170 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
171 ctx->flags |= E2F_FLAG_ABORT;
172 }
173 return;
174 }
175
176 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
177 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
178 ctx->flags |= E2F_FLAG_ABORT;
179 return;
180 }
181
182 e2fsck_read_bitmaps(ctx);
183
184 /*
185 * First, find a free block
186 */
187 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
188 if (pctx.errcode) {
189 pctx.str = "ext2fs_new_block";
190 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
191 ctx->flags |= E2F_FLAG_ABORT;
192 return;
193 }
194 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
195 ext2fs_mark_block_bitmap(fs->block_map, blk);
196 ext2fs_mark_bb_dirty(fs);
197
198 /*
199 * Now let's create the actual data block for the inode
200 */
201 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
202 &block);
203 if (pctx.errcode) {
204 pctx.str = "ext2fs_new_dir_block";
205 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
206 ctx->flags |= E2F_FLAG_ABORT;
207 return;
208 }
209
210 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
211 if (pctx.errcode) {
212 pctx.str = "ext2fs_write_dir_block";
213 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
214 ctx->flags |= E2F_FLAG_ABORT;
215 return;
216 }
217 ext2fs_free_mem(&block);
218
219 /*
220 * Set up the inode structure
221 */
222 memset(&inode, 0, sizeof(inode));
223 inode.i_mode = 040755;
224 inode.i_size = fs->blocksize;
225 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
226 inode.i_links_count = 2;
227 inode.i_blocks = fs->blocksize / 512;
228 inode.i_block[0] = blk;
229
230 /*
231 * Write out the inode.
232 */
233 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
234 if (pctx.errcode) {
235 pctx.str = "ext2fs_write_inode";
236 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
237 ctx->flags |= E2F_FLAG_ABORT;
238 return;
239 }
240
241 /*
242 * Miscellaneous bookkeeping...
243 */
244 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
245 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
246 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
247
248 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
249 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
250 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
251 ext2fs_mark_ib_dirty(fs);
252}
253
254/*
255 * This subroutine is responsible for making sure that a particular
256 * directory is connected to the root; if it isn't we trace it up as
257 * far as we can go, and then offer to connect the resulting parent to
258 * the lost+found. We have to do loop detection; if we ever discover
259 * a loop, we treat that as a disconnected directory and offer to
260 * reparent it to lost+found.
261 *
262 * However, loop detection is expensive, because for very large
263 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
264 * is non-trivial. Loops in filesystems are also a rare error case,
265 * and we shouldn't optimize for error cases. So we try two passes of
266 * the algorithm. The first time, we ignore loop detection and merely
267 * increment a counter; if the counter exceeds some extreme threshold,
268 * then we try again with the loop detection bitmap enabled.
269 */
270static int check_directory(e2fsck_t ctx, struct dir_info *dir,
271 struct problem_context *pctx)
272{
273 ext2_filsys fs = ctx->fs;
274 struct dir_info *p = dir;
275 int loop_pass = 0, parent_count = 0;
276
277 if (!p)
278 return 0;
279
280 while (1) {
281 /*
282 * Mark this inode as being "done"; by the time we
283 * return from this function, the inode we either be
284 * verified as being connected to the directory tree,
285 * or we will have offered to reconnect this to
286 * lost+found.
287 *
288 * If it was marked done already, then we've reached a
289 * parent we've already checked.
290 */
291 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
292 break;
293
294 /*
295 * If this directory doesn't have a parent, or we've
296 * seen the parent once already, then offer to
297 * reparent it to lost+found
298 */
299 if (!p->parent ||
300 (loop_pass &&
301 (ext2fs_test_inode_bitmap(inode_loop_detect,
302 p->parent)))) {
303 pctx->ino = p->ino;
304 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
305 if (e2fsck_reconnect_file(ctx, pctx->ino))
306 ext2fs_unmark_valid(fs);
307 else {
308 p = e2fsck_get_dir_info(ctx, pctx->ino);
309 p->parent = ctx->lost_and_found;
310 fix_dotdot(ctx, p, ctx->lost_and_found);
311 }
312 }
313 break;
314 }
315 p = e2fsck_get_dir_info(ctx, p->parent);
316 if (!p) {
317 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
318 return 0;
319 }
320 if (loop_pass) {
321 ext2fs_mark_inode_bitmap(inode_loop_detect,
322 p->ino);
323 } else if (parent_count++ > 2048) {
324 /*
325 * If we've run into a path depth that's
326 * greater than 2048, try again with the inode
327 * loop bitmap turned on and start from the
328 * top.
329 */
330 loop_pass = 1;
331 if (inode_loop_detect)
332 ext2fs_clear_inode_bitmap(inode_loop_detect);
333 else {
334 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
335 if (pctx->errcode) {
336 pctx->num = 1;
337 fix_problem(ctx,
338 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
339 ctx->flags |= E2F_FLAG_ABORT;
340 return -1;
341 }
342 }
343 p = dir;
344 }
345 }
346
347 /*
348 * Make sure that .. and the parent directory are the same;
349 * offer to fix it if not.
350 */
351 if (dir->parent != dir->dotdot) {
352 pctx->ino = dir->ino;
353 pctx->ino2 = dir->dotdot;
354 pctx->dir = dir->parent;
355 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
356 fix_dotdot(ctx, dir, dir->parent);
357 }
358 return 0;
359}
360
361/*
362 * This routine gets the lost_and_found inode, making it a directory
363 * if necessary
364 */
365ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
366{
367 ext2_filsys fs = ctx->fs;
368 ext2_ino_t ino;
369 blk_t blk;
370 errcode_t retval;
371 struct ext2_inode inode;
372 char * block;
373 static const char name[] = "lost+found";
374 struct problem_context pctx;
375 struct dir_info *dirinfo;
376
377 if (ctx->lost_and_found)
378 return ctx->lost_and_found;
379
380 clear_problem_context(&pctx);
381
382 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
383 sizeof(name)-1, 0, &ino);
384 if (retval && !fix)
385 return 0;
386 if (!retval) {
387 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
388 ctx->lost_and_found = ino;
389 return ino;
390 }
391
392 /* Lost+found isn't a directory! */
393 if (!fix)
394 return 0;
395 pctx.ino = ino;
396 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
397 return 0;
398
399 /* OK, unlink the old /lost+found file. */
400 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
401 if (pctx.errcode) {
402 pctx.str = "ext2fs_unlink";
403 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
404 return 0;
405 }
406 dirinfo = e2fsck_get_dir_info(ctx, ino);
407 if (dirinfo)
408 dirinfo->parent = 0;
409 e2fsck_adjust_inode_count(ctx, ino, -1);
410 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
411 pctx.errcode = retval;
412 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
413 }
414 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
415 return 0;
416
417 /*
418 * Read the inode and block bitmaps in; we'll be messing with
419 * them.
420 */
421 e2fsck_read_bitmaps(ctx);
422
423 /*
424 * First, find a free block
425 */
426 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
427 if (retval) {
428 pctx.errcode = retval;
429 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
430 return 0;
431 }
432 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
433 ext2fs_block_alloc_stats(fs, blk, +1);
434
435 /*
436 * Next find a free inode.
437 */
438 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
439 ctx->inode_used_map, &ino);
440 if (retval) {
441 pctx.errcode = retval;
442 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
443 return 0;
444 }
445 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
446 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
447 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
448
449 /*
450 * Now let's create the actual data block for the inode
451 */
452 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
453 if (retval) {
454 pctx.errcode = retval;
455 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
456 return 0;
457 }
458
459 retval = ext2fs_write_dir_block(fs, blk, block);
460 ext2fs_free_mem(&block);
461 if (retval) {
462 pctx.errcode = retval;
463 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
464 return 0;
465 }
466
467 /*
468 * Set up the inode structure
469 */
470 memset(&inode, 0, sizeof(inode));
471 inode.i_mode = 040700;
472 inode.i_size = fs->blocksize;
473 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
474 inode.i_links_count = 2;
475 inode.i_blocks = fs->blocksize / 512;
476 inode.i_block[0] = blk;
477
478 /*
479 * Next, write out the inode.
480 */
481 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
482 if (pctx.errcode) {
483 pctx.str = "ext2fs_write_inode";
484 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
485 return 0;
486 }
487 /*
488 * Finally, create the directory link
489 */
490 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
491 if (pctx.errcode) {
492 pctx.str = "ext2fs_link";
493 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
494 return 0;
495 }
496
497 /*
498 * Miscellaneous bookkeeping that needs to be kept straight.
499 */
500 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
501 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
502 ext2fs_icount_store(ctx->inode_count, ino, 2);
503 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
504 ctx->lost_and_found = ino;
505#if 0
506 printf("/lost+found created; inode #%lu\n", ino);
507#endif
508 return ino;
509}
510
511/*
512 * This routine will connect a file to lost+found
513 */
514int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
515{
516 ext2_filsys fs = ctx->fs;
517 errcode_t retval;
518 char name[80];
519 struct problem_context pctx;
520 struct ext2_inode inode;
521 int file_type = 0;
522
523 clear_problem_context(&pctx);
524 pctx.ino = ino;
525
526 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
527 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
528 ctx->bad_lost_and_found++;
529 }
530 if (ctx->bad_lost_and_found) {
531 fix_problem(ctx, PR_3_NO_LPF, &pctx);
532 return 1;
533 }
534
535 sprintf(name, "#%u", ino);
536 if (ext2fs_read_inode(fs, ino, &inode) == 0)
537 file_type = ext2_file_type(inode.i_mode);
538 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
539 if (retval == EXT2_ET_DIR_NO_SPACE) {
540 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
541 return 1;
542 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
543 1, 0);
544 if (retval) {
545 pctx.errcode = retval;
546 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
547 return 1;
548 }
549 retval = ext2fs_link(fs, ctx->lost_and_found, name,
550 ino, file_type);
551 }
552 if (retval) {
553 pctx.errcode = retval;
554 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
555 return 1;
556 }
557 e2fsck_adjust_inode_count(ctx, ino, 1);
558
559 return 0;
560}
561
562/*
563 * Utility routine to adjust the inode counts on an inode.
564 */
565errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
566{
567 ext2_filsys fs = ctx->fs;
568 errcode_t retval;
569 struct ext2_inode inode;
570
571 if (!ino)
572 return 0;
573
574 retval = ext2fs_read_inode(fs, ino, &inode);
575 if (retval)
576 return retval;
577
578#if 0
579 printf("Adjusting link count for inode %lu by %d (from %d)\n", ino, adj,
580 inode.i_links_count);
581#endif
582
583 if (adj == 1) {
584 ext2fs_icount_increment(ctx->inode_count, ino, 0);
585 if (inode.i_links_count == (__u16) ~0)
586 return 0;
587 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
588 inode.i_links_count++;
589 } else if (adj == -1) {
590 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
591 if (inode.i_links_count == 0)
592 return 0;
593 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
594 inode.i_links_count--;
595 }
596
597 retval = ext2fs_write_inode(fs, ino, &inode);
598 if (retval)
599 return retval;
600
601 return 0;
602}
603
604/*
605 * Fix parent --- this routine fixes up the parent of a directory.
606 */
607struct fix_dotdot_struct {
608 ext2_filsys fs;
609 ext2_ino_t parent;
610 int done;
611 e2fsck_t ctx;
612};
613
614static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
615 int offset EXT2FS_ATTR((unused)),
616 int blocksize EXT2FS_ATTR((unused)),
617 char *buf EXT2FS_ATTR((unused)),
618 void *priv_data)
619{
620 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
621 errcode_t retval;
622 struct problem_context pctx;
623
624 if ((dirent->name_len & 0xFF) != 2)
625 return 0;
626 if (strncmp(dirent->name, "..", 2))
627 return 0;
628
629 clear_problem_context(&pctx);
630
631 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
632 if (retval) {
633 pctx.errcode = retval;
634 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
635 }
636 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
637 if (retval) {
638 pctx.errcode = retval;
639 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
640 }
641 dirent->inode = fp->parent;
642
643 fp->done++;
644 return DIRENT_ABORT | DIRENT_CHANGED;
645}
646
647static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
648{
649 ext2_filsys fs = ctx->fs;
650 errcode_t retval;
651 struct fix_dotdot_struct fp;
652 struct problem_context pctx;
653
654 fp.fs = fs;
655 fp.parent = parent;
656 fp.done = 0;
657 fp.ctx = ctx;
658
659#if 0
660 printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent);
661#endif
662
663 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
664 0, fix_dotdot_proc, &fp);
665 if (retval || !fp.done) {
666 clear_problem_context(&pctx);
667 pctx.ino = dir->ino;
668 pctx.errcode = retval;
669 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
670 PR_3_FIX_PARENT_NOFIND, &pctx);
671 ext2fs_unmark_valid(fs);
672 }
673 dir->dotdot = parent;
674
675 return;
676}
677
678/*
679 * These routines are responsible for expanding a /lost+found if it is
680 * too small.
681 */
682
683struct expand_dir_struct {
684 int num;
685 int guaranteed_size;
686 int newblocks;
687 int last_block;
688 errcode_t err;
689 e2fsck_t ctx;
690};
691
692static int expand_dir_proc(ext2_filsys fs,
693 blk_t *blocknr,
694 e2_blkcnt_t blockcnt,
695 blk_t ref_block EXT2FS_ATTR((unused)),
696 int ref_offset EXT2FS_ATTR((unused)),
697 void *priv_data)
698{
699 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
700 blk_t new_blk;
701 static blk_t last_blk = 0;
702 char *block;
703 errcode_t retval;
704 e2fsck_t ctx;
705
706 ctx = es->ctx;
707
708 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
709 return BLOCK_ABORT;
710
711 if (blockcnt > 0)
712 es->last_block = blockcnt;
713 if (*blocknr) {
714 last_blk = *blocknr;
715 return 0;
716 }
717 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
718 &new_blk);
719 if (retval) {
720 es->err = retval;
721 return BLOCK_ABORT;
722 }
723 if (blockcnt > 0) {
724 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
725 if (retval) {
726 es->err = retval;
727 return BLOCK_ABORT;
728 }
729 es->num--;
730 retval = ext2fs_write_dir_block(fs, new_blk, block);
731 } else {
732 retval = ext2fs_get_mem(fs->blocksize, &block);
733 if (retval) {
734 es->err = retval;
735 return BLOCK_ABORT;
736 }
737 memset(block, 0, fs->blocksize);
738 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
739 }
740 if (retval) {
741 es->err = retval;
742 return BLOCK_ABORT;
743 }
744 ext2fs_free_mem(&block);
745 *blocknr = new_blk;
746 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
747 ext2fs_block_alloc_stats(fs, new_blk, +1);
748 es->newblocks++;
749
750 if (es->num == 0)
751 return (BLOCK_CHANGED | BLOCK_ABORT);
752 else
753 return BLOCK_CHANGED;
754}
755
756errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
757 int num, int guaranteed_size)
758{
759 ext2_filsys fs = ctx->fs;
760 errcode_t retval;
761 struct expand_dir_struct es;
762 struct ext2_inode inode;
763
764 if (!(fs->flags & EXT2_FLAG_RW))
765 return EXT2_ET_RO_FILSYS;
766
767 /*
768 * Read the inode and block bitmaps in; we'll be messing with
769 * them.
770 */
771 e2fsck_read_bitmaps(ctx);
772
773 retval = ext2fs_check_directory(fs, dir);
774 if (retval)
775 return retval;
776
777 es.num = num;
778 es.guaranteed_size = guaranteed_size;
779 es.last_block = 0;
780 es.err = 0;
781 es.newblocks = 0;
782 es.ctx = ctx;
783
784 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
785 0, expand_dir_proc, &es);
786
787 if (es.err)
788 return es.err;
789
790 /*
791 * Update the size and block count fields in the inode.
792 */
793 retval = ext2fs_read_inode(fs, dir, &inode);
794 if (retval)
795 return retval;
796
797 inode.i_size = (es.last_block + 1) * fs->blocksize;
798 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
799
800 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
801
802 return 0;
803}
804
diff --git a/e2fsprogs/e2fsck/pass4.c b/e2fsprogs/e2fsck/pass4.c
new file mode 100644
index 000000000..a9e49f178
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass4.c
@@ -0,0 +1,178 @@
1/*
2 * pass4.c -- pass #4 of e2fsck: Check reference counts
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * Pass 4 frees the following data structures:
12 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
13 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
14 */
15
16#include "e2fsck.h"
17#include "problem.h"
18
19/*
20 * This routine is called when an inode is not connected to the
21 * directory tree.
22 *
23 * This subroutine returns 1 then the caller shouldn't bother with the
24 * rest of the pass 4 tests.
25 */
26static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
27{
28 ext2_filsys fs = ctx->fs;
29 struct ext2_inode inode;
30 struct problem_context pctx;
31
32 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
33 clear_problem_context(&pctx);
34 pctx.ino = i;
35 pctx.inode = &inode;
36
37 /*
38 * Offer to delete any zero-length files that does not have
39 * blocks. If there is an EA block, it might have useful
40 * information, so we won't prompt to delete it, but let it be
41 * reconnected to lost+found.
42 */
43 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
44 LINUX_S_ISDIR(inode.i_mode))) {
45 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
46 ext2fs_icount_store(ctx->inode_link_info, i, 0);
47 inode.i_links_count = 0;
48 inode.i_dtime = time(0);
49 e2fsck_write_inode(ctx, i, &inode,
50 "disconnect_inode");
51 /*
52 * Fix up the bitmaps...
53 */
54 e2fsck_read_bitmaps(ctx);
55 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
56 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
57 ext2fs_inode_alloc_stats2(fs, i, -1,
58 LINUX_S_ISDIR(inode.i_mode));
59 return 0;
60 }
61 }
62
63 /*
64 * Prompt to reconnect.
65 */
66 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
67 if (e2fsck_reconnect_file(ctx, i))
68 ext2fs_unmark_valid(fs);
69 } else {
70 /*
71 * If we don't attach the inode, then skip the
72 * i_links_test since there's no point in trying to
73 * force i_links_count to zero.
74 */
75 ext2fs_unmark_valid(fs);
76 return 1;
77 }
78 return 0;
79}
80
81
82void e2fsck_pass4(e2fsck_t ctx)
83{
84 ext2_filsys fs = ctx->fs;
85 ext2_ino_t i;
86 struct ext2_inode inode;
87#ifdef RESOURCE_TRACK
88 struct resource_track rtrack;
89#endif
90 struct problem_context pctx;
91 __u16 link_count, link_counted;
92 char *buf = 0;
93 int group, maxgroup;
94
95#ifdef RESOURCE_TRACK
96 init_resource_track(&rtrack);
97#endif
98
99#ifdef MTRACE
100 mtrace_print("Pass 4");
101#endif
102
103 clear_problem_context(&pctx);
104
105 if (!(ctx->options & E2F_OPT_PREEN))
106 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
107
108 group = 0;
109 maxgroup = fs->group_desc_count;
110 if (ctx->progress)
111 if ((ctx->progress)(ctx, 4, 0, maxgroup))
112 return;
113
114 for (i=1; i <= fs->super->s_inodes_count; i++) {
115 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
116 return;
117 if ((i % fs->super->s_inodes_per_group) == 0) {
118 group++;
119 if (ctx->progress)
120 if ((ctx->progress)(ctx, 4, group, maxgroup))
121 return;
122 }
123 if (i == EXT2_BAD_INO ||
124 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
125 continue;
126 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
127 (ctx->inode_imagic_map &&
128 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) ||
129 (ctx->inode_bb_map &&
130 ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
131 continue;
132 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
133 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
134 if (link_counted == 0) {
135 if (!buf)
136 buf = e2fsck_allocate_memory(ctx,
137 fs->blocksize, "bad_inode buffer");
138 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
139 continue;
140 if (disconnect_inode(ctx, i))
141 continue;
142 ext2fs_icount_fetch(ctx->inode_link_info, i,
143 &link_count);
144 ext2fs_icount_fetch(ctx->inode_count, i,
145 &link_counted);
146 }
147 if (link_counted != link_count) {
148 e2fsck_read_inode(ctx, i, &inode, "pass4");
149 pctx.ino = i;
150 pctx.inode = &inode;
151 if (link_count != inode.i_links_count) {
152 pctx.num = link_count;
153 fix_problem(ctx,
154 PR_4_INCONSISTENT_COUNT, &pctx);
155 }
156 pctx.num = link_counted;
157 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
158 inode.i_links_count = link_counted;
159 e2fsck_write_inode(ctx, i, &inode, "pass4");
160 }
161 }
162 }
163 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
164 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
165 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
166 ctx->inode_bb_map = 0;
167 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
168 ctx->inode_imagic_map = 0;
169 if (buf)
170 ext2fs_free_mem(&buf);
171#ifdef RESOURCE_TRACK
172 if (ctx->options & E2F_OPT_TIME2) {
173 e2fsck_clear_progbar(ctx);
174 print_resource_track(_("Pass 4"), &rtrack);
175 }
176#endif
177}
178
diff --git a/e2fsprogs/e2fsck/pass5.c b/e2fsprogs/e2fsck/pass5.c
new file mode 100644
index 000000000..e708fa41d
--- /dev/null
+++ b/e2fsprogs/e2fsck/pass5.c
@@ -0,0 +1,547 @@
1/*
2 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 */
12
13#include "e2fsck.h"
14#include "problem.h"
15
16static void check_block_bitmaps(e2fsck_t ctx);
17static void check_inode_bitmaps(e2fsck_t ctx);
18static void check_inode_end(e2fsck_t ctx);
19static void check_block_end(e2fsck_t ctx);
20
21void e2fsck_pass5(e2fsck_t ctx)
22{
23#ifdef RESOURCE_TRACK
24 struct resource_track rtrack;
25#endif
26 struct problem_context pctx;
27
28#ifdef MTRACE
29 mtrace_print("Pass 5");
30#endif
31
32#ifdef RESOURCE_TRACK
33 init_resource_track(&rtrack);
34#endif
35
36 clear_problem_context(&pctx);
37
38 if (!(ctx->options & E2F_OPT_PREEN))
39 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
40
41 if (ctx->progress)
42 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
43 return;
44
45 e2fsck_read_bitmaps(ctx);
46
47 check_block_bitmaps(ctx);
48 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
49 return;
50 check_inode_bitmaps(ctx);
51 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
52 return;
53 check_inode_end(ctx);
54 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
55 return;
56 check_block_end(ctx);
57 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
58 return;
59
60 ext2fs_free_inode_bitmap(ctx->inode_used_map);
61 ctx->inode_used_map = 0;
62 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
63 ctx->inode_dir_map = 0;
64 ext2fs_free_block_bitmap(ctx->block_found_map);
65 ctx->block_found_map = 0;
66
67#ifdef RESOURCE_TRACK
68 if (ctx->options & E2F_OPT_TIME2) {
69 e2fsck_clear_progbar(ctx);
70 print_resource_track(_("Pass 5"), &rtrack);
71 }
72#endif
73}
74
75#define NO_BLK ((blk_t) -1)
76
77static void print_bitmap_problem(e2fsck_t ctx, int problem,
78 struct problem_context *pctx)
79{
80 switch (problem) {
81 case PR_5_BLOCK_UNUSED:
82 if (pctx->blk == pctx->blk2)
83 pctx->blk2 = 0;
84 else
85 problem = PR_5_BLOCK_RANGE_UNUSED;
86 break;
87 case PR_5_BLOCK_USED:
88 if (pctx->blk == pctx->blk2)
89 pctx->blk2 = 0;
90 else
91 problem = PR_5_BLOCK_RANGE_USED;
92 break;
93 case PR_5_INODE_UNUSED:
94 if (pctx->ino == pctx->ino2)
95 pctx->ino2 = 0;
96 else
97 problem = PR_5_INODE_RANGE_UNUSED;
98 break;
99 case PR_5_INODE_USED:
100 if (pctx->ino == pctx->ino2)
101 pctx->ino2 = 0;
102 else
103 problem = PR_5_INODE_RANGE_USED;
104 break;
105 }
106 fix_problem(ctx, problem, pctx);
107 pctx->blk = pctx->blk2 = NO_BLK;
108 pctx->ino = pctx->ino2 = 0;
109}
110
111static void check_block_bitmaps(e2fsck_t ctx)
112{
113 ext2_filsys fs = ctx->fs;
114 blk_t i;
115 int *free_array;
116 int group = 0;
117 unsigned int blocks = 0;
118 unsigned int free_blocks = 0;
119 int group_free = 0;
120 int actual, bitmap;
121 struct problem_context pctx;
122 int problem, save_problem, fixit, had_problem;
123 errcode_t retval;
124
125 clear_problem_context(&pctx);
126 free_array = (int *) e2fsck_allocate_memory(ctx,
127 fs->group_desc_count * sizeof(int), "free block count array");
128
129 if ((fs->super->s_first_data_block <
130 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
131 (fs->super->s_blocks_count-1 >
132 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
133 pctx.num = 1;
134 pctx.blk = fs->super->s_first_data_block;
135 pctx.blk2 = fs->super->s_blocks_count -1;
136 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
137 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
138 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
139
140 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
141 return;
142 }
143
144 if ((fs->super->s_first_data_block <
145 ext2fs_get_block_bitmap_start(fs->block_map)) ||
146 (fs->super->s_blocks_count-1 >
147 ext2fs_get_block_bitmap_end(fs->block_map))) {
148 pctx.num = 2;
149 pctx.blk = fs->super->s_first_data_block;
150 pctx.blk2 = fs->super->s_blocks_count -1;
151 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
152 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
153 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
154
155 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
156 return;
157 }
158
159redo_counts:
160 had_problem = 0;
161 save_problem = 0;
162 pctx.blk = pctx.blk2 = NO_BLK;
163 for (i = fs->super->s_first_data_block;
164 i < fs->super->s_blocks_count;
165 i++) {
166 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
167 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
168
169 if (actual == bitmap)
170 goto do_counts;
171
172 if (!actual && bitmap) {
173 /*
174 * Block not used, but marked in use in the bitmap.
175 */
176 problem = PR_5_BLOCK_UNUSED;
177 } else {
178 /*
179 * Block used, but not marked in use in the bitmap.
180 */
181 problem = PR_5_BLOCK_USED;
182 }
183 if (pctx.blk == NO_BLK) {
184 pctx.blk = pctx.blk2 = i;
185 save_problem = problem;
186 } else {
187 if ((problem == save_problem) &&
188 (pctx.blk2 == i-1))
189 pctx.blk2++;
190 else {
191 print_bitmap_problem(ctx, save_problem, &pctx);
192 pctx.blk = pctx.blk2 = i;
193 save_problem = problem;
194 }
195 }
196 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
197 had_problem++;
198
199 do_counts:
200 if (!bitmap) {
201 group_free++;
202 free_blocks++;
203 }
204 blocks ++;
205 if ((blocks == fs->super->s_blocks_per_group) ||
206 (i == fs->super->s_blocks_count-1)) {
207 free_array[group] = group_free;
208 group ++;
209 blocks = 0;
210 group_free = 0;
211 if (ctx->progress)
212 if ((ctx->progress)(ctx, 5, group,
213 fs->group_desc_count*2))
214 return;
215 }
216 }
217 if (pctx.blk != NO_BLK)
218 print_bitmap_problem(ctx, save_problem, &pctx);
219 if (had_problem)
220 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
221 else
222 fixit = -1;
223 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
224
225 if (fixit == 1) {
226 ext2fs_free_block_bitmap(fs->block_map);
227 retval = ext2fs_copy_bitmap(ctx->block_found_map,
228 &fs->block_map);
229 if (retval) {
230 clear_problem_context(&pctx);
231 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
232 ctx->flags |= E2F_FLAG_ABORT;
233 return;
234 }
235 ext2fs_set_bitmap_padding(fs->block_map);
236 ext2fs_mark_bb_dirty(fs);
237
238 /* Redo the counts */
239 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
240 memset(free_array, 0, fs->group_desc_count * sizeof(int));
241 goto redo_counts;
242 } else if (fixit == 0)
243 ext2fs_unmark_valid(fs);
244
245 for (i = 0; i < fs->group_desc_count; i++) {
246 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
247 pctx.group = i;
248 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
249 pctx.blk2 = free_array[i];
250
251 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
252 &pctx)) {
253 fs->group_desc[i].bg_free_blocks_count =
254 free_array[i];
255 ext2fs_mark_super_dirty(fs);
256 } else
257 ext2fs_unmark_valid(fs);
258 }
259 }
260 if (free_blocks != fs->super->s_free_blocks_count) {
261 pctx.group = 0;
262 pctx.blk = fs->super->s_free_blocks_count;
263 pctx.blk2 = free_blocks;
264
265 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
266 fs->super->s_free_blocks_count = free_blocks;
267 ext2fs_mark_super_dirty(fs);
268 } else
269 ext2fs_unmark_valid(fs);
270 }
271 ext2fs_free_mem(&free_array);
272}
273
274static void check_inode_bitmaps(e2fsck_t ctx)
275{
276 ext2_filsys fs = ctx->fs;
277 ext2_ino_t i;
278 unsigned int free_inodes = 0;
279 int group_free = 0;
280 int dirs_count = 0;
281 int group = 0;
282 unsigned int inodes = 0;
283 int *free_array;
284 int *dir_array;
285 int actual, bitmap;
286 errcode_t retval;
287 struct problem_context pctx;
288 int problem, save_problem, fixit, had_problem;
289
290 clear_problem_context(&pctx);
291 free_array = (int *) e2fsck_allocate_memory(ctx,
292 fs->group_desc_count * sizeof(int), "free inode count array");
293
294 dir_array = (int *) e2fsck_allocate_memory(ctx,
295 fs->group_desc_count * sizeof(int), "directory count array");
296
297 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
298 (fs->super->s_inodes_count >
299 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
300 pctx.num = 3;
301 pctx.blk = 1;
302 pctx.blk2 = fs->super->s_inodes_count;
303 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
304 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
305 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
306
307 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
308 return;
309 }
310 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
311 (fs->super->s_inodes_count >
312 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
313 pctx.num = 4;
314 pctx.blk = 1;
315 pctx.blk2 = fs->super->s_inodes_count;
316 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
317 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
318 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
319
320 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
321 return;
322 }
323
324redo_counts:
325 had_problem = 0;
326 save_problem = 0;
327 pctx.ino = pctx.ino2 = 0;
328 for (i = 1; i <= fs->super->s_inodes_count; i++) {
329 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
330 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
331
332 if (actual == bitmap)
333 goto do_counts;
334
335 if (!actual && bitmap) {
336 /*
337 * Inode wasn't used, but marked in bitmap
338 */
339 problem = PR_5_INODE_UNUSED;
340 } else /* if (actual && !bitmap) */ {
341 /*
342 * Inode used, but not in bitmap
343 */
344 problem = PR_5_INODE_USED;
345 }
346 if (pctx.ino == 0) {
347 pctx.ino = pctx.ino2 = i;
348 save_problem = problem;
349 } else {
350 if ((problem == save_problem) &&
351 (pctx.ino2 == i-1))
352 pctx.ino2++;
353 else {
354 print_bitmap_problem(ctx, save_problem, &pctx);
355 pctx.ino = pctx.ino2 = i;
356 save_problem = problem;
357 }
358 }
359 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
360 had_problem++;
361
362do_counts:
363 if (!bitmap) {
364 group_free++;
365 free_inodes++;
366 } else {
367 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
368 dirs_count++;
369 }
370 inodes++;
371 if ((inodes == fs->super->s_inodes_per_group) ||
372 (i == fs->super->s_inodes_count)) {
373 free_array[group] = group_free;
374 dir_array[group] = dirs_count;
375 group ++;
376 inodes = 0;
377 group_free = 0;
378 dirs_count = 0;
379 if (ctx->progress)
380 if ((ctx->progress)(ctx, 5,
381 group + fs->group_desc_count,
382 fs->group_desc_count*2))
383 return;
384 }
385 }
386 if (pctx.ino)
387 print_bitmap_problem(ctx, save_problem, &pctx);
388
389 if (had_problem)
390 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
391 else
392 fixit = -1;
393 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
394
395 if (fixit == 1) {
396 ext2fs_free_inode_bitmap(fs->inode_map);
397 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
398 &fs->inode_map);
399 if (retval) {
400 clear_problem_context(&pctx);
401 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
402 ctx->flags |= E2F_FLAG_ABORT;
403 return;
404 }
405 ext2fs_set_bitmap_padding(fs->inode_map);
406 ext2fs_mark_ib_dirty(fs);
407
408 /* redo counts */
409 inodes = 0; free_inodes = 0; group_free = 0;
410 dirs_count = 0; group = 0;
411 memset(free_array, 0, fs->group_desc_count * sizeof(int));
412 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
413 goto redo_counts;
414 } else if (fixit == 0)
415 ext2fs_unmark_valid(fs);
416
417 for (i = 0; i < fs->group_desc_count; i++) {
418 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
419 pctx.group = i;
420 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
421 pctx.ino2 = free_array[i];
422 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
423 &pctx)) {
424 fs->group_desc[i].bg_free_inodes_count =
425 free_array[i];
426 ext2fs_mark_super_dirty(fs);
427 } else
428 ext2fs_unmark_valid(fs);
429 }
430 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
431 pctx.group = i;
432 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
433 pctx.ino2 = dir_array[i];
434
435 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
436 &pctx)) {
437 fs->group_desc[i].bg_used_dirs_count =
438 dir_array[i];
439 ext2fs_mark_super_dirty(fs);
440 } else
441 ext2fs_unmark_valid(fs);
442 }
443 }
444 if (free_inodes != fs->super->s_free_inodes_count) {
445 pctx.group = -1;
446 pctx.ino = fs->super->s_free_inodes_count;
447 pctx.ino2 = free_inodes;
448
449 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
450 fs->super->s_free_inodes_count = free_inodes;
451 ext2fs_mark_super_dirty(fs);
452 } else
453 ext2fs_unmark_valid(fs);
454 }
455 ext2fs_free_mem(&free_array);
456 ext2fs_free_mem(&dir_array);
457}
458
459static void check_inode_end(e2fsck_t ctx)
460{
461 ext2_filsys fs = ctx->fs;
462 ext2_ino_t end, save_inodes_count, i;
463 struct problem_context pctx;
464
465 clear_problem_context(&pctx);
466
467 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
468 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
469 &save_inodes_count);
470 if (pctx.errcode) {
471 pctx.num = 1;
472 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
473 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
474 return;
475 }
476 if (save_inodes_count == end)
477 return;
478
479 for (i = save_inodes_count + 1; i <= end; i++) {
480 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
481 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
482 for (i = save_inodes_count + 1; i <= end; i++)
483 ext2fs_mark_inode_bitmap(fs->inode_map,
484 i);
485 ext2fs_mark_ib_dirty(fs);
486 } else
487 ext2fs_unmark_valid(fs);
488 break;
489 }
490 }
491
492 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
493 save_inodes_count, 0);
494 if (pctx.errcode) {
495 pctx.num = 2;
496 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
497 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
498 return;
499 }
500}
501
502static void check_block_end(e2fsck_t ctx)
503{
504 ext2_filsys fs = ctx->fs;
505 blk_t end, save_blocks_count, i;
506 struct problem_context pctx;
507
508 clear_problem_context(&pctx);
509
510 end = fs->block_map->start +
511 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
512 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
513 &save_blocks_count);
514 if (pctx.errcode) {
515 pctx.num = 3;
516 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
517 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
518 return;
519 }
520 if (save_blocks_count == end)
521 return;
522
523 for (i = save_blocks_count + 1; i <= end; i++) {
524 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
525 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
526 for (i = save_blocks_count + 1; i <= end; i++)
527 ext2fs_mark_block_bitmap(fs->block_map,
528 i);
529 ext2fs_mark_bb_dirty(fs);
530 } else
531 ext2fs_unmark_valid(fs);
532 break;
533 }
534 }
535
536 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
537 save_blocks_count, 0);
538 if (pctx.errcode) {
539 pctx.num = 4;
540 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
541 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
542 return;
543 }
544}
545
546
547
diff --git a/e2fsprogs/e2fsck/problem.c b/e2fsprogs/e2fsck/problem.c
new file mode 100644
index 000000000..7be4d6245
--- /dev/null
+++ b/e2fsprogs/e2fsck/problem.c
@@ -0,0 +1,1655 @@
1/*
2 * problem.c --- report filesystem problems to the user
3 *
4 * Copyright 1996, 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdlib.h>
13#include <unistd.h>
14#include <string.h>
15#include <ctype.h>
16#include <termios.h>
17
18#include "e2fsck.h"
19
20#include "problem.h"
21#include "problemP.h"
22
23#define PROMPT_NONE 0
24#define PROMPT_FIX 1
25#define PROMPT_CLEAR 2
26#define PROMPT_RELOCATE 3
27#define PROMPT_ALLOCATE 4
28#define PROMPT_EXPAND 5
29#define PROMPT_CONNECT 6
30#define PROMPT_CREATE 7
31#define PROMPT_SALVAGE 8
32#define PROMPT_TRUNCATE 9
33#define PROMPT_CLEAR_INODE 10
34#define PROMPT_ABORT 11
35#define PROMPT_SPLIT 12
36#define PROMPT_CONTINUE 13
37#define PROMPT_CLONE 14
38#define PROMPT_DELETE 15
39#define PROMPT_SUPPRESS 16
40#define PROMPT_UNLINK 17
41#define PROMPT_CLEAR_HTREE 18
42#define PROMPT_RECREATE 19
43#define PROMPT_NULL 20
44
45/*
46 * These are the prompts which are used to ask the user if they want
47 * to fix a problem.
48 */
49static const char *prompt[] = {
50 N_("(no prompt)"), /* 0 */
51 N_("Fix"), /* 1 */
52 N_("Clear"), /* 2 */
53 N_("Relocate"), /* 3 */
54 N_("Allocate"), /* 4 */
55 N_("Expand"), /* 5 */
56 N_("Connect to /lost+found"), /* 6 */
57 N_("Create"), /* 7 */
58 N_("Salvage"), /* 8 */
59 N_("Truncate"), /* 9 */
60 N_("Clear inode"), /* 10 */
61 N_("Abort"), /* 11 */
62 N_("Split"), /* 12 */
63 N_("Continue"), /* 13 */
64 N_("Clone duplicate/bad blocks"), /* 14 */
65 N_("Delete file"), /* 15 */
66 N_("Suppress messages"),/* 16 */
67 N_("Unlink"), /* 17 */
68 N_("Clear HTree index"),/* 18 */
69 N_("Recreate"), /* 19 */
70 "", /* 20 */
71};
72
73/*
74 * These messages are printed when we are preen mode and we will be
75 * automatically fixing the problem.
76 */
77static const char *preen_msg[] = {
78 N_("(NONE)"), /* 0 */
79 N_("FIXED"), /* 1 */
80 N_("CLEARED"), /* 2 */
81 N_("RELOCATED"), /* 3 */
82 N_("ALLOCATED"), /* 4 */
83 N_("EXPANDED"), /* 5 */
84 N_("RECONNECTED"), /* 6 */
85 N_("CREATED"), /* 7 */
86 N_("SALVAGED"), /* 8 */
87 N_("TRUNCATED"), /* 9 */
88 N_("INODE CLEARED"), /* 10 */
89 N_("ABORTED"), /* 11 */
90 N_("SPLIT"), /* 12 */
91 N_("CONTINUING"), /* 13 */
92 N_("DUPLICATE/BAD BLOCKS CLONED"), /* 14 */
93 N_("FILE DELETED"), /* 15 */
94 N_("SUPPRESSED"), /* 16 */
95 N_("UNLINKED"), /* 17 */
96 N_("HTREE INDEX CLEARED"),/* 18 */
97 N_("WILL RECREATE"), /* 19 */
98 "", /* 20 */
99};
100
101static const struct e2fsck_problem problem_table[] = {
102
103 /* Pre-Pass 1 errors */
104
105 /* Block bitmap not in group */
106 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
107 PROMPT_RELOCATE, PR_LATCH_RELOC },
108
109 /* Inode bitmap not in group */
110 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
111 PROMPT_RELOCATE, PR_LATCH_RELOC },
112
113 /* Inode table not in group */
114 { PR_0_ITABLE_NOT_GROUP,
115 N_("@i table for @g %g is not in @g. (@b %b)\n"
116 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
117 PROMPT_RELOCATE, PR_LATCH_RELOC },
118
119 /* Superblock corrupt */
120 { PR_0_SB_CORRUPT,
121 N_("\nThe @S could not be read or does not describe a correct ext2\n"
122 "@f. If the @v is valid and it really contains an ext2\n"
123 "@f (and not swap or ufs or something else), then the @S\n"
124 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
125 " e2fsck -b %S <@v>\n\n"),
126 PROMPT_NONE, PR_FATAL },
127
128 /* Filesystem size is wrong */
129 { PR_0_FS_SIZE_WRONG,
130 N_("The @f size (according to the @S) is %b @bs\n"
131 "The physical size of the @v is %c @bs\n"
132 "Either the @S or the partition table is likely to be corrupt!\n"),
133 PROMPT_ABORT, 0 },
134
135 /* Fragments not supported */
136 { PR_0_NO_FRAGMENTS,
137 N_("@S @b_size = %b, fragsize = %c.\n"
138 "This version of e2fsck does not support fragment sizes different\n"
139 "from the @b size.\n"),
140 PROMPT_NONE, PR_FATAL },
141
142 /* Bad blocks_per_group */
143 { PR_0_BLOCKS_PER_GROUP,
144 N_("@S @bs_per_group = %b, should have been %c\n"),
145 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
146
147 /* Bad first_data_block */
148 { PR_0_FIRST_DATA_BLOCK,
149 N_("@S first_data_@b = %b, should have been %c\n"),
150 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
151
152 /* Adding UUID to filesystem */
153 { PR_0_ADD_UUID,
154 N_("@f did not have a UUID; generating one.\n\n"),
155 PROMPT_NONE, 0 },
156
157 /* Relocate hint */
158 { PR_0_RELOCATE_HINT,
159 N_("Note: if there is several inode or block bitmap blocks\n"
160 "which require relocation, or one part of the inode table\n"
161 "which must be moved, you may wish to try running e2fsck\n"
162 "with the '-b %S' option first. The problem may lie only\n"
163 "with the primary block group descriptor, and the backup\n"
164 "block group descriptor may be OK.\n\n"),
165 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
166
167 /* Miscellaneous superblock corruption */
168 { PR_0_MISC_CORRUPT_SUPER,
169 N_("Corruption found in @S. (%s = %N).\n"),
170 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
171
172 /* Error determing physical device size of filesystem */
173 { PR_0_GETSIZE_ERROR,
174 N_("Error determining size of the physical @v: %m\n"),
175 PROMPT_NONE, PR_FATAL },
176
177 /* Inode count in superblock is incorrect */
178 { PR_0_INODE_COUNT_WRONG,
179 N_("@i count in @S is %i, should be %j.\n"),
180 PROMPT_FIX, 0 },
181
182 { PR_0_HURD_CLEAR_FILETYPE,
183 N_("The Hurd does not support the filetype feature.\n"),
184 PROMPT_CLEAR, 0 },
185
186 /* Journal inode is invalid */
187 { PR_0_JOURNAL_BAD_INODE,
188 N_("@S has a bad ext3 @j (@i %i).\n"),
189 PROMPT_CLEAR, PR_PREEN_OK },
190
191 /* The external journal has (unsupported) multiple filesystems */
192 { PR_0_JOURNAL_UNSUPP_MULTIFS,
193 N_("External @j has multiple @f users (unsupported).\n"),
194 PROMPT_NONE, PR_FATAL },
195
196 /* Can't find external journal */
197 { PR_0_CANT_FIND_JOURNAL,
198 N_("Can't find external @j\n"),
199 PROMPT_NONE, PR_FATAL },
200
201 /* External journal has bad superblock */
202 { PR_0_EXT_JOURNAL_BAD_SUPER,
203 N_("External @j has bad @S\n"),
204 PROMPT_NONE, PR_FATAL },
205
206 /* Superblock has a bad journal UUID */
207 { PR_0_JOURNAL_BAD_UUID,
208 N_("External @j does not support this @f\n"),
209 PROMPT_NONE, PR_FATAL },
210
211 /* Journal has an unknown superblock type */
212 { PR_0_JOURNAL_UNSUPP_SUPER,
213 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
214 "It is likely that your copy of e2fsck is old and/or doesn't "
215 "support this @j format.\n"
216 "It is also possible the @j @S is corrupt.\n"),
217 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
218
219 /* Journal superblock is corrupt */
220 { PR_0_JOURNAL_BAD_SUPER,
221 N_("Ext3 @j @S is corrupt.\n"),
222 PROMPT_FIX, PR_PREEN_OK },
223
224 /* Superblock flag should be cleared */
225 { PR_0_JOURNAL_HAS_JOURNAL,
226 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
227 PROMPT_CLEAR, PR_PREEN_OK },
228
229 /* Superblock flag is incorrect */
230 { PR_0_JOURNAL_RECOVER_SET,
231 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
232 PROMPT_CLEAR, PR_PREEN_OK },
233
234 /* Journal has data, but recovery flag is clear */
235 { PR_0_JOURNAL_RECOVERY_CLEAR,
236 N_("ext3 recovery flag clear, but @j has data.\n"),
237 PROMPT_NONE, 0 },
238
239 /* Ask if we should clear the journal */
240 { PR_0_JOURNAL_RESET_JOURNAL,
241 N_("Clear @j"),
242 PROMPT_NULL, PR_PREEN_NOMSG },
243
244 /* Ask if we should run the journal anyway */
245 { PR_0_JOURNAL_RUN,
246 N_("Run @j anyway"),
247 PROMPT_NULL, 0 },
248
249 /* Run the journal by default */
250 { PR_0_JOURNAL_RUN_DEFAULT,
251 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
252 PROMPT_NONE, 0 },
253
254 /* Clearing orphan inode */
255 { PR_0_ORPHAN_CLEAR_INODE,
256 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
257 PROMPT_NONE, 0 },
258
259 /* Illegal block found in orphaned inode */
260 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
261 N_("@I @b #%B (%b) found in @o @i %i.\n"),
262 PROMPT_NONE, 0 },
263
264 /* Already cleared block found in orphaned inode */
265 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
266 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
267 PROMPT_NONE, 0 },
268
269 /* Illegal orphan inode in superblock */
270 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
271 N_("@I @o @i %i in @S.\n"),
272 PROMPT_NONE, 0 },
273
274 /* Illegal inode in orphaned inode list */
275 { PR_0_ORPHAN_ILLEGAL_INODE,
276 N_("@I @i %i in @o @i list.\n"),
277 PROMPT_NONE, 0 },
278
279 /* Filesystem revision is 0, but feature flags are set */
280 { PR_0_FS_REV_LEVEL,
281 "@f has feature flag(s) set, but is a revision 0 @f. ",
282 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
283
284 /* Journal superblock has an unknown read-only feature flag set */
285 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
286 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
287 PROMPT_ABORT, 0 },
288
289 /* Journal superblock has an unknown incompatible feature flag set */
290 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
291 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
292 PROMPT_ABORT, 0 },
293
294 /* Journal has unsupported version number */
295 { PR_0_JOURNAL_UNSUPP_VERSION,
296 N_("@j version not supported by this e2fsck.\n"),
297 PROMPT_ABORT, 0 },
298
299 /* Moving journal to hidden file */
300 { PR_0_MOVE_JOURNAL,
301 N_("Moving @j from /%s to hidden inode.\n\n"),
302 PROMPT_NONE, 0 },
303
304 /* Error moving journal to hidden file */
305 { PR_0_ERR_MOVE_JOURNAL,
306 N_("Error moving @j: %m\n\n"),
307 PROMPT_NONE, 0 },
308
309 /* Clearing V2 journal superblock */
310 { PR_0_CLEAR_V2_JOURNAL,
311 N_("Found invalid V2 @j @S fields (from V1 journal).\n"
312 "Clearing fields beyond the V1 @j @S...\n\n"),
313 PROMPT_NONE, 0 },
314
315 /* Backup journal inode blocks */
316 { PR_0_BACKUP_JNL,
317 N_("Backing up @j @i @b information.\n\n"),
318 PROMPT_NONE, 0 },
319
320 /* Reserved blocks w/o resize_inode */
321 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
322 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
323 "is %N; @s zero. "),
324 PROMPT_FIX, 0 },
325
326 /* Resize_inode not enabled, but resize inode is non-zero */
327 { PR_0_CLEAR_RESIZE_INODE,
328 N_("Resize_@i not enabled, but the resize inode is non-zero. "),
329 PROMPT_CLEAR, 0 },
330
331 /* Resize inode invalid */
332 { PR_0_RESIZE_INODE_INVALID,
333 N_("Resize @i not valid. "),
334 PROMPT_RECREATE, 0 },
335
336 /* Pass 1 errors */
337
338 /* Pass 1: Checking inodes, blocks, and sizes */
339 { PR_1_PASS_HEADER,
340 N_("Pass 1: Checking @is, @bs, and sizes\n"),
341 PROMPT_NONE, 0 },
342
343 /* Root directory is not an inode */
344 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
345 PROMPT_CLEAR, 0 },
346
347 /* Root directory has dtime set */
348 { PR_1_ROOT_DTIME,
349 N_("@r has dtime set (probably due to old mke2fs). "),
350 PROMPT_FIX, PR_PREEN_OK },
351
352 /* Reserved inode has bad mode */
353 { PR_1_RESERVED_BAD_MODE,
354 N_("Reserved @i %i %Q has bad mode. "),
355 PROMPT_CLEAR, PR_PREEN_OK },
356
357 /* Deleted inode has zero dtime */
358 { PR_1_ZERO_DTIME,
359 N_("@D @i %i has zero dtime. "),
360 PROMPT_FIX, PR_PREEN_OK },
361
362 /* Inode in use, but dtime set */
363 { PR_1_SET_DTIME,
364 N_("@i %i is in use, but has dtime set. "),
365 PROMPT_FIX, PR_PREEN_OK },
366
367 /* Zero-length directory */
368 { PR_1_ZERO_LENGTH_DIR,
369 N_("@i %i is a @z @d. "),
370 PROMPT_CLEAR, PR_PREEN_OK },
371
372 /* Block bitmap conflicts with some other fs block */
373 { PR_1_BB_CONFLICT,
374 N_("@g %g's @b @B at %b @C.\n"),
375 PROMPT_RELOCATE, 0 },
376
377 /* Inode bitmap conflicts with some other fs block */
378 { PR_1_IB_CONFLICT,
379 N_("@g %g's @i @B at %b @C.\n"),
380 PROMPT_RELOCATE, 0 },
381
382 /* Inode table conflicts with some other fs block */
383 { PR_1_ITABLE_CONFLICT,
384 N_("@g %g's @i table at %b @C.\n"),
385 PROMPT_RELOCATE, 0 },
386
387 /* Block bitmap is on a bad block */
388 { PR_1_BB_BAD_BLOCK,
389 N_("@g %g's @b @B (%b) is bad. "),
390 PROMPT_RELOCATE, 0 },
391
392 /* Inode bitmap is on a bad block */
393 { PR_1_IB_BAD_BLOCK,
394 N_("@g %g's @i @B (%b) is bad. "),
395 PROMPT_RELOCATE, 0 },
396
397 /* Inode has incorrect i_size */
398 { PR_1_BAD_I_SIZE,
399 N_("@i %i, i_size is %Is, @s %N. "),
400 PROMPT_FIX, PR_PREEN_OK },
401
402 /* Inode has incorrect i_blocks */
403 { PR_1_BAD_I_BLOCKS,
404 N_("@i %i, i_@bs is %Ib, @s %N. "),
405 PROMPT_FIX, PR_PREEN_OK },
406
407 /* Illegal blocknumber in inode */
408 { PR_1_ILLEGAL_BLOCK_NUM,
409 N_("@I @b #%B (%b) in @i %i. "),
410 PROMPT_CLEAR, PR_LATCH_BLOCK },
411
412 /* Block number overlaps fs metadata */
413 { PR_1_BLOCK_OVERLAPS_METADATA,
414 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
415 PROMPT_CLEAR, PR_LATCH_BLOCK },
416
417 /* Inode has illegal blocks (latch question) */
418 { PR_1_INODE_BLOCK_LATCH,
419 N_("@i %i has illegal @b(s). "),
420 PROMPT_CLEAR, 0 },
421
422 /* Too many bad blocks in inode */
423 { PR_1_TOO_MANY_BAD_BLOCKS,
424 N_("Too many illegal @bs in @i %i.\n"),
425 PROMPT_CLEAR_INODE, PR_NO_OK },
426
427 /* Illegal block number in bad block inode */
428 { PR_1_BB_ILLEGAL_BLOCK_NUM,
429 N_("@I @b #%B (%b) in bad @b @i. "),
430 PROMPT_CLEAR, PR_LATCH_BBLOCK },
431
432 /* Bad block inode has illegal blocks (latch question) */
433 { PR_1_INODE_BBLOCK_LATCH,
434 N_("Bad @b @i has illegal @b(s). "),
435 PROMPT_CLEAR, 0 },
436
437 /* Duplicate or bad blocks in use! */
438 { PR_1_DUP_BLOCKS_PREENSTOP,
439 N_("Duplicate or bad @b in use!\n"),
440 PROMPT_NONE, 0 },
441
442 /* Bad block used as bad block indirect block */
443 { PR_1_BBINODE_BAD_METABLOCK,
444 N_("Bad @b %b used as bad @b @i indirect @b. "),
445 PROMPT_CLEAR, PR_LATCH_BBLOCK },
446
447 /* Inconsistency can't be fixed prompt */
448 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
449 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
450 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
451 "in the @f.\n"),
452 PROMPT_CONTINUE, PR_PREEN_NOMSG },
453
454 /* Bad primary block */
455 { PR_1_BAD_PRIMARY_BLOCK,
456 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
457 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
458
459 /* Bad primary block prompt */
460 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
461 N_("You can clear the this @b (and hope for the best) from the\n"
462 "bad @b list and hope that @b is really OK, but there are no\n"
463 "guarantees.\n\n"),
464 PROMPT_CLEAR, PR_PREEN_NOMSG },
465
466 /* Bad primary superblock */
467 { PR_1_BAD_PRIMARY_SUPERBLOCK,
468 N_("The primary @S (%b) is on the bad @b list.\n"),
469 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
470
471 /* Bad primary block group descriptors */
472 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
473 N_("Block %b in the primary @g descriptors "
474 "is on the bad @b list\n"),
475 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
476
477 /* Bad superblock in group */
478 { PR_1_BAD_SUPERBLOCK,
479 N_("Warning: Group %g's @S (%b) is bad.\n"),
480 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
481
482 /* Bad block group descriptors in group */
483 { PR_1_BAD_GROUP_DESCRIPTORS,
484 N_("Warning: Group %g's copy of the @g descriptors has a bad "
485 "@b (%b).\n"),
486 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
487
488 /* Block claimed for no reason */
489 { PR_1_PROGERR_CLAIMED_BLOCK,
490 N_("Programming error? @b #%b claimed for no reason in "
491 "process_bad_@b.\n"),
492 PROMPT_NONE, PR_PREEN_OK },
493
494 /* Error allocating blocks for relocating metadata */
495 { PR_1_RELOC_BLOCK_ALLOCATE,
496 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
497 PROMPT_NONE, PR_PREEN_OK },
498
499 /* Error allocating block buffer during relocation process */
500 { PR_1_RELOC_MEMORY_ALLOCATE,
501 N_("@A @b buffer for relocating %s\n"),
502 PROMPT_NONE, PR_PREEN_OK },
503
504 /* Relocating metadata group information from X to Y */
505 { PR_1_RELOC_FROM_TO,
506 N_("Relocating @g %g's %s from %b to %c...\n"),
507 PROMPT_NONE, PR_PREEN_OK },
508
509 /* Relocating metatdata group information to X */
510 { PR_1_RELOC_TO,
511 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
512 PROMPT_NONE, PR_PREEN_OK },
513
514 /* Block read error during relocation process */
515 { PR_1_RELOC_READ_ERR,
516 N_("Warning: could not read @b %b of %s: %m\n"),
517 PROMPT_NONE, PR_PREEN_OK },
518
519 /* Block write error during relocation process */
520 { PR_1_RELOC_WRITE_ERR,
521 N_("Warning: could not write @b %b for %s: %m\n"),
522 PROMPT_NONE, PR_PREEN_OK },
523
524 /* Error allocating inode bitmap */
525 { PR_1_ALLOCATE_IBITMAP_ERROR,
526 "@A @i @B (%N): %m\n",
527 PROMPT_NONE, PR_FATAL },
528
529 /* Error allocating block bitmap */
530 { PR_1_ALLOCATE_BBITMAP_ERROR,
531 "@A @b @B (%N): %m\n",
532 PROMPT_NONE, PR_FATAL },
533
534 /* Error allocating icount structure */
535 { PR_1_ALLOCATE_ICOUNT,
536 N_("@A icount link information: %m\n"),
537 PROMPT_NONE, PR_FATAL },
538
539 /* Error allocating dbcount */
540 { PR_1_ALLOCATE_DBCOUNT,
541 N_("@A @d @b array: %m\n"),
542 PROMPT_NONE, PR_FATAL },
543
544 /* Error while scanning inodes */
545 { PR_1_ISCAN_ERROR,
546 N_("Error while scanning @is (%i): %m\n"),
547 PROMPT_NONE, PR_FATAL },
548
549 /* Error while iterating over blocks */
550 { PR_1_BLOCK_ITERATE,
551 N_("Error while iterating over @bs in @i %i: %m\n"),
552 PROMPT_NONE, PR_FATAL },
553
554 /* Error while storing inode count information */
555 { PR_1_ICOUNT_STORE,
556 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
557 PROMPT_NONE, PR_FATAL },
558
559 /* Error while storing directory block information */
560 { PR_1_ADD_DBLOCK,
561 N_("Error storing @d @b information "
562 "(@i=%i, @b=%b, num=%N): %m\n"),
563 PROMPT_NONE, PR_FATAL },
564
565 /* Error while reading inode (for clearing) */
566 { PR_1_READ_INODE,
567 N_("Error reading @i %i: %m\n"),
568 PROMPT_NONE, PR_FATAL },
569
570 /* Suppress messages prompt */
571 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
572
573 /* Imagic flag set on an inode when filesystem doesn't support it */
574 { PR_1_SET_IMAGIC,
575 N_("@i %i has imagic flag set. "),
576 PROMPT_CLEAR, 0 },
577
578 /* Immutable flag set on a device or socket inode */
579 { PR_1_SET_IMMUTABLE,
580 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
581 "or append-only flag set. "),
582 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
583
584 /* Compression flag set on an inode when filesystem doesn't support it */
585 { PR_1_COMPR_SET,
586 N_("@i %i has @cion flag set on @f without @cion support. "),
587 PROMPT_CLEAR, 0 },
588
589 /* Non-zero size for device, fifo or socket inode */
590 { PR_1_SET_NONZSIZE,
591 "Special (@v/socket/fifo) @i %i has non-zero size. ",
592 PROMPT_FIX, PR_PREEN_OK },
593
594 /* Filesystem revision is 0, but feature flags are set */
595 { PR_1_FS_REV_LEVEL,
596 "@f has feature flag(s) set, but is a revision 0 @f. ",
597 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
598
599 /* Journal inode is not in use, but contains data */
600 { PR_1_JOURNAL_INODE_NOT_CLEAR,
601 "@j @i is not in use, but contains data. ",
602 PROMPT_CLEAR, PR_PREEN_OK },
603
604 /* Journal has bad mode */
605 { PR_1_JOURNAL_BAD_MODE,
606 N_("@j is not regular file. "),
607 PROMPT_FIX, PR_PREEN_OK },
608
609 /* Deal with inodes that were part of orphan linked list */
610 { PR_1_LOW_DTIME,
611 N_("@i %i was part of the orphaned @i list. "),
612 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
613
614 /* Deal with inodes that were part of corrupted orphan linked
615 list (latch question) */
616 { PR_1_ORPHAN_LIST_REFUGEES,
617 N_("@is that were part of a corrupted orphan linked list found. "),
618 PROMPT_FIX, 0 },
619
620 /* Error allocating refcount structure */
621 { PR_1_ALLOCATE_REFCOUNT,
622 "@A refcount structure (%N): %m\n",
623 PROMPT_NONE, PR_FATAL },
624
625 /* Error reading extended attribute block */
626 { PR_1_READ_EA_BLOCK,
627 N_("Error reading @a @b %b for @i %i. "),
628 PROMPT_CLEAR, 0 },
629
630 /* Invalid extended attribute block */
631 { PR_1_BAD_EA_BLOCK,
632 N_("@i %i has a bad @a @b %b. "),
633 PROMPT_CLEAR, 0 },
634
635 /* Error reading Extended Attribute block while fixing refcount */
636 { PR_1_EXTATTR_READ_ABORT,
637 N_("Error reading @a @b %b (%m). "),
638 PROMPT_ABORT, 0 },
639
640 /* Extended attribute reference count incorrect */
641 { PR_1_EXTATTR_REFCOUNT,
642 N_("@a @b %b has reference count %B, should be %N. "),
643 PROMPT_FIX, 0 },
644
645 /* Error writing Extended Attribute block while fixing refcount */
646 { PR_1_EXTATTR_WRITE,
647 N_("Error writing @a @b %b (%m). "),
648 PROMPT_ABORT, 0 },
649
650 /* Multiple EA blocks not supported */
651 { PR_1_EA_MULTI_BLOCK,
652 N_("@a @b %b has h_blocks > 1. "),
653 PROMPT_CLEAR, 0},
654
655 /* Error allocating EA region allocation structure */
656 { PR_1_EA_ALLOC_REGION,
657 N_("Error allocating @a @b %b. "),
658 PROMPT_ABORT, 0},
659
660 /* Error EA allocation collision */
661 { PR_1_EA_ALLOC_COLLISION,
662 N_("@a @b %b is corrupt (allocation collision). "),
663 PROMPT_CLEAR, 0},
664
665 /* Bad extended attribute name */
666 { PR_1_EA_BAD_NAME,
667 N_("@a @b %b is corrupt (invalid name). "),
668 PROMPT_CLEAR, 0},
669
670 /* Bad extended attribute value */
671 { PR_1_EA_BAD_VALUE,
672 N_("@a @b %b is corrupt (invalid value). "),
673 PROMPT_CLEAR, 0},
674
675 /* Inode too big (latch question) */
676 { PR_1_INODE_TOOBIG,
677 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
678
679 /* Directory too big */
680 { PR_1_TOOBIG_DIR,
681 N_("@b #%B (%b) causes @d to be too big. "),
682 PROMPT_CLEAR, PR_LATCH_TOOBIG },
683
684 /* Regular file too big */
685 { PR_1_TOOBIG_REG,
686 N_("@b #%B (%b) causes file to be too big. "),
687 PROMPT_CLEAR, PR_LATCH_TOOBIG },
688
689 /* Symlink too big */
690 { PR_1_TOOBIG_SYMLINK,
691 N_("@b #%B (%b) causes symlink to be too big. "),
692 PROMPT_CLEAR, PR_LATCH_TOOBIG },
693
694 /* INDEX_FL flag set on a non-HTREE filesystem */
695 { PR_1_HTREE_SET,
696 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
697 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
698
699 /* INDEX_FL flag set on a non-directory */
700 { PR_1_HTREE_NODIR,
701 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
702 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
703
704 /* Invalid root node in HTREE directory */
705 { PR_1_HTREE_BADROOT,
706 N_("@h %i has an invalid root node.\n"),
707 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
708
709 /* Unsupported hash version in HTREE directory */
710 { PR_1_HTREE_HASHV,
711 N_("@h %i has an unsupported hash version (%N)\n"),
712 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
713
714 /* Incompatible flag in HTREE root node */
715 { PR_1_HTREE_INCOMPAT,
716 N_("@h %i uses an incompatible htree root node flag.\n"),
717 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
718
719 /* HTREE too deep */
720 { PR_1_HTREE_DEPTH,
721 N_("@h %i has a tree depth (%N) which is too big\n"),
722 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
723
724 /* Bad block has indirect block that conflicts with filesystem block */
725 { PR_1_BB_FS_BLOCK,
726 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
727 "@f metadata. "),
728 PROMPT_CLEAR, PR_LATCH_BBLOCK },
729
730 /* Resize inode failed */
731 { PR_1_RESIZE_INODE_CREATE,
732 N_("Resize @i (re)creation failed: %m."),
733 PROMPT_ABORT, 0 },
734
735 /* invalid inode->i_extra_isize */
736 { PR_1_EXTRA_ISIZE,
737 N_("@i %i has a extra size (%IS) which is invalid\n"),
738 PROMPT_FIX, PR_PREEN_OK },
739
740 /* invalid ea entry->e_name_len */
741 { PR_1_ATTR_NAME_LEN,
742 N_("@a in @i %i has a namelen (%N) which is invalid\n"),
743 PROMPT_CLEAR, PR_PREEN_OK },
744
745 /* invalid ea entry->e_value_size */
746 { PR_1_ATTR_VALUE_SIZE,
747 N_("@a in @i %i has a value size (%N) which is invalid\n"),
748 PROMPT_CLEAR, PR_PREEN_OK },
749
750 /* invalid ea entry->e_value_offs */
751 { PR_1_ATTR_VALUE_OFFSET,
752 N_("@a in @i %i has a value offset (%N) which is invalid\n"),
753 PROMPT_CLEAR, PR_PREEN_OK },
754
755 /* invalid ea entry->e_value_block */
756 { PR_1_ATTR_VALUE_BLOCK,
757 N_("@a in @i %i has a value block (%N) which is invalid (must be 0)\n"),
758 PROMPT_CLEAR, PR_PREEN_OK },
759
760 /* invalid ea entry->e_hash */
761 { PR_1_ATTR_HASH,
762 N_("@a in @i %i has a hash (%N) which is invalid (must be 0)\n"),
763 PROMPT_CLEAR, PR_PREEN_OK },
764
765 /* Pass 1b errors */
766
767 /* Pass 1B: Rescan for duplicate/bad blocks */
768 { PR_1B_PASS_HEADER,
769 N_("Duplicate @bs found... invoking duplicate @b passes.\n"
770 "Pass 1B: Rescan for duplicate/bad @bs\n"),
771 PROMPT_NONE, 0 },
772
773 /* Duplicate/bad block(s) header */
774 { PR_1B_DUP_BLOCK_HEADER,
775 N_("Duplicate/bad @b(s) in @i %i:"),
776 PROMPT_NONE, 0 },
777
778 /* Duplicate/bad block(s) in inode */
779 { PR_1B_DUP_BLOCK,
780 " %b",
781 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
782
783 /* Duplicate/bad block(s) end */
784 { PR_1B_DUP_BLOCK_END,
785 "\n",
786 PROMPT_NONE, PR_PREEN_NOHDR },
787
788 /* Error while scanning inodes */
789 { PR_1B_ISCAN_ERROR,
790 N_("Error while scanning inodes (%i): %m\n"),
791 PROMPT_NONE, PR_FATAL },
792
793 /* Error allocating inode bitmap */
794 { PR_1B_ALLOCATE_IBITMAP_ERROR,
795 N_("@A @i @B (inode_dup_map): %m\n"),
796 PROMPT_NONE, PR_FATAL },
797
798 /* Error while iterating over blocks */
799 { PR_1B_BLOCK_ITERATE,
800 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
801 PROMPT_NONE, 0 },
802
803 /* Error adjusting EA refcount */
804 { PR_1B_ADJ_EA_REFCOUNT,
805 N_("Error addjusting refcount for @a @b %b (@i %i): %m\n"),
806 PROMPT_NONE, 0 },
807
808
809 /* Pass 1C: Scan directories for inodes with dup blocks. */
810 { PR_1C_PASS_HEADER,
811 N_("Pass 1C: Scan directories for @is with dup @bs.\n"),
812 PROMPT_NONE, 0 },
813
814
815 /* Pass 1D: Reconciling duplicate blocks */
816 { PR_1D_PASS_HEADER,
817 N_("Pass 1D: Reconciling duplicate @bs\n"),
818 PROMPT_NONE, 0 },
819
820 /* File has duplicate blocks */
821 { PR_1D_DUP_FILE,
822 N_("File %Q (@i #%i, mod time %IM) \n"
823 " has %B duplicate @b(s), shared with %N file(s):\n"),
824 PROMPT_NONE, 0 },
825
826 /* List of files sharing duplicate blocks */
827 { PR_1D_DUP_FILE_LIST,
828 N_("\t%Q (@i #%i, mod time %IM)\n"),
829 PROMPT_NONE, 0 },
830
831 /* File sharing blocks with filesystem metadata */
832 { PR_1D_SHARE_METADATA,
833 N_("\t<@f metadata>\n"),
834 PROMPT_NONE, 0 },
835
836 /* Report of how many duplicate/bad inodes */
837 { PR_1D_NUM_DUP_INODES,
838 N_("(There are %N @is containing duplicate/bad @bs.)\n\n"),
839 PROMPT_NONE, 0 },
840
841 /* Duplicated blocks already reassigned or cloned. */
842 { PR_1D_DUP_BLOCKS_DEALT,
843 N_("Duplicated @bs already reassigned or cloned.\n\n"),
844 PROMPT_NONE, 0 },
845
846 /* Clone duplicate/bad blocks? */
847 { PR_1D_CLONE_QUESTION,
848 "", PROMPT_CLONE, PR_NO_OK },
849
850 /* Delete file? */
851 { PR_1D_DELETE_QUESTION,
852 "", PROMPT_DELETE, 0 },
853
854 /* Couldn't clone file (error) */
855 { PR_1D_CLONE_ERROR,
856 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
857
858 /* Pass 2 errors */
859
860 /* Pass 2: Checking directory structure */
861 { PR_2_PASS_HEADER,
862 N_("Pass 2: Checking @d structure\n"),
863 PROMPT_NONE, 0 },
864
865 /* Bad inode number for '.' */
866 { PR_2_BAD_INODE_DOT,
867 N_("Bad @i number for '.' in @d @i %i.\n"),
868 PROMPT_FIX, 0 },
869
870 /* Directory entry has bad inode number */
871 { PR_2_BAD_INO,
872 N_("@E has bad @i #: %Di.\n"),
873 PROMPT_CLEAR, 0 },
874
875 /* Directory entry has deleted or unused inode */
876 { PR_2_UNUSED_INODE,
877 N_("@E has @D/unused @i %Di. "),
878 PROMPT_CLEAR, PR_PREEN_OK },
879
880 /* Directry entry is link to '.' */
881 { PR_2_LINK_DOT,
882 N_("@E @L to '.' "),
883 PROMPT_CLEAR, 0 },
884
885 /* Directory entry points to inode now located in a bad block */
886 { PR_2_BB_INODE,
887 N_("@E points to @i (%Di) located in a bad @b.\n"),
888 PROMPT_CLEAR, 0 },
889
890 /* Directory entry contains a link to a directory */
891 { PR_2_LINK_DIR,
892 N_("@E @L to @d %P (%Di).\n"),
893 PROMPT_CLEAR, 0 },
894
895 /* Directory entry contains a link to the root directry */
896 { PR_2_LINK_ROOT,
897 N_("@E @L to the @r.\n"),
898 PROMPT_CLEAR, 0 },
899
900 /* Directory entry has illegal characters in its name */
901 { PR_2_BAD_NAME,
902 N_("@E has illegal characters in its name.\n"),
903 PROMPT_FIX, 0 },
904
905 /* Missing '.' in directory inode */
906 { PR_2_MISSING_DOT,
907 N_("Missing '.' in @d @i %i.\n"),
908 PROMPT_FIX, 0 },
909
910 /* Missing '..' in directory inode */
911 { PR_2_MISSING_DOT_DOT,
912 N_("Missing '..' in @d @i %i.\n"),
913 PROMPT_FIX, 0 },
914
915 /* First entry in directory inode doesn't contain '.' */
916 { PR_2_1ST_NOT_DOT,
917 N_("First @e '%Dn' (inode=%Di) in @d @i %i (%p) @s '.'\n"),
918 PROMPT_FIX, 0 },
919
920 /* Second entry in directory inode doesn't contain '..' */
921 { PR_2_2ND_NOT_DOT_DOT,
922 N_("Second @e '%Dn' (inode=%Di) in @d @i %i @s '..'\n"),
923 PROMPT_FIX, 0 },
924
925 /* i_faddr should be zero */
926 { PR_2_FADDR_ZERO,
927 N_("i_faddr @F %IF, @s zero.\n"),
928 PROMPT_CLEAR, 0 },
929
930 /* i_file_acl should be zero */
931 { PR_2_FILE_ACL_ZERO,
932 N_("i_file_acl @F %If, @s zero.\n"),
933 PROMPT_CLEAR, 0 },
934
935 /* i_dir_acl should be zero */
936 { PR_2_DIR_ACL_ZERO,
937 N_("i_dir_acl @F %Id, @s zero.\n"),
938 PROMPT_CLEAR, 0 },
939
940 /* i_frag should be zero */
941 { PR_2_FRAG_ZERO,
942 N_("i_frag @F %N, @s zero.\n"),
943 PROMPT_CLEAR, 0 },
944
945 /* i_fsize should be zero */
946 { PR_2_FSIZE_ZERO,
947 N_("i_fsize @F %N, @s zero.\n"),
948 PROMPT_CLEAR, 0 },
949
950 /* inode has bad mode */
951 { PR_2_BAD_MODE,
952 N_("@i %i (%Q) has a bad mode (%Im).\n"),
953 PROMPT_CLEAR, 0 },
954
955 /* directory corrupted */
956 { PR_2_DIR_CORRUPTED,
957 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
958 PROMPT_SALVAGE, 0 },
959
960 /* filename too long */
961 { PR_2_FILENAME_LONG,
962 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
963 PROMPT_TRUNCATE, 0 },
964
965 /* Directory inode has a missing block (hole) */
966 { PR_2_DIRECTORY_HOLE,
967 N_("@d @i %i has an unallocated @b #%B. "),
968 PROMPT_ALLOCATE, 0 },
969
970 /* '.' is not NULL terminated */
971 { PR_2_DOT_NULL_TERM,
972 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
973 PROMPT_FIX, 0 },
974
975 /* '..' is not NULL terminated */
976 { PR_2_DOT_DOT_NULL_TERM,
977 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
978 PROMPT_FIX, 0 },
979
980 /* Illegal character device inode */
981 { PR_2_BAD_CHAR_DEV,
982 N_("@i %i (%Q) is an @I character @v.\n"),
983 PROMPT_CLEAR, 0 },
984
985 /* Illegal block device inode */
986 { PR_2_BAD_BLOCK_DEV,
987 N_("@i %i (%Q) is an @I @b @v.\n"),
988 PROMPT_CLEAR, 0 },
989
990 /* Duplicate '.' entry */
991 { PR_2_DUP_DOT,
992 N_("@E is duplicate '.' @e.\n"),
993 PROMPT_FIX, 0 },
994
995 /* Duplicate '..' entry */
996 { PR_2_DUP_DOT_DOT,
997 N_("@E is duplicate '..' @e.\n"),
998 PROMPT_FIX, 0 },
999
1000 /* Internal error: couldn't find dir_info */
1001 { PR_2_NO_DIRINFO,
1002 N_("Internal error: couldn't find dir_info for %i.\n"),
1003 PROMPT_NONE, PR_FATAL },
1004
1005 /* Final rec_len is wrong */
1006 { PR_2_FINAL_RECLEN,
1007 N_("@E has rec_len of %Dr, should be %N.\n"),
1008 PROMPT_FIX, 0 },
1009
1010 /* Error allocating icount structure */
1011 { PR_2_ALLOCATE_ICOUNT,
1012 N_("@A icount structure: %m\n"),
1013 PROMPT_NONE, PR_FATAL },
1014
1015 /* Error iterating over directory blocks */
1016 { PR_2_DBLIST_ITERATE,
1017 N_("Error iterating over @d @bs: %m\n"),
1018 PROMPT_NONE, PR_FATAL },
1019
1020 /* Error reading directory block */
1021 { PR_2_READ_DIRBLOCK,
1022 N_("Error reading @d @b %b (@i %i): %m\n"),
1023 PROMPT_CONTINUE, 0 },
1024
1025 /* Error writing directory block */
1026 { PR_2_WRITE_DIRBLOCK,
1027 N_("Error writing @d @b %b (@i %i): %m\n"),
1028 PROMPT_CONTINUE, 0 },
1029
1030 /* Error allocating new directory block */
1031 { PR_2_ALLOC_DIRBOCK,
1032 N_("@A new @d @b for @i %i (%s): %m\n"),
1033 PROMPT_NONE, 0 },
1034
1035 /* Error deallocating inode */
1036 { PR_2_DEALLOC_INODE,
1037 N_("Error deallocating @i %i: %m\n"),
1038 PROMPT_NONE, PR_FATAL },
1039
1040 /* Directory entry for '.' is big. Split? */
1041 { PR_2_SPLIT_DOT,
1042 N_("@d @e for '.' is big. "),
1043 PROMPT_SPLIT, PR_NO_OK },
1044
1045 /* Illegal FIFO inode */
1046 { PR_2_BAD_FIFO,
1047 N_("@i %i (%Q) is an @I FIFO.\n"),
1048 PROMPT_CLEAR, 0 },
1049
1050 /* Illegal socket inode */
1051 { PR_2_BAD_SOCKET,
1052 N_("@i %i (%Q) is an @I socket.\n"),
1053 PROMPT_CLEAR, 0 },
1054
1055 /* Directory filetype not set */
1056 { PR_2_SET_FILETYPE,
1057 N_("Setting filetype for @E to %N.\n"),
1058 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
1059
1060 /* Directory filetype incorrect */
1061 { PR_2_BAD_FILETYPE,
1062 N_("@E has an incorrect filetype (was %Dt, should be %N).\n"),
1063 PROMPT_FIX, 0 },
1064
1065 /* Directory filetype set on filesystem */
1066 { PR_2_CLEAR_FILETYPE,
1067 N_("@E has filetype set.\n"),
1068 PROMPT_CLEAR, PR_PREEN_OK },
1069
1070 /* Directory filename is null */
1071 { PR_2_NULL_NAME,
1072 N_("@E has a zero-length name.\n"),
1073 PROMPT_CLEAR, 0 },
1074
1075 /* Invalid symlink */
1076 { PR_2_INVALID_SYMLINK,
1077 N_("Symlink %Q (@i #%i) is invalid.\n"),
1078 PROMPT_CLEAR, 0 },
1079
1080 /* i_file_acl (extended attribute block) is bad */
1081 { PR_2_FILE_ACL_BAD,
1082 N_("@a @b @F invalid (%If).\n"),
1083 PROMPT_CLEAR, 0 },
1084
1085 /* Filesystem contains large files, but has no such flag in sb */
1086 { PR_2_FEATURE_LARGE_FILES,
1087 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
1088 PROMPT_FIX, 0 },
1089
1090 /* Node in HTREE directory not referenced */
1091 { PR_2_HTREE_NOTREF,
1092 N_("@p @h %d: node (%B) not referenced\n"),
1093 PROMPT_NONE, 0 },
1094
1095 /* Node in HTREE directory referenced twice */
1096 { PR_2_HTREE_DUPREF,
1097 N_("@p @h %d: node (%B) referenced twice\n"),
1098 PROMPT_NONE, 0 },
1099
1100 /* Node in HTREE directory has bad min hash */
1101 { PR_2_HTREE_MIN_HASH,
1102 N_("@p @h %d: node (%B) has bad min hash\n"),
1103 PROMPT_NONE, 0 },
1104
1105 /* Node in HTREE directory has bad max hash */
1106 { PR_2_HTREE_MAX_HASH,
1107 N_("@p @h %d: node (%B) has bad max hash\n"),
1108 PROMPT_NONE, 0 },
1109
1110 /* Clear invalid HTREE directory */
1111 { PR_2_HTREE_CLEAR,
1112 N_("Invalid @h %d (%q). "), PROMPT_CLEAR, 0 },
1113
1114 /* Bad block in htree interior node */
1115 { PR_2_HTREE_BADBLK,
1116 N_("@p @h %d (%q): bad @b number %b.\n"),
1117 PROMPT_CLEAR_HTREE, 0 },
1118
1119 /* Error adjusting EA refcount */
1120 { PR_2_ADJ_EA_REFCOUNT,
1121 N_("Error addjusting refcount for @a @b %b (@i %i): %m\n"),
1122 PROMPT_NONE, PR_FATAL },
1123
1124 /* Invalid HTREE root node */
1125 { PR_2_HTREE_BAD_ROOT,
1126 N_("@p @h %d: root node is invalid\n"),
1127 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
1128
1129 /* Invalid HTREE limit */
1130 { PR_2_HTREE_BAD_LIMIT,
1131 N_("@p @h %d: node (%B) has bad limit (%N)\n"),
1132 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
1133
1134 /* Invalid HTREE count */
1135 { PR_2_HTREE_BAD_COUNT,
1136 N_("@p @h %d: node (%B) has bad count (%N)\n"),
1137 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
1138
1139 /* HTREE interior node has out-of-order hashes in table */
1140 { PR_2_HTREE_HASH_ORDER,
1141 N_("@p @h %d: node (%B) has an unordered hash table\n"),
1142 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
1143
1144 /* Node in HTREE directory has bad depth */
1145 { PR_2_HTREE_BAD_DEPTH,
1146 N_("@p @h %d: node (%B) has bad depth\n"),
1147 PROMPT_NONE, 0 },
1148
1149 /* Duplicate directory entry found */
1150 { PR_2_DUPLICATE_DIRENT,
1151 N_("Duplicate @E found. "),
1152 PROMPT_CLEAR, 0 },
1153
1154 /* Non-unique filename found */
1155 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
1156 N_("@E has a non-unique filename.\nRename to %s"),
1157 PROMPT_NULL, 0 },
1158
1159 /* Duplicate directory entry found */
1160 { PR_2_REPORT_DUP_DIRENT,
1161 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
1162 PROMPT_NONE, 0 },
1163
1164 /* Pass 3 errors */
1165
1166 /* Pass 3: Checking directory connectivity */
1167 { PR_3_PASS_HEADER,
1168 N_("Pass 3: Checking @d connectivity\n"),
1169 PROMPT_NONE, 0 },
1170
1171 /* Root inode not allocated */
1172 { PR_3_NO_ROOT_INODE,
1173 N_("@r not allocated. "),
1174 PROMPT_ALLOCATE, 0 },
1175
1176 /* No room in lost+found */
1177 { PR_3_EXPAND_LF_DIR,
1178 N_("No room in @l @d. "),
1179 PROMPT_EXPAND, 0 },
1180
1181 /* Unconnected directory inode */
1182 { PR_3_UNCONNECTED_DIR,
1183 N_("Unconnected @d @i %i (%p)\n"),
1184 PROMPT_CONNECT, 0 },
1185
1186 /* /lost+found not found */
1187 { PR_3_NO_LF_DIR,
1188 N_("/@l not found. "),
1189 PROMPT_CREATE, PR_PREEN_OK },
1190
1191 /* .. entry is incorrect */
1192 { PR_3_BAD_DOT_DOT,
1193 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
1194 PROMPT_FIX, 0 },
1195
1196 /* Bad or non-existent /lost+found. Cannot reconnect */
1197 { PR_3_NO_LPF,
1198 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
1199 PROMPT_NONE, 0 },
1200
1201 /* Could not expand /lost+found */
1202 { PR_3_CANT_EXPAND_LPF,
1203 N_("Could not expand /@l: %m\n"),
1204 PROMPT_NONE, 0 },
1205
1206 /* Could not reconnect inode */
1207 { PR_3_CANT_RECONNECT,
1208 N_("Could not reconnect %i: %m\n"),
1209 PROMPT_NONE, 0 },
1210
1211 /* Error while trying to find /lost+found */
1212 { PR_3_ERR_FIND_LPF,
1213 N_("Error while trying to find /@l: %m\n"),
1214 PROMPT_NONE, 0 },
1215
1216 /* Error in ext2fs_new_block while creating /lost+found */
1217 { PR_3_ERR_LPF_NEW_BLOCK,
1218 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
1219 PROMPT_NONE, 0 },
1220
1221 /* Error in ext2fs_new_inode while creating /lost+found */
1222 { PR_3_ERR_LPF_NEW_INODE,
1223 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
1224 PROMPT_NONE, 0 },
1225
1226 /* Error in ext2fs_new_dir_block while creating /lost+found */
1227 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
1228 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
1229 PROMPT_NONE, 0 },
1230
1231 /* Error while writing directory block for /lost+found */
1232 { PR_3_ERR_LPF_WRITE_BLOCK,
1233 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
1234 PROMPT_NONE, 0 },
1235
1236 /* Error while adjusting inode count */
1237 { PR_3_ADJUST_INODE,
1238 N_("Error while adjusting @i count on @i %i\n"),
1239 PROMPT_NONE, 0 },
1240
1241 /* Couldn't fix parent directory -- error */
1242 { PR_3_FIX_PARENT_ERR,
1243 N_("Couldn't fix parent of @i %i: %m\n\n"),
1244 PROMPT_NONE, 0 },
1245
1246 /* Couldn't fix parent directory -- couldn't find it */
1247 { PR_3_FIX_PARENT_NOFIND,
1248 N_("Couldn't fix parent of @i %i: Couldn't find parent @d entry\n\n"),
1249 PROMPT_NONE, 0 },
1250
1251 /* Error allocating inode bitmap */
1252 { PR_3_ALLOCATE_IBITMAP_ERROR,
1253 N_("@A @i @B (%N): %m\n"),
1254 PROMPT_NONE, PR_FATAL },
1255
1256 /* Error creating root directory */
1257 { PR_3_CREATE_ROOT_ERROR,
1258 N_("Error creating root @d (%s): %m\n"),
1259 PROMPT_NONE, PR_FATAL },
1260
1261 /* Error creating lost and found directory */
1262 { PR_3_CREATE_LPF_ERROR,
1263 N_("Error creating /@l @d (%s): %m\n"),
1264 PROMPT_NONE, PR_FATAL },
1265
1266 /* Root inode is not directory; aborting */
1267 { PR_3_ROOT_NOT_DIR_ABORT,
1268 N_("@r is not a @d; aborting.\n"),
1269 PROMPT_NONE, PR_FATAL },
1270
1271 /* Cannot proceed without a root inode. */
1272 { PR_3_NO_ROOT_INODE_ABORT,
1273 N_("Cannot proceed without a @r.\n"),
1274 PROMPT_NONE, PR_FATAL },
1275
1276 /* Internal error: couldn't find dir_info */
1277 { PR_3_NO_DIRINFO,
1278 N_("Internal error: couldn't find dir_info for %i.\n"),
1279 PROMPT_NONE, PR_FATAL },
1280
1281 /* Lost+found not a directory */
1282 { PR_3_LPF_NOTDIR,
1283 N_("/@l is not a @d (ino=%i)\n"),
1284 PROMPT_UNLINK, 0 },
1285
1286 /* Pass 3A Directory Optimization */
1287
1288 /* Pass 3A: Optimizing directories */
1289 { PR_3A_PASS_HEADER,
1290 N_("Pass 3A: Optimizing directories\n"),
1291 PROMPT_NONE, PR_PREEN_NOMSG },
1292
1293 /* Error iterating over directories */
1294 { PR_3A_OPTIMIZE_ITER,
1295 N_("Failed to create dirs_to_hash iterator: %m"),
1296 PROMPT_NONE, 0 },
1297
1298 /* Error rehash directory */
1299 { PR_3A_OPTIMIZE_DIR_ERR,
1300 N_("Failed to optimize directory %q (%d): %m"),
1301 PROMPT_NONE, 0 },
1302
1303 /* Rehashing dir header */
1304 { PR_3A_OPTIMIZE_DIR_HEADER,
1305 N_("Optimizing directories: "),
1306 PROMPT_NONE, PR_MSG_ONLY },
1307
1308 /* Rehashing directory %d */
1309 { PR_3A_OPTIMIZE_DIR,
1310 " %d",
1311 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
1312
1313 /* Rehashing dir end */
1314 { PR_3A_OPTIMIZE_DIR_END,
1315 "\n",
1316 PROMPT_NONE, PR_PREEN_NOHDR },
1317
1318 /* Pass 4 errors */
1319
1320 /* Pass 4: Checking reference counts */
1321 { PR_4_PASS_HEADER,
1322 N_("Pass 4: Checking reference counts\n"),
1323 PROMPT_NONE, 0 },
1324
1325 /* Unattached zero-length inode */
1326 { PR_4_ZERO_LEN_INODE,
1327 "@u @z @i %i. ",
1328 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
1329
1330 /* Unattached inode */
1331 { PR_4_UNATTACHED_INODE,
1332 "@u @i %i\n",
1333 PROMPT_CONNECT, 0 },
1334
1335 /* Inode ref count wrong */
1336 { PR_4_BAD_REF_COUNT,
1337 N_("@i %i ref count is %Il, @s %N. "),
1338 PROMPT_FIX, PR_PREEN_OK },
1339
1340 { PR_4_INCONSISTENT_COUNT,
1341 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
1342 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
1343 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
1344 "They should be the same!\n"),
1345 PROMPT_NONE, 0 },
1346
1347 /* Pass 5 errors */
1348
1349 /* Pass 5: Checking group summary information */
1350 { PR_5_PASS_HEADER,
1351 N_("Pass 5: Checking @g summary information\n"),
1352 PROMPT_NONE, 0 },
1353
1354 /* Padding at end of inode bitmap is not set. */
1355 { PR_5_INODE_BMAP_PADDING,
1356 N_("Padding at end of @i @B is not set. "),
1357 PROMPT_FIX, PR_PREEN_OK },
1358
1359 /* Padding at end of block bitmap is not set. */
1360 { PR_5_BLOCK_BMAP_PADDING,
1361 N_("Padding at end of @b @B is not set. "),
1362 PROMPT_FIX, PR_PREEN_OK },
1363
1364 /* Block bitmap differences header */
1365 { PR_5_BLOCK_BITMAP_HEADER,
1366 N_("@b @B differences: "),
1367 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
1368
1369 /* Block not used, but marked in bitmap */
1370 { PR_5_BLOCK_UNUSED,
1371 " -%b",
1372 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1373
1374 /* Block used, but not marked used in bitmap */
1375 { PR_5_BLOCK_USED,
1376 " +%b",
1377 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1378
1379 /* Block bitmap differences end */
1380 { PR_5_BLOCK_BITMAP_END,
1381 "\n",
1382 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1383
1384 /* Inode bitmap differences header */
1385 { PR_5_INODE_BITMAP_HEADER,
1386 N_("@i @B differences: "),
1387 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
1388
1389 /* Inode not used, but marked in bitmap */
1390 { PR_5_INODE_UNUSED,
1391 " -%i",
1392 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1393
1394 /* Inode used, but not marked used in bitmap */
1395 { PR_5_INODE_USED,
1396 " +%i",
1397 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1398
1399 /* Inode bitmap differences end */
1400 { PR_5_INODE_BITMAP_END,
1401 "\n",
1402 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1403
1404 /* Free inodes count for group wrong */
1405 { PR_5_FREE_INODE_COUNT_GROUP,
1406 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
1407 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1408
1409 /* Directories count for group wrong */
1410 { PR_5_FREE_DIR_COUNT_GROUP,
1411 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
1412 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1413
1414 /* Free inodes count wrong */
1415 { PR_5_FREE_INODE_COUNT,
1416 N_("Free @is count wrong (%i, counted=%j).\n"),
1417 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1418
1419 /* Free blocks count for group wrong */
1420 { PR_5_FREE_BLOCK_COUNT_GROUP,
1421 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
1422 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1423
1424 /* Free blocks count wrong */
1425 { PR_5_FREE_BLOCK_COUNT,
1426 N_("Free @bs count wrong (%b, counted=%c).\n"),
1427 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
1428
1429 /* Programming error: bitmap endpoints don't match */
1430 { PR_5_BMAP_ENDPOINTS,
1431 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
1432 "match calculated @B endpoints (%i, %j)\n"),
1433 PROMPT_NONE, PR_FATAL },
1434
1435 /* Internal error: fudging end of bitmap */
1436 { PR_5_FUDGE_BITMAP_ERROR,
1437 N_("Internal error: fudging end of bitmap (%N)\n"),
1438 PROMPT_NONE, PR_FATAL },
1439
1440 /* Error copying in replacement inode bitmap */
1441 { PR_5_COPY_IBITMAP_ERROR,
1442 "Error copying in replacement @i @B: %m\n",
1443 PROMPT_NONE, PR_FATAL },
1444
1445 /* Error copying in replacement block bitmap */
1446 { PR_5_COPY_BBITMAP_ERROR,
1447 "Error copying in replacement @b @B: %m\n",
1448 PROMPT_NONE, PR_FATAL },
1449
1450 /* Block range not used, but marked in bitmap */
1451 { PR_5_BLOCK_RANGE_UNUSED,
1452 " -(%b--%c)",
1453 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1454
1455 /* Block range used, but not marked used in bitmap */
1456 { PR_5_BLOCK_RANGE_USED,
1457 " +(%b--%c)",
1458 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1459
1460 /* Inode range not used, but marked in bitmap */
1461 { PR_5_INODE_RANGE_UNUSED,
1462 " -(%i--%j)",
1463 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1464
1465 /* Inode range used, but not marked used in bitmap */
1466 { PR_5_INODE_RANGE_USED,
1467 " +(%i--%j)",
1468 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
1469
1470 { 0 }
1471};
1472
1473/*
1474 * This is the latch flags register. It allows several problems to be
1475 * "latched" together. This means that the user has to answer but one
1476 * question for the set of problems, and all of the associated
1477 * problems will be either fixed or not fixed.
1478 */
1479static struct latch_descr pr_latch_info[] = {
1480 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
1481 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
1482 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
1483 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
1484 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
1485 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
1486 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
1487 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
1488 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
1489 { -1, 0, 0 },
1490};
1491
1492static const struct e2fsck_problem *find_problem(problem_t code)
1493{
1494 int i;
1495
1496 for (i=0; problem_table[i].e2p_code; i++) {
1497 if (problem_table[i].e2p_code == code)
1498 return &problem_table[i];
1499 }
1500 return 0;
1501}
1502
1503static struct latch_descr *find_latch(int code)
1504{
1505 int i;
1506
1507 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
1508 if (pr_latch_info[i].latch_code == code)
1509 return &pr_latch_info[i];
1510 }
1511 return 0;
1512}
1513
1514int end_problem_latch(e2fsck_t ctx, int mask)
1515{
1516 struct latch_descr *ldesc;
1517 struct problem_context pctx;
1518 int answer = -1;
1519
1520 ldesc = find_latch(mask);
1521 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
1522 clear_problem_context(&pctx);
1523 answer = fix_problem(ctx, ldesc->end_message, &pctx);
1524 }
1525 ldesc->flags &= ~(PRL_VARIABLE);
1526 return answer;
1527}
1528
1529int set_latch_flags(int mask, int setflags, int clearflags)
1530{
1531 struct latch_descr *ldesc;
1532
1533 ldesc = find_latch(mask);
1534 if (!ldesc)
1535 return -1;
1536 ldesc->flags |= setflags;
1537 ldesc->flags &= ~clearflags;
1538 return 0;
1539}
1540
1541int get_latch_flags(int mask, int *value)
1542{
1543 struct latch_descr *ldesc;
1544
1545 ldesc = find_latch(mask);
1546 if (!ldesc)
1547 return -1;
1548 *value = ldesc->flags;
1549 return 0;
1550}
1551
1552void clear_problem_context(struct problem_context *ctx)
1553{
1554 memset(ctx, 0, sizeof(struct problem_context));
1555 ctx->blkcount = -1;
1556 ctx->group = -1;
1557}
1558
1559int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
1560{
1561 ext2_filsys fs = ctx->fs;
1562 const struct e2fsck_problem *ptr;
1563 struct latch_descr *ldesc = 0;
1564 const char *message;
1565 int def_yn, answer, ans;
1566 int print_answer = 0;
1567 int suppress = 0;
1568
1569 ptr = find_problem(code);
1570 if (!ptr) {
1571 printf(_("Unhandled error code (0x%x)!\n"), code);
1572 return 0;
1573 }
1574 def_yn = 1;
1575 if ((ptr->flags & PR_NO_DEFAULT) ||
1576 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
1577 (ctx->options & E2F_OPT_NO))
1578 def_yn= 0;
1579
1580 /*
1581 * Do special latch processing. This is where we ask the
1582 * latch question, if it exists
1583 */
1584 if (ptr->flags & PR_LATCH_MASK) {
1585 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
1586 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
1587 ans = fix_problem(ctx, ldesc->question, pctx);
1588 if (ans == 1)
1589 ldesc->flags |= PRL_YES;
1590 if (ans == 0)
1591 ldesc->flags |= PRL_NO;
1592 ldesc->flags |= PRL_LATCHED;
1593 }
1594 if (ldesc->flags & PRL_SUPPRESS)
1595 suppress++;
1596 }
1597 if ((ptr->flags & PR_PREEN_NOMSG) &&
1598 (ctx->options & E2F_OPT_PREEN))
1599 suppress++;
1600 if ((ptr->flags & PR_NO_NOMSG) &&
1601 (ctx->options & E2F_OPT_NO))
1602 suppress++;
1603 if (!suppress) {
1604 message = ptr->e2p_description;
1605 if ((ctx->options & E2F_OPT_PREEN) &&
1606 !(ptr->flags & PR_PREEN_NOHDR)) {
1607 printf("%s: ", ctx->device_name ?
1608 ctx->device_name : ctx->filesystem_name);
1609 }
1610 if (*message)
1611 print_e2fsck_message(ctx, _(message), pctx, 1);
1612 }
1613 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
1614 preenhalt(ctx);
1615
1616 if (ptr->flags & PR_FATAL)
1617 fatal_error(ctx, 0);
1618
1619 if (ptr->prompt == PROMPT_NONE) {
1620 if (ptr->flags & PR_NOCOLLATE)
1621 answer = -1;
1622 else
1623 answer = def_yn;
1624 } else {
1625 if (ctx->options & E2F_OPT_PREEN) {
1626 answer = def_yn;
1627 if (!(ptr->flags & PR_PREEN_NOMSG))
1628 print_answer = 1;
1629 } else if ((ptr->flags & PR_LATCH_MASK) &&
1630 (ldesc->flags & (PRL_YES | PRL_NO))) {
1631 if (!suppress)
1632 print_answer = 1;
1633 if (ldesc->flags & PRL_YES)
1634 answer = 1;
1635 else
1636 answer = 0;
1637 } else
1638 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
1639 if (!answer && !(ptr->flags & PR_NO_OK))
1640 ext2fs_unmark_valid(fs);
1641
1642 if (print_answer)
1643 printf("%s.\n", answer ?
1644 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
1645
1646 }
1647
1648 if ((ptr->prompt == PROMPT_ABORT) && answer)
1649 fatal_error(ctx, 0);
1650
1651 if (ptr->flags & PR_AFTER_CODE)
1652 answer = fix_problem(ctx, ptr->second_code, pctx);
1653
1654 return answer;
1655}
diff --git a/e2fsprogs/e2fsck/problem.h b/e2fsprogs/e2fsck/problem.h
new file mode 100644
index 000000000..0e39a2935
--- /dev/null
+++ b/e2fsprogs/e2fsck/problem.h
@@ -0,0 +1,897 @@
1/*
2 * problem.h --- e2fsck problem error codes
3 *
4 * Copyright 1996 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12typedef __u32 problem_t;
13
14struct problem_context {
15 errcode_t errcode;
16 ext2_ino_t ino, ino2, dir;
17 struct ext2_inode *inode;
18 struct ext2_dir_entry *dirent;
19 blk_t blk, blk2;
20 e2_blkcnt_t blkcount;
21 int group;
22 __u64 num;
23 const char *str;
24};
25
26/*
27 * We define a set of "latch groups"; these are problems which are
28 * handled as a set. The user answers once for a particular latch
29 * group.
30 */
31#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
32#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
33#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
34#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
35#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
36#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
37#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
38#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
39#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
40#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
41
42#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
43
44/*
45 * Latch group descriptor flags
46 */
47#define PRL_YES 0x0001 /* Answer yes */
48#define PRL_NO 0x0002 /* Answer no */
49#define PRL_LATCHED 0x0004 /* The latch group is latched */
50#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
51
52#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
53
54/*
55 * Pre-Pass 1 errors
56 */
57
58/* Block bitmap not in group */
59#define PR_0_BB_NOT_GROUP 0x000001
60
61/* Inode bitmap not in group */
62#define PR_0_IB_NOT_GROUP 0x000002
63
64/* Inode table not in group */
65#define PR_0_ITABLE_NOT_GROUP 0x000003
66
67/* Superblock corrupt */
68#define PR_0_SB_CORRUPT 0x000004
69
70/* Filesystem size is wrong */
71#define PR_0_FS_SIZE_WRONG 0x000005
72
73/* Fragments not supported */
74#define PR_0_NO_FRAGMENTS 0x000006
75
76/* Bad blocks_per_group */
77#define PR_0_BLOCKS_PER_GROUP 0x000007
78
79/* Bad first_data_block */
80#define PR_0_FIRST_DATA_BLOCK 0x000008
81
82/* Adding UUID to filesystem */
83#define PR_0_ADD_UUID 0x000009
84
85/* Relocate hint */
86#define PR_0_RELOCATE_HINT 0x00000A
87
88/* Miscellaneous superblock corruption */
89#define PR_0_MISC_CORRUPT_SUPER 0x00000B
90
91/* Error determing physical device size of filesystem */
92#define PR_0_GETSIZE_ERROR 0x00000C
93
94/* Inode count in the superblock incorrect */
95#define PR_0_INODE_COUNT_WRONG 0x00000D
96
97/* The Hurd does not support the filetype feature */
98#define PR_0_HURD_CLEAR_FILETYPE 0x00000E
99
100/* Journal inode is invalid */
101#define PR_0_JOURNAL_BAD_INODE 0x00000F
102
103/* The external journal has multiple filesystems (which we can't handle yet) */
104#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010
105
106/* Can't find external journal */
107#define PR_0_CANT_FIND_JOURNAL 0x000011
108
109/* External journal has bad superblock */
110#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012
111
112/* Superblock has a bad journal UUID */
113#define PR_0_JOURNAL_BAD_UUID 0x000013
114
115/* Journal has an unknown superblock type */
116#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014
117
118/* Journal superblock is corrupt */
119#define PR_0_JOURNAL_BAD_SUPER 0x000015
120
121/* Journal superblock is corrupt */
122#define PR_0_JOURNAL_HAS_JOURNAL 0x000016
123
124/* Superblock has recovery flag set but no journal */
125#define PR_0_JOURNAL_RECOVER_SET 0x000017
126
127/* Journal has data, but recovery flag is clear */
128#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018
129
130/* Ask if we should clear the journal */
131#define PR_0_JOURNAL_RESET_JOURNAL 0x000019
132
133/* Filesystem revision is 0, but feature flags are set */
134#define PR_0_FS_REV_LEVEL 0x00001A
135
136/* Clearing orphan inode */
137#define PR_0_ORPHAN_CLEAR_INODE 0x000020
138
139/* Illegal block found in orphaned inode */
140#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021
141
142/* Already cleared block found in orphaned inode */
143#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022
144
145/* Illegal orphan inode in superblock */
146#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023
147
148/* Illegal inode in orphaned inode list */
149#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024
150
151/* Journal has unsupported read-only feature - abort */
152#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025
153
154/* Journal has unsupported incompatible feature - abort */
155#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026
156
157/* Journal has unsupported version number */
158#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027
159
160/* Moving journal to hidden file */
161#define PR_0_MOVE_JOURNAL 0x000028
162
163/* Error moving journal */
164#define PR_0_ERR_MOVE_JOURNAL 0x000029
165
166/* Clearing V2 journal superblock */
167#define PR_0_CLEAR_V2_JOURNAL 0x00002A
168
169/* Run journal anyway */
170#define PR_0_JOURNAL_RUN 0x00002B
171
172/* Run journal anyway by default */
173#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C
174
175/* Backup journal inode blocks */
176#define PR_0_BACKUP_JNL 0x00002D
177
178/* Reserved blocks w/o resize_inode */
179#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E
180
181/* Resize_inode not enabled, but resize inode is non-zero */
182#define PR_0_CLEAR_RESIZE_INODE 0x00002F
183
184/* Resize inode invalid */
185#define PR_0_RESIZE_INODE_INVALID 0x000030
186
187/*
188 * Pass 1 errors
189 */
190
191/* Pass 1: Checking inodes, blocks, and sizes */
192#define PR_1_PASS_HEADER 0x010000
193
194/* Root directory is not an inode */
195#define PR_1_ROOT_NO_DIR 0x010001
196
197/* Root directory has dtime set */
198#define PR_1_ROOT_DTIME 0x010002
199
200/* Reserved inode has bad mode */
201#define PR_1_RESERVED_BAD_MODE 0x010003
202
203/* Deleted inode has zero dtime */
204#define PR_1_ZERO_DTIME 0x010004
205
206/* Inode in use, but dtime set */
207#define PR_1_SET_DTIME 0x010005
208
209/* Zero-length directory */
210#define PR_1_ZERO_LENGTH_DIR 0x010006
211
212/* Block bitmap conflicts with some other fs block */
213#define PR_1_BB_CONFLICT 0x010007
214
215/* Inode bitmap conflicts with some other fs block */
216#define PR_1_IB_CONFLICT 0x010008
217
218/* Inode table conflicts with some other fs block */
219#define PR_1_ITABLE_CONFLICT 0x010009
220
221/* Block bitmap is on a bad block */
222#define PR_1_BB_BAD_BLOCK 0x01000A
223
224/* Inode bitmap is on a bad block */
225#define PR_1_IB_BAD_BLOCK 0x01000B
226
227/* Inode has incorrect i_size */
228#define PR_1_BAD_I_SIZE 0x01000C
229
230/* Inode has incorrect i_blocks */
231#define PR_1_BAD_I_BLOCKS 0x01000D
232
233/* Illegal block number in inode */
234#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E
235
236/* Block number overlaps fs metadata */
237#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F
238
239/* Inode has illegal blocks (latch question) */
240#define PR_1_INODE_BLOCK_LATCH 0x010010
241
242/* Too many bad blocks in inode */
243#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011
244
245/* Illegal block number in bad block inode */
246#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012
247
248/* Bad block inode has illegal blocks (latch question) */
249#define PR_1_INODE_BBLOCK_LATCH 0x010013
250
251/* Duplicate or bad blocks in use! */
252#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014
253
254/* Bad block used as bad block indirect block */
255#define PR_1_BBINODE_BAD_METABLOCK 0x010015
256
257/* Inconsistency can't be fixed prompt */
258#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016
259
260/* Bad primary block */
261#define PR_1_BAD_PRIMARY_BLOCK 0x010017
262
263/* Bad primary block prompt */
264#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018
265
266/* Bad primary superblock */
267#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019
268
269/* Bad primary block group descriptors */
270#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A
271
272/* Bad superblock in group */
273#define PR_1_BAD_SUPERBLOCK 0x01001B
274
275/* Bad block group descriptors in group */
276#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C
277
278/* Block claimed for no reason */
279#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D
280
281/* Error allocating blocks for relocating metadata */
282#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E
283
284/* Error allocating block buffer during relocation process */
285#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F
286
287/* Relocating metadata group information from X to Y */
288#define PR_1_RELOC_FROM_TO 0x010020
289
290/* Relocating metatdata group information to X */
291#define PR_1_RELOC_TO 0x010021
292
293/* Block read error during relocation process */
294#define PR_1_RELOC_READ_ERR 0x010022
295
296/* Block write error during relocation process */
297#define PR_1_RELOC_WRITE_ERR 0x010023
298
299/* Error allocating inode bitmap */
300#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024
301
302/* Error allocating block bitmap */
303#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025
304
305/* Error allocating icount structure */
306#define PR_1_ALLOCATE_ICOUNT 0x010026
307
308/* Error allocating dbcount */
309#define PR_1_ALLOCATE_DBCOUNT 0x010027
310
311/* Error while scanning inodes */
312#define PR_1_ISCAN_ERROR 0x010028
313
314/* Error while iterating over blocks */
315#define PR_1_BLOCK_ITERATE 0x010029
316
317/* Error while storing inode count information */
318#define PR_1_ICOUNT_STORE 0x01002A
319
320/* Error while storing directory block information */
321#define PR_1_ADD_DBLOCK 0x01002B
322
323/* Error while reading inode (for clearing) */
324#define PR_1_READ_INODE 0x01002C
325
326/* Suppress messages prompt */
327#define PR_1_SUPPRESS_MESSAGES 0x01002D
328
329/* Imagic flag set on an inode when filesystem doesn't support it */
330#define PR_1_SET_IMAGIC 0x01002F
331
332/* Immutable flag set on a device or socket inode */
333#define PR_1_SET_IMMUTABLE 0x010030
334
335/* Compression flag set on a non-compressed filesystem */
336#define PR_1_COMPR_SET 0x010031
337
338/* Non-zero size on on device, fifo or socket inode */
339#define PR_1_SET_NONZSIZE 0x010032
340
341/* Filesystem revision is 0, but feature flags are set */
342#define PR_1_FS_REV_LEVEL 0x010033
343
344/* Journal inode not in use, needs clearing */
345#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034
346
347/* Journal inode has wrong mode */
348#define PR_1_JOURNAL_BAD_MODE 0x010035
349
350/* Inode that was part of orphan linked list */
351#define PR_1_LOW_DTIME 0x010036
352
353/* Latch question which asks how to deal with low dtime inodes */
354#define PR_1_ORPHAN_LIST_REFUGEES 0x010037
355
356/* Error allocating refcount structure */
357#define PR_1_ALLOCATE_REFCOUNT 0x010038
358
359/* Error reading Extended Attribute block */
360#define PR_1_READ_EA_BLOCK 0x010039
361
362/* Invalid Extended Attribute block */
363#define PR_1_BAD_EA_BLOCK 0x01003A
364
365/* Error reading Extended Attribute block while fixing refcount -- abort */
366#define PR_1_EXTATTR_READ_ABORT 0x01003B
367
368/* Extended attribute reference count incorrect */
369#define PR_1_EXTATTR_REFCOUNT 0x01003C
370
371/* Error writing Extended Attribute block while fixing refcount */
372#define PR_1_EXTATTR_WRITE 0x01003D
373
374/* Multiple EA blocks not supported */
375#define PR_1_EA_MULTI_BLOCK 0x01003E
376
377/* Error allocating EA region allocation structure */
378#define PR_1_EA_ALLOC_REGION 0x01003F
379
380/* Error EA allocation collision */
381#define PR_1_EA_ALLOC_COLLISION 0x010040
382
383/* Bad extended attribute name */
384#define PR_1_EA_BAD_NAME 0x010041
385
386/* Bad extended attribute value */
387#define PR_1_EA_BAD_VALUE 0x010042
388
389/* Inode too big (latch question) */
390#define PR_1_INODE_TOOBIG 0x010043
391
392/* Directory too big */
393#define PR_1_TOOBIG_DIR 0x010044
394
395/* Regular file too big */
396#define PR_1_TOOBIG_REG 0x010045
397
398/* Symlink too big */
399#define PR_1_TOOBIG_SYMLINK 0x010046
400
401/* INDEX_FL flag set on a non-HTREE filesystem */
402#define PR_1_HTREE_SET 0x010047
403
404/* INDEX_FL flag set on a non-directory */
405#define PR_1_HTREE_NODIR 0x010048
406
407/* Invalid root node in HTREE directory */
408#define PR_1_HTREE_BADROOT 0x010049
409
410/* Unsupported hash version in HTREE directory */
411#define PR_1_HTREE_HASHV 0x01004A
412
413/* Incompatible flag in HTREE root node */
414#define PR_1_HTREE_INCOMPAT 0x01004B
415
416/* HTREE too deep */
417#define PR_1_HTREE_DEPTH 0x01004C
418
419/* Bad block has indirect block that conflicts with filesystem block */
420#define PR_1_BB_FS_BLOCK 0x01004D
421
422/* Resize inode failed */
423#define PR_1_RESIZE_INODE_CREATE 0x01004E
424
425/* inode->i_size is too long */
426#define PR_1_EXTRA_ISIZE 0x01004F
427
428/* attribute name is too long */
429#define PR_1_ATTR_NAME_LEN 0x010050
430
431/* wrong EA value offset */
432#define PR_1_ATTR_VALUE_OFFSET 0x010051
433
434/* wrong EA blocknumber */
435#define PR_1_ATTR_VALUE_BLOCK 0x010052
436
437/* wrong EA value size */
438#define PR_1_ATTR_VALUE_SIZE 0x010053
439
440/* wrong EA hash value */
441#define PR_1_ATTR_HASH 0x010054
442
443/*
444 * Pass 1b errors
445 */
446
447/* Pass 1B: Rescan for duplicate/bad blocks */
448#define PR_1B_PASS_HEADER 0x011000
449
450/* Duplicate/bad block(s) header */
451#define PR_1B_DUP_BLOCK_HEADER 0x011001
452
453/* Duplicate/bad block(s) in inode */
454#define PR_1B_DUP_BLOCK 0x011002
455
456/* Duplicate/bad block(s) end */
457#define PR_1B_DUP_BLOCK_END 0x011003
458
459/* Error while scanning inodes */
460#define PR_1B_ISCAN_ERROR 0x011004
461
462/* Error allocating inode bitmap */
463#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005
464
465/* Error while iterating over blocks */
466#define PR_1B_BLOCK_ITERATE 0x0110006
467
468/* Error adjusting EA refcount */
469#define PR_1B_ADJ_EA_REFCOUNT 0x0110007
470
471
472/* Pass 1C: Scan directories for inodes with dup blocks. */
473#define PR_1C_PASS_HEADER 0x012000
474
475
476/* Pass 1D: Reconciling duplicate blocks */
477#define PR_1D_PASS_HEADER 0x013000
478
479/* File has duplicate blocks */
480#define PR_1D_DUP_FILE 0x013001
481
482/* List of files sharing duplicate blocks */
483#define PR_1D_DUP_FILE_LIST 0x013002
484
485/* File sharing blocks with filesystem metadata */
486#define PR_1D_SHARE_METADATA 0x013003
487
488/* Report of how many duplicate/bad inodes */
489#define PR_1D_NUM_DUP_INODES 0x013004
490
491/* Duplicated blocks already reassigned or cloned. */
492#define PR_1D_DUP_BLOCKS_DEALT 0x013005
493
494/* Clone duplicate/bad blocks? */
495#define PR_1D_CLONE_QUESTION 0x013006
496
497/* Delete file? */
498#define PR_1D_DELETE_QUESTION 0x013007
499
500/* Couldn't clone file (error) */
501#define PR_1D_CLONE_ERROR 0x013008
502
503/*
504 * Pass 2 errors
505 */
506
507/* Pass 2: Checking directory structure */
508#define PR_2_PASS_HEADER 0x020000
509
510/* Bad inode number for '.' */
511#define PR_2_BAD_INODE_DOT 0x020001
512
513/* Directory entry has bad inode number */
514#define PR_2_BAD_INO 0x020002
515
516/* Directory entry has deleted or unused inode */
517#define PR_2_UNUSED_INODE 0x020003
518
519/* Directry entry is link to '.' */
520#define PR_2_LINK_DOT 0x020004
521
522/* Directory entry points to inode now located in a bad block */
523#define PR_2_BB_INODE 0x020005
524
525/* Directory entry contains a link to a directory */
526#define PR_2_LINK_DIR 0x020006
527
528/* Directory entry contains a link to the root directry */
529#define PR_2_LINK_ROOT 0x020007
530
531/* Directory entry has illegal characters in its name */
532#define PR_2_BAD_NAME 0x020008
533
534/* Missing '.' in directory inode */
535#define PR_2_MISSING_DOT 0x020009
536
537/* Missing '..' in directory inode */
538#define PR_2_MISSING_DOT_DOT 0x02000A
539
540/* First entry in directory inode doesn't contain '.' */
541#define PR_2_1ST_NOT_DOT 0x02000B
542
543/* Second entry in directory inode doesn't contain '..' */
544#define PR_2_2ND_NOT_DOT_DOT 0x02000C
545
546/* i_faddr should be zero */
547#define PR_2_FADDR_ZERO 0x02000D
548
549/* i_file_acl should be zero */
550#define PR_2_FILE_ACL_ZERO 0x02000E
551
552/* i_dir_acl should be zero */
553#define PR_2_DIR_ACL_ZERO 0x02000F
554
555/* i_frag should be zero */
556#define PR_2_FRAG_ZERO 0x020010
557
558/* i_fsize should be zero */
559#define PR_2_FSIZE_ZERO 0x020011
560
561/* inode has bad mode */
562#define PR_2_BAD_MODE 0x020012
563
564/* directory corrupted */
565#define PR_2_DIR_CORRUPTED 0x020013
566
567/* filename too long */
568#define PR_2_FILENAME_LONG 0x020014
569
570/* Directory inode has a missing block (hole) */
571#define PR_2_DIRECTORY_HOLE 0x020015
572
573/* '.' is not NULL terminated */
574#define PR_2_DOT_NULL_TERM 0x020016
575
576/* '..' is not NULL terminated */
577#define PR_2_DOT_DOT_NULL_TERM 0x020017
578
579/* Illegal character device in inode */
580#define PR_2_BAD_CHAR_DEV 0x020018
581
582/* Illegal block device in inode */
583#define PR_2_BAD_BLOCK_DEV 0x020019
584
585/* Duplicate '.' entry */
586#define PR_2_DUP_DOT 0x02001A
587
588/* Duplicate '..' entry */
589#define PR_2_DUP_DOT_DOT 0x02001B
590
591/* Internal error: couldn't find dir_info */
592#define PR_2_NO_DIRINFO 0x02001C
593
594/* Final rec_len is wrong */
595#define PR_2_FINAL_RECLEN 0x02001D
596
597/* Error allocating icount structure */
598#define PR_2_ALLOCATE_ICOUNT 0x02001E
599
600/* Error iterating over directory blocks */
601#define PR_2_DBLIST_ITERATE 0x02001F
602
603/* Error reading directory block */
604#define PR_2_READ_DIRBLOCK 0x020020
605
606/* Error writing directory block */
607#define PR_2_WRITE_DIRBLOCK 0x020021
608
609/* Error allocating new directory block */
610#define PR_2_ALLOC_DIRBOCK 0x020022
611
612/* Error deallocating inode */
613#define PR_2_DEALLOC_INODE 0x020023
614
615/* Directory entry for '.' is big. Split? */
616#define PR_2_SPLIT_DOT 0x020024
617
618/* Illegal FIFO */
619#define PR_2_BAD_FIFO 0x020025
620
621/* Illegal socket */
622#define PR_2_BAD_SOCKET 0x020026
623
624/* Directory filetype not set */
625#define PR_2_SET_FILETYPE 0x020027
626
627/* Directory filetype incorrect */
628#define PR_2_BAD_FILETYPE 0x020028
629
630/* Directory filetype set when it shouldn't be */
631#define PR_2_CLEAR_FILETYPE 0x020029
632
633/* Directory filename can't be zero-length */
634#define PR_2_NULL_NAME 0x020030
635
636/* Invalid symlink */
637#define PR_2_INVALID_SYMLINK 0x020031
638
639/* i_file_acl (extended attribute) is bad */
640#define PR_2_FILE_ACL_BAD 0x020032
641
642/* Filesystem contains large files, but has no such flag in sb */
643#define PR_2_FEATURE_LARGE_FILES 0x020033
644
645/* Node in HTREE directory not referenced */
646#define PR_2_HTREE_NOTREF 0x020034
647
648/* Node in HTREE directory referenced twice */
649#define PR_2_HTREE_DUPREF 0x020035
650
651/* Node in HTREE directory has bad min hash */
652#define PR_2_HTREE_MIN_HASH 0x020036
653
654/* Node in HTREE directory has bad max hash */
655#define PR_2_HTREE_MAX_HASH 0x020037
656
657/* Clear invalid HTREE directory */
658#define PR_2_HTREE_CLEAR 0x020038
659
660/* Clear the htree flag forcibly */
661/* #define PR_2_HTREE_FCLR 0x020039 */
662
663/* Bad block in htree interior node */
664#define PR_2_HTREE_BADBLK 0x02003A
665
666/* Error adjusting EA refcount */
667#define PR_2_ADJ_EA_REFCOUNT 0x02003B
668
669/* Invalid HTREE root node */
670#define PR_2_HTREE_BAD_ROOT 0x02003C
671
672/* Invalid HTREE limit */
673#define PR_2_HTREE_BAD_LIMIT 0x02003D
674
675/* Invalid HTREE count */
676#define PR_2_HTREE_BAD_COUNT 0x02003E
677
678/* HTREE interior node has out-of-order hashes in table */
679#define PR_2_HTREE_HASH_ORDER 0x02003F
680
681/* Node in HTREE directory has bad depth */
682#define PR_2_HTREE_BAD_DEPTH 0x020040
683
684/* Duplicate directory entry found */
685#define PR_2_DUPLICATE_DIRENT 0x020041
686
687/* Non-unique filename found */
688#define PR_2_NON_UNIQUE_FILE 0x020042
689
690/* Duplicate directory entry found */
691#define PR_2_REPORT_DUP_DIRENT 0x020043
692
693/*
694 * Pass 3 errors
695 */
696
697/* Pass 3: Checking directory connectivity */
698#define PR_3_PASS_HEADER 0x030000
699
700/* Root inode not allocated */
701#define PR_3_NO_ROOT_INODE 0x030001
702
703/* No room in lost+found */
704#define PR_3_EXPAND_LF_DIR 0x030002
705
706/* Unconnected directory inode */
707#define PR_3_UNCONNECTED_DIR 0x030003
708
709/* /lost+found not found */
710#define PR_3_NO_LF_DIR 0x030004
711
712/* .. entry is incorrect */
713#define PR_3_BAD_DOT_DOT 0x030005
714
715/* Bad or non-existent /lost+found. Cannot reconnect */
716#define PR_3_NO_LPF 0x030006
717
718/* Could not expand /lost+found */
719#define PR_3_CANT_EXPAND_LPF 0x030007
720
721/* Could not reconnect inode */
722#define PR_3_CANT_RECONNECT 0x030008
723
724/* Error while trying to find /lost+found */
725#define PR_3_ERR_FIND_LPF 0x030009
726
727/* Error in ext2fs_new_block while creating /lost+found */
728#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A
729
730/* Error in ext2fs_new_inode while creating /lost+found */
731#define PR_3_ERR_LPF_NEW_INODE 0x03000B
732
733/* Error in ext2fs_new_dir_block while creating /lost+found */
734#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C
735
736/* Error while writing directory block for /lost+found */
737#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D
738
739/* Error while adjusting inode count */
740#define PR_3_ADJUST_INODE 0x03000E
741
742/* Couldn't fix parent directory -- error */
743#define PR_3_FIX_PARENT_ERR 0x03000F
744
745/* Couldn't fix parent directory -- couldn't find it */
746#define PR_3_FIX_PARENT_NOFIND 0x030010
747
748/* Error allocating inode bitmap */
749#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011
750
751/* Error creating root directory */
752#define PR_3_CREATE_ROOT_ERROR 0x030012
753
754/* Error creating lost and found directory */
755#define PR_3_CREATE_LPF_ERROR 0x030013
756
757/* Root inode is not directory; aborting */
758#define PR_3_ROOT_NOT_DIR_ABORT 0x030014
759
760/* Cannot proceed without a root inode. */
761#define PR_3_NO_ROOT_INODE_ABORT 0x030015
762
763/* Internal error: couldn't find dir_info */
764#define PR_3_NO_DIRINFO 0x030016
765
766/* Lost+found is not a directory */
767#define PR_3_LPF_NOTDIR 0x030017
768
769/*
770 * Pass 3a --- rehashing diretories
771 */
772/* Pass 3a: Reindexing directories */
773#define PR_3A_PASS_HEADER 0x031000
774
775/* Error iterating over directories */
776#define PR_3A_OPTIMIZE_ITER 0x031001
777
778/* Error rehash directory */
779#define PR_3A_OPTIMIZE_DIR_ERR 0x031002
780
781/* Rehashing dir header */
782#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003
783
784/* Rehashing directory %d */
785#define PR_3A_OPTIMIZE_DIR 0x031004
786
787/* Rehashing dir end */
788#define PR_3A_OPTIMIZE_DIR_END 0x031005
789
790/*
791 * Pass 4 errors
792 */
793
794/* Pass 4: Checking reference counts */
795#define PR_4_PASS_HEADER 0x040000
796
797/* Unattached zero-length inode */
798#define PR_4_ZERO_LEN_INODE 0x040001
799
800/* Unattached inode */
801#define PR_4_UNATTACHED_INODE 0x040002
802
803/* Inode ref count wrong */
804#define PR_4_BAD_REF_COUNT 0x040003
805
806/* Inconsistent inode count information cached */
807#define PR_4_INCONSISTENT_COUNT 0x040004
808
809/*
810 * Pass 5 errors
811 */
812
813/* Pass 5: Checking group summary information */
814#define PR_5_PASS_HEADER 0x050000
815
816/* Padding at end of inode bitmap is not set. */
817#define PR_5_INODE_BMAP_PADDING 0x050001
818
819/* Padding at end of block bitmap is not set. */
820#define PR_5_BLOCK_BMAP_PADDING 0x050002
821
822/* Block bitmap differences header */
823#define PR_5_BLOCK_BITMAP_HEADER 0x050003
824
825/* Block not used, but marked in bitmap */
826#define PR_5_BLOCK_UNUSED 0x050004
827
828/* Block used, but not marked used in bitmap */
829#define PR_5_BLOCK_USED 0x050005
830
831/* Block bitmap differences end */
832#define PR_5_BLOCK_BITMAP_END 0x050006
833
834/* Inode bitmap differences header */
835#define PR_5_INODE_BITMAP_HEADER 0x050007
836
837/* Inode not used, but marked in bitmap */
838#define PR_5_INODE_UNUSED 0x050008
839
840/* Inode used, but not marked used in bitmap */
841#define PR_5_INODE_USED 0x050009
842
843/* Inode bitmap differences end */
844#define PR_5_INODE_BITMAP_END 0x05000A
845
846/* Free inodes count for group wrong */
847#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B
848
849/* Directories count for group wrong */
850#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C
851
852/* Free inodes count wrong */
853#define PR_5_FREE_INODE_COUNT 0x05000D
854
855/* Free blocks count for group wrong */
856#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E
857
858/* Free blocks count wrong */
859#define PR_5_FREE_BLOCK_COUNT 0x05000F
860
861/* Programming error: bitmap endpoints don't match */
862#define PR_5_BMAP_ENDPOINTS 0x050010
863
864/* Internal error: fudging end of bitmap */
865#define PR_5_FUDGE_BITMAP_ERROR 0x050011
866
867/* Error copying in replacement inode bitmap */
868#define PR_5_COPY_IBITMAP_ERROR 0x050012
869
870/* Error copying in replacement block bitmap */
871#define PR_5_COPY_BBITMAP_ERROR 0x050013
872
873/* Block range not used, but marked in bitmap */
874#define PR_5_BLOCK_RANGE_UNUSED 0x050014
875
876/* Block range used, but not marked used in bitmap */
877#define PR_5_BLOCK_RANGE_USED 0x050015
878
879/* Inode range not used, but marked in bitmap */
880#define PR_5_INODE_RANGE_UNUSED 0x050016
881
882/* Inode rangeused, but not marked used in bitmap */
883#define PR_5_INODE_RANGE_USED 0x050017
884
885/*
886 * Function declarations
887 */
888int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
889int end_problem_latch(e2fsck_t ctx, int mask);
890int set_latch_flags(int mask, int setflags, int clearflags);
891int get_latch_flags(int mask, int *value);
892void clear_problem_context(struct problem_context *ctx);
893
894/* message.c */
895void print_e2fsck_message(e2fsck_t ctx, const char *msg,
896 struct problem_context *pctx, int first);
897
diff --git a/e2fsprogs/e2fsck/problemP.h b/e2fsprogs/e2fsck/problemP.h
new file mode 100644
index 000000000..329056b95
--- /dev/null
+++ b/e2fsprogs/e2fsck/problemP.h
@@ -0,0 +1,42 @@
1/*
2 * problemP.h --- Private header file for fix_problem()
3 *
4 * Copyright 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12struct e2fsck_problem {
13 problem_t e2p_code;
14 const char * e2p_description;
15 char prompt;
16 int flags;
17 problem_t second_code;
18};
19
20struct latch_descr {
21 int latch_code;
22 problem_t question;
23 problem_t end_message;
24 int flags;
25};
26
27#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
28#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
29#define PR_NO_DEFAULT 0x000004 /* Default to no */
30#define PR_MSG_ONLY 0x000008 /* Print message only */
31
32/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
33
34#define PR_FATAL 0x001000 /* Fatal error */
35#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
36 /* ask another */
37#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
38#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
39#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
40#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
41#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
42
diff --git a/e2fsprogs/e2fsck/recovery.c b/e2fsprogs/e2fsck/recovery.c
new file mode 100644
index 000000000..edfa9bc83
--- /dev/null
+++ b/e2fsprogs/e2fsck/recovery.c
@@ -0,0 +1,586 @@
1/*
2 * linux/fs/recovery.c
3 *
4 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
5 *
6 * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
7 *
8 * This file is part of the Linux kernel and is made available under
9 * the terms of the GNU General Public License, version 2, or at your
10 * option, any later version, incorporated herein by reference.
11 *
12 * Journal recovery routines for the generic filesystem journaling code;
13 * part of the ext2fs journaling system.
14 */
15
16#ifndef __KERNEL__
17#include "jfs_user.h"
18#else
19#include <linux/time.h>
20#include <linux/fs.h>
21#include <linux/jbd.h>
22#include <linux/errno.h>
23#include <linux/slab.h>
24#endif
25
26/*
27 * Maintain information about the progress of the recovery job, so that
28 * the different passes can carry information between them.
29 */
30struct recovery_info
31{
32 tid_t start_transaction;
33 tid_t end_transaction;
34
35 int nr_replays;
36 int nr_revokes;
37 int nr_revoke_hits;
38};
39
40enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
41static int do_one_pass(journal_t *journal,
42 struct recovery_info *info, enum passtype pass);
43static int scan_revoke_records(journal_t *, struct buffer_head *,
44 tid_t, struct recovery_info *);
45
46#ifdef __KERNEL__
47
48/* Release readahead buffers after use */
49void journal_brelse_array(struct buffer_head *b[], int n)
50{
51 while (--n >= 0)
52 brelse (b[n]);
53}
54
55
56/*
57 * When reading from the journal, we are going through the block device
58 * layer directly and so there is no readahead being done for us. We
59 * need to implement any readahead ourselves if we want it to happen at
60 * all. Recovery is basically one long sequential read, so make sure we
61 * do the IO in reasonably large chunks.
62 *
63 * This is not so critical that we need to be enormously clever about
64 * the readahead size, though. 128K is a purely arbitrary, good-enough
65 * fixed value.
66 */
67
68#define MAXBUF 8
69static int do_readahead(journal_t *journal, unsigned int start)
70{
71 int err;
72 unsigned int max, nbufs, next;
73 unsigned long blocknr;
74 struct buffer_head *bh;
75
76 struct buffer_head * bufs[MAXBUF];
77
78 /* Do up to 128K of readahead */
79 max = start + (128 * 1024 / journal->j_blocksize);
80 if (max > journal->j_maxlen)
81 max = journal->j_maxlen;
82
83 /* Do the readahead itself. We'll submit MAXBUF buffer_heads at
84 * a time to the block device IO layer. */
85
86 nbufs = 0;
87
88 for (next = start; next < max; next++) {
89 err = journal_bmap(journal, next, &blocknr);
90
91 if (err) {
92 printk (KERN_ERR "JBD: bad block at offset %u\n",
93 next);
94 goto failed;
95 }
96
97 bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
98 if (!bh) {
99 err = -ENOMEM;
100 goto failed;
101 }
102
103 if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
104 bufs[nbufs++] = bh;
105 if (nbufs == MAXBUF) {
106 ll_rw_block(READ, nbufs, bufs);
107 journal_brelse_array(bufs, nbufs);
108 nbufs = 0;
109 }
110 } else
111 brelse(bh);
112 }
113
114 if (nbufs)
115 ll_rw_block(READ, nbufs, bufs);
116 err = 0;
117
118failed:
119 if (nbufs)
120 journal_brelse_array(bufs, nbufs);
121 return err;
122}
123
124#endif /* __KERNEL__ */
125
126
127/*
128 * Read a block from the journal
129 */
130
131static int jread(struct buffer_head **bhp, journal_t *journal,
132 unsigned int offset)
133{
134 int err;
135 unsigned long blocknr;
136 struct buffer_head *bh;
137
138 *bhp = NULL;
139
140 J_ASSERT (offset < journal->j_maxlen);
141
142 err = journal_bmap(journal, offset, &blocknr);
143
144 if (err) {
145 printk (KERN_ERR "JBD: bad block at offset %u\n",
146 offset);
147 return err;
148 }
149
150 bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
151 if (!bh)
152 return -ENOMEM;
153
154 if (!buffer_uptodate(bh)) {
155 /* If this is a brand new buffer, start readahead.
156 Otherwise, we assume we are already reading it. */
157 if (!buffer_req(bh))
158 do_readahead(journal, offset);
159 wait_on_buffer(bh);
160 }
161
162 if (!buffer_uptodate(bh)) {
163 printk (KERN_ERR "JBD: Failed to read block at offset %u\n",
164 offset);
165 brelse(bh);
166 return -EIO;
167 }
168
169 *bhp = bh;
170 return 0;
171}
172
173
174/*
175 * Count the number of in-use tags in a journal descriptor block.
176 */
177
178static int count_tags(struct buffer_head *bh, int size)
179{
180 char * tagp;
181 journal_block_tag_t * tag;
182 int nr = 0;
183
184 tagp = &bh->b_data[sizeof(journal_header_t)];
185
186 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
187 tag = (journal_block_tag_t *) tagp;
188
189 nr++;
190 tagp += sizeof(journal_block_tag_t);
191 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
192 tagp += 16;
193
194 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
195 break;
196 }
197
198 return nr;
199}
200
201
202/* Make sure we wrap around the log correctly! */
203#define wrap(journal, var) \
204do { \
205 if (var >= (journal)->j_last) \
206 var -= ((journal)->j_last - (journal)->j_first); \
207} while (0)
208
209/**
210 * int journal_recover(journal_t *journal) - recovers a on-disk journal
211 * @journal: the journal to recover
212 *
213 * The primary function for recovering the log contents when mounting a
214 * journaled device.
215 *
216 * Recovery is done in three passes. In the first pass, we look for the
217 * end of the log. In the second, we assemble the list of revoke
218 * blocks. In the third and final pass, we replay any un-revoked blocks
219 * in the log.
220 */
221int journal_recover(journal_t *journal)
222{
223 int err;
224 journal_superblock_t * sb;
225
226 struct recovery_info info;
227
228 memset(&info, 0, sizeof(info));
229 sb = journal->j_superblock;
230
231 /*
232 * The journal superblock's s_start field (the current log head)
233 * is always zero if, and only if, the journal was cleanly
234 * unmounted.
235 */
236
237 if (!sb->s_start) {
238 jbd_debug(1, "No recovery required, last transaction %d\n",
239 ntohl(sb->s_sequence));
240 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
241 return 0;
242 }
243
244 err = do_one_pass(journal, &info, PASS_SCAN);
245 if (!err)
246 err = do_one_pass(journal, &info, PASS_REVOKE);
247 if (!err)
248 err = do_one_pass(journal, &info, PASS_REPLAY);
249
250 jbd_debug(0, "JBD: recovery, exit status %d, "
251 "recovered transactions %u to %u\n",
252 err, info.start_transaction, info.end_transaction);
253 jbd_debug(0, "JBD: Replayed %d and revoked %d/%d blocks\n",
254 info.nr_replays, info.nr_revoke_hits, info.nr_revokes);
255
256 /* Restart the log at the next transaction ID, thus invalidating
257 * any existing commit records in the log. */
258 journal->j_transaction_sequence = ++info.end_transaction;
259
260 journal_clear_revoke(journal);
261 sync_blockdev(journal->j_fs_dev);
262 return err;
263}
264
265/**
266 * int journal_skip_recovery() - Start journal and wipe exiting records
267 * @journal: journal to startup
268 *
269 * Locate any valid recovery information from the journal and set up the
270 * journal structures in memory to ignore it (presumably because the
271 * caller has evidence that it is out of date).
272 * This function does'nt appear to be exorted..
273 *
274 * We perform one pass over the journal to allow us to tell the user how
275 * much recovery information is being erased, and to let us initialise
276 * the journal transaction sequence numbers to the next unused ID.
277 */
278int journal_skip_recovery(journal_t *journal)
279{
280 int err;
281 journal_superblock_t * sb;
282
283 struct recovery_info info;
284
285 memset (&info, 0, sizeof(info));
286 sb = journal->j_superblock;
287
288 err = do_one_pass(journal, &info, PASS_SCAN);
289
290 if (err) {
291 printk(KERN_ERR "JBD: error %d scanning journal\n", err);
292 ++journal->j_transaction_sequence;
293 } else {
294#ifdef __CONFIG_JBD_DEBUG__E2FS
295 int dropped = info.end_transaction - ntohl(sb->s_sequence);
296#endif
297 jbd_debug(0,
298 "JBD: ignoring %d transaction%s from the journal.\n",
299 dropped, (dropped == 1) ? "" : "s");
300 journal->j_transaction_sequence = ++info.end_transaction;
301 }
302
303 journal->j_tail = 0;
304 return err;
305}
306
307static int do_one_pass(journal_t *journal,
308 struct recovery_info *info, enum passtype pass)
309{
310 unsigned int first_commit_ID, next_commit_ID;
311 unsigned long next_log_block;
312 int err, success = 0;
313 journal_superblock_t * sb;
314 journal_header_t * tmp;
315 struct buffer_head * bh;
316 unsigned int sequence;
317 int blocktype;
318
319 /* Precompute the maximum metadata descriptors in a descriptor block */
320 int MAX_BLOCKS_PER_DESC;
321 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
322 / sizeof(journal_block_tag_t));
323
324 /*
325 * First thing is to establish what we expect to find in the log
326 * (in terms of transaction IDs), and where (in terms of log
327 * block offsets): query the superblock.
328 */
329
330 sb = journal->j_superblock;
331 next_commit_ID = ntohl(sb->s_sequence);
332 next_log_block = ntohl(sb->s_start);
333
334 first_commit_ID = next_commit_ID;
335 if (pass == PASS_SCAN)
336 info->start_transaction = first_commit_ID;
337
338 jbd_debug(1, "Starting recovery pass %d\n", pass);
339
340 /*
341 * Now we walk through the log, transaction by transaction,
342 * making sure that each transaction has a commit block in the
343 * expected place. Each complete transaction gets replayed back
344 * into the main filesystem.
345 */
346
347 while (1) {
348 int flags;
349 char * tagp;
350 journal_block_tag_t * tag;
351 struct buffer_head * obh;
352 struct buffer_head * nbh;
353
354 /* If we already know where to stop the log traversal,
355 * check right now that we haven't gone past the end of
356 * the log. */
357
358 if (pass != PASS_SCAN)
359 if (tid_geq(next_commit_ID, info->end_transaction))
360 break;
361
362 jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n",
363 next_commit_ID, next_log_block, journal->j_last);
364
365 /* Skip over each chunk of the transaction looking
366 * either the next descriptor block or the final commit
367 * record. */
368
369 jbd_debug(3, "JBD: checking block %ld\n", next_log_block);
370 err = jread(&bh, journal, next_log_block);
371 if (err)
372 goto failed;
373
374 next_log_block++;
375 wrap(journal, next_log_block);
376
377 /* What kind of buffer is it?
378 *
379 * If it is a descriptor block, check that it has the
380 * expected sequence number. Otherwise, we're all done
381 * here. */
382
383 tmp = (journal_header_t *)bh->b_data;
384
385 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
386 brelse(bh);
387 break;
388 }
389
390 blocktype = ntohl(tmp->h_blocktype);
391 sequence = ntohl(tmp->h_sequence);
392 jbd_debug(3, "Found magic %d, sequence %d\n",
393 blocktype, sequence);
394
395 if (sequence != next_commit_ID) {
396 brelse(bh);
397 break;
398 }
399
400 /* OK, we have a valid descriptor block which matches
401 * all of the sequence number checks. What are we going
402 * to do with it? That depends on the pass... */
403
404 switch(blocktype) {
405 case JFS_DESCRIPTOR_BLOCK:
406 /* If it is a valid descriptor block, replay it
407 * in pass REPLAY; otherwise, just skip over the
408 * blocks it describes. */
409 if (pass != PASS_REPLAY) {
410 next_log_block +=
411 count_tags(bh, journal->j_blocksize);
412 wrap(journal, next_log_block);
413 brelse(bh);
414 continue;
415 }
416
417 /* A descriptor block: we can now write all of
418 * the data blocks. Yay, useful work is finally
419 * getting done here! */
420
421 tagp = &bh->b_data[sizeof(journal_header_t)];
422 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
423 <= journal->j_blocksize) {
424 unsigned long io_block;
425
426 tag = (journal_block_tag_t *) tagp;
427 flags = ntohl(tag->t_flags);
428
429 io_block = next_log_block++;
430 wrap(journal, next_log_block);
431 err = jread(&obh, journal, io_block);
432 if (err) {
433 /* Recover what we can, but
434 * report failure at the end. */
435 success = err;
436 printk (KERN_ERR
437 "JBD: IO error %d recovering "
438 "block %ld in log\n",
439 err, io_block);
440 } else {
441 unsigned long blocknr;
442
443 J_ASSERT(obh != NULL);
444 blocknr = ntohl(tag->t_blocknr);
445
446 /* If the block has been
447 * revoked, then we're all done
448 * here. */
449 if (journal_test_revoke
450 (journal, blocknr,
451 next_commit_ID)) {
452 brelse(obh);
453 ++info->nr_revoke_hits;
454 goto skip_write;
455 }
456
457 /* Find a buffer for the new
458 * data being restored */
459 nbh = __getblk(journal->j_fs_dev,
460 blocknr,
461 journal->j_blocksize);
462 if (nbh == NULL) {
463 printk(KERN_ERR
464 "JBD: Out of memory "
465 "during recovery.\n");
466 err = -ENOMEM;
467 brelse(bh);
468 brelse(obh);
469 goto failed;
470 }
471
472 lock_buffer(nbh);
473 memcpy(nbh->b_data, obh->b_data,
474 journal->j_blocksize);
475 if (flags & JFS_FLAG_ESCAPE) {
476 *((unsigned int *)bh->b_data) =
477 htonl(JFS_MAGIC_NUMBER);
478 }
479
480 BUFFER_TRACE(nbh, "marking dirty");
481 set_buffer_uptodate(nbh);
482 mark_buffer_dirty(nbh);
483 BUFFER_TRACE(nbh, "marking uptodate");
484 ++info->nr_replays;
485 /* ll_rw_block(WRITE, 1, &nbh); */
486 unlock_buffer(nbh);
487 brelse(obh);
488 brelse(nbh);
489 }
490
491 skip_write:
492 tagp += sizeof(journal_block_tag_t);
493 if (!(flags & JFS_FLAG_SAME_UUID))
494 tagp += 16;
495
496 if (flags & JFS_FLAG_LAST_TAG)
497 break;
498 }
499
500 brelse(bh);
501 continue;
502
503 case JFS_COMMIT_BLOCK:
504 /* Found an expected commit block: not much to
505 * do other than move on to the next sequence
506 * number. */
507 brelse(bh);
508 next_commit_ID++;
509 continue;
510
511 case JFS_REVOKE_BLOCK:
512 /* If we aren't in the REVOKE pass, then we can
513 * just skip over this block. */
514 if (pass != PASS_REVOKE) {
515 brelse(bh);
516 continue;
517 }
518
519 err = scan_revoke_records(journal, bh,
520 next_commit_ID, info);
521 brelse(bh);
522 if (err)
523 goto failed;
524 continue;
525
526 default:
527 jbd_debug(3, "Unrecognised magic %d, end of scan.\n",
528 blocktype);
529 goto done;
530 }
531 }
532
533 done:
534 /*
535 * We broke out of the log scan loop: either we came to the
536 * known end of the log or we found an unexpected block in the
537 * log. If the latter happened, then we know that the "current"
538 * transaction marks the end of the valid log.
539 */
540
541 if (pass == PASS_SCAN)
542 info->end_transaction = next_commit_ID;
543 else {
544 /* It's really bad news if different passes end up at
545 * different places (but possible due to IO errors). */
546 if (info->end_transaction != next_commit_ID) {
547 printk (KERN_ERR "JBD: recovery pass %d ended at "
548 "transaction %u, expected %u\n",
549 pass, next_commit_ID, info->end_transaction);
550 if (!success)
551 success = -EIO;
552 }
553 }
554
555 return success;
556
557 failed:
558 return err;
559}
560
561
562/* Scan a revoke record, marking all blocks mentioned as revoked. */
563
564static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
565 tid_t sequence, struct recovery_info *info)
566{
567 journal_revoke_header_t *header;
568 int offset, max;
569
570 header = (journal_revoke_header_t *) bh->b_data;
571 offset = sizeof(journal_revoke_header_t);
572 max = ntohl(header->r_count);
573
574 while (offset < max) {
575 unsigned long blocknr;
576 int err;
577
578 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
579 offset += 4;
580 err = journal_set_revoke(journal, blocknr, sequence);
581 if (err)
582 return err;
583 ++info->nr_revokes;
584 }
585 return 0;
586}
diff --git a/e2fsprogs/e2fsck/region.c b/e2fsprogs/e2fsck/region.c
new file mode 100644
index 000000000..9ccb684a3
--- /dev/null
+++ b/e2fsprogs/e2fsck/region.c
@@ -0,0 +1,204 @@
1/*
2 * region.c --- code which manages allocations within a region.
3 *
4 * Copyright (C) 2001 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#if HAVE_UNISTD_H
13#include <unistd.h>
14#endif
15#include <string.h>
16
17#include "e2fsck.h"
18
19struct region_el {
20 region_addr_t start;
21 region_addr_t end;
22 struct region_el *next;
23};
24
25struct region_struct {
26 region_addr_t min;
27 region_addr_t max;
28 struct region_el *allocated;
29};
30
31region_t region_create(region_addr_t min, region_addr_t max)
32{
33 region_t region;
34
35 region = malloc(sizeof(struct region_struct));
36 if (!region)
37 return NULL;
38 memset(region, 0, sizeof(struct region_struct));
39 region->min = min;
40 region->max = max;
41 return region;
42}
43
44void region_free(region_t region)
45{
46 struct region_el *r, *next;
47
48 for (r = region->allocated; r; r = next) {
49 next = r->next;
50 free(r);
51 }
52 memset(region, 0, sizeof(struct region_struct));
53 free(region);
54}
55
56int region_allocate(region_t region, region_addr_t start, int n)
57{
58 struct region_el *r, *new_region, *prev, *next;
59 region_addr_t end;
60
61 end = start+n;
62 if ((start < region->min) || (end > region->max))
63 return -1;
64 if (n == 0)
65 return 1;
66
67 /*
68 * Search through the linked list. If we find that it
69 * conflicts witih something that's already allocated, return
70 * 1; if we can find an existing region which we can grow, do
71 * so. Otherwise, stop when we find the appropriate place
72 * insert a new region element into the linked list.
73 */
74 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
75 if (((start >= r->start) && (start < r->end)) ||
76 ((end > r->start) && (end <= r->end)) ||
77 ((start <= r->start) && (end >= r->end)))
78 return 1;
79 if (end == r->start) {
80 r->start = start;
81 return 0;
82 }
83 if (start == r->end) {
84 if ((next = r->next)) {
85 if (end > next->start)
86 return 1;
87 if (end == next->start) {
88 r->end = next->end;
89 r->next = next->next;
90 free(next);
91 return 0;
92 }
93 }
94 r->end = end;
95 return 0;
96 }
97 if (start < r->start)
98 break;
99 }
100 /*
101 * Insert a new region element structure into the linked list
102 */
103 new_region = malloc(sizeof(struct region_el));
104 if (!new_region)
105 return -1;
106 new_region->start = start;
107 new_region->end = start + n;
108 new_region->next = r;
109 if (prev)
110 prev->next = new_region;
111 else
112 region->allocated = new_region;
113 return 0;
114}
115
116#ifdef TEST_PROGRAM
117#include <stdio.h>
118
119#define BCODE_END 0
120#define BCODE_CREATE 1
121#define BCODE_FREE 2
122#define BCODE_ALLOCATE 3
123#define BCODE_PRINT 4
124
125int bcode_program[] = {
126 BCODE_CREATE, 1, 1001,
127 BCODE_PRINT,
128 BCODE_ALLOCATE, 10, 10,
129 BCODE_ALLOCATE, 30, 10,
130 BCODE_PRINT,
131 BCODE_ALLOCATE, 1, 15,
132 BCODE_ALLOCATE, 15, 8,
133 BCODE_ALLOCATE, 1, 20,
134 BCODE_ALLOCATE, 1, 8,
135 BCODE_PRINT,
136 BCODE_ALLOCATE, 40, 10,
137 BCODE_PRINT,
138 BCODE_ALLOCATE, 22, 5,
139 BCODE_PRINT,
140 BCODE_ALLOCATE, 27, 3,
141 BCODE_PRINT,
142 BCODE_ALLOCATE, 20, 2,
143 BCODE_PRINT,
144 BCODE_ALLOCATE, 49, 1,
145 BCODE_ALLOCATE, 50, 5,
146 BCODE_ALLOCATE, 9, 2,
147 BCODE_ALLOCATE, 9, 1,
148 BCODE_PRINT,
149 BCODE_FREE,
150 BCODE_END
151};
152
153void region_print(region_t region, FILE *f)
154{
155 struct region_el *r;
156 int i = 0;
157
158 fprintf(f, "Printing region (min=%d. max=%d)\n\t", region->min,
159 region->max);
160 for (r = region->allocated; r; r = r->next) {
161 fprintf(f, "(%d, %d) ", r->start, r->end);
162 if (++i >= 8)
163 fprintf(f, "\n\t");
164 }
165 fprintf(f, "\n");
166}
167
168int main(int argc, char **argv)
169{
170 region_t r;
171 int pc = 0, ret;
172 region_addr_t start, end, len;
173
174
175 while (1) {
176 switch (bcode_program[pc++]) {
177 case BCODE_END:
178 exit(0);
179 case BCODE_CREATE:
180 start = bcode_program[pc++];
181 end = bcode_program[pc++];
182 printf("Creating region with args(%d, %d)\n",
183 start, end);
184 r = region_create(start, end);
185 if (!r) {
186 fprintf(stderr, "Couldn't create region.\n");
187 exit(1);
188 }
189 break;
190 case BCODE_ALLOCATE:
191 start = bcode_program[pc++];
192 end = bcode_program[pc++];
193 ret = region_allocate(r, start, end);
194 printf("Region_allocate(%d, %d) returns %d\n",
195 start, end, ret);
196 break;
197 case BCODE_PRINT:
198 region_print(r, stdout);
199 break;
200 }
201 }
202}
203
204#endif /* TEST_PROGRAM */
diff --git a/e2fsprogs/e2fsck/rehash.c b/e2fsprogs/e2fsck/rehash.c
new file mode 100644
index 000000000..727e08c2b
--- /dev/null
+++ b/e2fsprogs/e2fsck/rehash.c
@@ -0,0 +1,840 @@
1/*
2 * rehash.c --- rebuild hash tree directories
3 *
4 * Copyright (C) 2002 Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 * This algorithm is designed for simplicity of implementation and to
12 * pack the directory as much as possible. It however requires twice
13 * as much memory as the size of the directory. The maximum size
14 * directory supported using a 4k blocksize is roughly a gigabyte, and
15 * so there may very well be problems with machines that don't have
16 * virtual memory, and obscenely large directories.
17 *
18 * An alternate algorithm which is much more disk intensive could be
19 * written, and probably will need to be written in the future. The
20 * design goals of such an algorithm are: (a) use (roughly) constant
21 * amounts of memory, no matter how large the directory, (b) the
22 * directory must be safe at all times, even if e2fsck is interrupted
23 * in the middle, (c) we must use minimal amounts of extra disk
24 * blocks. This pretty much requires an incremental approach, where
25 * we are reading from one part of the directory, and inserting into
26 * the front half. So the algorithm will have to keep track of a
27 * moving block boundary between the new tree and the old tree, and
28 * files will need to be moved from the old directory and inserted
29 * into the new tree. If the new directory requires space which isn't
30 * yet available, blocks from the beginning part of the old directory
31 * may need to be moved to the end of the directory to make room for
32 * the new tree:
33 *
34 * --------------------------------------------------------
35 * | new tree | | old tree |
36 * --------------------------------------------------------
37 * ^ ptr ^ptr
38 * tail new head old
39 *
40 * This is going to be a pain in the tuckus to implement, and will
41 * require a lot more disk accesses. So I'm going to skip it for now;
42 * it's only really going to be an issue for really, really big
43 * filesystems (when we reach the level of tens of millions of files
44 * in a single directory). It will probably be easier to simply
45 * require that e2fsck use VM first.
46 */
47
48#include <string.h>
49#include <ctype.h>
50#include <errno.h>
51#include "e2fsck.h"
52#include "problem.h"
53
54struct fill_dir_struct {
55 char *buf;
56 struct ext2_inode *inode;
57 int err;
58 e2fsck_t ctx;
59 struct hash_entry *harray;
60 int max_array, num_array;
61 int dir_size;
62 int compress;
63 ino_t parent;
64};
65
66struct hash_entry {
67 ext2_dirhash_t hash;
68 ext2_dirhash_t minor_hash;
69 struct ext2_dir_entry *dir;
70};
71
72struct out_dir {
73 int num;
74 int max;
75 char *buf;
76 ext2_dirhash_t *hashes;
77};
78
79static int fill_dir_block(ext2_filsys fs,
80 blk_t *block_nr,
81 e2_blkcnt_t blockcnt,
82 blk_t ref_block EXT2FS_ATTR((unused)),
83 int ref_offset EXT2FS_ATTR((unused)),
84 void *priv_data)
85{
86 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
87 struct hash_entry *new_array, *ent;
88 struct ext2_dir_entry *dirent;
89 char *dir;
90 unsigned int offset, dir_offset;
91
92 if (blockcnt < 0)
93 return 0;
94
95 offset = blockcnt * fs->blocksize;
96 if (offset + fs->blocksize > fd->inode->i_size) {
97 fd->err = EXT2_ET_DIR_CORRUPTED;
98 return BLOCK_ABORT;
99 }
100 dir = (fd->buf+offset);
101 if (HOLE_BLKADDR(*block_nr)) {
102 memset(dir, 0, fs->blocksize);
103 dirent = (struct ext2_dir_entry *) dir;
104 dirent->rec_len = fs->blocksize;
105 } else {
106 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
107 if (fd->err)
108 return BLOCK_ABORT;
109 }
110 /* While the directory block is "hot", index it. */
111 dir_offset = 0;
112 while (dir_offset < fs->blocksize) {
113 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
114 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
115 (dirent->rec_len < 8) ||
116 ((dirent->rec_len % 4) != 0) ||
117 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
118 fd->err = EXT2_ET_DIR_CORRUPTED;
119 return BLOCK_ABORT;
120 }
121 dir_offset += dirent->rec_len;
122 if (dirent->inode == 0)
123 continue;
124 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
125 (dirent->name[0] == '.'))
126 continue;
127 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
128 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
129 fd->parent = dirent->inode;
130 continue;
131 }
132 if (fd->num_array >= fd->max_array) {
133 new_array = realloc(fd->harray,
134 sizeof(struct hash_entry) * (fd->max_array+500));
135 if (!new_array) {
136 fd->err = ENOMEM;
137 return BLOCK_ABORT;
138 }
139 fd->harray = new_array;
140 fd->max_array += 500;
141 }
142 ent = fd->harray + fd->num_array++;
143 ent->dir = dirent;
144 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
145 if (fd->compress)
146 ent->hash = ent->minor_hash = 0;
147 else {
148 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
149 dirent->name,
150 dirent->name_len & 0xFF,
151 fs->super->s_hash_seed,
152 &ent->hash, &ent->minor_hash);
153 if (fd->err)
154 return BLOCK_ABORT;
155 }
156 }
157
158 return 0;
159}
160
161/* Used for sorting the hash entry */
162static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b)
163{
164 const struct hash_entry *he_a = (const struct hash_entry *) a;
165 const struct hash_entry *he_b = (const struct hash_entry *) b;
166 int ret;
167 int min_len;
168
169 min_len = he_a->dir->name_len;
170 if (min_len > he_b->dir->name_len)
171 min_len = he_b->dir->name_len;
172
173 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
174 if (ret == 0) {
175 if (he_a->dir->name_len > he_b->dir->name_len)
176 ret = 1;
177 else if (he_a->dir->name_len < he_b->dir->name_len)
178 ret = -1;
179 else
180 ret = he_b->dir->inode - he_a->dir->inode;
181 }
182 return ret;
183}
184
185/* Used for sorting the hash entry */
186static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b)
187{
188 const struct hash_entry *he_a = (const struct hash_entry *) a;
189 const struct hash_entry *he_b = (const struct hash_entry *) b;
190 int ret;
191
192 if (he_a->hash > he_b->hash)
193 ret = 1;
194 else if (he_a->hash < he_b->hash)
195 ret = -1;
196 else {
197 if (he_a->minor_hash > he_b->minor_hash)
198 ret = 1;
199 else if (he_a->minor_hash < he_b->minor_hash)
200 ret = -1;
201 else
202 ret = name_cmp(a, b);
203 }
204 return ret;
205}
206
207static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
208 int blocks)
209{
210 void *new_mem;
211
212 if (outdir->max) {
213 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
214 if (!new_mem)
215 return ENOMEM;
216 outdir->buf = new_mem;
217 new_mem = realloc(outdir->hashes,
218 blocks * sizeof(ext2_dirhash_t));
219 if (!new_mem)
220 return ENOMEM;
221 outdir->hashes = new_mem;
222 } else {
223 outdir->buf = malloc(blocks * fs->blocksize);
224 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
225 outdir->num = 0;
226 }
227 outdir->max = blocks;
228 return 0;
229}
230
231static void free_out_dir(struct out_dir *outdir)
232{
233 if (outdir->buf)
234 free(outdir->buf);
235 if (outdir->hashes)
236 free(outdir->hashes);
237 outdir->max = 0;
238 outdir->num =0;
239}
240
241static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
242 char ** ret)
243{
244 errcode_t retval;
245
246 if (outdir->num >= outdir->max) {
247 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
248 if (retval)
249 return retval;
250 }
251 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
252 memset(*ret, 0, fs->blocksize);
253 return 0;
254}
255
256/*
257 * This function is used to make a unique filename. We do this by
258 * appending ~0, and then incrementing the number. However, we cannot
259 * expand the length of the filename beyond the padding available in
260 * the directory entry.
261 */
262static void mutate_name(char *str, __u16 *len)
263{
264 int i;
265 __u16 l = *len & 0xFF, h = *len & 0xff00;
266
267 /*
268 * First check to see if it looks the name has been mutated
269 * already
270 */
271 for (i = l-1; i > 0; i--) {
272 if (!isdigit(str[i]))
273 break;
274 }
275 if ((i == l-1) || (str[i] != '~')) {
276 if (((l-1) & 3) < 2)
277 l += 2;
278 else
279 l = (l+3) & ~3;
280 str[l-2] = '~';
281 str[l-1] = '0';
282 *len = l | h;
283 return;
284 }
285 for (i = l-1; i >= 0; i--) {
286 if (isdigit(str[i])) {
287 if (str[i] == '9')
288 str[i] = '0';
289 else {
290 str[i]++;
291 return;
292 }
293 continue;
294 }
295 if (i == 1) {
296 if (str[0] == 'z')
297 str[0] = 'A';
298 else if (str[0] == 'Z') {
299 str[0] = '~';
300 str[1] = '0';
301 } else
302 str[0]++;
303 } else if (i > 0) {
304 str[i] = '1';
305 str[i-1] = '~';
306 } else {
307 if (str[0] == '~')
308 str[0] = 'a';
309 else
310 str[0]++;
311 }
312 break;
313 }
314}
315
316static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
317 ext2_ino_t ino,
318 struct fill_dir_struct *fd)
319{
320 struct problem_context pctx;
321 struct hash_entry *ent, *prev;
322 int i, j;
323 int fixed = 0;
324 char new_name[256];
325 __u16 new_len;
326
327 clear_problem_context(&pctx);
328 pctx.ino = ino;
329
330 for (i=1; i < fd->num_array; i++) {
331 ent = fd->harray + i;
332 prev = ent - 1;
333 if (!ent->dir->inode ||
334 ((ent->dir->name_len & 0xFF) !=
335 (prev->dir->name_len & 0xFF)) ||
336 (strncmp(ent->dir->name, prev->dir->name,
337 ent->dir->name_len & 0xFF)))
338 continue;
339 pctx.dirent = ent->dir;
340 if ((ent->dir->inode == prev->dir->inode) &&
341 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
342 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
343 ent->dir->inode = 0;
344 fixed++;
345 continue;
346 }
347 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
348 new_len = ent->dir->name_len;
349 mutate_name(new_name, &new_len);
350 for (j=0; j < fd->num_array; j++) {
351 if ((i==j) ||
352 ((ent->dir->name_len & 0xFF) !=
353 (fd->harray[j].dir->name_len & 0xFF)) ||
354 (strncmp(new_name, fd->harray[j].dir->name,
355 new_len & 0xFF)))
356 continue;
357 mutate_name(new_name, &new_len);
358
359 j = -1;
360 }
361 new_name[new_len & 0xFF] = 0;
362 pctx.str = new_name;
363 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
364 memcpy(ent->dir->name, new_name, new_len & 0xFF);
365 ent->dir->name_len = new_len;
366 ext2fs_dirhash(fs->super->s_def_hash_version,
367 ent->dir->name,
368 ent->dir->name_len & 0xFF,
369 fs->super->s_hash_seed,
370 &ent->hash, &ent->minor_hash);
371 fixed++;
372 }
373 }
374 return fixed;
375}
376
377
378static errcode_t copy_dir_entries(ext2_filsys fs,
379 struct fill_dir_struct *fd,
380 struct out_dir *outdir)
381{
382 errcode_t retval;
383 char *block_start;
384 struct hash_entry *ent;
385 struct ext2_dir_entry *dirent;
386 int i, rec_len, left;
387 ext2_dirhash_t prev_hash;
388 int offset;
389
390 outdir->max = 0;
391 retval = alloc_size_dir(fs, outdir,
392 (fd->dir_size / fs->blocksize) + 2);
393 if (retval)
394 return retval;
395 outdir->num = fd->compress ? 0 : 1;
396 offset = 0;
397 outdir->hashes[0] = 0;
398 prev_hash = 1;
399 if ((retval = get_next_block(fs, outdir, &block_start)))
400 return retval;
401 dirent = (struct ext2_dir_entry *) block_start;
402 left = fs->blocksize;
403 for (i=0; i < fd->num_array; i++) {
404 ent = fd->harray + i;
405 if (ent->dir->inode == 0)
406 continue;
407 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
408 if (rec_len > left) {
409 if (left)
410 dirent->rec_len += left;
411 if ((retval = get_next_block(fs, outdir,
412 &block_start)))
413 return retval;
414 offset = 0;
415 }
416 left = fs->blocksize - offset;
417 dirent = (struct ext2_dir_entry *) (block_start + offset);
418 if (offset == 0) {
419 if (ent->hash == prev_hash)
420 outdir->hashes[outdir->num-1] = ent->hash | 1;
421 else
422 outdir->hashes[outdir->num-1] = ent->hash;
423 }
424 dirent->inode = ent->dir->inode;
425 dirent->name_len = ent->dir->name_len;
426 dirent->rec_len = rec_len;
427 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
428 offset += rec_len;
429 left -= rec_len;
430 if (left < 12) {
431 dirent->rec_len += left;
432 offset += left;
433 left = 0;
434 }
435 prev_hash = ent->hash;
436 }
437 if (left)
438 dirent->rec_len += left;
439
440 return 0;
441}
442
443
444static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
445 ext2_ino_t ino, ext2_ino_t parent)
446{
447 struct ext2_dir_entry *dir;
448 struct ext2_dx_root_info *root;
449 struct ext2_dx_countlimit *limits;
450 int filetype = 0;
451
452 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
453 filetype = EXT2_FT_DIR << 8;
454
455 memset(buf, 0, fs->blocksize);
456 dir = (struct ext2_dir_entry *) buf;
457 dir->inode = ino;
458 dir->name[0] = '.';
459 dir->name_len = 1 | filetype;
460 dir->rec_len = 12;
461 dir = (struct ext2_dir_entry *) (buf + 12);
462 dir->inode = parent;
463 dir->name[0] = '.';
464 dir->name[1] = '.';
465 dir->name_len = 2 | filetype;
466 dir->rec_len = fs->blocksize - 12;
467
468 root = (struct ext2_dx_root_info *) (buf+24);
469 root->reserved_zero = 0;
470 root->hash_version = fs->super->s_def_hash_version;
471 root->info_length = 8;
472 root->indirect_levels = 0;
473 root->unused_flags = 0;
474
475 limits = (struct ext2_dx_countlimit *) (buf+32);
476 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
477 limits->count = 0;
478
479 return root;
480}
481
482
483static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
484{
485 struct ext2_dir_entry *dir;
486 struct ext2_dx_countlimit *limits;
487
488 memset(buf, 0, fs->blocksize);
489 dir = (struct ext2_dir_entry *) buf;
490 dir->inode = 0;
491 dir->rec_len = fs->blocksize;
492
493 limits = (struct ext2_dx_countlimit *) (buf+8);
494 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
495 limits->count = 0;
496
497 return (struct ext2_dx_entry *) limits;
498}
499
500/*
501 * This function takes the leaf nodes which have been written in
502 * outdir, and populates the root node and any necessary interior nodes.
503 */
504static errcode_t calculate_tree(ext2_filsys fs,
505 struct out_dir *outdir,
506 ext2_ino_t ino,
507 ext2_ino_t parent)
508{
509 struct ext2_dx_root_info *root_info;
510 struct ext2_dx_entry *root, *dx_ent = 0;
511 struct ext2_dx_countlimit *root_limit, *limit;
512 errcode_t retval;
513 char * block_start;
514 int i, c1, c2, nblks;
515 int limit_offset, root_offset;
516
517 root_info = set_root_node(fs, outdir->buf, ino, parent);
518 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
519 root_info->info_length;
520 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
521 c1 = root_limit->limit;
522 nblks = outdir->num;
523
524 /* Write out the pointer blocks */
525 if (nblks-1 <= c1) {
526 /* Just write out the root block, and we're done */
527 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
528 for (i=1; i < nblks; i++) {
529 root->block = ext2fs_cpu_to_le32(i);
530 if (i != 1)
531 root->hash =
532 ext2fs_cpu_to_le32(outdir->hashes[i]);
533 root++;
534 c1--;
535 }
536 } else {
537 c2 = 0;
538 limit = 0;
539 root_info->indirect_levels = 1;
540 for (i=1; i < nblks; i++) {
541 if (c1 == 0)
542 return ENOSPC;
543 if (c2 == 0) {
544 if (limit)
545 limit->limit = limit->count =
546 ext2fs_cpu_to_le16(limit->limit);
547 root = (struct ext2_dx_entry *)
548 (outdir->buf + root_offset);
549 root->block = ext2fs_cpu_to_le32(outdir->num);
550 if (i != 1)
551 root->hash =
552 ext2fs_cpu_to_le32(outdir->hashes[i]);
553 if ((retval = get_next_block(fs, outdir,
554 &block_start)))
555 return retval;
556 dx_ent = set_int_node(fs, block_start);
557 limit = (struct ext2_dx_countlimit *) dx_ent;
558 c2 = limit->limit;
559 root_offset += sizeof(struct ext2_dx_entry);
560 c1--;
561 }
562 dx_ent->block = ext2fs_cpu_to_le32(i);
563 if (c2 != limit->limit)
564 dx_ent->hash =
565 ext2fs_cpu_to_le32(outdir->hashes[i]);
566 dx_ent++;
567 c2--;
568 }
569 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
570 limit->limit = ext2fs_cpu_to_le16(limit->limit);
571 }
572 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
573 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
574 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
575
576 return 0;
577}
578
579struct write_dir_struct {
580 struct out_dir *outdir;
581 errcode_t err;
582 e2fsck_t ctx;
583 int cleared;
584};
585
586/*
587 * Helper function which writes out a directory block.
588 */
589static int write_dir_block(ext2_filsys fs,
590 blk_t *block_nr,
591 e2_blkcnt_t blockcnt,
592 blk_t ref_block EXT2FS_ATTR((unused)),
593 int ref_offset EXT2FS_ATTR((unused)),
594 void *priv_data)
595{
596 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
597 blk_t blk;
598 char *dir;
599
600 if (*block_nr == 0)
601 return 0;
602 if (blockcnt >= wd->outdir->num) {
603 e2fsck_read_bitmaps(wd->ctx);
604 blk = *block_nr;
605 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
606 ext2fs_block_alloc_stats(fs, blk, -1);
607 *block_nr = 0;
608 wd->cleared++;
609 return BLOCK_CHANGED;
610 }
611 if (blockcnt < 0)
612 return 0;
613
614 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
615 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
616 if (wd->err)
617 return BLOCK_ABORT;
618 return 0;
619}
620
621static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
622 struct out_dir *outdir,
623 ext2_ino_t ino, int compress)
624{
625 struct write_dir_struct wd;
626 errcode_t retval;
627 struct ext2_inode inode;
628
629 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
630 if (retval)
631 return retval;
632
633 wd.outdir = outdir;
634 wd.err = 0;
635 wd.ctx = ctx;
636 wd.cleared = 0;
637
638 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
639 write_dir_block, &wd);
640 if (retval)
641 return retval;
642 if (wd.err)
643 return wd.err;
644
645 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
646 if (compress)
647 inode.i_flags &= ~EXT2_INDEX_FL;
648 else
649 inode.i_flags |= EXT2_INDEX_FL;
650 inode.i_size = outdir->num * fs->blocksize;
651 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
652 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
653
654 return 0;
655}
656
657errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
658{
659 ext2_filsys fs = ctx->fs;
660 errcode_t retval;
661 struct ext2_inode inode;
662 char *dir_buf = 0;
663 struct fill_dir_struct fd;
664 struct out_dir outdir;
665
666 outdir.max = outdir.num = 0;
667 outdir.buf = 0;
668 outdir.hashes = 0;
669 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
670
671 retval = ENOMEM;
672 fd.harray = 0;
673 dir_buf = malloc(inode.i_size);
674 if (!dir_buf)
675 goto errout;
676
677 fd.max_array = inode.i_size / 32;
678 fd.num_array = 0;
679 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
680 if (!fd.harray)
681 goto errout;
682
683 fd.ctx = ctx;
684 fd.buf = dir_buf;
685 fd.inode = &inode;
686 fd.err = 0;
687 fd.dir_size = 0;
688 fd.compress = 0;
689 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
690 (inode.i_size / fs->blocksize) < 2)
691 fd.compress = 1;
692 fd.parent = 0;
693
694 /* Read in the entire directory into memory */
695 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
696 fill_dir_block, &fd);
697 if (fd.err) {
698 retval = fd.err;
699 goto errout;
700 }
701
702#if 0
703 printf("%d entries (%d bytes) found in inode %d\n",
704 fd.num_array, fd.dir_size, ino);
705#endif
706
707 /* Sort the list */
708resort:
709 if (fd.compress)
710 qsort(fd.harray+2, fd.num_array-2,
711 sizeof(struct hash_entry), name_cmp);
712 else
713 qsort(fd.harray, fd.num_array,
714 sizeof(struct hash_entry), hash_cmp);
715
716 /*
717 * Look for duplicates
718 */
719 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
720 goto resort;
721
722 if (ctx->options & E2F_OPT_NO) {
723 retval = 0;
724 goto errout;
725 }
726
727 /*
728 * Copy the directory entries. In a htree directory these
729 * will become the leaf nodes.
730 */
731 retval = copy_dir_entries(fs, &fd, &outdir);
732 if (retval)
733 goto errout;
734
735 free(dir_buf); dir_buf = 0;
736
737 if (!fd.compress) {
738 /* Calculate the interior nodes */
739 retval = calculate_tree(fs, &outdir, ino, fd.parent);
740 if (retval)
741 goto errout;
742 }
743
744 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
745 if (retval)
746 goto errout;
747
748errout:
749 if (dir_buf)
750 free(dir_buf);
751 if (fd.harray)
752 free(fd.harray);
753
754 free_out_dir(&outdir);
755 return retval;
756}
757
758void e2fsck_rehash_directories(e2fsck_t ctx)
759{
760 struct problem_context pctx;
761#ifdef RESOURCE_TRACK
762 struct resource_track rtrack;
763#endif
764 struct dir_info *dir;
765 ext2_u32_iterate iter;
766 ext2_ino_t ino;
767 errcode_t retval;
768 int i, cur, max, all_dirs, dir_index, first = 1;
769
770#ifdef RESOURCE_TRACK
771 init_resource_track(&rtrack);
772#endif
773
774 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
775
776 if (!ctx->dirs_to_hash && !all_dirs)
777 return;
778
779 e2fsck_get_lost_and_found(ctx, 0);
780
781 clear_problem_context(&pctx);
782
783 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
784 cur = 0;
785 if (all_dirs) {
786 i = 0;
787 max = e2fsck_get_num_dirinfo(ctx);
788 } else {
789 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
790 &iter);
791 if (retval) {
792 pctx.errcode = retval;
793 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
794 return;
795 }
796 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
797 }
798 while (1) {
799 if (all_dirs) {
800 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
801 break;
802 ino = dir->ino;
803 } else {
804 if (!ext2fs_u32_list_iterate(iter, &ino))
805 break;
806 }
807 if (ino == ctx->lost_and_found)
808 continue;
809 pctx.dir = ino;
810 if (first) {
811 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
812 first = 0;
813 }
814#if 0
815 fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx);
816#endif
817 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
818 if (pctx.errcode) {
819 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
820 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
821 }
822 if (ctx->progress && !ctx->progress_fd)
823 e2fsck_simple_progress(ctx, "Rebuilding directory",
824 100.0 * (float) (++cur) / (float) max, ino);
825 }
826 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
827 if (!all_dirs)
828 ext2fs_u32_list_iterate_end(iter);
829
830 if (ctx->dirs_to_hash)
831 ext2fs_u32_list_free(ctx->dirs_to_hash);
832 ctx->dirs_to_hash = 0;
833
834#ifdef RESOURCE_TRACK
835 if (ctx->options & E2F_OPT_TIME2) {
836 e2fsck_clear_progbar(ctx);
837 print_resource_track("Pass 3A", &rtrack);
838 }
839#endif
840}
diff --git a/e2fsprogs/e2fsck/revoke.c b/e2fsprogs/e2fsck/revoke.c
new file mode 100644
index 000000000..388bf5b5d
--- /dev/null
+++ b/e2fsprogs/e2fsck/revoke.c
@@ -0,0 +1,640 @@
1/*
2 * linux/fs/revoke.c
3 *
4 * Written by Stephen C. Tweedie <sct@redhat.com>, 2000
5 *
6 * Copyright 2000 Red Hat corp --- All Rights Reserved
7 *
8 * This file is part of the Linux kernel and is made available under
9 * the terms of the GNU General Public License, version 2, or at your
10 * option, any later version, incorporated herein by reference.
11 *
12 * Journal revoke routines for the generic filesystem journaling code;
13 * part of the ext2fs journaling system.
14 *
15 * Revoke is the mechanism used to prevent old log records for deleted
16 * metadata from being replayed on top of newer data using the same
17 * blocks. The revoke mechanism is used in two separate places:
18 *
19 * + Commit: during commit we write the entire list of the current
20 * transaction's revoked blocks to the journal
21 *
22 * + Recovery: during recovery we record the transaction ID of all
23 * revoked blocks. If there are multiple revoke records in the log
24 * for a single block, only the last one counts, and if there is a log
25 * entry for a block beyond the last revoke, then that log entry still
26 * gets replayed.
27 *
28 * We can get interactions between revokes and new log data within a
29 * single transaction:
30 *
31 * Block is revoked and then journaled:
32 * The desired end result is the journaling of the new block, so we
33 * cancel the revoke before the transaction commits.
34 *
35 * Block is journaled and then revoked:
36 * The revoke must take precedence over the write of the block, so we
37 * need either to cancel the journal entry or to write the revoke
38 * later in the log than the log block. In this case, we choose the
39 * latter: journaling a block cancels any revoke record for that block
40 * in the current transaction, so any revoke for that block in the
41 * transaction must have happened after the block was journaled and so
42 * the revoke must take precedence.
43 *
44 * Block is revoked and then written as data:
45 * The data write is allowed to succeed, but the revoke is _not_
46 * cancelled. We still need to prevent old log records from
47 * overwriting the new data. We don't even need to clear the revoke
48 * bit here.
49 *
50 * Revoke information on buffers is a tri-state value:
51 *
52 * RevokeValid clear: no cached revoke status, need to look it up
53 * RevokeValid set, Revoked clear:
54 * buffer has not been revoked, and cancel_revoke
55 * need do nothing.
56 * RevokeValid set, Revoked set:
57 * buffer has been revoked.
58 */
59
60#ifndef __KERNEL__
61#include "jfs_user.h"
62#else
63#include <linux/sched.h>
64#include <linux/fs.h>
65#include <linux/jbd.h>
66#include <linux/errno.h>
67#include <linux/slab.h>
68#include <linux/locks.h>
69#include <linux/list.h>
70#include <linux/smp_lock.h>
71#include <linux/init.h>
72#endif
73
74static kmem_cache_t *revoke_record_cache;
75static kmem_cache_t *revoke_table_cache;
76
77/* Each revoke record represents one single revoked block. During
78 journal replay, this involves recording the transaction ID of the
79 last transaction to revoke this block. */
80
81struct jbd_revoke_record_s
82{
83 struct list_head hash;
84 tid_t sequence; /* Used for recovery only */
85 unsigned long blocknr;
86};
87
88
89/* The revoke table is just a simple hash table of revoke records. */
90struct jbd_revoke_table_s
91{
92 /* It is conceivable that we might want a larger hash table
93 * for recovery. Must be a power of two. */
94 int hash_size;
95 int hash_shift;
96 struct list_head *hash_table;
97};
98
99
100#ifdef __KERNEL__
101static void write_one_revoke_record(journal_t *, transaction_t *,
102 struct journal_head **, int *,
103 struct jbd_revoke_record_s *);
104static void flush_descriptor(journal_t *, struct journal_head *, int);
105#endif
106
107/* Utility functions to maintain the revoke table */
108
109/* Borrowed from buffer.c: this is a tried and tested block hash function */
110static inline int hash(journal_t *journal, unsigned long block)
111{
112 struct jbd_revoke_table_s *table = journal->j_revoke;
113 int hash_shift = table->hash_shift;
114
115 return ((block << (hash_shift - 6)) ^
116 (block >> 13) ^
117 (block << (hash_shift - 12))) & (table->hash_size - 1);
118}
119
120static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
121 tid_t seq)
122{
123 struct list_head *hash_list;
124 struct jbd_revoke_record_s *record;
125
126#ifdef __KERNEL__
127repeat:
128#endif
129 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
130 if (!record)
131 goto oom;
132
133 record->sequence = seq;
134 record->blocknr = blocknr;
135 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
136 list_add(&record->hash, hash_list);
137 return 0;
138
139oom:
140#ifdef __KERNEL__
141 if (!journal_oom_retry)
142 return -ENOMEM;
143 jbd_debug(1, "ENOMEM in " __FUNCTION__ ", retrying.\n");
144 current->policy |= SCHED_YIELD;
145 schedule();
146 goto repeat;
147#else
148 return -ENOMEM;
149#endif
150}
151
152/* Find a revoke record in the journal's hash table. */
153
154static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
155 unsigned long blocknr)
156{
157 struct list_head *hash_list;
158 struct jbd_revoke_record_s *record;
159
160 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
161
162 record = (struct jbd_revoke_record_s *) hash_list->next;
163 while (&(record->hash) != hash_list) {
164 if (record->blocknr == blocknr)
165 return record;
166 record = (struct jbd_revoke_record_s *) record->hash.next;
167 }
168 return NULL;
169}
170
171int __init journal_init_revoke_caches(void)
172{
173 revoke_record_cache = kmem_cache_create("revoke_record",
174 sizeof(struct jbd_revoke_record_s),
175 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
176 if (revoke_record_cache == 0)
177 return -ENOMEM;
178
179 revoke_table_cache = kmem_cache_create("revoke_table",
180 sizeof(struct jbd_revoke_table_s),
181 0, 0, NULL, NULL);
182 if (revoke_table_cache == 0) {
183 kmem_cache_destroy(revoke_record_cache);
184 revoke_record_cache = NULL;
185 return -ENOMEM;
186 }
187 return 0;
188}
189
190void journal_destroy_revoke_caches(void)
191{
192 kmem_cache_destroy(revoke_record_cache);
193 revoke_record_cache = 0;
194 kmem_cache_destroy(revoke_table_cache);
195 revoke_table_cache = 0;
196}
197
198/* Initialise the revoke table for a given journal to a given size. */
199
200int journal_init_revoke(journal_t *journal, int hash_size)
201{
202 int shift, tmp;
203
204 J_ASSERT (journal->j_revoke == NULL);
205
206 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
207 if (!journal->j_revoke)
208 return -ENOMEM;
209
210 /* Check that the hash_size is a power of two */
211 J_ASSERT ((hash_size & (hash_size-1)) == 0);
212
213 journal->j_revoke->hash_size = hash_size;
214
215 shift = 0;
216 tmp = hash_size;
217 while((tmp >>= 1UL) != 0UL)
218 shift++;
219 journal->j_revoke->hash_shift = shift;
220
221 journal->j_revoke->hash_table =
222 kmalloc(hash_size * sizeof(struct list_head), GFP_KERNEL);
223 if (!journal->j_revoke->hash_table) {
224 kmem_cache_free(revoke_table_cache, journal->j_revoke);
225 journal->j_revoke = NULL;
226 return -ENOMEM;
227 }
228
229 for (tmp = 0; tmp < hash_size; tmp++)
230 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
231
232 return 0;
233}
234
235/* Destoy a journal's revoke table. The table must already be empty! */
236
237void journal_destroy_revoke(journal_t *journal)
238{
239 struct jbd_revoke_table_s *table;
240 struct list_head *hash_list;
241 int i;
242
243 table = journal->j_revoke;
244 if (!table)
245 return;
246
247 for (i=0; i<table->hash_size; i++) {
248 hash_list = &table->hash_table[i];
249 J_ASSERT (list_empty(hash_list));
250 }
251
252 kfree(table->hash_table);
253 kmem_cache_free(revoke_table_cache, table);
254 journal->j_revoke = NULL;
255}
256
257
258#ifdef __KERNEL__
259
260/*
261 * journal_revoke: revoke a given buffer_head from the journal. This
262 * prevents the block from being replayed during recovery if we take a
263 * crash after this current transaction commits. Any subsequent
264 * metadata writes of the buffer in this transaction cancel the
265 * revoke.
266 *
267 * Note that this call may block --- it is up to the caller to make
268 * sure that there are no further calls to journal_write_metadata
269 * before the revoke is complete. In ext3, this implies calling the
270 * revoke before clearing the block bitmap when we are deleting
271 * metadata.
272 *
273 * Revoke performs a journal_forget on any buffer_head passed in as a
274 * parameter, but does _not_ forget the buffer_head if the bh was only
275 * found implicitly.
276 *
277 * bh_in may not be a journalled buffer - it may have come off
278 * the hash tables without an attached journal_head.
279 *
280 * If bh_in is non-zero, journal_revoke() will decrement its b_count
281 * by one.
282 */
283
284int journal_revoke(handle_t *handle, unsigned long blocknr,
285 struct buffer_head *bh_in)
286{
287 struct buffer_head *bh = NULL;
288 journal_t *journal;
289 kdev_t dev;
290 int err;
291
292 if (bh_in)
293 BUFFER_TRACE(bh_in, "enter");
294
295 journal = handle->h_transaction->t_journal;
296 if (!journal_set_features(journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE)){
297 J_ASSERT (!"Cannot set revoke feature!");
298 return -EINVAL;
299 }
300
301 dev = journal->j_fs_dev;
302 bh = bh_in;
303
304 if (!bh) {
305 bh = get_hash_table(dev, blocknr, journal->j_blocksize);
306 if (bh)
307 BUFFER_TRACE(bh, "found on hash");
308 }
309#ifdef JBD_EXPENSIVE_CHECKING
310 else {
311 struct buffer_head *bh2;
312
313 /* If there is a different buffer_head lying around in
314 * memory anywhere... */
315 bh2 = get_hash_table(dev, blocknr, journal->j_blocksize);
316 if (bh2) {
317 /* ... and it has RevokeValid status... */
318 if ((bh2 != bh) &&
319 test_bit(BH_RevokeValid, &bh2->b_state))
320 /* ...then it better be revoked too,
321 * since it's illegal to create a revoke
322 * record against a buffer_head which is
323 * not marked revoked --- that would
324 * risk missing a subsequent revoke
325 * cancel. */
326 J_ASSERT_BH(bh2, test_bit(BH_Revoked, &
327 bh2->b_state));
328 __brelse(bh2);
329 }
330 }
331#endif
332
333 /* We really ought not ever to revoke twice in a row without
334 first having the revoke cancelled: it's illegal to free a
335 block twice without allocating it in between! */
336 if (bh) {
337 J_ASSERT_BH(bh, !test_bit(BH_Revoked, &bh->b_state));
338 set_bit(BH_Revoked, &bh->b_state);
339 set_bit(BH_RevokeValid, &bh->b_state);
340 if (bh_in) {
341 BUFFER_TRACE(bh_in, "call journal_forget");
342 journal_forget(handle, bh_in);
343 } else {
344 BUFFER_TRACE(bh, "call brelse");
345 __brelse(bh);
346 }
347 }
348
349 lock_journal(journal);
350 jbd_debug(2, "insert revoke for block %lu, bh_in=%p\n", blocknr, bh_in);
351 err = insert_revoke_hash(journal, blocknr,
352 handle->h_transaction->t_tid);
353 unlock_journal(journal);
354 BUFFER_TRACE(bh_in, "exit");
355 return err;
356}
357
358/*
359 * Cancel an outstanding revoke. For use only internally by the
360 * journaling code (called from journal_get_write_access).
361 *
362 * We trust the BH_Revoked bit on the buffer if the buffer is already
363 * being journaled: if there is no revoke pending on the buffer, then we
364 * don't do anything here.
365 *
366 * This would break if it were possible for a buffer to be revoked and
367 * discarded, and then reallocated within the same transaction. In such
368 * a case we would have lost the revoked bit, but when we arrived here
369 * the second time we would still have a pending revoke to cancel. So,
370 * do not trust the Revoked bit on buffers unless RevokeValid is also
371 * set.
372 *
373 * The caller must have the journal locked.
374 */
375int journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
376{
377 struct jbd_revoke_record_s *record;
378 journal_t *journal = handle->h_transaction->t_journal;
379 int need_cancel;
380 int did_revoke = 0; /* akpm: debug */
381 struct buffer_head *bh = jh2bh(jh);
382
383 jbd_debug(4, "journal_head %p, cancelling revoke\n", jh);
384
385 /* Is the existing Revoke bit valid? If so, we trust it, and
386 * only perform the full cancel if the revoke bit is set. If
387 * not, we can't trust the revoke bit, and we need to do the
388 * full search for a revoke record. */
389 if (test_and_set_bit(BH_RevokeValid, &bh->b_state))
390 need_cancel = (test_and_clear_bit(BH_Revoked, &bh->b_state));
391 else {
392 need_cancel = 1;
393 clear_bit(BH_Revoked, &bh->b_state);
394 }
395
396 if (need_cancel) {
397 record = find_revoke_record(journal, bh->b_blocknr);
398 if (record) {
399 jbd_debug(4, "cancelled existing revoke on "
400 "blocknr %lu\n", bh->b_blocknr);
401 list_del(&record->hash);
402 kmem_cache_free(revoke_record_cache, record);
403 did_revoke = 1;
404 }
405 }
406
407#ifdef JBD_EXPENSIVE_CHECKING
408 /* There better not be one left behind by now! */
409 record = find_revoke_record(journal, bh->b_blocknr);
410 J_ASSERT_JH(jh, record == NULL);
411#endif
412
413 /* Finally, have we just cleared revoke on an unhashed
414 * buffer_head? If so, we'd better make sure we clear the
415 * revoked status on any hashed alias too, otherwise the revoke
416 * state machine will get very upset later on. */
417 if (need_cancel && !bh->b_pprev) {
418 struct buffer_head *bh2;
419 bh2 = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size);
420 if (bh2) {
421 clear_bit(BH_Revoked, &bh2->b_state);
422 __brelse(bh2);
423 }
424 }
425
426 return did_revoke;
427}
428
429
430/*
431 * Write revoke records to the journal for all entries in the current
432 * revoke hash, deleting the entries as we go.
433 *
434 * Called with the journal lock held.
435 */
436
437void journal_write_revoke_records(journal_t *journal,
438 transaction_t *transaction)
439{
440 struct journal_head *descriptor;
441 struct jbd_revoke_record_s *record;
442 struct jbd_revoke_table_s *revoke;
443 struct list_head *hash_list;
444 int i, offset, count;
445
446 descriptor = NULL;
447 offset = 0;
448 count = 0;
449 revoke = journal->j_revoke;
450
451 for (i = 0; i < revoke->hash_size; i++) {
452 hash_list = &revoke->hash_table[i];
453
454 while (!list_empty(hash_list)) {
455 record = (struct jbd_revoke_record_s *)
456 hash_list->next;
457 write_one_revoke_record(journal, transaction,
458 &descriptor, &offset,
459 record);
460 count++;
461 list_del(&record->hash);
462 kmem_cache_free(revoke_record_cache, record);
463 }
464 }
465 if (descriptor)
466 flush_descriptor(journal, descriptor, offset);
467 jbd_debug(1, "Wrote %d revoke records\n", count);
468}
469
470/*
471 * Write out one revoke record. We need to create a new descriptor
472 * block if the old one is full or if we have not already created one.
473 */
474
475static void write_one_revoke_record(journal_t *journal,
476 transaction_t *transaction,
477 struct journal_head **descriptorp,
478 int *offsetp,
479 struct jbd_revoke_record_s *record)
480{
481 struct journal_head *descriptor;
482 int offset;
483 journal_header_t *header;
484
485 /* If we are already aborting, this all becomes a noop. We
486 still need to go round the loop in
487 journal_write_revoke_records in order to free all of the
488 revoke records: only the IO to the journal is omitted. */
489 if (is_journal_aborted(journal))
490 return;
491
492 descriptor = *descriptorp;
493 offset = *offsetp;
494
495 /* Make sure we have a descriptor with space left for the record */
496 if (descriptor) {
497 if (offset == journal->j_blocksize) {
498 flush_descriptor(journal, descriptor, offset);
499 descriptor = NULL;
500 }
501 }
502
503 if (!descriptor) {
504 descriptor = journal_get_descriptor_buffer(journal);
505 if (!descriptor)
506 return;
507 header = (journal_header_t *) &jh2bh(descriptor)->b_data[0];
508 header->h_magic = htonl(JFS_MAGIC_NUMBER);
509 header->h_blocktype = htonl(JFS_REVOKE_BLOCK);
510 header->h_sequence = htonl(transaction->t_tid);
511
512 /* Record it so that we can wait for IO completion later */
513 JBUFFER_TRACE(descriptor, "file as BJ_LogCtl");
514 journal_file_buffer(descriptor, transaction, BJ_LogCtl);
515
516 offset = sizeof(journal_revoke_header_t);
517 *descriptorp = descriptor;
518 }
519
520 * ((unsigned int *)(&jh2bh(descriptor)->b_data[offset])) =
521 htonl(record->blocknr);
522 offset += 4;
523 *offsetp = offset;
524}
525
526/*
527 * Flush a revoke descriptor out to the journal. If we are aborting,
528 * this is a noop; otherwise we are generating a buffer which needs to
529 * be waited for during commit, so it has to go onto the appropriate
530 * journal buffer list.
531 */
532
533static void flush_descriptor(journal_t *journal,
534 struct journal_head *descriptor,
535 int offset)
536{
537 journal_revoke_header_t *header;
538
539 if (is_journal_aborted(journal)) {
540 JBUFFER_TRACE(descriptor, "brelse");
541 __brelse(jh2bh(descriptor));
542 return;
543 }
544
545 header = (journal_revoke_header_t *) jh2bh(descriptor)->b_data;
546 header->r_count = htonl(offset);
547 set_bit(BH_JWrite, &jh2bh(descriptor)->b_state);
548 {
549 struct buffer_head *bh = jh2bh(descriptor);
550 BUFFER_TRACE(bh, "write");
551 ll_rw_block (WRITE, 1, &bh);
552 }
553}
554
555#endif
556
557/*
558 * Revoke support for recovery.
559 *
560 * Recovery needs to be able to:
561 *
562 * record all revoke records, including the tid of the latest instance
563 * of each revoke in the journal
564 *
565 * check whether a given block in a given transaction should be replayed
566 * (ie. has not been revoked by a revoke record in that or a subsequent
567 * transaction)
568 *
569 * empty the revoke table after recovery.
570 */
571
572/*
573 * First, setting revoke records. We create a new revoke record for
574 * every block ever revoked in the log as we scan it for recovery, and
575 * we update the existing records if we find multiple revokes for a
576 * single block.
577 */
578
579int journal_set_revoke(journal_t *journal,
580 unsigned long blocknr,
581 tid_t sequence)
582{
583 struct jbd_revoke_record_s *record;
584
585 record = find_revoke_record(journal, blocknr);
586 if (record) {
587 /* If we have multiple occurences, only record the
588 * latest sequence number in the hashed record */
589 if (tid_gt(sequence, record->sequence))
590 record->sequence = sequence;
591 return 0;
592 }
593 return insert_revoke_hash(journal, blocknr, sequence);
594}
595
596/*
597 * Test revoke records. For a given block referenced in the log, has
598 * that block been revoked? A revoke record with a given transaction
599 * sequence number revokes all blocks in that transaction and earlier
600 * ones, but later transactions still need replayed.
601 */
602
603int journal_test_revoke(journal_t *journal,
604 unsigned long blocknr,
605 tid_t sequence)
606{
607 struct jbd_revoke_record_s *record;
608
609 record = find_revoke_record(journal, blocknr);
610 if (!record)
611 return 0;
612 if (tid_gt(sequence, record->sequence))
613 return 0;
614 return 1;
615}
616
617/*
618 * Finally, once recovery is over, we need to clear the revoke table so
619 * that it can be reused by the running filesystem.
620 */
621
622void journal_clear_revoke(journal_t *journal)
623{
624 int i;
625 struct list_head *hash_list;
626 struct jbd_revoke_record_s *record;
627 struct jbd_revoke_table_s *revoke_var;
628
629 revoke_var = journal->j_revoke;
630
631 for (i = 0; i < revoke_var->hash_size; i++) {
632 hash_list = &revoke_var->hash_table[i];
633 while (!list_empty(hash_list)) {
634 record = (struct jbd_revoke_record_s*) hash_list->next;
635 list_del(&record->hash);
636 kmem_cache_free(revoke_record_cache, record);
637 }
638 }
639}
640
diff --git a/e2fsprogs/e2fsck/super.c b/e2fsprogs/e2fsck/super.c
new file mode 100644
index 000000000..294cd80e4
--- /dev/null
+++ b/e2fsprogs/e2fsck/super.c
@@ -0,0 +1,713 @@
1/*
2 * e2fsck.c - superblock checks
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#ifdef HAVE_ERRNO_H
13#include <errno.h>
14#endif
15
16#ifndef EXT2_SKIP_UUID
17#include "uuid/uuid.h"
18#endif
19#include "e2fsck.h"
20#include "problem.h"
21
22#define MIN_CHECK 1
23#define MAX_CHECK 2
24
25static void check_super_value(e2fsck_t ctx, const char *descr,
26 unsigned long value, int flags,
27 unsigned long min_val, unsigned long max_val)
28{
29 struct problem_context pctx;
30
31 if (((flags & MIN_CHECK) && (value < min_val)) ||
32 ((flags & MAX_CHECK) && (value > max_val))) {
33 clear_problem_context(&pctx);
34 pctx.num = value;
35 pctx.str = descr;
36 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
37 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
38 }
39}
40
41/*
42 * This routine may get stubbed out in special compilations of the
43 * e2fsck code..
44 */
45#ifndef EXT2_SPECIAL_DEVICE_SIZE
46errcode_t e2fsck_get_device_size(e2fsck_t ctx)
47{
48 return (ext2fs_get_device_size(ctx->filesystem_name,
49 EXT2_BLOCK_SIZE(ctx->fs->super),
50 &ctx->num_blocks));
51}
52#endif
53
54/*
55 * helper function to release an inode
56 */
57struct process_block_struct {
58 e2fsck_t ctx;
59 char *buf;
60 struct problem_context *pctx;
61 int truncating;
62 int truncate_offset;
63 e2_blkcnt_t truncate_block;
64 int truncated_blocks;
65 int abort;
66 errcode_t errcode;
67};
68
69static int release_inode_block(ext2_filsys fs,
70 blk_t *block_nr,
71 e2_blkcnt_t blockcnt,
72 blk_t ref_blk EXT2FS_ATTR((unused)),
73 int ref_offset EXT2FS_ATTR((unused)),
74 void *priv_data)
75{
76 struct process_block_struct *pb;
77 e2fsck_t ctx;
78 struct problem_context *pctx;
79 blk_t blk = *block_nr;
80 int retval = 0;
81
82 pb = (struct process_block_struct *) priv_data;
83 ctx = pb->ctx;
84 pctx = pb->pctx;
85
86 pctx->blk = blk;
87 pctx->blkcount = blockcnt;
88
89 if (HOLE_BLKADDR(blk))
90 return 0;
91
92 if ((blk < fs->super->s_first_data_block) ||
93 (blk >= fs->super->s_blocks_count)) {
94 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
95 return_abort:
96 pb->abort = 1;
97 return BLOCK_ABORT;
98 }
99
100 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
101 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
102 goto return_abort;
103 }
104
105 /*
106 * If we are deleting an orphan, then we leave the fields alone.
107 * If we are truncating an orphan, then update the inode fields
108 * and clean up any partial block data.
109 */
110 if (pb->truncating) {
111 /*
112 * We only remove indirect blocks if they are
113 * completely empty.
114 */
115 if (blockcnt < 0) {
116 int i, limit;
117 blk_t *bp;
118
119 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
120 pb->buf);
121 if (pb->errcode)
122 goto return_abort;
123
124 limit = fs->blocksize >> 2;
125 for (i = 0, bp = (blk_t *) pb->buf;
126 i < limit; i++, bp++)
127 if (*bp)
128 return 0;
129 }
130 /*
131 * We don't remove direct blocks until we've reached
132 * the truncation block.
133 */
134 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
135 return 0;
136 /*
137 * If part of the last block needs truncating, we do
138 * it here.
139 */
140 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
141 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
142 pb->buf);
143 if (pb->errcode)
144 goto return_abort;
145 memset(pb->buf + pb->truncate_offset, 0,
146 fs->blocksize - pb->truncate_offset);
147 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
148 pb->buf);
149 if (pb->errcode)
150 goto return_abort;
151 }
152 pb->truncated_blocks++;
153 *block_nr = 0;
154 retval |= BLOCK_CHANGED;
155 }
156
157 ext2fs_block_alloc_stats(fs, blk, -1);
158 return retval;
159}
160
161/*
162 * This function releases an inode. Returns 1 if an inconsistency was
163 * found. If the inode has a link count, then it is being truncated and
164 * not deleted.
165 */
166static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
167 struct ext2_inode *inode, char *block_buf,
168 struct problem_context *pctx)
169{
170 struct process_block_struct pb;
171 ext2_filsys fs = ctx->fs;
172 errcode_t retval;
173 __u32 count;
174
175 if (!ext2fs_inode_has_valid_blocks(inode))
176 return 0;
177
178 pb.buf = block_buf + 3 * ctx->fs->blocksize;
179 pb.ctx = ctx;
180 pb.abort = 0;
181 pb.errcode = 0;
182 pb.pctx = pctx;
183 if (inode->i_links_count) {
184 pb.truncating = 1;
185 pb.truncate_block = (e2_blkcnt_t)
186 ((((long long)inode->i_size_high << 32) +
187 inode->i_size + fs->blocksize - 1) /
188 fs->blocksize);
189 pb.truncate_offset = inode->i_size % fs->blocksize;
190 } else {
191 pb.truncating = 0;
192 pb.truncate_block = 0;
193 pb.truncate_offset = 0;
194 }
195 pb.truncated_blocks = 0;
196 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
197 block_buf, release_inode_block, &pb);
198 if (retval) {
199 com_err("release_inode_blocks", retval,
200 _("while calling ext2fs_block_iterate for inode %d"),
201 ino);
202 return 1;
203 }
204 if (pb.abort)
205 return 1;
206
207 /* Refresh the inode since ext2fs_block_iterate may have changed it */
208 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
209
210 if (pb.truncated_blocks)
211 inode->i_blocks -= pb.truncated_blocks *
212 (fs->blocksize / 512);
213
214 if (inode->i_file_acl) {
215 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
216 block_buf, -1, &count);
217 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
218 retval = 0;
219 count = 1;
220 }
221 if (retval) {
222 com_err("release_inode_blocks", retval,
223 _("while calling ext2fs_adjust_ea_refocunt for inode %d"),
224 ino);
225 return 1;
226 }
227 if (count == 0)
228 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
229 inode->i_file_acl = 0;
230 }
231 return 0;
232}
233
234/*
235 * This function releases all of the orphan inodes. It returns 1 if
236 * it hit some error, and 0 on success.
237 */
238static int release_orphan_inodes(e2fsck_t ctx)
239{
240 ext2_filsys fs = ctx->fs;
241 ext2_ino_t ino, next_ino;
242 struct ext2_inode inode;
243 struct problem_context pctx;
244 char *block_buf;
245
246 if ((ino = fs->super->s_last_orphan) == 0)
247 return 0;
248
249 /*
250 * Win or lose, we won't be using the head of the orphan inode
251 * list again.
252 */
253 fs->super->s_last_orphan = 0;
254 ext2fs_mark_super_dirty(fs);
255
256 /*
257 * If the filesystem contains errors, don't run the orphan
258 * list, since the orphan list can't be trusted; and we're
259 * going to be running a full e2fsck run anyway...
260 */
261 if (fs->super->s_state & EXT2_ERROR_FS)
262 return 0;
263
264 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
265 (ino > fs->super->s_inodes_count)) {
266 clear_problem_context(&pctx);
267 pctx.ino = ino;
268 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
269 return 1;
270 }
271
272 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
273 "block iterate buffer");
274 e2fsck_read_bitmaps(ctx);
275
276 while (ino) {
277 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
278 clear_problem_context(&pctx);
279 pctx.ino = ino;
280 pctx.inode = &inode;
281 pctx.str = inode.i_links_count ? _("Truncating") :
282 _("Clearing");
283
284 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
285
286 next_ino = inode.i_dtime;
287 if (next_ino &&
288 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
289 (next_ino > fs->super->s_inodes_count))) {
290 pctx.ino = next_ino;
291 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
292 goto return_abort;
293 }
294
295 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
296 goto return_abort;
297
298 if (!inode.i_links_count) {
299 ext2fs_inode_alloc_stats2(fs, ino, -1,
300 LINUX_S_ISDIR(inode.i_mode));
301 inode.i_dtime = time(0);
302 } else {
303 inode.i_dtime = 0;
304 }
305 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
306 ino = next_ino;
307 }
308 ext2fs_free_mem(&block_buf);
309 return 0;
310return_abort:
311 ext2fs_free_mem(&block_buf);
312 return 1;
313}
314
315/*
316 * Check the resize inode to make sure it is sane. We check both for
317 * the case where on-line resizing is not enabled (in which case the
318 * resize inode should be cleared) as well as the case where on-line
319 * resizing is enabled.
320 */
321void check_resize_inode(e2fsck_t ctx)
322{
323 ext2_filsys fs = ctx->fs;
324 struct ext2_inode inode;
325 struct problem_context pctx;
326 int i, j, gdt_off, ind_off;
327 blk_t blk, pblk, expect;
328 __u32 *dind_buf = 0, *ind_buf;
329 errcode_t retval;
330
331 clear_problem_context(&pctx);
332
333 /*
334 * If the resize inode feature isn't set, then
335 * s_reserved_gdt_blocks must be zero.
336 */
337 if (!(fs->super->s_feature_compat &
338 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
339 if (fs->super->s_reserved_gdt_blocks) {
340 pctx.num = fs->super->s_reserved_gdt_blocks;
341 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
342 &pctx)) {
343 fs->super->s_reserved_gdt_blocks = 0;
344 ext2fs_mark_super_dirty(fs);
345 }
346 }
347 }
348
349 /* Read the resizde inode */
350 pctx.ino = EXT2_RESIZE_INO;
351 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
352 if (retval) {
353 if (fs->super->s_feature_compat &
354 EXT2_FEATURE_COMPAT_RESIZE_INODE)
355 ctx->flags |= E2F_FLAG_RESIZE_INODE;
356 return;
357 }
358
359 /*
360 * If the resize inode feature isn't set, check to make sure
361 * the resize inode is cleared; then we're done.
362 */
363 if (!(fs->super->s_feature_compat &
364 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
365 for (i=0; i < EXT2_N_BLOCKS; i++) {
366 if (inode.i_block[i])
367 break;
368 }
369 if ((i < EXT2_N_BLOCKS) &&
370 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
371 memset(&inode, 0, sizeof(inode));
372 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
373 "clear_resize");
374 }
375 return;
376 }
377
378 /*
379 * The resize inode feature is enabled; check to make sure the
380 * only block in use is the double indirect block
381 */
382 blk = inode.i_block[EXT2_DIND_BLOCK];
383 for (i=0; i < EXT2_N_BLOCKS; i++) {
384 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
385 break;
386 }
387 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
388 !(inode.i_mode & LINUX_S_IFREG) ||
389 (blk < fs->super->s_first_data_block ||
390 blk >= fs->super->s_blocks_count)) {
391 resize_inode_invalid:
392 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
393 memset(&inode, 0, sizeof(inode));
394 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
395 "clear_resize");
396 ctx->flags |= E2F_FLAG_RESIZE_INODE;
397 }
398 if (!(ctx->options & E2F_OPT_READONLY)) {
399 fs->super->s_state &= ~EXT2_VALID_FS;
400 ext2fs_mark_super_dirty(fs);
401 }
402 goto cleanup;
403 }
404 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
405 "resize dind buffer");
406 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
407
408 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
409 if (retval)
410 goto resize_inode_invalid;
411
412 gdt_off = fs->desc_blocks;
413 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
414 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
415 i++, gdt_off++, pblk++) {
416 gdt_off %= fs->blocksize/4;
417 if (dind_buf[gdt_off] != pblk)
418 goto resize_inode_invalid;
419 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
420 if (retval)
421 goto resize_inode_invalid;
422 ind_off = 0;
423 for (j = 1; j < fs->group_desc_count; j++) {
424 if (!ext2fs_bg_has_super(fs, j))
425 continue;
426 expect = pblk + (j * fs->super->s_blocks_per_group);
427 if (ind_buf[ind_off] != expect)
428 goto resize_inode_invalid;
429 ind_off++;
430 }
431 }
432
433cleanup:
434 if (dind_buf)
435 ext2fs_free_mem(&dind_buf);
436
437 }
438
439void check_super_block(e2fsck_t ctx)
440{
441 ext2_filsys fs = ctx->fs;
442 blk_t first_block, last_block;
443 struct ext2_super_block *sb = fs->super;
444 struct ext2_group_desc *gd;
445 blk_t blocks_per_group = fs->super->s_blocks_per_group;
446 blk_t bpg_max;
447 int inodes_per_block;
448 int ipg_max;
449 int inode_size;
450 dgrp_t i;
451 blk_t should_be;
452 struct problem_context pctx;
453 __u32 free_blocks = 0, free_inodes = 0;
454
455 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
456 ipg_max = inodes_per_block * (blocks_per_group - 4);
457 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
458 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
459 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
460 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
461 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
462
463 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
464 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
465 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
466 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
467 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
468 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
469
470 clear_problem_context(&pctx);
471
472 /*
473 * Verify the super block constants...
474 */
475 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
476 MIN_CHECK, 1, 0);
477 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
478 MIN_CHECK, 1, 0);
479 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
480 MAX_CHECK, 0, sb->s_blocks_count);
481 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
482 MIN_CHECK | MAX_CHECK, 0,
483 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
484 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
485 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
486 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
487 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
488 bpg_max);
489 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
490 MIN_CHECK | MAX_CHECK, 8, bpg_max);
491 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
492 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
493 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
494 MAX_CHECK, 0, sb->s_blocks_count / 2);
495 check_super_value(ctx, "reserved_gdt_blocks",
496 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
497 fs->blocksize/4);
498 inode_size = EXT2_INODE_SIZE(sb);
499 check_super_value(ctx, "inode_size",
500 inode_size, MIN_CHECK | MAX_CHECK,
501 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
502 if (inode_size & (inode_size - 1)) {
503 pctx.num = inode_size;
504 pctx.str = "inode_size";
505 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
506 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
507 return;
508 }
509
510 if (!ctx->num_blocks) {
511 pctx.errcode = e2fsck_get_device_size(ctx);
512 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
513 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
514 ctx->flags |= E2F_FLAG_ABORT;
515 return;
516 }
517 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
518 (ctx->num_blocks < sb->s_blocks_count)) {
519 pctx.blk = sb->s_blocks_count;
520 pctx.blk2 = ctx->num_blocks;
521 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
522 ctx->flags |= E2F_FLAG_ABORT;
523 return;
524 }
525 }
526 }
527
528 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
529 pctx.blk = EXT2_BLOCK_SIZE(sb);
530 pctx.blk2 = EXT2_FRAG_SIZE(sb);
531 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
532 ctx->flags |= E2F_FLAG_ABORT;
533 return;
534 }
535
536 should_be = sb->s_frags_per_group >>
537 (sb->s_log_block_size - sb->s_log_frag_size);
538 if (sb->s_blocks_per_group != should_be) {
539 pctx.blk = sb->s_blocks_per_group;
540 pctx.blk2 = should_be;
541 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
542 ctx->flags |= E2F_FLAG_ABORT;
543 return;
544 }
545
546 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
547 if (sb->s_first_data_block != should_be) {
548 pctx.blk = sb->s_first_data_block;
549 pctx.blk2 = should_be;
550 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
551 ctx->flags |= E2F_FLAG_ABORT;
552 return;
553 }
554
555 should_be = sb->s_inodes_per_group * fs->group_desc_count;
556 if (sb->s_inodes_count != should_be) {
557 pctx.ino = sb->s_inodes_count;
558 pctx.ino2 = should_be;
559 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
560 sb->s_inodes_count = should_be;
561 ext2fs_mark_super_dirty(fs);
562 }
563 }
564
565 /*
566 * Verify the group descriptors....
567 */
568 first_block = sb->s_first_data_block;
569 last_block = first_block + blocks_per_group;
570
571 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
572 pctx.group = i;
573
574 if (i == fs->group_desc_count - 1)
575 last_block = sb->s_blocks_count;
576 if ((gd->bg_block_bitmap < first_block) ||
577 (gd->bg_block_bitmap >= last_block)) {
578 pctx.blk = gd->bg_block_bitmap;
579 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
580 gd->bg_block_bitmap = 0;
581 }
582 if (gd->bg_block_bitmap == 0) {
583 ctx->invalid_block_bitmap_flag[i]++;
584 ctx->invalid_bitmaps++;
585 }
586 if ((gd->bg_inode_bitmap < first_block) ||
587 (gd->bg_inode_bitmap >= last_block)) {
588 pctx.blk = gd->bg_inode_bitmap;
589 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
590 gd->bg_inode_bitmap = 0;
591 }
592 if (gd->bg_inode_bitmap == 0) {
593 ctx->invalid_inode_bitmap_flag[i]++;
594 ctx->invalid_bitmaps++;
595 }
596 if ((gd->bg_inode_table < first_block) ||
597 ((gd->bg_inode_table +
598 fs->inode_blocks_per_group - 1) >= last_block)) {
599 pctx.blk = gd->bg_inode_table;
600 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
601 gd->bg_inode_table = 0;
602 }
603 if (gd->bg_inode_table == 0) {
604 ctx->invalid_inode_table_flag[i]++;
605 ctx->invalid_bitmaps++;
606 }
607 free_blocks += gd->bg_free_blocks_count;
608 free_inodes += gd->bg_free_inodes_count;
609 first_block += sb->s_blocks_per_group;
610 last_block += sb->s_blocks_per_group;
611
612 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
613 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
614 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
615 ext2fs_unmark_valid(fs);
616
617 }
618
619 /*
620 * Update the global counts from the block group counts. This
621 * is needed for an experimental patch which eliminates
622 * locking the entire filesystem when allocating blocks or
623 * inodes; if the filesystem is not unmounted cleanly, the
624 * global counts may not be accurate.
625 */
626 if ((free_blocks != sb->s_free_blocks_count) ||
627 (free_inodes != sb->s_free_inodes_count)) {
628 if (ctx->options & E2F_OPT_READONLY)
629 ext2fs_unmark_valid(fs);
630 else {
631 sb->s_free_blocks_count = free_blocks;
632 sb->s_free_inodes_count = free_inodes;
633 ext2fs_mark_super_dirty(fs);
634 }
635 }
636
637 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
638 (sb->s_free_inodes_count > sb->s_inodes_count))
639 ext2fs_unmark_valid(fs);
640
641
642 /*
643 * If we have invalid bitmaps, set the error state of the
644 * filesystem.
645 */
646 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
647 sb->s_state &= ~EXT2_VALID_FS;
648 ext2fs_mark_super_dirty(fs);
649 }
650
651 clear_problem_context(&pctx);
652
653#ifndef EXT2_SKIP_UUID
654 /*
655 * If the UUID field isn't assigned, assign it.
656 */
657 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
658 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
659 uuid_generate(sb->s_uuid);
660 ext2fs_mark_super_dirty(fs);
661 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
662 }
663 }
664#endif
665
666 /*
667 * For the Hurd, check to see if the filetype option is set,
668 * since it doesn't support it.
669 */
670 if (!(ctx->options & E2F_OPT_READONLY) &&
671 fs->super->s_creator_os == EXT2_OS_HURD &&
672 (fs->super->s_feature_incompat &
673 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
674 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
675 fs->super->s_feature_incompat &=
676 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
677 ext2fs_mark_super_dirty(fs);
678
679 }
680 }
681
682 /*
683 * If we have any of the compatibility flags set, we need to have a
684 * revision 1 filesystem. Most kernels will not check the flags on
685 * a rev 0 filesystem and we may have corruption issues because of
686 * the incompatible changes to the filesystem.
687 */
688 if (!(ctx->options & E2F_OPT_READONLY) &&
689 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
690 (fs->super->s_feature_compat ||
691 fs->super->s_feature_ro_compat ||
692 fs->super->s_feature_incompat) &&
693 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
694 ext2fs_update_dynamic_rev(fs);
695 ext2fs_mark_super_dirty(fs);
696 }
697
698 check_resize_inode(ctx);
699
700 /*
701 * Clean up any orphan inodes, if present.
702 */
703 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
704 fs->super->s_state &= ~EXT2_VALID_FS;
705 ext2fs_mark_super_dirty(fs);
706 }
707
708 /*
709 * Move the ext3 journal file, if necessary.
710 */
711 e2fsck_move_ext3_journal(ctx);
712 return;
713}
diff --git a/e2fsprogs/e2fsck/swapfs.c b/e2fsprogs/e2fsck/swapfs.c
new file mode 100644
index 000000000..8d464043a
--- /dev/null
+++ b/e2fsprogs/e2fsck/swapfs.c
@@ -0,0 +1,269 @@
1/*
2 * swapfs.c --- byte-swap an ext2 filesystem
3 *
4 * Copyright 1996, 1997 by Theodore Ts'o
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 *
11 */
12
13#ifdef HAVE_ERRNO_H
14#include <errno.h>
15#endif
16#include <et/com_err.h>
17#include "e2fsck.h"
18
19#ifdef ENABLE_SWAPFS
20
21struct swap_block_struct {
22 ext2_ino_t ino;
23 int isdir;
24 errcode_t errcode;
25 char *dir_buf;
26 struct ext2_inode *inode;
27};
28
29/*
30 * This is a helper function for block_iterate. We mark all of the
31 * indirect and direct blocks as changed, so that block_iterate will
32 * write them out.
33 */
34static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
35 void *priv_data)
36{
37 errcode_t retval;
38
39 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
40
41 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
42 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
43 if (retval) {
44 sb->errcode = retval;
45 return BLOCK_ABORT;
46 }
47 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
48 if (retval) {
49 sb->errcode = retval;
50 return BLOCK_ABORT;
51 }
52 }
53 if (blockcnt >= 0) {
54 if (blockcnt < EXT2_NDIR_BLOCKS)
55 return 0;
56 return BLOCK_CHANGED;
57 }
58 if (blockcnt == BLOCK_COUNT_IND) {
59 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
60 return 0;
61 return BLOCK_CHANGED;
62 }
63 if (blockcnt == BLOCK_COUNT_DIND) {
64 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
65 return 0;
66 return BLOCK_CHANGED;
67 }
68 if (blockcnt == BLOCK_COUNT_TIND) {
69 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
70 return 0;
71 return BLOCK_CHANGED;
72 }
73 return BLOCK_CHANGED;
74}
75
76/*
77 * This function is responsible for byte-swapping all of the indirect,
78 * block pointers. It is also responsible for byte-swapping directories.
79 */
80static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
81 struct ext2_inode *inode)
82{
83 errcode_t retval;
84 struct swap_block_struct sb;
85
86 sb.ino = ino;
87 sb.inode = inode;
88 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
89 sb.errcode = 0;
90 sb.isdir = 0;
91 if (LINUX_S_ISDIR(inode->i_mode))
92 sb.isdir = 1;
93
94 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
95 swap_block, &sb);
96 if (retval) {
97 com_err("swap_inode_blocks", retval,
98 _("while calling ext2fs_block_iterate"));
99 ctx->flags |= E2F_FLAG_ABORT;
100 return;
101 }
102 if (sb.errcode) {
103 com_err("swap_inode_blocks", sb.errcode,
104 _("while calling iterator function"));
105 ctx->flags |= E2F_FLAG_ABORT;
106 return;
107 }
108}
109
110static void swap_inodes(e2fsck_t ctx)
111{
112 ext2_filsys fs = ctx->fs;
113 dgrp_t group;
114 unsigned int i;
115 ext2_ino_t ino = 1;
116 char *buf, *block_buf;
117 errcode_t retval;
118 struct ext2_inode * inode;
119
120 e2fsck_use_inode_shortcuts(ctx, 1);
121
122 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
123 &buf);
124 if (retval) {
125 com_err("swap_inodes", retval,
126 _("while allocating inode buffer"));
127 ctx->flags |= E2F_FLAG_ABORT;
128 return;
129 }
130 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
131 "block interate buffer");
132 for (group = 0; group < fs->group_desc_count; group++) {
133 retval = io_channel_read_blk(fs->io,
134 fs->group_desc[group].bg_inode_table,
135 fs->inode_blocks_per_group, buf);
136 if (retval) {
137 com_err("swap_inodes", retval,
138 _("while reading inode table (group %d)"),
139 group);
140 ctx->flags |= E2F_FLAG_ABORT;
141 return;
142 }
143 inode = (struct ext2_inode *) buf;
144 for (i=0; i < fs->super->s_inodes_per_group;
145 i++, ino++, inode++) {
146 ctx->stashed_ino = ino;
147 ctx->stashed_inode = inode;
148
149 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
150 ext2fs_swap_inode(fs, inode, inode, 0);
151
152 /*
153 * Skip deleted files.
154 */
155 if (inode->i_links_count == 0)
156 continue;
157
158 if (LINUX_S_ISDIR(inode->i_mode) ||
159 ((inode->i_block[EXT2_IND_BLOCK] ||
160 inode->i_block[EXT2_DIND_BLOCK] ||
161 inode->i_block[EXT2_TIND_BLOCK]) &&
162 ext2fs_inode_has_valid_blocks(inode)))
163 swap_inode_blocks(ctx, ino, block_buf, inode);
164
165 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
166 return;
167
168 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
169 ext2fs_swap_inode(fs, inode, inode, 1);
170 }
171 retval = io_channel_write_blk(fs->io,
172 fs->group_desc[group].bg_inode_table,
173 fs->inode_blocks_per_group, buf);
174 if (retval) {
175 com_err("swap_inodes", retval,
176 _("while writing inode table (group %d)"),
177 group);
178 ctx->flags |= E2F_FLAG_ABORT;
179 return;
180 }
181 }
182 ext2fs_free_mem(&buf);
183 ext2fs_free_mem(&block_buf);
184 e2fsck_use_inode_shortcuts(ctx, 0);
185 ext2fs_flush_icache(fs);
186}
187
188#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
189/*
190 * On the PowerPC, the big-endian variant of the ext2 filesystem
191 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
192 * of each word. Thus a bitmap with only bit 0 set would be, as
193 * a string of bytes, 00 00 00 01 00 ...
194 * To cope with this, we byte-reverse each word of a bitmap if
195 * we have a big-endian filesystem, that is, if we are *not*
196 * byte-swapping other word-sized numbers.
197 */
198#define EXT2_BIG_ENDIAN_BITMAPS
199#endif
200
201#ifdef EXT2_BIG_ENDIAN_BITMAPS
202static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
203{
204 __u32 *p = (__u32 *) bmap->bitmap;
205 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
206
207 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
208 *p = ext2fs_swab32(*p);
209}
210#endif
211
212
213void swap_filesys(e2fsck_t ctx)
214{
215 ext2_filsys fs = ctx->fs;
216#ifdef RESOURCE_TRACK
217 struct resource_track rtrack;
218
219 init_resource_track(&rtrack);
220#endif
221
222 if (!(ctx->options & E2F_OPT_PREEN))
223 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
224
225#ifdef MTRACE
226 mtrace_print("Byte swap");
227#endif
228
229 if (fs->super->s_mnt_count) {
230 fprintf(stderr, _("%s: the filesystem must be freshly "
231 "checked using fsck\n"
232 "and not mounted before trying to "
233 "byte-swap it.\n"), ctx->device_name);
234 ctx->flags |= E2F_FLAG_ABORT;
235 return;
236 }
237 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
238 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
239 EXT2_FLAG_SWAP_BYTES_WRITE);
240 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
241 } else {
242 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
243 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
244 }
245 swap_inodes(ctx);
246 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
247 return;
248 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
249 fs->flags |= EXT2_FLAG_SWAP_BYTES;
250 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
251 EXT2_FLAG_SWAP_BYTES_WRITE);
252
253#ifdef EXT2_BIG_ENDIAN_BITMAPS
254 e2fsck_read_bitmaps(ctx);
255 ext2fs_swap_bitmap(fs->inode_map);
256 ext2fs_swap_bitmap(fs->block_map);
257 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
258#endif
259 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
260 ext2fs_flush(fs);
261 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
262
263#ifdef RESOURCE_TRACK
264 if (ctx->options & E2F_OPT_TIME2)
265 print_resource_track(_("Byte swap"), &rtrack);
266#endif
267}
268
269#endif
diff --git a/e2fsprogs/e2fsck/util.c b/e2fsprogs/e2fsck/util.c
new file mode 100644
index 000000000..311156e23
--- /dev/null
+++ b/e2fsprogs/e2fsck/util.c
@@ -0,0 +1,503 @@
1/*
2 * util.c --- miscellaneous utilities
3 *
4 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdlib.h>
13#include <unistd.h>
14#include <string.h>
15#include <ctype.h>
16
17#ifdef HAVE_CONIO_H
18#undef HAVE_TERMIOS_H
19#include <conio.h>
20#define read_a_char() getch()
21#else
22#ifdef HAVE_TERMIOS_H
23#include <termios.h>
24#endif
25#include <stdio.h>
26#endif
27
28#ifdef HAVE_MALLOC_H
29#include <malloc.h>
30#endif
31
32#include "e2fsck.h"
33
34extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
35
36#include <sys/time.h>
37#include <sys/resource.h>
38
39#if 0
40void fatal_error(e2fsck_t ctx, const char *msg)
41{
42 if (msg)
43 fprintf (stderr, "e2fsck: %s\n", msg);
44 if (ctx->fs && ctx->fs->io) {
45 if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL)
46 io_channel_flush(ctx->fs->io);
47 else
48 fprintf(stderr, "e2fsck: io manager magic bad!\n");
49 }
50 ctx->flags |= E2F_FLAG_ABORT;
51 if (ctx->flags & E2F_FLAG_SETJMP_OK)
52 longjmp(ctx->abort_loc, 1);
53 exit(FSCK_ERROR);
54}
55#endif
56
57void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
58 const char *description)
59{
60 void *ret;
61 char buf[256];
62
63#ifdef DEBUG_ALLOCATE_MEMORY
64 printf("Allocating %d bytes for %s...\n", size, description);
65#endif
66 ret = malloc(size);
67 if (!ret) {
68 sprintf(buf, "Can't allocate %s\n", description);
69 fatal_error(ctx, buf);
70 }
71 memset(ret, 0, size);
72 return ret;
73}
74
75char *string_copy(e2fsck_t ctx EXT2FS_ATTR((unused)),
76 const char *str, int len)
77{
78 char *ret;
79
80 if (!str)
81 return NULL;
82 if (!len)
83 len = strlen(str);
84 ret = malloc(len+1);
85 if (ret) {
86 strncpy(ret, str, len);
87 ret[len] = 0;
88 }
89 return ret;
90}
91
92#ifndef HAVE_STRNLEN
93/*
94 * Incredibly, libc5 doesn't appear to have strnlen. So we have to
95 * provide our own.
96 */
97int e2fsck_strnlen(const char * s, int count)
98{
99 const char *cp = s;
100
101 while (count-- && *cp)
102 cp++;
103 return cp - s;
104}
105#endif
106
107#ifndef HAVE_CONIO_H
108static int read_a_char(void)
109{
110 char c;
111 int r;
112 int fail = 0;
113
114 while(1) {
115 if (e2fsck_global_ctx &&
116 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
117 return 3;
118 }
119 r = read(0, &c, 1);
120 if (r == 1)
121 return c;
122 if (fail++ > 100)
123 break;
124 }
125 return EOF;
126}
127#endif
128
129int ask_yn(const char * string, int def)
130{
131 int c;
132 const char *defstr;
133 const char *short_yes = _("yY");
134 const char *short_no = _("nN");
135
136#ifdef HAVE_TERMIOS_H
137 struct termios termios, tmp;
138
139 tcgetattr (0, &termios);
140 tmp = termios;
141 tmp.c_lflag &= ~(ICANON | ECHO);
142 tmp.c_cc[VMIN] = 1;
143 tmp.c_cc[VTIME] = 0;
144 tcsetattr (0, TCSANOW, &tmp);
145#endif
146
147 if (def == 1)
148 defstr = _(_("<y>"));
149 else if (def == 0)
150 defstr = _(_("<n>"));
151 else
152 defstr = _(" (y/n)");
153 printf("%s%s? ", string, defstr);
154 while (1) {
155 fflush (stdout);
156 if ((c = read_a_char()) == EOF)
157 break;
158 if (c == 3) {
159#ifdef HAVE_TERMIOS_H
160 tcsetattr (0, TCSANOW, &termios);
161#endif
162 if (e2fsck_global_ctx &&
163 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
164 puts("\n");
165 longjmp(e2fsck_global_ctx->abort_loc, 1);
166 }
167 puts(_("cancelled!\n"));
168 return 0;
169 }
170 if (strchr(short_yes, (char) c)) {
171 def = 1;
172 break;
173 }
174 else if (strchr(short_no, (char) c)) {
175 def = 0;
176 break;
177 }
178 else if ((c == ' ' || c == '\n') && (def != -1))
179 break;
180 }
181 if (def)
182 puts(_("yes\n"));
183 else
184 puts (_("no\n"));
185#ifdef HAVE_TERMIOS_H
186 tcsetattr (0, TCSANOW, &termios);
187#endif
188 return def;
189}
190
191int ask (e2fsck_t ctx, const char * string, int def)
192{
193 if (ctx->options & E2F_OPT_NO) {
194 printf (_("%s? no\n\n"), string);
195 return 0;
196 }
197 if (ctx->options & E2F_OPT_YES) {
198 printf (_("%s? yes\n\n"), string);
199 return 1;
200 }
201 if (ctx->options & E2F_OPT_PREEN) {
202 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
203 return def;
204 }
205 return ask_yn(string, def);
206}
207
208void e2fsck_read_bitmaps(e2fsck_t ctx)
209{
210 ext2_filsys fs = ctx->fs;
211 errcode_t retval;
212
213 if (ctx->invalid_bitmaps) {
214 com_err(ctx->program_name, 0,
215 _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
216 ctx->device_name);
217 fatal_error(ctx, 0);
218 }
219
220 ehandler_operation(_("reading inode and block bitmaps"));
221 retval = ext2fs_read_bitmaps(fs);
222 ehandler_operation(0);
223 if (retval) {
224 com_err(ctx->program_name, retval,
225 _("while retrying to read bitmaps for %s"),
226 ctx->device_name);
227 fatal_error(ctx, 0);
228 }
229}
230
231void e2fsck_write_bitmaps(e2fsck_t ctx)
232{
233 ext2_filsys fs = ctx->fs;
234 errcode_t retval;
235
236 if (ext2fs_test_bb_dirty(fs)) {
237 ehandler_operation(_("writing block bitmaps"));
238 retval = ext2fs_write_block_bitmap(fs);
239 ehandler_operation(0);
240 if (retval) {
241 com_err(ctx->program_name, retval,
242 _("while retrying to write block bitmaps for %s"),
243 ctx->device_name);
244 fatal_error(ctx, 0);
245 }
246 }
247
248 if (ext2fs_test_ib_dirty(fs)) {
249 ehandler_operation(_("writing inode bitmaps"));
250 retval = ext2fs_write_inode_bitmap(fs);
251 ehandler_operation(0);
252 if (retval) {
253 com_err(ctx->program_name, retval,
254 _("while retrying to write inode bitmaps for %s"),
255 ctx->device_name);
256 fatal_error(ctx, 0);
257 }
258 }
259}
260
261void preenhalt(e2fsck_t ctx)
262{
263 ext2_filsys fs = ctx->fs;
264
265 if (!(ctx->options & E2F_OPT_PREEN))
266 return;
267 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
268 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
269 ctx->device_name);
270 if (fs != NULL) {
271 fs->super->s_state |= EXT2_ERROR_FS;
272 ext2fs_mark_super_dirty(fs);
273 ext2fs_close(fs);
274 }
275 exit(FSCK_UNCORRECTED);
276}
277
278#ifdef RESOURCE_TRACK
279void init_resource_track(struct resource_track *track)
280{
281#ifdef HAVE_GETRUSAGE
282 struct rusage r;
283#endif
284
285 track->brk_start = sbrk(0);
286 gettimeofday(&track->time_start, 0);
287#ifdef HAVE_GETRUSAGE
288#ifdef sun
289 memset(&r, 0, sizeof(struct rusage));
290#endif
291 getrusage(RUSAGE_SELF, &r);
292 track->user_start = r.ru_utime;
293 track->system_start = r.ru_stime;
294#else
295 track->user_start.tv_sec = track->user_start.tv_usec = 0;
296 track->system_start.tv_sec = track->system_start.tv_usec = 0;
297#endif
298}
299
300#ifdef __GNUC__
301#define _INLINE_ __inline__
302#else
303#define _INLINE_
304#endif
305
306static _INLINE_ float timeval_subtract(struct timeval *tv1,
307 struct timeval *tv2)
308{
309 return ((tv1->tv_sec - tv2->tv_sec) +
310 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
311}
312
313void print_resource_track(const char *desc, struct resource_track *track)
314{
315#ifdef HAVE_GETRUSAGE
316 struct rusage r;
317#endif
318#ifdef HAVE_MALLINFO
319 struct mallinfo malloc_info;
320#endif
321 struct timeval time_end;
322
323 gettimeofday(&time_end, 0);
324
325 if (desc)
326 printf("%s: ", desc);
327
328#ifdef HAVE_MALLINFO
329#define kbytes(x) (((x) + 1023) / 1024)
330
331 malloc_info = mallinfo();
332 printf(_("Memory used: %dk/%dk (%dk/%dk), "),
333 kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd),
334 kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
335#else
336 printf(_("Memory used: %d, "),
337 (int) (((char *) sbrk(0)) - ((char *) track->brk_start)));
338#endif
339#ifdef HAVE_GETRUSAGE
340 getrusage(RUSAGE_SELF, &r);
341
342 printf(_("time: %5.2f/%5.2f/%5.2f\n"),
343 timeval_subtract(&time_end, &track->time_start),
344 timeval_subtract(&r.ru_utime, &track->user_start),
345 timeval_subtract(&r.ru_stime, &track->system_start));
346#else
347 printf(_("elapsed time: %6.3f\n"),
348 timeval_subtract(&time_end, &track->time_start));
349#endif
350}
351#endif /* RESOURCE_TRACK */
352
353void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
354 struct ext2_inode * inode, const char *proc)
355{
356 int retval;
357
358 retval = ext2fs_read_inode(ctx->fs, ino, inode);
359 if (retval) {
360 com_err("ext2fs_read_inode", retval,
361 _("while reading inode %ld in %s"), ino, proc);
362 fatal_error(ctx, 0);
363 }
364}
365
366extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
367 struct ext2_inode * inode, int bufsize,
368 const char *proc)
369{
370 int retval;
371
372 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
373 if (retval) {
374 com_err("ext2fs_write_inode", retval,
375 _("while writing inode %ld in %s"), ino, proc);
376 fatal_error(ctx, 0);
377 }
378}
379
380extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
381 struct ext2_inode * inode, const char *proc)
382{
383 int retval;
384
385 retval = ext2fs_write_inode(ctx->fs, ino, inode);
386 if (retval) {
387 com_err("ext2fs_write_inode", retval,
388 _("while writing inode %ld in %s"), ino, proc);
389 fatal_error(ctx, 0);
390 }
391}
392
393#ifdef MTRACE
394void mtrace_print(char *mesg)
395{
396 FILE *malloc_get_mallstream();
397 FILE *f = malloc_get_mallstream();
398
399 if (f)
400 fprintf(f, "============= %s\n", mesg);
401}
402#endif
403
404blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
405 io_manager manager)
406{
407 struct ext2_super_block *sb;
408 io_channel io = NULL;
409 void *buf = NULL;
410 int blocksize;
411 blk_t superblock, ret_sb = 8193;
412
413 if (fs && fs->super) {
414 ret_sb = (fs->super->s_blocks_per_group +
415 fs->super->s_first_data_block);
416 if (ctx) {
417 ctx->superblock = ret_sb;
418 ctx->blocksize = fs->blocksize;
419 }
420 return ret_sb;
421 }
422
423 if (ctx) {
424 if (ctx->blocksize) {
425 ret_sb = ctx->blocksize * 8;
426 if (ctx->blocksize == 1024)
427 ret_sb++;
428 ctx->superblock = ret_sb;
429 return ret_sb;
430 }
431 ctx->superblock = ret_sb;
432 ctx->blocksize = 1024;
433 }
434
435 if (!name || !manager)
436 goto cleanup;
437
438 if (manager->open(name, 0, &io) != 0)
439 goto cleanup;
440
441 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
442 goto cleanup;
443 sb = (struct ext2_super_block *) buf;
444
445 for (blocksize = EXT2_MIN_BLOCK_SIZE;
446 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
447 superblock = blocksize*8;
448 if (blocksize == 1024)
449 superblock++;
450 io_channel_set_blksize(io, blocksize);
451 if (io_channel_read_blk(io, superblock,
452 -SUPERBLOCK_SIZE, buf))
453 continue;
454#ifdef EXT2FS_ENABLE_SWAPFS
455 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
456 ext2fs_swap_super(sb);
457#endif
458 if (sb->s_magic == EXT2_SUPER_MAGIC) {
459 ret_sb = superblock;
460 if (ctx) {
461 ctx->superblock = superblock;
462 ctx->blocksize = blocksize;
463 }
464 break;
465 }
466 }
467
468cleanup:
469 if (io)
470 io_channel_close(io);
471 if (buf)
472 ext2fs_free_mem(&buf);
473 return (ret_sb);
474}
475
476/*
477 * Given a mode, return the ext2 file type
478 */
479int ext2_file_type(unsigned int mode)
480{
481 if (LINUX_S_ISREG(mode))
482 return EXT2_FT_REG_FILE;
483
484 if (LINUX_S_ISDIR(mode))
485 return EXT2_FT_DIR;
486
487 if (LINUX_S_ISCHR(mode))
488 return EXT2_FT_CHRDEV;
489
490 if (LINUX_S_ISBLK(mode))
491 return EXT2_FT_BLKDEV;
492
493 if (LINUX_S_ISLNK(mode))
494 return EXT2_FT_SYMLINK;
495
496 if (LINUX_S_ISFIFO(mode))
497 return EXT2_FT_FIFO;
498
499 if (LINUX_S_ISSOCK(mode))
500 return EXT2_FT_SOCK;
501
502 return 0;
503}
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
new file mode 100644
index 000000000..b638e4ab9
--- /dev/null
+++ b/e2fsprogs/fsck.c
@@ -0,0 +1,1302 @@
1/*
2 * pfsck --- A generic, parallelizing front-end for the fsck program.
3 * It will automatically try to run fsck programs in parallel if the
4 * devices are on separate spindles. It is based on the same ideas as
5 * the generic front end for fsck by David Engel and Fred van Kempen,
6 * but it has been completely rewritten from scratch to support
7 * parallel execution.
8 *
9 * Written by Theodore Ts'o, <tytso@mit.edu>
10 *
11 * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
12 * o Changed -t fstype to behave like with mount when -A (all file
13 * systems) or -M (like mount) is specified.
14 * o fsck looks if it can find the fsck.type program to decide
15 * if it should ignore the fs type. This way more fsck programs
16 * can be added without changing this front-end.
17 * o -R flag skip root file system.
18 *
19 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
20 * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
21 *
22 * %Begin-Header%
23 * This file may be redistributed under the terms of the GNU Public
24 * License.
25 * %End-Header%
26 */
27
28#include <sys/types.h>
29#include <sys/wait.h>
30#include <sys/signal.h>
31#include <sys/stat.h>
32#include <limits.h>
33#include <stdio.h>
34#include <ctype.h>
35#include <string.h>
36#include <time.h>
37#include <stdlib.h>
38#include <errno.h>
39#include <paths.h>
40#include <unistd.h>
41#include <errno.h>
42#include <malloc.h>
43#include <signal.h>
44
45#include "fsck.h"
46#include "blkid/blkid.h"
47
48#include "e2fsbb.h"
49
50#ifndef _PATH_MNTTAB
51#define _PATH_MNTTAB "/etc/fstab"
52#endif
53
54static const char *ignored_types[] = {
55 "ignore",
56 "iso9660",
57 "nfs",
58 "proc",
59 "sw",
60 "swap",
61 "tmpfs",
62 "devpts",
63 NULL
64};
65
66static const char *really_wanted[] = {
67 "minix",
68 "ext2",
69 "ext3",
70 "jfs",
71 "reiserfs",
72 "xiafs",
73 "xfs",
74 NULL
75};
76
77#define BASE_MD "/dev/md"
78
79/*
80 * Global variables for options
81 */
82static char *devices[MAX_DEVICES];
83static char *args[MAX_ARGS];
84static int num_devices, num_args;
85
86static int verbose = 0;
87static int doall = 0;
88static int noexecute = 0;
89static int serialize = 0;
90static int skip_root = 0;
91static int like_mount = 0;
92static int notitle = 0;
93static int parallel_root = 0;
94static int progress = 0;
95static int progress_fd = 0;
96static int force_all_parallel = 0;
97static int num_running = 0;
98static int max_running = 0;
99static volatile int cancel_requested = 0;
100static int kill_sent = 0;
101static char *progname;
102static char *fstype = NULL;
103static struct fs_info *filesys_info = NULL, *filesys_last = NULL;
104static struct fsck_instance *instance_list;
105static const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc";
106static char *fsck_path = 0;
107static blkid_cache cache = NULL;
108
109static char *string_copy(const char *s)
110{
111 char *ret;
112
113 if (!s)
114 return 0;
115 ret = malloc(strlen(s)+1);
116 if (ret)
117 strcpy(ret, s);
118 return ret;
119}
120
121static int string_to_int(const char *s)
122{
123 long l;
124 char *p;
125
126 l = strtol(s, &p, 0);
127 if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
128 return -1;
129 else
130 return (int) l;
131}
132
133static int ignore(struct fs_info *);
134
135static char *skip_over_blank(char *cp)
136{
137 while (*cp && isspace(*cp))
138 cp++;
139 return cp;
140}
141
142static char *skip_over_word(char *cp)
143{
144 while (*cp && !isspace(*cp))
145 cp++;
146 return cp;
147}
148
149static void strip_line(char *line)
150{
151 char *p;
152
153 while (*line) {
154 p = line + strlen(line) - 1;
155 if ((*p == '\n') || (*p == '\r'))
156 *p = 0;
157 else
158 break;
159 }
160}
161
162static char *parse_word(char **buf)
163{
164 char *word, *next;
165
166 word = *buf;
167 if (*word == 0)
168 return 0;
169
170 word = skip_over_blank(word);
171 next = skip_over_word(word);
172 if (*next)
173 *next++ = 0;
174 *buf = next;
175 return word;
176}
177
178static void parse_escape(char *word)
179{
180 char *p, *q;
181 int ac, i;
182
183 if (!word)
184 return;
185
186 for (p = word, q = word; *p; p++, q++) {
187 *q = *p;
188 if (*p != '\\')
189 continue;
190 if (*++p == 0)
191 break;
192 if (*p == 't') {
193 *q = '\t';
194 continue;
195 }
196 if (*p == 'n') {
197 *q = '\n';
198 continue;
199 }
200 if (!isdigit(*p)) {
201 *q = *p;
202 continue;
203 }
204 ac = 0;
205 for (i = 0; i < 3; i++, p++) {
206 if (!isdigit(*p))
207 break;
208 ac = (ac * 8) + (*p - '0');
209 }
210 *q = ac;
211 p--;
212 }
213 *q = 0;
214}
215
216static void free_instance(struct fsck_instance *i)
217{
218 if (i->prog)
219 free(i->prog);
220 if (i->device)
221 free(i->device);
222 if (i->base_device)
223 free(i->base_device);
224 free(i);
225 return;
226}
227
228static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
229 const char *type, const char *opts,
230 int freq, int passno)
231{
232 struct fs_info *fs;
233
234 if (!(fs = malloc(sizeof(struct fs_info))))
235 return NULL;
236
237 fs->device = string_copy(device);
238 fs->mountpt = string_copy(mntpnt);
239 fs->type = string_copy(type);
240 fs->opts = string_copy(opts ? opts : "");
241 fs->freq = freq;
242 fs->passno = passno;
243 fs->flags = 0;
244 fs->next = NULL;
245
246 if (!filesys_info)
247 filesys_info = fs;
248 else
249 filesys_last->next = fs;
250 filesys_last = fs;
251
252 return fs;
253}
254
255
256
257static int parse_fstab_line(char *line, struct fs_info **ret_fs)
258{
259 char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
260 struct fs_info *fs;
261
262 *ret_fs = 0;
263 strip_line(line);
264 if ((cp = strchr(line, '#')))
265 *cp = 0; /* Ignore everything after the comment char */
266 cp = line;
267
268 device = parse_word(&cp);
269 mntpnt = parse_word(&cp);
270 type = parse_word(&cp);
271 opts = parse_word(&cp);
272 freq = parse_word(&cp);
273 passno = parse_word(&cp);
274
275 if (!device)
276 return 0; /* Allow blank lines */
277
278 if (!mntpnt || !type)
279 return -1;
280
281 parse_escape(device);
282 parse_escape(mntpnt);
283 parse_escape(type);
284 parse_escape(opts);
285 parse_escape(freq);
286 parse_escape(passno);
287
288 dev = blkid_get_devname(cache, device, NULL);
289 if (dev)
290 device = dev;
291
292 if (strchr(type, ','))
293 type = 0;
294
295 fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
296 freq ? atoi(freq) : -1,
297 passno ? atoi(passno) : -1);
298 if (dev)
299 free(dev);
300
301 if (!fs)
302 return -1;
303 *ret_fs = fs;
304 return 0;
305}
306
307static void interpret_type(struct fs_info *fs)
308{
309 char *t;
310
311 if (strcmp(fs->type, "auto") != 0)
312 return;
313 t = blkid_get_tag_value(cache, "TYPE", fs->device);
314 if (t) {
315 free(fs->type);
316 fs->type = t;
317 }
318}
319
320/*
321 * Load the filesystem database from /etc/fstab
322 */
323static void load_fs_info(const char *filename)
324{
325 FILE *f;
326 char buf[1024];
327 int lineno = 0;
328 int old_fstab = 1;
329 struct fs_info *fs;
330
331 if ((f = fopen(filename, "r")) == NULL) {
332 fprintf(stderr, _("WARNING: couldn't open %s: %s\n"),
333 filename, strerror(errno));
334 return;
335 }
336 while (!feof(f)) {
337 lineno++;
338 if (!fgets(buf, sizeof(buf), f))
339 break;
340 buf[sizeof(buf)-1] = 0;
341 if (parse_fstab_line(buf, &fs) < 0) {
342 fprintf(stderr, _("WARNING: bad format "
343 "on line %d of %s\n"), lineno, filename);
344 continue;
345 }
346 if (!fs)
347 continue;
348 if (fs->passno < 0)
349 fs->passno = 0;
350 else
351 old_fstab = 0;
352 }
353
354 fclose(f);
355
356 if (old_fstab) {
357 fputs(_("\007\007\007"
358 "WARNING: Your /etc/fstab does not contain the fsck passno\n"
359 " field. I will kludge around things for you, but you\n"
360 " should fix your /etc/fstab file as soon as you can.\n\n"), stderr);
361
362 for (fs = filesys_info; fs; fs = fs->next) {
363 fs->passno = 1;
364 }
365 }
366}
367
368/* Lookup filesys in /etc/fstab and return the corresponding entry. */
369static struct fs_info *lookup(char *filesys)
370{
371 struct fs_info *fs;
372
373 /* No filesys name given. */
374 if (filesys == NULL)
375 return NULL;
376
377 for (fs = filesys_info; fs; fs = fs->next) {
378 if (!strcmp(filesys, fs->device) ||
379 (fs->mountpt && !strcmp(filesys, fs->mountpt)))
380 break;
381 }
382
383 return fs;
384}
385
386/* Find fsck program for a given fs type. */
387static char *find_fsck(char *type)
388{
389 char *s;
390 const char *tpl;
391 static char prog[256];
392 char *p = string_copy(fsck_path);
393 struct stat st;
394
395 /* Are we looking for a program or just a type? */
396 tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
397
398 for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
399 sprintf(prog, tpl, s, type);
400 if (stat(prog, &st) == 0) break;
401 }
402 free(p);
403 return(s ? prog : NULL);
404}
405
406static int progress_active(void)
407{
408 struct fsck_instance *inst;
409
410 for (inst = instance_list; inst; inst = inst->next) {
411 if (inst->flags & FLAG_DONE)
412 continue;
413 if (inst->flags & FLAG_PROGRESS)
414 return 1;
415 }
416 return 0;
417}
418
419/*
420 * Execute a particular fsck program, and link it into the list of
421 * child processes we are waiting for.
422 */
423static int execute(const char *type, const char *device, const char *mntpt,
424 int interactive)
425{
426 char *s, *argv[80], prog[80];
427 int argc, i;
428 struct fsck_instance *inst, *p;
429 pid_t pid;
430
431 inst = malloc(sizeof(struct fsck_instance));
432 if (!inst)
433 return ENOMEM;
434 memset(inst, 0, sizeof(struct fsck_instance));
435
436 sprintf(prog, "fsck.%s", type);
437 argv[0] = string_copy(prog);
438 argc = 1;
439
440 for (i=0; i <num_args; i++)
441 argv[argc++] = string_copy(args[i]);
442
443 if (progress && !progress_active()) {
444 if ((strcmp(type, "ext2") == 0) ||
445 (strcmp(type, "ext3") == 0)) {
446 char tmp[80];
447 snprintf(tmp, 80, "-C%d", progress_fd);
448 argv[argc++] = string_copy(tmp);
449 inst->flags |= FLAG_PROGRESS;
450 }
451 }
452
453 argv[argc++] = string_copy(device);
454 argv[argc] = 0;
455
456 s = find_fsck(prog);
457 if (s == NULL) {
458 fprintf(stderr, _("fsck: %s: not found\n"), prog);
459 return ENOENT;
460 }
461
462 if (verbose || noexecute) {
463 printf("[%s (%d) -- %s] ", s, num_running,
464 mntpt ? mntpt : device);
465 for (i=0; i < argc; i++)
466 printf("%s ", argv[i]);
467 printf("\n");
468 }
469
470 /* Fork and execute the correct program. */
471 if (noexecute)
472 pid = -1;
473 else if ((pid = fork()) < 0) {
474 perror("fork");
475 return errno;
476 } else if (pid == 0) {
477 if (!interactive)
478 close(0);
479 (void) execv(s, argv);
480 perror(argv[0]);
481 exit(EXIT_ERROR);
482 }
483
484 for (i=0; i < argc; i++)
485 free(argv[i]);
486
487 inst->pid = pid;
488 inst->prog = string_copy(prog);
489 inst->type = string_copy(type);
490 inst->device = string_copy(device);
491 inst->base_device = base_device(device);
492 inst->start_time = time(0);
493 inst->next = NULL;
494
495 /*
496 * Find the end of the list, so we add the instance on at the end.
497 */
498 for (p = instance_list; p && p->next; p = p->next);
499
500 if (p)
501 p->next = inst;
502 else
503 instance_list = inst;
504
505 return 0;
506}
507
508/*
509 * Send a signal to all outstanding fsck child processes
510 */
511static int kill_all(int signum)
512{
513 struct fsck_instance *inst;
514 int n = 0;
515
516 for (inst = instance_list; inst; inst = inst->next) {
517 if (inst->flags & FLAG_DONE)
518 continue;
519 kill(inst->pid, signum);
520 n++;
521 }
522 return n;
523}
524
525/*
526 * Wait for one child process to exit; when it does, unlink it from
527 * the list of executing child processes, and return it.
528 */
529static struct fsck_instance *wait_one(int flags)
530{
531 int status;
532 int sig;
533 struct fsck_instance *inst, *inst2, *prev;
534 pid_t pid;
535
536 if (!instance_list)
537 return NULL;
538
539 if (noexecute) {
540 inst = instance_list;
541 prev = 0;
542#ifdef RANDOM_DEBUG
543 while (inst->next && (random() & 1)) {
544 prev = inst;
545 inst = inst->next;
546 }
547#endif
548 inst->exit_status = 0;
549 goto ret_inst;
550 }
551
552 /*
553 * gcc -Wall fails saving throw against stupidity
554 * (inst and prev are thought to be uninitialized variables)
555 */
556 inst = prev = NULL;
557
558 do {
559 pid = waitpid(-1, &status, flags);
560 if (cancel_requested && !kill_sent) {
561 kill_all(SIGTERM);
562 kill_sent++;
563 }
564 if ((pid == 0) && (flags & WNOHANG))
565 return NULL;
566 if (pid < 0) {
567 if ((errno == EINTR) || (errno == EAGAIN))
568 continue;
569 if (errno == ECHILD) {
570 fprintf(stderr,
571 _("%s: wait: No more child process?!?\n"),
572 progname);
573 return NULL;
574 }
575 perror("wait");
576 continue;
577 }
578 for (prev = 0, inst = instance_list;
579 inst;
580 prev = inst, inst = inst->next) {
581 if (inst->pid == pid)
582 break;
583 }
584 } while (!inst);
585
586 if (WIFEXITED(status))
587 status = WEXITSTATUS(status);
588 else if (WIFSIGNALED(status)) {
589 sig = WTERMSIG(status);
590 if (sig == SIGINT) {
591 status = EXIT_UNCORRECTED;
592 } else {
593 printf(_("Warning... %s for device %s exited "
594 "with signal %d.\n"),
595 inst->prog, inst->device, sig);
596 status = EXIT_ERROR;
597 }
598 } else {
599 printf(_("%s %s: status is %x, should never happen.\n"),
600 inst->prog, inst->device, status);
601 status = EXIT_ERROR;
602 }
603 inst->exit_status = status;
604 if (progress && (inst->flags & FLAG_PROGRESS) &&
605 !progress_active()) {
606 for (inst2 = instance_list; inst2; inst2 = inst2->next) {
607 if (inst2->flags & FLAG_DONE)
608 continue;
609 if (strcmp(inst2->type, "ext2") &&
610 strcmp(inst2->type, "ext3"))
611 continue;
612 /*
613 * If we've just started the fsck, wait a tiny
614 * bit before sending the kill, to give it
615 * time to set up the signal handler
616 */
617 if (inst2->start_time < time(0)+2) {
618 if (fork() == 0) {
619 sleep(1);
620 kill(inst2->pid, SIGUSR1);
621 exit(0);
622 }
623 } else
624 kill(inst2->pid, SIGUSR1);
625 inst2->flags |= FLAG_PROGRESS;
626 break;
627 }
628 }
629ret_inst:
630 if (prev)
631 prev->next = inst->next;
632 else
633 instance_list = inst->next;
634 if (verbose > 1)
635 printf(_("Finished with %s (exit status %d)\n"),
636 inst->device, inst->exit_status);
637 num_running--;
638 return inst;
639}
640
641#define FLAG_WAIT_ALL 0
642#define FLAG_WAIT_ATLEAST_ONE 1
643/*
644 * Wait until all executing child processes have exited; return the
645 * logical OR of all of their exit code values.
646 */
647static int wait_many(int flags)
648{
649 struct fsck_instance *inst;
650 int global_status = 0;
651 int wait_flags = 0;
652
653 while ((inst = wait_one(wait_flags))) {
654 global_status |= inst->exit_status;
655 free_instance(inst);
656#ifdef RANDOM_DEBUG
657 if (noexecute && (flags & WNOHANG) && !(random() % 3))
658 break;
659#endif
660 if (flags & FLAG_WAIT_ATLEAST_ONE)
661 wait_flags = WNOHANG;
662 }
663 return global_status;
664}
665
666/*
667 * Run the fsck program on a particular device
668 *
669 * If the type is specified using -t, and it isn't prefixed with "no"
670 * (as in "noext2") and only one filesystem type is specified, then
671 * use that type regardless of what is specified in /etc/fstab.
672 *
673 * If the type isn't specified by the user, then use either the type
674 * specified in /etc/fstab, or DEFAULT_FSTYPE.
675 */
676static void fsck_device(struct fs_info *fs, int interactive)
677{
678 const char *type;
679 int retval;
680
681 interpret_type(fs);
682
683 if (strcmp(fs->type, "auto") != 0)
684 type = fs->type;
685 else if (fstype && strncmp(fstype, "no", 2) &&
686 strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
687 !strchr(fstype, ','))
688 type = fstype;
689 else
690 type = DEFAULT_FSTYPE;
691
692 num_running++;
693 retval = execute(type, fs->device, fs->mountpt, interactive);
694 if (retval) {
695 fprintf(stderr, _("%s: Error %d while executing fsck.%s "
696 "for %s\n"), progname, retval, type, fs->device);
697 num_running--;
698 }
699}
700
701
702/*
703 * Deal with the fsck -t argument.
704 */
705struct fs_type_compile {
706 char **list;
707 int *type;
708 int negate;
709} fs_type_compiled;
710
711#define FS_TYPE_NORMAL 0
712#define FS_TYPE_OPT 1
713#define FS_TYPE_NEGOPT 2
714
715static const char *fs_type_syntax_error =
716N_("Either all or none of the filesystem types passed to -t must be prefixed\n"
717 "with 'no' or '!'.\n");
718
719static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
720{
721 char *cp, *list, *s;
722 int num = 2;
723 int negate, first_negate = 1;
724
725 if (fs_type) {
726 for (cp=fs_type; *cp; cp++) {
727 if (*cp == ',')
728 num++;
729 }
730 }
731
732 cmp->list = malloc(num * sizeof(char *));
733 cmp->type = malloc(num * sizeof(int));
734 if (!cmp->list || !cmp->type) {
735 fputs(_("Couldn't allocate memory for filesystem types\n"),
736 stderr);
737 exit(EXIT_ERROR);
738 }
739 memset(cmp->list, 0, num * sizeof(char *));
740 memset(cmp->type, 0, num * sizeof(int));
741 cmp->negate = 0;
742
743 if (!fs_type)
744 return;
745
746 list = string_copy(fs_type);
747 num = 0;
748 s = strtok(list, ",");
749 while(s) {
750 negate = 0;
751 if (strncmp(s, "no", 2) == 0) {
752 s += 2;
753 negate = 1;
754 } else if (*s == '!') {
755 s++;
756 negate = 1;
757 }
758 if (strcmp(s, "loop") == 0)
759 /* loop is really short-hand for opts=loop */
760 goto loop_special_case;
761 else if (strncmp(s, "opts=", 5) == 0) {
762 s += 5;
763 loop_special_case:
764 cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
765 } else {
766 if (first_negate) {
767 cmp->negate = negate;
768 first_negate = 0;
769 }
770 if ((negate && !cmp->negate) ||
771 (!negate && cmp->negate)) {
772 fputs(_(fs_type_syntax_error), stderr);
773 exit(EXIT_USAGE);
774 }
775 }
776#if 0
777 printf("Adding %s to list (type %d).\n", s, cmp->type[num]);
778#endif
779 cmp->list[num++] = string_copy(s);
780 s = strtok(NULL, ",");
781 }
782 free(list);
783}
784
785/*
786 * This function returns true if a particular option appears in a
787 * comma-delimited options list
788 */
789static int opt_in_list(char *opt, char *optlist)
790{
791 char *list, *s;
792
793 if (!optlist)
794 return 0;
795 list = string_copy(optlist);
796
797 s = strtok(list, ",");
798 while(s) {
799 if (strcmp(s, opt) == 0) {
800 free(list);
801 return 1;
802 }
803 s = strtok(NULL, ",");
804 }
805 free(list);
806 return 0;
807}
808
809/* See if the filesystem matches the criteria given by the -t option */
810static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
811{
812 int n, ret = 0, checked_type = 0;
813 char *cp;
814
815 if (cmp->list == 0 || cmp->list[0] == 0)
816 return 1;
817
818 for (n=0; (cp = cmp->list[n]); n++) {
819 switch (cmp->type[n]) {
820 case FS_TYPE_NORMAL:
821 checked_type++;
822 if (strcmp(cp, fs->type) == 0) {
823 ret = 1;
824 }
825 break;
826 case FS_TYPE_NEGOPT:
827 if (opt_in_list(cp, fs->opts))
828 return 0;
829 break;
830 case FS_TYPE_OPT:
831 if (!opt_in_list(cp, fs->opts))
832 return 0;
833 break;
834 }
835 }
836 if (checked_type == 0)
837 return 1;
838 return (cmp->negate ? !ret : ret);
839}
840
841/* Check if we should ignore this filesystem. */
842static int ignore(struct fs_info *fs)
843{
844 const char **ip;
845 int wanted = 0;
846
847 /*
848 * If the pass number is 0, ignore it.
849 */
850 if (fs->passno == 0)
851 return 1;
852
853 interpret_type(fs);
854
855 /*
856 * If a specific fstype is specified, and it doesn't match,
857 * ignore it.
858 */
859 if (!fs_match(fs, &fs_type_compiled)) return 1;
860
861 /* Are we ignoring this type? */
862 for(ip = ignored_types; *ip; ip++)
863 if (strcmp(fs->type, *ip) == 0) return 1;
864
865 /* Do we really really want to check this fs? */
866 for(ip = really_wanted; *ip; ip++)
867 if (strcmp(fs->type, *ip) == 0) {
868 wanted = 1;
869 break;
870 }
871
872 /* See if the <fsck.fs> program is available. */
873 if (find_fsck(fs->type) == NULL) {
874 if (wanted)
875 fprintf(stderr, _("fsck: cannot check %s: fsck.%s not found\n"),
876 fs->device, fs->type);
877 return 1;
878 }
879
880 /* We can and want to check this file system type. */
881 return 0;
882}
883
884/*
885 * Returns TRUE if a partition on the same disk is already being
886 * checked.
887 */
888static int device_already_active(char *device)
889{
890 struct fsck_instance *inst;
891 char *base;
892
893 if (force_all_parallel)
894 return 0;
895
896#ifdef BASE_MD
897 /* Don't check a soft raid disk with any other disk */
898 if (instance_list &&
899 (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
900 !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
901 return 1;
902#endif
903
904 base = base_device(device);
905 /*
906 * If we don't know the base device, assume that the device is
907 * already active if there are any fsck instances running.
908 */
909 if (!base)
910 return (instance_list != 0);
911 for (inst = instance_list; inst; inst = inst->next) {
912 if (!inst->base_device || !strcmp(base, inst->base_device)) {
913 free(base);
914 return 1;
915 }
916 }
917 free(base);
918 return 0;
919}
920
921/* Check all file systems, using the /etc/fstab table. */
922static int check_all(void)
923{
924 struct fs_info *fs = NULL;
925 int status = EXIT_OK;
926 int not_done_yet = 1;
927 int passno = 1;
928 int pass_done;
929
930 if (verbose)
931 fputs(_("Checking all file systems.\n"), stdout);
932
933 /*
934 * Do an initial scan over the filesystem; mark filesystems
935 * which should be ignored as done, and resolve any "auto"
936 * filesystem types (done as a side-effect of calling ignore()).
937 */
938 for (fs = filesys_info; fs; fs = fs->next) {
939 if (ignore(fs))
940 fs->flags |= FLAG_DONE;
941 }
942
943 /*
944 * Find and check the root filesystem.
945 */
946 if (!parallel_root) {
947 for (fs = filesys_info; fs; fs = fs->next) {
948 if (!strcmp(fs->mountpt, "/"))
949 break;
950 }
951 if (fs) {
952 if (!skip_root && !ignore(fs)) {
953 fsck_device(fs, 1);
954 status |= wait_many(FLAG_WAIT_ALL);
955 if (status > EXIT_NONDESTRUCT)
956 return status;
957 }
958 fs->flags |= FLAG_DONE;
959 }
960 }
961 /*
962 * This is for the bone-headed user who enters the root
963 * filesystem twice. Skip root will skep all root entries.
964 */
965 if (skip_root)
966 for (fs = filesys_info; fs; fs = fs->next)
967 if (!strcmp(fs->mountpt, "/"))
968 fs->flags |= FLAG_DONE;
969
970 while (not_done_yet) {
971 not_done_yet = 0;
972 pass_done = 1;
973
974 for (fs = filesys_info; fs; fs = fs->next) {
975 if (cancel_requested)
976 break;
977 if (fs->flags & FLAG_DONE)
978 continue;
979 /*
980 * If the filesystem's pass number is higher
981 * than the current pass number, then we don't
982 * do it yet.
983 */
984 if (fs->passno > passno) {
985 not_done_yet++;
986 continue;
987 }
988 /*
989 * If a filesystem on a particular device has
990 * already been spawned, then we need to defer
991 * this to another pass.
992 */
993 if (device_already_active(fs->device)) {
994 pass_done = 0;
995 continue;
996 }
997 /*
998 * Spawn off the fsck process
999 */
1000 fsck_device(fs, serialize);
1001 fs->flags |= FLAG_DONE;
1002
1003 /*
1004 * Only do one filesystem at a time, or if we
1005 * have a limit on the number of fsck's extant
1006 * at one time, apply that limit.
1007 */
1008 if (serialize ||
1009 (max_running && (num_running >= max_running))) {
1010 pass_done = 0;
1011 break;
1012 }
1013 }
1014 if (cancel_requested)
1015 break;
1016 if (verbose > 1)
1017 printf(_("--waiting-- (pass %d)\n"), passno);
1018 status |= wait_many(pass_done ? FLAG_WAIT_ALL :
1019 FLAG_WAIT_ATLEAST_ONE);
1020 if (pass_done) {
1021 if (verbose > 1)
1022 printf("----------------------------------\n");
1023 passno++;
1024 } else
1025 not_done_yet++;
1026 }
1027 if (cancel_requested && !kill_sent) {
1028 kill_all(SIGTERM);
1029 kill_sent++;
1030 }
1031 status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
1032 return status;
1033}
1034
1035#if 0
1036static void usage(void)
1037{
1038 fputs(_("Usage: fsck [-ANPRTV] [ -C [ fd ] ] [-t fstype] [fs-options] [filesys ...]\n"), stderr);
1039 exit(EXIT_USAGE);
1040}
1041#endif
1042
1043#ifdef HAVE_SIGNAL_H
1044static void signal_cancel(int sig FSCK_ATTR((unused)))
1045{
1046 cancel_requested++;
1047}
1048#endif
1049
1050static void PRS(int argc, char *argv[])
1051{
1052 int i, j;
1053 char *arg, *dev, *tmp = 0;
1054 char options[128];
1055 int opt = 0;
1056 int opts_for_fsck = 0;
1057#ifdef HAVE_SIGNAL_H
1058 struct sigaction sa;
1059
1060 /*
1061 * Set up signal action
1062 */
1063 memset(&sa, 0, sizeof(struct sigaction));
1064 sa.sa_handler = signal_cancel;
1065 sigaction(SIGINT, &sa, 0);
1066 sigaction(SIGTERM, &sa, 0);
1067#endif
1068
1069 num_devices = 0;
1070 num_args = 0;
1071 instance_list = 0;
1072
1073 progname = argv[0];
1074
1075 for (i=1; i < argc; i++) {
1076 arg = argv[i];
1077 if (!arg)
1078 continue;
1079 if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
1080 if (num_devices >= MAX_DEVICES) {
1081 fprintf(stderr, _("%s: too many devices\n"),
1082 progname);
1083 exit(EXIT_ERROR);
1084 }
1085 dev = blkid_get_devname(cache, arg, NULL);
1086 if (!dev && strchr(arg, '=')) {
1087 /*
1088 * Check to see if we failed because
1089 * /proc/partitions isn't found.
1090 */
1091 if (access("/proc/partitions", R_OK) < 0) {
1092 fprintf(stderr, "Couldn't open /proc/partitions: %s\n",
1093 strerror(errno));
1094 fprintf(stderr, "Is /proc mounted?\n");
1095 exit(EXIT_ERROR);
1096 }
1097 /*
1098 * Check to see if this is because
1099 * we're not running as root
1100 */
1101 if (geteuid())
1102 fprintf(stderr,
1103 "Must be root to scan for matching filesystems: %s\n", arg);
1104 else
1105 fprintf(stderr,
1106 "Couldn't find matching filesystem: %s\n", arg);
1107 exit(EXIT_ERROR);
1108 }
1109 devices[num_devices++] = dev ? dev : string_copy(arg);
1110 continue;
1111 }
1112 if (arg[0] != '-' || opts_for_fsck) {
1113 if (num_args >= MAX_ARGS) {
1114 fprintf(stderr, _("%s: too many arguments\n"),
1115 progname);
1116 exit(EXIT_ERROR);
1117 }
1118 args[num_args++] = string_copy(arg);
1119 continue;
1120 }
1121 for (j=1; arg[j]; j++) {
1122 if (opts_for_fsck) {
1123 options[++opt] = arg[j];
1124 continue;
1125 }
1126 switch (arg[j]) {
1127 case 'A':
1128 doall++;
1129 break;
1130 case 'C':
1131 progress++;
1132 if (arg[j+1]) {
1133 progress_fd = string_to_int(arg+j+1);
1134 if (progress_fd < 0)
1135 progress_fd = 0;
1136 else
1137 goto next_arg;
1138 } else if ((i+1) < argc &&
1139 !strncmp(argv[i+1], "-", 1) == 0) {
1140 progress_fd = string_to_int(argv[i]);
1141 if (progress_fd < 0)
1142 progress_fd = 0;
1143 else {
1144 goto next_arg;
1145 i++;
1146 }
1147 }
1148 break;
1149 case 'V':
1150 verbose++;
1151 break;
1152 case 'N':
1153 noexecute++;
1154 break;
1155 case 'R':
1156 skip_root++;
1157 break;
1158 case 'T':
1159 notitle++;
1160 break;
1161 case 'M':
1162 like_mount++;
1163 break;
1164 case 'P':
1165 parallel_root++;
1166 break;
1167 case 's':
1168 serialize++;
1169 break;
1170 case 't':
1171 tmp = 0;
1172 if (fstype)
1173 usage();
1174 if (arg[j+1])
1175 tmp = arg+j+1;
1176 else if ((i+1) < argc)
1177 tmp = argv[++i];
1178 else
1179 usage();
1180 fstype = string_copy(tmp);
1181 compile_fs_type(fstype, &fs_type_compiled);
1182 goto next_arg;
1183 case '-':
1184 opts_for_fsck++;
1185 break;
1186 case '?':
1187 usage();
1188 break;
1189 default:
1190 options[++opt] = arg[j];
1191 break;
1192 }
1193 }
1194 next_arg:
1195 if (opt) {
1196 options[0] = '-';
1197 options[++opt] = '\0';
1198 if (num_args >= MAX_ARGS) {
1199 fprintf(stderr,
1200 _("%s: too many arguments\n"),
1201 progname);
1202 exit(EXIT_ERROR);
1203 }
1204 args[num_args++] = string_copy(options);
1205 opt = 0;
1206 }
1207 }
1208 if (getenv("FSCK_FORCE_ALL_PARALLEL"))
1209 force_all_parallel++;
1210 if ((tmp = getenv("FSCK_MAX_INST")))
1211 max_running = atoi(tmp);
1212}
1213
1214int fsck_main(int argc, char *argv[])
1215{
1216 int i, status = 0;
1217 int interactive = 0;
1218 char *oldpath = getenv("PATH");
1219 const char *fstab;
1220 struct fs_info *fs;
1221
1222 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
1223 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
1224
1225#ifdef ENABLE_NLS
1226 setlocale(LC_MESSAGES, "");
1227 setlocale(LC_CTYPE, "");
1228 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1229 textdomain(NLS_CAT_NAME);
1230#endif
1231 blkid_get_cache(&cache, NULL);
1232 PRS(argc, argv);
1233
1234 if (!notitle)
1235 printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
1236
1237 fstab = getenv("FSTAB_FILE");
1238 if (!fstab)
1239 fstab = _PATH_MNTTAB;
1240 load_fs_info(fstab);
1241
1242 /* Update our search path to include uncommon directories. */
1243 if (oldpath) {
1244 fsck_path = malloc (strlen (fsck_prefix_path) + 1 +
1245 strlen (oldpath) + 1);
1246 if (!fsck_path) {
1247 fprintf(stderr, "%s: Unable to allocate memory for fsck_path\n", progname);
1248 exit(EXIT_ERROR);
1249 }
1250 strcpy (fsck_path, fsck_prefix_path);
1251 strcat (fsck_path, ":");
1252 strcat (fsck_path, oldpath);
1253 } else {
1254 fsck_path = string_copy(fsck_prefix_path);
1255 }
1256
1257 if ((num_devices == 1) || (serialize))
1258 interactive = 1;
1259
1260 /* If -A was specified ("check all"), do that! */
1261 if (doall)
1262 return check_all();
1263
1264 if (num_devices == 0) {
1265 serialize++;
1266 interactive++;
1267 return check_all();
1268 }
1269 for (i = 0 ; i < num_devices; i++) {
1270 if (cancel_requested) {
1271 if (!kill_sent) {
1272 kill_all(SIGTERM);
1273 kill_sent++;
1274 }
1275 break;
1276 }
1277 fs = lookup(devices[i]);
1278 if (!fs) {
1279 fs = create_fs_device(devices[i], 0, "auto",
1280 0, -1, -1);
1281 if (!fs)
1282 continue;
1283 }
1284 fsck_device(fs, interactive);
1285 if (serialize ||
1286 (max_running && (num_running >= max_running))) {
1287 struct fsck_instance *inst;
1288
1289 inst = wait_one(0);
1290 if (inst) {
1291 status |= inst->exit_status;
1292 free_instance(inst);
1293 }
1294 if (verbose > 1)
1295 printf("----------------------------------\n");
1296 }
1297 }
1298 status |= wait_many(FLAG_WAIT_ALL);
1299 free(fsck_path);
1300 blkid_put_cache(cache);
1301 return status;
1302}
diff --git a/e2fsprogs/fsck.h b/e2fsprogs/fsck.h
new file mode 100644
index 000000000..59626122f
--- /dev/null
+++ b/e2fsprogs/fsck.h
@@ -0,0 +1,59 @@
1/*
2 * fsck.h
3 */
4
5#include <time.h>
6
7#define FSCK_ATTR(x) __attribute__(x)
8
9
10#ifndef DEFAULT_FSTYPE
11#define DEFAULT_FSTYPE "ext2"
12#endif
13
14#define MAX_DEVICES 32
15#define MAX_ARGS 32
16
17#define EXIT_OK 0
18#define EXIT_NONDESTRUCT 1
19#define EXIT_DESTRUCT 2
20#define EXIT_UNCORRECTED 4
21#define EXIT_ERROR 8
22#define EXIT_USAGE 16
23#define EXIT_LIBRARY 128
24
25/*
26 * Internal structure for mount tabel entries.
27 */
28
29struct fs_info {
30 char *device;
31 char *mountpt;
32 char *type;
33 char *opts;
34 int freq;
35 int passno;
36 int flags;
37 struct fs_info *next;
38};
39
40#define FLAG_DONE 1
41#define FLAG_PROGRESS 2
42
43/*
44 * Structure to allow exit codes to be stored
45 */
46struct fsck_instance {
47 int pid;
48 int flags;
49 int exit_status;
50 time_t start_time;
51 char * prog;
52 char * type;
53 char * device;
54 char * base_device;
55 struct fsck_instance *next;
56};
57
58extern char *base_device(const char *device);
59extern const char *identify_fs(const char *fs_name, const char *fs_types);
diff --git a/include/applets.h b/include/applets.h
index 0b4faacaa..a814ce1b1 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -182,6 +182,9 @@
182#ifdef CONFIG_DUMPLEASES 182#ifdef CONFIG_DUMPLEASES
183 APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) 183 APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
184#endif 184#endif
185#ifdef CONFIG_E2FSCK
186 APPLET(e2fsck, e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
187#endif
185#ifdef CONFIG_E2LABEL 188#ifdef CONFIG_E2LABEL
186 APPLET_NOUSAGE("e2label", tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER) 189 APPLET_NOUSAGE("e2label", tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
187#endif 190#endif
@@ -239,6 +242,13 @@
239#ifdef CONFIG_FREERAMDISK 242#ifdef CONFIG_FREERAMDISK
240 APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER) 243 APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
241#endif 244#endif
245#ifdef CONFIG_FSCK
246 APPLET(fsck, fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
247#endif
248#ifdef CONFIG_E2FSCK
249 APPLET_NOUSAGE("fsck.ext2", e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
250 APPLET_NOUSAGE("fsck.ext3", e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
251#endif
242#ifdef CONFIG_FSCK_MINIX 252#ifdef CONFIG_FSCK_MINIX
243 APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix) 253 APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix)
244#endif 254#endif
diff --git a/include/usage.h b/include/usage.h
index c4516e0eb..fee8bc889 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -546,6 +546,26 @@
546 "\t-r,\t--remaining\tInterpret lease times as time remaing\n" \ 546 "\t-r,\t--remaining\tInterpret lease times as time remaing\n" \
547 "\t-a,\t--absolute\tInterpret lease times as expire time" 547 "\t-a,\t--absolute\tInterpret lease times as expire time"
548 548
549#define e2fsck_trivial_usage \
550 "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " \
551 "[-I inode_buffer_blocks] [-P process_inode_size] " \
552 "[-l|-L bad_blocks_file] [-C fd] [-j ext-journal] " \
553 "[-E extended-options] device"
554#define e2fsck_full_usage \
555 "Check a Linux ext2/ext3 file system.\n\n" \
556 "Options:\n" \
557 "\t-p\tAutomatic repair (no questions)\n" \
558 "\t-n\tMake no changes to the filesystem\n" \
559 "\t-y\tAssume 'yes' to all questions\n" \
560 "\t-c\tCheck for bad blocks and add them to the badblock list\n" \
561 "\t-f\tForce checking even if filesystem is marked clean\n" \
562 "\t-v\tBe verbose\n" \
563 "\t-b superblock\tUse alternative superblock\n" \
564 "\t-B blocksize\tForce blocksize when looking for superblock\n" \
565 "\t-j journal\tSet location of the external journal\n" \
566 "\t-l file\tAdd to badblocks list\n" \
567 "\t-L file\tSet badblocks list"
568
549#ifdef CONFIG_FEATURE_FANCY_ECHO 569#ifdef CONFIG_FEATURE_FANCY_ECHO
550# define USAGE_FANCY_ECHO(a) a 570# define USAGE_FANCY_ECHO(a) a
551#else 571#else
@@ -764,6 +784,20 @@
764#define freeramdisk_example_usage \ 784#define freeramdisk_example_usage \
765 "$ freeramdisk /dev/ram2\n" 785 "$ freeramdisk /dev/ram2\n"
766 786
787#define fsck_trivial_usage \
788 "[-ANPRTV] [ -C [ fd ] ] [-t fstype] [fs-options] [filesys ...]"
789#define fsck_full_usage \
790 "Check and repair filesystems.\n\n" \
791 "Options:\n" \
792 "\t-A\tWalk /etc/fstab and check all filesystems\n" \
793 "\t-N\tDon't execute, just show what would be done\n" \
794 "\t-P\tWhen using -A, check filesystems in parallel\n" \
795 "\t-R\tWhen using -A, skip the root filesystem\n" \
796 "\t-T\tDon't show title on startup\n" \
797 "\t-V\tVerbose mode\n" \
798 "\t-C\tWrite status information to specified filedescriptor\n" \
799 "\t-t\tList of filesystem types to check"
800
767#define fsck_minix_trivial_usage \ 801#define fsck_minix_trivial_usage \
768 "[-larvsmf] /dev/name" 802 "[-larvsmf] /dev/name"
769#define fsck_minix_full_usage \ 803#define fsck_minix_full_usage \