mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 20:39:01 +00:00
464 lines
15 KiB
C#
464 lines
15 KiB
C#
using System;
|
|
using Cosmos.Hardware.Storage;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace Cosmos.Kernel.FileSystem {
|
|
public unsafe partial class Ext2 {
|
|
private class FileStream: Stream {
|
|
private uint mINodeNumber;
|
|
private Ext2 mFilesystem;
|
|
private uint mPosition = 0;
|
|
public FileStream(uint aINodeNumber, Ext2 aFilesystem) {
|
|
mINodeNumber = aINodeNumber;
|
|
mFilesystem = aFilesystem;
|
|
}
|
|
|
|
public override bool CanRead {
|
|
get {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override bool CanSeek {
|
|
get {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override bool CanWrite {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override void Flush() {
|
|
}
|
|
|
|
public override long Length {
|
|
get {
|
|
INode xINode;
|
|
mFilesystem.ReadINode(mINodeNumber, out xINode);
|
|
long xSize = xINode.Size;
|
|
return xSize;
|
|
}
|
|
}
|
|
|
|
public override long Position {
|
|
get {
|
|
return mPosition;
|
|
}
|
|
set {
|
|
mPosition = (uint)value;
|
|
}
|
|
}
|
|
|
|
// todo: add support for reading one chunk of data which spans multiple logical blocks
|
|
public override int Read(byte[] buffer, int offset, int count) {
|
|
uint xBlock = (mPosition / mFilesystem.mBlockSize);
|
|
if (xBlock != 0) {
|
|
return 0;
|
|
}
|
|
INode xINode;
|
|
if (!mFilesystem.ReadINode(mINodeNumber, out xINode)) {
|
|
return -1;
|
|
}
|
|
int xBytesToRead = count;
|
|
//byte* xBuffer = (byte*)Heap.MemAlloc(mFilesystem.mBlockSize);
|
|
byte* xBuffer = null;
|
|
while (xBytesToRead > 0) {
|
|
if (!mFilesystem.ReadINodeContents(&xINode, xBlock, xBuffer)) {
|
|
return -2;
|
|
}
|
|
uint xBufferOffset = mPosition % mFilesystem.mBlockSize;
|
|
for (int i = 0; i < count; i++) {
|
|
buffer[offset + i] = xBuffer[xBufferOffset + i];
|
|
}
|
|
xBytesToRead -= (int)(mFilesystem.mBlockSize - (mPosition % mFilesystem.mBlockSize));
|
|
xBlock++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void SetLength(long value) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count) {
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
private Hardware.BlockDevice mBackend;
|
|
private SuperBlock mSuperBlock;
|
|
private uint mBlockSize;
|
|
private uint mGroupsCount;
|
|
private uint mGroupDescriptorsPerBlock;
|
|
private GroupDescriptor[] mGroupDescriptors;
|
|
public const uint EXT2_ROOT_INO = 0x02;
|
|
|
|
public Ext2(Hardware.BlockDevice aBackend) {
|
|
if (aBackend == null) {
|
|
throw new ArgumentNullException("aBackend");
|
|
}
|
|
mBackend = aBackend;
|
|
}
|
|
|
|
private bool ReadSuperBlock() {
|
|
|
|
var xBytes = mBackend.ReadBlock(2);
|
|
fixed (byte* xByteBuff = &xBytes[0]) {
|
|
var xByteBuffAsSuperBlock = (SuperBlock*)xByteBuff;
|
|
mSuperBlock = xByteBuffAsSuperBlock[0];
|
|
// DebugUtil.SendExt2_SuperBlock("", mSuperBlock);
|
|
mBlockSize = (uint)(1024 << (byte)(mSuperBlock.LogBlockSize));
|
|
// DebugUtil.SendDoubleNumber("Numbers", "", mSuperBlock.INodesCount, 32, mSuperBlock.INodesPerGroup, 32);
|
|
mGroupsCount = mSuperBlock.INodesCount / mSuperBlock.INodesPerGroup;
|
|
mGroupDescriptorsPerBlock = (uint)(mBlockSize / sizeof(GroupDescriptor));
|
|
if (!ReadGroupDescriptorsOfBlock()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private unsafe bool ReadGroupDescriptorsOfBlock() {
|
|
byte[] xBytes;
|
|
mGroupDescriptors = new GroupDescriptor[mGroupsCount];
|
|
GroupDescriptor* xDescriptorPtr=(GroupDescriptor*)0;
|
|
for (int i = 0; i < mGroupsCount; i++) {
|
|
// DebugUtil.SendNumber("Ext2", "ReadGroupDescriptorsOfBlock, I", (uint)i, 16);
|
|
uint xATABlock = (uint)(mBlockSize / mBackend.BlockSize);
|
|
xATABlock += (uint)(i / mGroupDescriptorsPerBlock);
|
|
if ((i % 16) == 0) {
|
|
xBytes= mBackend.ReadBlock(xATABlock);
|
|
fixed (byte* xDescriptorPtr2 = &xBytes[0]) {
|
|
xDescriptorPtr = (GroupDescriptor*)xDescriptorPtr2;
|
|
}
|
|
}
|
|
//GroupDescriptor* xItem = (GroupDescriptor*)Heap.MemAlloc((uint)sizeof(GroupDescriptor));
|
|
//CopyPointers((byte*)&xDescriptorPtr[i % mGroupDescriptorsPerBlock], (byte*)xItem, (uint)sizeof(GroupDescriptor));
|
|
//mGroupDescriptors[i] = *xItem;
|
|
// DebugUtil.SendExt2_GroupDescriptor("ReadGroupDescriptorsOfBlock", xATABlock, i, 0, xDescriptorPtr[i % mGroupDescriptorsPerBlock]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool Initialize() {
|
|
return ReadSuperBlock();
|
|
}
|
|
|
|
private static unsafe bool ConvertBitmapToBoolArray(uint* aBitmap, bool[] aArray) {
|
|
if (aArray == null || aArray.Length != 32) {
|
|
Console.WriteLine("[ConvertBitmapToBoolArray] Incorrect Array");
|
|
return false;
|
|
}
|
|
uint xValue = *aBitmap;
|
|
for (byte b = 0; b < 32; b++) {
|
|
uint xCheckBit = (uint)(1 << b);
|
|
aArray[b] = (xValue & xCheckBit) != 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void CopyPointers(byte* aSource, byte* aDest, uint aLength) {
|
|
for (int i = 0; i < aLength; i++) {
|
|
aDest[i] = aSource[i];
|
|
}
|
|
}
|
|
|
|
public Stream OpenFile(string[] xPath) {
|
|
//ushort* xBuffer = (ushort*)Heap.MemAlloc(mBackend.BlockSize);
|
|
//byte* xExt2BlockBuffer = (byte*)Heap.MemAlloc(mBlockSize);
|
|
//INode xCurrentINode;
|
|
//if (!ReadINode(EXT2_ROOT_INO, out xCurrentINode)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
// return null;
|
|
//}
|
|
//bool xCurrentINodeChanged = true;
|
|
//uint xInspectedINodeCount = 0;
|
|
//uint xINodeNumber = EXT2_ROOT_INO;
|
|
//for (int i = 0; i < xPath.Length; i++) {
|
|
// Console.Write("ReadFile, Iteration ");
|
|
// ATAOld.WriteNumber((uint)i, 8);
|
|
// Console.WriteLine("");
|
|
// if (!xCurrentINodeChanged) {
|
|
// Console.WriteLine("Terminating for loop, CurrentINode didn't change");
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
// return null;
|
|
// }
|
|
// xCurrentINodeChanged = false;
|
|
// if (!ReadINodeContents(&xCurrentINode, 0, xExt2BlockBuffer)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
// return null;
|
|
// }
|
|
// DirectoryEntry* xEntryPtr = (DirectoryEntry*)xExt2BlockBuffer;
|
|
// uint xTotalSize = mBlockSize;
|
|
// while (xTotalSize != 0) {
|
|
// DebugUtil.SendExt2_DirectoryEntry(xEntryPtr);
|
|
// uint xPtrAddress = (uint)xEntryPtr;
|
|
// char[] xName = new char[xEntryPtr->NameLength];
|
|
// byte* xNamePtr = &xEntryPtr->FirstNameChar;
|
|
// for (int c = 0; c < xName.Length; c++) {
|
|
// xName[c] = (char)xNamePtr[c];
|
|
// }
|
|
// xInspectedINodeCount++;
|
|
// if (EqualsName(xPath[i], xName)) {
|
|
// if (!ReadINode(xEntryPtr->INodeNumber, out xCurrentINode)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
// return null;
|
|
// }
|
|
// xCurrentINodeChanged = true;
|
|
// xINodeNumber = xEntryPtr->INodeNumber;
|
|
// xTotalSize = 0;
|
|
// continue;
|
|
// }
|
|
// xPtrAddress += xEntryPtr->RecordLength;
|
|
// xTotalSize -= xEntryPtr->RecordLength;
|
|
// xEntryPtr = (DirectoryEntry*)xPtrAddress;
|
|
// }
|
|
//}
|
|
//if (xCurrentINodeChanged) {
|
|
// DebugUtil.SendMessage("Ext2", "ReadFile, for loop exited with an inode change");
|
|
//} else {
|
|
// DebugUtil.SendMessage("Ext2", "ReadFile, for loop exited without an inode change");
|
|
//}
|
|
//if ((xCurrentINode.Mode & INodeModeEnum.RegularFile) == 0) {
|
|
// Console.WriteLine("No file after for loop");
|
|
// return null;
|
|
//}
|
|
//Stream xResult = new FileStream(xINodeNumber, this);
|
|
//Heap.MemFree((uint)xBuffer);
|
|
//Heap.MemFree((uint)xExt2BlockBuffer);
|
|
//return xResult;
|
|
throw new Exception("Ext2 not supported");
|
|
}
|
|
|
|
private unsafe bool ReadINode(uint aINodeNumber, out INode aINode) {
|
|
//DebugUtil.SendNumber("Ext2", "reading INode", aINodeNumber, 32);
|
|
//aINodeNumber--;
|
|
//uint xGroup = aINodeNumber / mSuperBlock.INodesPerGroup;
|
|
//DebugUtil.SendNumber("Ext2", "xGroup", xGroup, 32);
|
|
//uint xIndex = (aINodeNumber % mSuperBlock.INodesPerGroup) * mSuperBlock.INodeSize;
|
|
//DebugUtil.SendNumber("Ext2", "xIndex", xIndex, 32);
|
|
//uint xOffset = xIndex % mBlockSize;
|
|
//DebugUtil.SendNumber("Ext2", "xOffset", xOffset, 32);
|
|
//uint xLogBlockNumber = mGroupDescriptors[xGroup].INodeTable;
|
|
//DebugUtil.SendNumber("Ext2", "InodeBitmap", mGroupDescriptors[xGroup].INodeBitmap, 32);
|
|
//DebugUtil.SendNumber("Ext2", "xLogBlockNumber(1)", xLogBlockNumber, 32);
|
|
//xLogBlockNumber += (xIndex / mBlockSize);
|
|
//DebugUtil.SendNumber("Ext2", "xLogBlockNumber(2)", xLogBlockNumber, 32);
|
|
//uint xPhBlockNumber = (xLogBlockNumber * mBlockSize) / mBackend.BlockSize;
|
|
uint xPhBlockNumber = 0;
|
|
//xPhBlockNumber += xIndex / mBackend.BlockSize;
|
|
//xIndex %= mBackend.BlockSize;
|
|
var xBytes = mBackend.ReadBlock(xPhBlockNumber);
|
|
//DebugUtil.SendNumber("Ext2", "BlockOffset", xIndex, 32);
|
|
//fixed (byte* xINodePtr = &xBytes[xIndex]) {
|
|
fixed (byte* xINodePtr = &xBytes[0]) {
|
|
aINode = *(INode*)xINodePtr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the logical block at index <paramref name="aBlock"/>. Make sure the buffer is of the correct size
|
|
/// </summary>
|
|
private bool ReadINodeContents(INode* aINode, uint aBlock, byte* aBuffer) {
|
|
uint xBlock;
|
|
|
|
switch (aBlock) {
|
|
case 0:
|
|
xBlock = aINode->Block1;
|
|
break;
|
|
case 1:
|
|
xBlock = aINode->Block2;
|
|
break;
|
|
case 2:
|
|
xBlock = aINode->Block3;
|
|
break;
|
|
case 3:
|
|
xBlock = aINode->Block4;
|
|
break;
|
|
case 4:
|
|
xBlock = aINode->Block5;
|
|
break;
|
|
case 5:
|
|
xBlock = aINode->Block6;
|
|
break;
|
|
case 6:
|
|
xBlock = aINode->Block7;
|
|
break;
|
|
case 7:
|
|
xBlock = aINode->Block8;
|
|
break;
|
|
case 8:
|
|
xBlock = aINode->Block9;
|
|
break;
|
|
case 9:
|
|
xBlock = aINode->Block10;
|
|
break;
|
|
case 10:
|
|
xBlock = aINode->Block11;
|
|
break;
|
|
case 11:
|
|
xBlock = aINode->Block12;
|
|
break;
|
|
default:
|
|
Console.WriteLine("Ext2|ReadINodeContents, Only reading of direct blocks supported (12 logical blocks)");
|
|
return false;
|
|
}
|
|
uint xBase = xBlock * (mBlockSize / mBackend.BlockSize);
|
|
for (int i = 0; i < (mBlockSize / mBackend.BlockSize); i++) {
|
|
var xBytes = mBackend.ReadBlock((uint)(xBase + i));
|
|
for (int b = 0; b < xBytes.Length; b++) {
|
|
aBuffer[(i * mBackend.BlockSize) + b] = xBytes[b];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public unsafe byte[] ReadFile(string[] xPath) {
|
|
INode xINode;
|
|
if (!GetINodeByName(xPath, out xINode)) {
|
|
return null;
|
|
}
|
|
uint xSize = xINode.Size;
|
|
if (xSize > mBackend.BlockSize) {
|
|
xSize = mBackend.BlockSize;
|
|
}
|
|
// DebugUtil.SendNumber("Ext2", "ReadFile, Block1", xINode.Block1, 32);
|
|
//byte* xBuff = (byte*)Heap.MemAlloc(mBlockSize);
|
|
try {
|
|
//if (!ReadINodeContents(&xINode, 0, xBuff)) {
|
|
return null;
|
|
//}
|
|
byte[] xResult = new byte[xSize];
|
|
for (int i = 0; i < xSize; i++) {
|
|
//xResult[i] = xBuff[i];
|
|
}
|
|
return xResult;
|
|
} finally {
|
|
//Heap.MemFree((uint)xBuff);
|
|
}
|
|
}
|
|
|
|
private bool GetINodeByName(string[] aPath, out INode aINode) {
|
|
//ushort* xBuffer = (ushort*)Heap.MemAlloc(mBackend.BlockSize);
|
|
//byte* xExt2BlockBuffer = (byte*)Heap.MemAlloc(mBlockSize);
|
|
byte* xExt2BlockBuffer = null;
|
|
INode xCurrentINode = new INode();
|
|
aINode = xCurrentINode;
|
|
if (!ReadINode(EXT2_ROOT_INO, out xCurrentINode)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return false;
|
|
}
|
|
bool xCurrentINodeChanged = true;
|
|
uint xInspectedINodeCount = 0;
|
|
for (int i = 0; i < aPath.Length; i++) {
|
|
Console.Write("ReadFile, Iteration ");
|
|
ATAOld.WriteNumber((uint)i, 8);
|
|
Console.WriteLine("");
|
|
if (!xCurrentINodeChanged) {
|
|
Console.WriteLine("Terminating for loop, CurrentINode didn't change");
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return false;
|
|
}
|
|
xCurrentINodeChanged = false;
|
|
if (!ReadINodeContents(&xCurrentINode, 0, xExt2BlockBuffer)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return false;
|
|
}
|
|
DirectoryEntry* xEntryPtr = (DirectoryEntry*)xExt2BlockBuffer;
|
|
uint xTotalSize = mBlockSize;
|
|
while (xTotalSize != 0) {
|
|
// DebugUtil.SendExt2_DirectoryEntry(xEntryPtr);
|
|
uint xPtrAddress = (uint)xEntryPtr;
|
|
char[] xName = new char[xEntryPtr->NameLength];
|
|
byte* xNamePtr = &xEntryPtr->FirstNameChar;
|
|
for (int c = 0; c < xName.Length; c++) {
|
|
xName[c] = (char)xNamePtr[c];
|
|
}
|
|
xInspectedINodeCount++;
|
|
if (EqualsName(aPath[i], xName)) {
|
|
if (!ReadINode(xEntryPtr->INodeNumber, out xCurrentINode)) {
|
|
// Heap.MemFree((uint)xBuffer);
|
|
//Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return false;
|
|
}
|
|
xCurrentINodeChanged = true;
|
|
xTotalSize = 0;
|
|
continue;
|
|
}
|
|
xPtrAddress += xEntryPtr->RecordLength;
|
|
xTotalSize -= xEntryPtr->RecordLength;
|
|
xEntryPtr = (DirectoryEntry*)xPtrAddress;
|
|
}
|
|
}
|
|
aINode = xCurrentINode;
|
|
return true;
|
|
}
|
|
|
|
public string[] GetDirectoryEntries(string[] aPath) {
|
|
List<string> xResult = new List<string>(32);
|
|
INode xINode;
|
|
//byte* xExt2BlockBuffer = (byte*)Heap.MemAlloc(mBlockSize);
|
|
byte* xExt2BlockBuffer = null;
|
|
if (!GetINodeByName(aPath, out xINode)) {
|
|
throw new Exception("Couldn't find path!");
|
|
}
|
|
if ((xINode.Mode & INodeModeEnum.Directory) == 0) {
|
|
Console.WriteLine("Ext2|GetDirectoryEntries, No directory after for loop");
|
|
return null;
|
|
}
|
|
// if (!ReadINodeContents(&xINode, 0, xExt2BlockBuffer)) {
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return null;
|
|
// }
|
|
DirectoryEntry* xEntry = (DirectoryEntry*)xExt2BlockBuffer;
|
|
uint xSize = mBlockSize;
|
|
// DebugUtil.SendMessage("Ext2", "GetDirectoryEntries");
|
|
while (xSize != 0) {
|
|
// DebugUtil.SendExt2_DirectoryEntry(xEntry);
|
|
uint xPtrAddress = (uint)xEntry;
|
|
char[] xName = new char[xEntry->NameLength];
|
|
byte* xNamePtr = &xEntry->FirstNameChar;
|
|
for (int c = 0; c < xName.Length; c++) {
|
|
byte b = xNamePtr[c];
|
|
xName[c] = (char)b;
|
|
}
|
|
xResult.Add(new String(xName));
|
|
xPtrAddress += xEntry->RecordLength;
|
|
xSize -= xEntry->RecordLength;
|
|
xEntry = (DirectoryEntry*)xPtrAddress;
|
|
}
|
|
// Heap.MemFree((uint)xExt2BlockBuffer);
|
|
return xResult.ToArray();
|
|
}
|
|
|
|
private static bool EqualsName(string a1, char[] a2) {
|
|
if (a1 == null || a2 == null) {
|
|
return false;
|
|
}
|
|
if (a1.Length != a2.Length) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < a1.Length; i++) {
|
|
if (a1[i] != a2[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|