3 * Ext2 Driver Version 1
7 * \brief Second Extended Filesystem Driver
8 * \todo Implement file full write support
12 #include "ext2_common.h"
15 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
16 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block);
17 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block);
21 * \brief Write to a file
23 size_t Ext2_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
25 tExt2_Disk *disk = Node->ImplPtr;
33 //Debug_HexDump("Ext2_Write", Buffer, Length);
35 // TODO: Handle (Flags & VFS_IOFLAG_NOBLOCK)
37 Ext2_int_ReadInode(disk, Node->Inode, &inode);
39 // Get the ammount of space already allocated
40 // - Round size up to block size
41 // - block size is a power of two, so this will work
42 allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
44 // Are we writing to inside the allocated space?
45 if( Offset > allocSize ) return 0;
47 if( Offset < allocSize )
49 // Will we go out of it?
50 if(Offset + Length > allocSize) {
52 retLen = allocSize - Offset;
56 // Within the allocated space
57 block = Offset / disk->BlockSize;
58 Offset %= disk->BlockSize;
59 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
61 // Write only block (if only one)
62 if(Offset + retLen <= disk->BlockSize) {
63 VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
64 if(!bNewBlocks) return Length;
65 goto addBlocks; // Ugh! A goto, but it seems unavoidable
69 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
70 Buffer += disk->BlockSize-Offset;
71 retLen -= disk->BlockSize-Offset;
74 // Write middle blocks
75 while(retLen > disk->BlockSize)
77 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
78 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
79 Buffer += disk->BlockSize;
80 retLen -= disk->BlockSize;
85 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
86 VFS_WriteAt(disk->FD, base, retLen, Buffer);
87 if(!bNewBlocks) return Length; // Writing in only allocated space
90 base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
93 Log_Notice("EXT2", "File extending is untested");
95 // Allocate blocks and copy data to them
96 retLen = Length - (allocSize-Offset);
97 while( retLen > disk->BlockSize )
100 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
101 if(!block) return Length - retLen;
102 // Add it to this inode
103 if( Ext2_int_AppendBlock(disk, &inode, block) ) {
104 Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
105 block, disk, Node->Inode);
106 Ext2_int_DeallocateBlock(disk, block);
109 // Copy data to the node
110 base = block * disk->BlockSize;
111 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
112 // Update pointer and size remaining
113 inode.i_size += disk->BlockSize;
114 Buffer += disk->BlockSize;
115 retLen -= disk->BlockSize;
118 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
120 if( Ext2_int_AppendBlock(disk, &inode, block) ) {
121 Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
122 block, disk, Node->Inode);
123 Ext2_int_DeallocateBlock(disk, block);
126 base = block * disk->BlockSize;
127 VFS_WriteAt(disk->FD, base, retLen, Buffer);
129 // TODO: When should the size update be committed?
130 inode.i_size += retLen;
131 Node->Size += retLen;
132 Node->Flags |= VFS_FFLAG_DIRTY;
136 ret: // Makes sure the changes to the inode are committed
137 Ext2_int_WriteInode(disk, Node->Inode, &inode);
138 return Length - retLen;
142 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
143 * \brief Allocate a block from the best possible location
144 * \param Disk EXT2 Disk Information Structure
145 * \param PrevBlock Previous block ID in the file
147 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
149 int bpg = Disk->SuperBlock.s_blocks_per_group;
150 Uint firstgroup = PrevBlock / bpg;
151 Uint blockgroup = firstgroup;
154 // TODO: Need to do locking on the bitmaps
156 // Are there any free blocks?
157 if(Disk->SuperBlock.s_free_blocks_count == 0)
160 // First: Check the next block after `PrevBlock`
161 int iblock = (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group;
162 if( iblock != 0 && Disk->Groups[blockgroup].bg_free_blocks_count > 0 )
164 LOG("Checking %i:%i", blockgroup, iblock);
166 bg = &Disk->Groups[blockgroup];
168 const int sector_size = 512;
169 Uint8 buf[sector_size];
170 int byte = (iblock/8) % sector_size;
171 Uint8 bit = 1 << (iblock % 8);
172 int ofs = (iblock/8) / sector_size * sector_size;
174 Uint64 vol_ofs = Disk->BlockSize*bg->bg_block_bitmap+ofs;
175 VFS_ReadAt(Disk->FD, vol_ofs, sector_size, buf);
177 LOG("buf@%llx[%i] = %02x (& %02x)", vol_ofs, byte, buf[byte], bit);
179 if( (buf[byte] & bit) == 0 )
181 // Free block - nice and contig allocation
183 VFS_WriteAt(Disk->FD, vol_ofs, sector_size, buf);
185 bg->bg_free_blocks_count --;
186 Disk->SuperBlock.s_free_blocks_count --;
187 #if EXT2_UPDATE_WRITEBACK
188 Ext2_int_UpdateSuperblock(Disk);
190 return PrevBlock + 1;
193 // Fall through and search further
196 // Second: Search for a group with free blocks
197 while( blockgroup < Disk->GroupCount && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
199 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
203 while( blockgroup < firstgroup && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
206 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 ) {
207 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of blockss on %p, but superblock says some free",
211 LOG("BG%i has free blocks", blockgroup);
213 // Search the bitmap for a free block
214 bg = &Disk->Groups[blockgroup];
217 const int sector_size = 512;
218 Uint8 buf[sector_size];
219 Uint64 vol_ofs = Disk->BlockSize*bg->bg_block_bitmap+ofs;
220 VFS_ReadAt(Disk->FD, vol_ofs, sector_size, buf);
223 for( byte = 0; byte < sector_size && buf[byte] == 0xFF; byte ++ )
225 if( byte < sector_size )
227 LOG("buf@%llx[%i] = %02x", vol_ofs, byte, buf[byte]);
228 for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)
231 buf[byte] |= 1 << bit;
232 VFS_WriteAt(Disk->FD, vol_ofs, sector_size, buf);
234 bg->bg_free_blocks_count --;
235 Disk->SuperBlock.s_free_blocks_count --;
237 #if EXT2_UPDATE_WRITEBACK
238 Ext2_int_UpdateSuperblock(Disk);
241 Uint32 ret = blockgroup * Disk->SuperBlock.s_blocks_per_group + byte * 8 + bit;
242 Log_Debug("Ext2", "Ext2_int_AllocateBlock - Allocated 0x%x", ret);
245 } while(ofs < Disk->SuperBlock.s_blocks_per_group / 8);
247 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of block in group %p:%i but header reported free",
253 * \brief Deallocates a block
255 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
257 Log_Warning("Ext2", "TODO: Impliment Ext2_int_DeallocateBlock");
261 * \brief Append a block to an inode
263 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
266 int dwPerBlock = Disk->BlockSize / 4;
270 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
274 Inode->i_block[nBlocks] = Block;
278 blocks = malloc( Disk->BlockSize );
279 if(!blocks) return 1;
283 if( nBlocks < dwPerBlock)
285 // Allocate/Get Indirect block
287 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
288 if( !Inode->i_block[12] ) {
289 Log_Warning("Ext2", "Allocating indirect block failed");
293 memset(blocks, 0, Disk->BlockSize);
296 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
298 blocks[nBlocks] = Block;
300 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
305 nBlocks += dwPerBlock;
307 if( nBlocks < dwPerBlock*dwPerBlock )
309 // Allocate/Get Indirect block
311 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
312 if( !Inode->i_block[13] ) {
313 Log_Warning("Ext2", "Allocating double indirect block failed");
317 memset(blocks, 0, Disk->BlockSize);
320 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
322 // Allocate / Get Indirect lvl2 Block
323 if( nBlocks % dwPerBlock == 0 ) {
324 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
327 Log_Warning("Ext2", "Allocating double indirect block (l2) failed");
330 blocks[nBlocks/dwPerBlock] = id1;
331 // Write back indirect 1 block
332 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
333 memset(blocks, 0, Disk->BlockSize);
336 id1 = blocks[nBlocks / dwPerBlock];
337 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
340 blocks[nBlocks % dwPerBlock] = Block;
342 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
347 nBlocks -= dwPerBlock*dwPerBlock;
349 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
351 // Allocate/Get Indirect block
353 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
354 if( !Inode->i_block[14] ) {
355 Log_Warning("Ext2", "Allocating triple indirect block failed");
359 memset(blocks, 0, Disk->BlockSize);
362 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
364 // Allocate / Get Indirect lvl2 Block
365 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
367 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
369 Log_Warning("Ext2", "Allocating triple indirect block (l2) failed");
373 blocks[nBlocks/dwPerBlock] = id1;
374 // Write back indirect 1 block
375 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
376 memset(blocks, 0, Disk->BlockSize);
379 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
380 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
383 // Allocate / Get Indirect Level 3 Block
384 if( nBlocks % dwPerBlock == 0 ) {
385 id2 = Ext2_int_AllocateBlock(Disk, id1);
387 Log_Warning("Ext2", "Allocating triple indirect block (l3) failed");
391 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
392 // Write back indirect 1 block
393 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
394 memset(blocks, 0, Disk->BlockSize);
397 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
398 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
401 blocks[nBlocks % dwPerBlock] = Block;
403 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
408 Log_Warning("Ext2", "Inode ?? cannot have a block appended to it, all indirects used");