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 ) 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 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 Ext2_int_DeallocateBlock(disk, block);
122 base = block * disk->BlockSize;
123 VFS_WriteAt(disk->FD, base, retLen, Buffer);
124 inode.i_size += retLen;
127 ret: // Makes sure the changes to the inode are committed
128 Ext2_int_WriteInode(disk, Node->Inode, &inode);
129 return Length - retLen;
133 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
134 * \brief Allocate a block from the best possible location
135 * \param Disk EXT2 Disk Information Structure
136 * \param PrevBlock Previous block ID in the file
138 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
140 int bpg = Disk->SuperBlock.s_blocks_per_group;
141 Uint blockgroup = PrevBlock / bpg;
142 Uint bitmap[Disk->BlockSize/sizeof(Uint)];
143 Uint bitsperblock = 8*Disk->BlockSize;
147 // Are there any free blocks?
148 if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
150 if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
152 // Search block group's bitmap
153 for(i = 0; i < bpg; i++)
155 // Get the block in the bitmap block
156 j = i & (bitsperblock-1);
162 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
169 if( bitmap[j/32] == 0xFFFFFFFF ) {
175 if( bitmap[j/32] & (1 << (j%32)) )
182 Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
183 goto checkAll; // Search the entire filesystem for a free block
184 // Goto needed for neatness
188 bitmap[j/32] |= (1 << (j%32));
191 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
196 Disk->Groups[blockgroup].bg_free_blocks_count --;
197 #if EXT2_UPDATE_WRITEBACK
198 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
204 Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
208 // Reduce global count
209 Disk->SuperBlock.s_free_blocks_count --;
210 #if EXT2_UPDATE_WRITEBACK
211 Ext2_int_UpdateSuperblock(Disk);
218 * \brief Deallocates a block
220 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
225 * \brief Append a block to an inode
227 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
230 int dwPerBlock = Disk->BlockSize / 4;
234 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
238 Inode->i_block[nBlocks] = Block;
242 blocks = malloc( Disk->BlockSize );
243 if(!blocks) return 1;
247 if( nBlocks < dwPerBlock)
249 // Allocate/Get Indirect block
251 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
252 if( !Inode->i_block[12] ) {
256 memset(blocks, 0, Disk->BlockSize);
259 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
261 blocks[nBlocks] = Block;
263 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
268 nBlocks += dwPerBlock;
270 if( nBlocks < dwPerBlock*dwPerBlock )
272 // Allocate/Get Indirect block
274 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
275 if( !Inode->i_block[13] ) {
279 memset(blocks, 0, Disk->BlockSize);
282 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
284 // Allocate / Get Indirect lvl2 Block
285 if( nBlocks % dwPerBlock == 0 ) {
286 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
291 blocks[nBlocks/dwPerBlock] = id1;
292 // Write back indirect 1 block
293 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
294 memset(blocks, 0, Disk->BlockSize);
297 id1 = blocks[nBlocks / dwPerBlock];
298 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
301 blocks[nBlocks % dwPerBlock] = Block;
303 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
308 nBlocks -= dwPerBlock*dwPerBlock;
310 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
312 // Allocate/Get Indirect block
314 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
315 if( !Inode->i_block[14] ) {
319 memset(blocks, 0, Disk->BlockSize);
322 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
324 // Allocate / Get Indirect lvl2 Block
325 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
327 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
332 blocks[nBlocks/dwPerBlock] = id1;
333 // Write back indirect 1 block
334 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
335 memset(blocks, 0, Disk->BlockSize);
338 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
339 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
342 // Allocate / Get Indirect Level 3 Block
343 if( nBlocks % dwPerBlock == 0 ) {
344 id2 = Ext2_int_AllocateBlock(Disk, id1);
349 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
350 // Write back indirect 1 block
351 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
352 memset(blocks, 0, Disk->BlockSize);
355 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
356 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
359 blocks[nBlocks % dwPerBlock] = Block;
361 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
366 Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");