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 * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
22 * \brief Write to a file
24 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
26 tExt2_Disk *disk = Node->ImplPtr;
34 Debug_HexDump("Ext2_Write", Buffer, Length);
36 Ext2_int_ReadInode(disk, Node->Inode, &inode);
38 // Get the ammount of space already allocated
39 // - Round size up to block size
40 // - block size is a power of two, so this will work
41 allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
43 // Are we writing to inside the allocated space?
44 if( Offset > allocSize ) return 0;
46 if( Offset < allocSize )
48 // Will we go out of it?
49 if(Offset + Length > allocSize) {
51 retLen = allocSize - Offset;
55 // Within the allocated space
56 block = Offset / disk->BlockSize;
57 Offset %= disk->BlockSize;
58 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
60 // Write only block (if only one)
61 if(Offset + retLen <= disk->BlockSize) {
62 VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
63 if(!bNewBlocks) return Length;
64 goto addBlocks; // Ugh! A goto, but it seems unavoidable
68 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
69 Buffer += disk->BlockSize-Offset;
70 retLen -= disk->BlockSize-Offset;
73 // Write middle blocks
74 while(retLen > disk->BlockSize)
76 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
77 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
78 Buffer += disk->BlockSize;
79 retLen -= disk->BlockSize;
84 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
85 VFS_WriteAt(disk->FD, base, retLen, Buffer);
86 if(!bNewBlocks) return Length; // Writing in only allocated space
89 base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
92 Log_Notice("EXT2", "File extending is untested");
94 // Allocate blocks and copy data to them
95 retLen = Length - (allocSize-Offset);
96 while( retLen > disk->BlockSize )
99 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
100 if(!block) return Length - retLen;
101 // Add it to this inode
102 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
103 Ext2_int_DeallocateBlock(disk, block);
106 // Copy data to the node
107 base = block * disk->BlockSize;
108 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
109 // Update pointer and size remaining
110 inode.i_size += disk->BlockSize;
111 Buffer += disk->BlockSize;
112 retLen -= disk->BlockSize;
115 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
117 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
118 Ext2_int_DeallocateBlock(disk, block);
121 base = block * disk->BlockSize;
122 VFS_WriteAt(disk->FD, base, retLen, Buffer);
123 inode.i_size += retLen;
126 ret: // Makes sure the changes to the inode are committed
127 Ext2_int_WriteInode(disk, Node->Inode, &inode);
128 return Length - retLen;
132 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
133 * \brief Allocate a block from the best possible location
134 * \param Disk EXT2 Disk Information Structure
135 * \param PrevBlock Previous block ID in the file
137 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
139 int bpg = Disk->SuperBlock.s_blocks_per_group;
140 Uint blockgroup = PrevBlock / bpg;
141 Uint bitmap[Disk->BlockSize/sizeof(Uint)];
142 Uint bitsperblock = 8*Disk->BlockSize;
146 // Are there any free blocks?
147 if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
149 if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
151 // Search block group's bitmap
152 for(i = 0; i < bpg; i++)
154 // Get the block in the bitmap block
155 j = i & (bitsperblock-1);
161 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
168 if( bitmap[j/32] == 0xFFFFFFFF ) {
174 if( bitmap[j/32] & (1 << (j%32)) )
181 Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
182 goto checkAll; // Search the entire filesystem for a free block
183 // Goto needed for neatness
187 bitmap[j/32] |= (1 << (j%32));
190 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
195 Disk->Groups[blockgroup].bg_free_blocks_count --;
196 #if EXT2_UPDATE_WRITEBACK
197 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
203 Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
207 // Reduce global count
208 Disk->SuperBlock.s_free_blocks_count --;
209 #if EXT2_UPDATE_WRITEBACK
210 Ext2_int_UpdateSuperblock(Disk);
217 * \brief Deallocates a block
219 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
224 * \brief Append a block to an inode
226 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
229 int dwPerBlock = Disk->BlockSize / 4;
233 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
237 Inode->i_block[nBlocks] = Block;
241 blocks = malloc( Disk->BlockSize );
242 if(!blocks) return 1;
246 if( nBlocks < dwPerBlock)
248 // Allocate/Get Indirect block
250 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
251 if( !Inode->i_block[12] ) {
255 memset(blocks, 0, Disk->BlockSize);
258 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
260 blocks[nBlocks] = Block;
262 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
267 nBlocks += dwPerBlock;
269 if( nBlocks < dwPerBlock*dwPerBlock )
271 // Allocate/Get Indirect block
273 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
274 if( !Inode->i_block[13] ) {
278 memset(blocks, 0, Disk->BlockSize);
281 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
283 // Allocate / Get Indirect lvl2 Block
284 if( nBlocks % dwPerBlock == 0 ) {
285 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
290 blocks[nBlocks/dwPerBlock] = id1;
291 // Write back indirect 1 block
292 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
293 memset(blocks, 0, Disk->BlockSize);
296 id1 = blocks[nBlocks / dwPerBlock];
297 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
300 blocks[nBlocks % dwPerBlock] = Block;
302 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
307 nBlocks -= dwPerBlock*dwPerBlock;
309 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
311 // Allocate/Get Indirect block
313 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
314 if( !Inode->i_block[14] ) {
318 memset(blocks, 0, Disk->BlockSize);
321 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
323 // Allocate / Get Indirect lvl2 Block
324 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
326 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
331 blocks[nBlocks/dwPerBlock] = id1;
332 // Write back indirect 1 block
333 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
334 memset(blocks, 0, Disk->BlockSize);
337 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
338 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
341 // Allocate / Get Indirect Level 3 Block
342 if( nBlocks % dwPerBlock == 0 ) {
343 id2 = Ext2_int_AllocateBlock(Disk, id1);
348 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
349 // Write back indirect 1 block
350 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
351 memset(blocks, 0, Disk->BlockSize);
354 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
355 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
358 blocks[nBlocks % dwPerBlock] = Block;
360 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
365 Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");