os161-1.99
 All Data Structures
sfsck.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 #include <sys/types.h>
00031 #include <assert.h>
00032 #include <limits.h>
00033 #include <stdint.h>
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <err.h>
00038 
00039 #include "support.h"
00040 #include "kern/sfs.h"
00041 
00042 #ifdef HOST
00043 #include <netinet/in.h> // for arpa/inet.h
00044 #include <arpa/inet.h>  // for ntohl
00045 #include "hostcompat.h"
00046 #define SWAPL(x) ntohl(x)
00047 #define SWAPS(x) ntohs(x)
00048 
00049 #else
00050 
00051 #define SWAPL(x) (x)
00052 #define SWAPS(x) (x)
00053 #define NO_REALLOC
00054 #define NO_QSORT
00055 
00056 #endif
00057 
00058 #include "disk.h"
00059 
00060 
00061 #define EXIT_USAGE    4
00062 #define EXIT_FATAL    3
00063 #define EXIT_UNRECOV  2
00064 #define EXIT_RECOV    1
00065 #define EXIT_CLEAN    0
00066 
00067 static int badness=0;
00068 
00069 static
00070 void
00071 setbadness(int code)
00072 {
00073         if (badness < code) {
00074                 badness = code;
00075         }
00076 }
00077 
00078 ////////////////////////////////////////////////////////////
00079 
00080 static
00081 void
00082 swapsb(struct sfs_super *sp)
00083 {
00084         sp->sp_magic = SWAPL(sp->sp_magic);
00085         sp->sp_nblocks = SWAPL(sp->sp_nblocks);
00086 }
00087 
00088 static
00089 void
00090 swapinode(struct sfs_inode *sfi)
00091 {
00092         int i;
00093 
00094         sfi->sfi_size = SWAPL(sfi->sfi_size);
00095         sfi->sfi_type = SWAPS(sfi->sfi_type);
00096         sfi->sfi_linkcount = SWAPS(sfi->sfi_linkcount);
00097 
00098         for (i=0; i<SFS_NDIRECT; i++) {
00099                 sfi->sfi_direct[i] = SWAPL(sfi->sfi_direct[i]);
00100         }
00101 
00102 #ifdef SFS_NIDIRECT
00103         for (i=0; i<SFS_NIDIRECT; i++) {
00104                 sfi->sfi_indirect[i] = SWAPL(sfi->sfi_indirect[i]);
00105         }
00106 #else
00107         sfi->sfi_indirect = SWAPL(sfi->sfi_indirect);
00108 #endif
00109 
00110 #ifdef SFS_NDIDIRECT
00111         for (i=0; i<SFS_NDIDIRECT; i++) {
00112                 sfi->sfi_dindirect[i] = SWAPL(sfi->sfi_dindirect[i]);
00113         }
00114 #else
00115 #ifdef HAS_DIDIRECT
00116         sfi->sfi_dindirect = SWAPL(sfi->sfi_dindirect);
00117 #endif
00118 #endif
00119 
00120 #ifdef SFS_NTIDIRECT
00121         for (i=0; i<SFS_NTIDIRECT; i++) {
00122                 sfi->sfi_tindirect[i] = SWAPL(sfi->sfi_tindirect[i]);
00123         }
00124 #else
00125 #ifdef HAS_TIDIRECT
00126         sfi->sfi_tindirect = SWAPL(sfi->sfi_tindirect);
00127 #endif
00128 #endif
00129 }
00130 
00131 static
00132 void
00133 swapdir(struct sfs_dir *sfd)
00134 {
00135         sfd->sfd_ino = SWAPL(sfd->sfd_ino);
00136 }
00137 
00138 static
00139 void
00140 swapindir(uint32_t *entries)
00141 {
00142         int i;
00143         for (i=0; i<SFS_DBPERIDB; i++) {
00144                 entries[i] = SWAPL(entries[i]);
00145         }
00146 }
00147 
00148 static
00149 void
00150 swapbits(uint8_t *bits)
00151 {
00152         /* nothing to do */
00153         (void)bits;
00154 }
00155 
00156 ////////////////////////////////////////////////////////////
00157 
00158 static
00159 void *
00160 domalloc(size_t len)
00161 {
00162         void *x;
00163         x = malloc(len);
00164         if (x==NULL) {
00165                 errx(EXIT_FATAL, "Out of memory");
00166         }
00167         return x;
00168 }
00169 
00170 ////////////////////////////////////////////////////////////
00171 
00172 typedef enum {
00173         B_SUPERBLOCK,   /* Block that is the superblock */
00174         B_BITBLOCK,     /* Block used by free-block bitmap */
00175         B_INODE,        /* Block that is an inode */
00176         B_IBLOCK,       /* Indirect (or doubly-indirect etc.) block */
00177         B_DIRDATA,      /* Data block of a directory */
00178         B_DATA,         /* Data block */
00179         B_TOFREE,       /* Block that was used but we are releasing */
00180         B_PASTEND,      /* Block off the end of the fs */
00181 } blockusage_t;
00182 
00183 static uint32_t nblocks, bitblocks;
00184 static uint32_t uniquecounter = 1;
00185 
00186 static unsigned long count_blocks=0, count_dirs=0, count_files=0;
00187 
00188 ////////////////////////////////////////////////////////////
00189 
00190 static uint8_t *bitmapdata;
00191 static uint8_t *tofreedata;
00192 
00193 static
00194 void
00195 bitmap_init(uint32_t bitblocks)
00196 {
00197         size_t i, mapsize = bitblocks * SFS_BLOCKSIZE;
00198         bitmapdata = domalloc(mapsize * sizeof(uint8_t));
00199         tofreedata = domalloc(mapsize * sizeof(uint8_t));
00200         for (i=0; i<mapsize; i++) {
00201                 bitmapdata[i] = tofreedata[i] = 0;
00202         }
00203 }
00204 
00205 static
00206 const char *
00207 blockusagestr(blockusage_t how, uint32_t howdesc)
00208 {
00209         static char rv[256];
00210         switch (how) {
00211             case B_SUPERBLOCK: return "superblock";
00212             case B_BITBLOCK: return "bitmap block";
00213             case B_INODE: return "inode";
00214             case B_IBLOCK: 
00215                 snprintf(rv, sizeof(rv), "indirect block of inode %lu", 
00216                          (unsigned long) howdesc);
00217                 break;
00218             case B_DIRDATA:
00219                 snprintf(rv, sizeof(rv), "directory data from inode %lu", 
00220                          (unsigned long) howdesc);
00221                 break;
00222             case B_DATA:
00223                 snprintf(rv, sizeof(rv), "file data from inode %lu", 
00224                          (unsigned long) howdesc);
00225                 break;
00226             case B_TOFREE:
00227                 assert(0);
00228                 break;
00229             case B_PASTEND:
00230                 return "past the end of the fs";
00231         }
00232         return rv;
00233 }
00234 
00235 static
00236 void
00237 bitmap_mark(uint32_t block, blockusage_t how, uint32_t howdesc)
00238 {
00239         unsigned index = block/8;
00240         uint8_t mask = ((uint8_t)1)<<(block%8);
00241 
00242         if (how == B_TOFREE) {
00243                 if (tofreedata[index] & mask) {
00244                         /* already marked to free once, ignore */
00245                         return;
00246                 }
00247                 if (bitmapdata[index] & mask) {
00248                         /* block is used elsewhere, ignore */
00249                         return;
00250                 }
00251                 tofreedata[index] |= mask;
00252                 return;
00253         }
00254 
00255         if (tofreedata[index] & mask) {
00256                 /* really using the block, don't free it */
00257                 tofreedata[index] &= ~mask;
00258         }
00259 
00260         if (bitmapdata[index] & mask) {
00261                 warnx("Block %lu (used as %s) already in use! (NOT FIXED)",
00262                       (unsigned long) block, blockusagestr(how, howdesc));
00263                 setbadness(EXIT_UNRECOV);
00264         }
00265 
00266         bitmapdata[index] |= mask;
00267 
00268         if (how != B_PASTEND) {
00269                 count_blocks++;
00270         }
00271 }
00272 
00273 static
00274 int
00275 countbits(uint8_t val)
00276 {
00277         uint8_t x;
00278         int ct=0;
00279 
00280         for (x=1; x; x<<=1) {
00281                 if (val & x) ct++;
00282         }
00283         return ct;
00284 }
00285 
00286 static
00287 void
00288 reportbits(uint32_t bitblock, uint32_t byte, uint8_t val, const char *what)
00289 {
00290         uint8_t x, y;
00291         uint32_t blocknum;
00292 
00293         for (x=1, y=0; x; x<<=1, y++) {
00294                 if (val & x) {
00295                         blocknum = bitblock*SFS_BLOCKBITS + byte*CHAR_BIT + y;
00296                         warnx("Block %lu erroneously shown %s in bitmap",
00297                               (unsigned long) blocknum, what);
00298                 }
00299         }
00300 }
00301 
00302 static
00303 void
00304 check_bitmap(void)
00305 {
00306         uint8_t bits[SFS_BLOCKSIZE], *found, *tofree, tmp;
00307         uint32_t alloccount=0, freecount=0, i, j;
00308         int bchanged;
00309 
00310         for (i=0; i<bitblocks; i++) {
00311                 diskread(bits, SFS_MAP_LOCATION+i);
00312                 swapbits(bits);
00313                 found = bitmapdata + i*SFS_BLOCKSIZE;
00314                 tofree = tofreedata + i*SFS_BLOCKSIZE;
00315                 bchanged = 0;
00316 
00317                 for (j=0; j<SFS_BLOCKSIZE; j++) {
00318                         /* we shouldn't have blocks marked both ways */
00319                         assert((found[j] & tofree[j])==0);
00320 
00321                         if (bits[j]==found[j]) {
00322                                 continue;
00323                         }
00324 
00325                         if (bits[j]==(found[j] | tofree[j])) {
00326                                 bits[j] = found[j];
00327                                 bchanged = 1;
00328                                 continue;
00329                         }
00330 
00331                         /* free the ones we're freeing */
00332                         bits[j] &= ~tofree[j];
00333 
00334                         /* are we short any? */
00335                         if ((bits[j] & found[j]) != found[j]) {
00336                                 tmp = found[j] & ~bits[j];
00337                                 alloccount += countbits(tmp);
00338                                 if (tmp != 0) {
00339                                         reportbits(i, j, tmp, "free");
00340                                 }
00341                         }
00342 
00343                         /* do we have any extra? */
00344                         if ((bits[j] & found[j]) != bits[j]) {
00345                                 tmp = bits[j] & ~found[j];
00346                                 freecount += countbits(tmp);
00347                                 if (tmp != 0) {
00348                                         reportbits(i, j, tmp, "allocated");
00349                                 }
00350                         }
00351 
00352                         bits[j] = found[j];
00353                         bchanged = 1;
00354                 }
00355 
00356                 if (bchanged) {
00357                         swapbits(bits);
00358                         diskwrite(bits, SFS_MAP_LOCATION+i);
00359                 }
00360         }
00361 
00362         if (alloccount > 0) {
00363                 warnx("%lu blocks erroneously shown free in bitmap (fixed)",
00364                       (unsigned long) alloccount);
00365                 setbadness(EXIT_RECOV);
00366         }
00367         if (freecount > 0) {
00368                 warnx("%lu blocks erroneously shown used in bitmap (fixed)",
00369                       (unsigned long) freecount);
00370                 setbadness(EXIT_RECOV);
00371         }
00372 }
00373 
00374 ////////////////////////////////////////////////////////////
00375 
00376 struct inodememory {
00377         uint32_t ino;
00378         uint32_t linkcount;     /* files only; 0 for dirs */
00379 };
00380 
00381 static struct inodememory *inodes = NULL;
00382 static int ninodes=0, maxinodes=0;
00383 
00384 static
00385 void
00386 addmemory(uint32_t ino, uint32_t linkcount)
00387 {
00388         assert(ninodes <= maxinodes);
00389         if (ninodes == maxinodes) {
00390 #ifdef NO_REALLOC
00391                 int newmax = (maxinodes+1)*2;
00392                 void *p = domalloc(newmax * sizeof(struct inodememory));
00393                 if (inodes) {
00394                         memcpy(p, inodes, ninodes);
00395                         free(inodes);
00396                 }
00397                 inodes = p;
00398 #else
00399                 maxinodes = (maxinodes+1)*2;
00400                 inodes = realloc(inodes, maxinodes * sizeof(uint32_t));
00401                 if (inodes==NULL) {
00402                         errx(EXIT_FATAL, "Out of memory");
00403                 }
00404 #endif
00405         }
00406         inodes[ninodes].ino = ino;
00407         inodes[ninodes].linkcount = linkcount;
00408 }
00409 
00410 /* returns nonzero if directory already remembered */
00411 static
00412 int
00413 remember_dir(uint32_t ino, const char *pathsofar)
00414 {
00415         int i;
00416 
00417         /* don't use this for now */
00418         (void)pathsofar;
00419 
00420         for (i=0; i<ninodes; i++) {
00421                 if (inodes[i].ino==ino) {
00422                         assert(inodes[i].linkcount==0);
00423                         return 1;
00424                 }
00425         }
00426 
00427         addmemory(ino, 0);
00428 
00429         return 0;
00430 }
00431 
00432 static
00433 void
00434 observe_filelink(uint32_t ino)
00435 {
00436         int i;
00437         for (i=0; i<ninodes; i++) {
00438                 if (inodes[i].ino==ino) {
00439                         assert(inodes[i].linkcount>0);
00440                         inodes[i].linkcount++;
00441                         return;
00442                 }
00443         }
00444         bitmap_mark(ino, B_INODE, ino);
00445         addmemory(ino, 1);
00446 }
00447 
00448 static
00449 void
00450 adjust_filelinks(void)
00451 {
00452         struct sfs_inode sfi;
00453         int i;
00454 
00455         for (i=0; i<ninodes; i++) {
00456                 if (inodes[i].linkcount==0) {
00457                         /* directory */
00458                         continue;
00459                 }
00460                 diskread(&sfi, inodes[i].ino);
00461                 swapinode(&sfi);
00462                 assert(sfi.sfi_type == SFS_TYPE_FILE);
00463                 if (sfi.sfi_linkcount != inodes[i].linkcount) {
00464                         warnx("File %lu link count %lu should be %lu (fixed)",
00465                               (unsigned long) inodes[i].ino,
00466                               (unsigned long) sfi.sfi_linkcount,
00467                               (unsigned long) inodes[i].linkcount);
00468                         sfi.sfi_linkcount = inodes[i].linkcount;
00469                         setbadness(EXIT_RECOV);
00470                         swapinode(&sfi);
00471                         diskwrite(&sfi, inodes[i].ino);
00472                 }
00473                 count_files++;
00474         }
00475 }
00476 
00477 ////////////////////////////////////////////////////////////
00478 
00479 static
00480 int
00481 checknullstring(char *buf, size_t maxlen)
00482 {
00483         size_t i;
00484         for (i=0; i<maxlen; i++) {
00485                 if (buf[i]==0) {
00486                         return 0;
00487                 }
00488         }
00489         buf[maxlen-1] = 0;
00490         return 1;
00491 }
00492 
00493 static
00494 int
00495 checkbadstring(char *buf)
00496 {
00497         size_t i;
00498         int rv=0;
00499 
00500         for (i=0; buf[i]; i++) {
00501                 if (buf[i]==':' || buf[i]=='/') {
00502                         buf[i] = '_';
00503                         rv = 1;
00504                 }
00505         }
00506         return rv;
00507 }
00508 
00509 ////////////////////////////////////////////////////////////
00510 
00511 static
00512 void
00513 check_sb(void)
00514 {
00515         struct sfs_super sp;
00516         uint32_t i;
00517         int schanged=0;
00518 
00519         diskread(&sp, SFS_SB_LOCATION);
00520         swapsb(&sp);
00521         if (sp.sp_magic != SFS_MAGIC) {
00522                 errx(EXIT_UNRECOV, "Not an sfs filesystem");
00523         }
00524 
00525         assert(nblocks==0);
00526         assert(bitblocks==0);
00527         nblocks = sp.sp_nblocks;
00528         bitblocks = SFS_BITBLOCKS(nblocks);
00529         assert(nblocks>0);
00530         assert(bitblocks>0);
00531 
00532         bitmap_init(bitblocks);
00533         for (i=nblocks; i<bitblocks*SFS_BLOCKBITS; i++) {
00534                 bitmap_mark(i, B_PASTEND, 0);
00535         }
00536 
00537         if (checknullstring(sp.sp_volname, sizeof(sp.sp_volname))) {
00538                 warnx("Volume name not null-terminated (fixed)");
00539                 setbadness(EXIT_RECOV);
00540                 schanged = 1;
00541         }
00542         if (checkbadstring(sp.sp_volname)) {
00543                 warnx("Volume name contains illegal characters (fixed)");
00544                 setbadness(EXIT_RECOV);
00545                 schanged = 1;
00546         }
00547 
00548         if (schanged) {
00549                 swapsb(&sp);
00550                 diskwrite(&sp, SFS_SB_LOCATION);
00551         }
00552 
00553         bitmap_mark(SFS_SB_LOCATION, B_SUPERBLOCK, 0);
00554         for (i=0; i<bitblocks; i++) {
00555                 bitmap_mark(SFS_MAP_LOCATION+i, B_BITBLOCK, i);
00556         }
00557 }
00558 
00559 ////////////////////////////////////////////////////////////
00560 
00561 static
00562 void
00563 check_indirect_block(uint32_t ino, uint32_t *ientry, uint32_t *blockp,
00564                      uint32_t nblocks, uint32_t *badcountp, 
00565                      int isdir, int indirection)
00566 {
00567         uint32_t entries[SFS_DBPERIDB];
00568         uint32_t i, ct;
00569 
00570         if (*ientry !=0) {
00571                 diskread(entries, *ientry);
00572                 swapindir(entries);
00573                 bitmap_mark(*ientry, B_IBLOCK, ino);
00574         }
00575         else {
00576                 for (i=0; i<SFS_DBPERIDB; i++) {
00577                         entries[i] = 0;
00578                 }
00579         }
00580 
00581         if (indirection > 1) {
00582                 for (i=0; i<SFS_DBPERIDB; i++) {
00583                         check_indirect_block(ino, &entries[i], 
00584                                              blockp, nblocks, 
00585                                              badcountp,
00586                                              isdir,
00587                                              indirection-1);
00588                 }
00589         }
00590         else {
00591                 assert(indirection==1);
00592 
00593                 for (i=0; i<SFS_DBPERIDB; i++) {
00594                         if (*blockp < nblocks) {
00595                                 if (entries[i] != 0) {
00596                                         bitmap_mark(entries[i],
00597                                                     isdir ? B_DIRDATA : B_DATA,
00598                                                     ino);
00599                                 }
00600                         }
00601                         else {
00602                                 if (entries[i] != 0) {
00603                                         (*badcountp)++;
00604                                         bitmap_mark(entries[i],
00605                                                     isdir ? B_DIRDATA : B_DATA,
00606                                                     ino);
00607                                         entries[i] = 0;
00608                                 }
00609                         }
00610                         (*blockp)++;
00611                 }
00612         }
00613 
00614         ct=0;
00615         for (i=ct=0; i<SFS_DBPERIDB; i++) {
00616                 if (entries[i]!=0) ct++;
00617         }
00618         if (ct==0) {
00619                 if (*ientry != 0) {
00620                         (*badcountp)++;
00621                         bitmap_mark(*ientry, B_TOFREE, 0);
00622                         *ientry = 0;
00623                 }
00624         }
00625         else {
00626                 assert(*ientry != 0);
00627                 if (*badcountp > 0) {
00628                         swapindir(entries);
00629                         diskwrite(entries, *ientry);
00630                 }
00631         }
00632 }
00633 
00634 /* returns nonzero if inode modified */
00635 static
00636 int
00637 check_inode_blocks(uint32_t ino, struct sfs_inode *sfi, int isdir)
00638 {
00639         uint32_t size, block, nblocks, badcount;
00640 
00641         badcount = 0;
00642 
00643         size = SFS_ROUNDUP(sfi->sfi_size, SFS_BLOCKSIZE);
00644         nblocks = size/SFS_BLOCKSIZE;
00645 
00646         for (block=0; block<SFS_NDIRECT; block++) {
00647                 if (block < nblocks) {
00648                         if (sfi->sfi_direct[block] != 0) {
00649                                 bitmap_mark(sfi->sfi_direct[block],
00650                                             isdir ? B_DIRDATA : B_DATA, ino);
00651                         }
00652                 }
00653                 else {
00654                         if (sfi->sfi_direct[block] != 0) {
00655                                 badcount++;
00656                                 bitmap_mark(sfi->sfi_direct[block],
00657                                             B_TOFREE, 0);
00658                         }                       
00659                 }
00660         }
00661 
00662 #ifdef SFS_NIDIRECT
00663         for (i=0; i<SFS_NIDIRECT; i++) {
00664                 check_indirect_block(ino, &sfi->sfi_indirect[i], 
00665                                      &block, nblocks, &badcount, isdir, 1);
00666         }
00667 #else
00668         check_indirect_block(ino, &sfi->sfi_indirect, 
00669                              &block, nblocks, &badcount, isdir, 1);
00670 #endif
00671 
00672 #ifdef SFS_NDIDIRECT
00673         for (i=0; i<SFS_NDIDIRECT; i++) {
00674                 check_indirect_block(ino, &sfi->sfi_dindirect[i], 
00675                                      &block, nblocks, &badcount, isdir, 2);
00676         }
00677 #else
00678 #ifdef HAS_DIDIRECT
00679         check_indirect_block(ino, &sfi->sfi_dindirect, 
00680                              &block, nblocks, &badcount, isdir, 2);
00681 #endif
00682 #endif
00683 
00684 #ifdef SFS_NTIDIRECT
00685         for (i=0; i<SFS_NTIDIRECT; i++) {
00686                 check_indirect_block(ino, &sfi->sfi_tindirect[i], 
00687                                      &block, nblocks, &badcount, isdir, 3);
00688         }
00689 #else
00690 #ifdef HAS_TIDIRECT
00691         check_indirect_block(ino, &sfi->sfi_tindirect, 
00692                              &block, nblocks, &badcount, isdir, 3);
00693 #endif
00694 #endif
00695 
00696         if (badcount > 0) {
00697                 warnx("Inode %lu: %lu blocks after EOF (freed)", 
00698                      (unsigned long) ino, (unsigned long) badcount);
00699                 setbadness(EXIT_RECOV);
00700                 return 1;
00701         }
00702 
00703         return 0;
00704 }
00705 
00706 ////////////////////////////////////////////////////////////
00707 
00708 static
00709 uint32_t
00710 ibmap(uint32_t iblock, uint32_t offset, uint32_t entrysize)
00711 {
00712         uint32_t entries[SFS_DBPERIDB];
00713 
00714         if (iblock == 0) {
00715                 return 0;
00716         }
00717 
00718         diskread(entries, iblock);
00719         swapindir(entries);
00720 
00721         if (entrysize > 1) {
00722                 uint32_t index = offset / entrysize;
00723                 offset %= entrysize;
00724                 return ibmap(entries[index], offset, entrysize/SFS_DBPERIDB);
00725         }
00726         else {
00727                 assert(offset < SFS_DBPERIDB);
00728                 return entries[offset];
00729         }
00730 }
00731 
00732 #define BMAP_ND                 SFS_NDIRECT
00733 #define BMAP_D(sfi, x)          ((sfi)->sfi_direct[(x)])
00734 
00735 #ifdef SFS_NIDIRECT
00736 #define BMAP_NI                 SFS_NIDIRECT
00737 #define BMAP_I(sfi, x)          ((sfi)->sfi_indirect[(x)])
00738 #else
00739 #define BMAP_NI                 1
00740 #define BMAP_I(sfi, x)          ((void)(x), (sfi)->sfi_indirect)
00741 #endif
00742 
00743 #ifdef SFS_NDIDIRECT
00744 #define BMAP_NII                SFS_NDIDIRECT
00745 #define BMAP_II(sfi, x)         ((sfi)->sfi_dindirect[(x)])
00746 #else
00747 #ifdef HAS_DIDIRECT
00748 #define BMAP_NII                1
00749 #define BMAP_II(sfi, x)         ((void)(x), (sfi)->sfi_dindirect)
00750 #else
00751 #define BMAP_NII                0
00752 #define BMAP_II(sfi, x)         ((void)(x), (void)(sfi), 0)
00753 #endif
00754 #endif
00755 
00756 #ifdef SFS_NTIDIRECT
00757 #define BMAP_NIII               SFS_NTIDIRECT
00758 #define BMAP_III(sfi, x)        ((sfi)->sfi_tindirect[(x)])
00759 #else
00760 #ifdef HAS_TIDIRECT
00761 #define BMAP_NIII               1
00762 #define BMAP_III(sfi, x)        ((void)(x), (sfi)->sfi_tindirect)
00763 #else
00764 #define BMAP_NIII               0
00765 #define BMAP_III(sfi, x)        ((void)(x), (void)(sfi), 0)
00766 #endif
00767 #endif
00768 
00769 #define BMAP_DMAX   BMAP_ND
00770 #define BMAP_IMAX   (BMAP_DMAX+SFS_DBPERIDB*BMAP_NI)
00771 #define BMAP_IIMAX  (BMAP_IMAX+SFS_DBPERIDB*BMAP_NII)
00772 #define BMAP_IIIMAX (BMAP_IIMAX+SFS_DBPERIDB*BMAP_NIII)
00773 
00774 #define BMAP_DSIZE      1
00775 #define BMAP_ISIZE      (BMAP_DSIZE*SFS_DBPERIDB)
00776 #define BMAP_IISIZE     (BMAP_ISIZE*SFS_DBPERIDB)
00777 #define BMAP_IIISIZE    (BMAP_IISIZE*SFS_DBPERIDB)
00778 
00779 static
00780 uint32_t
00781 dobmap(const struct sfs_inode *sfi, uint32_t fileblock)
00782 {
00783         uint32_t iblock, offset;
00784 
00785         if (fileblock < BMAP_DMAX) {
00786                 return BMAP_D(sfi, fileblock);
00787         }
00788         else if (fileblock < BMAP_IMAX) {
00789                 iblock = (fileblock - BMAP_DMAX)/BMAP_ISIZE;
00790                 offset = (fileblock - BMAP_DMAX)%BMAP_ISIZE;
00791                 return ibmap(BMAP_I(sfi, iblock), offset, BMAP_DSIZE);
00792         }
00793         else if (fileblock < BMAP_IIMAX) {
00794                 iblock = (fileblock - BMAP_IMAX)/BMAP_IISIZE;
00795                 offset = (fileblock - BMAP_IMAX)%BMAP_IISIZE;
00796                 return ibmap(BMAP_II(sfi, iblock), offset, BMAP_ISIZE);
00797         }
00798         else if (fileblock < BMAP_IIIMAX) {
00799                 iblock = (fileblock - BMAP_IIMAX)/BMAP_IIISIZE;
00800                 offset = (fileblock - BMAP_IIMAX)%BMAP_IIISIZE;
00801                 return ibmap(BMAP_III(sfi, iblock), offset, BMAP_IISIZE);
00802         }
00803         return 0;
00804 }
00805 
00806 static
00807 void
00808 dirread(struct sfs_inode *sfi, struct sfs_dir *d, unsigned nd)
00809 {
00810         const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir);
00811         unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
00812         unsigned i, j;
00813 
00814         for (i=0; i<nblocks; i++) {
00815                 uint32_t block = dobmap(sfi, i);
00816                 if (block!=0) {
00817                         diskread(d + i*atonce, block);
00818                         for (j=0; j<atonce; j++) {
00819                                 swapdir(&d[i*atonce+j]);
00820                         }
00821                 }
00822                 else {
00823                         warnx("Warning: sparse directory found");
00824                         bzero(d + i*atonce, SFS_BLOCKSIZE);
00825                 }
00826         }
00827 }
00828 
00829 static
00830 void
00831 dirwrite(const struct sfs_inode *sfi, struct sfs_dir *d, int nd)
00832 {
00833         const unsigned atonce = SFS_BLOCKSIZE/sizeof(struct sfs_dir);
00834         unsigned nblocks = SFS_ROUNDUP(nd, atonce) / atonce;
00835         unsigned i, j, bad;
00836 
00837         for (i=0; i<nblocks; i++) {
00838                 uint32_t block = dobmap(sfi, i);
00839                 if (block!=0) {
00840                         for (j=0; j<atonce; j++) {
00841                                 swapdir(&d[i*atonce+j]);
00842                         }
00843                         diskwrite(d + i*atonce, block);
00844                 }
00845                 else {
00846                         for (j=bad=0; j<atonce; j++) {
00847                                 if (d[i*atonce+j].sfd_ino != SFS_NOINO ||
00848                                     d[i*atonce+j].sfd_name[0] != 0) {
00849                                         bad = 1;
00850                                 }
00851                         }
00852                         if (bad) {
00853                                 warnx("Cannot write to missing block in "
00854                                       "sparse directory (ERROR)");
00855                                 setbadness(EXIT_UNRECOV);
00856                         }
00857                 }
00858         }
00859 }
00860 
00861 ////////////////////////////////////////////////////////////
00862 
00863 static struct sfs_dir *global_sortdirs;
00864 static
00865 int
00866 dirsortfunc(const void *aa, const void *bb)
00867 {
00868         const int *a = (const int *)aa;
00869         const int *b = (const int *)bb;
00870         const struct sfs_dir *ad = &global_sortdirs[*a];
00871         const struct sfs_dir *bd = &global_sortdirs[*b];
00872         return strcmp(ad->sfd_name, bd->sfd_name);
00873 }
00874 
00875 #ifdef NO_QSORT
00876 static
00877 void
00878 qsort(int *data, int num, size_t size, int (*f)(const void *, const void *))
00879 {
00880         int i, j;
00881         (void)size;
00882 
00883         /* because I'm lazy, bubble sort */
00884         for (i=0; i<num-1; i++) {
00885                 for (j=i+1; j<num; j++) {
00886                         if (f(&data[i], &data[j]) < 0) {
00887                                 int tmp = data[i];
00888                                 data[i] = data[j];
00889                                 data[j] = tmp;
00890                         }
00891                 }
00892         }
00893 }
00894 #endif
00895 
00896 static
00897 void
00898 sortdir(int *vector, struct sfs_dir *d, int nd)
00899 {
00900         global_sortdirs = d;
00901         qsort(vector, nd, sizeof(int), dirsortfunc);
00902 }
00903 
00904 /* tries to add a directory entry; returns 0 on success */
00905 static
00906 int
00907 dir_tryadd(struct sfs_dir *d, int nd, const char *name, uint32_t ino)
00908 {
00909         int i;
00910         for (i=0; i<nd; i++) {
00911                 if (d[i].sfd_ino==SFS_NOINO) {
00912                         d[i].sfd_ino = ino;
00913                         assert(strlen(name) < sizeof(d[i].sfd_name));
00914                         strcpy(d[i].sfd_name, name);
00915                         return 0;
00916                 }
00917         }
00918         return -1;
00919 }
00920 
00921 static
00922 int
00923 check_dir_entry(const char *pathsofar, uint32_t index, struct sfs_dir *sfd)
00924 {
00925         int dchanged = 0;
00926 
00927         if (sfd->sfd_ino == SFS_NOINO) {
00928                 if (sfd->sfd_name[0] != 0) {
00929                         setbadness(EXIT_RECOV);
00930                         warnx("Directory /%s entry %lu has name but no file",
00931                               pathsofar, (unsigned long) index);
00932                         sfd->sfd_name[0] = 0;
00933                         dchanged = 1;
00934                 }
00935         }
00936         else {
00937                 if (sfd->sfd_name[0] == 0) {
00938                         snprintf(sfd->sfd_name, sizeof(sfd->sfd_name),
00939                                  "FSCK.%lu.%lu",
00940                                  (unsigned long) sfd->sfd_ino,
00941                                  (unsigned long) uniquecounter++);
00942                         setbadness(EXIT_RECOV);
00943                         warnx("Directory /%s entry %lu has file but "
00944                               "no name (fixed: %s)",
00945                               pathsofar, (unsigned long) index,
00946                               sfd->sfd_name);
00947                         dchanged = 1;
00948                 }
00949                 if (checknullstring(sfd->sfd_name, sizeof(sfd->sfd_name))) {
00950                         setbadness(EXIT_RECOV);
00951                         warnx("Directory /%s entry %lu not "
00952                               "null-terminated (fixed)",
00953                               pathsofar, (unsigned long) index);
00954                         dchanged = 1;
00955                 }
00956                 if (checkbadstring(sfd->sfd_name)) {
00957                         setbadness(EXIT_RECOV);
00958                         warnx("Directory /%s entry %lu contains invalid "
00959                               "characters (fixed)",
00960                               pathsofar, (unsigned long) index);
00961                         dchanged = 1;
00962                 }
00963         }
00964         return dchanged;
00965 }
00966 
00967 ////////////////////////////////////////////////////////////
00968 
00969 static
00970 int
00971 check_dir(uint32_t ino, uint32_t parentino, const char *pathsofar)
00972 {
00973         struct sfs_inode sfi;
00974         struct sfs_dir *direntries;
00975         int *sortvector;
00976         uint32_t dirsize, ndirentries, maxdirentries, subdircount, i;
00977         int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0;
00978 
00979         diskread(&sfi, ino);
00980         swapinode(&sfi);
00981 
00982         if (remember_dir(ino, pathsofar)) {
00983                 /* crosslinked dir */
00984                 return 1;
00985         }
00986 
00987         bitmap_mark(ino, B_INODE, ino);
00988         count_dirs++;
00989 
00990         if (sfi.sfi_size % sizeof(struct sfs_dir) != 0) {
00991                 setbadness(EXIT_RECOV);
00992                 warnx("Directory /%s has illegal size %lu (fixed)",
00993                       pathsofar, (unsigned long) sfi.sfi_size);
00994                 sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size, 
00995                                            sizeof(struct sfs_dir));
00996                 ichanged = 1;
00997         }
00998 
00999         if (check_inode_blocks(ino, &sfi, 1)) {
01000                 ichanged = 1;
01001         }
01002 
01003         ndirentries = sfi.sfi_size/sizeof(struct sfs_dir);
01004         maxdirentries = SFS_ROUNDUP(ndirentries, 
01005                                     SFS_BLOCKSIZE/sizeof(struct sfs_dir));
01006         dirsize = maxdirentries * sizeof(struct sfs_dir);
01007         direntries = domalloc(dirsize);
01008         sortvector = domalloc(ndirentries * sizeof(int));
01009 
01010         dirread(&sfi, direntries, ndirentries);
01011         for (i=ndirentries; i<maxdirentries; i++) {
01012                 direntries[i].sfd_ino = SFS_NOINO;
01013                 bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name));
01014         }
01015 
01016         for (i=0; i<ndirentries; i++) {
01017                 if (check_dir_entry(pathsofar, i, &direntries[i])) {
01018                         dchanged = 1;
01019                 }
01020                 sortvector[i] = i;
01021         }
01022 
01023         sortdir(sortvector, direntries, ndirentries);
01024 
01025         /* don't use ndirentries-1 here in case ndirentries == 0 */
01026         for (i=0; i+1<ndirentries; i++) {
01027                 struct sfs_dir *d1 = &direntries[sortvector[i]];
01028                 struct sfs_dir *d2 = &direntries[sortvector[i+1]];
01029                 assert(d1 != d2);
01030 
01031                 if (d1->sfd_ino == SFS_NOINO) {
01032                         continue;
01033                 }
01034 
01035                 if (!strcmp(d1->sfd_name, d2->sfd_name)) {
01036                         if (d1->sfd_ino == d2->sfd_ino) {
01037                                 setbadness(EXIT_RECOV);
01038                                 warnx("Directory /%s: Duplicate entries for "
01039                                       "%s (merged)",
01040                                       pathsofar, d1->sfd_name);
01041                                 d1->sfd_ino = SFS_NOINO;
01042                                 d1->sfd_name[0] = 0;
01043                         }
01044                         else {
01045                                 snprintf(d1->sfd_name, sizeof(d1->sfd_name),
01046                                          "FSCK.%lu.%lu",
01047                                          (unsigned long) d1->sfd_ino,
01048                                          (unsigned long) uniquecounter++);
01049                                 setbadness(EXIT_RECOV);
01050                                 warnx("Directory /%s: Duplicate names %s "
01051                                       "(one renamed: %s)",
01052                                       pathsofar, d2->sfd_name, d1->sfd_name);
01053                         }
01054                         dchanged = 1;
01055                 }
01056         }
01057 
01058         for (i=0; i<ndirentries; i++) {
01059                 if (!strcmp(direntries[i].sfd_name, ".")) {
01060                         if (direntries[i].sfd_ino != ino) {
01061                                 setbadness(EXIT_RECOV);
01062                                 warnx("Directory /%s: Incorrect `.' entry "
01063                                       "(fixed)", pathsofar);
01064                                 direntries[i].sfd_ino = ino;
01065                                 dchanged = 1;
01066                         }
01067                         assert(dotseen==0); /* due to duplicate checking */
01068                         dotseen = 1;
01069                 }
01070                 else if (!strcmp(direntries[i].sfd_name, "..")) {
01071                         if (direntries[i].sfd_ino != parentino) {
01072                                 setbadness(EXIT_RECOV);
01073                                 warnx("Directory /%s: Incorrect `..' entry "
01074                                       "(fixed)", pathsofar);
01075                                 direntries[i].sfd_ino = parentino;
01076                                 dchanged = 1;
01077                         }
01078                         assert(dotdotseen==0); /* due to duplicate checking */
01079                         dotdotseen = 1;
01080                 }
01081         }
01082 
01083         if (!dotseen) {
01084                 if (dir_tryadd(direntries, ndirentries, ".", ino)==0) {
01085                         setbadness(EXIT_RECOV);
01086                         warnx("Directory /%s: No `.' entry (added)",
01087                               pathsofar);
01088                         dchanged = 1;
01089                 }
01090                 else if (dir_tryadd(direntries, maxdirentries, ".", ino)==0) {
01091                         setbadness(EXIT_RECOV);
01092                         warnx("Directory /%s: No `.' entry (added)",
01093                               pathsofar);
01094                         ndirentries++;
01095                         dchanged = 1;
01096                         sfi.sfi_size += sizeof(struct sfs_dir);
01097                         ichanged = 1;
01098                 }
01099                 else {
01100                         setbadness(EXIT_UNRECOV);
01101                         warnx("Directory /%s: No `.' entry (NOT FIXED)",
01102                               pathsofar);
01103                 }
01104         }
01105 
01106         if (!dotdotseen) {
01107                 if (dir_tryadd(direntries, ndirentries, "..", parentino)==0) {
01108                         setbadness(EXIT_RECOV);
01109                         warnx("Directory /%s: No `..' entry (added)",
01110                               pathsofar);
01111                         dchanged = 1;
01112                 }
01113                 else if (dir_tryadd(direntries, maxdirentries, "..", 
01114                                     parentino)==0) {
01115                         setbadness(EXIT_RECOV);
01116                         warnx("Directory /%s: No `..' entry (added)",
01117                               pathsofar);
01118                         ndirentries++;
01119                         dchanged = 1;
01120                         sfi.sfi_size += sizeof(struct sfs_dir);
01121                         ichanged = 1;
01122                 }
01123                 else {
01124                         setbadness(EXIT_UNRECOV);
01125                         warnx("Directory /%s: No `..' entry (NOT FIXED)",
01126                               pathsofar);
01127                 }
01128         }
01129 
01130         subdircount=0;
01131         for (i=0; i<ndirentries; i++) {
01132                 if (!strcmp(direntries[i].sfd_name, ".")) {
01133                         /* nothing */
01134                 }
01135                 else if (!strcmp(direntries[i].sfd_name, "..")) {
01136                         /* nothing */
01137                 }
01138                 else if (direntries[i].sfd_ino == SFS_NOINO) {
01139                         /* nothing */
01140                 }
01141                 else {
01142                         char path[strlen(pathsofar)+SFS_NAMELEN+1];
01143                         struct sfs_inode subsfi;
01144 
01145                         diskread(&subsfi, direntries[i].sfd_ino);
01146                         swapinode(&subsfi);
01147                         snprintf(path, sizeof(path), "%s/%s", 
01148                                  pathsofar, direntries[i].sfd_name);
01149 
01150                         switch (subsfi.sfi_type) {
01151                             case SFS_TYPE_FILE:
01152                                 if (check_inode_blocks(direntries[i].sfd_ino,
01153                                                        &subsfi, 0)) {
01154                                         swapinode(&subsfi);
01155                                         diskwrite(&subsfi, 
01156                                                   direntries[i].sfd_ino);
01157                                 }
01158                                 observe_filelink(direntries[i].sfd_ino);
01159                                 break;
01160                             case SFS_TYPE_DIR:
01161                                 if (check_dir(direntries[i].sfd_ino,
01162                                               ino,
01163                                               path)) {
01164                                         setbadness(EXIT_RECOV);
01165                                         warnx("Directory /%s: Crosslink to "
01166                                               "other directory (removed)",
01167                                               path);
01168                                         direntries[i].sfd_ino = SFS_NOINO;
01169                                         direntries[i].sfd_name[0] = 0;
01170                                         dchanged = 1;
01171                                 }
01172                                 else {
01173                                         subdircount++;
01174                                 }
01175                                 break;
01176                             default:
01177                                 setbadness(EXIT_RECOV);
01178                                 warnx("Object /%s: Invalid inode type "
01179                                       "(removed)", path);
01180                                 direntries[i].sfd_ino = SFS_NOINO;
01181                                 direntries[i].sfd_name[0] = 0;
01182                                 dchanged = 1;
01183                                 break;
01184                         }
01185                 }
01186         }
01187 
01188         if (sfi.sfi_linkcount != subdircount+2) {
01189                 setbadness(EXIT_RECOV);
01190                 warnx("Directory /%s: Link count %lu should be %lu (fixed)",
01191                       pathsofar, (unsigned long) sfi.sfi_linkcount,
01192                       (unsigned long) subdircount+2);
01193                 sfi.sfi_linkcount = subdircount+2;
01194                 ichanged = 1;
01195         }
01196 
01197         if (dchanged) {
01198                 dirwrite(&sfi, direntries, ndirentries);
01199         }
01200 
01201         if (ichanged) {
01202                 swapinode(&sfi);
01203                 diskwrite(&sfi, ino);
01204         }
01205 
01206         free(direntries);
01207         free(sortvector);
01208 
01209         return 0;
01210 }
01211 
01212 
01213 static
01214 void
01215 check_root_dir(void)
01216 {
01217         struct sfs_inode sfi;
01218         diskread(&sfi, SFS_ROOT_LOCATION);
01219         swapinode(&sfi);
01220 
01221         switch (sfi.sfi_type) {
01222             case SFS_TYPE_DIR:
01223                 break;
01224             case SFS_TYPE_FILE:
01225                 warnx("Root directory inode is a regular file (fixed)");
01226                 goto fix;
01227             default:
01228                 warnx("Root directory inode has invalid type %lu (fixed)",
01229                       (unsigned long) sfi.sfi_type);
01230             fix:
01231                 setbadness(EXIT_RECOV);
01232                 sfi.sfi_type = SFS_TYPE_DIR;
01233                 swapinode(&sfi);
01234                 diskwrite(&sfi, SFS_ROOT_LOCATION);
01235                 break;
01236         }
01237 
01238         check_dir(SFS_ROOT_LOCATION, SFS_ROOT_LOCATION, "");
01239 }
01240 
01241 ////////////////////////////////////////////////////////////
01242 
01243 int
01244 main(int argc, char **argv)
01245 {
01246 #ifdef HOST
01247         hostcompat_init(argc, argv);
01248 #endif
01249 
01250         if (argc!=2) {
01251                 errx(EXIT_USAGE, "Usage: sfsck device/diskfile");
01252         }
01253 
01254         assert(sizeof(struct sfs_super)==SFS_BLOCKSIZE);
01255         assert(sizeof(struct sfs_inode)==SFS_BLOCKSIZE);
01256         assert(SFS_BLOCKSIZE % sizeof(struct sfs_dir) == 0);
01257 
01258         opendisk(argv[1]);
01259 
01260         check_sb();
01261         check_root_dir();
01262         check_bitmap();
01263         adjust_filelinks();
01264 
01265         closedisk();
01266 
01267         warnx("%lu blocks used (of %lu); %lu directories; %lu files",
01268               count_blocks, (unsigned long) nblocks, count_dirs, count_files);
01269 
01270         switch (badness) {
01271             case EXIT_USAGE:
01272             case EXIT_FATAL:
01273             default:
01274                 /* not supposed to happen here */
01275                 assert(0);
01276                 break;
01277             case EXIT_UNRECOV:
01278                 warnx("WARNING - unrecoverable errors. Maybe try again?");
01279                 break;
01280             case EXIT_RECOV:
01281                 warnx("Caution - filesystem modified. Run again for luck.");
01282                 break;
01283             case EXIT_CLEAN:
01284                 break;
01285         }
01286 
01287         return badness;
01288 }
 All Data Structures