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)
25 tExt2_Disk *disk = Node->ImplPtr;
33 Debug_HexDump("Ext2_Write", Buffer, Length);
35 Ext2_int_ReadInode(disk, Node->Inode, &inode);
37 // Get the ammount of space already allocated
38 // - Round size up to block size
39 // - block size is a power of two, so this will work
40 allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
42 // Are we writing to inside the allocated space?
43 if( Offset > allocSize ) return 0;
45 if( Offset < allocSize )
47 // Will we go out of it?
48 if(Offset + Length > allocSize) {
50 retLen = allocSize - Offset;
54 // Within the allocated space
55 block = Offset / disk->BlockSize;
56 Offset %= disk->BlockSize;
57 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
59 // Write only block (if only one)
60 if(Offset + retLen <= disk->BlockSize) {
61 VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
62 if(!bNewBlocks) return Length;
63 goto addBlocks; // Ugh! A goto, but it seems unavoidable
67 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
68 Buffer += disk->BlockSize-Offset;
69 retLen -= disk->BlockSize-Offset;
72 // Write middle blocks
73 while(retLen > disk->BlockSize)
75 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
76 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
77 Buffer += disk->BlockSize;
78 retLen -= disk->BlockSize;
83 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
84 VFS_WriteAt(disk->FD, base, retLen, Buffer);
85 if(!bNewBlocks) return Length; // Writing in only allocated space
88 base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
91 Log_Notice("EXT2", "File extending is untested");
93 // Allocate blocks and copy data to them
94 retLen = Length - (allocSize-Offset);
95 while( retLen > disk->BlockSize )
98 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
99 if(!block) return Length - retLen;
100 // Add it to this inode
101 if( Ext2_int_AppendBlock(disk, &inode, block) ) {
102 Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
103 block, disk, Node->Inode);
104 Ext2_int_DeallocateBlock(disk, block);
107 // Copy data to the node
108 base = block * disk->BlockSize;
109 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
110 // Update pointer and size remaining
111 inode.i_size += disk->BlockSize;
112 Buffer += disk->BlockSize;
113 retLen -= disk->BlockSize;
116 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
118 if( Ext2_int_AppendBlock(disk, &inode, block) ) {
119 Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
120 block, disk, Node->Inode);
121 Ext2_int_DeallocateBlock(disk, block);
124 base = block * disk->BlockSize;
125 VFS_WriteAt(disk->FD, base, retLen, Buffer);
127 // TODO: When should the size update be committed?
128 inode.i_size += retLen;
129 Node->Size += retLen;
130 Node->Flags |= VFS_FFLAG_DIRTY;
134 ret: // Makes sure the changes to the inode are committed
135 Ext2_int_WriteInode(disk, Node->Inode, &inode);
136 return Length - retLen;
140 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
141 * \brief Allocate a block from the best possible location
142 * \param Disk EXT2 Disk Information Structure
143 * \param PrevBlock Previous block ID in the file
145 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
147 int bpg = Disk->SuperBlock.s_blocks_per_group;
148 Uint firstgroup = PrevBlock / bpg;
149 Uint blockgroup = firstgroup;
152 // TODO: Need to do locking on the bitmaps
154 // Are there any free blocks?
155 if(Disk->SuperBlock.s_free_blocks_count == 0)
158 // First: Check the next block after \a PrevBlock
159 if( (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group != 0
160 && Disk->Groups[blockgroup].bg_free_blocks_count > 0 )
162 bg = &Disk->Groups[blockgroup];
163 const int sector_size = 512;
164 Uint8 buf[sector_size];
165 int iblock = (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group;
166 int byte = iblock / 8;
167 int ofs = byte / sector_size * sector_size;
169 VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
171 if( (buf[byte] & (1 << (iblock%8))) == 0 )
173 // Free block - nice and contig allocation
174 buf[byte] |= (1 << (iblock%8));
175 VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
177 bg->bg_free_blocks_count --;
178 Disk->SuperBlock.s_free_blocks_count --;
179 #if EXT2_UPDATE_WRITEBACK
180 Ext2_int_UpdateSuperblock(Disk);
182 return PrevBlock + 1;
185 // Fall through and search further
188 // Second: Search for a group with free blocks
189 while( blockgroup < Disk->GroupCount && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
191 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
194 while( blockgroup < firstgroup && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
197 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 ) {
198 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of blockss on %p, but superblock says some free",
203 // Search the bitmap for a free block
204 bg = &Disk->Groups[blockgroup];
207 const int sector_size = 512;
208 Uint8 buf[sector_size];
209 VFS_ReadAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
212 for( byte = 0; byte < sector_size && buf[byte] != 0xFF; byte ++ )
214 if( byte < sector_size )
216 for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)
219 buf[byte] |= 1 << bit;
220 VFS_WriteAt(Disk->FD, Disk->BlockSize*bg->bg_block_bitmap+ofs, sector_size, buf);
222 bg->bg_free_blocks_count --;
223 Disk->SuperBlock.s_free_blocks_count --;
225 #if EXT2_UPDATE_WRITEBACK
226 Ext2_int_UpdateSuperblock(Disk);
229 Uint32 ret = blockgroup * Disk->SuperBlock.s_blocks_per_group + byte * 8 + bit;
230 Log_Debug("Ext2", "Ext2_int_AllocateBlock - Allocated 0x%x", ret);
233 } while(ofs < Disk->SuperBlock.s_blocks_per_group / 8);
235 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of block in group %p:%i but header reported free",
241 * \brief Deallocates a block
243 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
245 Log_Warning("Ext2", "TODO: Impliment Ext2_int_DeallocateBlock");
249 * \brief Append a block to an inode
251 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
254 int dwPerBlock = Disk->BlockSize / 4;
258 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
262 Inode->i_block[nBlocks] = Block;
266 blocks = malloc( Disk->BlockSize );
267 if(!blocks) return 1;
271 if( nBlocks < dwPerBlock)
273 // Allocate/Get Indirect block
275 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
276 if( !Inode->i_block[12] ) {
277 Log_Warning("Ext2", "Allocating indirect block failed");
281 memset(blocks, 0, Disk->BlockSize);
284 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
286 blocks[nBlocks] = Block;
288 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
293 nBlocks += dwPerBlock;
295 if( nBlocks < dwPerBlock*dwPerBlock )
297 // Allocate/Get Indirect block
299 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
300 if( !Inode->i_block[13] ) {
301 Log_Warning("Ext2", "Allocating double indirect block failed");
305 memset(blocks, 0, Disk->BlockSize);
308 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
310 // Allocate / Get Indirect lvl2 Block
311 if( nBlocks % dwPerBlock == 0 ) {
312 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
315 Log_Warning("Ext2", "Allocating double indirect block (l2) failed");
318 blocks[nBlocks/dwPerBlock] = id1;
319 // Write back indirect 1 block
320 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
321 memset(blocks, 0, Disk->BlockSize);
324 id1 = blocks[nBlocks / dwPerBlock];
325 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
328 blocks[nBlocks % dwPerBlock] = Block;
330 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
335 nBlocks -= dwPerBlock*dwPerBlock;
337 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
339 // Allocate/Get Indirect block
341 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
342 if( !Inode->i_block[14] ) {
343 Log_Warning("Ext2", "Allocating triple indirect block failed");
347 memset(blocks, 0, Disk->BlockSize);
350 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
352 // Allocate / Get Indirect lvl2 Block
353 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
355 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
357 Log_Warning("Ext2", "Allocating triple indirect block (l2) failed");
361 blocks[nBlocks/dwPerBlock] = id1;
362 // Write back indirect 1 block
363 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
364 memset(blocks, 0, Disk->BlockSize);
367 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
368 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
371 // Allocate / Get Indirect Level 3 Block
372 if( nBlocks % dwPerBlock == 0 ) {
373 id2 = Ext2_int_AllocateBlock(Disk, id1);
375 Log_Warning("Ext2", "Allocating triple indirect block (l3) failed");
379 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
380 // Write back indirect 1 block
381 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
382 memset(blocks, 0, Disk->BlockSize);
385 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
386 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
389 blocks[nBlocks % dwPerBlock] = Block;
391 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
396 Log_Warning("Ext2", "Inode ?? cannot have a block appended to it, all indirects used");