1 /* $NetBSD: pass2.c,v 1.15 2009/10/19 18:41:08 bouyer Exp $ */
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * Copyright (c) 1997 Manuel Bouyer.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
45 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
47 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
48 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
49 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
53 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 #include <sys/cdefs.h>
59 static char sccsid
[] = "@(#)pass2.c 8.6 (Berkeley) 10/27/94";
61 __RCSID("$NetBSD: pass2.c,v 1.15 2009/10/19 18:41:08 bouyer Exp $");
65 #include <sys/param.h>
67 #include <ufs/ext2fs/ext2fs_dinode.h>
68 #include <ufs/ext2fs/ext2fs_dir.h>
69 #include <ufs/ext2fs/ext2fs.h>
71 #include <ufs/ufs/dinode.h> /* for IFMT & friends */
80 #include "exitvalues.h"
82 #define MINDIRSIZE (sizeof (struct ext2fs_dirtemplate))
84 static int pass2check(struct inodesc
*);
85 static int blksort(const void *, const void *);
90 struct ext2fs_dinode
*dp
;
91 struct inoinfo
**inpp
, *inp
;
92 struct inoinfo
**inpend
;
93 struct inodesc curino
;
94 struct ext2fs_dinode dino
;
95 char pathbuf
[MAXPATHLEN
+ 1];
97 switch (statemap
[EXT2_ROOTINO
]) {
100 pfatal("ROOT INODE UNALLOCATED");
101 if (reply("ALLOCATE") == 0)
102 exit(FSCK_EXIT_CHECK_FAILED
);
103 if (allocdir(EXT2_ROOTINO
, EXT2_ROOTINO
, 0755) != EXT2_ROOTINO
)
104 errexit("CANNOT ALLOCATE ROOT INODE");
108 pfatal("DUPS/BAD IN ROOT INODE");
109 if (reply("REALLOCATE")) {
110 freeino(EXT2_ROOTINO
);
111 if (allocdir(EXT2_ROOTINO
, EXT2_ROOTINO
, 0755) != EXT2_ROOTINO
)
112 errexit("CANNOT ALLOCATE ROOT INODE");
115 if (reply("CONTINUE") == 0)
116 exit(FSCK_EXIT_CHECK_FAILED
);
121 pfatal("ROOT INODE NOT DIRECTORY");
122 if (reply("REALLOCATE")) {
123 freeino(EXT2_ROOTINO
);
124 if (allocdir(EXT2_ROOTINO
, EXT2_ROOTINO
, 0755) != EXT2_ROOTINO
)
125 errexit("CANNOT ALLOCATE ROOT INODE");
128 if (reply("FIX") == 0)
129 exit(FSCK_EXIT_CHECK_FAILED
);
130 dp
= ginode(EXT2_ROOTINO
);
131 dp
->e2di_mode
= h2fs16((fs2h16(dp
->e2di_mode
) & ~IFMT
) | IFDIR
);
139 errexit("BAD STATE %d FOR ROOT INODE", statemap
[EXT2_ROOTINO
]);
143 * Sort the directory list into disk block order.
145 qsort((char *)inpsort
, (size_t)inplast
, sizeof *inpsort
, blksort
);
147 * Check the integrity of each directory.
149 memset(&curino
, 0, sizeof(struct inodesc
));
150 curino
.id_type
= DATA
;
151 curino
.id_func
= pass2check
;
152 inpend
= &inpsort
[inplast
];
153 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
155 if (inp
->i_isize
== 0)
157 if (inp
->i_isize
< MINDIRSIZE
) {
158 direrror(inp
->i_number
, "DIRECTORY TOO SHORT");
159 inp
->i_isize
= roundup(MINDIRSIZE
, sblock
.e2fs_bsize
);
160 if (reply("FIX") == 1) {
161 dp
= ginode(inp
->i_number
);
162 inossize(dp
, inp
->i_isize
);
165 } else if ((inp
->i_isize
& (sblock
.e2fs_bsize
- 1)) != 0) {
166 getpathname(pathbuf
, sizeof(pathbuf
), inp
->i_number
,
168 pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
169 pathbuf
, (u_long
)inp
->i_isize
, sblock
.e2fs_bsize
);
171 printf(" (ADJUSTED)\n");
172 inp
->i_isize
= roundup(inp
->i_isize
, sblock
.e2fs_bsize
);
173 if (preen
|| reply("ADJUST") == 1) {
174 dp
= ginode(inp
->i_number
);
175 inossize(dp
, inp
->i_isize
);
179 memset(&dino
, 0, sizeof(struct ext2fs_dinode
));
180 dino
.e2di_mode
= h2fs16(IFDIR
);
181 inossize(&dino
, inp
->i_isize
);
182 memcpy(&dino
.e2di_blocks
[0], &inp
->i_blks
[0], (size_t)inp
->i_numblks
);
183 curino
.id_number
= inp
->i_number
;
184 curino
.id_parent
= inp
->i_parent
;
185 (void)ckinode(&dino
, &curino
);
188 * Now that the parents of all directories have been found,
189 * make another pass to verify the value of `..'
191 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
193 if (inp
->i_parent
== 0 || inp
->i_isize
== 0)
195 if (inp
->i_dotdot
== inp
->i_parent
||
196 inp
->i_dotdot
== (ino_t
)-1)
198 if (inp
->i_dotdot
== 0) {
199 inp
->i_dotdot
= inp
->i_parent
;
200 fileerror(inp
->i_parent
, inp
->i_number
, "MISSING '..'");
201 if (reply("FIX") == 0)
203 (void)makeentry(inp
->i_number
, inp
->i_parent
, "..");
204 lncntp
[inp
->i_parent
]--;
207 fileerror(inp
->i_parent
, inp
->i_number
,
208 "BAD INODE NUMBER FOR '..'");
209 if (reply("FIX") == 0)
211 lncntp
[inp
->i_dotdot
]++;
212 lncntp
[inp
->i_parent
]--;
213 inp
->i_dotdot
= inp
->i_parent
;
214 (void)changeino(inp
->i_number
, "..", inp
->i_parent
);
217 * Mark all the directories that can be found from the root.
223 pass2check(struct inodesc
*idesc
)
225 struct ext2fs_direct
*dirp
= idesc
->id_dirp
;
227 int n
, entrysize
, ret
= 0;
228 struct ext2fs_dinode
*dp
;
230 struct ext2fs_direct proto
;
231 char namebuf
[MAXPATHLEN
+ 1];
232 char pathbuf
[MAXPATHLEN
+ 1];
237 if (idesc
->id_entryno
!= 0)
239 if (fs2h32(dirp
->e2d_ino
) != 0 && dirp
->e2d_namlen
== 1 &&
240 dirp
->e2d_name
[0] == '.') {
241 if (fs2h32(dirp
->e2d_ino
) != idesc
->id_number
) {
242 direrror(idesc
->id_number
, "BAD INODE NUMBER FOR '.'");
243 dirp
->e2d_ino
= h2fs32(idesc
->id_number
);
244 if (reply("FIX") == 1)
247 if (sblock
.e2fs
.e2fs_rev
> E2FS_REV0
&&
248 (sblock
.e2fs
.e2fs_features_incompat
& EXT2F_INCOMPAT_FTYPE
)
249 && (dirp
->e2d_type
!= EXT2_FT_DIR
)) {
250 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '.'");
251 dirp
->e2d_type
= EXT2_FT_DIR
;
252 if (reply("FIX") == 1)
257 direrror(idesc
->id_number
, "MISSING '.'");
258 proto
.e2d_ino
= h2fs32(idesc
->id_number
);
259 proto
.e2d_namlen
= 1;
260 if (sblock
.e2fs
.e2fs_rev
> E2FS_REV0
&&
261 (sblock
.e2fs
.e2fs_features_incompat
& EXT2F_INCOMPAT_FTYPE
))
262 proto
.e2d_type
= EXT2_FT_DIR
;
265 (void)strlcpy(proto
.e2d_name
, ".", sizeof(proto
.e2d_name
));
266 entrysize
= EXT2FS_DIRSIZ(proto
.e2d_namlen
);
267 if (fs2h32(dirp
->e2d_ino
) != 0 && strcmp(dirp
->e2d_name
, "..") != 0) {
268 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
270 } else if (fs2h16(dirp
->e2d_reclen
) < entrysize
) {
271 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
272 } else if (fs2h16(dirp
->e2d_reclen
) < 2 * entrysize
) {
273 proto
.e2d_reclen
= dirp
->e2d_reclen
;
274 memcpy(dirp
, &proto
, (size_t)entrysize
);
275 if (reply("FIX") == 1)
278 n
= fs2h16(dirp
->e2d_reclen
) - entrysize
;
279 proto
.e2d_reclen
= h2fs16(entrysize
);
280 memcpy(dirp
, &proto
, (size_t)entrysize
);
282 lncntp
[fs2h32(dirp
->e2d_ino
)]--;
283 dirp
= (struct ext2fs_direct
*)((char *)(dirp
) + entrysize
);
284 memset(dirp
, 0, (size_t)n
);
285 dirp
->e2d_reclen
= h2fs16(n
);
286 if (reply("FIX") == 1)
290 if (idesc
->id_entryno
> 1)
292 inp
= getinoinfo(idesc
->id_number
);
293 proto
.e2d_ino
= h2fs32(inp
->i_parent
);
294 proto
.e2d_namlen
= 2;
295 if (sblock
.e2fs
.e2fs_rev
> E2FS_REV0
&&
296 (sblock
.e2fs
.e2fs_features_incompat
& EXT2F_INCOMPAT_FTYPE
))
297 proto
.e2d_type
= EXT2_FT_DIR
;
300 (void)strlcpy(proto
.e2d_name
, "..", sizeof(proto
.e2d_name
));
301 entrysize
= EXT2FS_DIRSIZ(2);
302 if (idesc
->id_entryno
== 0) {
303 n
= EXT2FS_DIRSIZ(dirp
->e2d_namlen
);
304 if (fs2h16(dirp
->e2d_reclen
) < n
+ entrysize
)
306 proto
.e2d_reclen
= h2fs16(fs2h16(dirp
->e2d_reclen
) - n
);
307 dirp
->e2d_reclen
= h2fs16(n
);
309 lncntp
[fs2h32(dirp
->e2d_ino
)]--;
310 dirp
= (struct ext2fs_direct
*)((char *)(dirp
) + n
);
311 memset(dirp
, 0, (size_t)fs2h16(proto
.e2d_reclen
));
312 dirp
->e2d_reclen
= proto
.e2d_reclen
;
314 if (fs2h32(dirp
->e2d_ino
) != 0 &&
315 dirp
->e2d_namlen
== 2 &&
316 strncmp(dirp
->e2d_name
, "..", 2) == 0) {
317 inp
->i_dotdot
= fs2h32(dirp
->e2d_ino
);
318 if (sblock
.e2fs
.e2fs_rev
> E2FS_REV0
&&
319 (sblock
.e2fs
.e2fs_features_incompat
& EXT2F_INCOMPAT_FTYPE
)
320 && dirp
->e2d_type
!= EXT2_FT_DIR
) {
321 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '..'");
322 dirp
->e2d_type
= EXT2_FT_DIR
;
323 if (reply("FIX") == 1)
328 if (fs2h32(dirp
->e2d_ino
) != 0 &&
329 dirp
->e2d_namlen
== 1 &&
330 strncmp(dirp
->e2d_name
, ".", 1) != 0) {
331 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
332 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
334 inp
->i_dotdot
= (ino_t
)-1;
335 } else if (fs2h16(dirp
->e2d_reclen
) < entrysize
) {
336 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
337 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
338 inp
->i_dotdot
= (ino_t
)-1;
339 } else if (inp
->i_parent
!= 0) {
341 * We know the parent, so fix now.
343 inp
->i_dotdot
= inp
->i_parent
;
344 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
345 proto
.e2d_reclen
= dirp
->e2d_reclen
;
346 memcpy(dirp
, &proto
, (size_t)entrysize
);
347 if (reply("FIX") == 1)
351 if (fs2h32(dirp
->e2d_ino
) != 0)
352 lncntp
[fs2h32(dirp
->e2d_ino
)]--;
355 if (fs2h32(dirp
->e2d_ino
) == 0)
357 if (dirp
->e2d_namlen
<= 2 &&
358 dirp
->e2d_name
[0] == '.' &&
359 idesc
->id_entryno
>= 2) {
360 if (dirp
->e2d_namlen
== 1) {
361 direrror(idesc
->id_number
, "EXTRA '.' ENTRY");
363 if (reply("FIX") == 1)
365 return (KEEPON
| ret
);
367 if (dirp
->e2d_name
[1] == '.') {
368 direrror(idesc
->id_number
, "EXTRA '..' ENTRY");
370 if (reply("FIX") == 1)
372 return (KEEPON
| ret
);
377 if (fs2h32(dirp
->e2d_ino
) > maxino
||
378 (fs2h32(dirp
->e2d_ino
) < EXT2_FIRSTINO
&&
379 fs2h32(dirp
->e2d_ino
) != EXT2_ROOTINO
)) {
380 fileerror(idesc
->id_number
, fs2h32(dirp
->e2d_ino
), "I OUT OF RANGE");
384 switch (statemap
[fs2h32(dirp
->e2d_ino
)]) {
386 if (idesc
->id_entryno
<= 2)
388 fileerror(idesc
->id_number
, fs2h32(dirp
->e2d_ino
), "UNALLOCATED");
394 if (idesc
->id_entryno
<= 2)
396 if (statemap
[fs2h32(dirp
->e2d_ino
)] == FCLEAR
)
399 errmsg
= "ZERO LENGTH DIRECTORY";
404 fileerror(idesc
->id_number
, fs2h32(dirp
->e2d_ino
), errmsg
);
405 if ((n
= reply("REMOVE")) == 1)
407 dp
= ginode(fs2h32(dirp
->e2d_ino
));
408 statemap
[fs2h32(dirp
->e2d_ino
)] =
409 (fs2h16(dp
->e2di_mode
) & IFMT
) == IFDIR
? DSTATE
: FSTATE
;
410 lncntp
[fs2h32(dirp
->e2d_ino
)] = fs2h16(dp
->e2di_nlink
);
415 inp
= getinoinfo(fs2h32(dirp
->e2d_ino
));
416 if (inp
->i_parent
!= 0 && idesc
->id_entryno
> 2) {
417 getpathname(pathbuf
, sizeof(pathbuf
),
418 idesc
->id_number
, idesc
->id_number
);
419 getpathname(namebuf
, sizeof(namebuf
),
420 fs2h32(dirp
->e2d_ino
),
421 fs2h32(dirp
->e2d_ino
));
422 pwarn("%s %s %s\n", pathbuf
,
423 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
426 printf(" (IGNORED)\n");
427 else if ((n
= reply("REMOVE")) == 1)
430 if (idesc
->id_entryno
> 2)
431 inp
->i_parent
= idesc
->id_number
;
435 if (sblock
.e2fs
.e2fs_rev
> E2FS_REV0
&&
436 (sblock
.e2fs
.e2fs_features_incompat
&
437 EXT2F_INCOMPAT_FTYPE
) &&
439 inot2ext2dt(typemap
[fs2h32(dirp
->e2d_ino
)])) {
441 inot2ext2dt(typemap
[fs2h32(dirp
->e2d_ino
)]);
442 fileerror(idesc
->id_number
,
443 fs2h32(dirp
->e2d_ino
),
445 if (reply("FIX") == 1)
448 lncntp
[fs2h32(dirp
->e2d_ino
)]--;
452 errexit("BAD STATE %d FOR INODE I=%d",
453 statemap
[fs2h32(dirp
->e2d_ino
)], fs2h32(dirp
->e2d_ino
));
459 return (ret
|KEEPON
|ALTERED
);
463 * Routine to sort disk blocks.
466 blksort(const void *inpp1
, const void *inpp2
)
468 return ((* (const struct inoinfo
* const*) inpp1
)->i_blks
[0] -
469 (* (const struct inoinfo
* const*) inpp2
)->i_blks
[0]);