3 * Ext2 Driver Version 1
7 * \brief Second Extended Filesystem Driver
8 * \todo Implement file full write support
12 #include "ext2_common.h"
15 Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);
16 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock);
17 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block);
18 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block);
22 * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
23 * \brief Write to a file
25 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
27 tExt2_Disk *disk = Node->ImplPtr;
35 Debug_HexDump("Ext2_Write", Buffer, Length);
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 )
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
89 Warning("[EXT2 ] File extending is untested");
91 // Allocate blocks and copy data to them
92 retLen = Length - allocSize;
93 while( retLen > disk->BlockSize )
96 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
97 if(!block) return Length - retLen;
98 // Add it to this inode
99 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
100 Ext2_int_DeallocateBlock(disk, block);
103 // Copy data to the node
104 base = block * disk->BlockSize;
105 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
106 // Update pointer and size remaining
107 inode.i_size += disk->BlockSize;
108 Buffer += disk->BlockSize;
109 retLen -= disk->BlockSize;
112 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
114 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
115 Ext2_int_DeallocateBlock(disk, block);
118 base = block * disk->BlockSize;
119 VFS_WriteAt(disk->FD, base, retLen, Buffer);
120 inode.i_size += retLen;
123 ret: // Makes sure the changes to the inode are committed
124 Ext2_int_WriteInode(disk, Node->Inode, &inode);
125 return Length - retLen;
129 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
130 * \brief Allocate a block from the best possible location
131 * \param Disk EXT2 Disk Information Structure
132 * \param PrevBlock Previous block ID in the file
134 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
136 int bpg = Disk->SuperBlock.s_blocks_per_group;
137 Uint blockgroup = PrevBlock / bpg;
138 Uint bitmap[Disk->BlockSize/sizeof(Uint)];
139 Uint bitsperblock = 8*Disk->BlockSize;
143 // Are there any free blocks?
144 if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
146 if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
148 // Search block group's bitmap
149 for(i = 0; i < bpg; i++)
151 // Get the block in the bitmap block
152 j = i & (bitsperblock-1);
158 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
165 if( bitmap[j/32] == -1 ) {
171 if( bitmap[j/32] & (1 << (j%32)) )
178 Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
179 goto checkAll; // Search the entire filesystem for a free block
180 // Goto needed for neatness
184 bitmap[j/32] |= (1 << (j%32));
187 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
192 Disk->Groups[blockgroup].bg_free_blocks_count --;
193 #if EXT2_UPDATE_WRITEBACK
194 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
200 Warning("[EXT2 ] TODO - Implement using blocks outside the current block group");
204 // Reduce global count
205 Disk->SuperBlock.s_free_blocks_count --;
206 #if EXT2_UPDATE_WRITEBACK
207 Ext2_int_UpdateSuperblock(Disk);
214 * \brief Deallocates a block
216 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
221 * \brief Append a block to an inode
223 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
226 int dwPerBlock = Disk->BlockSize / 4;
230 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
234 Inode->i_block[nBlocks] = Block;
238 blocks = malloc( Disk->BlockSize );
239 if(!blocks) return 1;
243 if( nBlocks < dwPerBlock)
245 // Allocate/Get Indirect block
247 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
248 if( !Inode->i_block[12] ) {
252 memset(blocks, 0, Disk->BlockSize);
255 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
257 blocks[nBlocks] = Block;
259 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
264 nBlocks += dwPerBlock;
266 if( nBlocks < dwPerBlock*dwPerBlock )
268 // Allocate/Get Indirect block
270 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
271 if( !Inode->i_block[13] ) {
275 memset(blocks, 0, Disk->BlockSize);
278 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
280 // Allocate / Get Indirect lvl2 Block
281 if( nBlocks % dwPerBlock == 0 ) {
282 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
287 blocks[nBlocks/dwPerBlock] = id1;
288 // Write back indirect 1 block
289 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
290 memset(blocks, 0, Disk->BlockSize);
293 id1 = blocks[nBlocks / dwPerBlock];
294 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
297 blocks[nBlocks % dwPerBlock] = Block;
299 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
304 nBlocks -= dwPerBlock*dwPerBlock;
306 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
308 // Allocate/Get Indirect block
310 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
311 if( !Inode->i_block[14] ) {
315 memset(blocks, 0, Disk->BlockSize);
318 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
320 // Allocate / Get Indirect lvl2 Block
321 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
323 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
328 blocks[nBlocks/dwPerBlock] = id1;
329 // Write back indirect 1 block
330 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
331 memset(blocks, 0, Disk->BlockSize);
334 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
335 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
338 // Allocate / Get Indirect Level 3 Block
339 if( nBlocks % dwPerBlock == 0 ) {
340 id2 = Ext2_int_AllocateBlock(Disk, id1);
345 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
346 // Write back indirect 1 block
347 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
348 memset(blocks, 0, Disk->BlockSize);
351 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
352 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
355 blocks[nBlocks % dwPerBlock] = Block;
357 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
362 Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");