Initial Spring 2016 commit.

This commit is contained in:
Geoffrey Challen
2015-12-23 00:50:04 +00:00
commit cafa9f5690
732 changed files with 92195 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
# Makefile for sfsck
TOP=../../..
.include "$(TOP)/mk/os161.config.mk"
PROG=sfsck
SRCS=\
main.c pass1.c pass2.c \
inode.c freemap.c sb.c \
sfs.c utils.c \
../mksfs/disk.c ../mksfs/support.c
CFLAGS+=-I../mksfs
HOST_CFLAGS+=-I../mksfs
BINDIR=/sbin
HOSTBINDIR=/hostbin
.include "$(TOP)/mk/os161.prog.mk"
.include "$(TOP)/mk/os161.hostprog.mk"

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 SFSCK_H
#define SFSCK_H
/*
* Compat definitions.
*/
/* get uint32_t and friends */
#include <stdint.h>
/* get the additional compat stuff shared with mksfs */
#include "support.h"
#ifdef HOST
/*
* OS/161 runs natively on a big-endian platform, so we can
* conveniently use the byteswapping functions for network byte order.
*/
#include <netinet/in.h> // for arpa/inet.h
#include <arpa/inet.h> // for ntohl
#include "hostcompat.h"
#define SWAP64(x) ntohll(x)
#define SWAP32(x) ntohl(x)
#define SWAP16(x) ntohs(x)
#else
#define SWAP64(x) (x)
#define SWAP32(x) (x)
#define SWAP16(x) (x)
#define NO_REALLOC
#endif /* HOST */
#endif /* SFSCK_H */

View File

