static void adjust_filelinks(void) { struct sfs_inode sfi; int i; for (i=0; i<ninodes; i++) { if (inodes[i].linkcount==0) { /* directory */ continue; } diskread(&sfi, inodes[i].ino); swapinode(&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); swapinode(&sfi); diskwrite(&sfi, inodes[i].ino); } count_files++; } }
static void check_root_dir(void) { struct sfs_inode sfi; diskread(&sfi, SFS_ROOT_LOCATION); swapinode(&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; swapinode(&sfi); diskwrite(&sfi, SFS_ROOT_LOCATION); break; } check_dir(SFS_ROOT_LOCATION, SFS_ROOT_LOCATION, ""); }
int set_bcache(fileid_t *fp) { /* * Insert Disk Block Cache Entry: * * In this case, we actually read the requested block into a * dynamically allocated buffer before inserting it into the * cache. If the read fails, we return a non-zero value. * * The search keys for disk blocks are the block number and * buffer size. The data associated with each entry is the * corresponding data buffer. */ bc_t *bcp; if (fp->fi_memp = bkmem_alloc(x_len = fp->fi_count)) { /* * We were able to succesffully allocate an input * buffer, now read the data into it. */ if (diskread(fp) != 0) { /* * I/O error on read. Free the input buffer, * print an error message, and bail out. */ bkmem_free(fp->fi_memp, x_len); printf("disk read error\n"); return (-1); } x_blkno = fp->fi_blocknum; x_dev = fp->fi_devp->di_dcookie; bcp = (bc_t *) set_cache(&bc_hash[BC_HASH(x_dev, x_blkno, x_len)], &bc_head, 0); bcp->bc_blk = x_blkno; bcp->bc_hdr.dev = x_dev; bcp->bc_hdr.size = x_len; bcp->bc_hdr.data = (void *)fp->fi_memp; } else { /* * We could be a bit more convervative here by * calling "set_cache" before we try to allocate a * buffer (thereby giving us a chance to re-use a * previously allocated buffer) but the error recovery * is a bit trickier, and if we're that short on memory * we'll have trouble elsewhere anyway! */ prom_panic("can't read - no memory"); } return (0); }
static int ext2sync(Fsys *fsys) { int i; Group g; Block *b; Super super; Ext2 *fs; Disk *disk; fs = fsys->priv; disk = fs->disk; if((b = diskread(disk, SBSIZE, SBOFF)) == nil) return -1; parsesuper(&super, b->data); blockput(b); if(checksuper(&super) < 0) return -1; fs->blocksize = MINBLOCKSIZE<<super.logblocksize; fs->nblock = super.nblock; fs->ngroup = (super.nblock+super.blockspergroup-1) / super.blockspergroup; fs->inospergroup = super.inospergroup; fs->blockspergroup = super.blockspergroup; if(super.revlevel >= 1) fs->inosize = super.inosize; else fs->inosize = 128; fs->inosperblock = fs->blocksize / fs->inosize; if(fs->blocksize == SBOFF) fs->groupaddr = 2; else fs->groupaddr = 1; fs->descperblock = fs->blocksize / GroupSize; fs->firstblock = super.firstdatablock; fsys->blocksize = fs->blocksize; fsys->nblock = fs->nblock; if(debug) fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n", fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup); if(0){ for(i=0; i<fs->ngroup; i++) if(ext2group(fs, i, &g) >= 0) fprint(2, "grp %d: bitblock=%d\n", i, g.bitblock); } return 0; }
static void check_sb(void) { struct sfs_super sp; uint32_t i; int schanged=0; diskread(&sp, SFS_SB_LOCATION); swapsb(&sp); if (sp.sp_magic != SFS_MAGIC) { errx(EXIT_UNRECOV, "Not an sfs filesystem"); } assert(nblocks==0); assert(bitblocks==0); nblocks = sp.sp_nblocks; bitblocks = SFS_BITBLOCKS(nblocks); assert(nblocks>0); assert(bitblocks>0); bitmap_init(bitblocks); for (i=nblocks; i<bitblocks*SFS_BLOCKBITS; i++) { bitmap_mark(i, B_PASTEND, 0); } if (checknullstring(sp.sp_volname, sizeof(sp.sp_volname))) { warnx("Volume name not null-terminated (fixed)"); setbadness(EXIT_RECOV); schanged = 1; } if (checkbadstring(sp.sp_volname)) { warnx("Volume name contains illegal characters (fixed)"); setbadness(EXIT_RECOV); schanged = 1; } if (schanged) { swapsb(&sp); diskwrite(&sp, SFS_SB_LOCATION); } bitmap_mark(SFS_SB_LOCATION, B_SUPERBLOCK, 0); for (i=0; i<bitblocks; i++) { bitmap_mark(SFS_MAP_LOCATION+i, B_BITBLOCK, i); } }
static int ext2group(Ext2 *fs, u32int i, Group *g) { Block *b; u64int addr; if(i >= fs->ngroup) return -1; addr = fs->groupaddr + i/fs->descperblock; b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize); if(b == nil) return -1; parsegroup(g, b->data+i%fs->descperblock*GroupSize); blockput(b); return 0; }
static void setcache(Buffer *b, uint q0) { Block **blp, *bl; uint i, q; if(q0 > b->nc) panic("internal error: setcache"); /* * flush and reload if q0 is not in cache. */ if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc)) return; /* * if q0 is at end of file and end of cache, continue to grow this block */ if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<=Maxblock) return; flush(b); /* find block */ if(q0 < b->cq){ q = 0; i = 0; }else{ q = b->cq; i = b->cbi; } blp = &b->bl[i]; while(q+(*blp)->n <= q0 && q+(*blp)->n < b->nc){ q += (*blp)->n; i++; blp++; if(i >= b->nbl) panic("block not found"); } bl = *blp; /* remember position */ b->cbi = i; b->cq = q; sizecache(b, bl->n); b->cnc = bl->n; /*read block*/ diskread(disk, bl, b->c, b->cnc); }
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; i<nblocks; i++) { uint32_t block = dobmap(sfi, i); if (block!=0) { diskread(d + i*atonce, block); for (j=0; j<atonce; j++) { swapdir(&d[i*atonce+j]); } } else { warnx("Warning: sparse directory found"); bzero(d + i*atonce, SFS_BLOCKSIZE); } } }
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]; } }
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; i<maxdirentries; i++) { direntries[i].sfd_ino = SFS_NOINO; bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name)); } for (i=0; i<ndirentries; i++) { if (check_dir_entry(pathsofar, i, &direntries[i])) { dchanged = 1; } sortvector[i] = i; } sortdir(sortvector, direntries, ndirentries); /* don't use ndirentries-1 here in case ndirentries == 0 */ for (i=0; i+1<ndirentries; i++) { struct sfs_dir *d1 = &direntries[sortvector[i]]; struct sfs_dir *d2 = &direntries[sortvector[i+1]]; assert(d1 != d2); if (d1->sfd_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<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; } assert(dotseen==0); /* due to duplicate checking */ 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; } assert(dotdotseen==0); /* due to duplicate checking */ dotdotseen = 1; } } if (!dotseen) { if (dir_tryadd(direntries, ndirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `.' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `.' entry (NOT FIXED)", pathsofar); } } if (!dotdotseen) { if (dir_tryadd(direntries, ndirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); dchanged = 1; } else if (dir_tryadd(direntries, maxdirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory /%s: No `..' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory /%s: No `..' entry (NOT FIXED)", pathsofar); } } subdircount=0; for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, "..")) { /* nothing */ } else if (direntries[i].sfd_ino == SFS_NOINO) { /* nothing */ } else { char path[strlen(pathsofar)+SFS_NAMELEN+1]; struct sfs_inode subsfi; diskread(&subsfi, direntries[i].sfd_ino); swapinode(&subsfi); snprintf(path, sizeof(path), "%s/%s", pathsofar, direntries[i].sfd_name); switch (subsfi.sfi_type) { case SFS_TYPE_FILE: if (check_inode_blocks(direntries[i].sfd_ino, &subsfi, 0)) { swapinode(&subsfi); diskwrite(&subsfi, direntries[i].sfd_ino); } observe_filelink(direntries[i].sfd_ino); break; case SFS_TYPE_DIR: if (check_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 " "(removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; break; } } } 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; } if (dchanged) { dirwrite(&sfi, direntries, ndirentries); } if (ichanged) { swapinode(&sfi); diskwrite(&sfi, ino); } free(direntries); free(sortvector); return 0; }
static void check_indirect_block(uint32_t ino, uint32_t *ientry, uint32_t *blockp, uint32_t nblocks, uint32_t *badcountp, int isdir, int indirection) { uint32_t entries[SFS_DBPERIDB]; uint32_t i, ct; if (*ientry !=0) { diskread(entries, *ientry); swapindir(entries); bitmap_mark(*ientry, B_IBLOCK, ino); } else { for (i=0; i<SFS_DBPERIDB; i++) { entries[i] = 0; } } if (indirection > 1) { for (i=0; i<SFS_DBPERIDB; i++) { check_indirect_block(ino, &entries[i], blockp, nblocks, badcountp, isdir, indirection-1); } } else { assert(indirection==1); for (i=0; i<SFS_DBPERIDB; i++) { if (*blockp < nblocks) { if (entries[i] != 0) { bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); } } else { if (entries[i] != 0) { (*badcountp)++; bitmap_mark(entries[i], isdir ? B_DIRDATA : B_DATA, ino); entries[i] = 0; } } (*blockp)++; } } ct=0; for (i=ct=0; i<SFS_DBPERIDB; i++) { if (entries[i]!=0) ct++; } if (ct==0) { if (*ientry != 0) { (*badcountp)++; bitmap_mark(*ientry, B_TOFREE, 0); *ientry = 0; } } else { assert(*ientry != 0); if (*badcountp > 0) { swapindir(entries); diskwrite(entries, *ientry); } } }
static void check_bitmap(void) { uint8_t bits[SFS_BLOCKSIZE], *found, *tofree, tmp; uint32_t alloccount=0, freecount=0, i, j; int bchanged; for (i=0; i<bitblocks; i++) { diskread(bits, SFS_MAP_LOCATION+i); swapbits(bits); found = bitmapdata + 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((found[j] & tofree[j])==0); if (bits[j]==found[j]) { continue; } if (bits[j]==(found[j] | tofree[j])) { bits[j] = found[j]; bchanged = 1; continue; } /* free the ones we're freeing */ bits[j] &= ~tofree[j]; /* are we short any? */ if ((bits[j] & found[j]) != found[j]) { tmp = found[j] & ~bits[j]; alloccount += countbits(tmp); if (tmp != 0) { reportbits(i, j, tmp, "free"); } } /* do we have any extra? */ if ((bits[j] & found[j]) != bits[j]) { tmp = bits[j] & ~found[j]; freecount += countbits(tmp); if (tmp != 0) { reportbits(i, j, tmp, "allocated"); } } bits[j] = found[j]; bchanged = 1; } if (bchanged) { swapbits(bits); diskwrite(bits, SFS_MAP_LOCATION+i); } } if (alloccount > 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); } }
static Block* ext2blockread(Fsys *fsys, u64int vbno) { Block *bitb; Group g; uchar *bits; u32int bno, boff, bitblock; u64int bitpos; Ext2 *fs; fs = fsys->priv; if(vbno >= fs->nblock) return nil; bno = vbno; if(bno != vbno) return nil; /* if(bno < fs->firstblock) return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize); */ if(bno < fs->firstblock) return nil; bno -= fs->firstblock; if(ext2group(fs, bno/fs->blockspergroup, &g) < 0){ if(debug) fprint(2, "loading group: %r..."); return nil; } /* if(debug) fprint(2, "ext2 group %d: bitblock=%ud inodebitblock=%ud inodeaddr=%ud freeblocks=%ud freeinodes=%ud useddirs=%ud\n", (int)(bno/fs->blockspergroup), g.bitblock, g.inodebitblock, g.inodeaddr, g.freeblockscount, g.freeinodescount, g.useddirscount); if(debug) fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g.bitblock); */ bitblock = g.bitblock; bitpos = (u64int)bitblock*fs->blocksize; if((bitb = diskread(fs->disk, fs->blocksize, bitpos)) == nil){ if(debug) fprint(2, "loading bitblock: %r..."); return nil; } bits = bitb->data; boff = bno%fs->blockspergroup; if((bits[boff>>3] & (1<<(boff&7))) == 0){ if(debug) fprint(2, "block %d not allocated in group %d: bitblock %d/%lld bits[%d] = %#x\n", boff, bno/fs->blockspergroup, (int)bitblock, bitpos, boff>>3, bits[boff>>3]); blockput(bitb); return nil; } blockput(bitb); bno += fs->firstblock; return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize); }