gctf2023/pwn/flipper/dist/common/source/fs/minixfs/MinixFSSuperblock.cpp
2023-11-24 13:11:34 -05:00

346 lines
11 KiB
C++

#include "FileDescriptor.h"
#include "MinixFSType.h"
#include "MinixFSSuperblock.h"
#include "MinixFSInode.h"
#include "MinixFSFile.h"
#include "Dentry.h"
#include "assert.h"
#include "kprintf.h"
#ifdef EXE2MINIXFS
#include <unistd.h>
#else
#include "kstring.h"
#include "BDManager.h"
#include "BDVirtualDevice.h"
#endif
#define ROOT_NAME "/"
MinixFSSuperblock::MinixFSSuperblock(MinixFSType* fs_type, size_t s_dev, uint64 offset) :
Superblock(fs_type, s_dev), superblock_(this), offset_(offset)
{
//read Superblock data from disc
readHeader();
debug(M_SB, "s_num_inodes_ : %d\ns_zones_ : %d\ns_num_inode_bm_blocks_ : %d\ns_num_zone_bm_blocks_ : %d\n"
"s_1st_datazone_ : %d\ns_log_zone_size_ : %d\ns_max_file_size_ : %d\ns_block_size_ : %d\ns_magic_ : %llu\n",
s_num_inodes_, s_zones_, s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_1st_datazone_, s_log_zone_size_,
s_max_file_size_, s_block_size_, (long long unsigned)s_magic_);
assert(s_log_zone_size_ == 0);
assert(s_block_size_ == 1024);
//create Storage Manager
uint32 bm_size = s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_;
char* bm_buffer = new char[BLOCK_SIZE * bm_size];
readBlocks(2, bm_size, bm_buffer);
debug(M_SB, "---creating Storage Manager\n");
storage_manager_ = new MinixStorageManager(bm_buffer, s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_num_inodes_,
s_zones_);
if (M_STORAGE_MANAGER & OUTPUT_ENABLED)
{
storage_manager_->printBitmap();
}
delete[] bm_buffer;
initInodes();
}
void MinixFSSuperblock::readHeader()
{
char buffer[BLOCK_SIZE];
readBlocks(1, 1, buffer);
s_magic_ = ((uint16*) buffer)[12];
s_num_inodes_ = V3_ARRAY(buffer,0);
if (s_magic_ == MINIX_V3)
{
s_block_size_ = ((uint16*) buffer)[14];
s_disk_version_ = buffer[30];
s_zones_ = ((uint32*) buffer)[5];
}
else
{
s_magic_ = ((uint16*) buffer)[8];
s_zones_ = ((uint16*) buffer)[1];
s_block_size_ = 1024;
}
s_num_inode_bm_blocks_ = ((uint16*) buffer)[2 + V3_OFFSET];
s_num_zone_bm_blocks_ = ((uint16*) buffer)[3 + V3_OFFSET];
s_1st_datazone_ = ((uint16*) buffer)[4 + V3_OFFSET];
s_log_zone_size_ = ((uint16*) buffer)[5 + V3_OFFSET];
s_max_file_size_ = ((uint32*) buffer)[3 + V3_OFFSET];
}
void MinixFSSuperblock::initInodes()
{
debug(M_SB, "init Inodes\n");
MinixFSInode *root_inode = getInode(1);
s_root_ = new Dentry(root_inode);
all_inodes_add_inode(root_inode);
//read children from disc
debug(M_SB, "Load children of root inode\n");
root_inode->loadChildren();
}
MinixFSInode* MinixFSSuperblock::getInode(uint16 i_num, bool &is_already_loaded)
{
MinixFSInode* tmp = (MinixFSInode*) all_inodes_set_[i_num];
if (tmp)
{
is_already_loaded = true;
return tmp;
}
tmp = getInode(i_num);
return tmp;
}
MinixFSInode* MinixFSSuperblock::getInode(uint16 i_num)
{
debug(M_SB, "getInode::called with i_num: %d\n", i_num);
if (i_num >= storage_manager_->getNumUsedInodes())
{
debug(M_SB, "getInode::bad inode number %d\n", i_num);
return 0;
}
if (!storage_manager_->isInodeSet(i_num))
{
if (i_num == 1)
assert(storage_manager_->isInodeSet(1));
return 0;
}
uint32 inodes_start = s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_ + 2;
uint32 inode_block_num = inodes_start + (i_num - 1) / INODES_PER_BLOCK;
MinixFSInode *inode = 0;
char ibuffer_array[BLOCK_SIZE];
char* ibuffer = ibuffer_array;
debug(M_SB, "getInode::reading block num: %d\n", inode_block_num);
readBlocks(inode_block_num, 1, ibuffer);
debug(M_SB, "getInode:: returned reading block num: %d\n", inode_block_num);
uint32 offset = ((i_num - 1) % INODES_PER_BLOCK) * INODE_SIZE;
debug(M_SB, "getInode:: setting offset: %d\n", offset);
ibuffer += offset;
uint32 i_zones[NUM_ZONES];
for (uint32 num_zone = 0; num_zone < NUM_ZONES; num_zone++)
{
i_zones[num_zone] = V3_ARRAY(ibuffer, 7 - V3_OFFSET + num_zone);
}
debug(M_SB, "getInode:: calling creating Inode\n");
inode = new MinixFSInode(this, ((uint16*) ibuffer)[0], ((uint32*) ibuffer)[1 + V3_OFFSET],
((uint16*) ibuffer)[V3_OFFSET], i_zones, i_num);
debug(M_SB, "getInode:: returned creating Inode\n");
return inode;
}
MinixFSSuperblock::~MinixFSSuperblock()
{
debug(M_SB, "~MinixSuperblock\n");
assert(dirty_inodes_.empty() == true);
storage_manager_->flush(this);
releaseAllOpenFiles();
debug(M_SB, "Open files released\n");
if (M_SB & OUTPUT_ENABLED)
{
for (auto it : all_inodes_)
debug(M_SB, "Inode: %p\n", it);
}
// Also writes back inodes to disk
deleteAllInodes();
all_inodes_set_.clear();
delete storage_manager_;
debug(M_SB, "~MinixSuperblock finished\n");
}
Inode* MinixFSSuperblock::createInode(uint32 type)
{
uint16 mode = 0x01ff;
if (type == I_FILE)
mode |= 0x8000;
else if (type == I_DIR)
mode |= 0x4000;
//else link etc.
uint32 zones[NUM_ZONES];
for (uint32 i = 0; i < NUM_ZONES; i++)
zones[i] = 0;
uint32 i_num = storage_manager_->allocInode();
debug(M_SB, "createInode> acquired inode %d mode: %d\n", i_num, mode);
Inode *inode = new MinixFSInode(this, mode, 0, 0, zones, i_num);
debug(M_SB, "createInode> created Inode\n");
all_inodes_add_inode(inode);
debug(M_SB, "createInode> calling write Inode to Disc\n");
writeInode(inode);
debug(M_SB, "createInode> finished\n");
return inode;
}
int32 MinixFSSuperblock::readInode(Inode* inode)
{
assert(inode);
MinixFSInode *minix_inode = (MinixFSInode *) inode;
assert(ustl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end());
uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_
+ ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE);
uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE;
char buffer[INODE_SIZE];
readBytes(block, offset, INODE_SIZE, buffer);
uint32 *i_zones = new uint32[NUM_ZONES];
for (uint32 num_zone = 0; num_zone < NUM_ZONES; num_zone++)
{
i_zones[num_zone] = V3_ARRAY(buffer, 7 - V3_OFFSET + num_zone);
}
MinixFSZone *to_delete_i_zones = minix_inode->i_zones_;
minix_inode->i_zones_ = new MinixFSZone(this, i_zones);
if (s_magic_ == MINIX_V3)
minix_inode->i_nlink_ = ((uint16*)buffer)[1];
else
minix_inode->i_nlink_ = buffer[13];
minix_inode->i_size_ = ((uint32*)buffer)[1 + V3_OFFSET];
delete to_delete_i_zones;
return 0;
}
void MinixFSSuperblock::writeInode(Inode* inode)
{
assert(inode);
assert(ustl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end());
//flush zones
MinixFSInode *minix_inode = (MinixFSInode *) inode;
uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_
+ ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE);
uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE;
char buffer[INODE_SIZE];
memset((void*) buffer, 0, sizeof(buffer));
debug(M_SB, "writeInode> reading block %d with offset %d from disc\n", block, offset);
readBytes(block, offset, INODE_SIZE, buffer);
debug(M_SB, "writeInode> read data from disc\n");
debug(M_SB, "writeInode> the inode: i_type_: %d, i_nlink_: %d, i_size_: %d\n", minix_inode->i_type_,
minix_inode->numLinks(), minix_inode->i_size_);
if (minix_inode->i_type_ == I_FILE)
{
debug(M_SB, "writeInode> setting mode to file : %x\n", *(uint16*) buffer | 0x81FF);
*(uint16*) buffer |= 0x81FF;
}
else if (minix_inode->i_type_ == I_DIR)
{
debug(M_SB, "writeInode> setting mode to dir : %x\n", *(uint16*) buffer | 0x41FF);
*(uint16*) buffer |= 0x41FF;
}
else
{
// link etc. unhandled
}
((uint32*)buffer)[1+V3_OFFSET] = minix_inode->i_size_;
debug(M_SB, "writeInode> write inode %p link count %u\n", inode, minix_inode->numLinks());
if (s_magic_ == MINIX_V3)
((uint16*)buffer)[1] = minix_inode->numLinks();
else
buffer[13] = minix_inode->numLinks();
debug(M_SB, "writeInode> writing bytes to disc on block %d with offset %d\n", block, offset);
writeBytes(block, offset, INODE_SIZE, buffer);
debug(M_SB, "writeInode> flushing zones of inode %p\n", inode);
minix_inode->i_zones_->flush(minix_inode->i_num_);
}
void MinixFSSuperblock::all_inodes_add_inode(Inode* inode)
{
all_inodes_.push_back(inode);
all_inodes_set_[((MinixFSInode*) inode)->i_num_] = inode;
}
void MinixFSSuperblock::all_inodes_remove_inode(Inode* inode)
{
all_inodes_.remove(inode);
all_inodes_set_.erase(((MinixFSInode*) inode)->i_num_);
}
void MinixFSSuperblock::deleteInode(Inode* inode)
{
assert(inode->getDentrys().size() == 0);
assert(ustl::find(used_inodes_.begin(), used_inodes_.end(), inode) == used_inodes_.end());
dirty_inodes_.remove(inode);
MinixFSInode *minix_inode = (MinixFSInode *) inode;
all_inodes_remove_inode(minix_inode);
assert(minix_inode->i_files_.empty());
minix_inode->i_zones_->freeZones();
storage_manager_->freeInode(minix_inode->i_num_);
uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_
+ ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE);
uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE;
char buffer[INODE_SIZE];
memset((void*) buffer, 0, sizeof(buffer));
writeBytes(block, offset, INODE_SIZE, buffer);
delete inode;
}
uint16 MinixFSSuperblock::allocateZone()
{
debug(M_ZONE, "MinixFSSuperblock allocateZone>\n");
uint16 ret = (storage_manager_->allocZone() + s_1st_datazone_ - 1); // -1 because the zone nr 0 is set in the bitmap and should never be used!
debug(M_ZONE, "MinixFSSuperblock allocateZone> returning %d\n", ret);
return ret;
}
void MinixFSSuperblock::readZone(uint16 zone, char* buffer)
{
assert(buffer);
readBlocks(zone, ZONE_SIZE / BLOCK_SIZE, buffer);
}
void MinixFSSuperblock::readBlocks(uint16 block, uint32 num_blocks, char* buffer)
{
assert(buffer);
#ifdef EXE2MINIXFS
fseek((FILE*)s_dev_, offset_ + block * BLOCK_SIZE, SEEK_SET);
assert(fread(buffer, 1, BLOCK_SIZE * num_blocks, (FILE*)s_dev_) == BLOCK_SIZE * num_blocks);
#else
BDVirtualDevice* bdvd = BDManager::getInstance()->getDeviceByNumber(s_dev_);
bdvd->readData(block * bdvd->getBlockSize(), num_blocks * bdvd->getBlockSize(), buffer);
#endif
}
void MinixFSSuperblock::writeZone(uint16 zone, char* buffer)
{
writeBlocks(zone, ZONE_SIZE / BLOCK_SIZE, buffer);
}
void MinixFSSuperblock::writeBlocks(uint16 block, uint32 num_blocks, char* buffer)
{
#ifdef EXE2MINIXFS
fseek((FILE*)s_dev_, offset_ + block * BLOCK_SIZE, SEEK_SET);
assert(fwrite(buffer, 1, BLOCK_SIZE * num_blocks, (FILE*)s_dev_) == BLOCK_SIZE * num_blocks);
#else
BDVirtualDevice* bdvd = BDManager::getInstance()->getDeviceByNumber(s_dev_);
bdvd->writeData(block * bdvd->getBlockSize(), num_blocks * bdvd->getBlockSize(), buffer);
#endif
}
int32 MinixFSSuperblock::readBytes(uint32 block, uint32 offset, uint32 size, char* buffer)
{
assert(offset+size <= BLOCK_SIZE);
char rbuffer[BLOCK_SIZE];
readBlocks(block, 1, rbuffer);
memcpy(rbuffer + offset, buffer, size);
return size;
}
int32 MinixFSSuperblock::writeBytes(uint32 block, uint32 offset, uint32 size, char* buffer)
{
assert(offset+size <= BLOCK_SIZE);
char wbuffer[BLOCK_SIZE];
readBlocks(block, 1, wbuffer);
memcpy(wbuffer + offset, buffer, size);
writeBlocks(block, 1, wbuffer);
return size;
}
void MinixFSSuperblock::freeZone(uint16 index)
{
storage_manager_->freeZone(index - s_1st_datazone_ + 1);
}