@@ -0,0 +1,310 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013, 2014
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <sys/types.h> /* for CHAR_BIT */
#include <limits.h> /* also for CHAR_BIT */
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "utils.h"
#include "sfs.h"
#include "sb.h"
#include "freemap.h"
#include "main.h"
static unsigned long blocksinuse = 0;
static uint8_t *freemapdata;
static uint8_t *tofreedata;
/*
* Allocate space to keep track of the free block bitmap. This is
* called after the superblock is loaded so we can ask how big the
* volume is.
*/
void
freemap_setup(void)
{
size_t i, mapbytes;
uint32_t fsblocks, mapblocks;
fsblocks = sb_totalblocks();
mapblocks = sb_freemapblocks();
mapbytes = mapblocks * SFS_BLOCKSIZE;
freemapdata = domalloc(mapbytes * sizeof(uint8_t));
tofreedata = domalloc(mapbytes * sizeof(uint8_t));
for (i=0; i<mapbytes; i++) {
freemapdata[i] = tofreedata[i] = 0;
}
/* Mark off what's in the freemap but past the volume end. */
for (i=fsblocks; i < mapblocks*SFS_BITSPERBLOCK; i++) {
freemap_blockinuse(i, B_PASTEND, 0);
}
/* Mark the superblock block and the freemap blocks in use */
freemap_blockinuse(SFS_SUPER_BLOCK, B_SUPERBLOCK, 0);
for (i=0; i < mapblocks; i++) {
freemap_blockinuse(SFS_FREEMAP_START+i, B_FREEMAPBLOCK, i);
}
}
/*
* Return a string for a blockusage; used for printing errors.
*/
static
const char *
blockusagestr(blockusage_t how, uint32_t howdesc)
{
static char rv[256];
switch (how) {
case B_SUPERBLOCK:
return "superblock";
case B_FREEMAPBLOCK:
snprintf(rv, sizeof(rv), "freemap block %lu",
(unsigned long) howdesc);
break;
case B_INODE:
snprintf(rv, sizeof(rv), "inode %lu",
(unsigned long) howdesc);
break;
case B_IBLOCK:
snprintf(rv, sizeof(rv), "indirect block of inode %lu",
(unsigned long) howdesc);
break;
case B_DIRDATA:
snprintf(rv, sizeof(rv), "directory data from inode %lu",
(unsigned long) howdesc);
break;
case B_DATA:
snprintf(rv, sizeof(rv), "file data from inode %lu",
(unsigned long) howdesc);
break;
case B_PASTEND:
return "past the end of the fs";
}
return rv;
}
/*
* Mark block BLOCK in use. HOW and HOWDESC describe how it was found
* to be in use, so we can print a useful message if it's wrong.
*
* FUTURE: this should not produce unrecoverable errors.
*/
void
freemap_blockinuse(uint32_t block, blockusage_t how, uint32_t howdesc)
{
unsigned index = block/8;
uint8_t mask = ((uint8_t)1)<<(block%8);
if (tofreedata[index] & mask) {
/* really using the block, don't free it */
tofreedata[index] &= ~mask;
}
if (freemapdata[index] & mask) {
warnx("Block %lu (used as %s) already in use! (NOT FIXED)",
(unsigned long) block, blockusagestr(how, howdesc));
setbadness(EXIT_UNRECOV);
}
freemapdata[index] |= mask;
if (how != B_PASTEND) {
blocksinuse++;
}
}
/*
* Mark a block free. This is specifically for blocks that we are
* freeing, that might be marked allocated in the on-disk freemap. If
* the block has been found in use, assume the reference that's in use
* is valid. This can be caused by freeing a block, reallocating it
* somewhere else and then dying without erasing all of the original
* usage on disk; most such cases will just show the block in use
* twice, which is (not) handled above, but it's possible for the
* original usage to be something we are dropping, e.g. if a truncate
* (to a nonzero length > INOMAX_D) got partially completed.
*/
void
freemap_blockfree(uint32_t block)
{
unsigned index = block/8;
uint8_t mask = ((uint8_t)1)<<(block%8);
if (tofreedata[index] & mask) {
/* already marked to free once, ignore */
return;
}
if (freemapdata[index] & mask) {
/* block is used elsewhere, ignore */
return;
}
tofreedata[index] |= mask;
}
/*
* Count the number of bits set.
*/
static
int
countbits(uint8_t val)
{
uint8_t x;
int ct=0;
for (x=1; x; x<<=1) {
if (val & x) ct++;
}
return ct;
}
/*
* Print a complaint about freemap bits being wrong.
*
* FREEMAPBLOCK is the block number within the freemap; BYTE is the
* byte offset within that block; VAL is the byte value; WHAT is a
* string indicating what happened.
*/
static
void
reportfreemap(uint32_t mapblock, uint32_t byte, uint8_t val, const char *what)
{
uint8_t x, y;
uint32_t blocknum;
for (x=1, y=0; x; x<<=1, y++) {
if (val & x) {
blocknum = mapblock*SFS_BITSPERBLOCK +
byte*CHAR_BIT + y;
warnx("Block %lu erroneously shown %s in freemap",
(unsigned long) blocknum, what);
}
}
}
/*
* Scan the freemap.
*
* This is called after (at the end of) pass 1, when we've recursively
* found all the reachable blocks and marked them.
*/
void
freemap_check(void)
{
uint8_t actual[SFS_BLOCKSIZE], *expected, *tofree, tmp;
uint32_t alloccount=0, freecount=0, i, j;
int bchanged;
uint32_t bitblocks;
bitblocks = sb_freemapblocks();
for (i=0; i<bitblocks; i++) {
sfs_readfreemapblock(i, actual);
expected = freemapdata + i*SFS_BLOCKSIZE;
tofree = tofreedata + i*SFS_BLOCKSIZE;
bchanged = 0;
for (j=0; j<SFS_BLOCKSIZE; j++) {
/* we shouldn't have blocks marked both ways */
assert((expected[j] & tofree[j])==0);
/* what's there is what should be there */
if (actual[j] == expected[j]) {
continue;
}
/* what's there is what should be there modulo frees */
if (actual[j] == (expected[j] | tofree[j])) {
actual[j] = expected[j];
bchanged = 1;
continue;
}
/* oops, it doesn't match... */
/* free the ones we're freeing (don't report these) */
actual[j] &= ~tofree[j];
/* are we short any? */
if ((actual[j] & expected[j]) != expected[j]) {
tmp = expected[j] & ~actual[j];
alloccount += countbits(tmp);
if (tmp != 0) {
reportfreemap(i, j, tmp, "free");
}
}
/* do we have any extra? */
if ((actual[j] & expected[j]) != actual[j]) {
tmp = actual[j] & ~expected[j];
freecount += countbits(tmp);
if (tmp != 0) {
reportfreemap(i, j, tmp, "allocated");
}
}
/* set it to what it should be */
actual[j] = expected[j];
bchanged = 1;
}
/* write the block back if necessary */
if (bchanged) {
sfs_writefreemapblock(i, actual);
}
}
if (alloccount > 0) {
warnx("%lu blocks erroneously shown free in freemap (fixed)",
(unsigned long) alloccount);
setbadness(EXIT_RECOV);
}
if (freecount > 0) {
warnx("%lu blocks erroneously shown used in freemap (fixed)",
(unsigned long) freecount);
setbadness(EXIT_RECOV);
}
}
/*
* Return the total number of blocks in use, which we count during
* pass 1.
*/
unsigned long
freemap_blocksused(void)
{
return blocksinuse;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 FREEMAP_H
#define FREEMAP_H
/*
* The freemap module accumulates information about the free block
* bitmap as other checks are made, and then uses that information
* to check and correct it.
*/
#include <stdint.h>
typedef enum {
B_SUPERBLOCK, /* Block that is the superblock */
B_FREEMAPBLOCK, /* Block used by free-block bitmap */
B_INODE, /* Block that is an inode */
B_IBLOCK, /* Indirect (or doubly-indirect etc.) block */
B_DIRDATA, /* Data block of a directory */
B_DATA, /* Data block */
B_PASTEND, /* Block off the end of the fs */
} blockusage_t;
/* Call this after loading the superblock but before doing any checks. */
void freemap_setup(void);
/* Call this to note that a block has been found in use. */
void freemap_blockinuse(uint32_t block, blockusage_t how, uint32_t howdesc);
/* Note that a block has been found where it should be dropped. */
void freemap_blockfree(uint32_t block);
/* Call this after all checks that call freemap_block{inuse,free}. */
void freemap_check(void);
/* Return the number of blocks in use. Valid after freemap_check(). */
unsigned long freemap_blocksused(void);
#endif /* FREEMAP_H */

View File

@@ -0,0 +1,159 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 IBMACROS_H
#define IBMACROS_H
/*
* Indirect block access macros
*
* These are macros for working with the direct and indirect block
* pointers in the inode. The scheme here supports a range of possible
* configurations, because sometimes adding large file support to SFS
* is part of an assignment; there is and should be no obligation to
* pick any particular layout, and we'd like sfsck to build and run
* seamlessly provided that the values declared in kern/sfs.h are
* correct.
*
* SFS_NDIRECT, SFS_NINDIRECT, SFS_NDINDIRECT, and SFS_NTINDIRECT
* should always be defined. If zero, no corresponding field is
* assumed to exist in the inode. If one, the field is assumed to
* be a single value and not an array. If greater than one, the
* field is assumed to be an array.
*/
#ifndef SFS_NDIRECT
#error "SFS_NDIRECT not defined"
#endif
#ifndef SFS_NINDIRECT
#error "SFS_NINDIRECT not defined"
#endif
#ifndef SFS_NDINDIRECT
#error "SFS_NDINDIRECT not defined"
#endif
#ifndef SFS_NTINDIRECT
#error "SFS_NTINDIRECT not defined"
#endif
/*
* For x in D, I, II, III (direct, indirect, 2x/3x indirect) we
* provide:
*
* NUM_x the number of blocks of this type
* GET_x retrieve the i'th block of this type
* SET_x lvalue for the i'th block of this type
* RANGE_x size of the block range mapped with one block of this type
* INOMAX_x maximum block number mapped by using this type in the inode
*
* It is important that the accessor macros (SET_x/GET_x) not refer to
* a nonexistent field of the inode in the case where there are zero
* blocks of that type, as that will lead to compile failure. Hence the
* lengthy definitions. So far I haven't thought any useful cpp hackery
* to make them more concise.
*/
/* numbers */
#define NUM_D SFS_NDIRECT
#define NUM_I SFS_NINDIRECT
#define NUM_II SFS_NDINDIRECT
#define NUM_III SFS_NTINDIRECT
/* blocks */
#if NUM_D == 0
#define GET_D(sfi, i) GET0_x(sfi, sfi_direct, i)
#define SET_D(sfi, i) SET0_x(sfi, sfi_direct, i)
#elif NUM_D == 1
#define GET_D(sfi, i) GET1_x(sfi, sfi_direct, i)
#define SET_D(sfi, i) SET1_x(sfi, sfi_direct, i)
#else
#define GET_D(sfi, i) GETN_x(sfi, sfi_direct, i)
#define SET_D(sfi, i) SETN_x(sfi, sfi_direct, i)
#endif
#if NUM_I == 0
#define GET_I(sfi, i) GET0_x(sfi, sfi_indirect, i)
#define SET_I(sfi, i) SET0_x(sfi, sfi_indirect, i)
#elif NUM_I == 1
#define GET_I(sfi, i) GET1_x(sfi, sfi_indirect, i)
#define SET_I(sfi, i) SET1_x(sfi, sfi_indirect, i)
#else
#define GET_I(sfi, i) GETN_x(sfi, sfi_indirect, i)
#define SET_I(sfi, i) SETN_x(sfi, sfi_indirect, i)
#endif
#if NUM_II == 0
#define GET_II(sfi, i) GET0_x(sfi, sfi_dindirect, i)
#define SET_II(sfi, i) SET0_x(sfi, sfi_dindirect, i)
#elif NUM_II == 1
#define GET_II(sfi, i) GET1_x(sfi, sfi_dindirect, i)
#define SET_II(sfi, i) SET1_x(sfi, sfi_dindirect, i)
#else
#define GET_II(sfi, i) GETN_x(sfi, sfi_dindirect, i)
#define SET_II(sfi, i) SETN_x(sfi, sfi_dindirect, i)
#endif
#if NUM_III == 0
#define GET_III(sfi, i) GET0_x(sfi, sfi_tindirect, i)
#define SET_III(sfi, i) SET0_x(sfi, sfi_tindirect, i)
#elif NUM_III == 1
#define GET_III(sfi, i) GET1_x(sfi, sfi_tindirect, i)
#define SET_III(sfi, i) SET1_x(sfi, sfi_tindirect, i)
#else
#define GET_III(sfi, i) GETN_x(sfi, sfi_tindirect, i)
#define SET_III(sfi, i) SETN_x(sfi, sfi_tindirect, i)
#endif
/* the generic forms of the block macros */
#define GET0_x(sfi, field, i) ((void)(i), (void)(sfi), 0)
#define GET1_x(sfi, field, i) ((void)(i), (sfi)->field)
#define GETN_x(sfi, field, i) ((sfi)->field[(i)])
#define SET0_x(sfi, field, i) (*((void)(i), (void)(sfi), (uint32_t *)NULL))
#define SET1_x(sfi, field, i) (*((void)(i), &(sfi)->field))
#define SETN_x(sfi, field, i) ((sfi)->field[(i)])
/* region sizes */
#define RANGE_D 1
#define RANGE_I (RANGE_D * SFS_DBPERIDB)
#define RANGE_II (RANGE_I * SFS_DBPERIDB)
#define RANGE_III (RANGE_II * SFS_DBPERIDB)
/* max blocks */
#define INOMAX_D NUM_D
#define INOMAX_I (INOMAX_D + SFS_DBPERIDB * NUM_I)
#define INOMAX_II (INOMAX_I + SFS_DBPERIDB * NUM_II)
#define INOMAX_III (INOMAX_II + SFS_DBPERIDB * NUM_III)
#endif /* IBMACROS_H */

266
userland/sbin/sfsck/inode.c Normal file
View File

@@ -0,0 +1,266 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "utils.h"
#include "sfs.h"
#include "freemap.h"
#include "inode.h"
#include "main.h"
/*
* Stuff we remember about inodes.
* FUTURE: should count the number of blocks allocated to this inode
*/
struct inodeinfo {
uint32_t ino;
uint32_t linkcount; /* files only */
int visited; /* dirs only */
int type;
};
/* Table of inodes found. */
static struct inodeinfo *inodes = NULL;
static unsigned ninodes = 0, maxinodes = 0;
/* Whether the table is sorted and can be looked up with binary search. */
static int inodes_sorted = 0;
////////////////////////////////////////////////////////////
// inode table ops
/*
* Add an entry to the inode table, realloc'ing it if needed.
*/
static
void
inode_addtable(uint32_t ino, int type)
{
unsigned newmax;
assert(ninodes <= maxinodes);
if (ninodes == maxinodes) {
newmax = maxinodes ? maxinodes * 2 : 4;
inodes = dorealloc(inodes, maxinodes * sizeof(inodes[0]),
newmax * sizeof(inodes[0]));
maxinodes = newmax;
}
inodes[ninodes].ino = ino;
inodes[ninodes].linkcount = 0;
inodes[ninodes].visited = 0;
inodes[ninodes].type = type;
ninodes++;
inodes_sorted = 0;
}
/*
* Compare function for inodes.
*/
static
int
inode_compare(const void *av, const void *bv)
{
const struct inodeinfo *a = av;
const struct inodeinfo *b = bv;
if (a->ino < b->ino) {
return -1;
}
if (a->ino > b->ino) {
return 1;
}
/*
* There should be no duplicates in the table! But C99 makes
* no guarantees about whether the implementation of qsort can
* ask us to compare an element to itself. Assert that this is
* what happened.
*/
assert(av == bv);
return 0;
}
/*
* After pass1, we sort the inode table for faster access.
*/
void
inode_sorttable(void)
{
qsort(inodes, ninodes, sizeof(inodes[0]), inode_compare);
inodes_sorted = 1;
}
/*
* Find an inode by binary search.
*
* This will error out if asked for an inode not in the table; that's
* not supposed to happen. (This might need to change; if we improve
* the handling of crosslinked directories as suggested in comments in
* pass2.c, we'll need to be able to ask if an inode number is valid
* and names a directory.)
*/
static
struct inodeinfo *
inode_find(uint32_t ino)
{
unsigned min, max, i;
assert(inodes_sorted);
assert(ninodes > 0);
min = 0;
max = ninodes;
while (1) {
assert(min <= max);
if (min == max) {
errx(EXIT_UNRECOV, "FATAL: inode %u wasn't found in my inode table", ino);
}
i = min + (max - min)/2;
if (inodes[i].ino < ino) {
min = i + 1;
}
else if (inodes[i].ino > ino) {
max = i;
}
else {
assert(inodes[i].ino == ino);
return &inodes[i];
}
}
/* NOTREACHED */
}
////////////////////////////////////////////////////////////
// inode ops
/*
* Add an inode; returns 1 if we've already seen it.
*
* Uses linear search because we only sort the table for faster access
* after all inodes have been added. In the FUTURE this could be
* changed to a better data structure.
*/
int
inode_add(uint32_t ino, int type)
{
unsigned i;
for (i=0; i<ninodes; i++) {
if (inodes[i].ino==ino) {
assert(inodes[i].linkcount == 0);
assert(inodes[i].type == type);
return 1;
}
}
inode_addtable(ino, type);
return 0;
}
/*
* Mark an inode (directories only, because that's all the caller
* does) visited. Returns nonzero if already visited.
*
* Note that there is no way to clear the visited flag for now because
* it's only used once (by pass2).
*/
int
inode_visitdir(uint32_t ino)
{
struct inodeinfo *inf;
inf = inode_find(ino);
assert(inf->type == SFS_TYPE_DIR);
assert(inf->linkcount == 0);
if (inf->visited) {
return 1;
}
inf->visited = 1;
return 0;
}
/*
* Count a link. Only for regular files because that's what the caller
* does. (And that, in turn, is because the link count of a directory
* is a local property.)
*/
void
inode_addlink(uint32_t ino)
{
struct inodeinfo *inf;
inf = inode_find(ino);
assert(inf->type == SFS_TYPE_FILE);
assert(inf->visited == 0);
inf->linkcount++;
}
/*
* Correct link counts. This is effectively pass3. (FUTURE: change the
* name accordingly.)
*/
void
inode_adjust_filelinks(void)
{
struct sfs_dinode sfi;
unsigned i;
for (i=0; i<ninodes; i++) {
if (inodes[i].type == SFS_TYPE_DIR) {
/* directory */
continue;
}
assert(inodes[i].type == SFS_TYPE_FILE);
/* because we've seen it, there must be at least one link */
assert(inodes[i].linkcount > 0);
sfs_readinode(inodes[i].ino, &sfi);
assert(sfi.sfi_type == SFS_TYPE_FILE);
if (sfi.sfi_linkcount != inodes[i].linkcount) {
warnx("File %lu link count %lu should be %lu (fixed)",
(unsigned long) inodes[i].ino,
(unsigned long) sfi.sfi_linkcount,
(unsigned long) inodes[i].linkcount);
sfi.sfi_linkcount = inodes[i].linkcount;
setbadness(EXIT_RECOV);
sfs_writeinode(inodes[i].ino, &sfi);
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 INODE_H
#define INODE_H
/*
* The inode module accumulates non-local information about files and
* directories as other checks are made, and then updates inodes
* accordingly after the other checks are done.
*/
/* Add an inode. Returns 1 if we've seen this inode before. */
int inode_add(uint32_t ino, int type);
/* Sort the inode table for faster lookup once all inode_add() done. */
void inode_sorttable(void);
/*
* Remember that we've seen a particular directory. Returns nonzero if
* we've seen this directory before, which means the directory is
* crosslinked. Requires inode_sorttable() first.
*/
int inode_visitdir(uint32_t ino);
/*
* Count a link to a regular file. (Not called for directories.)
* Requires inode_sorttable() first.
*/
void inode_addlink(uint32_t ino);
/*
* Correct the link counts of regular files, once all inode_addlink()
* done.
*/
void inode_adjust_filelinks(void);
#endif /* INODE_H */

118
userland/sbin/sfsck/main.c Normal file
View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <err.h>
#include "compat.h"
#include "disk.h"
#include "sfs.h"
#include "sb.h"
#include "freemap.h"
#include "inode.h"
#include "passes.h"
#include "main.h"
static int badness=0;
/*
* Update the badness state. (codes are in main.h)
*
* The badness state only gets worse, and is ultimately the process
* exit code.
*/
void
setbadness(int code)
{
if (badness < code) {
badness = code;
}
}
/*
* Main.
*/
int
main(int argc, char **argv)
{
#ifdef HOST
hostcompat_init(argc, argv);
#endif
/* FUTURE: add -n option */
if (argc!=2) {
errx(EXIT_USAGE, "Usage: sfsck device/diskfile");
}
opendisk(argv[1]);
sfs_setup();
sb_load();
sb_check();
freemap_setup();
printf("Phase 1 -- check blocks and sizes\n");
pass1();
freemap_check();
printf("Phase 2 -- check directory tree\n");
inode_sorttable();
pass2();
printf("Phase 3 -- check reference counts\n");
inode_adjust_filelinks();
closedisk();
warnx("%lu blocks used (of %lu); %lu directories; %lu files",
freemap_blocksused(), (unsigned long)sb_totalblocks(),
pass1_founddirs(), pass1_foundfiles());
switch (badness) {
case EXIT_USAGE:
case EXIT_FATAL:
default:
/* not supposed to happen here */
assert(0);
break;
case EXIT_UNRECOV:
warnx("WARNING - unrecoverable errors. Maybe try again?");
break;
case EXIT_RECOV:
warnx("Caution - filesystem modified. Run again for luck.");
break;
case EXIT_CLEAN:
break;
}
return badness;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 MAIN_H
#define MAIN_H
/*
* Levels of problems that we can encounter.
*
* setbadness() records the maximum level seen so far; this is
* the ultimate exit code of sfsck.
*/
#define EXIT_USAGE 4
#define EXIT_FATAL 3
#define EXIT_UNRECOV 2
#define EXIT_RECOV 1
#define EXIT_CLEAN 0
void setbadness(int code);
#endif /* MAIN_H */

496
userland/sbin/sfsck/pass1.c Normal file
View File

@@ -0,0 +1,496 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "disk.h"
#include "utils.h"
#include "ibmacros.h"
#include "sfs.h"
#include "sb.h"
#include "freemap.h"
#include "inode.h"
#include "passes.h"
#include "main.h"
static unsigned long count_dirs=0, count_files=0;
/*
* State for checking indirect blocks.
*/
struct ibstate {
uint32_t ino; /* inode we're doing (constant) */
uint32_t curfileblock; /* current block offset in the file */
uint32_t fileblocks; /* file size in blocks (constant) */
uint32_t volblocks; /* volume size in blocks (constant) */
unsigned pasteofcount; /* number of blocks found past eof */
blockusage_t usagetype; /* how to call freemap_blockinuse() */
};
/*
* Traverse an indirect block, recording blocks that are in use,
* dropping any entries that are past EOF, and clearing any entries
* that point outside the volume.
*
* XXX: this should be extended to be able to recover from crosslinked
* blocks. Currently it just complains in freemap.c and sets
* EXIT_UNRECOV.
*
* The traversal is recursive; the state is maintained in IBS (as
* described above). IENTRY is a pointer to the entry in the parent
* indirect block (or the inode) that names the block we're currently
* scanning. IECHANGEDP should be set to 1 if *IENTRY is changed.
* INDIRECTION is the indirection level of this block (1, 2, or 3).
*/
static
void
check_indirect_block(struct ibstate *ibs, uint32_t *ientry, int *iechangedp,
int indirection)
{
uint32_t entries[SFS_DBPERIDB];
uint32_t i, ct;
uint32_t coveredblocks;
int localchanged = 0;
int j;
if (*ientry > 0 && *ientry < ibs->volblocks) {
sfs_readindirect(*ientry, entries);
freemap_blockinuse(*ientry, B_IBLOCK, ibs->ino);
}
else {
if (*ientry >= ibs->volblocks) {
setbadness(EXIT_RECOV);
warnx("Inode %lu: indirect block pointer (level %d) "
"for block %lu outside of volume: %lu "
"(cleared)\n",
(unsigned long)ibs->ino, indirection,
(unsigned long)ibs->curfileblock,
(unsigned long)*ientry);
*ientry = 0;
*iechangedp = 1;
}
coveredblocks = 1;
for (j=0; j<indirection; j++) {
coveredblocks *= SFS_DBPERIDB;
}
ibs->curfileblock += coveredblocks;
return;
}
if (indirection > 1) {
for (i=0; i<SFS_DBPERIDB; i++) {
check_indirect_block(ibs, &entries[i], &localchanged,
indirection-1);
}
}
else {
assert(indirection==1);
for (i=0; i<SFS_DBPERIDB; i++) {
if (entries[i] >= ibs->volblocks) {
setbadness(EXIT_RECOV);
warnx("Inode %lu: direct block pointer for "
"block %lu outside of volume: %lu "
"(cleared)\n",
(unsigned long)ibs->ino,
(unsigned long)ibs->curfileblock,
(unsigned long)entries[i]);
entries[i] = 0;
localchanged = 1;
}
else if (entries[i] != 0) {
if (ibs->curfileblock < ibs->fileblocks) {
freemap_blockinuse(entries[i],
ibs->usagetype,
ibs->ino);
}
else {
setbadness(EXIT_RECOV);
ibs->pasteofcount++;
freemap_blockfree(entries[i]);
entries[i] = 0;
localchanged = 1;
}
}
ibs->curfileblock++;
}
}
ct=0;
for (i=ct=0; i<SFS_DBPERIDB; i++) {
if (entries[i]!=0) ct++;
}
if (ct==0) {
if (*ientry != 0) {
setbadness(EXIT_RECOV);
/* this is not necessarily correct */
/*ibs->pasteofcount++;*/
*iechangedp = 1;
freemap_blockfree(*ientry);
*ientry = 0;
}
}
else {
assert(*ientry != 0);
if (localchanged) {
sfs_writeindirect(*ientry, entries);
}
}
}
/*
* Check the blocks belonging to inode INO, whose inode has already
* been loaded into SFI. ISDIR is a shortcut telling us if the inode
* is a directory.
*
* Returns nonzero if SFI has been modified and needs to be written
* back.
*/
static
int
check_inode_blocks(uint32_t ino, struct sfs_dinode *sfi, int isdir)
{
struct ibstate ibs;
uint32_t size, datablock;
int changed;
int i;
size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE);
ibs.ino = ino;
/*ibs.curfileblock = 0;*/
ibs.fileblocks = size/SFS_BLOCKSIZE;
ibs.volblocks = sb_totalblocks();
ibs.pasteofcount = 0;
ibs.usagetype = isdir ? B_DIRDATA : B_DATA;
changed = 0;
for (ibs.curfileblock=0; ibs.curfileblock<NUM_D; ibs.curfileblock++) {
datablock = GET_D(sfi, ibs.curfileblock);
if (datablock >= ibs.volblocks) {
setbadness(EXIT_RECOV);
warnx("Inode %lu: direct block pointer for "
"block %lu outside of volume: %lu "
"(cleared)\n",
(unsigned long)ibs.ino,
(unsigned long)ibs.curfileblock,
(unsigned long)datablock);
SET_D(sfi, ibs.curfileblock) = 0;
changed = 1;
}
else if (datablock > 0) {
if (ibs.curfileblock < ibs.fileblocks) {
freemap_blockinuse(datablock, ibs.usagetype,
ibs.ino);
}
else {
setbadness(EXIT_RECOV);
ibs.pasteofcount++;
changed = 1;
freemap_blockfree(datablock);
SET_D(sfi, ibs.curfileblock) = 0;
}
}
}
for (i=0; i<NUM_I; i++) {
check_indirect_block(&ibs, &SET_I(sfi, i), &changed, 1);
}
for (i=0; i<NUM_II; i++) {
check_indirect_block(&ibs, &SET_II(sfi, i), &changed, 2);
}
for (i=0; i<NUM_III; i++) {
check_indirect_block(&ibs, &SET_III(sfi, i), &changed, 3);
}
if (ibs.pasteofcount > 0) {
warnx("Inode %lu: %u blocks after EOF (freed)",
(unsigned long) ibs.ino, ibs.pasteofcount);
setbadness(EXIT_RECOV);
}
return changed;
}
/*
* Do the pass1 inode-level checks on inode INO, which has already
* been loaded into SFI. Note that sfi_type has already been
* validated.
*
* Returns nonzero if SFI has been modified and needs to be written
* back.
*/
static
int
pass1_inode(uint32_t ino, struct sfs_dinode *sfi, int alreadychanged)
{
int changed = alreadychanged;
int isdir = sfi->sfi_type == SFS_TYPE_DIR;
if (inode_add(ino, sfi->sfi_type)) {
/* Already been here. */
assert(changed == 0);
return 1;
}
freemap_blockinuse(ino, B_INODE, ino);
if (checkzeroed(sfi->sfi_waste, sizeof(sfi->sfi_waste))) {
warnx("Inode %lu: sfi_waste section not zeroed (fixed)",
(unsigned long) ino);
setbadness(EXIT_RECOV);
changed = 1;
}
if (check_inode_blocks(ino, sfi, isdir)) {
changed = 1;
}
if (changed) {
sfs_writeinode(ino, sfi);
}
return 0;
}
/*
* Check the directory entry in SFD. INDEX is its offset, and PATH is
* its name; these are used for printing messages.
*/
static
int
pass1_direntry(const char *path, uint32_t index, struct sfs_direntry *sfd)
{
int dchanged = 0;
uint32_t nblocks;
nblocks = sb_totalblocks();
if (sfd->sfd_ino == SFS_NOINO) {
if (sfd->sfd_name[0] != 0) {
setbadness(EXIT_RECOV);
warnx("Directory %s entry %lu has name but no file",
path, (unsigned long) index);
sfd->sfd_name[0] = 0;
dchanged = 1;
}
}
else if (sfd->sfd_ino >= nblocks) {
setbadness(EXIT_RECOV);
warnx("Directory %s entry %lu has out of range "
"inode (cleared)",
path, (unsigned long) index);
sfd->sfd_ino = SFS_NOINO;
sfd->sfd_name[0] = 0;
dchanged = 1;
}
else {
if (sfd->sfd_name[0] == 0) {
/* XXX: what happens if FSCK.n.m already exists? */
snprintf(sfd->sfd_name, sizeof(sfd->sfd_name),
"FSCK.%lu.%lu",
(unsigned long) sfd->sfd_ino,
(unsigned long) uniqueid());
setbadness(EXIT_RECOV);
warnx("Directory %s entry %lu has file but "
"no name (fixed: %s)",
path, (unsigned long) index,
sfd->sfd_name);
dchanged = 1;
}
if (checknullstring(sfd->sfd_name, sizeof(sfd->sfd_name))) {
setbadness(EXIT_RECOV);
warnx("Directory %s entry %lu not "
"null-terminated (fixed)",
path, (unsigned long) index);
dchanged = 1;
}
if (checkbadstring(sfd->sfd_name)) {
setbadness(EXIT_RECOV);
warnx("Directory %s entry %lu contains invalid "
"characters (fixed)",
path, (unsigned long) index);
dchanged = 1;
}
}
return dchanged;
}
/*
* Check a directory. INO is the inode number; PATHSOFAR is the path
* to this directory. This traverses the volume directory tree
* recursively.
*/
static
void
pass1_dir(uint32_t ino, const char *pathsofar)
{
struct sfs_dinode sfi;
struct sfs_direntry *direntries;
uint32_t ndirentries, i;
int ichanged=0, dchanged=0;
sfs_readinode(ino, &sfi);
if (sfi.sfi_size % sizeof(struct sfs_direntry) != 0) {
setbadness(EXIT_RECOV);
warnx("Directory %s has illegal size %lu (fixed)",
pathsofar, (unsigned long) sfi.sfi_size);
sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size,
sizeof(struct sfs_direntry));
ichanged = 1;
}
count_dirs++;
if (pass1_inode(ino, &sfi, ichanged)) {
/* been here before; crosslinked dir, sort it out in pass 2 */
return;
}
ndirentries = sfi.sfi_size/sizeof(struct sfs_direntry);
direntries = domalloc(sfi.sfi_size);
sfs_readdir(&sfi, direntries, ndirentries);
for (i=0; i<ndirentries; i++) {
if (pass1_direntry(pathsofar, i, &direntries[i])) {
dchanged = 1;
}
}
for (i=0; i<ndirentries; i++) {
if (direntries[i].sfd_ino == SFS_NOINO) {
/* nothing */
}
else if (!strcmp(direntries[i].sfd_name, ".")) {
/* nothing */
}
else if (!strcmp(direntries[i].sfd_name, "..")) {
/* nothing */
}
else {
char path[strlen(pathsofar)+SFS_NAMELEN+1];
struct sfs_dinode subsfi;
uint32_t subino;
subino = direntries[i].sfd_ino;
sfs_readinode(subino, &subsfi);
snprintf(path, sizeof(path), "%s/%s",
pathsofar, direntries[i].sfd_name);
switch (subsfi.sfi_type) {
case SFS_TYPE_FILE:
if (pass1_inode(subino, &subsfi, 0)) {
/* been here before */
break;
}
count_files++;
break;
case SFS_TYPE_DIR:
pass1_dir(subino, path);
break;
default:
setbadness(EXIT_RECOV);
warnx("Object %s: Invalid inode type %u "
"(removed)", path, subsfi.sfi_type);
direntries[i].sfd_ino = SFS_NOINO;
direntries[i].sfd_name[0] = 0;
dchanged = 1;
break;
}
}
}
if (dchanged) {
sfs_writedir(&sfi, direntries, ndirentries);
}
free(direntries);
}
/*
* Check the root directory, and implicitly everything under it.
*/
static
void
pass1_rootdir(void)
{
struct sfs_dinode sfi;
char path[SFS_VOLNAME_SIZE + 2];
sfs_readinode(SFS_ROOTDIR_INO, &sfi);
switch (sfi.sfi_type) {
case SFS_TYPE_DIR:
break;
case SFS_TYPE_FILE:
warnx("Root directory inode is a regular file (fixed)");
goto fix;
default:
warnx("Root directory inode has invalid type %lu (fixed)",
(unsigned long) sfi.sfi_type);
fix:
setbadness(EXIT_RECOV);
sfi.sfi_type = SFS_TYPE_DIR;
sfs_writeinode(SFS_ROOTDIR_INO, &sfi);
break;
}
snprintf(path, sizeof(path), "%s:", sb_volname());
pass1_dir(SFS_ROOTDIR_INO, path);
}
////////////////////////////////////////////////////////////
// public interface
void
pass1(void)
{
pass1_rootdir();
}
unsigned long
pass1_founddirs(void)
{
return count_dirs;
}
unsigned long
pass1_foundfiles(void)
{
return count_files;
}

325
userland/sbin/sfsck/pass2.c Normal file
View File

@@ -0,0 +1,325 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "disk.h"
#include "utils.h"
#include "ibmacros.h"
#include "sfs.h"
#include "sb.h"
#include "freemap.h"
#include "inode.h"
#include "passes.h"
#include "main.h"
/*
* Process a directory. INO is the inode number; PARENTINO is the
* parent's inode number; PATHSOFAR is the path to this directory.
*
* Recursively checks its subdirs.
*
* In the FUTURE we might want to improve the handling of crosslinked
* directories so it picks the parent that the .. entry points to,
* instead of the first entry we recursively find. Beware of course
* that the .. entry might not point to anywhere valid at all...
*/
static
int
pass2_dir(uint32_t ino, uint32_t parentino, const char *pathsofar)
{
struct sfs_dinode sfi;
struct sfs_direntry *direntries;
int *sortvector;
uint32_t dirsize, ndirentries, maxdirentries, subdircount, i;
int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0;
if (inode_visitdir(ino)) {
/* crosslinked dir; tell parent to remove the entry */
return 1;
}
/* Load the inode. */
sfs_readinode(ino, &sfi);
/*
* Load the directory. If there is any leftover room in the
* last block, allocate space for it in case we want to insert
* entries.
*/
ndirentries = sfi.sfi_size/sizeof(struct sfs_direntry);
maxdirentries = SFS_ROUNDUP(ndirentries,
SFS_BLOCKSIZE/sizeof(struct sfs_direntry));
dirsize = maxdirentries * sizeof(struct sfs_direntry);
direntries = domalloc(dirsize);
sortvector = domalloc(ndirentries * sizeof(int));
sfs_readdir(&sfi, direntries, ndirentries);
for (i=ndirentries; i<maxdirentries; i++) {
direntries[i].sfd_ino = SFS_NOINO;
bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name));
}
/*
* Sort by name and check for duplicate names.
*/
sfsdir_sort(direntries, ndirentries, sortvector);
/* don't use ndirentries-1 here, in case ndirentries == 0 */
for (i=0; i+1<ndirentries; i++) {
struct sfs_direntry *d1 = &direntries[sortvector[i]];
struct sfs_direntry *d2 = &direntries[sortvector[i+1]];
assert(d1 != d2);
if (d1->sfd_ino == SFS_NOINO || d2->sfd_ino == SFS_NOINO) {
/* sfsdir_sort puts these last */
continue;
}
if (!strcmp(d1->sfd_name, d2->sfd_name)) {
if (d1->sfd_ino == d2->sfd_ino) {
setbadness(EXIT_RECOV);
warnx("Directory %s: Duplicate entries for "
"%s (merged)",
pathsofar, d1->sfd_name);
d1->sfd_ino = SFS_NOINO;
d1->sfd_name[0] = 0;
}
else {
/* XXX: what if FSCK.n.m already exists? */
snprintf(d1->sfd_name, sizeof(d1->sfd_name),
"FSCK.%lu.%lu",
(unsigned long) d1->sfd_ino,
(unsigned long) uniqueid());
setbadness(EXIT_RECOV);
warnx("Directory %s: Duplicate names %s "
"(one renamed: %s)",
pathsofar, d2->sfd_name, d1->sfd_name);
}
dchanged = 1;
}
}
/*
* Look for the . and .. entries.
*/
for (i=0; i<ndirentries; i++) {
if (!strcmp(direntries[i].sfd_name, ".")) {
if (direntries[i].sfd_ino != ino) {
setbadness(EXIT_RECOV);
warnx("Directory %s: Incorrect `.' entry "
"(fixed)", pathsofar);
direntries[i].sfd_ino = ino;
dchanged = 1;
}
/* duplicates are checked above -> only one . here */
assert(dotseen==0);
dotseen = 1;
}
else if (!strcmp(direntries[i].sfd_name, "..")) {
if (direntries[i].sfd_ino != parentino) {
setbadness(EXIT_RECOV);
warnx("Directory %s: Incorrect `..' entry "
"(fixed)", pathsofar);
direntries[i].sfd_ino = parentino;
dchanged = 1;
}
/* duplicates are checked above -> only one .. here */
assert(dotdotseen==0);
dotdotseen = 1;
}
}
/*
* If no . entry, try to insert one.
*/
if (!dotseen) {
if (sfsdir_tryadd(direntries, ndirentries, ".", ino)==0) {
setbadness(EXIT_RECOV);
warnx("Directory %s: No `.' entry (added)",
pathsofar);
dchanged = 1;
}
else if (sfsdir_tryadd(direntries, maxdirentries, ".",
ino)==0) {
setbadness(EXIT_RECOV);
warnx("Directory %s: No `.' entry (added)",
pathsofar);
ndirentries++;
dchanged = 1;
sfi.sfi_size += sizeof(struct sfs_direntry);
ichanged = 1;
}
else {
setbadness(EXIT_UNRECOV);
warnx("Directory %s: No `.' entry (NOT FIXED)",
pathsofar);
}
}
/*
* If no .. entry, try to insert one.
*/
if (!dotdotseen) {
if (sfsdir_tryadd(direntries, ndirentries, "..",
parentino)==0) {
setbadness(EXIT_RECOV);
warnx("Directory %s: No `..' entry (added)",
pathsofar);
dchanged = 1;
}
else if (sfsdir_tryadd(direntries, maxdirentries, "..",
parentino)==0) {
setbadness(EXIT_RECOV);
warnx("Directory %s: No `..' entry (added)",
pathsofar);
ndirentries++;
dchanged = 1;
sfi.sfi_size += sizeof(struct sfs_direntry);
ichanged = 1;
}
else {
setbadness(EXIT_UNRECOV);
warnx("Directory %s: No `..' entry (NOT FIXED)",
pathsofar);
}
}
/*
* Now load each inode in the directory.
*
* For regular files, count the number of links we see; for
* directories, recurse. Count the number of subdirs seen
* so we can correct our own link count if necessary.
*/
subdircount=0;
for (i=0; i<ndirentries; i++) {
if (direntries[i].sfd_ino == SFS_NOINO) {
/* nothing */
}
else if (!strcmp(direntries[i].sfd_name, ".")) {
/* nothing */
}
else if (!strcmp(direntries[i].sfd_name, "..")) {
/* nothing */
}
else {
char path[strlen(pathsofar)+SFS_NAMELEN+1];
struct sfs_dinode subsfi;
sfs_readinode(direntries[i].sfd_ino, &subsfi);
snprintf(path, sizeof(path), "%s/%s",
pathsofar, direntries[i].sfd_name);
switch (subsfi.sfi_type) {
case SFS_TYPE_FILE:
inode_addlink(direntries[i].sfd_ino);
break;
case SFS_TYPE_DIR:
if (pass2_dir(direntries[i].sfd_ino,
ino,
path)) {
setbadness(EXIT_RECOV);
warnx("Directory %s: Crosslink to "
"other directory (removed)",
path);
direntries[i].sfd_ino = SFS_NOINO;
direntries[i].sfd_name[0] = 0;
dchanged = 1;
}
else {
subdircount++;
}
break;
default:
setbadness(EXIT_RECOV);
warnx("Object %s: Invalid inode type %u "
"(removed)", path, subsfi.sfi_type);
direntries[i].sfd_ino = SFS_NOINO;
direntries[i].sfd_name[0] = 0;
dchanged = 1;
break;
}
}
}
/*
* Fix up the link count if needed.
*/
if (sfi.sfi_linkcount != subdircount+2) {
setbadness(EXIT_RECOV);
warnx("Directory %s: Link count %lu should be %lu (fixed)",
pathsofar, (unsigned long) sfi.sfi_linkcount,
(unsigned long) subdircount+2);
sfi.sfi_linkcount = subdircount+2;
ichanged = 1;
}
/*
* Write back anything that changed, clean up, and return.
*/
if (dchanged) {
sfs_writedir(&sfi, direntries, ndirentries);
}
if (ichanged) {
sfs_writeinode(ino, &sfi);
}
free(direntries);
free(sortvector);
return 0;
}
void
pass2(void)
{
char path[SFS_VOLNAME_SIZE + 2];
snprintf(path, sizeof(path), "%s:", sb_volname());
pass2_dir(SFS_ROOTDIR_INO, SFS_ROOTDIR_INO, path);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 PASSES_H
#define PASSES_H
/*
* Pass 1 scans the filesystem starting at the root directory, finding
* all reachable directories, files, and blocks, and correcting gross
* local errors. The results are used to fix the free block bitmap.
*
* Pass 2 scans the filesystem starting at the root directory,
* checking for crosslinked and malformed directories and accumulating
* link count information. Because it runs after we've fixed the free
* block bitmap, we can (cautiously) allocate blocks if we need to.
*/
void pass1(void);
void pass2(void);
/* After pass1 is done, return the number of dirs and files on the volume. */
unsigned long pass1_founddirs(void);
unsigned long pass1_foundfiles(void);
#endif /* PASSES_H */

124
userland/sbin/sfsck/sb.c Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <sys/types.h> /* for CHAR_BIT */
#include <limits.h> /* also for CHAR_BIT */
#include <stdint.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "utils.h"
#include "sfs.h"
#include "sb.h"
#include "freemap.h"
#include "main.h"
static struct sfs_superblock sb;
/*
* Load the superblock.
*/
void
sb_load(void)
{
sfs_readsb(SFS_SUPER_BLOCK, &sb);
if (sb.sb_magic != SFS_MAGIC) {
errx(EXIT_FATAL, "Not an sfs filesystem");
}
assert(sb.sb_nblocks > 0);
assert(SFS_FREEMAPBLOCKS(sb.sb_nblocks) > 0);
}
/*
* Validate the superblock.
*/
void
sb_check(void)
{
int schanged=0;
/*
* FUTURE: should we check sb.sb_nblocks against diskblocks()?
*/
/* Check the superblock fields */
if (checknullstring(sb.sb_volname, sizeof(sb.sb_volname))) {
warnx("Volume name not null-terminated (fixed)");
setbadness(EXIT_RECOV);
schanged = 1;
}
if (checkbadstring(sb.sb_volname)) {
warnx("Volume name contains illegal characters (fixed)");
setbadness(EXIT_RECOV);
schanged = 1;
}
if (checkzeroed(sb.reserved, sizeof(sb.reserved))) {
warnx("Reserved section of superblock not zeroed (fixed)");
setbadness(EXIT_RECOV);
schanged = 1;
}
/* Write the superblock back if necessary */
if (schanged) {
sfs_writesb(SFS_SUPER_BLOCK, &sb);
}
}
/*
* Return the total number of blocks in the volume.
*/
uint32_t
sb_totalblocks(void)
{
return sb.sb_nblocks;
}
/*
* Return the number of freemap blocks.
* (this function probably ought to go away)
*/
uint32_t
sb_freemapblocks(void)
{
return SFS_FREEMAPBLOCKS(sb.sb_nblocks);
}
/*
* Return the volume name.
*/
const char *
sb_volname(void)
{
return sb.sb_volname;
}

55
userland/sbin/sfsck/sb.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 SB_H
#define SB_H
/*
* The superblock module checks the superblock and provides
* information from the superblock to other modules.
*/
#include <stdint.h>
/* Load the superblock. Should be done before virtually anything else. */
void sb_load(void);
/* After the superblock is loaded: return volume size. */
uint32_t sb_totalblocks(void);
/* After the superblock is loaded: return number of freemap blocks. */
uint32_t sb_freemapblocks(void);
/* After the superblock is loaded: return volume name. */
const char *sb_volname(void);
/* Check the superblock. Must load it first. */
void sb_check(void);
#endif /* SB_H */

464
userland/sbin/sfsck/sfs.c Normal file
View File

@@ -0,0 +1,464 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include "compat.h"
#include <kern/sfs.h>
#include "disk.h"
#include "utils.h"
#include "ibmacros.h"
#include "sfs.h"
#include "main.h"
////////////////////////////////////////////////////////////
// global setup
void
sfs_setup(void)
{
assert(sizeof(struct sfs_superblock)==SFS_BLOCKSIZE);
assert(sizeof(struct sfs_dinode)==SFS_BLOCKSIZE);
assert(SFS_BLOCKSIZE % sizeof(struct sfs_direntry) == 0);
}
////////////////////////////////////////////////////////////
// byte-swap functions
static
void
swapsb(struct sfs_superblock *sb)
{
sb->sb_magic = SWAP32(sb->sb_magic);
sb->sb_nblocks = SWAP32(sb->sb_nblocks);
}
static
void
swapbits(uint8_t *bits)
{
/* nothing to do */
(void)bits;
}
static
void
swapinode(struct sfs_dinode *sfi)
{
int i;
sfi->sfi_size = SWAP32(sfi->sfi_size);
sfi->sfi_type = SWAP16(sfi->sfi_type);
sfi->sfi_linkcount = SWAP16(sfi->sfi_linkcount);
for (i=0; i<NUM_D; i++) {
SET_D(sfi, i) = SWAP32(GET_D(sfi, i));
}
for (i=0; i<NUM_I; i++) {
SET_I(sfi, i) = SWAP32(GET_I(sfi, i));
}
for (i=0; i<NUM_II; i++) {
SET_II(sfi, i) = SWAP32(GET_II(sfi, i));
}
for (i=0; i<NUM_III; i++) {
SET_III(sfi, i) = SWAP32(GET_III(sfi, i));
}
}
static
void
swapdir(struct sfs_direntry *sfd)
{
sfd->sfd_ino = SWAP32(sfd->sfd_ino);
}
static
void
swapindir(uint32_t *entries)
{
int i;
for (i=0; i<SFS_DBPERIDB; i++) {
entries[i] = SWAP32(entries[i]);
}
}
////////////////////////////////////////////////////////////
// bmap()
/*
* Indirect block bmap: in indirect block IBLOCK, read the entry at
* block OFFSET from the first file block mapped by this indirect
* block.
*
* ENTRYSIZE is how many blocks each entry describes; for a
* singly-indirect block this is 1. For a multiply-indirect block,
* it is more than 1; in this case recurse.
*/
static
uint32_t
ibmap(uint32_t iblock, uint32_t offset, uint32_t entrysize)
{
uint32_t entries[SFS_DBPERIDB];
if (iblock == 0) {
return 0;
}
diskread(entries, iblock);
swapindir(entries);
if (entrysize > 1) {
uint32_t index = offset / entrysize;
offset %= entrysize;
return ibmap(entries[index], offset, entrysize/SFS_DBPERIDB);
}
else {
assert(offset < SFS_DBPERIDB);
return entries[offset];
}
}
/*
* bmap() for SFS.
*
* Given an inode and a file block, returns a disk block.
*/
static
uint32_t
bmap(const struct sfs_dinode *sfi, uint32_t fileblock)
{
uint32_t iblock, offset;
if (fileblock < INOMAX_D) {
return GET_D(sfi, fileblock);
}
else if (fileblock < INOMAX_I) {
iblock = (fileblock - INOMAX_D) / RANGE_I;
offset = (fileblock - INOMAX_D) % RANGE_I;
return ibmap(GET_I(sfi, iblock), offset, RANGE_D);
}
else if (fileblock < INOMAX_II) {
iblock = (fileblock - INOMAX_I) / RANGE_II;
offset = (fileblock - INOMAX_I) % RANGE_II;
return ibmap(GET_II(sfi, iblock), offset, RANGE_I);
}
else if (fileblock < INOMAX_III) {
iblock = (fileblock - INOMAX_II) / RANGE_III;
offset = (fileblock - INOMAX_II) % RANGE_III;
return ibmap(GET_III(sfi, iblock), offset, RANGE_II);
}
return 0;
}
////////////////////////////////////////////////////////////
// superblock, free block bitmap, and inode I/O
/*
* superblock - blocknum is a disk block number.
*/
void
sfs_readsb(uint32_t blocknum, struct sfs_superblock *sb)
{
diskread(sb, blocknum);
swapsb(sb);
}
void
sfs_writesb(uint32_t blocknum, struct sfs_superblock *sb)
{
swapsb(sb);
diskwrite(sb, blocknum);
swapsb(sb);
}
/*
* freemap blocks - whichblock is a block number within the free block
* bitmap.
*/
void
sfs_readfreemapblock(uint32_t whichblock, uint8_t *bits)
{
diskread(bits, SFS_FREEMAP_START + whichblock);
swapbits(bits);
}
void
sfs_writefreemapblock(uint32_t whichblock, uint8_t *bits)
{
swapbits(bits);
diskwrite(bits, SFS_FREEMAP_START + whichblock);
swapbits(bits);
}
/*
* inodes - ino is an inode number, which is a disk block number.
*/
void
sfs_readinode(uint32_t ino, struct sfs_dinode *sfi)
{
diskread(sfi, ino);
swapinode(sfi);
}
void
sfs_writeinode(uint32_t ino, struct sfs_dinode *sfi)
{
swapinode(sfi);
diskwrite(sfi, ino);
swapinode(sfi);
}
/*
* indirect blocks - blocknum is a disk block number.
*/
void
sfs_readindirect(uint32_t blocknum, uint32_t *entries)
{
diskread(entries, blocknum);
swapindir(entries);
}
void
sfs_writeindirect(uint32_t blocknum, uint32_t *entries)
{
swapindir(entries);
diskwrite(entries, blocknum);
swapindir(entries);
}
////////////////////////////////////////////////////////////
// directory I/O
/*
* Read the directory block at DISKBLOCK into D.
*/
static
void
sfs_readdirblock(struct sfs_direntry *d, uint32_t diskblock)
{
const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_direntry);
unsigned j;
if (diskblock != 0) {
diskread(d, diskblock);
for (j=0; j<atonce; j++) {
swapdir(&d[j]);
}
}
else {
warnx("Warning: sparse directory found");
bzero(d, SFS_BLOCKSIZE);
}
}
/*
* Read in a directory, from the inode SFI, into D, which is a buffer
* with ND slots. The caller is assumed to have figured out the right
* number of slots.
*/
void
sfs_readdir(struct sfs_dinode *sfi, struct sfs_direntry *d, unsigned nd)
{
const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_direntry);
unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
unsigned i, j;
unsigned left, thismany;
struct sfs_direntry buffer[atonce];
uint32_t diskblock;
left = nd;
for (i=0; i<nblocks; i++) {
diskblock = bmap(sfi, i);
if (left < atonce) {
thismany = left;
sfs_readdirblock(buffer, diskblock);
for (j=0; j<thismany; j++) {
d[i*atonce + j] = buffer[j];
}
}
else {
thismany = atonce;
sfs_readdirblock(d + i*atonce, diskblock);
}
left -= thismany;
}
assert(left == 0);
}
/*
* Write the directory block D to DISKBLOCK.
*/
static
void
sfs_writedirblock(struct sfs_direntry *d, uint32_t diskblock)
{
const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_direntry);
unsigned j, bad;
if (diskblock != 0) {
for (j=0; j<atonce; j++) {
swapdir(&d[j]);
}
diskwrite(d, diskblock);
}
else {
for (j=bad=0; j<atonce; j++) {
if (d[j].sfd_ino != SFS_NOINO ||
d[j].sfd_name[0] != 0) {
bad = 1;
}
}
if (bad) {
warnx("Cannot write to missing block in "
"sparse directory (ERROR)");
setbadness(EXIT_UNRECOV);
}
}
}
/*
* Write out a directory, from the inode SFI, using D, which is a
* buffer with ND slots. The caller is assumed to have set the inode
* size accordingly.
*/
void
sfs_writedir(const struct sfs_dinode *sfi, struct sfs_direntry *d, unsigned nd)
{
const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_direntry);
unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
unsigned i, j;
unsigned left, thismany;
struct sfs_direntry buffer[atonce];
uint32_t diskblock;
left = nd;
for (i=0; i<nblocks; i++) {
diskblock = bmap(sfi, i);
if (left < atonce) {
thismany = left;
for (j=0; j<thismany; j++) {
buffer[j] = d[i*atonce + j];
}
for (; j<atonce; j++) {
memset(&buffer[j], 0, sizeof(buffer[j]));
}
sfs_writedirblock(buffer, diskblock);
}
else {
thismany = atonce;
sfs_writedirblock(d + i*atonce, diskblock);
}
left -= thismany;
}
assert(left == 0);
}
////////////////////////////////////////////////////////////
// directory utilities
/* this exists because qsort() doesn't pass a context pointer through */
static struct sfs_direntry *global_sortdirs;
/*
* Compare function for the permutation vector produced by
* sfsdir_sort().
*/
static
int
dirsortfunc(const void *aa, const void *bb)
{
const int *a = (const int *)aa;
const int *b = (const int *)bb;
const struct sfs_direntry *ad = &global_sortdirs[*a];
const struct sfs_direntry *bd = &global_sortdirs[*b];
/* Sort unallocated entries last */
if (ad->sfd_ino == SFS_NOINO && bd->sfd_ino == SFS_NOINO) {
return 0;
}
if (ad->sfd_ino == SFS_NOINO) {
return 1;
}
if (bd->sfd_ino == SFS_NOINO) {
return -1;
}
return strcmp(ad->sfd_name, bd->sfd_name);
}
/*
* Sort the directory contents in D (with ND entries) by producing a
* permutation vector into VECTOR, which should be allocated to hold
* ND ints.
*/
void
sfsdir_sort(struct sfs_direntry *d, unsigned nd, int *vector)
{
unsigned i;
for (i=0; i<nd; i++) {
vector[i] = i;
}
global_sortdirs = d;
qsort(vector, nd, sizeof(int), dirsortfunc);
}
/*
* Try to add an entry NAME/INO to D (which has ND entries) by
* finding an empty slot. Cannot allocate new space.
*
* Returns 0 on success and nonzero on failure.
*/
int
sfsdir_tryadd(struct sfs_direntry *d, int nd, const char *name, uint32_t ino)
{
int i;
for (i=0; i<nd; i++) {
if (d[i].sfd_ino==SFS_NOINO) {
d[i].sfd_ino = ino;
assert(strlen(name) < sizeof(d[i].sfd_name));
strcpy(d[i].sfd_name, name);
return 0;
}
}
return -1;
}

