1 /* $NetBSD: utilities.c,v 1.57 2010/01/06 18:12:37 christos 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
[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
37 __RCSID("$NetBSD: utilities.c,v 1.57 2010/01/06 18:12:37 christos Exp $");
41 #include <sys/param.h>
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ufs/dir.h>
46 #include <ufs/ffs/fs.h>
47 #include <ufs/ffs/ffs_extern.h>
48 #include <ufs/ufs/ufs_bswap.h>
62 #include "exitvalues.h"
64 long diskreads
, totalreads
; /* Disk cache statistics */
66 static void rwerror(const char *, daddr_t
);
68 extern volatile sig_atomic_t returntosingle
;
71 ftypeok(union dinode
*dp
)
73 switch (iswap16(DIP(dp
, mode
)) & IFMT
) {
86 printf("bad file type 0%o\n", iswap16(DIP(dp
, mode
)));
92 reply(const char *question
)
98 pfatal("INTERNAL ERROR: GOT TO reply()");
99 persevere
= !strcmp(question
, "CONTINUE");
101 if (!persevere
&& (nflag
|| fswritefd
< 0)) {
102 printf("%s? no\n\n", question
);
106 if (yflag
|| (persevere
&& nflag
)) {
107 printf("%s? yes\n\n", question
);
111 printf("%s? [yn] ", question
);
112 (void) fflush(stdout
);
114 while (c
!= '\n' && getc(stdin
) != '\n') {
120 } while (c
!= 'y' && c
!= 'Y' && c
!= 'n' && c
!= 'N');
122 if (c
== 'y' || c
== 'Y')
129 * Malloc buffers and set up cache.
138 pbp
= pdirbp
= (struct bufarea
*)0;
139 bufp
= malloc((unsigned int)sblock
->fs_bsize
);
141 errexit("cannot allocate buffer pool");
142 cgblk
.b_un
.b_buf
= bufp
;
144 bufp
= malloc((unsigned int)APPLEUFS_LABEL_SIZE
);
146 errexit("cannot allocate buffer pool");
147 appleufsblk
.b_un
.b_buf
= bufp
;
148 initbarea(&appleufsblk
);
149 bufhead
.b_next
= bufhead
.b_prev
= &bufhead
;
150 bufcnt
= MAXBUFSPACE
/ sblock
->fs_bsize
;
151 if (bufcnt
< MINBUFS
)
153 for (i
= 0; i
< bufcnt
; i
++) {
154 bp
= malloc(sizeof(struct bufarea
));
155 bufp
= malloc((unsigned int)sblock
->fs_bsize
);
156 if (bp
== NULL
|| bufp
== NULL
) {
164 errexit("cannot allocate buffer pool");
166 bp
->b_un
.b_buf
= bufp
;
167 bp
->b_prev
= &bufhead
;
168 bp
->b_next
= bufhead
.b_next
;
169 bufhead
.b_next
->b_prev
= bp
;
173 bufhead
.b_size
= i
; /* save number of buffers */
177 * Manage a cache of directory blocks.
180 getdatablk(daddr_t blkno
, long size
)
184 for (bp
= bufhead
.b_next
; bp
!= &bufhead
; bp
= bp
->b_next
)
185 if (bp
->b_bno
== fsbtodb(sblock
, blkno
))
187 for (bp
= bufhead
.b_prev
; bp
!= &bufhead
; bp
= bp
->b_prev
)
188 if ((bp
->b_flags
& B_INUSE
) == 0)
191 errexit("deadlocked buffer pool");
194 getblk(bp
, blkno
, size
);
195 bp
->b_prev
->b_next
= bp
->b_next
;
196 bp
->b_next
->b_prev
= bp
->b_prev
;
197 bp
->b_prev
= &bufhead
;
198 bp
->b_next
= bufhead
.b_next
;
199 bufhead
.b_next
->b_prev
= bp
;
201 bp
->b_flags
|= B_INUSE
;
206 getblk(struct bufarea
*bp
, daddr_t blk
, long size
)
210 dblk
= fsbtodb(sblock
, blk
);
212 if (bp
->b_bno
!= dblk
) {
213 flush(fswritefd
, bp
);
215 bp
->b_errs
= bread(fsreadfd
, bp
->b_un
.b_buf
, dblk
, size
);
222 flush(int fd
, struct bufarea
*bp
)
230 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
231 (bp
->b_errs
== bp
->b_size
/ dev_bsize
) ? "" : "PARTIALLY ",
232 (long long)bp
->b_bno
);
235 bwrite(fd
, bp
->b_un
.b_buf
, bp
->b_bno
, (long)bp
->b_size
);
238 for (i
= 0, j
= 0; i
< sblock
->fs_cssize
; i
+= sblock
->fs_bsize
, j
++) {
239 int size
= sblock
->fs_cssize
- i
< sblock
->fs_bsize
?
240 sblock
->fs_cssize
- i
: sblock
->fs_bsize
;
241 ccsp
= (struct csum
*)((char *)sblock
->fs_csp
+ i
);
243 ffs_csum_swap(ccsp
, ccsp
, size
);
244 bwrite(fswritefd
, (char *)ccsp
,
245 fsbtodb(sblock
, sblock
->fs_csaddr
+ j
* sblock
->fs_frag
),
248 ffs_csum_swap(ccsp
, ccsp
, size
);
253 rwerror(const char *mesg
, daddr_t blk
)
258 pfatal("CANNOT %s: BLK %lld", mesg
, (long long)blk
);
259 if (reply("CONTINUE") == 0)
260 exit(FSCK_EXIT_CHECK_FAILED
);
266 struct bufarea
*bp
, *nbp
;
267 int ofsmodified
, cnt
= 0;
270 (void)close(fsreadfd
);
273 flush(fswritefd
, &sblk
);
274 if (havesb
&& bflag
!= 0 &&
275 (preen
|| reply("UPDATE STANDARD SUPERBLOCK"))) {
277 pwarn("UPDATING STANDARD SUPERBLOCK\n");
278 if (!is_ufs2
&& (sblock
->fs_old_flags
& FS_FLAGS_UPDATED
) == 0)
279 sblk
.b_bno
= SBLOCK_UFS1
/ dev_bsize
;
281 sblk
.b_bno
= sblock
->fs_sblockloc
/ dev_bsize
;
283 flush(fswritefd
, &sblk
);
285 flush(fswritefd
, &appleufsblk
);
286 free(appleufsblk
.b_un
.b_buf
);
287 flush(fswritefd
, &cgblk
);
288 free(cgblk
.b_un
.b_buf
);
289 for (bp
= bufhead
.b_prev
; bp
&& bp
!= &bufhead
; bp
= nbp
) {
291 flush(fswritefd
, bp
);
293 free(bp
->b_un
.b_buf
);
296 if (bufhead
.b_size
!= cnt
)
297 errexit("Panic: lost %d buffers", bufhead
.b_size
- cnt
);
298 pbp
= pdirbp
= (struct bufarea
*)0;
299 if (markclean
&& (sblock
->fs_clean
& FS_ISCLEAN
) == 0) {
301 * Mark the file system as clean, and sync the superblock.
304 pwarn("MARKING FILE SYSTEM CLEAN\n");
305 else if (!reply("MARK FILE SYSTEM CLEAN"))
308 sblock
->fs_clean
= FS_ISCLEAN
;
309 sblock
->fs_pendingblocks
= 0;
310 sblock
->fs_pendinginodes
= 0;
312 ofsmodified
= fsmodified
;
313 flush(fswritefd
, &sblk
);
315 fsmodified
= ofsmodified
;
319 "\n***** FILE SYSTEM MARKED CLEAN *****\n");
323 printf("cache missed %ld of %ld (%d%%)\n", diskreads
,
324 totalreads
, (int)(diskreads
* 100 / totalreads
));
326 (void)close(fsreadfd
);
327 (void)close(fswritefd
);
331 bread(int fd
, char *buf
, daddr_t blk
, long size
)
339 if ((pread(fd
, buf
, (int)size
, offset
) == size
) &&
340 read_wapbl(buf
, size
, blk
) == 0)
342 rwerror("READ", blk
);
344 memset(buf
, 0, (size_t)size
);
345 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
346 for (cp
= buf
, i
= 0; i
< size
; i
+= secsize
, cp
+= secsize
) {
347 if (pread(fd
, cp
, (int)secsize
, offset
+ i
) != secsize
) {
348 if (secsize
!= dev_bsize
&& dev_bsize
!= 1)
349 printf(" %lld (%lld),",
350 (long long)((blk
*dev_bsize
+ i
) / secsize
),
351 (long long)(blk
+ i
/ dev_bsize
));
354 (long long)(blk
+ i
/ dev_bsize
));
363 bwrite(int fd
, char *buf
, daddr_t blk
, long size
)
373 if (pwrite(fd
, buf
, (int)size
, offset
) == size
) {
377 rwerror("WRITE", blk
);
378 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
379 for (cp
= buf
, i
= 0; i
< size
; i
+= dev_bsize
, cp
+= dev_bsize
)
380 if (pwrite(fd
, cp
, (int)dev_bsize
, offset
+ i
) != dev_bsize
)
381 printf(" %lld,", (long long)(blk
+ i
/ dev_bsize
));
387 * allocate a data block with the specified number of fragments
392 int i
, j
, k
, cg
, baseblk
;
393 struct cg
*cgp
= cgrp
;
395 if (frags
<= 0 || frags
> sblock
->fs_frag
)
397 for (i
= 0; i
< maxfsblock
- sblock
->fs_frag
; i
+= sblock
->fs_frag
) {
398 for (j
= 0; j
<= sblock
->fs_frag
- frags
; j
++) {
401 for (k
= 1; k
< frags
; k
++)
402 if (testbmap(i
+ j
+ k
))
408 cg
= dtog(sblock
, i
+ j
);
409 getblk(&cgblk
, cgtod(sblock
, cg
), sblock
->fs_cgsize
);
410 memcpy(cgp
, cgblk
.b_un
.b_cg
, sblock
->fs_cgsize
);
411 if ((doswap
&& !needswap
) || (!doswap
&& needswap
))
412 ffs_cg_swap(cgblk
.b_un
.b_cg
, cgp
, sblock
);
413 if (!cg_chkmagic(cgp
, 0))
414 pfatal("CG %d: ALLOCBLK: BAD MAGIC NUMBER\n",
416 baseblk
= dtogd(sblock
, i
+ j
);
417 for (k
= 0; k
< frags
; k
++) {
419 clrbit(cg_blksfree(cgp
, 0), baseblk
+ k
);
422 if (frags
== sblock
->fs_frag
)
423 cgp
->cg_cs
.cs_nbfree
--;
425 cgp
->cg_cs
.cs_nffree
-= frags
;
434 * Free a previously allocated block
437 freeblk(daddr_t blkno
, long frags
)
439 struct inodesc idesc
;
441 idesc
.id_blkno
= blkno
;
442 idesc
.id_numfrags
= frags
;
443 (void)pass4check(&idesc
);
450 getpathname(char *namebuf
, size_t namebuflen
, ino_t curdir
, ino_t ino
)
454 struct inodesc idesc
;
456 struct inostat
*info
;
458 if (curdir
== ino
&& ino
== ROOTINO
) {
459 (void)strlcpy(namebuf
, "/", namebuflen
);
462 info
= inoinfo(curdir
);
463 if (busy
|| (info
->ino_state
!= DSTATE
&& info
->ino_state
!= DFOUND
)) {
464 (void)strlcpy(namebuf
, "?", namebuflen
);
468 memset(&idesc
, 0, sizeof(struct inodesc
));
469 idesc
.id_type
= DATA
;
470 idesc
.id_fix
= IGNORE
;
471 cp
= &namebuf
[MAXPATHLEN
- 1];
474 idesc
.id_parent
= curdir
;
477 while (ino
!= ROOTINO
) {
478 idesc
.id_number
= ino
;
479 idesc
.id_func
= findino
;
480 idesc
.id_name
= "..";
481 if ((ckinode(ginode(ino
), &idesc
) & FOUND
) == 0)
484 idesc
.id_number
= idesc
.id_parent
;
485 idesc
.id_parent
= ino
;
486 idesc
.id_func
= findname
;
487 idesc
.id_name
= namebuf
;
488 if ((ckinode(ginode(idesc
.id_number
), &idesc
)&FOUND
) == 0)
490 len
= strlen(namebuf
);
492 memmove(cp
, namebuf
, (size_t)len
);
494 if (cp
< &namebuf
[FFS_MAXNAMLEN
])
496 ino
= idesc
.id_number
;
501 memmove(namebuf
, cp
, (size_t)(&namebuf
[MAXPATHLEN
] - cp
));
511 _exit(FSCK_EXIT_SIGNALLED
);
515 * When preening, allow a single quit to signal
516 * a special exit after filesystem checks complete
517 * so that reboot sequence may be interrupted.
522 static const char msg
[] =
523 "returning to single-user after file system check\n";
526 (void)write(STDOUT_FILENO
, msg
, sizeof(msg
) - 1);
528 (void)signal(SIGQUIT
, SIG_DFL
);
533 * Ignore a single quit signal; wait and flush just in case.
534 * Used by child processes in preen.
542 (void)signal(SIGQUIT
, SIG_IGN
);
543 (void)signal(SIGQUIT
, SIG_DFL
);
548 * determine whether an inode should be fixed.
551 dofix(struct inodesc
*idesc
, const char *msg
)
554 switch (idesc
->id_fix
) {
557 if (idesc
->id_type
== DATA
)
558 direrror(idesc
->id_number
, msg
);
562 printf(" (SALVAGED)\n");
566 if (reply("SALVAGE") == 0) {
567 idesc
->id_fix
= NOFIX
;
581 errexit("UNKNOWN INODESC FIX MODE %d", idesc
->id_fix
);
588 copyback_cg(struct bufarea
*blk
)
591 memcpy(blk
->b_un
.b_cg
, cgrp
, sblock
->fs_cgsize
);
593 ffs_cg_swap(cgrp
, blk
->b_un
.b_cg
, sblock
);
603 * Look up state information for an inode.
608 static struct inostat unallocated
= { USTATE
, 0, 0 };
609 struct inostatlist
*ilp
;
613 errexit("inoinfo: inumber %llu out of range",
614 (unsigned long long)inum
);
615 ilp
= &inostathead
[inum
/ sblock
->fs_ipg
];
616 iloff
= inum
% sblock
->fs_ipg
;
617 if (iloff
>= ilp
->il_numalloced
)
618 return (&unallocated
);
619 return (&ilp
->il_stat
[iloff
]);
623 sb_oldfscompat_read(struct fs
*fs
, struct fs
**fssave
)
625 if ((fs
->fs_magic
!= FS_UFS1_MAGIC
) ||
626 (fs
->fs_old_flags
& FS_FLAGS_UPDATED
))
629 /* Save a copy of fields that may be modified for compatibility */
632 *fssave
= malloc(sizeof(struct fs
));
634 errexit("cannot allocate space for compat store");
635 memmove(*fssave
, fs
, sizeof(struct fs
));
638 printf("detected ufs1 superblock not yet updated for ufs2 kernels\n");
641 uint16_t postbl
[256];
644 if (fs
->fs_old_postblformat
== FS_42POSTBLFMT
)
649 /* extract the postbl from the unswapped superblock */
651 ffs_sb_swap(*fssave
, *fssave
);
652 memmove(postbl
, (&(*fssave
)->fs_old_postbl_start
),
653 n
* sizeof(postbl
[0]));
655 ffs_sb_swap(*fssave
, *fssave
);
658 for (i
=0; i
< n
; i
++)
659 postbl
[i
] = bswap16(postbl
[i
]);
661 /* And put it back such that it will get correctly
662 * unscrambled if it is swapped again on the way out
665 ffs_sb_swap(*fssave
, *fssave
);
666 memmove((&(*fssave
)->fs_old_postbl_start
), postbl
,
667 n
* sizeof(postbl
[0]));
669 ffs_sb_swap(*fssave
, *fssave
);
674 /* These fields will be overwritten by their
675 * original values in fs_oldfscompat_write, so it is harmless
676 * to modify them here.
678 fs
->fs_cstotal
.cs_ndir
=
679 fs
->fs_old_cstotal
.cs_ndir
;
680 fs
->fs_cstotal
.cs_nbfree
=
681 fs
->fs_old_cstotal
.cs_nbfree
;
682 fs
->fs_cstotal
.cs_nifree
=
683 fs
->fs_old_cstotal
.cs_nifree
;
684 fs
->fs_cstotal
.cs_nffree
=
685 fs
->fs_old_cstotal
.cs_nffree
;
687 fs
->fs_maxbsize
= fs
->fs_bsize
;
688 fs
->fs_time
= fs
->fs_old_time
;
689 fs
->fs_size
= fs
->fs_old_size
;
690 fs
->fs_dsize
= fs
->fs_old_dsize
;
691 fs
->fs_csaddr
= fs
->fs_old_csaddr
;
692 fs
->fs_sblockloc
= SBLOCK_UFS1
;
694 fs
->fs_flags
= fs
->fs_old_flags
;
696 if (fs
->fs_old_postblformat
== FS_42POSTBLFMT
) {
697 fs
->fs_old_nrpos
= 8;
698 fs
->fs_old_npsect
= fs
->fs_old_nsect
;
699 fs
->fs_old_interleave
= 1;
700 fs
->fs_old_trackskew
= 0;
705 sb_oldfscompat_write(struct fs
*fs
, struct fs
*fssave
)
707 if ((fs
->fs_magic
!= FS_UFS1_MAGIC
) ||
708 (fs
->fs_old_flags
& FS_FLAGS_UPDATED
))
711 fs
->fs_old_flags
= fs
->fs_flags
;
712 fs
->fs_old_time
= fs
->fs_time
;
713 fs
->fs_old_cstotal
.cs_ndir
= fs
->fs_cstotal
.cs_ndir
;
714 fs
->fs_old_cstotal
.cs_nbfree
= fs
->fs_cstotal
.cs_nbfree
;
715 fs
->fs_old_cstotal
.cs_nifree
= fs
->fs_cstotal
.cs_nifree
;
716 fs
->fs_old_cstotal
.cs_nffree
= fs
->fs_cstotal
.cs_nffree
;
718 fs
->fs_flags
= fssave
->fs_flags
;
720 if (fs
->fs_old_postblformat
== FS_42POSTBLFMT
) {
721 fs
->fs_old_nrpos
= fssave
->fs_old_nrpos
;
722 fs
->fs_old_npsect
= fssave
->fs_old_npsect
;
723 fs
->fs_old_interleave
= fssave
->fs_old_interleave
;
724 fs
->fs_old_trackskew
= fssave
->fs_old_trackskew
;
727 memmove(&fs
->fs_old_postbl_start
, &fssave
->fs_old_postbl_start
,
728 ((fs
->fs_old_postblformat
== FS_42POSTBLFMT
) ?