1 /* $NetBSD: pass2.c,v 1.44 2006/11/14 21:01:46 apb 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
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95";
37 __RCSID("$NetBSD: pass2.c,v 1.44 2006/11/14 21:01:46 apb Exp $");
41 #include <sys/param.h>
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
56 #include "exitvalues.h"
58 #define MINDIRSIZE (sizeof (struct dirtemplate))
60 static int blksort(const void *, const void *);
61 static int pass2check(struct inodesc
*);
67 struct inoinfo
**inpp
, *inp
, *pinp
;
68 struct inoinfo
**inpend
;
69 struct inostat
*rinfo
, *info
;
70 struct inodesc curino
;
73 char pathbuf
[MAXPATHLEN
+ 1];
75 rinfo
= inoinfo(ROOTINO
);
76 switch (rinfo
->ino_state
) {
79 pfatal("ROOT INODE UNALLOCATED");
80 if (reply("ALLOCATE") == 0) {
83 exit(FSCK_EXIT_CHECK_FAILED
);
85 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
86 errexit("CANNOT ALLOCATE ROOT INODE");
90 pfatal("DUPS/BAD IN ROOT INODE");
91 if (reply("REALLOCATE")) {
93 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
94 errexit("CANNOT ALLOCATE ROOT INODE");
98 if (reply("CONTINUE") == 0) {
100 exit(FSCK_EXIT_CHECK_FAILED
);
106 pfatal("ROOT INODE NOT DIRECTORY");
107 if (reply("REALLOCATE")) {
109 if (allocdir(ROOTINO
, ROOTINO
, 0755) != ROOTINO
)
110 errexit("CANNOT ALLOCATE ROOT INODE");
113 if (reply("FIX") == 0) {
116 exit(FSCK_EXIT_CHECK_FAILED
);
118 dp
= ginode(ROOTINO
);
120 iswap16((iswap16(DIP(dp
, mode
)) & ~IFMT
) | IFDIR
));
128 errexit("BAD STATE %d FOR ROOT INODE", rinfo
->ino_state
);
131 info
= inoinfo(WINO
);
132 info
->ino_state
= FSTATE
;
133 info
->ino_type
= DT_WHT
;
136 * Sort the directory list into disk block order.
138 qsort((char *)inpsort
, (size_t)inplast
, sizeof *inpsort
, blksort
);
140 * Check the integrity of each directory.
142 memset(&curino
, 0, sizeof(struct inodesc
));
143 curino
.id_type
= DATA
;
144 curino
.id_func
= pass2check
;
145 inpend
= &inpsort
[inplast
];
146 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
149 "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(),
150 (long)(inpp
- inpsort
), (int)inplast
,
151 (int)((inpp
- inpsort
) * 100 / inplast
));
155 progress_bar(cdevname(), preen
? NULL
: "phase 2",
156 (inpp
- inpsort
), inplast
);
157 #endif /* PROGRESS */
159 if (inp
->i_isize
== 0)
161 if (inp
->i_isize
< MINDIRSIZE
) {
162 direrror(inp
->i_number
, "DIRECTORY TOO SHORT");
163 inp
->i_isize
= roundup(MINDIRSIZE
, dirblksiz
);
164 if (reply("FIX") == 1) {
165 dp
= ginode(inp
->i_number
);
166 DIP_SET(dp
, size
, iswap64(inp
->i_isize
));
170 } else if ((inp
->i_isize
& (dirblksiz
- 1)) != 0) {
171 getpathname(pathbuf
, sizeof(pathbuf
), inp
->i_number
,
174 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
175 "DIRECTORY", pathbuf
,
176 (long long)inp
->i_isize
, dirblksiz
);
178 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d",
179 "DIRECTORY", pathbuf
,
180 (long long)inp
->i_isize
, dirblksiz
);
182 printf(" (ADJUSTED)\n");
183 inp
->i_isize
= roundup(inp
->i_isize
, dirblksiz
);
184 if (preen
|| reply("ADJUST") == 1) {
185 dp
= ginode(inp
->i_number
);
186 DIP_SET(dp
, size
, iswap64(inp
->i_isize
));
191 memset(&dino
, 0, sizeof dino
);
194 dp
->dp1
.di_mode
= iswap16(IFDIR
);
195 dp
->dp1
.di_size
= iswap64(inp
->i_isize
);
196 maxblk
= inp
->i_numblks
< NDADDR
? inp
->i_numblks
:
198 for (i
= 0; i
< maxblk
; i
++)
199 dp
->dp1
.di_db
[i
] = inp
->i_blks
[i
];
200 if (inp
->i_numblks
> NDADDR
) {
201 for (i
= 0; i
< NIADDR
; i
++)
203 inp
->i_blks
[NDADDR
+ i
];
206 dp
->dp2
.di_mode
= iswap16(IFDIR
);
207 dp
->dp2
.di_size
= iswap64(inp
->i_isize
);
208 maxblk
= inp
->i_numblks
< NDADDR
? inp
->i_numblks
:
210 for (i
= 0; i
< maxblk
; i
++)
211 dp
->dp2
.di_db
[i
] = inp
->i_blks
[i
];
212 if (inp
->i_numblks
> NDADDR
) {
213 for (i
= 0; i
< NIADDR
; i
++)
215 inp
->i_blks
[NDADDR
+ i
];
218 curino
.id_number
= inp
->i_number
;
219 curino
.id_parent
= inp
->i_parent
;
220 (void)ckinode(&dino
, &curino
);
224 * Byte swapping in directory entries, if needed, has been done.
225 * Now rescan dirs for pass2check()
229 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
231 if (inp
->i_isize
== 0)
233 memset(&dino
, 0, sizeof dino
);
235 dino
.dp1
.di_mode
= iswap16(IFDIR
);
236 dino
.dp1
.di_size
= iswap64(inp
->i_isize
);
237 for (i
= 0; i
< inp
->i_numblks
; i
++)
238 dino
.dp1
.di_db
[i
] = inp
->i_blks
[i
];
240 dino
.dp2
.di_mode
= iswap16(IFDIR
);
241 dino
.dp2
.di_size
= iswap64(inp
->i_isize
);
242 for (i
= 0; i
< inp
->i_numblks
; i
++)
243 dino
.dp2
.di_db
[i
] = inp
->i_blks
[i
];
245 curino
.id_number
= inp
->i_number
;
246 curino
.id_parent
= inp
->i_parent
;
247 (void)ckinode(&dino
, &curino
);
252 * Now that the parents of all directories have been found,
253 * make another pass to verify the value of `..'
255 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
257 if (inp
->i_parent
== 0 || inp
->i_isize
== 0)
259 if (inp
->i_dotdot
== inp
->i_parent
||
260 inp
->i_dotdot
== (ino_t
)-1)
262 info
= inoinfo(inp
->i_parent
);
263 if (inp
->i_dotdot
== 0) {
264 inp
->i_dotdot
= inp
->i_parent
;
265 fileerror(inp
->i_parent
, inp
->i_number
, "MISSING '..'");
266 if (reply("FIX") == 0) {
270 (void)makeentry(inp
->i_number
, inp
->i_parent
, "..");
274 fileerror(inp
->i_parent
, inp
->i_number
,
275 "BAD INODE NUMBER FOR '..'");
276 if (reply("FIX") == 0) {
280 inoinfo(inp
->i_dotdot
)->ino_linkcnt
++;
282 inp
->i_dotdot
= inp
->i_parent
;
283 (void)changeino(inp
->i_number
, "..", inp
->i_parent
);
286 * Create a list of children for each directory.
288 inpend
= &inpsort
[inplast
];
289 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
291 info
= inoinfo(inp
->i_number
);
292 inp
->i_child
= inp
->i_sibling
= 0;
293 if (info
->ino_state
== DFOUND
)
294 info
->ino_state
= DSTATE
;
296 for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
298 if (inp
->i_parent
== 0 ||
299 inp
->i_number
== ROOTINO
)
301 pinp
= getinoinfo(inp
->i_parent
);
302 inp
->i_sibling
= pinp
->i_child
;
306 * Mark all the directories that can be found from the root.
313 #endif /* PROGRESS */
317 pass2check(struct inodesc
*idesc
)
319 struct direct
*dirp
= idesc
->id_dirp
;
321 struct inostat
*info
;
322 int n
, entrysize
, ret
= 0;
326 char namebuf
[MAXPATHLEN
+ 1];
327 char pathbuf
[MAXPATHLEN
+ 1];
330 * If converting, set directory entry type.
332 if (!is_ufs2
&& doinglevel2
&& iswap32(dirp
->d_ino
) > 0 &&
333 iswap32(dirp
->d_ino
) < maxino
) {
334 dirp
->d_type
= inoinfo(iswap32(dirp
->d_ino
))->ino_type
;
340 if (idesc
->id_entryno
!= 0)
342 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, ".") == 0) {
343 if (iswap32(dirp
->d_ino
) != idesc
->id_number
) {
344 direrror(idesc
->id_number
, "BAD INODE NUMBER FOR '.'");
345 dirp
->d_ino
= iswap32(idesc
->id_number
);
346 if (reply("FIX") == 1)
351 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
352 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '.'");
353 dirp
->d_type
= DT_DIR
;
354 if (reply("FIX") == 1)
361 direrror(idesc
->id_number
, "MISSING '.'");
362 proto
.d_ino
= iswap32(idesc
->id_number
);
364 proto
.d_type
= DT_DIR
;
368 (void)strlcpy(proto
.d_name
, ".", sizeof(proto
.d_name
));
369 # if BYTE_ORDER == LITTLE_ENDIAN
370 if (!newinofmt
&& !needswap
) {
372 if (!newinofmt
&& needswap
) {
377 proto
.d_type
= proto
.d_namlen
;
378 proto
.d_namlen
= tmp
;
380 entrysize
= DIRSIZ(0, &proto
, 0);
381 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") != 0) {
382 pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
385 } else if (iswap16(dirp
->d_reclen
) < entrysize
) {
386 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
388 } else if (iswap16(dirp
->d_reclen
) < 2 * entrysize
) {
389 proto
.d_reclen
= dirp
->d_reclen
;
390 memmove(dirp
, &proto
, (size_t)entrysize
);
391 if (reply("FIX") == 1)
396 n
= iswap16(dirp
->d_reclen
) - entrysize
;
397 proto
.d_reclen
= iswap16(entrysize
);
398 memmove(dirp
, &proto
, (size_t)entrysize
);
400 inoinfo(iswap32(dirp
->d_ino
))->ino_linkcnt
--;
401 dirp
= (struct direct
*)((char *)(dirp
) + entrysize
);
402 memset(dirp
, 0, (size_t)n
);
403 dirp
->d_reclen
= iswap16(n
);
404 if (reply("FIX") == 1)
410 if (idesc
->id_entryno
> 1)
412 inp
= getinoinfo(idesc
->id_number
);
413 proto
.d_ino
= iswap32(inp
->i_parent
);
415 proto
.d_type
= DT_DIR
;
419 (void)strlcpy(proto
.d_name
, "..", sizeof(proto
.d_name
));
420 #if BYTE_ORDER == LITTLE_ENDIAN
421 if (!newinofmt
&& !needswap
) {
423 if (!newinofmt
&& needswap
) {
428 proto
.d_type
= proto
.d_namlen
;
429 proto
.d_namlen
= tmp
;
431 entrysize
= DIRSIZ(0, &proto
, 0);
432 if (idesc
->id_entryno
== 0) {
433 n
= DIRSIZ(0, dirp
, 0);
434 if (iswap16(dirp
->d_reclen
) < n
+ entrysize
)
436 proto
.d_reclen
= iswap16(iswap16(dirp
->d_reclen
) - n
);
437 dirp
->d_reclen
= iswap16(n
);
439 inoinfo(iswap32(dirp
->d_ino
))->ino_linkcnt
--;
440 dirp
= (struct direct
*)((char *)(dirp
) + n
);
441 memset(dirp
, 0, (size_t)iswap16(proto
.d_reclen
));
442 dirp
->d_reclen
= proto
.d_reclen
;
444 if (dirp
->d_ino
!= 0 && strcmp(dirp
->d_name
, "..") == 0) {
445 inp
->i_dotdot
= iswap32(dirp
->d_ino
);
446 if (newinofmt
&& dirp
->d_type
!= DT_DIR
) {
447 direrror(idesc
->id_number
, "BAD TYPE VALUE FOR '..'");
448 dirp
->d_type
= DT_DIR
;
449 if (reply("FIX") == 1)
456 if (iswap32(dirp
->d_ino
) != 0 && strcmp(dirp
->d_name
, ".") != 0) {
457 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
458 pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
460 inp
->i_dotdot
= (ino_t
)-1;
462 } else if (iswap16(dirp
->d_reclen
) < entrysize
) {
463 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
464 pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
465 inp
->i_dotdot
= (ino_t
)-1;
467 } else if (inp
->i_parent
!= 0) {
469 * We know the parent, so fix now.
471 inp
->i_dotdot
= inp
->i_parent
;
472 fileerror(inp
->i_parent
, idesc
->id_number
, "MISSING '..'");
473 proto
.d_reclen
= dirp
->d_reclen
;
474 memmove(dirp
, &proto
, (size_t)entrysize
);
475 if (reply("FIX") == 1)
481 if (dirp
->d_ino
!= 0)
482 inoinfo(iswap32(dirp
->d_ino
))->ino_linkcnt
--;
485 if (dirp
->d_ino
== 0)
487 if (dirp
->d_namlen
<= 2 &&
488 dirp
->d_name
[0] == '.' &&
489 idesc
->id_entryno
>= 2) {
490 if (dirp
->d_namlen
== 1) {
491 direrror(idesc
->id_number
, "EXTRA '.' ENTRY");
493 if (reply("FIX") == 1)
497 return (KEEPON
| ret
);
499 if (dirp
->d_name
[1] == '.') {
500 direrror(idesc
->id_number
, "EXTRA '..' ENTRY");
502 if (reply("FIX") == 1)
506 return (KEEPON
| ret
);
511 if (iswap32(dirp
->d_ino
) > maxino
) {
512 fileerror(idesc
->id_number
, dirp
->d_ino
, "I OUT OF RANGE");
516 } else if (newinofmt
&&
517 ((iswap32(dirp
->d_ino
) == WINO
&& dirp
->d_type
!= DT_WHT
) ||
518 (iswap32(dirp
->d_ino
) != WINO
&& dirp
->d_type
== DT_WHT
))) {
519 fileerror(idesc
->id_number
, iswap32(dirp
->d_ino
), "BAD WHITEOUT ENTRY");
520 dirp
->d_ino
= iswap32(WINO
);
521 dirp
->d_type
= DT_WHT
;
522 if (reply("FIX") == 1)
528 info
= inoinfo(iswap32(dirp
->d_ino
));
529 switch (info
->ino_state
) {
531 if (idesc
->id_entryno
<= 2)
533 fileerror(idesc
->id_number
, iswap32(dirp
->d_ino
), "UNALLOCATED");
541 if (idesc
->id_entryno
<= 2)
543 if (info
->ino_state
== FCLEAR
)
545 else if (!preen
&& !usedsoftdep
)
546 errmsg
= "ZERO LENGTH DIRECTORY";
551 fileerror(idesc
->id_number
, iswap32(dirp
->d_ino
), errmsg
);
552 if ((n
= reply("REMOVE")) == 1)
554 dp
= ginode(iswap32(dirp
->d_ino
));
556 (iswap16(DIP(dp
, mode
)) & IFMT
) == IFDIR
? DSTATE
: FSTATE
;
557 info
->ino_linkcnt
= iswap16(DIP(dp
, nlink
));
562 inp
= getinoinfo(iswap32(dirp
->d_ino
));
563 if (inp
->i_parent
!= 0 && idesc
->id_entryno
> 2) {
564 getpathname(pathbuf
, sizeof(pathbuf
),
565 idesc
->id_number
, idesc
->id_number
);
566 getpathname(namebuf
, sizeof(namebuf
),
567 iswap32(dirp
->d_ino
), iswap32(dirp
->d_ino
));
568 pwarn("%s %s %s\n", pathbuf
,
569 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
572 printf(" (IGNORED)\n");
573 else if ((n
= reply("REMOVE")) == 1)
576 if (idesc
->id_entryno
> 2)
577 inp
->i_parent
= idesc
->id_number
;
581 if (newinofmt
&& dirp
->d_type
!= info
->ino_type
) {
582 fileerror(idesc
->id_number
, iswap32(dirp
->d_ino
),
584 dirp
->d_type
= info
->ino_type
;
585 if (reply("FIX") == 1)
594 errexit("BAD STATE %d FOR INODE I=%d",
595 info
->ino_state
, iswap32(dirp
->d_ino
));
601 return (ret
|KEEPON
|ALTERED
);
605 * Routine to sort disk blocks.
608 blksort(const void *arg1
, const void *arg2
)
611 return ((*(const struct inoinfo
*const *)arg1
)->i_blks
[0] -
612 (*(const struct inoinfo
*const *)arg2
)->i_blks
[0]);