/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 * 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 #include #include #include #include #include #include #include #include "support.h" #include "kern/sfs.h" #ifdef HOST #include // for arpa/inet.h #include // for ntohl #include "hostcompat.h" #define SWAPL(x) ntohl(x) #define SWAPS(x) ntohs(x) #else #define SWAPL(x) (x) #define SWAPS(x) (x) #define NO_REALLOC #define NO_QSORT #endif #include "disk.h" #define EXIT_USAGE 4 #define EXIT_FATAL 3 #define EXIT_UNRECOV 2 #define EXIT_RECOV 1 #define EXIT_CLEAN 0 static int badness=0; static void setbadness(int code) { if (badness < code) { badness = code; } } //////////////////////////////////////////////////////////// static void swapsb(struct sfs_super *sp) { sp->sp_magic = SWAPL(sp->sp_magic); sp->sp_nblocks = SWAPL(sp->sp_nblocks); } static void swapinode(struct sfs_inode *sfi) { int i; sfi->sfi_size = SWAPL(sfi->sfi_size); sfi->sfi_type = SWAPS(sfi->sfi_type); sfi->sfi_linkcount = SWAPS(sfi->sfi_linkcount); for (i=0; isfi_direct[i] = SWAPL(sfi->sfi_direct[i]); } #ifdef SFS_NIDIRECT for (i=0; isfi_indirect[i] = SWAPL(sfi->sfi_indirect[i]); } #else sfi->sfi_indirect = SWAPL(sfi->sfi_indirect); #endif #ifdef SFS_NDIDIRECT for (i=0; isfi_dindirect[i] = SWAPL(sfi->sfi_dindirect[i]); } #else #ifdef HAS_DIDIRECT sfi->sfi_dindirect = SWAPL(sfi->sfi_dindirect); #endif #endif #ifdef SFS_NTIDIRECT for (i=0; isfi_tindirect[i] = SWAPL(sfi->sfi_tindirect[i]); } #else #ifdef HAS_TIDIRECT sfi->sfi_tindirect = SWAPL(sfi->sfi_tindirect); #endif #endif } static void swapdir(struct sfs_dir *sfd) { sfd->sfd_ino = SWAPL(sfd->sfd_ino); } static void swapindir(uint32_t *entries) { int i; for (i=0; i 0) { warnx("%lu blocks erroneously shown free in bitmap (fixed)", (unsigned long) alloccount); setbadness(EXIT_RECOV); } if (freecount > 0) { warnx("%lu blocks erroneously shown used in bitmap (fixed)", (unsigned long) freecount); setbadness(EXIT_RECOV); } } //////////////////////////////////////////////////////////// struct inodememory { uint32_t ino; uint32_t linkcount; /* files only; 0 for dirs */ }; static struct inodememory *inodes = NULL; static int ninodes=0, maxinodes=0; static void addmemory(uint32_t ino, uint32_t linkcount) { assert(ninodes <= maxinodes); if (ninodes == maxinodes) { #ifdef NO_REALLOC int newmax = (maxinodes+1)*2; void *p = domalloc(newmax * sizeof(struct inodememory)); if (inodes) { memcpy(p, inodes, ninodes); free(inodes); } inodes = p; #else maxinodes = (maxinodes+1)*2; inodes = realloc(inodes, maxinodes * sizeof(uint32_t)); if (inodes==NULL) { errx(EXIT_FATAL, "Out of memory"); } #endif } inodes[ninodes].ino = ino; inodes[ninodes].linkcount = linkcount; } /* returns nonzero if directory already remembered */ static int remember_dir(uint32_t ino, const char *pathsofar) { int i; /* don't use this for now */ (void)pathsofar; for (i=0; i0); inodes[i].linkcount++; return; } } bitmap_mark(ino, B_INODE, ino); addmemory(ino, 1); } static void adjust_filelinks(void) { struct sfs_inode sfi; int i; for (i=0; i0); assert(bitblocks>0); bitmap_init(bitblocks); for (i=nblocks; i 1) { for (i=0; i 0) { swapindir(entries); diskwrite(entries, *ientry); } } } /* returns nonzero if inode modified */ static int check_inode_blocks(uint32_t ino, struct sfs_inode *sfi, int isdir) { uint32_t size, block, nblocks, badcount; badcount = 0; size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE); nblocks = size/SFS_BLOCKSIZE; for (block=0; blocksfi_direct[block] != 0) { bitmap_mark(sfi->sfi_direct[block], isdir ? B_DIRDATA : B_DATA, ino); } } else { if (sfi->sfi_direct[block] != 0) { badcount++; bitmap_mark(sfi->sfi_direct[block], B_TOFREE, 0); } } } #ifdef SFS_NIDIRECT for (i=0; isfi_indirect[i], &block, nblocks, &badcount, isdir, 1); } #else check_indirect_block(ino, &sfi->sfi_indirect, &block, nblocks, &badcount, isdir, 1); #endif #ifdef SFS_NDIDIRECT for (i=0; isfi_dindirect[i], &block, nblocks, &badcount, isdir, 2); } #else #ifdef HAS_DIDIRECT check_indirect_block(ino, &sfi->sfi_dindirect, &block, nblocks, &badcount, isdir, 2); #endif #endif #ifdef SFS_NTIDIRECT for (i=0; isfi_tindirect[i], &block, nblocks, &badcount, isdir, 3); } #else #ifdef HAS_TIDIRECT check_indirect_block(ino, &sfi->sfi_tindirect, &block, nblocks, &badcount, isdir, 3); #endif #endif if (badcount > 0) { warnx("Inode %lu: %lu blocks after EOF (freed)", (unsigned long) ino, (unsigned long) badcount); setbadness(EXIT_RECOV); return 1; } return 0; } //////////////////////////////////////////////////////////// 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]; } } #define BMAP_ND SFS_NDIRECT #define BMAP_D(sfi, x) ((sfi)->sfi_direct[(x)]) #ifdef SFS_NIDIRECT #define BMAP_NI SFS_NIDIRECT #define BMAP_I(sfi, x) ((sfi)->sfi_indirect[(x)]) #else #define BMAP_NI 1 #define BMAP_I(sfi, x) ((void)(x), (sfi)->sfi_indirect) #endif #ifdef SFS_NDIDIRECT #define BMAP_NII SFS_NDIDIRECT #define BMAP_II(sfi, x) ((sfi)->sfi_dindirect[(x)]) #else #ifdef HAS_DIDIRECT #define BMAP_NII 1 #define BMAP_II(sfi, x) ((void)(x), (sfi)->sfi_dindirect) #else #define BMAP_NII 0 #define BMAP_II(sfi, x) ((void)(x), (void)(sfi), 0) #endif #endif #ifdef SFS_NTIDIRECT #define BMAP_NIII SFS_NTIDIRECT #define BMAP_III(sfi, x) ((sfi)->sfi_tindirect[(x)]) #else #ifdef HAS_TIDIRECT #define BMAP_NIII 1 #define BMAP_III(sfi, x) ((void)(x), (sfi)->sfi_tindirect) #else #define BMAP_NIII 0 #define BMAP_III(sfi, x) ((void)(x), (void)(sfi), 0) #endif #endif #define BMAP_DMAX BMAP_ND #define BMAP_IMAX (BMAP_DMAX+SFS_DBPERIDB*BMAP_NI) #define BMAP_IIMAX (BMAP_IMAX+SFS_DBPERIDB*BMAP_NII) #define BMAP_IIIMAX (BMAP_IIMAX+SFS_DBPERIDB*BMAP_NIII) #define BMAP_DSIZE 1 #define BMAP_ISIZE (BMAP_DSIZE*SFS_DBPERIDB) #define BMAP_IISIZE (BMAP_ISIZE*SFS_DBPERIDB) #define BMAP_IIISIZE (BMAP_IISIZE*SFS_DBPERIDB) static uint32_t dobmap(const struct sfs_inode *sfi, uint32_t fileblock) { uint32_t iblock, offset; if (fileblock < BMAP_DMAX) { return BMAP_D(sfi, fileblock); } else if (fileblock < BMAP_IMAX) { iblock = (fileblock - BMAP_DMAX)/BMAP_ISIZE; offset = (fileblock - BMAP_DMAX)%BMAP_ISIZE; return ibmap(BMAP_I(sfi, iblock), offset, BMAP_DSIZE); } else if (fileblock < BMAP_IIMAX) { iblock = (fileblock - BMAP_IMAX)/BMAP_IISIZE; offset = (fileblock - BMAP_IMAX)%BMAP_IISIZE; return ibmap(BMAP_II(sfi, iblock), offset, BMAP_ISIZE); } else if (fileblock < BMAP_IIIMAX) { iblock = (fileblock - BMAP_IIMAX)/BMAP_IIISIZE; offset = (fileblock - BMAP_IIMAX)%BMAP_IIISIZE; return ibmap(BMAP_III(sfi, iblock), offset, BMAP_IISIZE); } return 0; } static void dirread(struct sfs_inode *sfi, struct sfs_dir *d, unsigned nd) { const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir); unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce; unsigned i, j; for (i=0; isfd_name, bd->sfd_name); } #ifdef NO_QSORT static void qsort(int *data, int num, size_t size, int (*f)(const void *, const void *)) { int i, j; (void)size; /* because I'm lazy, bubble sort */ for (i=0; isfd_ino == SFS_NOINO) { if (sfd->sfd_name[0] != 0) { setbadness(EXIT_RECOV); warnx("Directory /%s entry %lu has name but no file", pathsofar, (unsigned long) index); sfd->sfd_name[0] = 0; dchanged = 1; } } else { if (sfd->sfd_name[0] == 0) { snprintf(sfd->sfd_name, sizeof(sfd->sfd_name), "FSCK.%lu.%lu", (unsigned long) sfd->sfd_ino, (unsigned long) uniquecounter++); setbadness(EXIT_RECOV); warnx("Directory /%s entry %lu has file but " "no name (fixed: %s)", pathsofar, (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)", pathsofar, (unsigned long) index); dchanged = 1; } if (checkbadstring(sfd->sfd_name)) { setbadness(EXIT_RECOV); warnx("Directory /%s entry %lu contains invalid " "characters (fixed)", pathsofar, (unsigned long) index); dchanged = 1; } } return dchanged; } //////////////////////////////////////////////////////////// static int check_dir(uint32_t ino, uint32_t parentino, const char *pathsofar) { struct sfs_inode sfi; struct sfs_dir *direntries; int *sortvector; uint32_t dirsize, ndirentries, maxdirentries, subdircount, i; int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0; diskread(&sfi, ino); swapinode(&sfi); if (remember_dir(ino, pathsofar)) { /* crosslinked dir */ return 1; } bitmap_mark(ino, B_INODE, ino); count_dirs++; if (sfi.sfi_size % sizeof(struct sfs_dir) != 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_dir)); ichanged = 1; } if (check_inode_blocks(ino, &sfi, 1)) { ichanged = 1; } ndirentries = sfi.sfi_size/sizeof(struct sfs_dir); maxdirentries = SFS_ROUNDUP(ndirentries, SFS_BLOCKSIZE/sizeof(struct sfs_dir)); dirsize = maxdirentries * sizeof(struct sfs_dir); direntries = domalloc(dirsize); sortvector = domalloc(ndirentries * sizeof(int)); dirread(&sfi, direntries, ndirentries); for (i=ndirentries; isfd_ino == SFS_NOINO) { 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 { snprintf(d1->sfd_name, sizeof(d1->sfd_name), "FSCK.%lu.%lu", (unsigned long) d1->sfd_ino, (unsigned long) uniquecounter++); setbadness(EXIT_RECOV); warnx("Directory /%s: Duplicate names %s " "(one renamed: %s)", pathsofar, d2->sfd_name, d1->sfd_name); } dchanged = 1; } } for (i=0; i