#include "ext2_common.h"
// === MACROS ===
-#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)])
+#define BLOCK_DIR_OFS(_data, _block) (((Uint16*)(_data))[(_block)])
// === PROTOTYPES ===
char *Ext2_ReadDir(tVFS_Node *Node, int Pos);
Uint64 inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode);
if( inodeNum == 0 ) {
+ LOG("Inode allocation failed");
+ LEAVE('i', -1);
return -1;
}
tVFS_Node *child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum);
if( !child ) {
Ext2_int_DereferenceInode(Parent->ImplPtr, inodeNum);
+ Log_Warning("Ext2", "Ext2_MkNod - Node creation failed");
+ LEAVE('i', -1);
return -1;
}
*/
int Ext2_Unlink(tVFS_Node *Node, const char *OldName)
{
+ Log_Warning("Ext2", "TODO: Impliment Ext2_Unlink");
return 1;
}
*/
int Ext2_Link(tVFS_Node *Node, const char *Name, tVFS_Node *Child)
{
- #if 0
+ #if 1
tExt2_Disk *disk = Node->ImplPtr;
tExt2_Inode inode;
- tExt2_DirEnt dirent;
+ tExt2_DirEnt *dirent;
tExt2_DirEnt newEntry;
- Uint64 Base; // Block's Base Address
+ Uint64 base; // Block's Base Address
int block = 0, ofs = 0;
Uint size;
void *blockData;
- int bestMatch = -1, bestSize, bestBlock, bestOfs;
+ int bestMatch = -1, bestSize, bestBlock, bestOfs, bestNeedsSplit;
int nEntries;
+
+ ENTER("pNode sName pChild",
+ Node, Name, Child);
blockData = malloc(disk->BlockSize);
// Create a stub entry
newEntry.inode = Child->Inode;
newEntry.name_len = strlen(Name);
- newEntry.rec_len = (newEntry.name_len+3+8)&~3;
+ newEntry.rec_len = ((newEntry.name_len+3)&~3) + EXT2_DIRENT_SIZE;
newEntry.type = inode.i_mode >> 12;
memcpy(newEntry.name, Name, newEntry.name_len);
size = inode.i_size;
// Get a lock on the inode
- Ext2_int_LockInode(disk, Node->Inode);
-
+ //Ext2_int_LockInode(disk, Node->Inode);
+ Mutex_Acquire(&Node->Lock);
+
+// if( !Node->Data ) {
+// }
+
// Get First Block
// - Do this ourselves as it is a simple operation
base = inode.i_block[0] * disk->BlockSize;
"Directory entry %i of inode 0x%x extends over a block boundary",
nEntries, (Uint)Node->Inode);
}
- else {
-
+ else
+ {
+ LOG("Entry %i: %x %i bytes", nEntries, dirent->type, dirent->rec_len);
// Free entry
- if(dirent->type == 0) {
+ if(dirent->type == 0)
+ {
if( dirent->rec_len >= newEntry.rec_len
&& (bestMatch == -1 || bestSize > dirent->rec_len) )
{
bestSize = dirent->rec_len;
bestBlock = block;
bestOfs = ofs;
+ bestNeedsSplit = 0;
}
}
// Non free - check name to avoid duplicates
- else {
+ else
+ {
+ LOG(" name='%.*s'", dirent->name_len, dirent->name);
if(strncmp(Name, dirent->name, dirent->name_len) == 0) {
- Ext2_int_UnlockInode(disk, Node->Inode);
+ //Ext2_int_UnlockInode(disk, Node->Inode);
+ Mutex_Release(&Node->Lock);
+ LEAVE('i', 1);
return 1; // ERR_???
}
+
+ int spare_space = dirent->rec_len - (dirent->name_len + EXT2_DIRENT_SIZE);
+ if( spare_space > newEntry.rec_len
+ && (bestMatch == -1 || bestSize > spare_space) )
+ {
+ bestMatch = nEntries;
+ bestSize = spare_space;
+ bestBlock = block;
+ bestOfs = ofs;
+ bestNeedsSplit = 1;
+ }
}
}
// Increment the pointer
nEntries ++;
ofs += dirent->rec_len;
- if( ofs >= disk->BlockSize ) {
+ size -= dirent->rec_len;
+ if( size > 0 && ofs >= disk->BlockSize ) {
// Read the next block if needed
- BLOCK_DIR_OFS(Node->Data, block) = nEntries;
+ // BLOCK_DIR_OFS(Node->Data, block) = nEntries;
block ++;
ofs = 0;
base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
}
}
+ LOG("bestMatch = %i", bestMatch);
+ // If EOF was reached with no space, check if we can fit one on the end
+ if( bestMatch < 0 && ofs + newEntry.rec_len < disk->BlockSize ) {
+ Node->Size += newEntry.rec_len;
+ Node->Flags |= VFS_FFLAG_DIRTY;
+ bestBlock = block;
+ bestOfs = ofs;
+ bestSize = newEntry.rec_len;
+ bestNeedsSplit = 0;
+ }
// Check if a free slot was found
- if( bestMatch >= 0 ) {
+ if( bestMatch >= 0 )
+ {
// Read-Modify-Write
- bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
- if( block > 0 )
- bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock);
+ base = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock);
VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData );
dirent = blockData + bestOfs;
- memcpy(dirent, newEntry, newEntry.rec_len);
+ // Shorten a pre-existing entry
+ if(bestNeedsSplit)
+ {
+ dirent->rec_len = EXT2_DIRENT_SIZE + dirent->name_len;
+ bestOfs += dirent->rec_len;
+ //bestSize -= dirent->rec_len; // (not needed, bestSize is the spare space after)
+ dirent = blockData + bestOfs;
+ }
+ // Insert new file entry
+ memcpy(dirent, &newEntry, newEntry.rec_len);
+ // Create a new blank entry
+ if( bestSize != newEntry.rec_len )
+ {
+ bestOfs += newEntry.rec_len;
+ dirent = blockData + bestOfs;
+
+ dirent->rec_len = bestSize - newEntry.rec_len;
+ dirent->type = 0;
+ }
+ // Save changes
VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
}
else {
// Allocate block, Write
- block = Ext2_int_AllocateBlock(Disk, block);
- Log_Warning("EXT2", "");
+ Uint32 newblock = Ext2_int_AllocateBlock(disk, base / disk->BlockSize);
+ Ext2_int_AppendBlock(disk, &inode, newblock);
+ base = newblock * disk->BlockSize;
+ Node->Size += newEntry.rec_len;
+ Node->Flags |= VFS_FFLAG_DIRTY;
+ memcpy(blockData, &newEntry, newEntry.rec_len);
+ memset(blockData + newEntry.rec_len, 0, disk->BlockSize - newEntry.rec_len);
+ VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData );
}
- Ext2_int_UnlockInode(disk, Node->Inode);
+ Child->ImplInt ++;
+ Child->Flags |= VFS_FFLAG_DIRTY;
+
+ //Ext2_int_UnlockInode(disk, Node->Inode);
+ Mutex_Release(&Node->Lock);
+ LEAVE('i', 0);
return 0;
#else
+ Log_Warning("Ext2", "TODO: Impliment Ext2_Link");
return 1;
#endif
}
// Save in node cache and return saved node
return Inode_CacheNode(Disk->CacheID, &retNode);
}
+
+int Ext2_int_WritebackNode(tExt2_Disk *Disk, tVFS_Node *Node)
+{
+ Log_Warning("Ext2","TODO: Impliment Ext2_int_WritebackNode");
+ return 0;
+}
+
#include "ext2_common.h"\r
#include <modules.h>\r
\r
+#define MIN_BLOCKS_PER_GROUP 2\r
+#define MAX_BLOCK_LOG_SIZE 10 // 1024 << 10 = 1MiB\r
+\r
// === IMPORTS ===\r
extern tVFS_NodeType gExt2_DirType;\r
\r
LEAVE('n');\r
return NULL;\r
}\r
- \r
+\r
+ if( sb.s_blocks_per_group < MIN_BLOCKS_PER_GROUP ) {\r
+ Log_Warning("Ext2", "Blocks per group is too small (%i < %i)",\r
+ sb.s_blocks_per_group, MIN_BLOCKS_PER_GROUP);\r
+ goto _error;\r
+ } \r
+\r
// Get Group count\r
groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group);\r
LOG("groupCount = %i", groupCount);\r
disk->CacheID = Inode_GetHandle();\r
\r
// Get Block Size\r
+ if( sb.s_log_block_size > MAX_BLOCK_LOG_SIZE ) {\r
+ Log_Warning("Ext2", "Block size (log2) too large (%i > %i)",\r
+ sb.s_log_block_size, MAX_BLOCK_LOG_SIZE);\r
+ goto _error;\r
+ }\r
disk->BlockSize = 1024 << sb.s_log_block_size;\r
LOG("Disk->BlockSie = 0x%x (1024 << %i)", disk->BlockSize, sb.s_log_block_size);\r
\r
\r
LEAVE('p', &disk->RootNode);\r
return &disk->RootNode;\r
+_error:\r
+ free(disk);\r
+ VFS_Close(fd);\r
+ LEAVE('n');\r
+ return NULL;\r
}\r
\r
/**\r
*/\r
Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent)\r
{\r
-// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
- Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented");\r
+ Uint start_group = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group;\r
+ Uint group = start_group;\r
+\r
+ if( Disk->SuperBlock.s_free_inodes_count == 0 ) \r
+ {\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p", Disk);\r
+ return 0;\r
+ }\r
+\r
+ while( group < Disk->GroupCount && Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ group ++;\r
+ if( group == Disk->GroupCount )\r
+ {\r
+ group = 0;\r
+ while( group < start_group && Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ group ++;\r
+ }\r
+ \r
+ if( Disk->Groups[group].bg_free_inodes_count == 0 )\r
+ {\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes on %p, but superblock says some free", Disk);\r
+ return 0;\r
+ }\r
+\r
+ // Load bitmap for group\r
+ // (s_inodes_per_group / 8) bytes worth\r
+ // - Allocate a buffer the size of a sector/block\r
+ // - Read in part of the bitmap\r
+ // - Search for a free inode\r
+ tExt2_Group *bg = &Disk->Groups[group];\r
+ int ofs = 0;\r
+ do {\r
+ const int sector_size = 512;\r
+ Uint8 buf[sector_size];\r
+ VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
+\r
+ int byte, bit;\r
+ for( byte = 0; byte < sector_size && buf[byte] != 0xFF; byte ++ )\r
+ ;\r
+ if( byte < sector_size )\r
+ {\r
+ for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)\r
+ ;\r
+ ASSERT(bit != 8);\r
+ buf[byte] |= 1 << bit;\r
+ VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_inode_bitmap+ofs, sector_size, buf);\r
+\r
+ bg->bg_free_inodes_count --;\r
+ Disk->SuperBlock.s_free_inodes_count --;\r
+\r
+ Uint32 ret = group * Disk->SuperBlock.s_inodes_per_group + byte * 8 + bit + 1;\r
+ Log_Debug("Ext2", "Ext2_int_AllocateInode - Allocated 0x%x", ret);\r
+ return ret;\r
+ }\r
+\r
+ ofs += sector_size;\r
+ } while(ofs < Disk->SuperBlock.s_inodes_per_group / 8);\r
+\r
+ Log_Notice("Ext2", "Ext2_int_AllocateInode - Out of inodes in group %p:%i but header reported free",\r
+ Disk, group);\r
+\r
return 0;\r
}\r
\r
*/\r
void Ext2_int_DereferenceInode(tExt2_Disk *Disk, Uint32 Inode)\r
{\r
- \r
+ Log_Warning("Ext2", "TODO: Impliment Ext2_int_DereferenceInode");\r
}\r
\r
/**\r
* Acess OS
* Ext2 Driver Version 1
*/
-/**
- * \file ext2_common.h
- * \brief Second Extended Filesystem Driver
- */
#ifndef _EXT2_COMMON_H
#define _EXT2_COMMON_H
#include <acess.h>
extern size_t Ext2_Read(tVFS_Node *node, off_t offset, size_t length, void *buffer);
// --- Write ---
extern size_t Ext2_Write(tVFS_Node *node, off_t offset, size_t length, const void *buffer);
+extern Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 LastBlock);
+extern void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block);
+extern int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block);
#endif
Uint8 type; //!< File Type (Duplicate of ext2_inode_s.i_mode)\r
char name[EXT2_NAME_LEN+1]; //!< File name\r
};\r
-#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-EXT2_NAME_LEN+1)\r
+#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-(EXT2_NAME_LEN+1))\r
\r
#endif\r
block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
if(!block) return Length - retLen;
// Add it to this inode
- if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
+ if( Ext2_int_AppendBlock(disk, &inode, block) ) {
+ Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
+ block, disk, Node->Inode);
Ext2_int_DeallocateBlock(disk, block);
goto ret;
}
// Last block :D
block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
if(!block) goto ret;
- if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
+ if( Ext2_int_AppendBlock(disk, &inode, block) ) {
+ Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
+ block, disk, Node->Inode);
Ext2_int_DeallocateBlock(disk, block);
goto ret;
}
Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
{
int bpg = Disk->SuperBlock.s_blocks_per_group;
- Uint blockgroup = PrevBlock / bpg;
- Uint bitmap[Disk->BlockSize/sizeof(Uint)];
- Uint bitsperblock = 8*Disk->BlockSize;
- int i, j = 0;
- Uint block;
-
+ Uint firstgroup = PrevBlock / bpg;
+ Uint blockgroup = firstgroup;
+ tExt2_Group *bg;
+
+ // TODO: Need to do locking on the bitmaps
+
// Are there any free blocks?
- if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
-
- if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
+ if(Disk->SuperBlock.s_free_blocks_count == 0)
+ return 0;
+
+ // First: Check the next block after \a PrevBlock
+ if( (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group != 0
+ && Disk->Groups[blockgroup].bg_free_blocks_count > 0 )
{
- // Search block group's bitmap
- for(i = 0; i < bpg; i++)
+ bg = &Disk->Groups[blockgroup];
+ const int sector_size = 512;
+ Uint8 buf[sector_size];
+ int iblock = (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group;
+ int byte = iblock / 8;
+ int ofs = byte / sector_size * sector_size;
+ byte %= sector_size;
+ VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
+
+ if( (buf[byte] & (1 << (iblock%8))) == 0 )
{
- // Get the block in the bitmap block
- j = i & (bitsperblock-1);
-
- // Read in if needed
- if(j == 0) {
- VFS_ReadAt(
- Disk->FD,
- (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
- Disk->BlockSize,
- bitmap
- );
- }
-
- // Fast Check
- if( bitmap[j/32] == 0xFFFFFFFF ) {
- j = (j + 31) & ~31;
- continue;
- }
-
- // Is the bit set?
- if( bitmap[j/32] & (1 << (j%32)) )
- continue;
-
- // Ooh! We found one
- break;
- }
- if( i < bpg ) {
- Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
- goto checkAll; // Search the entire filesystem for a free block
- // Goto needed for neatness
+ // Free block - nice and contig allocation
+ buf[byte] |= (1 << (iblock%8));
+ VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
+
+ bg->bg_free_blocks_count --;
+ Disk->SuperBlock.s_free_blocks_count --;
+ #if EXT2_UPDATE_WRITEBACK
+ Ext2_int_UpdateSuperblock(Disk);
+ #endif
+ return PrevBlock + 1;
}
-
- // Mark as used
- bitmap[j/32] |= (1 << (j%32));
- VFS_WriteAt(
- Disk->FD,
- (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
- Disk->BlockSize,
- bitmap
- );
- block = i;
- Disk->Groups[blockgroup].bg_free_blocks_count --;
- #if EXT2_UPDATE_WRITEBACK
- //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
- #endif
+ // Used... darnit
+ // Fall through and search further
}
- else
+
+ // Second: Search for a group with free blocks
+ while( blockgroup < Disk->GroupCount && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
+ blockgroup ++;
+ if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
{
- checkAll:
- Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
+ blockgroup = 0;
+ while( blockgroup < firstgroup && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
+ blockgroup ++;
+ }
+ if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 ) {
+ Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of blockss on %p, but superblock says some free",
+ Disk);
return 0;
}
+
+ // Search the bitmap for a free block
+ bg = &Disk->Groups[blockgroup];
+ int ofs = 0;
+ do {
+ const int sector_size = 512;
+ Uint8 buf[sector_size];
+ VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
+
+ int byte, bit;
+ for( byte = 0; byte < sector_size && buf[byte] != 0xFF; byte ++ )
+ ;
+ if( byte < sector_size )
+ {
+ for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)
+ ;
+ ASSERT(bit != 8);
+ buf[byte] |= 1 << bit;
+ VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
+
+ bg->bg_free_blocks_count --;
+ Disk->SuperBlock.s_free_blocks_count --;
+
+ #if EXT2_UPDATE_WRITEBACK
+ Ext2_int_UpdateSuperblock(Disk);
+ #endif
+
+ Uint32 ret = blockgroup * Disk->SuperBlock.s_blocks_per_group + byte * 8 + bit;
+ Log_Debug("Ext2", "Ext2_int_AllocateBlock - Allocated 0x%x", ret);
+ return ret;
+ }
+ } while(ofs < Disk->SuperBlock.s_blocks_per_group / 8);
- // Reduce global count
- Disk->SuperBlock.s_free_blocks_count --;
- #if EXT2_UPDATE_WRITEBACK
- Ext2_int_UpdateSuperblock(Disk);
- #endif
-
- return block;
+ Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of block in group %p:%i but header reported free",
+ Disk, blockgroup);
+ return 0;
}
/**
*/
void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
{
+ Log_Warning("Ext2", "TODO: Impliment Ext2_int_DeallocateBlock");
}
/**
if( nBlocks == 0 ) {
Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
if( !Inode->i_block[12] ) {
+ Log_Warning("Ext2", "Allocating indirect block failed");
free(blocks);
return 1;
}
if( nBlocks == 0 ) {
Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
if( !Inode->i_block[13] ) {
+ Log_Warning("Ext2", "Allocating double indirect block failed");
free(blocks);
return 1;
}
id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
if( !id1 ) {
free(blocks);
+ Log_Warning("Ext2", "Allocating double indirect block (l2) failed");
return 1;
}
blocks[nBlocks/dwPerBlock] = id1;
if( nBlocks == 0 ) {
Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
if( !Inode->i_block[14] ) {
+ Log_Warning("Ext2", "Allocating triple indirect block failed");
free(blocks);
return 1;
}
{
id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
if( !id1 ) {
+ Log_Warning("Ext2", "Allocating triple indirect block (l2) failed");
free(blocks);
return 1;
}
if( nBlocks % dwPerBlock == 0 ) {
id2 = Ext2_int_AllocateBlock(Disk, id1);
if( !id2 ) {
+ Log_Warning("Ext2", "Allocating triple indirect block (l3) failed");
free(blocks);
return 1;
}
return 0;
}
- Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");
+ Log_Warning("Ext2", "Inode ?? cannot have a block appended to it, all indirects used");
free(blocks);
return 1;
}