2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * @(#)pass2.c 8.9 (Berkeley) 4/28/95
30 * $FreeBSD: src/sbin/fsck/pass2.c,v 1.10.2.2 2001/11/24 15:14:59 iedowse Exp $
33 #include <sys/param.h>
35 #include <vfs/ufs/dinode.h>
36 #include <vfs/ufs/dir.h>
43 #define MINDIRSIZE (sizeof (struct dirtemplate))
45 static int blksort(const void *, const void *);
46 static int pass2check(struct inodesc
*);
51 struct ufs1_dinode
*dp
;
52 struct inoinfo
**inpp
, *inp
;
53 struct inoinfo
**inpend
;
54 struct inodesc curino
;
55 struct ufs1_dinode dino
;
56 char pathbuf
[MAXPATHLEN
+ 1];
59 switch (inoinfo(ROOTINO
)->ino_state
) {
62 pfatal("ROOT INODE UNALLOCATED");
63 if (reply("ALLOCATE") == 0) {
67 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
68 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
72 pfatal("DUPS/BAD IN ROOT INODE");
73 if (reply("REALLOCATE")) {
75 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
76 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
79 if (reply("CONTINUE") == 0) {
87 pfatal("ROOT INODE NOT DIRECTORY");
88 if (reply("REALLOCATE")) {
90 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
91 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
94 if (reply("FIX") == 0) {
100 dp
->di_mode
|= IFDIR
;
108 errx(EEXIT
, "BAD STATE %d FOR ROOT INODE",
109 inoinfo(ROOTINO
)->ino_state
);
111 inoinfo(ROOTINO
)->ino_state
= DFOUND
;
113 inoinfo(WINO
)->ino_state
= FSTATE
;
114 inoinfo(WINO
)->ino_type
= DT_WHT
;
117 * Sort the directory list into disk block order. Do this in blocks
118 * of 1000 directories in order to maintain locality of reference
119 * in memory in case fsck is using swap space.
121 for (i
= 0; i
< inplast
; i
+= 1000) {
122 if ((n
= inplast
- i
) > 1000)
124 qsort(inpsort
+ i
, (size_t)n
, sizeof *inpsort
, blksort
);
128 * Check the integrity of each directory.
130 memset(&curino
, 0, sizeof(struct inodesc
));
131 curino
.id_type
= DATA
;
132 curino
.id_func
= pass2check
;
134 inpend
= &inpsort
[inplast
];
135 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
137 printf("%s: phase 2: dir %d of %ld (%d%%)\n", cdevname
,
138 (int)(inpp
- inpsort
), inplast
, (int)((inpp
- inpsort
) * 100 /
143 if (inp
->i_isize
== 0)
145 if (inp
->i_isize
< MINDIRSIZE
) {
146 direrror(inp
->i_number
, "DIRECTORY TOO SHORT");
147 inp
->i_isize
= roundup(MINDIRSIZE
, DIRBLKSIZ
);
148 if (reply("FIX") == 1) {
149 dp
= ginode(inp
->i_number
);
150 dp
->di_size
= inp
->i_isize
;
154 } else if ((inp
->i_isize
& (DIRBLKSIZ
- 1)) != 0) {
155 getpathname(pathbuf
, inp
->i_number
, inp
->i_number
);
157 pfatal("%s %s: LENGTH %zu NOT MULTIPLE OF %d",
158 "DIRECTORY", pathbuf
, inp
->i_isize
,
161 pwarn("%s %s: LENGTH %zu NOT MULTIPLE OF %d",
162 "DIRECTORY", pathbuf
, inp
->i_isize
,
165 printf(" (ADJUSTED)\n");
166 inp
->i_isize
= roundup(inp
->i_isize
, DIRBLKSIZ
);
167 if (preen
|| reply("ADJUST") == 1) {
168 dp
= ginode(inp
->i_number
);
169 dp
->di_size
= roundup(inp
->i_isize
, DIRBLKSIZ
);
174 memset(&dino
, 0, sizeof(struct ufs1_dinode
));
175 dino
.di_mode
= IFDIR
;
176 dp
->di_size
= inp
->i_isize
;
177 memmove(&dp
->di_db
[0], &inp
->i_blks
[0], (size_t)inp
->i_numblks
);
178 curino
.id_number
= inp
->i_number
;
179 curino
.id_parent
= inp
->i_parent
;
180 ckinode(dp
, &curino
);
183 * Now that the parents of all directories have been found,
184 * make another pass to verify the value of `..'
186 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
188 if (inp
->i_parent
== 0 || inp
->i_isize
== 0)
190 if (inoinfo(inp
->i_parent
)->ino_state
== DFOUND
&&
191 inoinfo(inp
->i_number
)->ino_state
== DSTATE
)
192 inoinfo(inp
->i_number
)->ino_state
= DFOUND
;
193 if (inp
->i_dotdot
== inp
->i_parent
||
194 inp
->i_dotdot
== (ufs1_ino_t
)-1)
196 if (inp
->i_dotdot
== 0) {
197 inp
->i_dotdot
= inp
->i_parent
;
198 fileerror(inp
->i_parent
, inp
->i_number
, "MISSING '..'");
199 if (reply("FIX") == 0)
201 makeentry(inp
->i_number
, inp
->i_parent
, "..");
202 inoinfo(inp
->i_parent
)->ino_linkcnt
--;
205 fileerror(inp
->i_parent
, inp
->i_number
,
206 "BAD INODE NUMBER FOR '..'");
207 if (reply("FIX") == 0)
209 inoinfo(inp
->i_dotdot
)->ino_linkcnt
++;
210 inoinfo(inp
->i_parent
)->ino_linkcnt
--;
211 inp
->i_dotdot
= inp
->i_parent
;
212 changeino(inp
->i_number
, "..", inp
->i_parent
);
215 * Mark all the directories that can be found from the root.
221 pass2check(struct inodesc
*idesc
)
223 struct direct
*dirp
= idesc
->id_dirp
;
225 int n
, entrysize
, ret
= 0;
226 struct ufs1_dinode
*dp
;
229 char namebuf
[MAXPATHLEN
+ 1];
230 char pathbuf
[MAXPATHLEN
+ 1];
233 * If converting, set directory entry type.
235 if (doinglevel2
&& dirp
->d_ino
> 0 && dirp
->d_ino
< maxino
) {
236 dirp
->d_type
= inoinfo(dirp
->d_ino
)->ino_type
;
242 if (idesc
->id_entryno
!= 0)
244 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, ".") == 0) {
245 if (dirp
->d_ino
!= idesc
->id_number
) {
246 direrror(idesc
->id_number
, "BAD INODE NUMBER FOR '.'");
247 dirp
->d_ino
= idesc
->id_number
;
248 if (reply("FIX") == 1)
251 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
252 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '.'");
253 dirp
->d_type
= DT_DIR
;
254 if (reply("FIX") == 1)
259 direrror(idesc
->id_number
, "MISSING '.'");
260 proto
.d_ino
= idesc
->id_number
;
262 proto
.d_type
= DT_DIR
;
266 strcpy(proto
.d_name
, ".");
267 # if BYTE_ORDER == LITTLE_ENDIAN
272 proto
.d_type
= proto
.d_namlen
;
273 proto
.d_namlen
= tmp
;
276 entrysize
= DIRSIZ(0, &proto
);
277 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") != 0) {
278 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
280 } else if (dirp
->d_reclen
< entrysize
) {
281 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
282 } else if (dirp
->d_reclen
< 2 * entrysize
) {
283 proto
.d_reclen
= dirp
->d_reclen
;
284 memmove(dirp
, &proto
, (size_t)entrysize
);
285 if (reply("FIX") == 1)
288 n
= dirp
->d_reclen
- entrysize
;
289 proto
.d_reclen
= entrysize
;
290 memmove(dirp
, &proto
, (size_t)entrysize
);
292 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
293 dirp
= (struct direct
*)((char *)(dirp
) + entrysize
);
294 memset(dirp
, 0, (size_t)n
);
296 if (reply("FIX") == 1)
300 if (idesc
->id_entryno
> 1)
302 inp
= getinoinfo(idesc
->id_number
);
303 proto
.d_ino
= inp
->i_parent
;
305 proto
.d_type
= DT_DIR
;
309 strcpy(proto
.d_name
, "..");
310 # if BYTE_ORDER == LITTLE_ENDIAN
315 proto
.d_type
= proto
.d_namlen
;
316 proto
.d_namlen
= tmp
;
319 entrysize
= DIRSIZ(0, &proto
);
320 if (idesc
->id_entryno
== 0) {
322 if (dirp
->d_reclen
< n
+ entrysize
)
324 proto
.d_reclen
= dirp
->d_reclen
- n
;
327 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
328 dirp
= (struct direct
*)((char *)(dirp
) + n
);
329 memset(dirp
, 0, (size_t)proto
.d_reclen
);
330 dirp
->d_reclen
= proto
.d_reclen
;
332 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") == 0) {
333 inp
->i_dotdot
= dirp
->d_ino
;
334 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
335 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '..'");
336 dirp
->d_type
= DT_DIR
;
337 if (reply("FIX") == 1)
342 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, ".") != 0) {
343 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
344 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
346 inp
->i_dotdot
= (ufs1_ino_t
)-1;
347 } else if (dirp
->d_reclen
< entrysize
) {
348 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
349 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
350 inp
->i_dotdot
= (ufs1_ino_t
)-1;
351 } else if (inp
->i_parent
!= 0) {
353 * We know the parent, so fix now.
355 inp
->i_dotdot
= inp
->i_parent
;
356 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
357 proto
.d_reclen
= dirp
->d_reclen
;
358 memmove(dirp
, &proto
, (size_t)entrysize
);
359 if (reply("FIX") == 1)
363 if (dirp
->d_ino
!= 0)
364 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
367 if (dirp
->d_ino
== 0)
369 if (dirp
->d_namlen
<= 2 &&
370 dirp
->d_name
[0] == '.' &&
371 idesc
->id_entryno
>= 2) {
372 if (dirp
->d_namlen
== 1) {
373 direrror(idesc
->id_number
, "EXTRA '.' ENTRY");
375 if (reply("FIX") == 1)
377 return (KEEPON
| ret
);
379 if (dirp
->d_name
[1] == '.') {
380 direrror(idesc
->id_number
, "EXTRA '..' ENTRY");
382 if (reply("FIX") == 1)
384 return (KEEPON
| ret
);
389 if (dirp
->d_ino
> maxino
) {
390 fileerror(idesc
->id_number
, dirp
->d_ino
, "I OUT OF RANGE");
392 } else if (newinofmt
&&
393 ((dirp
->d_ino
== WINO
&& dirp
->d_type
!= DT_WHT
) ||
394 (dirp
->d_ino
!= WINO
&& dirp
->d_type
== DT_WHT
))) {
395 fileerror(idesc
->id_number
, dirp
->d_ino
, "BAD WHITEOUT ENTRY");
397 dirp
->d_type
= DT_WHT
;
398 if (reply("FIX") == 1)
402 switch (inoinfo(dirp
->d_ino
)->ino_state
) {
404 if (idesc
->id_entryno
<= 2)
406 fileerror(idesc
->id_number
, dirp
->d_ino
, "UNALLOCATED");
412 if (idesc
->id_entryno
<= 2)
414 if (inoinfo(dirp
->d_ino
)->ino_state
== FCLEAR
)
416 else if (!preen
&& !usedsoftdep
)
417 errmsg
= "ZERO LENGTH DIRECTORY";
422 fileerror(idesc
->id_number
, dirp
->d_ino
, errmsg
);
423 if ((n
= reply("REMOVE")) == 1)
425 dp
= ginode(dirp
->d_ino
);
426 inoinfo(dirp
->d_ino
)->ino_state
=
427 (dp
->di_mode
& IFMT
) == IFDIR
? DSTATE
: FSTATE
;
428 inoinfo(dirp
->d_ino
)->ino_linkcnt
= dp
->di_nlink
;
432 if (inoinfo(idesc
->id_number
)->ino_state
== DFOUND
)
433 inoinfo(dirp
->d_ino
)->ino_state
= DFOUND
;
437 inp
= getinoinfo(dirp
->d_ino
);
438 if (inp
->i_parent
!= 0 && idesc
->id_entryno
> 2) {
439 getpathname(pathbuf
, idesc
->id_number
,
441 getpathname(namebuf
, dirp
->d_ino
, dirp
->d_ino
);
442 pwarn("%s%s%s %s %s\n", pathbuf
,
443 (strcmp(pathbuf
, "/") == 0 ? "" : "/"),
445 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
448 printf(" (REMOVED)\n");
452 if ((n
= reply("REMOVE")) == 1)
455 if (idesc
->id_entryno
> 2)
456 inp
->i_parent
= idesc
->id_number
;
461 dirp
->d_type
!= inoinfo(dirp
->d_ino
)->ino_type
) {
462 fileerror(idesc
->id_number
, dirp
->d_ino
,
464 dirp
->d_type
= inoinfo(dirp
->d_ino
)->ino_type
;
465 if (reply("FIX") == 1)
468 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
472 errx(EEXIT
, "BAD STATE %d FOR INODE I=%d",
473 inoinfo(dirp
->d_ino
)->ino_state
, dirp
->d_ino
);
479 return (ret
|KEEPON
|ALTERED
);
483 * Routine to sort disk blocks.
486 blksort(const void *arg1
, const void *arg2
)
489 return ((*(struct inoinfo
**)arg1
)->i_blks
[0] -
490 (*(struct inoinfo
**)arg2
)->i_blks
[0]);