diff options
Diffstat (limited to 'sbin/fsck')
| -rw-r--r-- | sbin/fsck/Makefile | 9 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/0.t | 150 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/1.t | 83 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/2.t | 265 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/3.t | 439 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/4.t | 1424 | ||||
| -rw-r--r-- | sbin/fsck/SMM.doc/Makefile | 7 | ||||
| -rw-r--r-- | sbin/fsck/dir.c | 681 | ||||
| -rw-r--r-- | sbin/fsck/fsck.8 | 291 | ||||
| -rw-r--r-- | sbin/fsck/fsck.h | 215 | ||||
| -rw-r--r-- | sbin/fsck/inode.c | 543 | ||||
| -rw-r--r-- | sbin/fsck/main.c | 318 | ||||
| -rw-r--r-- | sbin/fsck/pass1.c | 314 | ||||
| -rw-r--r-- | sbin/fsck/pass1b.c | 99 | ||||
| -rw-r--r-- | sbin/fsck/pass2.c | 430 | ||||
| -rw-r--r-- | sbin/fsck/pass3.c | 71 | ||||
| -rw-r--r-- | sbin/fsck/pass4.c | 133 | ||||
| -rw-r--r-- | sbin/fsck/pass5.c | 319 | ||||
| -rw-r--r-- | sbin/fsck/preen.c | 354 | ||||
| -rw-r--r-- | sbin/fsck/setup.c | 466 | ||||
| -rw-r--r-- | sbin/fsck/utilities.c | 566 | 
21 files changed, 7177 insertions, 0 deletions
| diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile new file mode 100644 index 000000000000..224f6b2200e7 --- /dev/null +++ b/sbin/fsck/Makefile @@ -0,0 +1,9 @@ +#	@(#)Makefile	8.1 (Berkeley) 6/5/93 + +PROG=	fsck +MAN8=	fsck.0 +SRCS=	dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \ +	pass5.c preen.c setup.c utilities.c ffs_subr.c ffs_tables.c +.PATH:	${.CURDIR}/../../sys/ufs/ffs + +.include <bsd.prog.mk> diff --git a/sbin/fsck/SMM.doc/0.t b/sbin/fsck/SMM.doc/0.t new file mode 100644 index 000000000000..9de391eec1ef --- /dev/null +++ b/sbin/fsck/SMM.doc/0.t @@ -0,0 +1,150 @@ +.\" Copyright (c) 1986, 1993 +.\"  The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)0.t	8.1 (Berkeley) 6/8/93 +.\" +.if n .ND +.TL +Fsck \- The UNIX\(dg File System Check Program +.EH 'SMM:3-%''The \s-2UNIX\s+2 File System Check Program' +.OH 'The \s-2UNIX\s+2 File System Check Program''SMM:3-%' +.AU +Marshall Kirk McKusick +.AI +Computer Systems Research Group +Computer Science Division +Department of Electrical Engineering and Computer Science +University of California, Berkeley +Berkeley, CA  94720 +.AU +T. J. Kowalski +.AI +Bell Laboratories +Murray Hill, New Jersey 07974 +.AB +.FS +\(dgUNIX is a trademark of Bell Laboratories. +.FE +.FS +This work was done under grants from +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. +.FE +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization.  This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +File System Check Program (\fIfsck\fR) +is an interactive file system check and repair program. +.I Fsck +uses the redundant structural information in the +UNIX file system to perform several consistency checks. +If an inconsistency is detected, it is reported +to the operator, who may elect to fix or ignore +each inconsistency. +These inconsistencies result from the permanent interruption +of the file system updates, which are performed every +time a file is modified. +Unless there has been a hardware failure, +.I fsck +is able to repair corrupted file systems +using procedures based upon the order in which UNIX honors +these file system update requests. +.PP +The purpose of this document is to describe the normal updating +of the file system, +to discuss the possible causes of file system corruption, +and to present the corrective actions implemented +by +.I fsck. +Both the program and the interaction between the +program and the operator are described. +.sp 2 +.LP +Revised July 16, 1985 +.AE +.LP +.bp +.ce +.B "TABLE OF CONTENTS" +.LP +.sp 1 +.nf +.B "1.  Introduction" +.LP +.sp .5v +.nf +.B "2.  Overview of the file system +2.1.    Superblock +2.2.    Summary Information +2.3.    Cylinder groups +2.4.    Fragments +2.5.    Updates to the file system +.LP +.sp .5v +.nf +.B "3.  Fixing corrupted file systems +3.1.    Detecting and correcting corruption +3.2.    Super block checking +3.3.    Free block checking +3.4.    Checking the inode state +3.5.    Inode links +3.6.    Inode data size +3.7.    Checking the data associated with an inode +3.8.    File system connectivity +.LP +.sp .5v +.nf +.B Acknowledgements +.LP +.sp .5v +.nf +.B References +.LP +.sp .5v +.nf +.B "4.  Appendix A +4.1.     Conventions +4.2.     Initialization +4.3.     Phase 1 - Check Blocks and Sizes +4.4.     Phase 1b - Rescan for more Dups +4.5.     Phase 2 - Check Pathnames +4.6.     Phase 3 - Check Connectivity +4.7.     Phase 4 - Check Reference Counts +4.8.     Phase 5 - Check Cyl groups +4.9.     Cleanup +.ds RH Introduction +.bp diff --git a/sbin/fsck/SMM.doc/1.t b/sbin/fsck/SMM.doc/1.t new file mode 100644 index 000000000000..4d2f5357131b --- /dev/null +++ b/sbin/fsck/SMM.doc/1.t @@ -0,0 +1,83 @@ +.\" Copyright (c) 1982, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)1.t	8.1 (Berkeley) 6/5/93 +.\" +.ds RH Introduction +.NH +Introduction +.PP +This document reflects the use of +.I fsck +with the 4.2BSD and 4.3BSD file system organization.  This +is a revision of the +original paper written by +T. J. Kowalski. +.PP +When a UNIX +operating system is brought up, a consistency +check of the file systems should always be performed. +This precautionary measure helps to insure +a reliable environment for file storage on disk. +If an inconsistency is discovered, +corrective action must be taken. +.I Fsck +runs in two modes. +Normally it is run non-interactively by the system after  +a normal boot. +When running in this mode, +it will only make changes to the file system that are known +to always be correct. +If an unexpected inconsistency is found +.I fsck +will exit with a non-zero exit status,  +leaving the system running single-user. +Typically the operator then runs  +.I fsck +interactively. +When running in this mode, +each problem is listed followed by a suggested corrective action. +The operator must decide whether or not the suggested correction +should be made. +.PP +The purpose of this memo is to dispel the +mystique surrounding +file system inconsistencies. +It first describes the updating of the file system +(the calm before the storm) and +then describes file system corruption (the storm). +Finally, +the set of deterministic corrective actions +used by +.I fsck +(the Coast Guard +to the rescue) is presented. +.ds RH Overview of the File System diff --git a/sbin/fsck/SMM.doc/2.t b/sbin/fsck/SMM.doc/2.t new file mode 100644 index 000000000000..7d00ceaa4efd --- /dev/null +++ b/sbin/fsck/SMM.doc/2.t @@ -0,0 +1,265 @@ +.\" Copyright (c) 1982, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)2.t	8.1 (Berkeley) 6/5/93 +.\" +.ds RH Overview of the file system +.NH +Overview of the file system +.PP +The file system is discussed in detail in [Mckusick84]; +this section gives a brief overview. +.NH 2 +Superblock +.PP +A file system is described by its +.I "super-block" . +The super-block is built when the file system is created (\c +.I newfs (8)) +and never changes. +The super-block +contains the basic parameters of the file system, +such as the number of data blocks it contains +and a count of the maximum number of files. +Because the super-block contains critical data, +.I newfs +replicates it to protect against catastrophic loss. +The +.I "default super block" +always resides at a fixed offset from the beginning +of the file system's disk partition. +The +.I "redundant super blocks" +are not referenced unless a head crash +or other hard disk error causes the default super-block +to be unusable. +The redundant blocks are sprinkled throughout the disk partition. +.PP +Within the file system are files. +Certain files are distinguished as directories and contain collections +of pointers to files that may themselves be directories. +Every file has a descriptor associated with it called an +.I "inode". +The inode contains information describing ownership of the file, +time stamps indicating modification and access times for the file, +and an array of indices pointing to the data blocks for the file. +In this section, +we assume that the first 12 blocks +of the file are directly referenced by values stored +in the inode structure itself\(dg. +.FS +\(dgThe actual number may vary from system to system, but is usually in +the range 5-13. +.FE +The inode structure may also contain references to indirect blocks +containing further data block indices. +In a file system with a 4096 byte block size, a singly indirect +block contains 1024 further block addresses, +a doubly indirect block contains 1024 addresses of further single indirect +blocks, +and a triply indirect block contains 1024 addresses of further doubly indirect +blocks (the triple indirect block is never needed in practice). +.PP +In order to create files with up to +2\(ua32 bytes, +using only two levels of indirection, +the minimum size of a file system block is 4096 bytes. +The size of file system blocks can be any power of two +greater than or equal to 4096. +The block size of the file system is maintained in the super-block, +so it is possible for file systems of different block sizes +to be accessible simultaneously on the same system. +The block size must be decided when +.I newfs +creates the file system; +the block size cannot be subsequently +changed without rebuilding the file system. +.NH 2 +Summary information +.PP +Associated with the super block is non replicated +.I "summary information" . +The summary information changes +as the file system is modified. +The summary information contains +the number of blocks, fragments, inodes and directories in the file system. +.NH 2 +Cylinder groups +.PP +The file system partitions the disk into one or more areas called +.I "cylinder groups". +A cylinder group is comprised of one or more consecutive +cylinders on a disk. +Each cylinder group includes inode slots for files, a +.I "block map" +describing available blocks in the cylinder group, +and summary information describing the usage of data blocks +within the cylinder group. +A fixed number of inodes is allocated for each cylinder group +when the file system is created. +The current policy is to allocate one inode for each 2048 +bytes of disk space; +this is expected to be far more inodes than will ever be needed. +.PP +All the cylinder group bookkeeping information could be +placed at the beginning of each cylinder group. +However if this approach were used, +all the redundant information would be on the top platter. +A single hardware failure that destroyed the top platter +could cause the loss of all copies of the redundant super-blocks. +Thus the cylinder group bookkeeping information +begins at a floating offset from the beginning of the cylinder group. +The offset for +the +.I "i+1" st +cylinder group is about one track further +from the beginning of the cylinder group +than it was for the +.I "i" th +cylinder group. +In this way, +the redundant +information spirals down into the pack; +any single track, cylinder, +or platter can be lost without losing all copies of the super-blocks. +Except for the first cylinder group, +the space between the beginning of the cylinder group +and the beginning of the cylinder group information stores data. +.NH 2 +Fragments +.PP +To avoid waste in storing small files, +the file system space allocator divides a single +file system block into one or more +.I "fragments". +The fragmentation of the file system is specified +when the file system is created; +each file system block can be optionally broken into +2, 4, or 8 addressable fragments. +The lower bound on the size of these fragments is constrained +by the disk sector size; +typically 512 bytes is the lower bound on fragment size. +The block map associated with each cylinder group +records the space availability at the fragment level. +Aligned fragments are examined +to determine block availability. +.PP +On a file system with a block size of 4096 bytes +and a fragment size of 1024 bytes, +a file is represented by zero or more 4096 byte blocks of data, +and possibly a single fragmented block. +If a file system block must be fragmented to obtain +space for a small amount of data, +the remainder of the block is made available for allocation +to other files. +For example, +consider an 11000 byte file stored on +a 4096/1024 byte file system. +This file uses two full size blocks and a 3072 byte fragment. +If no fragments with at least 3072 bytes +are available when the file is created, +a full size block is split yielding the necessary 3072 byte +fragment and an unused 1024 byte fragment. +This remaining fragment can be allocated to another file, as needed. +.NH 2 +Updates to the file system +.PP +Every working day hundreds of files +are created, modified, and removed. +Every time a file is modified, +the operating system performs a +series of file system updates. +These updates, when written on disk, yield a consistent file system. +The file system stages +all modifications of critical information; +modification can +either be completed or cleanly backed out after a crash. +Knowing the information that is first written to the file system, +deterministic procedures can be developed to +repair a corrupted file system. +To understand this process, +the order that the update +requests were being honored must first be understood. +.PP +When a user program does an operation to change the file system, +such as a  +.I write , +the data to be written is copied into an internal +.I "in-core" +buffer in the kernel. +Normally, the disk update is handled asynchronously; +the user process is allowed to proceed even though +the data has not yet been written to the disk. +The data, +along with the inode information reflecting the change, +is eventually written out to disk. +The real disk write may not happen until long after the +.I write +system call has returned. +Thus at any given time, the file system, +as it resides on the disk, +lags the state of the file system represented by the in-core information. +.PP +The disk information is updated to reflect the in-core information +when the buffer is required for another use, +when a +.I sync (2) +is done (at 30 second intervals) by +.I "/etc/update" "(8)," +or by manual operator intervention with the +.I sync (8) +command. +If the system is halted without writing out the in-core information, +the file system on the disk will be in an inconsistent state. +.PP +If all updates are done asynchronously, several serious +inconsistencies can arise. +One inconsistency is that a block may be claimed by two inodes. +Such an inconsistency can occur when the system is halted before +the pointer to the block in the old inode has been cleared +in the copy of the old inode on the disk, +and after the pointer to the block in the new inode has been written out +to the copy of the new inode on the disk. +Here, +there is no deterministic method for deciding +which inode should really claim the block. +A similar problem can arise with a multiply claimed inode. +.PP +The problem with asynchronous inode updates +can be avoided by doing all inode deallocations synchronously.  +Consequently, +inodes and indirect blocks are written to the disk synchronously +(\fIi.e.\fP the process blocks until the information is +really written to disk) +when they are being deallocated. +Similarly inodes are kept consistent by synchronously +deleting, adding, or changing directory entries. +.ds RH Fixing corrupted file systems diff --git a/sbin/fsck/SMM.doc/3.t b/sbin/fsck/SMM.doc/3.t new file mode 100644 index 000000000000..07b5431f3e59 --- /dev/null +++ b/sbin/fsck/SMM.doc/3.t @@ -0,0 +1,439 @@ +.\" Copyright (c) 1982, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)3.t	8.1 (Berkeley) 6/5/93 +.\" +.ds RH Fixing corrupted file systems +.NH +Fixing corrupted file systems +.PP +A file system +can become corrupted in several ways. +The most common of these ways are +improper shutdown procedures +and hardware failures. +.PP +File systems may become corrupted during an +.I "unclean halt" . +This happens when proper shutdown +procedures are not observed, +physically write-protecting a mounted file system, +or a mounted file system is taken off-line. +The most common operator procedural failure is forgetting to +.I sync +the system before halting the CPU. +.PP +File systems may become further corrupted if proper startup +procedures are not observed, e.g., +not checking a file system for inconsistencies, +and not repairing inconsistencies. +Allowing a corrupted file system to be used (and, thus, to be modified +further) can be disastrous. +.PP +Any piece of hardware can fail at any time. +Failures +can be as subtle as a bad block +on a disk pack, or as blatant as a non-functional disk-controller. +.NH 2 +Detecting and correcting corruption +.PP +Normally +.I fsck +is run non-interactively. +In this mode it will only fix +corruptions that are expected to occur from an unclean halt. +These actions are a proper subset of the actions that  +.I fsck +will take when it is running interactively. +Throughout this paper we assume that  +.I fsck  +is being run interactively, +and all possible errors can be encountered. +When an inconsistency is discovered in this mode, +.I fsck +reports the inconsistency for the operator to +chose a corrective action. +.PP +A quiescent\(dd +.FS +\(dd I.e., unmounted and not being written on. +.FE +file system may be checked for structural integrity +by performing consistency checks on the +redundant data intrinsic to a file system. +The redundant data is either read from +the file system, +or computed from other known values. +The file system +.B must +be in a quiescent state when +.I fsck +is run, +since +.I fsck +is a multi-pass program. +.PP +In the following sections, +we discuss methods to discover inconsistencies +and possible corrective actions +for the cylinder group blocks, the inodes, the indirect blocks, and +the data blocks containing directory entries. +.NH 2 +Super-block checking +.PP +The most commonly corrupted item in a file system +is the summary information +associated with the super-block. +The summary information is prone to corruption +because it is modified with every change to the file +system's blocks or inodes, +and is usually corrupted +after an unclean halt. +.PP +The super-block is checked for inconsistencies +involving file-system size, number of inodes, +free-block count, and the free-inode count. +The file-system size must be larger than the +number of blocks used by the super-block +and the number of blocks used by the list of inodes. +The file-system size and layout information +are the most critical pieces of information for +.I fsck . +While there is no way to actually check these sizes, +since they are statically determined by +.I newfs , +.I fsck +can check that these sizes are within reasonable bounds. +All other file system checks require that these sizes be correct. +If +.I fsck  +detects corruption in the static parameters of the default super-block, +.I fsck +requests the operator to specify the location of an alternate super-block. +.NH 2 +Free block checking +.PP +.I Fsck +checks that all the blocks +marked as free in the cylinder group block maps +are not claimed by any files. +When all the blocks have been initially accounted for, +.I fsck +checks that +the number of free blocks +plus the number of blocks claimed by the inodes +equals the total number of blocks in the file system. +.PP +If anything is wrong with the block allocation maps, +.I fsck +will rebuild them, +based on the list it has computed of allocated blocks. +.PP +The summary information associated with the super-block +counts the total number of free blocks within the file system. +.I Fsck +compares this count to the +number of free blocks it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the summary information +by the actual free-block count. +.PP +The summary information +counts the total number of free inodes within the file system. +.I Fsck +compares this count to the number +of free inodes it found within the file system. +If the two counts do not agree, then +.I fsck +replaces the incorrect count in the +summary information by the actual free-inode count. +.NH 2 +Checking the inode state +.PP +An individual inode is not as likely to be corrupted as +the allocation information. +However, because of the great number of active inodes, +a few of the inodes are usually corrupted. +.PP +The list of inodes in the file system +is checked sequentially starting with inode 2 +(inode 0 marks unused inodes; +inode 1 is saved for future generations) +and progressing through the last inode in the file system. +The state of each inode is checked for +inconsistencies involving format and type, +link count, +duplicate blocks, +bad blocks, +and inode size. +.PP +Each inode contains a mode word. +This mode word describes the type and state of the inode. +Inodes must be one of six types: +regular inode, directory inode, symbolic link inode, +special block inode, special character inode, or socket inode. +Inodes may be found in one of three allocation states: +unallocated, allocated, and neither unallocated nor allocated. +This last state suggests an incorrectly formated inode. +An inode can get in this state if +bad data is written into the inode list. +The only possible corrective action is for +.I fsck +is to clear the inode. +.NH 2 +Inode links +.PP +Each inode counts the +total number of directory entries +linked to the inode. +.I Fsck +verifies the link count of each inode +by starting at the root of the file system, +and descending through the directory structure. +The actual link count for each inode +is calculated during the descent. +.PP +If the stored link count is non-zero and the actual +link count is zero, +then no directory entry appears for the inode. +If this happens, +.I fsck +will place the disconnected file in the +.I lost+found +directory. +If the stored and actual link counts are non-zero and unequal, +a directory entry may have been added or removed without the inode being +updated. +If this happens, +.I fsck +replaces the incorrect stored link count by the actual link count. +.PP +Each inode contains a list, +or pointers to +lists (indirect blocks), +of all the blocks claimed by the inode. +Since indirect blocks are owned by an inode, +inconsistencies in indirect blocks directly +affect the inode that owns it. +.PP +.I Fsck +compares each block number claimed by an inode +against a list of already allocated blocks. +If another inode already claims a block number, +then the block number is added to a list of +.I "duplicate blocks" . +Otherwise, the list of allocated blocks +is updated to include the block number. +.PP +If there are any duplicate blocks, +.I fsck +will perform a partial second +pass over the inode list +to find the inode of the duplicated block. +The second pass is needed, +since without examining the files associated with +these inodes for correct content, +not enough information is available +to determine which inode is corrupted and should be cleared. +If this condition does arise +(only hardware failure will cause it), +then the inode with the earliest +modify time is usually incorrect, +and should be cleared. +If this happens, +.I fsck +prompts the operator to clear both inodes. +The operator must decide which one should be kept +and which one should be cleared. +.PP +.I Fsck +checks the range of each block number claimed by an inode. +If the block number is +lower than the first data block in the file system, +or greater than the last data block, +then the block number is a +.I "bad block number" . +Many bad blocks in an inode are usually caused by +an indirect block that was not written to the file system, +a condition which can only occur if there has been a hardware failure. +If an inode contains bad block numbers, +.I fsck +prompts the operator to clear it. +.NH 2 +Inode data size +.PP +Each inode contains a count of the number of data blocks +that it contains. +The number of actual data blocks +is the sum of the allocated data blocks +and the indirect blocks. +.I Fsck +computes the actual number of data blocks +and compares that block count against +the actual number of blocks the inode claims. +If an inode contains an incorrect count +.I fsck +prompts the operator to fix it. +.PP +Each inode contains a thirty-two bit size field. +The size is the number of data bytes +in the file associated with the inode. +The consistency of the byte size field is roughly checked +by computing from the size field the maximum number of blocks +that should be associated with the inode, +and comparing that expected block count against +the actual number of blocks the inode claims. +.NH 2 +Checking the data associated with an inode +.PP +An inode can directly or indirectly +reference three kinds of data blocks. +All referenced blocks must be the same kind. +The three types of data blocks are: +plain data blocks, symbolic link data blocks, and directory data blocks. +Plain data blocks +contain the information stored in a file; +symbolic link data blocks +contain the path name stored in a link. +Directory data blocks contain directory entries. +.I Fsck +can only check the validity of directory data blocks. +.PP +Each directory data block is checked for +several types of inconsistencies. +These inconsistencies include +directory inode numbers pointing to unallocated inodes, +directory inode numbers that are greater than +the number of inodes in the file system, +incorrect directory inode numbers for ``\fB.\fP'' and ``\fB..\fP'', +and directories that are not attached to the file system. +If the inode number in a directory data block +references an unallocated inode, +then +.I fsck +will remove that directory entry. +Again, +this condition can only arise when there has been a hardware failure. +.PP +If a directory entry inode number references +outside the inode list, then +.I fsck +will remove that directory entry. +This condition occurs if bad data is written into a directory data block. +.PP +The directory inode number entry for ``\fB.\fP'' +must be the first entry in the directory data block. +The inode number for ``\fB.\fP'' +must reference itself; +e.g., it must equal the inode number +for the directory data block. +The directory inode number entry +for ``\fB..\fP'' must be +the second entry in the directory data block. +Its value must equal the inode number for the +parent of the directory entry +(or the inode number of the directory +data block if the directory is the +root directory). +If the directory inode numbers are +incorrect, +.I fsck +will replace them with the correct values. +If there are multiple hard links to a directory, +the first one encountered is considered the real parent +to which ``\fB..\fP'' should point; +\fIfsck\fP recommends deletion for the subsequently discovered names. +.NH 2 +File system connectivity +.PP +.I Fsck +checks the general connectivity of the file system. +If directories are not linked into the file system, then +.I fsck +links the directory back into the file system in the +.I lost+found +directory. +This condition only occurs when there has been a hardware failure. +.ds RH "References" +.SH +\s+2Acknowledgements\s0 +.PP +I thank Bill Joy, Sam Leffler, Robert Elz and Dennis Ritchie  +for their suggestions and help in implementing the new file system. +Thanks also to Robert Henry for his editorial input to +get this document together. +Finally we thank our sponsors, +the National Science Foundation under grant MCS80-05144, +and the Defense Advance Research Projects Agency (DoD) under +Arpa Order No. 4031 monitored by Naval Electronic System Command under +Contract No. N00039-82-C-0235. (Kirk McKusick, July 1983) +.PP +I would like to thank Larry A. Wehr for advice that lead +to the first version of +.I fsck +and Rick B. Brandt for adapting +.I fsck +to +UNIX/TS. (T. Kowalski, July 1979) +.sp 2 +.SH +\s+2References\s0 +.LP +.IP [Dolotta78] 20 +Dolotta, T. A., and Olsson, S. B. eds., +.I "UNIX User's Manual, Edition 1.1\^" , +January 1978. +.IP [Joy83] 20 +Joy, W., Cooper, E., Fabry, R., Leffler, S., McKusick, M., and Mosher, D. +4.2BSD System Manual, +.I "University of California at Berkeley" , +.I "Computer Systems Research Group Technical Report" +#4, 1982. +.IP [McKusick84] 20 +McKusick, M., Joy, W., Leffler, S., and Fabry, R. +A Fast File System for UNIX, +\fIACM Transactions on Computer Systems 2\fP, 3. +pp. 181-197, August 1984. +.IP [Ritchie78] 20 +Ritchie, D. M., and Thompson, K., +The UNIX Time-Sharing System, +.I "The Bell System Technical Journal" +.B 57 , +6 (July-August 1978, Part 2), pp. 1905-29. +.IP [Thompson78] 20 +Thompson, K., +UNIX Implementation, +.I "The Bell System Technical Journal\^" +.B 57 , +6 (July-August 1978, Part 2), pp. 1931-46. +.ds RH Appendix A \- Fsck Error Conditions +.bp diff --git a/sbin/fsck/SMM.doc/4.t b/sbin/fsck/SMM.doc/4.t new file mode 100644 index 000000000000..5ea8179fa904 --- /dev/null +++ b/sbin/fsck/SMM.doc/4.t @@ -0,0 +1,1424 @@ +.\" Copyright (c) 1982, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)4.t	8.1 (Berkeley) 6/5/93 +.\" +.ds RH Appendix A \- Fsck Error Conditions +.NH +Appendix A \- Fsck Error Conditions +.NH 2  +Conventions +.PP +.I Fsck +is +a multi-pass file system check program. +Each file system pass invokes a different Phase of the +.I fsck +program. +After the initial setup, +.I fsck +performs successive Phases over each file system, +checking blocks and sizes, +path-names, +connectivity, +reference counts, +and the map of free blocks, +(possibly rebuilding it), +and performs some cleanup. +.LP +Normally +.I fsck +is run non-interactively to +.I preen +the file systems after an unclean halt. +While preen'ing a file system, +it will only fix corruptions that are expected +to occur from an unclean halt. +These actions are a proper subset of the actions that  +.I fsck +will take when it is running interactively. +Throughout this appendix many errors have several options +that the operator can take. +When an inconsistency is detected, +.I fsck +reports the error condition to the operator. +If a response is required, +.I fsck +prints a prompt message and +waits for a response. +When preen'ing most errors are fatal. +For those that are expected, +the response taken is noted. +This appendix explains the meaning of each error condition, +the possible responses, and the related error conditions. +.LP +The error conditions are organized by the +.I Phase +of the +.I fsck +program in which they can occur. +The error conditions that may occur +in more than one Phase +will be discussed in initialization. +.NH 2  +Initialization +.PP +Before a file system check can be performed, certain +tables have to be set up and certain files opened. +This section concerns itself with the opening of files and +the initialization of tables. +This section lists error conditions resulting from +command line options, +memory requests, +opening of files, +status of files, +file system size checks, +and creation of the scratch file. +All the initialization errors are fatal +when the file system is being preen'ed. +.sp +.LP +.B "\fIC\fP option?" +.br +\fIC\fP is not a legal option to +.I fsck ; +legal options are \-b, \-c, \-y, \-n, and \-p. +.I Fsck +terminates on this error condition. +See the +.I fsck (8) +manual entry for further detail. +.sp +.LP +.B "cannot alloc NNN bytes for blockmap" +.br +.B "cannot alloc NNN bytes for freemap" +.br +.B "cannot alloc NNN bytes for statemap" +.br +.B "cannot alloc NNN bytes for lncntp" +.br +.I Fsck 's +request for memory for its virtual +memory tables failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't open checklist file: \fIF\fP" +.br +The file system checklist file +\fIF\fP (usually +.I /etc/fstab ) +can not be opened for reading. +.I Fsck +terminates on this error condition. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't stat root" +.br +.I Fsck 's +request for statistics about the root directory ``/'' failed. +This should never happen. +.I Fsck +terminates on this error condition. +See a guru. +.sp +.LP +.B "Can't stat \fIF\fP" +.br +.B "Can't make sense out of name \fIF\fP" +.br +.I Fsck 's +request for statistics about the file system \fIF\fP failed. +When running manually, +it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "Can't open \fIF\fP" +.br +.I Fsck 's +request attempt to open the file system \fIF\fP failed. +When running manually, it ignores this file system +and continues checking the next file system given. +Check access modes of \fIF\fP. +.sp +.LP +.B "\fIF\fP: (NO WRITE)" +.br +Either the \-n flag was specified or +.I fsck 's +attempt to open the file system \fIF\fP for writing failed. +When running manually, +all the diagnostics are printed out, +but no modifications are attempted to fix them. +.sp +.LP +.B "file is not a block or character device; OK" +.br +You have given +.I fsck +a regular file name by mistake. +Check the type of the file specified. +.LP +Possible responses to the OK prompt are: +.IP YES +ignore this error condition. +.IP NO +ignore this file system and continues checking +the next file system given. +.sp +.LP +.B "UNDEFINED OPTIMIZATION IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock optimization parameter is neither OPT_TIME +nor OPT_SPACE. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The superblock is set to request optimization to minimize +running time of the system. +(If optimization to minimize disk space utilization is +desired, it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE MINFREE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The superblock minimum space percentage is greater than 99% +or less then 0%. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The minfree parameter is set to 10%. +(If some other percentage is desired, +it can be set using \fItunefs\fP(8).) +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE INTERLEAVE=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The file system interleave is less than or equal to zero. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The interleave parameter is set to 1. +.IP NO +ignore this error condition. +.sp +.LP +.B "IMPOSSIBLE NPSECT=\fID\fP IN SUPERBLOCK (SET TO DEFAULT)" +.br +The number of physical sectors per track is less than the number +of usable sectors per track. +.LP +Possible responses to the SET TO DEFAULT prompt are: +.IP YES +The npsect parameter is set to the number of usable sectors per track. +.IP NO +ignore this error condition. +.sp +.LP +One of the following messages will appear: +.br +.B "MAGIC NUMBER WRONG" +.br +.B "NCG OUT OF RANGE" +.br +.B "CPG OUT OF RANGE" +.br +.B "NCYL DOES NOT JIVE WITH NCG*CPG" +.br +.B "SIZE PREPOSTEROUSLY LARGE" +.br +.B "TRASHED VALUES IN SUPER BLOCK" +.br +and will be followed by the message: +.br +.B "\fIF\fP: BAD SUPER BLOCK: \fIB\fP" +.br +.B "USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE" +.br +.B "SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8)." +.br +The super block has been corrupted.  +An alternative super block must be selected from among those +listed by +.I newfs +(8) when the file system was created. +For file systems with a blocksize less than 32K, +specifying \-b 32 is a good first choice. +.sp +.LP +.B "INTERNAL INCONSISTENCY: \fIM\fP" +.br +.I Fsck 's +has had an internal panic, whose message is specified as \fIM\fP. +This should never happen. +See a guru. +.sp +.LP +.B "CAN NOT SEEK: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for moving to a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +Often, +however the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT READ: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for reading a specified block number \fIB\fP in +the file system failed. +This should never happen. +See a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +It will retry the read and print out the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE READ: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be read. +If  +.I fsck +ever tries to write back one of the blocks on which the read failed +it will print the message: +.br +.B "WRITING ZERO'ED BLOCK \fIN\fP TO DISK" +.br +where \fIN\fP indicates the sector that was written with zero's. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "CAN NOT WRITE: BLK \fIB\fP (CONTINUE)" +.br +.I Fsck 's +request for writing a specified block number \fIB\fP +in the file system failed. +The disk is write-protected; +check the write protect lock on the drive. +If that is not the problem, see a guru. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +attempt to continue to run the file system check. +The write operation will be retried with the failed blocks +indicated by the message: +.br +.B "THE FOLLOWING SECTORS COULD NOT BE WRITTEN: \fIN\fP" +.br +where \fIN\fP indicates the sectors that could not be written. +If the disk is experiencing hardware problems, the problem will persist. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If the block was part of the virtual memory buffer +cache, +.I fsck +will terminate with the message ``Fatal I/O error''. +.IP NO +terminate the program. +.sp +.LP +.B "bad inode number DDD to ginode" +.br +An internal error has attempted to read non-existent inode \fIDDD\fP. +This error causes  +.I fsck +to exit. +See a guru. +.NH 2  +Phase 1 \- Check Blocks and Sizes +.PP +This phase concerns itself with +the inode list. +This section lists error conditions resulting from +checking inode types, +setting up the zero-link-count table, +examining inode block numbers for bad or duplicate blocks, +checking inode size, +and checking inode format. +All errors in this phase except +.B "INCORRECT BLOCK COUNT" +and +.B "PARTIALLY TRUNCATED INODE" +are fatal if the file system is being preen'ed. +.sp +.LP +.B "UNKNOWN FILE TYPE I=\fII\fP (CLEAR)" +.br +The mode word of the inode \fII\fP indicates that the inode is not a +special block inode, special character inode, socket inode, regular inode, +symbolic link, or directory inode. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +This will always invoke the UNALLOCATED error condition in Phase 2 +for each directory entry pointing to this inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "PARTIALLY TRUNCATED INODE I=\fII\fP (SALVAGE)" +.br +.I Fsck +has found inode \fII\fP whose size is shorter than the number of +blocks allocated to it. +This condition should only occur if the system crashes while in the +midst of truncating a file. +When preen'ing the file system,  +.I fsck +completes the truncation to the specified size. +.LP +Possible responses to SALVAGE are: +.IP YES +complete the truncation to the size specified in the inode. +.IP NO +ignore this error condition. +.sp +.LP +.B "LINK COUNT TABLE OVERFLOW (CONTINUE)" +.br +An internal table for +.I fsck +containing allocated inodes with a link count of +zero cannot allocate more memory. +Increase the virtual memory for +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another allocated inode with a zero link count is found, +this error condition is repeated. +.IP NO +terminate the program. +.sp +.LP +.B "\fIB\fP BAD I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP with a number +lower than the number of the first data block in the file system or +greater than the number of the last block +in the file system. +This error condition may invoke the +.B "EXCESSIVE BAD BLKS" +error condition in Phase 1 (see next paragraph) if +inode \fII\fP has too many block numbers outside the file system range. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE BAD BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks with a number +lower than the number of the first data block in the file system or greater than +the number of last block in the file system associated with inode \fII\fP. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "BAD STATE DDD TO BLKERR" +.br +An internal error has scrambled  +.I fsck 's +state map to have the impossible value \fIDDD\fP. +.I Fsck +exits immediately.  +See a guru. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that is already claimed by +another inode. +This error condition may invoke the +.B "EXCESSIVE DUP BLKS" +error condition in Phase 1 if +inode \fII\fP has too many block numbers claimed by other inodes. +This error condition will always invoke Phase 1b and the +.B "BAD/DUP" +error condition in Phase 2 and Phase 4. +.sp +.LP +.B "EXCESSIVE DUP BLKS I=\fII\fP (CONTINUE)" +.br +There is more than a tolerable number (usually 10) of blocks claimed by other +inodes. +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the rest of the blocks in this inode +and continue checking with the next inode in the file system. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +.IP NO +terminate the program. +.sp +.LP +.B "DUP TABLE OVERFLOW (CONTINUE)" +.br +An internal table in +.I fsck +containing duplicate block numbers cannot allocate any more space. +Increase the amount of virtual memory available to +.I fsck . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +continue with the program. +This error condition will not allow a complete check of the file system. +A second run of +.I fsck +should be made to re-check this file system. +If another duplicate block is found, this error condition will repeat. +.IP NO +terminate the program. +.sp +.LP +.B "PARTIALLY ALLOCATED INODE I=\fII\fP (CLEAR)" +.br +Inode \fII\fP is neither allocated nor unallocated. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "INCORRECT BLOCK COUNT I=\fII\fP (\fIX\fP should be \fIY\fP) (CORRECT)" +.br +The block count for inode \fII\fP is \fIX\fP blocks, +but should be \fIY\fP blocks. +When preen'ing the count is corrected. +.LP +Possible responses to the CORRECT prompt are: +.IP YES +replace the block count of inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.NH 2  +Phase 1B: Rescan for More Dups +.PP +When a duplicate block is found in the file system, the file system is +rescanned to find the inode that previously claimed that block. +This section lists the error condition when the duplicate block is found. +.sp +.LP +.B "\fIB\fP DUP I=\fII\fP" +.br +Inode \fII\fP contains block number \fIB\fP that +is already claimed by another inode. +This error condition will always invoke the +.B "BAD/DUP" +error condition in Phase 2. +You can determine which inodes have overlapping blocks by examining +this error condition and the DUP error condition in Phase 1. +.NH 2  +Phase 2 \- Check Pathnames +.PP +This phase concerns itself with removing directory entries +pointing to +error conditioned inodes +from Phase 1 and Phase 1b. +This section lists error conditions resulting from +root inode mode and status, +directory inode pointers in range, +and directory entries pointing to bad inodes, +and directory integrity checks. +All errors in this phase are fatal if the file system is being preen'ed, +except for directories not being a multiple of the blocks size +and extraneous hard links. +.sp +.LP +.B "ROOT INODE UNALLOCATED (ALLOCATE)" +.br +The root inode (usually inode number 2) has no allocate mode bits. +This should never happen. +.LP +Possible responses to the ALLOCATE prompt are: +.IP YES +allocate inode 2 as the root inode. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will exit. +.sp +.LP +.B "ROOT INODE NOT DIRECTORY (REALLOCATE)" +.br +The root inode (usually inode number 2) +is not directory inode type. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "FIX" +.LP +Possible responses to the FIX prompt are: +.IP YES +replace the root inode's type to be a directory. +If the root inode's data blocks are not directory blocks, +many error conditions will be produced. +.IP NO +terminate the program. +.sp +.LP +.B "DUPS/BAD IN ROOT INODE (REALLOCATE)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks in the root inode (usually inode number 2) for the file system. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +clear the existing contents of the root inode +and reallocate it. +The files and directories usually found in the root will be recovered +in Phase 3 and put into +.I lost+found . +If the attempt to allocate the root fails, +.I fsck +will exit with the message: +.br +.B "CANNOT ALLOCATE ROOT INODE" . +.IP NO +.I fsck +will then prompt with +.B "CONTINUE" . +.LP +Possible responses to the CONTINUE prompt are: +.IP YES +ignore the +.B "DUPS/BAD" +error condition in the root inode and +attempt to continue to run the file system check. +If the root inode is not correct, +then this may result in many other error conditions. +.IP NO +terminate the program. +.sp +.LP +.B "NAME TOO LONG \fIF\fP" +.br +An excessively long path name has been found. +This usually indicates loops in the file system name space. +This can occur if the super user has made circular links to directories. +The offending links must be removed (by a guru). +.sp +.LP +.B "I OUT OF RANGE I=\fII\fP NAME=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has an inode number \fII\fP that is greater than +the end of the inode list. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNALLOCATED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +A directory or file entry \fIF\fP points to an unallocated inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "DUP/BAD I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP \fItype\fP=\fIF\fP (REMOVE)" +.br +Phase 1 or Phase 1b have found duplicate blocks or bad blocks +associated with directory or file entry \fIF\fP, inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed. +.IP NO +ignore this error condition. +.sp +.LP +.B "ZERO LENGTH DIRECTORY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (REMOVE)" +.br +A directory entry \fIF\fP has a size \fIS\fP that is zero. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +the directory entry \fIF\fP is removed; +this will always invoke the BAD/DUP error condition in Phase 4. +.IP NO +ignore this error condition. +.sp +.LP +.B "DIRECTORY TOO SHORT I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fIF\fP has been found whose size \fIS\fP +is less than the minimum size directory. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, modify time \fIT\fP, +and directory name \fIF\fP are printed. +.LP +Possible responses to the FIX prompt are: +.IP YES +increase the size of the directory to the minimum directory size. +.IP NO +ignore this directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP. +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "DIRECTORY CORRUPTED I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (SALVAGE)" +.br +A directory with an inconsistent internal state has been found. +.LP +Possible responses to the FIX prompt are: +.IP YES +throw away all entries up to the next directory boundary (usually 512-byte) +boundary. +This drastic action can throw away up to 42 entries, +and should be taken only after other recovery efforts have failed. +.IP NO +skip up to the next directory boundary and resume reading, +but do not modify the directory. +.sp +.LP +.B "BAD INODE NUMBER FOR `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `.' does +does not equal \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `.' to be equal to \fII\fP. +.IP NO +leave the inode number for `.' unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose first entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `.' with inode number equal to \fII\fP. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose first entry is \fIF\fP. +.I Fsck +cannot resolve this problem.  +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `.' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `.'" +.br +A directory \fII\fP has been found whose first entry is not `.'. +.I Fsck +cannot resolve this problem as it should never happen. +See a guru. +.sp +.LP +.B "EXTRA `.' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `.'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `.'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "BAD INODE NUMBER FOR `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose inode number for `..' does +does not equal the parent of \fII\fP. +.LP +Possible responses to the FIX prompt are: +.IP YES +change the inode number for `..' to be equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the inode number for `..' unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found whose second entry is unallocated. +.LP +Possible responses to the FIX prompt are: +.IP YES +build an entry for `..' with inode number equal to the parent of \fII\fP +(``\fB..\fP'' in the root inode points to itself). +.IP NO +leave the directory unchanged. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS \fIF\fP" +.br +A directory \fII\fP has been found whose second entry is \fIF\fP. +.I Fsck +cannot resolve this problem.  +The file system should be mounted and the offending entry \fIF\fP +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "MISSING `..' I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP" +.br +.B "CANNOT FIX, INSUFFICIENT SPACE TO ADD `..'" +.br +A directory \fII\fP has been found whose second entry is not `..'. +.I Fsck +cannot resolve this problem. +The file system should be mounted and the second entry in the directory +moved elsewhere. +The file system should then be unmounted and +.I fsck +should be run again. +.sp +.LP +.B "EXTRA `..' ENTRY I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP DIR=\fIF\fP (FIX)" +.br +A directory \fII\fP has been found that has more than one entry for `..'. +.LP +Possible responses to the FIX prompt are: +.IP YES +remove the extra entry for `..'. +.IP NO +leave the directory unchanged. +.sp +.LP +.B "\fIN\fP IS AN EXTRANEOUS HARD LINK TO A DIRECTORY \fID\fP (REMOVE) +.br +.I Fsck +has found a hard link, \fIN\fP, to a directory, \fID\fP. +When preen'ing the extraneous links are ignored. +.LP +Possible responses to the REMOVE prompt are: +.IP YES +delete the extraneous entry, \fIN\fP. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD RETURN STATE \fIS\fP FROM DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be returned +from the routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.sp +.LP +.B "BAD STATE \fIS\fP FOR ROOT INODE" +.br +An internal error has caused an impossible state \fIS\fP to be assigned +to the root inode. +.I Fsck +exits. +See a guru. +.NH 2  +Phase 3 \- Check Connectivity +.PP +This phase concerns itself with the directory connectivity seen in +Phase 2. +This section lists error conditions resulting from +unreferenced directories, +and missing or full +.I lost+found +directories. +.sp +.LP +.B "UNREF DIR I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +The directory inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of directory inode \fII\fP are printed. +When preen'ing, the directory is reconnected if its size is non-zero, +otherwise it is cleared. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect directory inode \fII\fP to the file system in the +directory for lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 3 +if there are problems connecting directory inode \fII\fP to \fIlost+found\fP. +This may also invoke the CONNECTED error condition in Phase 3 if the link +was successful. +.IP NO +ignore this error condition. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the  +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the  +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "DIR I=\fII1\fP CONNECTED. PARENT WAS I=\fII2\fP" +.br +This is an advisory message indicating a directory inode \fII1\fP was +successfully connected to the +.I lost+found +directory. +The parent inode \fII2\fP of the directory inode \fII1\fP is +replaced by the inode number of the +.I lost+found +directory. +.sp +.LP +.B "DIRECTORY \fIF\fP LENGTH \fIS\fP NOT MULTIPLE OF \fIB\fP (ADJUST) +.br +A directory \fIF\fP has been found with size \fIS\fP that is not +a multiple of the directory blocksize \fIB\fP +(this can reoccur in Phase 3 if it is not adjusted in Phase 2). +.LP +Possible responses to the ADJUST prompt are: +.IP YES +the length is rounded up to the appropriate block size. +This error can occur on 4.2BSD file systems. +Thus when preen'ing the file system only a warning is printed +and the directory is adjusted. +.IP NO +ignore the error condition. +.sp +.LP +.B "BAD INODE \fIS\fP TO DESCEND" +.br +An internal error has caused an impossible state \fIS\fP to be passed to the +routine that descends the file system directory structure. +.I Fsck +exits. +See a guru. +.NH 2  +Phase 4 \- Check Reference Counts +.PP +This phase concerns itself with the link count information +seen in Phase 2 and Phase 3. +This section lists error conditions resulting from +unreferenced files, +missing or full +.I lost+found +directory, +incorrect link counts for files, directories, symbolic links, or special files, +unreferenced files, symbolic links, and directories, +and bad or duplicate blocks in files, symbolic links, and directories. +All errors in this phase are correctable if the file system is being preen'ed +except running out of space in the \fIlost+found\fP directory. +.sp +.LP +.B "UNREF FILE I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (RECONNECT)" +.br +Inode \fII\fP was not connected to a directory entry +when the file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and +modify time \fIT\fP of inode \fII\fP are printed. +When preen'ing the file is cleared if either its size or its +link count is zero, +otherwise it is reconnected. +.LP +Possible responses to the RECONNECT prompt are: +.IP YES +reconnect inode \fII\fP to the file system in the directory for +lost files (usually \fIlost+found\fP). +This may invoke the +.I lost+found +error condition in Phase 4 +if there are problems connecting inode \fII\fP to +.I lost+found . +.IP NO +ignore this error condition. +This will always invoke the CLEAR error condition in Phase 4. +.sp +.LP +.B "(CLEAR)" +.br +The inode mentioned in the immediately previous error condition can not be +reconnected. +This cannot occur if the file system is being preen'ed, +since lack of space to reconnect files is a fatal error. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate the inode mentioned in the immediately previous error condition by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "NO lost+found DIRECTORY (CREATE)" +.br +There is no +.I lost+found +directory in the root directory of the file system; +When preen'ing +.I fsck +tries to create a \fIlost+found\fP directory. +.LP +Possible responses to the CREATE prompt are: +.IP YES +create a \fIlost+found\fP directory in the root of the file system. +This may raise the message: +.br +.B "NO SPACE LEFT IN / (EXPAND)" +.br +See below for the possible responses. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "lost+found IS NOT A DIRECTORY (REALLOCATE)" +.br +The entry for +.I lost+found +is not a directory. +.LP +Possible responses to the REALLOCATE prompt are: +.IP YES +allocate a directory inode, and change \fIlost+found\fP to reference it. +The previous inode reference by the \fIlost+found\fP name is not cleared. +Thus it will either be reclaimed as an UNREF'ed inode or have its +link count ADJUST'ed later in this Phase. +Inability to create a \fIlost+found\fP directory generates the message: +.br +.B "SORRY. CANNOT CREATE lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "NO SPACE LEFT IN /lost+found (EXPAND)" +.br +There is no space to add another entry to the +.I lost+found +directory in the root directory +of the file system. +When preen'ing the  +.I lost+found +directory is expanded. +.LP +Possible responses to the EXPAND prompt are: +.IP YES +the  +.I lost+found +directory is expanded to make room for the new entry. +If the attempted expansion fails +.I fsck +prints the message: +.br +.B "SORRY. NO SPACE IN lost+found DIRECTORY" +.br +and aborts the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +Clean out unnecessary entries in +.I lost+found . +This error is fatal if the file system is being preen'ed. +.IP NO +abort the attempt to linkup the lost inode. +This will always invoke the UNREF error condition in Phase 4. +.sp +.LP +.B "LINK COUNT \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP COUNT=\fIX\fP SHOULD BE \fIY\fP (ADJUST)" +.br +The link count for inode \fII\fP, +is \fIX\fP but should be \fIY\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, and modify time \fIT\fP +are printed. +When preen'ing the link count is adjusted unless the number of references +is increasing, a condition that should never occur unless precipitated +by a hardware failure. +When the number of references is increasing under preen mode, +.I fsck +exits with the message: +.br +.B "LINK COUNT INCREASING" +.LP +Possible responses to the ADJUST prompt are: +.IP YES +replace the link count of file inode \fII\fP with \fIY\fP. +.IP NO +ignore this error condition. +.sp +.LP +.B "UNREF \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Inode \fII\fP, was not connected to a directory entry when the +file system was traversed. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +When preen'ing, +this is a file that was not connected because its size or link count was zero, +hence it is cleared. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.sp +.LP +.B "BAD/DUP \fItype\fP I=\fII\fP OWNER=\fIO\fP MODE=\fIM\fP SIZE=\fIS\fP MTIME=\fIT\fP (CLEAR)" +.br +Phase 1 or Phase 1b have found duplicate blocks +or bad blocks associated with +inode \fII\fP. +The owner \fIO\fP, mode \fIM\fP, size \fIS\fP, +and modify time \fIT\fP of inode \fII\fP +are printed. +This error cannot arise when the file system is being preen'ed, +as it would have caused a fatal error earlier. +.LP +Possible responses to the CLEAR prompt are: +.IP YES +de-allocate inode \fII\fP by zeroing its contents. +.IP NO +ignore this error condition. +.NH 2  +Phase 5 - Check Cyl groups +.PP +This phase concerns itself with the free-block and used-inode maps. +This section lists error conditions resulting from +allocated blocks in the free-block maps, +free blocks missing from free-block maps, +and the total free-block count incorrect. +It also lists error conditions resulting from +free inodes in the used-inode maps, +allocated inodes missing from used-inode maps, +and the total used-inode count incorrect. +.sp +.LP +.B "CG \fIC\fP: BAD MAGIC NUMBER" +.br +The magic number of cylinder group \fIC\fP is wrong. +This usually indicates that the cylinder group maps have been destroyed. +When running manually the cylinder group is marked as needing +to be reconstructed. +This error is fatal if the file system is being preen'ed. +.sp +.LP +.B "BLK(S) MISSING IN BIT MAPS (SALVAGE)" +.br +A cylinder group block map is missing some free blocks. +During preen'ing the maps are reconstructed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the free block map. +.IP NO +ignore this error condition. +.sp +.LP +.B "SUMMARY INFORMATION BAD (SALVAGE)" +.br +The summary information was found to be incorrect. +When preen'ing, +the summary information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the summary information. +.IP NO +ignore this error condition. +.sp +.LP +.B "FREE BLK COUNT(S) WRONG IN SUPERBLOCK (SALVAGE)" +.br +The superblock free block information was found to be incorrect. +When preen'ing, +the superblock free block information is recomputed. +.LP +Possible responses to the SALVAGE prompt are: +.IP YES +reconstruct the superblock free block information. +.IP NO +ignore this error condition. +.NH 2  +Cleanup +.PP +Once a file system has been checked, a few cleanup functions are performed. +This section lists advisory messages about +the file system +and modify status of the file system. +.sp +.LP +.B "\fIV\fP files, \fIW\fP used, \fIX\fP free (\fIY\fP frags, \fIZ\fP blocks)" +.br +This is an advisory message indicating that +the file system checked contained +\fIV\fP files using +\fIW\fP fragment sized blocks leaving +\fIX\fP fragment sized blocks free in the file system. +The numbers in parenthesis breaks the free count down into +\fIY\fP free fragments and +\fIZ\fP free full sized blocks. +.sp +.LP +.B "***** REBOOT UNIX *****" +.br +This is an advisory message indicating that +the root file system has been modified by +.I fsck. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. +When preen'ing, +.I fsck +will exit with a code of 4. +The standard auto-reboot script distributed with 4.3BSD  +interprets an exit code of 4 by issuing a reboot system call. +.sp +.LP +.B "***** FILE SYSTEM WAS MODIFIED *****" +.br +This is an advisory message indicating that +the current file system was modified by +.I fsck. +If this file system is mounted or is the current root file system, +.I fsck +should be halted and UNIX rebooted. +If UNIX is not rebooted immediately, +the work done by +.I fsck +may be undone by the in-core copies of tables +UNIX keeps. diff --git a/sbin/fsck/SMM.doc/Makefile b/sbin/fsck/SMM.doc/Makefile new file mode 100644 index 000000000000..26823bcc94f2 --- /dev/null +++ b/sbin/fsck/SMM.doc/Makefile @@ -0,0 +1,7 @@ +#	@(#)Makefile	8.1 (Berkeley) 6/8/93 + +DIR=	smm/03.fsck +SRCS=	0.t 1.t 2.t 3.t 4.t +MACROS=	-ms + +.include <bsd.doc.mk> diff --git a/sbin/fsck/dir.c b/sbin/fsck/dir.c new file mode 100644 index 000000000000..ee5d095e6c75 --- /dev/null +++ b/sbin/fsck/dir.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +char	*lfname = "lost+found"; +int	lfmode = 01777; +struct	dirtemplate emptydir = { 0, DIRBLKSIZ }; +struct	dirtemplate dirhead = { +	0, 12, DT_DIR, 1, ".", +	0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +struct	odirtemplate odirhead = { +	0, 12, 1, ".", +	0, DIRBLKSIZ - 12, 2, ".." +}; + +struct direct	*fsck_readdir(); +struct bufarea	*getdirblk(); + +/* + * Propagate connected state through the tree. + */ +propagate() +{ +	register struct inoinfo **inpp, *inp; +	struct inoinfo **inpend; +	long change; + +	inpend = &inpsort[inplast]; +	do { +		change = 0; +		for (inpp = inpsort; inpp < inpend; inpp++) { +			inp = *inpp; +			if (inp->i_parent == 0) +				continue; +			if (statemap[inp->i_parent] == DFOUND && +			    statemap[inp->i_number] == DSTATE) { +				statemap[inp->i_number] = DFOUND; +				change++; +			} +		} +	} while (change > 0); +} + +/* + * Scan each entry in a directory block. + */ +dirscan(idesc) +	register struct inodesc *idesc; +{ +	register struct direct *dp; +	register struct bufarea *bp; +	int dsize, n; +	long blksiz; +	char dbuf[DIRBLKSIZ]; + +	if (idesc->id_type != DATA) +		errexit("wrong type to dirscan %d\n", idesc->id_type); +	if (idesc->id_entryno == 0 && +	    (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0) +		idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ); +	blksiz = idesc->id_numfrags * sblock.fs_fsize; +	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { +		idesc->id_filesize -= blksiz; +		return (SKIP); +	} +	idesc->id_loc = 0; +	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { +		dsize = dp->d_reclen; +		bcopy((char *)dp, dbuf, (size_t)dsize); +#		if (BYTE_ORDER == LITTLE_ENDIAN) +			if (!newinofmt) { +				struct direct *tdp = (struct direct *)dbuf; +				u_char tmp; + +				tmp = tdp->d_namlen; +				tdp->d_namlen = tdp->d_type; +				tdp->d_type = tmp; +			} +#		endif +		idesc->id_dirp = (struct direct *)dbuf; +		if ((n = (*idesc->id_func)(idesc)) & ALTERED) { +#			if (BYTE_ORDER == LITTLE_ENDIAN) +				if (!newinofmt && !doinglevel2) { +					struct direct *tdp; +					u_char tmp; + +					tdp = (struct direct *)dbuf; +					tmp = tdp->d_namlen; +					tdp->d_namlen = tdp->d_type; +					tdp->d_type = tmp; +				} +#			endif +			bp = getdirblk(idesc->id_blkno, blksiz); +			bcopy(dbuf, bp->b_un.b_buf + idesc->id_loc - dsize, +			    (size_t)dsize); +			dirty(bp); +			sbdirty(); +		} +		if (n & STOP)  +			return (n); +	} +	return (idesc->id_filesize > 0 ? KEEPON : STOP); +} + +/* + * get next entry in a directory. + */ +struct direct * +fsck_readdir(idesc) +	register struct inodesc *idesc; +{ +	register struct direct *dp, *ndp; +	register struct bufarea *bp; +	long size, blksiz, fix, dploc; + +	blksiz = idesc->id_numfrags * sblock.fs_fsize; +	bp = getdirblk(idesc->id_blkno, blksiz); +	if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0 && +	    idesc->id_loc < blksiz) { +		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); +		if (dircheck(idesc, dp)) +			goto dpok; +		fix = dofix(idesc, "DIRECTORY CORRUPTED"); +		bp = getdirblk(idesc->id_blkno, blksiz); +		dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); +		dp->d_reclen = DIRBLKSIZ; +		dp->d_ino = 0; +		dp->d_type = 0; +		dp->d_namlen = 0; +		dp->d_name[0] = '\0'; +		if (fix) +			dirty(bp); +		idesc->id_loc += DIRBLKSIZ; +		idesc->id_filesize -= DIRBLKSIZ; +		return (dp); +	} +dpok: +	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) +		return NULL; +	dploc = idesc->id_loc; +	dp = (struct direct *)(bp->b_un.b_buf + dploc); +	idesc->id_loc += dp->d_reclen; +	idesc->id_filesize -= dp->d_reclen; +	if ((idesc->id_loc % DIRBLKSIZ) == 0) +		return (dp); +	ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); +	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && +	    dircheck(idesc, ndp) == 0) { +		size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +		idesc->id_loc += size; +		idesc->id_filesize -= size; +		fix = dofix(idesc, "DIRECTORY CORRUPTED"); +		bp = getdirblk(idesc->id_blkno, blksiz); +		dp = (struct direct *)(bp->b_un.b_buf + dploc); +		dp->d_reclen += size; +		if (fix) +			dirty(bp); +	} +	return (dp); +} + +/* + * Verify that a directory entry is valid. + * This is a superset of the checks made in the kernel. + */ +dircheck(idesc, dp) +	struct inodesc *idesc; +	register struct direct *dp; +{ +	register int size; +	register char *cp; +	u_char namlen, type; +	int spaceleft; + +	size = DIRSIZ(!newinofmt, dp); +	spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ); +#	if (BYTE_ORDER == LITTLE_ENDIAN) +		if (!newinofmt) { +			type = dp->d_namlen; +			namlen = dp->d_type; +		} else { +			namlen = dp->d_namlen; +			type = dp->d_type; +		} +#	else +		namlen = dp->d_namlen; +		type = dp->d_type; +#	endif +	if (dp->d_ino < maxino && +	    dp->d_reclen != 0 && +	    dp->d_reclen <= spaceleft && +	    (dp->d_reclen & 0x3) == 0 && +	    dp->d_reclen >= size && +	    idesc->id_filesize >= size && +	    namlen <= MAXNAMLEN && +	    type <= 15) { +		if (dp->d_ino == 0) +			return (1); +		for (cp = dp->d_name, size = 0; size < namlen; size++) +			if (*cp == 0 || (*cp++ == '/')) +				return (0); +		if (*cp == 0) +			return (1); +	} +	return (0); +} + +direrror(ino, errmesg) +	ino_t ino; +	char *errmesg; +{ + +	fileerror(ino, ino, errmesg); +} + +fileerror(cwd, ino, errmesg) +	ino_t cwd, ino; +	char *errmesg; +{ +	register struct dinode *dp; +	char pathbuf[MAXPATHLEN + 1]; + +	pwarn("%s ", errmesg); +	pinode(ino); +	printf("\n"); +	getpathname(pathbuf, cwd, ino); +	if (ino < ROOTINO || ino > maxino) { +		pfatal("NAME=%s\n", pathbuf); +		return; +	} +	dp = ginode(ino); +	if (ftypeok(dp)) +		pfatal("%s=%s\n", +		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); +	else +		pfatal("NAME=%s\n", pathbuf); +} + +adjust(idesc, lcnt) +	register struct inodesc *idesc; +	short lcnt; +{ +	register struct dinode *dp; + +	dp = ginode(idesc->id_number); +	if (dp->di_nlink == lcnt) { +		if (linkup(idesc->id_number, (ino_t)0) == 0) +			clri(idesc, "UNREF", 0); +	} else { +		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : +			((dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE")); +		pinode(idesc->id_number); +		printf(" COUNT %d SHOULD BE %d", +			dp->di_nlink, dp->di_nlink - lcnt); +		if (preen) { +			if (lcnt < 0) { +				printf("\n"); +				pfatal("LINK COUNT INCREASING"); +			} +			printf(" (ADJUSTED)\n"); +		} +		if (preen || reply("ADJUST") == 1) { +			dp->di_nlink -= lcnt; +			inodirty(); +		} +	} +} + +mkentry(idesc) +	struct inodesc *idesc; +{ +	register struct direct *dirp = idesc->id_dirp; +	struct direct newent; +	int newlen, oldlen; + +	newent.d_namlen = strlen(idesc->id_name); +	newlen = DIRSIZ(0, &newent); +	if (dirp->d_ino != 0) +		oldlen = DIRSIZ(0, dirp); +	else +		oldlen = 0; +	if (dirp->d_reclen - oldlen < newlen) +		return (KEEPON); +	newent.d_reclen = dirp->d_reclen - oldlen; +	dirp->d_reclen = oldlen; +	dirp = (struct direct *)(((char *)dirp) + oldlen); +	dirp->d_ino = idesc->id_parent;	/* ino to be entered is in id_parent */ +	if (newinofmt) +		dirp->d_type = typemap[idesc->id_parent]; +	else +		dirp->d_type = 0; +	dirp->d_reclen = newent.d_reclen; +	dirp->d_namlen = newent.d_namlen; +	bcopy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); +	return (ALTERED|STOP); +} + +chgino(idesc) +	struct inodesc *idesc; +{ +	register struct direct *dirp = idesc->id_dirp; + +	if (bcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1)) +		return (KEEPON); +	dirp->d_ino = idesc->id_parent; +	if (newinofmt) +		dirp->d_type = typemap[idesc->id_parent]; +	else +		dirp->d_type = 0; +	return (ALTERED|STOP); +} + +linkup(orphan, parentdir) +	ino_t orphan; +	ino_t parentdir; +{ +	register struct dinode *dp; +	int lostdir; +	ino_t oldlfdir; +	struct inodesc idesc; +	char tempname[BUFSIZ]; +	extern int pass4check(); + +	bzero((char *)&idesc, sizeof(struct inodesc)); +	dp = ginode(orphan); +	lostdir = (dp->di_mode & IFMT) == IFDIR; +	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); +	pinode(orphan); +	if (preen && dp->di_size == 0) +		return (0); +	if (preen) +		printf(" (RECONNECTED)\n"); +	else +		if (reply("RECONNECT") == 0) +			return (0); +	if (lfdir == 0) { +		dp = ginode(ROOTINO); +		idesc.id_name = lfname; +		idesc.id_type = DATA; +		idesc.id_func = findino; +		idesc.id_number = ROOTINO; +		if ((ckinode(dp, &idesc) & FOUND) != 0) { +			lfdir = idesc.id_parent; +		} else { +			pwarn("NO lost+found DIRECTORY"); +			if (preen || reply("CREATE")) { +				lfdir = allocdir(ROOTINO, (ino_t)0, lfmode); +				if (lfdir != 0) { +					if (makeentry(ROOTINO, lfdir, lfname) != 0) { +						if (preen) +							printf(" (CREATED)\n"); +					} else { +						freedir(lfdir, ROOTINO); +						lfdir = 0; +						if (preen) +							printf("\n"); +					} +				} +			} +		} +		if (lfdir == 0) { +			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); +			printf("\n\n"); +			return (0); +		} +	} +	dp = ginode(lfdir); +	if ((dp->di_mode & IFMT) != IFDIR) { +		pfatal("lost+found IS NOT A DIRECTORY"); +		if (reply("REALLOCATE") == 0) +			return (0); +		oldlfdir = lfdir; +		if ((lfdir = allocdir(ROOTINO, (ino_t)0, lfmode)) == 0) { +			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); +			return (0); +		} +		if ((changeino(ROOTINO, lfname, lfdir) & ALTERED) == 0) { +			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); +			return (0); +		} +		inodirty(); +		idesc.id_type = ADDR; +		idesc.id_func = pass4check; +		idesc.id_number = oldlfdir; +		adjust(&idesc, lncntp[oldlfdir] + 1); +		lncntp[oldlfdir] = 0; +		dp = ginode(lfdir); +	} +	if (statemap[lfdir] != DFOUND) { +		pfatal("SORRY. NO lost+found DIRECTORY\n\n"); +		return (0); +	} +	(void)lftempname(tempname, orphan); +	if (makeentry(lfdir, orphan, tempname) == 0) { +		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); +		printf("\n\n"); +		return (0); +	} +	lncntp[orphan]--; +	if (lostdir) { +		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && +		    parentdir != (ino_t)-1) +			(void)makeentry(orphan, lfdir, ".."); +		dp = ginode(lfdir); +		dp->di_nlink++; +		inodirty(); +		lncntp[lfdir]++; +		pwarn("DIR I=%lu CONNECTED. ", orphan); +		if (parentdir != (ino_t)-1) +			printf("PARENT WAS I=%lu\n", parentdir); +		if (preen == 0) +			printf("\n"); +	} +	return (1); +} + +/* + * fix an entry in a directory. + */ +changeino(dir, name, newnum) +	ino_t dir; +	char *name; +	ino_t newnum; +{ +	struct inodesc idesc; + +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = DATA; +	idesc.id_func = chgino; +	idesc.id_number = dir; +	idesc.id_fix = DONTKNOW; +	idesc.id_name = name; +	idesc.id_parent = newnum;	/* new value for name */ +	return (ckinode(ginode(dir), &idesc)); +} + +/* + * make an entry in a directory + */ +makeentry(parent, ino, name) +	ino_t parent, ino; +	char *name; +{ +	struct dinode *dp; +	struct inodesc idesc; +	char pathbuf[MAXPATHLEN + 1]; +	 +	if (parent < ROOTINO || parent >= maxino || +	    ino < ROOTINO || ino >= maxino) +		return (0); +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = DATA; +	idesc.id_func = mkentry; +	idesc.id_number = parent; +	idesc.id_parent = ino;	/* this is the inode to enter */ +	idesc.id_fix = DONTKNOW; +	idesc.id_name = name; +	dp = ginode(parent); +	if (dp->di_size % DIRBLKSIZ) { +		dp->di_size = roundup(dp->di_size, DIRBLKSIZ); +		inodirty(); +	} +	if ((ckinode(dp, &idesc) & ALTERED) != 0) +		return (1); +	getpathname(pathbuf, parent, parent); +	dp = ginode(parent); +	if (expanddir(dp, pathbuf) == 0) +		return (0); +	return (ckinode(dp, &idesc) & ALTERED); +} + +/* + * Attempt to expand the size of a directory + */ +expanddir(dp, name) +	register struct dinode *dp; +	char *name; +{ +	daddr_t lastbn, newblk; +	register struct bufarea *bp; +	char *cp, firstblk[DIRBLKSIZ]; + +	lastbn = lblkno(&sblock, dp->di_size); +	if (lastbn >= NDADDR - 1 || dp->di_db[lastbn] == 0 || dp->di_size == 0) +		return (0); +	if ((newblk = allocblk(sblock.fs_frag)) == 0) +		return (0); +	dp->di_db[lastbn + 1] = dp->di_db[lastbn]; +	dp->di_db[lastbn] = newblk; +	dp->di_size += sblock.fs_bsize; +	dp->di_blocks += btodb(sblock.fs_bsize); +	bp = getdirblk(dp->di_db[lastbn + 1], +		(long)dblksize(&sblock, dp, lastbn + 1)); +	if (bp->b_errs) +		goto bad; +	bcopy(bp->b_un.b_buf, firstblk, DIRBLKSIZ); +	bp = getdirblk(newblk, sblock.fs_bsize); +	if (bp->b_errs) +		goto bad; +	bcopy(firstblk, bp->b_un.b_buf, DIRBLKSIZ); +	for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; +	     cp < &bp->b_un.b_buf[sblock.fs_bsize]; +	     cp += DIRBLKSIZ) +		bcopy((char *)&emptydir, cp, sizeof emptydir); +	dirty(bp); +	bp = getdirblk(dp->di_db[lastbn + 1], +		(long)dblksize(&sblock, dp, lastbn + 1)); +	if (bp->b_errs) +		goto bad; +	bcopy((char *)&emptydir, bp->b_un.b_buf, sizeof emptydir); +	pwarn("NO SPACE LEFT IN %s", name); +	if (preen) +		printf(" (EXPANDED)\n"); +	else if (reply("EXPAND") == 0) +		goto bad; +	dirty(bp); +	inodirty(); +	return (1); +bad: +	dp->di_db[lastbn] = dp->di_db[lastbn + 1]; +	dp->di_db[lastbn + 1] = 0; +	dp->di_size -= sblock.fs_bsize; +	dp->di_blocks -= btodb(sblock.fs_bsize); +	freeblk(newblk, sblock.fs_frag); +	return (0); +} + +/* + * allocate a new directory + */ +allocdir(parent, request, mode) +	ino_t parent, request; +	int mode; +{ +	ino_t ino; +	char *cp; +	struct dinode *dp; +	register struct bufarea *bp; +	struct dirtemplate *dirp; + +	ino = allocino(request, IFDIR|mode); +	if (newinofmt) +		dirp = &dirhead; +	else +		dirp = (struct dirtemplate *)&odirhead; +	dirp->dot_ino = ino; +	dirp->dotdot_ino = parent; +	dp = ginode(ino); +	bp = getdirblk(dp->di_db[0], sblock.fs_fsize); +	if (bp->b_errs) { +		freeino(ino); +		return (0); +	} +	bcopy((char *)dirp, bp->b_un.b_buf, sizeof(struct dirtemplate)); +	for (cp = &bp->b_un.b_buf[DIRBLKSIZ]; +	     cp < &bp->b_un.b_buf[sblock.fs_fsize]; +	     cp += DIRBLKSIZ) +		bcopy((char *)&emptydir, cp, sizeof emptydir); +	dirty(bp); +	dp->di_nlink = 2; +	inodirty(); +	if (ino == ROOTINO) { +		lncntp[ino] = dp->di_nlink; +		cacheino(dp, ino); +		return(ino); +	} +	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { +		freeino(ino); +		return (0); +	} +	cacheino(dp, ino); +	statemap[ino] = statemap[parent]; +	if (statemap[ino] == DSTATE) { +		lncntp[ino] = dp->di_nlink; +		lncntp[parent]++; +	} +	dp = ginode(parent); +	dp->di_nlink++; +	inodirty(); +	return (ino); +} + +/* + * free a directory inode + */ +freedir(ino, parent) +	ino_t ino, parent; +{ +	struct dinode *dp; + +	if (ino != parent) { +		dp = ginode(parent); +		dp->di_nlink--; +		inodirty(); +	} +	freeino(ino); +} + +/* + * generate a temporary name for the lost+found directory. + */ +lftempname(bufp, ino) +	char *bufp; +	ino_t ino; +{ +	register ino_t in; +	register char *cp; +	int namlen; + +	cp = bufp + 2; +	for (in = maxino; in > 0; in /= 10) +		cp++; +	*--cp = 0; +	namlen = cp - bufp; +	in = ino; +	while (cp > bufp) { +		*--cp = (in % 10) + '0'; +		in /= 10; +	} +	*cp = '#'; +	return (namlen); +} + +/* + * Get a directory block. + * Insure that it is held until another is requested. + */ +struct bufarea * +getdirblk(blkno, size) +	daddr_t blkno; +	long size; +{ + +	if (pdirbp != 0) +		pdirbp->b_flags &= ~B_INUSE; +	pdirbp = getdatablk(blkno, size); +	return (pdirbp); +} diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8 new file mode 100644 index 000000000000..cf8c499a35e9 --- /dev/null +++ b/sbin/fsck/fsck.8 @@ -0,0 +1,291 @@ +.\" Copyright (c) 1980, 1989, 1991, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"	@(#)fsck.8	8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt FSCK 8 +.Os BSD 4 +.Sh NAME +.Nm fsck +.Nd filesystem consistency check and interactive repair +.Sh SYNOPSIS +.Nm fsck +.Fl p +.Op Fl m Ar mode +.Nm fsck +.Op Fl b Ar block# +.Op Fl c Ar level +.Op Fl l Ar maxparallel +.Op Fl y +.Op Fl n +.Op Fl m Ar mode +.Op Ar filesystem +.Ar ... +.Sh DESCRIPTION +The first form of +.Nm fsck +preens a standard set of filesystems or the specified filesystems. +It is normally used in the script +.Pa /etc/rc +during automatic reboot. +Here +.Nm fsck +reads the table +.Pa /etc/fstab +to determine which filesystems to check. +Only partitions in fstab that are mounted ``rw,'' ``rq'' or ``ro'' +and that have non-zero pass number are checked. +Filesystems with pass number 1 (normally just the root filesystem) +are checked one at a time. +When pass 1 completes, all remaining filesystems are checked, +running one process per disk drive. +The disk drive containing each filesystem is inferred from the longest prefix +of the device name that ends in a digit; the remaining characters are assumed +to be the partition designator. +.Pp +The kernel takes care that only a restricted class of innocuous filesystem +inconsistencies can happen unless hardware or software failures intervene. +These are limited to the following: +.Bl -item -compact +.It +Unreferenced inodes +.It +Link counts in inodes too large +.It +Missing blocks in the free map +.It +Blocks in the free map also in files +.It +Counts in the super-block wrong +.El +.Pp +These are the only inconsistencies that +.Nm fsck +with the +.Fl p +option will correct; if it encounters other inconsistencies, it exits +with an abnormal return status and an automatic reboot will then fail. +For each corrected inconsistency one or more lines will be printed +identifying the filesystem on which the correction will take place, +and the nature of the correction.  After successfully correcting a filesystem, +.Nm fsck +will print the number of files on that filesystem, +the number of used and free blocks, +and the percentage of fragmentation. +.Pp +If sent a +.Dv QUIT +signal, +.Nm fsck +will finish the filesystem checks, then exit with an abnormal +return status that causes an automatic reboot to fail. +This is useful when you want to finish the filesystem checks during an +automatic reboot, +but do not want the machine to come up multiuser after the checks complete. +.Pp +Without the +.Fl p +option, +.Nm fsck +audits and interactively repairs inconsistent conditions for filesystems.  +If the filesystem is inconsistent the operator is prompted for concurrence +before each correction is attempted. +It should be noted that some of the corrective actions which are not +correctable under the +.Fl p +option will result in some loss of data. +The amount and severity of data lost may be determined from the diagnostic +output. +The default action for each consistency correction +is to wait for the operator to respond +.Li yes +or +.Li no . +If the operator does not have write permission on the filesystem +.Nm fsck +will default to a  +.Fl n +action. +.Pp +.Nm Fsck +has more consistency checks than +its predecessors +.Em check , dcheck , fcheck , +and +.Em icheck +combined. +.Pp +The following flags are interpreted by +.Nm fsck . +.Bl -tag -width indent +.It Fl b +Use the block specified immediately after the flag as +the super block for the filesystem.  Block 32 is usually +an alternate super block. +.It Fl l +Limit the number of parallel checks to the number specified in the following +argument. +By default, the limit is the number of disks, running one process per disk. +If a smaller limit is given, the disks are checked round-robin, one filesystem +at a time. +.It Fl m +Use the mode specified in octal immediately after the flag as the +permission bits to use when creating the +.Pa lost+found +directory rather than the default 1777. +In particular, systems that do not wish to have lost files accessible +by all users on the system should use a more restrictive +set of permissions such as 700. +.It Fl y +Assume a yes response to all questions asked by  +.Nm fsck ; +this should be used with great caution as this is a free license +to continue after essentially unlimited trouble has been encountered. +.It Fl n +Assume a no response to all questions asked by  +.Nm fsck +except for +.Ql CONTINUE? , +which is assumed to be affirmative; +do not open the filesystem for writing. +.It Fl c +Convert the filesystem to the specified level. +Note that the level of a filesystem can only be raised. +.Bl -tag -width indent +There are currently three levels defined: +.It 0 +The filesystem is in the old (static table) format. +.It 1 +The filesystem is in the new (dynamic table) format. +.It 2 +The filesystem supports 32-bit uid's and gid's, +short symbolic links are stored in the inode,  +and directories have an added field showing the file type. +.El +.Pp +In interactive mode, +.Nm fsck +will list the conversion to be made +and ask whether the conversion should be done. +If a negative answer is given, +no further operations are done on the filesystem. +In preen mode, +the conversion is listed and done if +possible without user interaction. +Conversion in preen mode is best used when all the filesystems +are being converted at once. +The format of a filesystem can be determined from the +first line of output from  +.Xr dumpfs 8 . +.El +.Pp +If no filesystems are given to  +.Nm fsck +then a default list of filesystems is read from +the file +.Pa /etc/fstab . +.Pp +.Bl -enum -indent indent -compact +Inconsistencies checked are as follows: +.It +Blocks claimed by more than one inode or the free map. +.It +Blocks claimed by an inode outside the range of the filesystem. +.It +Incorrect link counts. +.It +Size checks: +.Bl -item -indent indent -compact +.It  +Directory size not a multiple of DIRBLKSIZ. +.It +Partially truncated file. +.El +.It +Bad inode format. +.It +Blocks not accounted for anywhere. +.It +Directory checks: +.Bl -item -indent indent -compact +.It  +File pointing to unallocated inode. +.It +Inode number out of range. +.It +Dot or dot-dot not the first two entries of a directory +or having the wrong inode number. +.El +.It +Super Block checks: +.Bl -item -indent indent -compact +.It  +More blocks for inodes than there are in the filesystem. +.It +Bad free block map format. +.It +Total free block and/or free inode count incorrect. +.El +.El +.Pp +Orphaned files and directories (allocated but unreferenced) are, +with the operator's concurrence, reconnected by +placing them in the  +.Pa lost+found +directory. +The name assigned is the inode number. +If the +.Pa lost+found +directory does not exist, it is created. +If there is insufficient space its size is increased. +.Pp +Because of inconsistencies between the block device and the buffer cache, +the raw device should always be used. +.Sh FILES +.Bl -tag -width /etc/fstab -compact +.It Pa /etc/fstab +contains default list of filesystems to check. +.El +.Sh DIAGNOSTICS +The diagnostics produced by  +.Nm fsck +are fully enumerated and explained in Appendix A of +.Rs +.%T "Fsck \- The UNIX File System Check Program" +.Re +.Sh SEE ALSO +.Xr fstab 5 , +.Xr fs 5 , +.Xr fsdb 8 , +.Xr newfs 8 , +.Xr mkfs 8 , +.Xr reboot 8 diff --git a/sbin/fsck/fsck.h b/sbin/fsck/fsck.h new file mode 100644 index 000000000000..7fa831f6c52e --- /dev/null +++ b/sbin/fsck/fsck.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + *	@(#)fsck.h	8.1 (Berkeley) 6/5/93 + */ + +#define	MAXDUP		10	/* limit on dup blks (per inode) */ +#define	MAXBAD		10	/* limit on bad blks (per inode) */ +#define	MAXBUFSPACE	40*1024	/* maximum space to allocate to buffers */ +#define	INOBUFSIZE	56*1024	/* size of buffer to read inodes in pass1 */ + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + +#define	USTATE	01		/* inode not allocated */ +#define	FSTATE	02		/* inode is file */ +#define	DSTATE	03		/* inode is directory */ +#define	DFOUND	04		/* directory found during descent */ +#define	DCLEAR	05		/* directory is to be cleared */ +#define	FCLEAR	06		/* file is to be cleared */ + +/* + * buffer cache structure. + */ +struct bufarea { +	struct bufarea	*b_next;		/* free list queue */ +	struct bufarea	*b_prev;		/* free list queue */ +	daddr_t	b_bno; +	int	b_size; +	int	b_errs; +	int	b_flags; +	union { +		char	*b_buf;			/* buffer space */ +		daddr_t	*b_indir;		/* indirect block */ +		struct	fs *b_fs;		/* super block */ +		struct	cg *b_cg;		/* cylinder group */ +		struct	dinode *b_dinode;	/* inode block */ +	} b_un; +	char	b_dirty; +}; + +#define	B_INUSE 1 + +#define	MINBUFS		5	/* minimum number of buffers required */ +struct bufarea bufhead;		/* head of list of other blks in filesys */ +struct bufarea sblk;		/* file system superblock */ +struct bufarea cgblk;		/* cylinder group blocks */ +struct bufarea *pdirbp;		/* current directory contents */ +struct bufarea *pbp;		/* current inode block */ +struct bufarea *getdatablk(); + +#define	dirty(bp)	(bp)->b_dirty = 1 +#define	initbarea(bp) \ +	(bp)->b_dirty = 0; \ +	(bp)->b_bno = (daddr_t)-1; \ +	(bp)->b_flags = 0; + +#define	sbdirty()	sblk.b_dirty = 1 +#define	cgdirty()	cgblk.b_dirty = 1 +#define	sblock		(*sblk.b_un.b_fs) +#define	cgrp		(*cgblk.b_un.b_cg) + +enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; + +struct inodesc { +	enum fixstate id_fix;	/* policy on fixing errors */ +	int (*id_func)();	/* function to be applied to blocks of inode */ +	ino_t id_number;	/* inode number described */ +	ino_t id_parent;	/* for DATA nodes, their parent */ +	daddr_t id_blkno;	/* current block number being examined */ +	int id_numfrags;	/* number of frags contained in block */ +	quad_t id_filesize;	/* for DATA nodes, the size of the directory */ +	int id_loc;		/* for DATA nodes, current location in dir */ +	int id_entryno;		/* for DATA nodes, current entry number */ +	struct direct *id_dirp;	/* for DATA nodes, ptr to current entry */ +	char *id_name;		/* for DATA nodes, name to find or enter */ +	char id_type;		/* type of descriptor, DATA or ADDR */ +}; +/* file types */ +#define	DATA	1 +#define	ADDR	2 + +/* + * Linked list of duplicate blocks. + *  + * The list is composed of two parts. The first part of the + * list (from duplist through the node pointed to by muldup) + * contains a single copy of each duplicate block that has been  + * found. The second part of the list (from muldup to the end) + * contains duplicate blocks that have been found more than once. + * To check if a block has been found as a duplicate it is only + * necessary to search from duplist through muldup. To find the  + * total number of times that a block has been found as a duplicate + * the entire list must be searched for occurences of the block + * in question. The following diagram shows a sample list where + * w (found twice), x (found once), y (found three times), and z + * (found once) are duplicate block numbers: + * + *    w -> y -> x -> z -> y -> w -> y + *    ^		     ^ + *    |		     | + * duplist	  muldup + */ +struct dups { +	struct dups *next; +	daddr_t dup; +}; +struct dups *duplist;		/* head of dup list */ +struct dups *muldup;		/* end of unique duplicate dup block numbers */ + +/* + * Linked list of inodes with zero link counts. + */ +struct zlncnt { +	struct zlncnt *next; +	ino_t zlncnt; +}; +struct zlncnt *zlnhead;		/* head of zero link count list */ + +/* + * Inode cache data structures. + */ +struct inoinfo { +	struct	inoinfo *i_nexthash;	/* next entry in hash chain */ +	ino_t	i_number;		/* inode number of this entry */ +	ino_t	i_parent;		/* inode number of parent */ +	ino_t	i_dotdot;		/* inode number of `..' */ +	size_t	i_isize;		/* size of inode */ +	u_int	i_numblks;		/* size of block array in bytes */ +	daddr_t	i_blks[1];		/* actually longer */ +} **inphead, **inpsort; +long numdirs, listmax, inplast; + +char	*cdevname;		/* name of device being checked */ +long	dev_bsize;		/* computed value of DEV_BSIZE */ +long	secsize;		/* actual disk sector size */ +char	nflag;			/* assume a no response */ +char	yflag;			/* assume a yes response */ +int	bflag;			/* location of alternate super block */ +int	debug;			/* output debugging info */ +int	cvtlevel;		/* convert to newer file system format */ +int	doinglevel1;		/* converting to new cylinder group format */ +int	doinglevel2;		/* converting to new inode format */ +int	newinofmt;		/* filesystem has new inode format */ +char	preen;			/* just fix normal inconsistencies */ +char	hotroot;		/* checking root device */ +char	havesb;			/* superblock has been read */ +int	fsmodified;		/* 1 => write done to file system */ +int	fsreadfd;		/* file descriptor for reading file system */ +int	fswritefd;		/* file descriptor for writing file system */ + +daddr_t	maxfsblock;		/* number of blocks in the file system */ +char	*blockmap;		/* ptr to primary blk allocation map */ +ino_t	maxino;			/* number of inodes in file system */ +ino_t	lastino;		/* last inode in use */ +char	*statemap;		/* ptr to inode state table */ +char	*typemap;		/* ptr to inode type table */ +short	*lncntp;		/* ptr to link count table */ + +ino_t	lfdir;			/* lost & found directory inode number */ +char	*lfname;		/* lost & found directory name */ +int	lfmode;			/* lost & found directory creation mode */ + +daddr_t	n_blks;			/* number of blocks in use */ +daddr_t	n_files;		/* number of files in use */ + +#define	clearinode(dp)	(*(dp) = zino) +struct	dinode zino; + +#define	setbmap(blkno)	setbit(blockmap, blkno) +#define	testbmap(blkno)	isset(blockmap, blkno) +#define	clrbmap(blkno)	clrbit(blockmap, blkno) + +#define	STOP	0x01 +#define	SKIP	0x02 +#define	KEEPON	0x04 +#define	ALTERED	0x08 +#define	FOUND	0x10 + +time_t time(); +struct dinode *ginode(); +struct inoinfo *getinoinfo(); +void getblk(); +ino_t allocino(); +int findino(); diff --git a/sbin/fsck/inode.c b/sbin/fsck/inode.c new file mode 100644 index 000000000000..f1c1758f746a --- /dev/null +++ b/sbin/fsck/inode.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)inode.c	8.4 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +static ino_t startinum; + +ckinode(dp, idesc) +	struct dinode *dp; +	register struct inodesc *idesc; +{ +	register daddr_t *ap; +	long ret, n, ndb, offset; +	struct dinode dino; +	quad_t remsize, sizepb; +	mode_t mode; + +	if (idesc->id_fix != IGNORE) +		idesc->id_fix = DONTKNOW; +	idesc->id_entryno = 0; +	idesc->id_filesize = dp->di_size; +	mode = dp->di_mode & IFMT; +	if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && +	    dp->di_size < sblock.fs_maxsymlinklen)) +		return (KEEPON); +	dino = *dp; +	ndb = howmany(dino.di_size, sblock.fs_bsize); +	for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) { +		if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0) +			idesc->id_numfrags = +				numfrags(&sblock, fragroundup(&sblock, offset)); +		else +			idesc->id_numfrags = sblock.fs_frag; +		if (*ap == 0) +			continue; +		idesc->id_blkno = *ap; +		if (idesc->id_type == ADDR) +			ret = (*idesc->id_func)(idesc); +		else +			ret = dirscan(idesc); +		if (ret & STOP) +			return (ret); +	} +	idesc->id_numfrags = sblock.fs_frag; +	remsize = dino.di_size - sblock.fs_bsize * NDADDR; +	sizepb = sblock.fs_bsize; +	for (ap = &dino.di_ib[0], n = 1; n <= NIADDR; ap++, n++) { +		if (*ap) { +			idesc->id_blkno = *ap; +			ret = iblock(idesc, n, remsize); +			if (ret & STOP) +				return (ret); +		} +		sizepb *= NINDIR(&sblock); +		remsize -= sizepb; +	} +	return (KEEPON); +} + +iblock(idesc, ilevel, isize) +	struct inodesc *idesc; +	long ilevel; +	quad_t isize; +{ +	register daddr_t *ap; +	register daddr_t *aplim; +	register struct bufarea *bp; +	int i, n, (*func)(), nif; +	quad_t sizepb; +	char buf[BUFSIZ]; +	extern int dirscan(), pass1check(); + +	if (idesc->id_type == ADDR) { +		func = idesc->id_func; +		if (((n = (*func)(idesc)) & KEEPON) == 0) +			return (n); +	} else +		func = dirscan; +	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) +		return (SKIP); +	bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); +	ilevel--; +	for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) +		sizepb *= NINDIR(&sblock); +	nif = howmany(isize , sizepb); +	if (nif > NINDIR(&sblock)) +		nif = NINDIR(&sblock); +	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { +		aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; +		for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { +			if (*ap == 0) +				continue; +			(void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", +				idesc->id_number); +			if (dofix(idesc, buf)) { +				*ap = 0; +				dirty(bp); +			} +		} +		flush(fswritefd, bp); +	} +	aplim = &bp->b_un.b_indir[nif]; +	for (ap = bp->b_un.b_indir; ap < aplim; ap++) { +		if (*ap) { +			idesc->id_blkno = *ap; +			if (ilevel == 0) +				n = (*func)(idesc); +			else +				n = iblock(idesc, ilevel, isize); +			if (n & STOP) { +				bp->b_flags &= ~B_INUSE; +				return (n); +			} +		} +		isize -= sizepb; +	} +	bp->b_flags &= ~B_INUSE; +	return (KEEPON); +} + +/* + * Check that a block in a legal block number. + * Return 0 if in range, 1 if out of range. + */ +chkrange(blk, cnt) +	daddr_t blk; +	int cnt; +{ +	register int c; + +	if ((unsigned)(blk + cnt) > maxfsblock) +		return (1); +	c = dtog(&sblock, blk); +	if (blk < cgdmin(&sblock, c)) { +		if ((blk + cnt) > cgsblock(&sblock, c)) { +			if (debug) { +				printf("blk %ld < cgdmin %ld;", +				    blk, cgdmin(&sblock, c)); +				printf(" blk + cnt %ld > cgsbase %ld\n", +				    blk + cnt, cgsblock(&sblock, c)); +			} +			return (1); +		} +	} else { +		if ((blk + cnt) > cgbase(&sblock, c+1)) { +			if (debug)  { +				printf("blk %ld >= cgdmin %ld;", +				    blk, cgdmin(&sblock, c)); +				printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", +				    blk+cnt, sblock.fs_fpg); +			} +			return (1); +		} +	} +	return (0); +} + +/* + * General purpose interface for reading inodes. + */ +struct dinode * +ginode(inumber) +	ino_t inumber; +{ +	daddr_t iblk; + +	if (inumber < ROOTINO || inumber > maxino) +		errexit("bad inode number %d to ginode\n", inumber); +	if (startinum == 0 || +	    inumber < startinum || inumber >= startinum + INOPB(&sblock)) { +		iblk = ino_to_fsba(&sblock, inumber); +		if (pbp != 0) +			pbp->b_flags &= ~B_INUSE; +		pbp = getdatablk(iblk, sblock.fs_bsize); +		startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); +	} +	return (&pbp->b_un.b_dinode[inumber % INOPB(&sblock)]); +} + +/* + * Special purpose version of ginode used to optimize first pass + * over all the inodes in numerical order. + */ +ino_t nextino, lastinum; +long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; +struct dinode *inodebuf; + +struct dinode * +getnextinode(inumber) +	ino_t inumber; +{ +	long size; +	daddr_t dblk; +	static struct dinode *dp; + +	if (inumber != nextino++ || inumber > maxino) +		errexit("bad inode number %d to nextinode\n", inumber); +	if (inumber >= lastinum) { +		readcnt++; +		dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); +		if (readcnt % readpercg == 0) { +			size = partialsize; +			lastinum += partialcnt; +		} else { +			size = inobufsize; +			lastinum += fullcnt; +		} +		(void)bread(fsreadfd, (char *)inodebuf, dblk, size); /* ??? */ +		dp = inodebuf; +	} +	return (dp++); +} + +resetinodebuf() +{ + +	startinum = 0; +	nextino = 0; +	lastinum = 0; +	readcnt = 0; +	inobufsize = blkroundup(&sblock, INOBUFSIZE); +	fullcnt = inobufsize / sizeof(struct dinode); +	readpercg = sblock.fs_ipg / fullcnt; +	partialcnt = sblock.fs_ipg % fullcnt; +	partialsize = partialcnt * sizeof(struct dinode); +	if (partialcnt != 0) { +		readpercg++; +	} else { +		partialcnt = fullcnt; +		partialsize = inobufsize; +	} +	if (inodebuf == NULL && +	    (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) +		errexit("Cannot allocate space for inode buffer\n"); +	while (nextino < ROOTINO) +		(void)getnextinode(nextino); +} + +freeinodebuf() +{ + +	if (inodebuf != NULL) +		free((char *)inodebuf); +	inodebuf = NULL; +} + +/* + * Routines to maintain information about directory inodes. + * This is built during the first pass and used during the + * second and third passes. + * + * Enter inodes into the cache. + */ +cacheino(dp, inumber) +	register struct dinode *dp; +	ino_t inumber; +{ +	register struct inoinfo *inp; +	struct inoinfo **inpp; +	unsigned int blks; + +	blks = howmany(dp->di_size, sblock.fs_bsize); +	if (blks > NDADDR) +		blks = NDADDR + NIADDR; +	inp = (struct inoinfo *) +		malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr_t)); +	if (inp == NULL) +		return; +	inpp = &inphead[inumber % numdirs]; +	inp->i_nexthash = *inpp; +	*inpp = inp; +	inp->i_parent = (ino_t)0; +	inp->i_dotdot = (ino_t)0; +	inp->i_number = inumber; +	inp->i_isize = dp->di_size; +	inp->i_numblks = blks * sizeof(daddr_t); +	bcopy((char *)&dp->di_db[0], (char *)&inp->i_blks[0], +	    (size_t)inp->i_numblks); +	if (inplast == listmax) { +		listmax += 100; +		inpsort = (struct inoinfo **)realloc((char *)inpsort, +		    (unsigned)listmax * sizeof(struct inoinfo *)); +		if (inpsort == NULL) +			errexit("cannot increase directory list"); +	} +	inpsort[inplast++] = inp; +} + +/* + * Look up an inode cache structure. + */ +struct inoinfo * +getinoinfo(inumber) +	ino_t inumber; +{ +	register struct inoinfo *inp; + +	for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { +		if (inp->i_number != inumber) +			continue; +		return (inp); +	} +	errexit("cannot find inode %d\n", inumber); +	return ((struct inoinfo *)0); +} + +/* + * Clean up all the inode cache structure. + */ +inocleanup() +{ +	register struct inoinfo **inpp; + +	if (inphead == NULL) +		return; +	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) +		free((char *)(*inpp)); +	free((char *)inphead); +	free((char *)inpsort); +	inphead = inpsort = NULL; +} +	 +inodirty() +{ +	 +	dirty(pbp); +} + +clri(idesc, type, flag) +	register struct inodesc *idesc; +	char *type; +	int flag; +{ +	register struct dinode *dp; + +	dp = ginode(idesc->id_number); +	if (flag == 1) { +		pwarn("%s %s", type, +		    (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); +		pinode(idesc->id_number); +	} +	if (preen || reply("CLEAR") == 1) { +		if (preen) +			printf(" (CLEARED)\n"); +		n_files--; +		(void)ckinode(dp, idesc); +		clearinode(dp); +		statemap[idesc->id_number] = USTATE; +		inodirty(); +	} +} + +findname(idesc) +	struct inodesc *idesc; +{ +	register struct direct *dirp = idesc->id_dirp; + +	if (dirp->d_ino != idesc->id_parent) +		return (KEEPON); +	bcopy(dirp->d_name, idesc->id_name, (size_t)dirp->d_namlen + 1); +	return (STOP|FOUND); +} + +findino(idesc) +	struct inodesc *idesc; +{ +	register struct direct *dirp = idesc->id_dirp; + +	if (dirp->d_ino == 0) +		return (KEEPON); +	if (strcmp(dirp->d_name, idesc->id_name) == 0 && +	    dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { +		idesc->id_parent = dirp->d_ino; +		return (STOP|FOUND); +	} +	return (KEEPON); +} + +pinode(ino) +	ino_t ino; +{ +	register struct dinode *dp; +	register char *p; +	struct passwd *pw; +	char *ctime(); + +	printf(" I=%lu ", ino); +	if (ino < ROOTINO || ino > maxino) +		return; +	dp = ginode(ino); +	printf(" OWNER="); +	if ((pw = getpwuid((int)dp->di_uid)) != 0) +		printf("%s ", pw->pw_name); +	else +		printf("%u ", (unsigned)dp->di_uid); +	printf("MODE=%o\n", dp->di_mode); +	if (preen) +		printf("%s: ", cdevname); +	printf("SIZE=%qu ", dp->di_size); +	p = ctime(&dp->di_mtime.ts_sec); +	printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); +} + +blkerror(ino, type, blk) +	ino_t ino; +	char *type; +	daddr_t blk; +{ + +	pfatal("%ld %s I=%lu", blk, type, ino); +	printf("\n"); +	switch (statemap[ino]) { + +	case FSTATE: +		statemap[ino] = FCLEAR; +		return; + +	case DSTATE: +		statemap[ino] = DCLEAR; +		return; + +	case FCLEAR: +	case DCLEAR: +		return; + +	default: +		errexit("BAD STATE %d TO BLKERR", statemap[ino]); +		/* NOTREACHED */ +	} +} + +/* + * allocate an unused inode + */ +ino_t +allocino(request, type) +	ino_t request; +	int type; +{ +	register ino_t ino; +	register struct dinode *dp; + +	if (request == 0) +		request = ROOTINO; +	else if (statemap[request] != USTATE) +		return (0); +	for (ino = request; ino < maxino; ino++) +		if (statemap[ino] == USTATE) +			break; +	if (ino == maxino) +		return (0); +	switch (type & IFMT) { +	case IFDIR: +		statemap[ino] = DSTATE; +		break; +	case IFREG: +	case IFLNK: +		statemap[ino] = FSTATE; +		break; +	default: +		return (0); +	} +	dp = ginode(ino); +	dp->di_db[0] = allocblk((long)1); +	if (dp->di_db[0] == 0) { +		statemap[ino] = USTATE; +		return (0); +	} +	dp->di_mode = type; +	(void)time(&dp->di_atime.ts_sec); +	dp->di_mtime = dp->di_ctime = dp->di_atime; +	dp->di_size = sblock.fs_fsize; +	dp->di_blocks = btodb(sblock.fs_fsize); +	n_files++; +	inodirty(); +	if (newinofmt) +		typemap[ino] = IFTODT(type); +	return (ino); +} + +/* + * deallocate an inode + */ +freeino(ino) +	ino_t ino; +{ +	struct inodesc idesc; +	extern int pass4check(); +	struct dinode *dp; + +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = ADDR; +	idesc.id_func = pass4check; +	idesc.id_number = ino; +	dp = ginode(ino); +	(void)ckinode(dp, &idesc); +	clearinode(dp); +	inodirty(); +	statemap[ino] = USTATE; +	n_files--; +} diff --git a/sbin/fsck/main.c b/sbin/fsck/main.c new file mode 100644 index 000000000000..2e69715fecf0 --- /dev/null +++ b/sbin/fsck/main.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1986, 1993\n\ +	The Regents of the University of California.  All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 1/23/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/mount.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <fstab.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include "fsck.h" + +void	catch(), catchquit(), voidquit(); +int	returntosingle; + +main(argc, argv) +	int	argc; +	char	*argv[]; +{ +	int ch; +	int ret, maxrun = 0; +	extern int docheck(), checkfilesys(); +	extern char *optarg, *blockcheck(); +	extern int optind; + +	sync(); +	while ((ch = getopt(argc, argv, "dpnNyYb:c:l:m:")) != EOF) { +		switch (ch) { +		case 'p': +			preen++; +			break; + +		case 'b': +			bflag = argtoi('b', "number", optarg, 10); +			printf("Alternate super block location: %d\n", bflag); +			break; + +		case 'c': +			cvtlevel = argtoi('c', "conversion level", optarg, 10); +			break; +		 +		case 'd': +			debug++; +			break; + +		case 'l': +			maxrun = argtoi('l', "number", optarg, 10); +			break; + +		case 'm': +			lfmode = argtoi('m', "mode", optarg, 8); +			if (lfmode &~ 07777) +				errexit("bad mode to -m: %o\n", lfmode); +			printf("** lost+found creation mode %o\n", lfmode); +			break; + +		case 'n': +		case 'N': +			nflag++; +			yflag = 0; +			break; + +		case 'y': +		case 'Y': +			yflag++; +			nflag = 0; +			break; + +		default: +			errexit("%c option?\n", ch); +		} +	} +	argc -= optind; +	argv += optind; +	if (signal(SIGINT, SIG_IGN) != SIG_IGN) +		(void)signal(SIGINT, catch); +	if (preen) +		(void)signal(SIGQUIT, catchquit); +	if (argc) { +		while (argc-- > 0) +			(void)checkfilesys(blockcheck(*argv++), 0, 0L, 0); +		exit(0); +	} +	ret = checkfstab(preen, maxrun, docheck, checkfilesys); +	if (returntosingle) +		exit(2); +	exit(ret); +} + +argtoi(flag, req, str, base) +	int flag; +	char *req, *str; +	int base; +{ +	char *cp; +	int ret; + +	ret = (int)strtol(str, &cp, base); +	if (cp == str || *cp) +		errexit("-%c flag requires a %s\n", flag, req); +	return (ret); +} + +/* + * Determine whether a filesystem should be checked. + */ +docheck(fsp) +	register struct fstab *fsp; +{ + +	if (strcmp(fsp->fs_vfstype, "ufs") || +	    (strcmp(fsp->fs_type, FSTAB_RW) && +	     strcmp(fsp->fs_type, FSTAB_RO)) || +	    fsp->fs_passno == 0) +		return (0); +	return (1); +} + +/* + * Check the specified filesystem. + */ +/* ARGSUSED */ +checkfilesys(filesys, mntpt, auxdata, child) +	char *filesys, *mntpt; +	long auxdata; +{ +	daddr_t n_ffree, n_bfree; +	struct dups *dp; +	struct zlncnt *zlnp; +	int cylno; + +	if (preen && child) +		(void)signal(SIGQUIT, voidquit); +	cdevname = filesys; +	if (debug && preen) +		pwarn("starting\n"); +	if (setup(filesys) == 0) { +		if (preen) +			pfatal("CAN'T CHECK FILE SYSTEM."); +		return (0); +	} +	/* +	 * 1: scan inodes tallying blocks used +	 */ +	if (preen == 0) { +		printf("** Last Mounted on %s\n", sblock.fs_fsmnt); +		if (hotroot) +			printf("** Root file system\n"); +		printf("** Phase 1 - Check Blocks and Sizes\n"); +	} +	pass1(); + +	/* +	 * 1b: locate first references to duplicates, if any +	 */ +	if (duplist) { +		if (preen) +			pfatal("INTERNAL ERROR: dups with -p"); +		printf("** Phase 1b - Rescan For More DUPS\n"); +		pass1b(); +	} + +	/* +	 * 2: traverse directories from root to mark all connected directories +	 */ +	if (preen == 0) +		printf("** Phase 2 - Check Pathnames\n"); +	pass2(); + +	/* +	 * 3: scan inodes looking for disconnected directories +	 */ +	if (preen == 0) +		printf("** Phase 3 - Check Connectivity\n"); +	pass3(); + +	/* +	 * 4: scan inodes looking for disconnected files; check reference counts +	 */ +	if (preen == 0) +		printf("** Phase 4 - Check Reference Counts\n"); +	pass4(); + +	/* +	 * 5: check and repair resource counts in cylinder groups +	 */ +	if (preen == 0) +		printf("** Phase 5 - Check Cyl groups\n"); +	pass5(); + +	/* +	 * print out summary statistics +	 */ +	n_ffree = sblock.fs_cstotal.cs_nffree; +	n_bfree = sblock.fs_cstotal.cs_nbfree; +	pwarn("%ld files, %ld used, %ld free ", +	    n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree); +	printf("(%ld frags, %ld blocks, %d.%d%% fragmentation)\n", +	    n_ffree, n_bfree, (n_ffree * 100) / sblock.fs_dsize, +	    ((n_ffree * 1000 + sblock.fs_dsize / 2) / sblock.fs_dsize) % 10); +	if (debug && +	    (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree)) +		printf("%ld files missing\n", n_files); +	if (debug) { +		n_blks += sblock.fs_ncg * +			(cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); +		n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); +		n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize); +		if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree)) +			printf("%ld blocks missing\n", n_blks); +		if (duplist != NULL) { +			printf("The following duplicate blocks remain:"); +			for (dp = duplist; dp; dp = dp->next) +				printf(" %ld,", dp->dup); +			printf("\n"); +		} +		if (zlnhead != NULL) { +			printf("The following zero link count inodes remain:"); +			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) +				printf(" %lu,", zlnp->zlncnt); +			printf("\n"); +		} +	} +	zlnhead = (struct zlncnt *)0; +	duplist = (struct dups *)0; +	muldup = (struct dups *)0; +	inocleanup(); +	if (fsmodified) { +		(void)time(&sblock.fs_time); +		sbdirty(); +	} +	if (cvtlevel && sblk.b_dirty) { +		/*  +		 * Write out the duplicate super blocks +		 */ +		for (cylno = 0; cylno < sblock.fs_ncg; cylno++) +			bwrite(fswritefd, (char *)&sblock, +			    fsbtodb(&sblock, cgsblock(&sblock, cylno)), SBSIZE); +	} +	ckfini(); +	free(blockmap); +	free(statemap); +	free((char *)lncntp); +	if (!fsmodified) +		return (0); +	if (!preen) +		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); +	if (hotroot) { +		struct statfs stfs_buf; +		/* +		 * We modified the root.  Do a mount update on +		 * it, unless it is read-write, so we can continue. +		 */ +		if (statfs("/", &stfs_buf) == 0) { +			long flags = stfs_buf.f_flags; +			struct ufs_args args; +			int ret; + +			if (flags & MNT_RDONLY) { +				args.fspec = 0; +				args.export.ex_flags = 0; +				args.export.ex_root = 0; +				flags |= MNT_UPDATE | MNT_RELOAD; +				ret = mount(MOUNT_UFS, "/", flags, &args); +				if (ret == 0) +					return(0); +			} +		} +		if (!preen) +			printf("\n***** REBOOT NOW *****\n"); +		sync(); +		return (4); +	} +	return (0); +} diff --git a/sbin/fsck/pass1.c b/sbin/fsck/pass1.c new file mode 100644 index 000000000000..ca255fe78579 --- /dev/null +++ b/sbin/fsck/pass1.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +static daddr_t badblk; +static daddr_t dupblk; +int pass1check(); +struct dinode *getnextinode(); + +pass1() +{ +	ino_t inumber; +	int c, i, cgd; +	struct inodesc idesc; + +	/* +	 * Set file system reserved blocks in used block map. +	 */ +	for (c = 0; c < sblock.fs_ncg; c++) { +		cgd = cgdmin(&sblock, c); +		if (c == 0) { +			i = cgbase(&sblock, c); +			cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); +		} else +			i = cgsblock(&sblock, c); +		for (; i < cgd; i++) +			setbmap(i); +	} +	/* +	 * Find all allocated blocks. +	 */ +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = ADDR; +	idesc.id_func = pass1check; +	inumber = 0; +	n_files = n_blks = 0; +	resetinodebuf(); +	for (c = 0; c < sblock.fs_ncg; c++) { +		for (i = 0; i < sblock.fs_ipg; i++, inumber++) { +			if (inumber < ROOTINO) +				continue; +			checkinode(inumber, &idesc); +		} +	} +	freeinodebuf(); +} + +checkinode(inumber, idesc) +	ino_t inumber; +	register struct inodesc *idesc; +{ +	register struct dinode *dp; +	struct zlncnt *zlnp; +	int ndb, j; +	mode_t mode; +	char symbuf[MAXSYMLINKLEN]; + +	dp = getnextinode(inumber); +	mode = dp->di_mode & IFMT; +	if (mode == 0) { +		if (bcmp((char *)dp->di_db, (char *)zino.di_db, +			NDADDR * sizeof(daddr_t)) || +		    bcmp((char *)dp->di_ib, (char *)zino.di_ib, +			NIADDR * sizeof(daddr_t)) || +		    dp->di_mode || dp->di_size) { +			pfatal("PARTIALLY ALLOCATED INODE I=%lu", inumber); +			if (reply("CLEAR") == 1) { +				dp = ginode(inumber); +				clearinode(dp); +				inodirty(); +			} +		} +		statemap[inumber] = USTATE; +		return; +	} +	lastino = inumber; +	if (/* dp->di_size < 0 || */ +	    dp->di_size + sblock.fs_bsize - 1 < dp->di_size) { +		if (debug) +			printf("bad size %qu:", dp->di_size); +		goto unknown; +	} +	if (!preen && mode == IFMT && reply("HOLD BAD BLOCK") == 1) { +		dp = ginode(inumber); +		dp->di_size = sblock.fs_fsize; +		dp->di_mode = IFREG|0600; +		inodirty(); +	} +	ndb = howmany(dp->di_size, sblock.fs_bsize); +	if (ndb < 0) { +		if (debug) +			printf("bad size %qu ndb %d:", +				dp->di_size, ndb); +		goto unknown; +	} +	if (mode == IFBLK || mode == IFCHR) +		ndb++; +	if (mode == IFLNK) { +		if (doinglevel2 && +		    dp->di_size > 0 && dp->di_size < MAXSYMLINKLEN && +		    dp->di_blocks != 0) { +			if (bread(fsreadfd, symbuf, +			    fsbtodb(&sblock, dp->di_db[0]), +			    (long)dp->di_size) != 0) +				errexit("cannot read symlink"); +			if (debug) { +				symbuf[dp->di_size] = 0; +				printf("convert symlink %d(%s) of size %d\n", +					inumber, symbuf, (long)dp->di_size); +			} +			dp = ginode(inumber); +			bcopy(symbuf, (caddr_t)dp->di_shortlink, +			    (long)dp->di_size); +			dp->di_blocks = 0; +			inodirty(); +		} +		/* +		 * Fake ndb value so direct/indirect block checks below +		 * will detect any garbage after symlink string. +		 */ +		if (dp->di_size < sblock.fs_maxsymlinklen) { +			ndb = howmany(dp->di_size, sizeof(daddr_t)); +			if (ndb > NDADDR) { +				j = ndb - NDADDR; +				for (ndb = 1; j > 1; j--) +					ndb *= NINDIR(&sblock); +				ndb += NDADDR; +			} +		} +	} +	for (j = ndb; j < NDADDR; j++) +		if (dp->di_db[j] != 0) { +			if (debug) +				printf("bad direct addr: %ld\n", dp->di_db[j]); +			goto unknown; +		} +	for (j = 0, ndb -= NDADDR; ndb > 0; j++) +		ndb /= NINDIR(&sblock); +	for (; j < NIADDR; j++) +		if (dp->di_ib[j] != 0) { +			if (debug) +				printf("bad indirect addr: %ld\n", +					dp->di_ib[j]); +			goto unknown; +		} +	if (ftypeok(dp) == 0) +		goto unknown; +	n_files++; +	lncntp[inumber] = dp->di_nlink; +	if (dp->di_nlink <= 0) { +		zlnp = (struct zlncnt *)malloc(sizeof *zlnp); +		if (zlnp == NULL) { +			pfatal("LINK COUNT TABLE OVERFLOW"); +			if (reply("CONTINUE") == 0) +				errexit(""); +		} else { +			zlnp->zlncnt = inumber; +			zlnp->next = zlnhead; +			zlnhead = zlnp; +		} +	} +	if (mode == IFDIR) { +		if (dp->di_size == 0) +			statemap[inumber] = DCLEAR; +		else +			statemap[inumber] = DSTATE; +		cacheino(dp, inumber); +	} else +		statemap[inumber] = FSTATE; +	typemap[inumber] = IFTODT(mode); +	if (doinglevel2 && +	    (dp->di_ouid != (u_short)-1 || dp->di_ogid != (u_short)-1)) { +		dp = ginode(inumber); +		dp->di_uid = dp->di_ouid; +		dp->di_ouid = -1; +		dp->di_gid = dp->di_ogid; +		dp->di_ogid = -1; +		inodirty(); +	} +	badblk = dupblk = 0; +	idesc->id_number = inumber; +	(void)ckinode(dp, idesc); +	idesc->id_entryno *= btodb(sblock.fs_fsize); +	if (dp->di_blocks != idesc->id_entryno) { +		pwarn("INCORRECT BLOCK COUNT I=%lu (%ld should be %ld)", +		    inumber, dp->di_blocks, idesc->id_entryno); +		if (preen) +			printf(" (CORRECTED)\n"); +		else if (reply("CORRECT") == 0) +			return; +		dp = ginode(inumber); +		dp->di_blocks = idesc->id_entryno; +		inodirty(); +	} +	return; +unknown: +	pfatal("UNKNOWN FILE TYPE I=%lu", inumber); +	statemap[inumber] = FCLEAR; +	if (reply("CLEAR") == 1) { +		statemap[inumber] = USTATE; +		dp = ginode(inumber); +		clearinode(dp); +		inodirty(); +	} +} + +pass1check(idesc) +	register struct inodesc *idesc; +{ +	int res = KEEPON; +	int anyout, nfrags; +	daddr_t blkno = idesc->id_blkno; +	register struct dups *dlp; +	struct dups *new; + +	if ((anyout = chkrange(blkno, idesc->id_numfrags)) != 0) { +		blkerror(idesc->id_number, "BAD", blkno); +		if (badblk++ >= MAXBAD) { +			pwarn("EXCESSIVE BAD BLKS I=%lu", +				idesc->id_number); +			if (preen) +				printf(" (SKIPPING)\n"); +			else if (reply("CONTINUE") == 0) +				errexit(""); +			return (STOP); +		} +	} +	for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { +		if (anyout && chkrange(blkno, 1)) { +			res = SKIP; +		} else if (!testbmap(blkno)) { +			n_blks++; +			setbmap(blkno); +		} else { +			blkerror(idesc->id_number, "DUP", blkno); +			if (dupblk++ >= MAXDUP) { +				pwarn("EXCESSIVE DUP BLKS I=%lu", +					idesc->id_number); +				if (preen) +					printf(" (SKIPPING)\n"); +				else if (reply("CONTINUE") == 0) +					errexit(""); +				return (STOP); +			} +			new = (struct dups *)malloc(sizeof(struct dups)); +			if (new == NULL) { +				pfatal("DUP TABLE OVERFLOW."); +				if (reply("CONTINUE") == 0) +					errexit(""); +				return (STOP); +			} +			new->dup = blkno; +			if (muldup == 0) { +				duplist = muldup = new; +				new->next = 0; +			} else { +				new->next = muldup->next; +				muldup->next = new; +			} +			for (dlp = duplist; dlp != muldup; dlp = dlp->next) +				if (dlp->dup == blkno) +					break; +			if (dlp == muldup && dlp->dup != blkno) +				muldup = new; +		} +		/* +		 * count the number of blocks found in id_entryno +		 */ +		idesc->id_entryno++; +	} +	return (res); +} diff --git a/sbin/fsck/pass1b.c b/sbin/fsck/pass1b.c new file mode 100644 index 000000000000..27b2dfd6238c --- /dev/null +++ b/sbin/fsck/pass1b.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass1b.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <string.h> +#include "fsck.h" + +int	pass1bcheck(); +static  struct dups *duphead; + +pass1b() +{ +	register int c, i; +	register struct dinode *dp; +	struct inodesc idesc; +	ino_t inumber; + +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = ADDR; +	idesc.id_func = pass1bcheck; +	duphead = duplist; +	inumber = 0; +	for (c = 0; c < sblock.fs_ncg; c++) { +		for (i = 0; i < sblock.fs_ipg; i++, inumber++) { +			if (inumber < ROOTINO) +				continue; +			dp = ginode(inumber); +			if (dp == NULL) +				continue; +			idesc.id_number = inumber; +			if (statemap[inumber] != USTATE && +			    (ckinode(dp, &idesc) & STOP)) +				return; +		} +	} +} + +pass1bcheck(idesc) +	register struct inodesc *idesc; +{ +	register struct dups *dlp; +	int nfrags, res = KEEPON; +	daddr_t blkno = idesc->id_blkno; + +	for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { +		if (chkrange(blkno, 1)) +			res = SKIP; +		for (dlp = duphead; dlp; dlp = dlp->next) { +			if (dlp->dup == blkno) { +				blkerror(idesc->id_number, "DUP", blkno); +				dlp->dup = duphead->dup; +				duphead->dup = blkno; +				duphead = duphead->next; +			} +			if (dlp == muldup) +				break; +		} +		if (muldup == 0 || duphead == muldup->next) +			return (STOP); +	} +	return (res); +} diff --git a/sbin/fsck/pass2.c b/sbin/fsck/pass2.c new file mode 100644 index 000000000000..631475174372 --- /dev/null +++ b/sbin/fsck/pass2.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass2.c	8.2 (Berkeley) 2/27/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +#define MINDIRSIZE	(sizeof (struct dirtemplate)) + +int	pass2check(), blksort(); + +pass2() +{ +	register struct dinode *dp; +	register struct inoinfo **inpp, *inp; +	struct inoinfo **inpend; +	struct inodesc curino; +	struct dinode dino; +	char pathbuf[MAXPATHLEN + 1]; + +	switch (statemap[ROOTINO]) { + +	case USTATE: +		pfatal("ROOT INODE UNALLOCATED"); +		if (reply("ALLOCATE") == 0) +			errexit(""); +		if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) +			errexit("CANNOT ALLOCATE ROOT INODE\n"); +		break; + +	case DCLEAR: +		pfatal("DUPS/BAD IN ROOT INODE"); +		if (reply("REALLOCATE")) { +			freeino(ROOTINO); +			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) +				errexit("CANNOT ALLOCATE ROOT INODE\n"); +			break; +		} +		if (reply("CONTINUE") == 0) +			errexit(""); +		break; + +	case FSTATE: +	case FCLEAR: +		pfatal("ROOT INODE NOT DIRECTORY"); +		if (reply("REALLOCATE")) { +			freeino(ROOTINO); +			if (allocdir(ROOTINO, ROOTINO, 0755) != ROOTINO) +				errexit("CANNOT ALLOCATE ROOT INODE\n"); +			break; +		} +		if (reply("FIX") == 0) +			errexit(""); +		dp = ginode(ROOTINO); +		dp->di_mode &= ~IFMT; +		dp->di_mode |= IFDIR; +		inodirty(); +		break; + +	case DSTATE: +		break; + +	default: +		errexit("BAD STATE %d FOR ROOT INODE", statemap[ROOTINO]); +	} +	statemap[ROOTINO] = DFOUND; +	/* +	 * Sort the directory list into disk block order. +	 */ +	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); +	/* +	 * Check the integrity of each directory. +	 */ +	bzero((char *)&curino, sizeof(struct inodesc)); +	curino.id_type = DATA; +	curino.id_func = pass2check; +	dp = &dino; +	inpend = &inpsort[inplast]; +	for (inpp = inpsort; inpp < inpend; inpp++) { +		inp = *inpp; +		if (inp->i_isize == 0) +			continue; +		if (inp->i_isize < MINDIRSIZE) { +			direrror(inp->i_number, "DIRECTORY TOO SHORT"); +			inp->i_isize = roundup(MINDIRSIZE, DIRBLKSIZ); +			if (reply("FIX") == 1) { +				dp = ginode(inp->i_number); +				dp->di_size = inp->i_isize; +				inodirty(); +				dp = &dino; +			} +		} else if ((inp->i_isize & (DIRBLKSIZ - 1)) != 0) { +			getpathname(pathbuf, inp->i_number, inp->i_number); +			pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", +				pathbuf, inp->i_isize, DIRBLKSIZ); +			if (preen) +				printf(" (ADJUSTED)\n"); +			inp->i_isize = roundup(inp->i_isize, DIRBLKSIZ); +			if (preen || reply("ADJUST") == 1) { +				dp = ginode(inp->i_number); +				dp->di_size = roundup(inp->i_isize, DIRBLKSIZ); +				inodirty(); +				dp = &dino; +			} +		} +		bzero((char *)&dino, sizeof(struct dinode)); +		dino.di_mode = IFDIR; +		dp->di_size = inp->i_isize; +		bcopy((char *)&inp->i_blks[0], (char *)&dp->di_db[0], +			(size_t)inp->i_numblks); +		curino.id_number = inp->i_number; +		curino.id_parent = inp->i_parent; +		(void)ckinode(dp, &curino); +	} +	/* +	 * Now that the parents of all directories have been found, +	 * make another pass to verify the value of `..' +	 */ +	for (inpp = inpsort; inpp < inpend; inpp++) { +		inp = *inpp; +		if (inp->i_parent == 0 || inp->i_isize == 0) +			continue; +		if (statemap[inp->i_parent] == DFOUND && +		    statemap[inp->i_number] == DSTATE) +			statemap[inp->i_number] = DFOUND; +		if (inp->i_dotdot == inp->i_parent || +		    inp->i_dotdot == (ino_t)-1) +			continue; +		if (inp->i_dotdot == 0) { +			inp->i_dotdot = inp->i_parent; +			fileerror(inp->i_parent, inp->i_number, "MISSING '..'"); +			if (reply("FIX") == 0) +				continue; +			(void)makeentry(inp->i_number, inp->i_parent, ".."); +			lncntp[inp->i_parent]--; +			continue; +		} +		fileerror(inp->i_parent, inp->i_number, +		    "BAD INODE NUMBER FOR '..'"); +		if (reply("FIX") == 0) +			continue; +		lncntp[inp->i_dotdot]++; +		lncntp[inp->i_parent]--; +		inp->i_dotdot = inp->i_parent; +		(void)changeino(inp->i_number, "..", inp->i_parent); +	} +	/* +	 * Mark all the directories that can be found from the root. +	 */ +	propagate(); +} + +pass2check(idesc) +	struct inodesc *idesc; +{ +	register struct direct *dirp = idesc->id_dirp; +	register struct inoinfo *inp; +	int n, entrysize, ret = 0; +	struct dinode *dp; +	char *errmsg; +	struct direct proto; +	char namebuf[MAXPATHLEN + 1]; +	char pathbuf[MAXPATHLEN + 1]; + +	/* +	 * If converting, set directory entry type. +	 */ +	if (doinglevel2 && dirp->d_ino > 0 && dirp->d_ino < maxino) { +		dirp->d_type = typemap[dirp->d_ino]; +		ret |= ALTERED; +	} +	/*  +	 * check for "." +	 */ +	if (idesc->id_entryno != 0) +		goto chk1; +	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { +		if (dirp->d_ino != idesc->id_number) { +			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); +			dirp->d_ino = idesc->id_number; +			if (reply("FIX") == 1) +				ret |= ALTERED; +		} +		if (newinofmt && dirp->d_type != DT_DIR) { +			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); +			dirp->d_type = DT_DIR; +			if (reply("FIX") == 1) +				ret |= ALTERED; +		} +		goto chk1; +	} +	direrror(idesc->id_number, "MISSING '.'"); +	proto.d_ino = idesc->id_number; +	if (newinofmt) +		proto.d_type = DT_DIR; +	else +		proto.d_type = 0; +	proto.d_namlen = 1; +	(void)strcpy(proto.d_name, "."); +	entrysize = DIRSIZ(0, &proto); +	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") != 0) { +		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", +			dirp->d_name); +	} else if (dirp->d_reclen < entrysize) { +		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); +	} else if (dirp->d_reclen < 2 * entrysize) { +		proto.d_reclen = dirp->d_reclen; +		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); +		if (reply("FIX") == 1) +			ret |= ALTERED; +	} else { +		n = dirp->d_reclen - entrysize; +		proto.d_reclen = entrysize; +		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); +		idesc->id_entryno++; +		lncntp[dirp->d_ino]--; +		dirp = (struct direct *)((char *)(dirp) + entrysize); +		bzero((char *)dirp, (size_t)n); +		dirp->d_reclen = n; +		if (reply("FIX") == 1) +			ret |= ALTERED; +	} +chk1: +	if (idesc->id_entryno > 1) +		goto chk2; +	inp = getinoinfo(idesc->id_number); +	proto.d_ino = inp->i_parent; +	if (newinofmt) +		proto.d_type = DT_DIR; +	else +		proto.d_type = 0; +	proto.d_namlen = 2; +	(void)strcpy(proto.d_name, ".."); +	entrysize = DIRSIZ(0, &proto); +	if (idesc->id_entryno == 0) { +		n = DIRSIZ(0, dirp); +		if (dirp->d_reclen < n + entrysize) +			goto chk2; +		proto.d_reclen = dirp->d_reclen - n; +		dirp->d_reclen = n; +		idesc->id_entryno++; +		lncntp[dirp->d_ino]--; +		dirp = (struct direct *)((char *)(dirp) + n); +		bzero((char *)dirp, (size_t)proto.d_reclen); +		dirp->d_reclen = proto.d_reclen; +	} +	if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { +		inp->i_dotdot = dirp->d_ino; +		if (newinofmt && dirp->d_type != DT_DIR) { +			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); +			dirp->d_type = DT_DIR; +			if (reply("FIX") == 1) +				ret |= ALTERED; +		} +		goto chk2; +	} +	if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") != 0) { +		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); +		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", +			dirp->d_name); +		inp->i_dotdot = (ino_t)-1; +	} else if (dirp->d_reclen < entrysize) { +		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); +		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); +		inp->i_dotdot = (ino_t)-1; +	} else if (inp->i_parent != 0) { +		/* +		 * We know the parent, so fix now. +		 */ +		inp->i_dotdot = inp->i_parent; +		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); +		proto.d_reclen = dirp->d_reclen; +		bcopy((char *)&proto, (char *)dirp, (size_t)entrysize); +		if (reply("FIX") == 1) +			ret |= ALTERED; +	} +	idesc->id_entryno++; +	if (dirp->d_ino != 0) +		lncntp[dirp->d_ino]--; +	return (ret|KEEPON); +chk2: +	if (dirp->d_ino == 0) +		return (ret|KEEPON); +	if (dirp->d_namlen <= 2 && +	    dirp->d_name[0] == '.' && +	    idesc->id_entryno >= 2) { +		if (dirp->d_namlen == 1) { +			direrror(idesc->id_number, "EXTRA '.' ENTRY"); +			dirp->d_ino = 0; +			if (reply("FIX") == 1) +				ret |= ALTERED; +			return (KEEPON | ret); +		} +		if (dirp->d_name[1] == '.') { +			direrror(idesc->id_number, "EXTRA '..' ENTRY"); +			dirp->d_ino = 0; +			if (reply("FIX") == 1) +				ret |= ALTERED; +			return (KEEPON | ret); +		} +	} +	idesc->id_entryno++; +	n = 0; +	if (dirp->d_ino > maxino) { +		fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); +		n = reply("REMOVE"); +	} else { +again: +		switch (statemap[dirp->d_ino]) { +		case USTATE: +			if (idesc->id_entryno <= 2) +				break; +			fileerror(idesc->id_number, dirp->d_ino, "UNALLOCATED"); +			n = reply("REMOVE"); +			break; + +		case DCLEAR: +		case FCLEAR: +			if (idesc->id_entryno <= 2) +				break; +			if (statemap[dirp->d_ino] == FCLEAR) +				errmsg = "DUP/BAD"; +			else if (!preen) +				errmsg = "ZERO LENGTH DIRECTORY"; +			else { +				n = 1; +				break; +			} +			fileerror(idesc->id_number, dirp->d_ino, errmsg); +			if ((n = reply("REMOVE")) == 1) +				break; +			dp = ginode(dirp->d_ino); +			statemap[dirp->d_ino] = +			    (dp->di_mode & IFMT) == IFDIR ? DSTATE : FSTATE; +			lncntp[dirp->d_ino] = dp->di_nlink; +			goto again; + +		case DSTATE: +			if (statemap[idesc->id_number] == DFOUND) +				statemap[dirp->d_ino] = DFOUND; +			/* fall through */ + +		case DFOUND: +			inp = getinoinfo(dirp->d_ino); +			if (inp->i_parent != 0 && idesc->id_entryno > 2) { +				getpathname(pathbuf, idesc->id_number, +				    idesc->id_number); +				getpathname(namebuf, dirp->d_ino, dirp->d_ino); +				pwarn("%s %s %s\n", pathbuf, +				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", +				    namebuf); +				if (preen) +					printf(" (IGNORED)\n"); +				else if ((n = reply("REMOVE")) == 1) +					break; +			} +			if (idesc->id_entryno > 2) +				inp->i_parent = idesc->id_number; +			/* fall through */ + +		case FSTATE: +			if (newinofmt && dirp->d_type != typemap[dirp->d_ino]) { +				fileerror(idesc->id_number, dirp->d_ino, +				    "BAD TYPE VALUE"); +				dirp->d_type = typemap[dirp->d_ino]; +				if (reply("FIX") == 1) +					ret |= ALTERED; +			} +			lncntp[dirp->d_ino]--; +			break; + +		default: +			errexit("BAD STATE %d FOR INODE I=%d", +			    statemap[dirp->d_ino], dirp->d_ino); +		} +	} +	if (n == 0) +		return (ret|KEEPON); +	dirp->d_ino = 0; +	return (ret|KEEPON|ALTERED); +} + +/* + * Routine to sort disk blocks. + */ +blksort(inpp1, inpp2) +	struct inoinfo **inpp1, **inpp2; +{ + +	return ((*inpp1)->i_blks[0] - (*inpp2)->i_blks[0]); +} diff --git a/sbin/fsck/pass3.c b/sbin/fsck/pass3.c new file mode 100644 index 000000000000..5c6f09d041de --- /dev/null +++ b/sbin/fsck/pass3.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass3.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include "fsck.h" + +pass3() +{ +	register struct inoinfo **inpp, *inp; +	ino_t orphan; +	int loopcnt; + +	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) { +		inp = *inpp; +		if (inp->i_number == ROOTINO || +		    !(inp->i_parent == 0 || statemap[inp->i_number] == DSTATE)) +			continue; +		if (statemap[inp->i_number] == DCLEAR) +			continue; +		for (loopcnt = 0; ; loopcnt++) { +			orphan = inp->i_number; +			if (inp->i_parent == 0 || +			    statemap[inp->i_parent] != DSTATE || +			    loopcnt > numdirs) +				break; +			inp = getinoinfo(inp->i_parent); +		} +		(void)linkup(orphan, inp->i_dotdot); +		inp->i_parent = inp->i_dotdot = lfdir; +		lncntp[lfdir]--; +		statemap[orphan] = DFOUND; +		propagate(); +	} +} diff --git a/sbin/fsck/pass4.c b/sbin/fsck/pass4.c new file mode 100644 index 000000000000..7450530e8a4e --- /dev/null +++ b/sbin/fsck/pass4.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass4.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <stdlib.h> +#include <string.h> +#include "fsck.h" + +int	pass4check(); + +pass4() +{ +	register ino_t inumber; +	register struct zlncnt *zlnp; +	struct dinode *dp; +	struct inodesc idesc; +	int n; + +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = ADDR; +	idesc.id_func = pass4check; +	for (inumber = ROOTINO; inumber <= lastino; inumber++) { +		idesc.id_number = inumber; +		switch (statemap[inumber]) { + +		case FSTATE: +		case DFOUND: +			n = lncntp[inumber]; +			if (n) +				adjust(&idesc, (short)n); +			else { +				for (zlnp = zlnhead; zlnp; zlnp = zlnp->next) +					if (zlnp->zlncnt == inumber) { +						zlnp->zlncnt = zlnhead->zlncnt; +						zlnp = zlnhead; +						zlnhead = zlnhead->next; +						free((char *)zlnp); +						clri(&idesc, "UNREF", 1); +						break; +					} +			} +			break; + +		case DSTATE: +			clri(&idesc, "UNREF", 1); +			break; + +		case DCLEAR: +			dp = ginode(inumber); +			if (dp->di_size == 0) { +				clri(&idesc, "ZERO LENGTH", 1); +				break; +			} +			/* fall through */ +		case FCLEAR: +			clri(&idesc, "BAD/DUP", 1); +			break; + +		case USTATE: +			break; + +		default: +			errexit("BAD STATE %d FOR INODE I=%d", +			    statemap[inumber], inumber); +		} +	} +} + +pass4check(idesc) +	register struct inodesc *idesc; +{ +	register struct dups *dlp; +	int nfrags, res = KEEPON; +	daddr_t blkno = idesc->id_blkno; + +	for (nfrags = idesc->id_numfrags; nfrags > 0; blkno++, nfrags--) { +		if (chkrange(blkno, 1)) { +			res = SKIP; +		} else if (testbmap(blkno)) { +			for (dlp = duplist; dlp; dlp = dlp->next) { +				if (dlp->dup != blkno) +					continue; +				dlp->dup = duplist->dup; +				dlp = duplist; +				duplist = duplist->next; +				free((char *)dlp); +				break; +			} +			if (dlp == 0) { +				clrbmap(blkno); +				n_blks--; +			} +		} +	} +	return (res); +} diff --git a/sbin/fsck/pass5.c b/sbin/fsck/pass5.c new file mode 100644 index 000000000000..2e98f96b5ca8 --- /dev/null +++ b/sbin/fsck/pass5.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)pass5.c	8.2 (Berkeley) 2/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <string.h> +#include "fsck.h" + +pass5() +{ +	int c, blk, frags, basesize, sumsize, mapsize, savednrpos; +	register struct fs *fs = &sblock; +	register struct cg *cg = &cgrp; +	daddr_t dbase, dmax; +	register daddr_t d; +	register long i, j; +	struct csum *cs; +	struct csum cstotal; +	struct inodesc idesc[3]; +	char buf[MAXBSIZE]; +	register struct cg *newcg = (struct cg *)buf; +	struct ocg *ocg = (struct ocg *)buf; + +	bzero((char *)newcg, (size_t)fs->fs_cgsize); +	newcg->cg_niblk = fs->fs_ipg; +	if (cvtlevel > 3) { +		if (fs->fs_maxcontig < 2 && fs->fs_contigsumsize > 0) { +			if (preen) +				pwarn("DELETING CLUSTERING MAPS\n"); +			if (preen || reply("DELETE CLUSTERING MAPS")) { +				fs->fs_contigsumsize = 0; +				doinglevel1 = 1; +				sbdirty(); +			} +		} +		if (fs->fs_maxcontig > 1) { +			char *doit = 0; + +			if (fs->fs_contigsumsize < 1) { +				doit = "CREAT"; +			} else if (fs->fs_contigsumsize < fs->fs_maxcontig && +				   fs->fs_contigsumsize < FS_MAXCONTIG) { +				doit = "EXPAND"; +			} +			if (doit) { +				i = fs->fs_contigsumsize; +				fs->fs_contigsumsize = +				    MIN(fs->fs_maxcontig, FS_MAXCONTIG); +				if (CGSIZE(fs) > fs->fs_bsize) { +					pwarn("CANNOT %s CLUSTER MAPS\n", doit); +					fs->fs_contigsumsize = i; +				} else if (preen || +				    reply("CREATE CLUSTER MAPS")) { +					if (preen) +						pwarn("%sING CLUSTER MAPS\n", +						    doit); +					fs->fs_cgsize = +					    fragroundup(fs, CGSIZE(fs)); +					doinglevel1 = 1; +					sbdirty(); +				} +			} +		} +	} +	switch ((int)fs->fs_postblformat) { + +	case FS_42POSTBLFMT: +		basesize = (char *)(&ocg->cg_btot[0]) - (char *)(&ocg->cg_link); +		sumsize = &ocg->cg_iused[0] - (char *)(&ocg->cg_btot[0]); +		mapsize = &ocg->cg_free[howmany(fs->fs_fpg, NBBY)] - +			(u_char *)&ocg->cg_iused[0]; +		ocg->cg_magic = CG_MAGIC; +		savednrpos = fs->fs_nrpos; +		fs->fs_nrpos = 8; +		break; + +	case FS_DYNAMICPOSTBLFMT: +		newcg->cg_btotoff = +		     &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); +		newcg->cg_boff = +		    newcg->cg_btotoff + fs->fs_cpg * sizeof(long); +		newcg->cg_iusedoff = newcg->cg_boff +  +		    fs->fs_cpg * fs->fs_nrpos * sizeof(short); +		newcg->cg_freeoff = +		    newcg->cg_iusedoff + howmany(fs->fs_ipg, NBBY); +		if (fs->fs_contigsumsize <= 0) { +			newcg->cg_nextfreeoff = newcg->cg_freeoff + +			    howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY); +		} else { +			newcg->cg_clustersumoff = newcg->cg_freeoff + +			    howmany(fs->fs_cpg * fs->fs_spc / NSPF(fs), NBBY) - +			    sizeof(long); +			newcg->cg_clustersumoff = +			    roundup(newcg->cg_clustersumoff, sizeof(long)); +			newcg->cg_clusteroff = newcg->cg_clustersumoff + +			    (fs->fs_contigsumsize + 1) * sizeof(long); +			newcg->cg_nextfreeoff = newcg->cg_clusteroff + +			    howmany(fs->fs_cpg * fs->fs_spc / NSPB(fs), NBBY); +		} +		newcg->cg_magic = CG_MAGIC; +		basesize = &newcg->cg_space[0] - (u_char *)(&newcg->cg_link); +		sumsize = newcg->cg_iusedoff - newcg->cg_btotoff; +		mapsize = newcg->cg_nextfreeoff - newcg->cg_iusedoff; +		break; + +	default: +		errexit("UNKNOWN ROTATIONAL TABLE FORMAT %d\n", +			fs->fs_postblformat); +	} +	bzero((char *)&idesc[0], sizeof idesc); +	for (i = 0; i < 3; i++) { +		idesc[i].id_type = ADDR; +		if (doinglevel2) +			idesc[i].id_fix = FIX; +	} +	bzero((char *)&cstotal, sizeof(struct csum)); +	j = blknum(fs, fs->fs_size + fs->fs_frag - 1); +	for (i = fs->fs_size; i < j; i++) +		setbmap(i); +	for (c = 0; c < fs->fs_ncg; c++) { +		getblk(&cgblk, cgtod(fs, c), fs->fs_cgsize); +		if (!cg_chkmagic(cg)) +			pfatal("CG %d: BAD MAGIC NUMBER\n", c); +		dbase = cgbase(fs, c); +		dmax = dbase + fs->fs_fpg; +		if (dmax > fs->fs_size) +			dmax = fs->fs_size; +		newcg->cg_time = cg->cg_time; +		newcg->cg_cgx = c; +		if (c == fs->fs_ncg - 1) +			newcg->cg_ncyl = fs->fs_ncyl % fs->fs_cpg; +		else +			newcg->cg_ncyl = fs->fs_cpg; +		newcg->cg_ndblk = dmax - dbase; +		if (fs->fs_contigsumsize > 0) +			newcg->cg_nclusterblks = newcg->cg_ndblk / fs->fs_frag; +		newcg->cg_cs.cs_ndir = 0; +		newcg->cg_cs.cs_nffree = 0; +		newcg->cg_cs.cs_nbfree = 0; +		newcg->cg_cs.cs_nifree = fs->fs_ipg; +		if (cg->cg_rotor < newcg->cg_ndblk) +			newcg->cg_rotor = cg->cg_rotor; +		else +			newcg->cg_rotor = 0; +		if (cg->cg_frotor < newcg->cg_ndblk) +			newcg->cg_frotor = cg->cg_frotor; +		else +			newcg->cg_frotor = 0; +		if (cg->cg_irotor < newcg->cg_niblk) +			newcg->cg_irotor = cg->cg_irotor; +		else +			newcg->cg_irotor = 0; +		bzero((char *)&newcg->cg_frsum[0], sizeof newcg->cg_frsum); +		bzero((char *)&cg_blktot(newcg)[0], +		      (size_t)(sumsize + mapsize)); +		if (fs->fs_postblformat == FS_42POSTBLFMT) +			ocg->cg_magic = CG_MAGIC; +		j = fs->fs_ipg * c; +		for (i = 0; i < fs->fs_ipg; j++, i++) { +			switch (statemap[j]) { + +			case USTATE: +				break; + +			case DSTATE: +			case DCLEAR: +			case DFOUND: +				newcg->cg_cs.cs_ndir++; +				/* fall through */ + +			case FSTATE: +			case FCLEAR: +				newcg->cg_cs.cs_nifree--; +				setbit(cg_inosused(newcg), i); +				break; + +			default: +				if (j < ROOTINO) +					break; +				errexit("BAD STATE %d FOR INODE I=%d", +				    statemap[j], j); +			} +		} +		if (c == 0) +			for (i = 0; i < ROOTINO; i++) { +				setbit(cg_inosused(newcg), i); +				newcg->cg_cs.cs_nifree--; +			} +		for (i = 0, d = dbase; +		     d < dmax; +		     d += fs->fs_frag, i += fs->fs_frag) { +			frags = 0; +			for (j = 0; j < fs->fs_frag; j++) { +				if (testbmap(d + j)) +					continue; +				setbit(cg_blksfree(newcg), i + j); +				frags++; +			} +			if (frags == fs->fs_frag) { +				newcg->cg_cs.cs_nbfree++; +				j = cbtocylno(fs, i); +				cg_blktot(newcg)[j]++; +				cg_blks(fs, newcg, j)[cbtorpos(fs, i)]++; +				if (fs->fs_contigsumsize > 0) +					setbit(cg_clustersfree(newcg), +					    i / fs->fs_frag); +			} else if (frags > 0) { +				newcg->cg_cs.cs_nffree += frags; +				blk = blkmap(fs, cg_blksfree(newcg), i); +				ffs_fragacct(fs, blk, newcg->cg_frsum, 1); +			} +		} +		if (fs->fs_contigsumsize > 0) { +			long *sump = cg_clustersum(newcg); +			u_char *mapp = cg_clustersfree(newcg); +			int map = *mapp++; +			int bit = 1; +			int run = 0; + +			for (i = 0; i < newcg->cg_nclusterblks; i++) { +				if ((map & bit) != 0) { +					run++; +				} else if (run != 0) { +					if (run > fs->fs_contigsumsize) +						run = fs->fs_contigsumsize; +					sump[run]++; +					run = 0; +				} +				if ((i & (NBBY - 1)) != (NBBY - 1)) { +					bit <<= 1; +				} else { +					map = *mapp++; +					bit = 1; +				} +			} +			if (run != 0) { +				if (run > fs->fs_contigsumsize) +					run = fs->fs_contigsumsize; +				sump[run]++; +			} +		} +		cstotal.cs_nffree += newcg->cg_cs.cs_nffree; +		cstotal.cs_nbfree += newcg->cg_cs.cs_nbfree; +		cstotal.cs_nifree += newcg->cg_cs.cs_nifree; +		cstotal.cs_ndir += newcg->cg_cs.cs_ndir; +		cs = &fs->fs_cs(fs, c); +		if (bcmp((char *)&newcg->cg_cs, (char *)cs, sizeof *cs) != 0 && +		    dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { +			bcopy((char *)&newcg->cg_cs, (char *)cs, sizeof *cs); +			sbdirty(); +		} +		if (doinglevel1) { +			bcopy((char *)newcg, (char *)cg, (size_t)fs->fs_cgsize); +			cgdirty(); +			continue; +		} +		if (bcmp(cg_inosused(newcg), +			 cg_inosused(cg), mapsize) != 0 && +		    dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) { +			bcopy(cg_inosused(newcg), cg_inosused(cg), +			      (size_t)mapsize); +			cgdirty(); +		} +		if ((bcmp((char *)newcg, (char *)cg, basesize) != 0 || +		     bcmp((char *)&cg_blktot(newcg)[0], +			  (char *)&cg_blktot(cg)[0], sumsize) != 0) && +		    dofix(&idesc[2], "SUMMARY INFORMATION BAD")) { +			bcopy((char *)newcg, (char *)cg, (size_t)basesize); +			bcopy((char *)&cg_blktot(newcg)[0], +			      (char *)&cg_blktot(cg)[0], (size_t)sumsize); +			cgdirty(); +		} +	} +	if (fs->fs_postblformat == FS_42POSTBLFMT) +		fs->fs_nrpos = savednrpos; +	if (bcmp((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs) != 0 +	    && dofix(&idesc[0], "FREE BLK COUNT(S) WRONG IN SUPERBLK")) { +		bcopy((char *)&cstotal, (char *)&fs->fs_cstotal, sizeof *cs); +		fs->fs_ronly = 0; +		fs->fs_fmod = 0; +		sbdirty(); +	} +} diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c new file mode 100644 index 000000000000..005a65d97989 --- /dev/null +++ b/sbin/fsck/preen.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1990, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)preen.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fstab.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +char	*rawname(), *unrawname(), *blockcheck(); + +struct part { +	struct	part *next;		/* forward link of partitions on disk */ +	char	*name;			/* device name */ +	char	*fsname;		/* mounted filesystem name */ +	long	auxdata;		/* auxillary data for application */ +} *badlist, **badnext = &badlist; + +struct disk { +	char	*name;			/* disk base name */ +	struct	disk *next;		/* forward link for list of disks */ +	struct	part *part;		/* head of list of partitions on disk */ +	int	pid;			/* If != 0, pid of proc working on */ +} *disks; + +int	nrun, ndisks; +char	hotroot; + +checkfstab(preen, maxrun, docheck, chkit) +	int preen, maxrun; +	int (*docheck)(), (*chkit)(); +{ +	register struct fstab *fsp; +	register struct disk *dk, *nextdisk; +	register struct part *pt; +	int ret, pid, retcode, passno, sumstatus, status; +	long auxdata; +	char *name; + +	sumstatus = 0; +	for (passno = 1; passno <= 2; passno++) { +		if (setfsent() == 0) { +			fprintf(stderr, "Can't open checklist file: %s\n", +			    _PATH_FSTAB); +			return (8); +		} +		while ((fsp = getfsent()) != 0) { +			if ((auxdata = (*docheck)(fsp)) == 0) +				continue; +			if (preen == 0 || passno == 1 && fsp->fs_passno == 1) { +				if (name = blockcheck(fsp->fs_spec)) { +					if (sumstatus = (*chkit)(name, +					    fsp->fs_file, auxdata, 0)) +						return (sumstatus); +				} else if (preen) +					return (8); +			} else if (passno == 2 && fsp->fs_passno > 1) { +				if ((name = blockcheck(fsp->fs_spec)) == NULL) { +					fprintf(stderr, "BAD DISK NAME %s\n", +						fsp->fs_spec); +					sumstatus |= 8; +					continue; +				} +				addpart(name, fsp->fs_file, auxdata); +			} +		} +		if (preen == 0) +			return (0); +	} +	if (preen) { +		if (maxrun == 0) +			maxrun = ndisks; +		if (maxrun > ndisks) +			maxrun = ndisks; +		nextdisk = disks; +		for (passno = 0; passno < maxrun; ++passno) { +			while (ret = startdisk(nextdisk, chkit) && nrun > 0) +				sleep(10); +			if (ret) +				return (ret); +			nextdisk = nextdisk->next; +		} +		while ((pid = wait(&status)) != -1) { +			for (dk = disks; dk; dk = dk->next) +				if (dk->pid == pid) +					break; +			if (dk == 0) { +				printf("Unknown pid %d\n", pid); +				continue; +			} +			if (WIFEXITED(status)) +				retcode = WEXITSTATUS(status); +			else +				retcode = 0; +			if (WIFSIGNALED(status)) { +				printf("%s (%s): EXITED WITH SIGNAL %d\n", +					dk->part->name, dk->part->fsname, +					WTERMSIG(status)); +				retcode = 8; +			} +			if (retcode != 0) { +				sumstatus |= retcode; +				*badnext = dk->part; +				badnext = &dk->part->next; +				dk->part = dk->part->next; +				*badnext = NULL; +			} else +				dk->part = dk->part->next; +			dk->pid = 0; +			nrun--; +			if (dk->part == NULL) +				ndisks--; + +			if (nextdisk == NULL) { +				if (dk->part) { +					while (ret = startdisk(dk, chkit) && +					    nrun > 0) +						sleep(10); +					if (ret) +						return (ret); +				} +			} else if (nrun < maxrun && nrun < ndisks) { +				for ( ;; ) { +					if ((nextdisk = nextdisk->next) == NULL) +						nextdisk = disks; +					if (nextdisk->part != NULL && +					    nextdisk->pid == 0) +						break; +				} +				while (ret = startdisk(nextdisk, chkit) && +				    nrun > 0) +					sleep(10); +				if (ret) +					return (ret); +			} +		} +	} +	if (sumstatus) { +		if (badlist == 0) +			return (sumstatus); +		fprintf(stderr, "THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t", +			badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:"); +		for (pt = badlist; pt; pt = pt->next) +			fprintf(stderr, "%s (%s)%s", pt->name, pt->fsname, +			    pt->next ? ", " : "\n"); +		return (sumstatus); +	} +	(void)endfsent(); +	return (0); +} + +struct disk * +finddisk(name) +	char *name; +{ +	register struct disk *dk, **dkp; +	register char *p; +	size_t len; + +	for (p = name + strlen(name) - 1; p >= name; --p) +		if (isdigit(*p)) { +			len = p - name + 1; +			break; +		} +	if (p < name) +		len = strlen(name); + +	for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) { +		if (strncmp(dk->name, name, len) == 0 && +		    dk->name[len] == 0) +			return (dk); +	} +	if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL) { +		fprintf(stderr, "out of memory"); +		exit (8); +	} +	dk = *dkp; +	if ((dk->name = malloc(len + 1)) == NULL) { +		fprintf(stderr, "out of memory"); +		exit (8); +	} +	(void)strncpy(dk->name, name, len); +	dk->name[len] = '\0'; +	dk->part = NULL; +	dk->next = NULL; +	dk->pid = 0; +	ndisks++; +	return (dk); +} + +addpart(name, fsname, auxdata) +	char *name, *fsname; +	long auxdata; +{ +	struct disk *dk = finddisk(name); +	register struct part *pt, **ppt = &dk->part; + +	for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next) +		if (strcmp(pt->name, name) == 0) { +			printf("%s in fstab more than once!\n", name); +			return; +		} +	if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL) { +		fprintf(stderr, "out of memory"); +		exit (8); +	} +	pt = *ppt; +	if ((pt->name = malloc(strlen(name) + 1)) == NULL) { +		fprintf(stderr, "out of memory"); +		exit (8); +	} +	(void)strcpy(pt->name, name); +	if ((pt->fsname = malloc(strlen(fsname) + 1)) == NULL) { +		fprintf(stderr, "out of memory"); +		exit (8); +	} +	(void)strcpy(pt->fsname, fsname); +	pt->next = NULL; +	pt->auxdata = auxdata; +} + +startdisk(dk, checkit) +	register struct disk *dk; +	int (*checkit)(); +{ +	register struct part *pt = dk->part; + +	dk->pid = fork(); +	if (dk->pid < 0) { +		perror("fork"); +		return (8); +	} +	if (dk->pid == 0) +		exit((*checkit)(pt->name, pt->fsname, pt->auxdata, 1)); +	nrun++; +	return (0); +} + +char * +blockcheck(name) +	char *name; +{ +	struct stat stslash, stblock, stchar; +	char *raw; +	int retried = 0; + +	hotroot = 0; +	if (stat("/", &stslash) < 0) { +		perror("/"); +		printf("Can't stat root\n"); +		return (0); +	} +retry: +	if (stat(name, &stblock) < 0) { +		perror(name); +		printf("Can't stat %s\n", name); +		return (0); +	} +	if ((stblock.st_mode & S_IFMT) == S_IFBLK) { +		if (stslash.st_dev == stblock.st_rdev) +			hotroot++; +		raw = rawname(name); +		if (stat(raw, &stchar) < 0) { +			perror(raw); +			printf("Can't stat %s\n", raw); +			return (name); +		} +		if ((stchar.st_mode & S_IFMT) == S_IFCHR) { +			return (raw); +		} else { +			printf("%s is not a character device\n", raw); +			return (name); +		} +	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { +		name = unrawname(name); +		retried++; +		goto retry; +	} +	printf("Can't make sense out of name %s\n", name); +	return (0); +} + +char * +unrawname(name) +	char *name; +{ +	char *dp; +	struct stat stb; + +	if ((dp = rindex(name, '/')) == 0) +		return (name); +	if (stat(name, &stb) < 0) +		return (name); +	if ((stb.st_mode & S_IFMT) != S_IFCHR) +		return (name); +	if (dp[1] != 'r') +		return (name); +	(void)strcpy(&dp[1], &dp[2]); +	return (name); +} + +char * +rawname(name) +	char *name; +{ +	static char rawbuf[32]; +	char *dp; + +	if ((dp = rindex(name, '/')) == 0) +		return (0); +	*dp = 0; +	(void)strcpy(rawbuf, name); +	*dp = '/'; +	(void)strcat(rawbuf, "/r"); +	(void)strcat(rawbuf, &dp[1]); +	return (rawbuf); +} diff --git a/sbin/fsck/setup.c b/sbin/fsck/setup.c new file mode 100644 index 000000000000..a32b23cc294d --- /dev/null +++ b/sbin/fsck/setup.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)setup.c	8.2 (Berkeley) 2/21/94"; +#endif /* not lint */ + +#define DKTYPENAMES +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +#include <sys/file.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "fsck.h" + +struct bufarea asblk; +#define altsblock (*asblk.b_un.b_fs) +#define POWEROF2(num)	(((num) & ((num) - 1)) == 0) + +struct	disklabel *getdisklabel(); + +setup(dev) +	char *dev; +{ +	long cg, size, asked, i, j; +	long bmapsize; +	struct disklabel *lp; +	off_t sizepb; +	struct stat statb; +	struct fs proto; + +	havesb = 0; +	fswritefd = -1; +	if (stat(dev, &statb) < 0) { +		printf("Can't stat %s: %s\n", dev, strerror(errno)); +		return (0); +	} +	if ((statb.st_mode & S_IFMT) != S_IFCHR) { +		pfatal("%s is not a character device", dev); +		if (reply("CONTINUE") == 0) +			return (0); +	} +	if ((fsreadfd = open(dev, O_RDONLY)) < 0) { +		printf("Can't open %s: %s\n", dev, strerror(errno)); +		return (0); +	} +	if (preen == 0) +		printf("** %s", dev); +	if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) { +		fswritefd = -1; +		if (preen) +			pfatal("NO WRITE ACCESS"); +		printf(" (NO WRITE)"); +	} +	if (preen == 0) +		printf("\n"); +	fsmodified = 0; +	lfdir = 0; +	initbarea(&sblk); +	initbarea(&asblk); +	sblk.b_un.b_buf = malloc(SBSIZE); +	asblk.b_un.b_buf = malloc(SBSIZE); +	if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) +		errexit("cannot allocate space for superblock\n"); +	if (lp = getdisklabel((char *)NULL, fsreadfd)) +		dev_bsize = secsize = lp->d_secsize; +	else +		dev_bsize = secsize = DEV_BSIZE; +	/* +	 * Read in the superblock, looking for alternates if necessary +	 */ +	if (readsb(1) == 0) { +		if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) +			return(0); +		if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) +			return (0); +		for (cg = 0; cg < proto.fs_ncg; cg++) { +			bflag = fsbtodb(&proto, cgsblock(&proto, cg)); +			if (readsb(0) != 0) +				break; +		} +		if (cg >= proto.fs_ncg) { +			printf("%s %s\n%s %s\n%s %s\n", +				"SEARCH FOR ALTERNATE SUPER-BLOCK", +				"FAILED. YOU MUST USE THE", +				"-b OPTION TO FSCK TO SPECIFY THE", +				"LOCATION OF AN ALTERNATE", +				"SUPER-BLOCK TO SUPPLY NEEDED", +				"INFORMATION; SEE fsck(8)."); +			return(0); +		} +		pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); +	} +	maxfsblock = sblock.fs_size; +	maxino = sblock.fs_ncg * sblock.fs_ipg; +	/* +	 * Check and potentially fix certain fields in the super block. +	 */ +	if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { +		pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); +		if (reply("SET TO DEFAULT") == 1) { +			sblock.fs_optim = FS_OPTTIME; +			sbdirty(); +		} +	} +	if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { +		pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", +			sblock.fs_minfree); +		if (reply("SET TO DEFAULT") == 1) { +			sblock.fs_minfree = 10; +			sbdirty(); +		} +	} +	if (sblock.fs_interleave < 1 ||  +	    sblock.fs_interleave > sblock.fs_nsect) { +		pwarn("IMPOSSIBLE INTERLEAVE=%d IN SUPERBLOCK", +			sblock.fs_interleave); +		sblock.fs_interleave = 1; +		if (preen) +			printf(" (FIXED)\n"); +		if (preen || reply("SET TO DEFAULT") == 1) { +			sbdirty(); +			dirty(&asblk); +		} +	} +	if (sblock.fs_npsect < sblock.fs_nsect ||  +	    sblock.fs_npsect > sblock.fs_nsect*2) { +		pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK", +			sblock.fs_npsect); +		sblock.fs_npsect = sblock.fs_nsect; +		if (preen) +			printf(" (FIXED)\n"); +		if (preen || reply("SET TO DEFAULT") == 1) { +			sbdirty(); +			dirty(&asblk); +		} +	} +	if (sblock.fs_inodefmt >= FS_44INODEFMT) { +		newinofmt = 1; +	} else { +		sblock.fs_qbmask = ~sblock.fs_bmask; +		sblock.fs_qfmask = ~sblock.fs_fmask; +		newinofmt = 0; +	} +	/* +	 * Convert to new inode format. +	 */ +	if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) { +		if (preen) +			pwarn("CONVERTING TO NEW INODE FORMAT\n"); +		else if (!reply("CONVERT TO NEW INODE FORMAT")) +			return(0); +		doinglevel2++; +		sblock.fs_inodefmt = FS_44INODEFMT; +		sizepb = sblock.fs_bsize; +		sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1; +		for (i = 0; i < NIADDR; i++) { +			sizepb *= NINDIR(&sblock); +			sblock.fs_maxfilesize += sizepb; +		} +		sblock.fs_maxsymlinklen = MAXSYMLINKLEN; +		sblock.fs_qbmask = ~sblock.fs_bmask; +		sblock.fs_qfmask = ~sblock.fs_fmask; +		sbdirty(); +		dirty(&asblk); +	} +	/* +	 * Convert to new cylinder group format. +	 */ +	if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) { +		if (preen) +			pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n"); +		else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT")) +			return(0); +		doinglevel1++; +		sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT; +		sblock.fs_nrpos = 8; +		sblock.fs_postbloff = +		    (char *)(&sblock.fs_opostbl[0][0]) - +		    (char *)(&sblock.fs_link); +		sblock.fs_rotbloff = &sblock.fs_space[0] - +		    (u_char *)(&sblock.fs_link); +		sblock.fs_cgsize = +			fragroundup(&sblock, CGSIZE(&sblock)); +		sbdirty(); +		dirty(&asblk); +	} +	if (asblk.b_dirty) { +		bcopy((char *)&sblock, (char *)&altsblock, +			(size_t)sblock.fs_sbsize); +		flush(fswritefd, &asblk); +	} +	/* +	 * read in the summary info. +	 */ +	asked = 0; +	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { +		size = sblock.fs_cssize - i < sblock.fs_bsize ? +		    sblock.fs_cssize - i : sblock.fs_bsize; +		sblock.fs_csp[j] = (struct csum *)calloc(1, (unsigned)size); +		if (bread(fsreadfd, (char *)sblock.fs_csp[j], +		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), +		    size) != 0 && !asked) { +			pfatal("BAD SUMMARY INFORMATION"); +			if (reply("CONTINUE") == 0) +				errexit(""); +			asked++; +		} +	} +	/* +	 * allocate and initialize the necessary maps +	 */ +	bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(short)); +	blockmap = calloc((unsigned)bmapsize, sizeof (char)); +	if (blockmap == NULL) { +		printf("cannot alloc %u bytes for blockmap\n", +		    (unsigned)bmapsize); +		goto badsb; +	} +	statemap = calloc((unsigned)(maxino + 1), sizeof(char)); +	if (statemap == NULL) { +		printf("cannot alloc %u bytes for statemap\n", +		    (unsigned)(maxino + 1)); +		goto badsb; +	} +	typemap = calloc((unsigned)(maxino + 1), sizeof(char)); +	if (typemap == NULL) { +		printf("cannot alloc %u bytes for typemap\n", +		    (unsigned)(maxino + 1)); +		goto badsb; +	} +	lncntp = (short *)calloc((unsigned)(maxino + 1), sizeof(short)); +	if (lncntp == NULL) { +		printf("cannot alloc %u bytes for lncntp\n",  +		    (unsigned)(maxino + 1) * sizeof(short)); +		goto badsb; +	} +	numdirs = sblock.fs_cstotal.cs_ndir; +	inplast = 0; +	listmax = numdirs + 10; +	inpsort = (struct inoinfo **)calloc((unsigned)listmax, +	    sizeof(struct inoinfo *)); +	inphead = (struct inoinfo **)calloc((unsigned)numdirs, +	    sizeof(struct inoinfo *)); +	if (inpsort == NULL || inphead == NULL) { +		printf("cannot alloc %u bytes for inphead\n",  +		    (unsigned)numdirs * sizeof(struct inoinfo *)); +		goto badsb; +	} +	bufinit(); +	return (1); + +badsb: +	ckfini(); +	return (0); +} + +/* + * Read in the super block and its summary info. + */ +readsb(listerr) +	int listerr; +{ +	daddr_t super = bflag ? bflag : SBOFF / dev_bsize; + +	if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0) +		return (0); +	sblk.b_bno = super; +	sblk.b_size = SBSIZE; +	/* +	 * run a few consistency checks of the super block +	 */ +	if (sblock.fs_magic != FS_MAGIC) +		{ badsb(listerr, "MAGIC NUMBER WRONG"); return (0); } +	if (sblock.fs_ncg < 1) +		{ badsb(listerr, "NCG OUT OF RANGE"); return (0); } +	if (sblock.fs_cpg < 1) +		{ badsb(listerr, "CPG OUT OF RANGE"); return (0); } +	if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || +	    (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) +		{ badsb(listerr, "NCYL LESS THAN NCG*CPG"); return (0); } +	if (sblock.fs_sbsize > SBSIZE) +		{ badsb(listerr, "SIZE PREPOSTEROUSLY LARGE"); return (0); } +	/* +	 * Compute block size that the filesystem is based on, +	 * according to fsbtodb, and adjust superblock block number +	 * so we can tell if this is an alternate later. +	 */ +	super *= dev_bsize; +	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); +	sblk.b_bno = super / dev_bsize; +	if (bflag) { +		havesb = 1; +		return (1); +	} +	/* +	 * Set all possible fields that could differ, then do check +	 * of whole super block against an alternate super block. +	 * When an alternate super-block is specified this check is skipped. +	 */ +	getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); +	if (asblk.b_errs) +		return (0); +	altsblock.fs_link = sblock.fs_link; +	altsblock.fs_rlink = sblock.fs_rlink; +	altsblock.fs_time = sblock.fs_time; +	altsblock.fs_cstotal = sblock.fs_cstotal; +	altsblock.fs_cgrotor = sblock.fs_cgrotor; +	altsblock.fs_fmod = sblock.fs_fmod; +	altsblock.fs_clean = sblock.fs_clean; +	altsblock.fs_ronly = sblock.fs_ronly; +	altsblock.fs_flags = sblock.fs_flags; +	altsblock.fs_maxcontig = sblock.fs_maxcontig; +	altsblock.fs_minfree = sblock.fs_minfree; +	altsblock.fs_optim = sblock.fs_optim; +	altsblock.fs_rotdelay = sblock.fs_rotdelay; +	altsblock.fs_maxbpg = sblock.fs_maxbpg; +	bcopy((char *)sblock.fs_csp, (char *)altsblock.fs_csp, +		sizeof sblock.fs_csp); +	bcopy((char *)sblock.fs_fsmnt, (char *)altsblock.fs_fsmnt, +		sizeof sblock.fs_fsmnt); +	bcopy((char *)sblock.fs_sparecon, (char *)altsblock.fs_sparecon, +		sizeof sblock.fs_sparecon); +	/* +	 * The following should not have to be copied. +	 */ +	altsblock.fs_fsbtodb = sblock.fs_fsbtodb; +	altsblock.fs_interleave = sblock.fs_interleave; +	altsblock.fs_npsect = sblock.fs_npsect; +	altsblock.fs_nrpos = sblock.fs_nrpos; +	altsblock.fs_qbmask = sblock.fs_qbmask; +	altsblock.fs_qfmask = sblock.fs_qfmask; +	altsblock.fs_state = sblock.fs_state; +	altsblock.fs_maxfilesize = sblock.fs_maxfilesize; +	if (bcmp((char *)&sblock, (char *)&altsblock, (int)sblock.fs_sbsize)) { +		badsb(listerr, +		"VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN FIRST ALTERNATE"); +		return (0); +	} +	havesb = 1; +	return (1); +} + +badsb(listerr, s) +	int listerr; +	char *s; +{ + +	if (!listerr) +		return; +	if (preen) +		printf("%s: ", cdevname); +	pfatal("BAD SUPER BLOCK: %s\n", s); +} + +/* + * Calculate a prototype superblock based on information in the disk label. + * When done the cgsblock macro can be calculated and the fs_ncg field + * can be used. Do NOT attempt to use other macros without verifying that + * their needed information is available! + */ +calcsb(dev, devfd, fs) +	char *dev; +	int devfd; +	register struct fs *fs; +{ +	register struct disklabel *lp; +	register struct partition *pp; +	register char *cp; +	int i; + +	cp = index(dev, '\0') - 1; +	if (cp == (char *)-1 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp)) { +		pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev); +		return (0); +	} +	lp = getdisklabel(dev, devfd); +	if (isdigit(*cp)) +		pp = &lp->d_partitions[0]; +	else +		pp = &lp->d_partitions[*cp - 'a']; +	if (pp->p_fstype != FS_BSDFFS) { +		pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n", +			dev, pp->p_fstype < FSMAXTYPES ? +			fstypenames[pp->p_fstype] : "unknown"); +		return (0); +	} +	bzero((char *)fs, sizeof(struct fs)); +	fs->fs_fsize = pp->p_fsize; +	fs->fs_frag = pp->p_frag; +	fs->fs_cpg = pp->p_cpg; +	fs->fs_size = pp->p_size; +	fs->fs_ntrak = lp->d_ntracks; +	fs->fs_nsect = lp->d_nsectors; +	fs->fs_spc = lp->d_secpercyl; +	fs->fs_nspf = fs->fs_fsize / lp->d_secsize; +	fs->fs_sblkno = roundup( +		howmany(lp->d_bbsize + lp->d_sbsize, fs->fs_fsize), +		fs->fs_frag); +	fs->fs_cgmask = 0xffffffff; +	for (i = fs->fs_ntrak; i > 1; i >>= 1) +		fs->fs_cgmask <<= 1; +	if (!POWEROF2(fs->fs_ntrak)) +		fs->fs_cgmask <<= 1; +	fs->fs_cgoffset = roundup( +		howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag); +	fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs); +	fs->fs_ncg = howmany(fs->fs_size / fs->fs_spc, fs->fs_cpg); +	for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1) +		fs->fs_fsbtodb++; +	dev_bsize = lp->d_secsize; +	return (1); +} + +struct disklabel * +getdisklabel(s, fd) +	char *s; +	int	fd; +{ +	static struct disklabel lab; + +	if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { +		if (s == NULL) +			return ((struct disklabel *)NULL); +		pwarn("ioctl (GCINFO): %s\n", strerror(errno)); +		errexit("%s: can't read disk label\n", s); +	} +	return (&lab); +} diff --git a/sbin/fsck/utilities.c b/sbin/fsck/utilities.c new file mode 100644 index 000000000000..64a4cac19ab3 --- /dev/null +++ b/sbin/fsck/utilities.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1980, 1986, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utilities.c	8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "fsck.h" + +long	diskreads, totalreads;	/* Disk cache statistics */ + +ftypeok(dp) +	struct dinode *dp; +{ +	switch (dp->di_mode & IFMT) { + +	case IFDIR: +	case IFREG: +	case IFBLK: +	case IFCHR: +	case IFLNK: +	case IFSOCK: +	case IFIFO: +		return (1); + +	default: +		if (debug) +			printf("bad file type 0%o\n", dp->di_mode); +		return (0); +	} +} + +reply(question) +	char *question; +{ +	int persevere; +	char c; + +	if (preen) +		pfatal("INTERNAL ERROR: GOT TO reply()"); +	persevere = !strcmp(question, "CONTINUE"); +	printf("\n"); +	if (!persevere && (nflag || fswritefd < 0)) { +		printf("%s? no\n\n", question); +		return (0); +	} +	if (yflag || (persevere && nflag)) { +		printf("%s? yes\n\n", question); +		return (1); +	} +	do	{ +		printf("%s? [yn] ", question); +		(void) fflush(stdout); +		c = getc(stdin); +		while (c != '\n' && getc(stdin) != '\n') +			if (feof(stdin)) +				return (0); +	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); +	printf("\n"); +	if (c == 'y' || c == 'Y') +		return (1); +	return (0); +} + +/* + * Malloc buffers and set up cache. + */ +bufinit() +{ +	register struct bufarea *bp; +	long bufcnt, i; +	char *bufp; + +	pbp = pdirbp = (struct bufarea *)0; +	bufp = malloc((unsigned int)sblock.fs_bsize); +	if (bufp == 0) +		errexit("cannot allocate buffer pool\n"); +	cgblk.b_un.b_buf = bufp; +	initbarea(&cgblk); +	bufhead.b_next = bufhead.b_prev = &bufhead; +	bufcnt = MAXBUFSPACE / sblock.fs_bsize; +	if (bufcnt < MINBUFS) +		bufcnt = MINBUFS; +	for (i = 0; i < bufcnt; i++) { +		bp = (struct bufarea *)malloc(sizeof(struct bufarea)); +		bufp = malloc((unsigned int)sblock.fs_bsize); +		if (bp == NULL || bufp == NULL) { +			if (i >= MINBUFS) +				break; +			errexit("cannot allocate buffer pool\n"); +		} +		bp->b_un.b_buf = bufp; +		bp->b_prev = &bufhead; +		bp->b_next = bufhead.b_next; +		bufhead.b_next->b_prev = bp; +		bufhead.b_next = bp; +		initbarea(bp); +	} +	bufhead.b_size = i;	/* save number of buffers */ +} + +/* + * Manage a cache of directory blocks. + */ +struct bufarea * +getdatablk(blkno, size) +	daddr_t blkno; +	long size; +{ +	register struct bufarea *bp; + +	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) +		if (bp->b_bno == fsbtodb(&sblock, blkno)) +			goto foundit; +	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) +		if ((bp->b_flags & B_INUSE) == 0) +			break; +	if (bp == &bufhead) +		errexit("deadlocked buffer pool\n"); +	getblk(bp, blkno, size); +	/* fall through */ +foundit: +	totalreads++; +	bp->b_prev->b_next = bp->b_next; +	bp->b_next->b_prev = bp->b_prev; +	bp->b_prev = &bufhead; +	bp->b_next = bufhead.b_next; +	bufhead.b_next->b_prev = bp; +	bufhead.b_next = bp; +	bp->b_flags |= B_INUSE; +	return (bp); +} + +void +getblk(bp, blk, size) +	register struct bufarea *bp; +	daddr_t blk; +	long size; +{ +	daddr_t dblk; + +	dblk = fsbtodb(&sblock, blk); +	if (bp->b_bno != dblk) { +		flush(fswritefd, bp); +		diskreads++; +		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); +		bp->b_bno = dblk; +		bp->b_size = size; +	} +} + +flush(fd, bp) +	int fd; +	register struct bufarea *bp; +{ +	register int i, j; + +	if (!bp->b_dirty) +		return; +	if (bp->b_errs != 0) +		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n", +		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ", +		    bp->b_bno); +	bp->b_dirty = 0; +	bp->b_errs = 0; +	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); +	if (bp != &sblk) +		return; +	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { +		bwrite(fswritefd, (char *)sblock.fs_csp[j], +		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), +		    sblock.fs_cssize - i < sblock.fs_bsize ? +		    sblock.fs_cssize - i : sblock.fs_bsize); +	} +} + +rwerror(mesg, blk) +	char *mesg; +	daddr_t blk; +{ + +	if (preen == 0) +		printf("\n"); +	pfatal("CANNOT %s: BLK %ld", mesg, blk); +	if (reply("CONTINUE") == 0) +		errexit("Program terminated\n"); +} + +ckfini() +{ +	register struct bufarea *bp, *nbp; +	int cnt = 0; + +	if (fswritefd < 0) { +		(void)close(fsreadfd); +		return; +	} +	flush(fswritefd, &sblk); +	if (havesb && sblk.b_bno != SBOFF / dev_bsize && +	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) { +		sblk.b_bno = SBOFF / dev_bsize; +		sbdirty(); +		flush(fswritefd, &sblk); +	} +	flush(fswritefd, &cgblk); +	free(cgblk.b_un.b_buf); +	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { +		cnt++; +		flush(fswritefd, bp); +		nbp = bp->b_prev; +		free(bp->b_un.b_buf); +		free((char *)bp); +	} +	if (bufhead.b_size != cnt) +		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); +	pbp = pdirbp = (struct bufarea *)0; +	if (debug) +		printf("cache missed %ld of %ld (%d%%)\n", diskreads, +		    totalreads, (int)(diskreads * 100 / totalreads)); +	(void)close(fsreadfd); +	(void)close(fswritefd); +} + +bread(fd, buf, blk, size) +	int fd; +	char *buf; +	daddr_t blk; +	long size; +{ +	char *cp; +	int i, errs; +	off_t offset; + +	offset = blk; +	offset *= dev_bsize; +	if (lseek(fd, offset, 0) < 0) +		rwerror("SEEK", blk); +	else if (read(fd, buf, (int)size) == size) +		return (0); +	rwerror("READ", blk); +	if (lseek(fd, offset, 0) < 0) +		rwerror("SEEK", blk); +	errs = 0; +	bzero(buf, (size_t)size); +	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:"); +	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) { +		if (read(fd, cp, (int)secsize) != secsize) { +			(void)lseek(fd, offset + i + secsize, 0); +			if (secsize != dev_bsize && dev_bsize != 1) +				printf(" %ld (%ld),", +				    (blk * dev_bsize + i) / secsize, +				    blk + i / dev_bsize); +			else +				printf(" %ld,", blk + i / dev_bsize); +			errs++; +		} +	} +	printf("\n"); +	return (errs); +} + +bwrite(fd, buf, blk, size) +	int fd; +	char *buf; +	daddr_t blk; +	long size; +{ +	int i; +	char *cp; +	off_t offset; + +	if (fd < 0) +		return; +	offset = blk; +	offset *= dev_bsize; +	if (lseek(fd, offset, 0) < 0) +		rwerror("SEEK", blk); +	else if (write(fd, buf, (int)size) == size) { +		fsmodified = 1; +		return; +	} +	rwerror("WRITE", blk); +	if (lseek(fd, offset, 0) < 0) +		rwerror("SEEK", blk); +	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); +	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize) +		if (write(fd, cp, (int)dev_bsize) != dev_bsize) { +			(void)lseek(fd, offset + i + dev_bsize, 0); +			printf(" %ld,", blk + i / dev_bsize); +		} +	printf("\n"); +	return; +} + +/* + * allocate a data block with the specified number of fragments + */ +allocblk(frags) +	long frags; +{ +	register int i, j, k; + +	if (frags <= 0 || frags > sblock.fs_frag) +		return (0); +	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { +		for (j = 0; j <= sblock.fs_frag - frags; j++) { +			if (testbmap(i + j)) +				continue; +			for (k = 1; k < frags; k++) +				if (testbmap(i + j + k)) +					break; +			if (k < frags) { +				j += k; +				continue; +			} +			for (k = 0; k < frags; k++) +				setbmap(i + j + k); +			n_blks += frags; +			return (i + j); +		} +	} +	return (0); +} + +/* + * Free a previously allocated block + */ +freeblk(blkno, frags) +	daddr_t blkno; +	long frags; +{ +	struct inodesc idesc; + +	idesc.id_blkno = blkno; +	idesc.id_numfrags = frags; +	(void)pass4check(&idesc); +} + +/* + * Find a pathname + */ +getpathname(namebuf, curdir, ino) +	char *namebuf; +	ino_t curdir, ino; +{ +	int len; +	register char *cp; +	struct inodesc idesc; +	static int busy = 0; +	extern int findname(); + +	if (curdir == ino && ino == ROOTINO) { +		(void)strcpy(namebuf, "/"); +		return; +	} +	if (busy || +	    (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) { +		(void)strcpy(namebuf, "?"); +		return; +	} +	busy = 1; +	bzero((char *)&idesc, sizeof(struct inodesc)); +	idesc.id_type = DATA; +	idesc.id_fix = IGNORE; +	cp = &namebuf[MAXPATHLEN - 1]; +	*cp = '\0'; +	if (curdir != ino) { +		idesc.id_parent = curdir; +		goto namelookup; +	} +	while (ino != ROOTINO) { +		idesc.id_number = ino; +		idesc.id_func = findino; +		idesc.id_name = ".."; +		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) +			break; +	namelookup: +		idesc.id_number = idesc.id_parent; +		idesc.id_parent = ino; +		idesc.id_func = findname; +		idesc.id_name = namebuf; +		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) +			break; +		len = strlen(namebuf); +		cp -= len; +		bcopy(namebuf, cp, (size_t)len); +		*--cp = '/'; +		if (cp < &namebuf[MAXNAMLEN]) +			break; +		ino = idesc.id_number; +	} +	busy = 0; +	if (ino != ROOTINO) +		*--cp = '?'; +	bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp)); +} + +void +catch() +{ +	if (!doinglevel2) +		ckfini(); +	exit(12); +} + +/* + * When preening, allow a single quit to signal + * a special exit after filesystem checks complete + * so that reboot sequence may be interrupted. + */ +void +catchquit() +{ +	extern returntosingle; + +	printf("returning to single-user after filesystem check\n"); +	returntosingle = 1; +	(void)signal(SIGQUIT, SIG_DFL); +} + +/* + * Ignore a single quit signal; wait and flush just in case. + * Used by child processes in preen. + */ +void +voidquit() +{ + +	sleep(1); +	(void)signal(SIGQUIT, SIG_IGN); +	(void)signal(SIGQUIT, SIG_DFL); +} + +/* + * determine whether an inode should be fixed. + */ +dofix(idesc, msg) +	register struct inodesc *idesc; +	char *msg; +{ + +	switch (idesc->id_fix) { + +	case DONTKNOW: +		if (idesc->id_type == DATA) +			direrror(idesc->id_number, msg); +		else +			pwarn(msg); +		if (preen) { +			printf(" (SALVAGED)\n"); +			idesc->id_fix = FIX; +			return (ALTERED); +		} +		if (reply("SALVAGE") == 0) { +			idesc->id_fix = NOFIX; +			return (0); +		} +		idesc->id_fix = FIX; +		return (ALTERED); + +	case FIX: +		return (ALTERED); + +	case NOFIX: +	case IGNORE: +		return (0); + +	default: +		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); +	} +	/* NOTREACHED */ +} + +/* VARARGS1 */ +errexit(s1, s2, s3, s4) +	char *s1; +{ +	printf(s1, s2, s3, s4); +	exit(8); +} + +/* + * An unexpected inconsistency occured. + * Die if preening, otherwise just print message and continue. + */ +/* VARARGS1 */ +pfatal(s, a1, a2, a3) +	char *s; +{ + +	if (preen) { +		printf("%s: ", cdevname); +		printf(s, a1, a2, a3); +		printf("\n"); +		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", +			cdevname); +		exit(8); +	} +	printf(s, a1, a2, a3); +} + +/* + * Pwarn just prints a message when not preening, + * or a warning (preceded by filename) when preening. + */ +/* VARARGS1 */ +pwarn(s, a1, a2, a3, a4, a5, a6) +	char *s; +{ + +	if (preen) +		printf("%s: ", cdevname); +	printf(s, a1, a2, a3, a4, a5, a6); +} + +#ifndef lint +/* + * Stub for routines from kernel. + */ +panic(s) +	char *s; +{ + +	pfatal("INTERNAL INCONSISTENCY:"); +	errexit(s); +} +#endif | 
