// CSC_4730 | P8 | Crusade Against File System Corruption; with c++11| Caleb A. Collar | 12.4.21 // Includes: #include "fs.h" #include "types.h" #include #include #include #include #include #include #include #include #include // Definitions: #define T_UNUSED 0 // Unused #define T_DIR 1 // Directory #define T_FILE 2 // File #define T_DEV 3 // Special device // Set the file's namespace: using namespace std; // Globals: const uint pad = 25; struct inode { uint inode; uint numLinks; uint links; uint type; bool allocated; bool inDir = false; }; struct block { uint block; uint references = 0; bool allocated = false; }; struct dir { uint inode; uint numLinks; uint links; uint type; }; struct fsMembers { void *fsImage; superblock *sb; vector files; vector dinodeDirs; vector dirs; vector unused; vector special; vector dirents; vector bitmappedBlocks; vector dinodes; }; // Method declarations: // Create a pointer to Superblock, this is the return of the handle function. Handle printouts of superblock. superblock *HandleSuperblock(char **const &argv, void *fs) { superblock *sb = (superblock *)((char *)fs + BSIZE); struct stat fileSystem; stat(argv[1], &fileSystem); // If Superblock is not the correct size. if (fileSystem.st_size / BSIZE != sb->size) { // If the file system size is disputed. cout << "Superblock Info:" << endl; cout << left << setw(pad) << "FS Size in blocks:" << sb->size << endl; cout << left << setw(pad) << "Number of data blocks:" << sb->nblocks << endl; cout << left << setw(pad) << "Number of inodes:" << sb->ninodes << endl; cout << left << setw(pad) << "Number of log blocks:" << sb->nlog << endl; cout << left << setw(pad) << "Log start block:" << sb->logstart << endl; cout << left << setw(pad) << "BM start block:" << sb->bmapstart << endl; cout << left << setw(pad) << "Data blocks start at: " << sb->inodestart << endl; cout << left << setw(pad) << "Superblock Status:" << "BAD" << endl; cout << "Incorrect size of Superblock..." << endl; cout << left << setw(pad) << "Superblock reported size:" << sb->size << endl; munmap(fs, BSIZE * 1024); exit(1); } else { // Else print out the qualities that are needed. cout << "Superblock Info:" << endl; cout << left << setw(pad) << "FS Size in blocks:" << sb->size << endl; cout << left << setw(pad) << "Number of data blocks:" << sb->nblocks << endl; cout << left << setw(pad) << "Number of inodes:" << sb->ninodes << endl; cout << left << setw(pad) << "Number of log blocks:" << sb->nlog << endl; cout << left << setw(pad) << "Log start block:" << sb->logstart << endl; cout << left << setw(pad) << "BM start block:" << sb->bmapstart << endl; cout << left << setw(pad) << "Data blocks start at: " << sb->inodestart << endl; cout << left << setw(pad) << "Superblock Status:" << "OK" << endl; } return sb; } // pass CheckInodeSize the array of inodes bool CheckInodeSize(vector d, uint size) { bool valid = true; // check if each one's -size is < (512 * MAXFILE) for (uint i = 0; i < size; i++) { if ((d[i])->size > (512 * MAXFILE)) { cout << "inode " << i << " has invalid size." << endl; valid = false; break; } } return valid; } // Check number of blocks. bool NumBlocks(superblock *sb) { bool retval = false; if ((sb->nblocks + sb->bmapstart + 1) != sb->size) { retval = true; cout << "There is an invalid number of blocks listed in the superblock. The number should be " << (sb->size - sb->bmapstart - 1) << endl; } return retval; } // Parse image for directories, files, special, or unused. void ParseFs(fsMembers *members) { for (uint i = 0; i < members->sb->ninodes - 1; i++) { // Is unused if (members->dinodes[i]->type == T_UNUSED) { inode temp; temp.inode = i; temp.numLinks = members->dinodes[i]->nlink; temp.type = members->dinodes[i]->type; members->unused.push_back(temp); } // Is directory else if (members->dinodes[i]->type == T_DIR) { dir temp; temp.numLinks = members->dinodes[i]->nlink; temp.inode = i; temp.type = members->dinodes[i]->type; members->dirs.push_back(temp); members->dinodeDirs.push_back(members->dinodes[i]); } // Is file else if (members->dinodes[i]->type == T_FILE) { inode temp; temp.inode = i; temp.numLinks = members->dinodes[i]->nlink; temp.type = members->dinodes[i]->type; members->files.push_back(temp); } // Is special else if (members->dinodes[i]->type == T_DEV) { members->special.push_back(members->dinodes[i]); } } } //Check if inode 0 should be allocated. bool InodeZeroDir(vector d) { bool retval = false; if (d[0]->type != 0) { cout << "The first inode is allocated." << endl; retval = true; } return retval; } // Check if inode 1 is not a directory. bool InodeOneDir(vector d) { bool retval = false; if (d[1]->type != 1) { cout << "Inode one is not a directory." << endl; retval = true; } return retval; } // Check if first two names are . and .. bool FirstNamesInDir(vector dirents) { bool retval = false; if (dirents.at(0)->name[0] != '.') { cout << "Directory " << dirents.at(0)->inum << ", does not have its first directory set to \".\" " << endl; retval = true; } else if (dirents.at(1)->name[0] != '.' || dirents.at(1)->name[1] != '.') { cout << "Directory " << dirents.at(1)->inum << ", does not have its second directory set to \"..\" " << endl; } return retval; } // Check if an inode is unused. bool InodeUnused(vector inode_vec) { bool retval = false; for (uint i = 0; i < inode_vec.size(); i++) { if (inode_vec.at(i).type != 0 && inode_vec.at(i).inDir == false) { cout << "Inode " << inode_vec.at(i).inode << ", is marked as valid but is not referenced by a directory. " << endl; retval = true; break; } } return retval; } // An inode is found in a valid directory but is not marked as allocated. bool InodeMissing(vector unused_inode_vec) { bool retval = false; for (uint i = 0; i < unused_inode_vec.size(); i++) { if (unused_inode_vec.at(i).type == 0 && unused_inode_vec.at(i).inDir == true) { cout << "Inode Number " << unused_inode_vec.at(i).inode << ", is found in a valid directory but is not marked as allocated." << endl; retval = true; break; } } return retval; } //Check for missing blocks. bool MissingBlock(fsMembers *members) { bool retval = false; vector referenceToBlock; for (uint i = 0; i < members->sb->ninodes - 1; i++) { for (uint j = 0; j < NDIRECT + 1; j++) { if (j == NDIRECT) { uint indirect = members->dinodes[i]->addrs[j]; referenceToBlock.push_back(members->dinodes[i]->addrs[j]); uint *ptr = (uint *)members->fsImage + (indirect * BSIZE) / 4; for (int k = 0; k < 128; k++) { referenceToBlock.push_back(*ptr); ptr++; } } else { referenceToBlock.push_back(members->dinodes[i]->addrs[j]); } } } bool found = false; for (uint i = members->sb->bmapstart + 1; i < members->bitmappedBlocks.size(); i++) { if (members->bitmappedBlocks[i].allocated == true) { found = false; for (uint j = 0; j < referenceToBlock.size(); j++) { if (referenceToBlock[j] == i) { found = true; break; } } if (found == false) { cout << "Bitmap data suggests block " << i << " is allocated, but it does not belong to any file or " "directory." << endl; retval = true; break; } } } return retval; } //Check if inode ine is root. bool InodeOneIsNotRoot(vector dirents) { bool retval = false; for (uint i = 2; i < dirents.size(); i++) { if (dirents.at(i)->name[0] == '.' && dirents.at(i)->name[1] != '.' && dirents.at(i)->inum == 1) { cout << "A non-root directory lists itself as inode 1." << endl; retval = true; break; } } return retval; } //Directory links more than once not including . or .. bool DirectoryLoop(vector dirents) { vector names; bool retval = false; for (uint i = 0; i < dirents.size(); i++) { if (dirents.at(i)->name[0] == '.') continue; if (std::find(names.begin(), names.end(), dirents.at(i)->name) != names.end()) { cout << "Directory " << dirents.at(i)->name << " appears more than once. " << endl; retval = true; break; } else { names.push_back(dirents.at(i)->name); } } return retval; } //Check for valid links. bool ValidDir(vector dirents, vector d) { bool retval = false; for (uint i = 0; i < dirents.size(); i++) { // if name is . or .. if (dirents.at(i)->name[0] == '.') { if (d[dirents.at(i)->inum]->type != 1) { cout << "Name of File in inode " << dirents.at(i)->inum << ", is \".\", or \"..\" but doesn't link to a directory." << endl; retval = true; break; } } } return retval; } // Check number of links is valid. bool LinkCount(vector dirents, vector files) { bool retval = false; for (uint i = 0; i < files.size(); i++) { uint referenced = files.at(i).numLinks; uint linked = 0; for (uint j = 0; j < dirents.size(); j++) { if (dirents.at(j)->inum == files.at(i).inode) linked++; } if (linked != referenced) { cout << "File " << files.at(i).inode << " has a disputed file link count. The reported correct link number is " << linked << endl; retval = true; break; } } return retval; } // Check directory link number is valid. bool DirLinkCount(vector dirents, vector dirs) { bool retval = false; for (uint i = 0; i < dirs.size(); i++) { uint referenced = dirs.at(i).numLinks; uint linked = 0; for (uint j = 0; j < dirents.size(); j++) { if (dirents.at(j)->inum == dirs.at(i).inode) linked++; } if (linked != referenced) { cout << "Directory " << dirs.at(i).inode << " has a disputed file link count. The reported correct link number is " << linked << endl; retval = true; break; } } return retval; } //Check for unallocated blocks. bool UnallocatedBlock(fsMembers *members) { bool retval = false; for (uint i = 0; i < members->sb->ninodes - 1; i++) { for (int j = 0; j < NDIRECT + 1; j++) { if (members->dinodes[i]->addrs[j] > members->bitmappedBlocks.size()) { continue; } else if (j == NDIRECT) { uint indirectBlock = (members->dinodes[i]->addrs[j]); if (indirectBlock > (members->sb->nblocks)) { break; } if (members->dinodes[i]->addrs[j] == 1) { continue; } uint *ptr = (uint *)members->fsImage + (indirectBlock * BSIZE) / 4; for (int k = 0; k < 128; k++) { if (*ptr > members->sb->nblocks) { cout << "Inode address points to an unallocated block." << endl; retval = true; goto end; } if ((*ptr != 0) && (members->bitmappedBlocks[*ptr].allocated == false)) { cout << "Bitmap data suggests block, " << *ptr << " is free but it is referenced in a valid Inode." << endl; retval = true; goto end; } ptr++; } } else if ((members->dinodes[i]->addrs[j + 1] != 0) && (members->bitmappedBlocks[members->dinodes[i]->addrs[j + 1]].allocated == false)) { cout << "Bitmap data suggests block, " << members->bitmappedBlocks[members->dinodes[i]->addrs[j + 1]].block << " is free but it is referenced in a valid Inode." << endl; retval = true; break; } } } end: return retval; } //Check for multiply allocated blocks. bool MultiplyAllocatedBlock(fsMembers *members) { bool retval = false; int blockLog[members->sb->nblocks]; for (uint i = 0; i < members->sb->nblocks; i++) { blockLog[i] = 0; } for (uint i = 0; i < members->sb->ninodes - 1; i++) { for (uint j = 0; j < NDIRECT + 1; j++) { if (members->dinodes[i]->addrs[j] > members->bitmappedBlocks.size()) { continue; } else if (j == NDIRECT) { uint indirectBlock = (members->dinodes[i]->addrs[j]); if (indirectBlock > (members->sb->nblocks)) { break; } if (members->dinodes[i]->addrs[j] == 1) { continue; } uint *ptr = (uint *)members->fsImage + (indirectBlock * BSIZE) / 4; for (int k = 0; k < 128; k++) { if (*ptr > members->sb->nblocks) { cout << "Inode address points to an invalid block" << endl; retval = true; goto end; } blockLog[*ptr]++; ptr++; } } else { blockLog[members->dinodes[i]->addrs[j]]++; } } } for (uint i = 1; i < members->sb->nblocks; i++) { if (blockLog[i] > 1) { cout << "Block " << i << ", is found more than once in the inodes:" << endl; for (uint j = 0; j < members->sb->ninodes - 1; j++) { for (uint k = 0; k < NDIRECT + 1; k++) if (members->dinodes[j]->addrs[k] == i) { cout << j << endl; } } retval = true; goto end; } } end: return retval; } //Read in the image file. void *ReadImageFile(char **const &argv) { int fd = -1; // Making sure file system was given in command line correctly. if ((fd = open(argv[1], O_RDONLY)) < 0) { perror("Image file failed to be read."); close(fd); exit(1); } // Reading in File System at 512*1024 size. void *fs = mmap(nullptr, BSIZE * 1024, PROT_READ, MAP_SHARED, fd, 0); if (fs == MAP_FAILED) { perror("Unable to map image file."); munmap(fs, BSIZE * 1024); close(fd); exit(1); } close(fd); return fs; } //Handle the bitmap block data. void HandleBitmap(fsMembers *members) { members->bitmappedBlocks.resize(members->sb->size); uchar *bitmap_ptr = (uchar *)((char *)members->fsImage + (members->sb->bmapstart * BSIZE)); uchar *bitmap[members->sb->size / 8]; for (uint i = 0; i < members->sb->size / 8; i++) { bitmap[i] = bitmap_ptr; for (uint j = 0; j < 8; j++) { members->bitmappedBlocks[(i * 8) + j].allocated = ((*bitmap[i] >> (7 - j)) & 0x01); members->bitmappedBlocks[(i * 8) + j].block = (i * 8) + j; } bitmap_ptr++; } for (uint i = 0; i < members->sb->ninodes - 1; i++) { for (uint j = 0; j < NDIRECT; j++) { if (j == NDIRECT) { uint indirectBlock = (members->dinodes[i]->addrs[j]); if (indirectBlock > (members->sb->nblocks)) { break; } if (members->dinodes[i]->addrs[j] == 1) { continue; } uint *ptr = (uint *)members->fsImage + (indirectBlock * BSIZE) / 4; for (int k = 0; k < 128; k++) { members->bitmappedBlocks[(i * 8) + j].allocated = ((*bitmap[i] >> (7 - j)) & 0x01); ptr++; } } uint block_n = members->dinodes[i]->addrs[j]; members->bitmappedBlocks[block_n].references++; } } } //Handle inodes. void HandleInodes(fsMembers *members, char **const &argv) { members->dinodes.resize(members->sb->ninodes); dinode *i_ptr = (dinode *)((char *)members->fsImage + (members->sb->inodestart * BSIZE)); for (uint i = 0; i < members->sb->ninodes - 1; i++) { members->dinodes[i] = i_ptr; i_ptr += 1; } if (!CheckInodeSize(members->dinodes, members->sb->ninodes - 1)) { munmap(members->fsImage, BSIZE * 1024); exit(1); } } //Handle inodes that are in directories or unused. void HandleReferences(fsMembers *members) { // Iterate through the inodes that are directories. for (uint i = 0; i < members->dinodeDirs.size(); i++) { // Every directory size will be a multiple of BSIZE. for (uint k = 0; k < 11; k++) { dirent *data = (dirent *)(((uchar *)members->fsImage) + members->dinodeDirs[i]->addrs[k] * BSIZE); for (dirent *ptr = data; ptr != data + (BSIZE / sizeof(dirent)); ptr++) { if (ptr->inum != 0) { members->dirents.push_back(ptr); } } } } //Label inodes in directories. for (uint i = 0; i < members->files.size(); i++) { for (uint j = 0; j < members->dirents.size(); j++) { if (members->dirents.at(j)->inum == members->files.at(i).inode) { members->files.at(i).inDir = true; } } } // Label inodes that are unused. for (uint i = 0; i < members->unused.size(); i++) { for (uint j = 0; j < members->dirents.size(); j++) { if (members->dirents.at(j)->inum == members->unused.at(i).inode) { members->unused.at(i).inDir = true; } } } } //Method to check for each error type. //Will exit upon finding first error in order that they are checked. void CheckForErrors(fsMembers *members) { if (InodeUnused(members->files)) goto endCheck; if (InodeMissing(members->unused)) goto endCheck; if (NumBlocks(members->sb)) goto endCheck; if (InodeOneIsNotRoot(members->dirents)) goto endCheck; if (InodeOneDir(members->dinodes)) goto endCheck; if (InodeZeroDir(members->dinodes)) goto endCheck; if (FirstNamesInDir(members->dirents)) goto endCheck; if (DirectoryLoop(members->dirents)) goto endCheck; if (ValidDir(members->dirents, members->dinodes)) goto endCheck; if (LinkCount(members->dirents, members->files)) goto endCheck; if (DirLinkCount(members->dirents, members->dirs)) goto endCheck; if (MultiplyAllocatedBlock(members)) goto endCheck; if (MissingBlock(members)) goto endCheck; if (UnallocatedBlock(members)) goto endCheck; else { cout << "No errors found in this image." << endl; endCheck: munmap(members->fsImage, BSIZE * 1024); exit(1); } } //The main method. int main(int argc, char *argv[]) { fsMembers members; //This struct holds parsed information on blocks and inodes. members.fsImage = ReadImageFile(argv); members.sb = HandleSuperblock(argv, members.fsImage); HandleInodes(&members, argv); HandleBitmap(&members); ParseFs(&members); HandleReferences(&members); CheckForErrors(&members); return 0; }