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; } /// /// Reads the logical block at index . Make sure the buffer is of the correct size /// 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 xResult = new List(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; } } }