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 Ext2_int_DeallocateBlock(disk, block);
105 // Copy data to the node
106 base = block * disk->BlockSize;
107 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
108 // Update pointer and size remaining
109 inode.i_size += disk->BlockSize;
110 Buffer += disk->BlockSize;
111 retLen -= disk->BlockSize;
114 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
116 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
117 Ext2_int_DeallocateBlock(disk, block);
120 base = block * disk->BlockSize;
121 VFS_WriteAt(disk->FD, base, retLen, Buffer);
123 // TODO: When should the size update be committed?
124 inode.i_size += retLen;
125 Node->Size += retLen;
126 Node->Flags |= VFS_FFLAG_DIRTY;
130 ret: // Makes sure the changes to the inode are committed
131 Ext2_int_WriteInode(disk, Node->Inode, &inode);
132 return Length - retLen;
136 * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
137 * \brief Allocate a block from the best possible location
138 * \param Disk EXT2 Disk Information Structure
139 * \param PrevBlock Previous block ID in the file
141 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
143 int bpg = Disk->SuperBlock.s_blocks_per_group;
144 Uint blockgroup = PrevBlock / bpg;
145 Uint bitmap[Disk->BlockSize/sizeof(Uint)];
146 Uint bitsperblock = 8*Disk->BlockSize;
150 // Are there any free blocks?
151 if(Disk->SuperBlock.s_free_blocks_count == 0) return 0;
153 if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
155 // Search block group's bitmap
156 for(i = 0; i < bpg; i++)
158 // Get the block in the bitmap block
159 j = i & (bitsperblock-1);
165 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
172 if( bitmap[j/32] == 0xFFFFFFFF ) {
178 if( bitmap[j/32] & (1 << (j%32)) )
185 Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
186 goto checkAll; // Search the entire filesystem for a free block
187 // Goto needed for neatness
191 bitmap[j/32] |= (1 << (j%32));
194 (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
199 Disk->Groups[blockgroup].bg_free_blocks_count --;
200 #if EXT2_UPDATE_WRITEBACK
201 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
207 Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
211 // Reduce global count
212 Disk->SuperBlock.s_free_blocks_count --;
213 #if EXT2_UPDATE_WRITEBACK
214 Ext2_int_UpdateSuperblock(Disk);
221 * \brief Deallocates a block
223 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
228 * \brief Append a block to an inode
230 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
233 int dwPerBlock = Disk->BlockSize / 4;
237 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
241 Inode->i_block[nBlocks] = Block;
245 blocks = malloc( Disk->BlockSize );
246 if(!blocks) return 1;
250 if( nBlocks < dwPerBlock)
252 // Allocate/Get Indirect block
254 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
255 if( !Inode->i_block[12] ) {
259 memset(blocks, 0, Disk->BlockSize);
262 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
264 blocks[nBlocks] = Block;
266 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
271 nBlocks += dwPerBlock;
273 if( nBlocks < dwPerBlock*dwPerBlock )
275 // Allocate/Get Indirect block
277 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
278 if( !Inode->i_block[13] ) {
282 memset(blocks, 0, Disk->BlockSize);
285 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
287 // Allocate / Get Indirect lvl2 Block
288 if( nBlocks % dwPerBlock == 0 ) {
289 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
294 blocks[nBlocks/dwPerBlock] = id1;
295 // Write back indirect 1 block
296 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
297 memset(blocks, 0, Disk->BlockSize);
300 id1 = blocks[nBlocks / dwPerBlock];
301 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
304 blocks[nBlocks % dwPerBlock] = Block;
306 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
311 nBlocks -= dwPerBlock*dwPerBlock;
313 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
315 // Allocate/Get Indirect block
317 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
318 if( !Inode->i_block[14] ) {
322 memset(blocks, 0, Disk->BlockSize);
325 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
327 // Allocate / Get Indirect lvl2 Block
328 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
330 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
335 blocks[nBlocks/dwPerBlock] = id1;
336 // Write back indirect 1 block
337 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
338 memset(blocks, 0, Disk->BlockSize);
341 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
342 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
345 // Allocate / Get Indirect Level 3 Block
346 if( nBlocks % dwPerBlock == 0 ) {
347 id2 = Ext2_int_AllocateBlock(Disk, id1);
352 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
353 // Write back indirect 1 block
354 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
355 memset(blocks, 0, Disk->BlockSize);
358 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
359 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
362 blocks[nBlocks % dwPerBlock] = Block;
364 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
369 Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");