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(UFS_ROOTINO
)->ino_state
) {
62 pfatal("ROOT INODE UNALLOCATED");
63 if (reply("ALLOCATE") == 0) {
67 if (allocdir(UFS_ROOTINO
, UFS_ROOTINO
, 0755) != UFS_ROOTINO
)
68 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
72 pfatal("DUPS/BAD IN ROOT INODE");
73 if (reply("REALLOCATE")) {
75 if (allocdir(UFS_ROOTINO
, UFS_ROOTINO
, 0755) !=
77 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
80 if (reply("CONTINUE") == 0) {
88 pfatal("ROOT INODE NOT DIRECTORY");
89 if (reply("REALLOCATE")) {
91 if (allocdir(UFS_ROOTINO
, UFS_ROOTINO
, 0755) !=
93 errx(EEXIT
, "CANNOT ALLOCATE ROOT INODE");
96 if (reply("FIX") == 0) {
100 dp
= ginode(UFS_ROOTINO
);
101 dp
->di_mode
&= ~IFMT
;
102 dp
->di_mode
|= IFDIR
;
110 errx(EEXIT
, "BAD STATE %d FOR ROOT INODE",
111 inoinfo(UFS_ROOTINO
)->ino_state
);
113 inoinfo(UFS_ROOTINO
)->ino_state
= DFOUND
;
115 inoinfo(UFS_WINO
)->ino_state
= FSTATE
;
116 inoinfo(UFS_WINO
)->ino_type
= DT_WHT
;
119 * Sort the directory list into disk block order. Do this in blocks
120 * of 1000 directories in order to maintain locality of reference
121 * in memory in case fsck is using swap space.
123 for (i
= 0; i
< inplast
; i
+= 1000) {
124 if ((n
= inplast
- i
) > 1000)
126 qsort(inpsort
+ i
, (size_t)n
, sizeof *inpsort
, blksort
);
130 * Check the integrity of each directory.
132 memset(&curino
, 0, sizeof(struct inodesc
));
133 curino
.id_type
= DATA
;
134 curino
.id_func
= pass2check
;
136 inpend
= &inpsort
[inplast
];
137 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
139 printf("%s: phase 2: dir %d of %ld (%d%%)\n", cdevname
,
140 (int)(inpp
- inpsort
), inplast
, (int)((inpp
- inpsort
) * 100 /
145 if (inp
->i_isize
== 0)
147 if (inp
->i_isize
< MINDIRSIZE
) {
148 direrror(inp
->i_number
, "DIRECTORY TOO SHORT");
149 inp
->i_isize
= roundup(MINDIRSIZE
, DIRBLKSIZ
);
150 if (reply("FIX") == 1) {
151 dp
= ginode(inp
->i_number
);
152 dp
->di_size
= inp
->i_isize
;
156 } else if ((inp
->i_isize
& (DIRBLKSIZ
- 1)) != 0) {
157 getpathname(pathbuf
, inp
->i_number
, inp
->i_number
);
159 pfatal("%s %s: LENGTH %zu NOT MULTIPLE OF %d",
160 "DIRECTORY", pathbuf
, inp
->i_isize
,
163 pwarn("%s %s: LENGTH %zu NOT MULTIPLE OF %d",
164 "DIRECTORY", pathbuf
, inp
->i_isize
,
167 printf(" (ADJUSTED)\n");
168 inp
->i_isize
= roundup(inp
->i_isize
, DIRBLKSIZ
);
169 if (preen
|| reply("ADJUST") == 1) {
170 dp
= ginode(inp
->i_number
);
171 dp
->di_size
= roundup(inp
->i_isize
, DIRBLKSIZ
);
176 memset(&dino
, 0, sizeof(struct ufs1_dinode
));
177 dino
.di_mode
= IFDIR
;
178 dp
->di_size
= inp
->i_isize
;
179 memmove(&dp
->di_db
[0], &inp
->i_blks
[0], (size_t)inp
->i_numblks
);
180 curino
.id_number
= inp
->i_number
;
181 curino
.id_parent
= inp
->i_parent
;
182 ckinode(dp
, &curino
);
185 * Now that the parents of all directories have been found,
186 * make another pass to verify the value of `..'
188 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
190 if (inp
->i_parent
== 0 || inp
->i_isize
== 0)
192 if (inoinfo(inp
->i_parent
)->ino_state
== DFOUND
&&
193 inoinfo(inp
->i_number
)->ino_state
== DSTATE
)
194 inoinfo(inp
->i_number
)->ino_state
= DFOUND
;
195 if (inp
->i_dotdot
== inp
->i_parent
||
196 inp
->i_dotdot
== (ufs1_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 makeentry(inp
->i_number
, inp
->i_parent
, "..");
204 inoinfo(inp
->i_parent
)->ino_linkcnt
--;
207 fileerror(inp
->i_parent
, inp
->i_number
,
208 "BAD INODE NUMBER FOR '..'");
209 if (reply("FIX") == 0)
211 inoinfo(inp
->i_dotdot
)->ino_linkcnt
++;
212 inoinfo(inp
->i_parent
)->ino_linkcnt
--;
213 inp
->i_dotdot
= inp
->i_parent
;
214 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 direct
*dirp
= idesc
->id_dirp
;
227 int n
, entrysize
, ret
= 0;
228 struct ufs1_dinode
*dp
;
231 char namebuf
[MAXPATHLEN
+ 1];
232 char pathbuf
[MAXPATHLEN
+ 1];
235 * If converting, set directory entry type.
237 if (doinglevel2
&& dirp
->d_ino
> 0 && dirp
->d_ino
< maxino
) {
238 dirp
->d_type
= inoinfo(dirp
->d_ino
)->ino_type
;
244 if (idesc
->id_entryno
!= 0)
246 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, ".") == 0) {
247 if (dirp
->d_ino
!= idesc
->id_number
) {
248 direrror(idesc
->id_number
, "BAD INODE NUMBER FOR '.'");
249 dirp
->d_ino
= idesc
->id_number
;
250 if (reply("FIX") == 1)
253 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
254 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '.'");
255 dirp
->d_type
= DT_DIR
;
256 if (reply("FIX") == 1)
261 direrror(idesc
->id_number
, "MISSING '.'");
262 proto
.d_ino
= idesc
->id_number
;
264 proto
.d_type
= DT_DIR
;
268 strcpy(proto
.d_name
, ".");
269 # if BYTE_ORDER == LITTLE_ENDIAN
274 proto
.d_type
= proto
.d_namlen
;
275 proto
.d_namlen
= tmp
;
278 entrysize
= DIRSIZ(0, &proto
);
279 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") != 0) {
280 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
282 } else if (dirp
->d_reclen
< entrysize
) {
283 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
284 } else if (dirp
->d_reclen
< 2 * entrysize
) {
285 proto
.d_reclen
= dirp
->d_reclen
;
286 memmove(dirp
, &proto
, (size_t)entrysize
);
287 if (reply("FIX") == 1)
290 n
= dirp
->d_reclen
- entrysize
;
291 proto
.d_reclen
= entrysize
;
292 memmove(dirp
, &proto
, (size_t)entrysize
);
294 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
295 dirp
= (struct direct
*)((char *)(dirp
) + entrysize
);
296 memset(dirp
, 0, (size_t)n
);
298 if (reply("FIX") == 1)
302 if (idesc
->id_entryno
> 1)
304 inp
= getinoinfo(idesc
->id_number
);
305 proto
.d_ino
= inp
->i_parent
;
307 proto
.d_type
= DT_DIR
;
311 strcpy(proto
.d_name
, "..");
312 # if BYTE_ORDER == LITTLE_ENDIAN
317 proto
.d_type
= proto
.d_namlen
;
318 proto
.d_namlen
= tmp
;
321 entrysize
= DIRSIZ(0, &proto
);
322 if (idesc
->id_entryno
== 0) {
324 if (dirp
->d_reclen
< n
+ entrysize
)
326 proto
.d_reclen
= dirp
->d_reclen
- n
;
329 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
330 dirp
= (struct direct
*)((char *)(dirp
) + n
);
331 memset(dirp
, 0, (size_t)proto
.d_reclen
);
332 dirp
->d_reclen
= proto
.d_reclen
;
334 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") == 0) {
335 inp
->i_dotdot
= dirp
->d_ino
;
336 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
337 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '..'");
338 dirp
->d_type
= DT_DIR
;
339 if (reply("FIX") == 1)
344 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, ".") != 0) {
345 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
346 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
348 inp
->i_dotdot
= (ufs1_ino_t
)-1;
349 } else if (dirp
->d_reclen
< entrysize
) {
350 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
351 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
352 inp
->i_dotdot
= (ufs1_ino_t
)-1;
353 } else if (inp
->i_parent
!= 0) {
355 * We know the parent, so fix now.
357 inp
->i_dotdot
= inp
->i_parent
;
358 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
359 proto
.d_reclen
= dirp
->d_reclen
;
360 memmove(dirp
, &proto
, (size_t)entrysize
);
361 if (reply("FIX") == 1)
365 if (dirp
->d_ino
!= 0)
366 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
369 if (dirp
->d_ino
== 0)
371 if (dirp
->d_namlen
<= 2 &&
372 dirp
->d_name
[0] == '.' &&
373 idesc
->id_entryno
>= 2) {
374 if (dirp
->d_namlen
== 1) {
375 direrror(idesc
->id_number
, "EXTRA '.' ENTRY");
377 if (reply("FIX") == 1)
379 return (KEEPON
| ret
);
381 if (dirp
->d_name
[1] == '.') {
382 direrror(idesc
->id_number
, "EXTRA '..' ENTRY");
384 if (reply("FIX") == 1)
386 return (KEEPON
| ret
);
391 if (dirp
->d_ino
> maxino
) {
392 fileerror(idesc
->id_number
, dirp
->d_ino
, "I OUT OF RANGE");
394 } else if (newinofmt
&&
395 ((dirp
->d_ino
== UFS_WINO
&& dirp
->d_type
!= DT_WHT
) ||
396 (dirp
->d_ino
!= UFS_WINO
&& dirp
->d_type
== DT_WHT
))) {
397 fileerror(idesc
->id_number
, dirp
->d_ino
, "BAD WHITEOUT ENTRY");
398 dirp
->d_ino
= UFS_WINO
;
399 dirp
->d_type
= DT_WHT
;
400 if (reply("FIX") == 1)
404 switch (inoinfo(dirp
->d_ino
)->ino_state
) {
406 if (idesc
->id_entryno
<= 2)
408 fileerror(idesc
->id_number
, dirp
->d_ino
, "UNALLOCATED");
414 if (idesc
->id_entryno
<= 2)
416 if (inoinfo(dirp
->d_ino
)->ino_state
== FCLEAR
)
418 else if (!preen
&& !usedsoftdep
)
419 errmsg
= "ZERO LENGTH DIRECTORY";
424 fileerror(idesc
->id_number
, dirp
->d_ino
, errmsg
);
425 if ((n
= reply("REMOVE")) == 1)
427 dp
= ginode(dirp
->d_ino
);
428 inoinfo(dirp
->d_ino
)->ino_state
=
429 (dp
->di_mode
& IFMT
) == IFDIR
? DSTATE
: FSTATE
;
430 inoinfo(dirp
->d_ino
)->ino_linkcnt
= dp
->di_nlink
;
434 if (inoinfo(idesc
->id_number
)->ino_state
== DFOUND
)
435 inoinfo(dirp
->d_ino
)->ino_state
= DFOUND
;
439 inp
= getinoinfo(dirp
->d_ino
);
440 if (inp
->i_parent
!= 0 && idesc
->id_entryno
> 2) {
441 getpathname(pathbuf
, idesc
->id_number
,
443 getpathname(namebuf
, dirp
->d_ino
, dirp
->d_ino
);
444 pwarn("%s%s%s %s %s\n", pathbuf
,
445 (strcmp(pathbuf
, "/") == 0 ? "" : "/"),
447 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
450 printf(" (REMOVED)\n");
454 if ((n
= reply("REMOVE")) == 1)
457 if (idesc
->id_entryno
> 2)
458 inp
->i_parent
= idesc
->id_number
;
463 dirp
->d_type
!= inoinfo(dirp
->d_ino
)->ino_type
) {
464 fileerror(idesc
->id_number
, dirp
->d_ino
,
466 dirp
->d_type
= inoinfo(dirp
->d_ino
)->ino_type
;
467 if (reply("FIX") == 1)
470 inoinfo(dirp
->d_ino
)->ino_linkcnt
--;
474 errx(EEXIT
, "BAD STATE %d FOR INODE I=%d",
475 inoinfo(dirp
->d_ino
)->ino_state
, dirp
->d_ino
);
481 return (ret
|KEEPON
|ALTERED
);
485 * Routine to sort disk blocks.
488 blksort(const void *arg1
, const void *arg2
)
491 return ((*(struct inoinfo
**)arg1
)->i_blks
[0] -
492 (*(struct inoinfo
**)arg2
)->i_blks
[0]);