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 \a PrevBlock
161 if( (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group != 0
162 && Disk->Groups[blockgroup].bg_free_blocks_count > 0 )
164 bg = &Disk->Groups[blockgroup];
165 const int sector_size = 512;
166 Uint8 buf[sector_size];
167 int iblock = (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group;
168 int byte = iblock / 8;
169 int ofs = byte / sector_size * sector_size;
171 VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
173 if( (buf[byte] & (1 << (iblock%8))) == 0 )
175 // Free block - nice and contig allocation
176 buf[byte] |= (1 << (iblock%8));
177 VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
179 bg->bg_free_blocks_count --;
180 Disk->SuperBlock.s_free_blocks_count --;
181 #if EXT2_UPDATE_WRITEBACK
182 Ext2_int_UpdateSuperblock(Disk);
184 return PrevBlock + 1;
187 // Fall through and search further
190 // Second: Search for a group with free blocks
191 while( blockgroup < Disk->GroupCount && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
193 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
196 while( blockgroup < firstgroup && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
199 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 ) {
200 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of blockss on %p, but superblock says some free",
205 // Search the bitmap for a free block
206 bg = &Disk->Groups[blockgroup];
209 const int sector_size = 512;
210 Uint8 buf[sector_size];
211 VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
214 for( byte = 0; byte < sector_size && buf[byte] != 0xFF; byte ++ )
216 if( byte < sector_size )
218 for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)
221 buf[byte] |= 1 << bit;
222 VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
224 bg->bg_free_blocks_count --;
225 Disk->SuperBlock.s_free_blocks_count --;
227 #if EXT2_UPDATE_WRITEBACK
228 Ext2_int_UpdateSuperblock(Disk);
231 Uint32 ret = blockgroup * Disk->SuperBlock.s_blocks_per_group + byte * 8 + bit;
232 Log_Debug("Ext2", "Ext2_int_AllocateBlock - Allocated 0x%x", ret);
235 } while(ofs < Disk->SuperBlock.s_blocks_per_group / 8);
237 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of block in group %p:%i but header reported free",
243 * \brief Deallocates a block
245 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
247 Log_Warning("Ext2", "TODO: Impliment Ext2_int_DeallocateBlock");
251 * \brief Append a block to an inode
253 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
256 int dwPerBlock = Disk->BlockSize / 4;
260 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
264 Inode->i_block[nBlocks] = Block;
268 blocks = malloc( Disk->BlockSize );
269 if(!blocks) return 1;
273 if( nBlocks < dwPerBlock)
275 // Allocate/Get Indirect block
277 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
278 if( !Inode->i_block[12] ) {
279 Log_Warning("Ext2", "Allocating indirect block failed");
283 memset(blocks, 0, Disk->BlockSize);
286 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
288 blocks[nBlocks] = Block;
290 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
295 nBlocks += dwPerBlock;
297 if( nBlocks < dwPerBlock*dwPerBlock )
299 // Allocate/Get Indirect block
301 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
302 if( !Inode->i_block[13] ) {
303 Log_Warning("Ext2", "Allocating double indirect block failed");
307 memset(blocks, 0, Disk->BlockSize);
310 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
312 // Allocate / Get Indirect lvl2 Block
313 if( nBlocks % dwPerBlock == 0 ) {
314 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
317 Log_Warning("Ext2", "Allocating double indirect block (l2) failed");
320 blocks[nBlocks/dwPerBlock] = id1;
321 // Write back indirect 1 block
322 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
323 memset(blocks, 0, Disk->BlockSize);
326 id1 = blocks[nBlocks / dwPerBlock];
327 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
330 blocks[nBlocks % dwPerBlock] = Block;
332 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
337 nBlocks -= dwPerBlock*dwPerBlock;
339 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
341 // Allocate/Get Indirect block
343 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
344 if( !Inode->i_block[14] ) {
345 Log_Warning("Ext2", "Allocating triple indirect block failed");
349 memset(blocks, 0, Disk->BlockSize);
352 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
354 // Allocate / Get Indirect lvl2 Block
355 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
357 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
359 Log_Warning("Ext2", "Allocating triple indirect block (l2) failed");
363 blocks[nBlocks/dwPerBlock] = id1;
364 // Write back indirect 1 block
365 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
366 memset(blocks, 0, Disk->BlockSize);
369 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
370 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
373 // Allocate / Get Indirect Level 3 Block
374 if( nBlocks % dwPerBlock == 0 ) {
375 id2 = Ext2_int_AllocateBlock(Disk, id1);
377 Log_Warning("Ext2", "Allocating triple indirect block (l3) failed");
381 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
382 // Write back indirect 1 block
383 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
384 memset(blocks, 0, Disk->BlockSize);
387 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
388 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
391 blocks[nBlocks % dwPerBlock] = Block;
393 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
398 Log_Warning("Ext2", "Inode ?? cannot have a block appended to it, all indirects used");