81
userland/sbin/sfsck/sfs.h Normal file
View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 SFS_H
#define SFS_H
/*
* SFS operations. This module provides functions for reading and
* writing SFS structures, and knows under the covers how to byte-swap
* them. It also provides utility operations on the SFS structures.
*/
#include <stdint.h>
struct sfs_superblock;
struct sfs_dinode;
struct sfs_direntry;
/* Call this before anything else in this module */
void sfs_setup(void);
/*
* Read and write ops for SFS structures
*/
/* superblock */
void sfs_readsb(uint32_t blocknum, struct sfs_superblock *sb);
void sfs_writesb(uint32_t blocknum, struct sfs_superblock *sb);
/* freemap blocks; whichblock is the freemap block number (starts at 0) */
void sfs_readfreemapblock(uint32_t whichblock, uint8_t *bits);
void sfs_writefreemapblock(uint32_t whichblock, uint8_t *bits);
/* inode */
void sfs_readinode(uint32_t inum, struct sfs_dinode *sfi);
void sfs_writeinode(uint32_t inum, struct sfs_dinode *sfi);
/* indirect block (any indirection level) */
void sfs_readindirect(uint32_t blocknum, uint32_t *entries);
void sfs_writeindirect(uint32_t blocknum, uint32_t *entries);
/* directory - ND should be the number of directory entries D points to */
void sfs_readdir(struct sfs_dinode *sfi, struct sfs_direntry *d, unsigned nd);
void sfs_writedir(const struct sfs_dinode *sfi,
struct sfs_direntry *d, unsigned nd);
/* Try to add an entry to a directory. */
int sfsdir_tryadd(struct sfs_direntry *d, int nd,
const char *name, uint32_t ino);
/* Sort a directory by creating a permutation vector. */
void sfsdir_sort(struct sfs_direntry *d, unsigned nd, int *vector);
#endif /* SFS_H */

144
userland/sbin/sfsck/utils.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include "compat.h"
#include "utils.h"
#include "main.h"
/*
* Wrapper around malloc.
*/
void *
domalloc(size_t len)
{
void *x;
x = malloc(len);
if (x==NULL) {
errx(EXIT_FATAL, "Out of memory");
}
return x;
}
/*
* Wrapper around realloc. OSZ is the old block size, which we need if
* we're going to emulate realloc with malloc.
*/
void *
dorealloc(void *op, size_t osz, size_t nsz)
{
void *np;
#ifdef NO_REALLOC
size_t copysz;
np = domalloc(nsz);
if (op != NULL) {
copysz = osz < nsz ? osz : nsz;
memcpy(np, op, copysz);
free(op);
}
#else
(void)osz;
np = realloc(op, nsz);
if (np == NULL) {
errx(EXIT_FATAL, "Out of memory");
}
#endif
return np;
}
/*
* Get a unique id number. (unique as in for this run of sfsck...)
*/
uint32_t
uniqueid(void)
{
static uint32_t uniquecounter;
return uniquecounter++;
}
/*
* Check if BUF, a string field of length MAXLEN, contains a null
* terminator. If not, slam one in and return 1.
*/
int
checknullstring(char *buf, size_t maxlen)
{
size_t i;
for (i=0; i<maxlen; i++) {
if (buf[i]==0) {
return 0;
}
}
buf[maxlen-1] = 0;
return 1;
}
/*
* Check if BUF contains characters not allowed in file and volume
* names. If so, stomp them and return 1.
*/
int
checkbadstring(char *buf)
{
size_t i;
int rv = 0;
for (i=0; buf[i]; i++) {
if (buf[i]==':' || buf[i]=='/') {
buf[i] = '_';
rv = 1;
}
}
return rv;
}
/*
* Check if BUF, of size LEN, is zeroed. If not, zero it and return 1.
*/
int
checkzeroed(void *vbuf, size_t len)
{
char *buf = vbuf;
size_t i;
int rv = 0;
for (i=0; i < len; i++) {
if (buf[i] != 0) {
buf[i] = 0;
rv = 1;
}
}
return rv;
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2013
* The President and Fellows of Harvard College.
*
* 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. 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 UNIVERSITY 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 UNIVERSITY 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 UTILS_H
#define UTILS_H
#include <sys/types.h> /* for size_t */
#include <stdint.h> /* for uint32_t */
/* non-failing wrapper around malloc */
void *domalloc(size_t len);
void *dorealloc(void *op, size_t osz, size_t nsz);
/* return a fresh id number */
uint32_t uniqueid(void);
/* ensure that BUF (of size MAXLEN) is terminated; return 1 if changed */
int checknullstring(char *buf, size_t maxlen);
/* check for illegal filename characters; if found, zap them and return 1 */
int checkbadstring(char *buf);
/* check for nonzero bytes in a zeroed area; if found, zap and return 1 */
int checkzeroed(void *buf, size_t len);
#endif /* UTILS_H */