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);
20 * \brief Write to a file
22 size_t Ext2_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
24 tExt2_Disk *disk = Node->ImplPtr;
25 tExt2_Inode *inode = (void*)(Node+1);
32 //Debug_HexDump("Ext2_Write", Buffer, Length);
34 // TODO: Handle (Flags & VFS_IOFLAG_NOBLOCK)
36 // Get the ammount of space already allocated
37 // - Round size up to block size
38 // - block size is a power of two, so this will work
39 allocSize = (inode->i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
40 LOG("allocSize = %llx, Offset=%llx", allocSize, Offset);
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 // Allocate blocks and copy data to them
92 retLen = Length - (allocSize-Offset);
95 size_t blk_len = (retLen < disk->BlockSize ? retLen : disk->BlockSize);
97 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
98 if(!block) return Length - retLen;
99 // Add it to this inode
100 if( Ext2_int_AppendBlock(Node, inode, block) ) {
101 Log_Warning("Ext2", "Appending %x to inode %p:%X failed",
102 block, disk, Node->Inode);
103 Ext2_int_DeallocateBlock(disk, block);
106 // Copy data to the node
107 base = block * disk->BlockSize;
108 VFS_WriteAt(disk->FD, base, blk_len, Buffer);
109 // Update pointer and size remaining
116 retLen = Length - retLen;
119 // TODO: When should the size update be committed?
120 inode->i_size += retLen;
121 Node->Size += retLen;
122 Node->Flags |= VFS_FFLAG_DIRTY;
123 //Ext2_int_WriteInode(disk, Node->Inode, inode);
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 firstgroup = PrevBlock / bpg;
138 Uint blockgroup = firstgroup;
141 // TODO: Need to do locking on the bitmaps
143 // Are there any free blocks?
144 if(Disk->SuperBlock.s_free_blocks_count == 0)
147 // First: Check the next block after `PrevBlock`
148 int iblock = (PrevBlock + 1) % Disk->SuperBlock.s_blocks_per_group;
149 //LOG("iblock = %i, Disk=%p, blockgroup=%i", iblock, Disk, blockgroup);
150 if( iblock != 0 && Disk->Groups[blockgroup].bg_free_blocks_count > 0 )
152 //LOG("Checking %i:%i", blockgroup, iblock);
154 bg = &Disk->Groups[blockgroup];
156 const int sector_size = 512;
157 Uint8 buf[sector_size];
158 int byte = (iblock/8) % sector_size;
159 Uint8 bit = 1 << (iblock % 8);
160 int ofs = (iblock/8) / sector_size * sector_size;
162 Uint64 vol_ofs = Disk->BlockSize*bg->bg_block_bitmap+ofs;
163 VFS_ReadAt(Disk->FD, vol_ofs, sector_size, buf);
165 //LOG("buf@%llx[%i] = %02x (& %02x)", vol_ofs, byte, buf[byte], bit);
167 if( (buf[byte] & bit) == 0 )
169 // Free block - nice and contig allocation
171 VFS_WriteAt(Disk->FD, vol_ofs, sector_size, buf);
173 bg->bg_free_blocks_count --;
174 Disk->SuperBlock.s_free_blocks_count --;
175 #if EXT2_UPDATE_WRITEBACK
176 Ext2_int_UpdateSuperblock(Disk);
178 return PrevBlock + 1;
181 // Fall through and search further
184 // Second: Search for a group with free blocks
185 while( blockgroup < Disk->GroupCount && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
187 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
191 while( blockgroup < firstgroup && Disk->Groups[blockgroup].bg_free_blocks_count == 0 )
194 if( Disk->Groups[blockgroup].bg_free_blocks_count == 0 ) {
195 Log_Notice("Ext2", "Ext2_int_AllocateBlock - Out of blockss on %p, but superblock says some free",
199 //LOG("BG%i has free blocks", blockgroup);
201 // Search the bitmap for a free block
202 bg = &Disk->Groups[blockgroup];
205 const int sector_size = 512;
206 Uint8 buf[sector_size];
207 Uint64 vol_ofs = Disk->BlockSize*bg->bg_block_bitmap+ofs;
208 VFS_ReadAt(Disk->FD, vol_ofs, sector_size, buf);
211 for( byte = 0; byte < sector_size && buf[byte] == 0xFF; byte ++ )
213 if( byte < sector_size )
215 //LOG("buf@%llx[%i] = %02x", vol_ofs, byte, buf[byte]);
216 for( bit = 0; bit < 8 && buf[byte] & (1 << bit); bit ++)
219 buf[byte] |= 1 << bit;
220 VFS_WriteAt(Disk->FD, vol_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("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(tVFS_Node *Node, tExt2_Inode *Inode, Uint32 Block)
253 tExt2_Disk *Disk = Node->ImplPtr;
255 int dwPerBlock = Disk->BlockSize / 4;
259 nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
261 LOG("Append 0x%x to inode [%i]", Block, nBlocks);
265 Inode->i_block[nBlocks] = Block;
269 blocks = malloc( Disk->BlockSize );
270 if(!blocks) return 1;
274 if( nBlocks < dwPerBlock)
276 LOG("Indirect 1 %i", nBlocks);
277 // Allocate/Get Indirect block
279 Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
280 if( !Inode->i_block[12] ) {
281 Log_Warning("Ext2", "Allocating indirect block failed");
285 memset(blocks, 0, Disk->BlockSize);
288 VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
290 blocks[nBlocks] = Block;
292 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
293 Node->Flags |= VFS_FFLAG_DIRTY;
298 nBlocks -= dwPerBlock;
300 if( nBlocks < dwPerBlock*dwPerBlock )
302 LOG("Indirect 2 %i/%i", nBlocks/dwPerBlock, nBlocks%dwPerBlock);
303 // Allocate/Get Indirect block
305 Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
306 if( !Inode->i_block[13] ) {
307 Log_Warning("Ext2", "Allocating double indirect block failed");
311 memset(blocks, 0, Disk->BlockSize);
312 Node->Flags |= VFS_FFLAG_DIRTY;
315 VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
317 // Allocate / Get Indirect lvl2 Block
318 if( nBlocks % dwPerBlock == 0 ) {
319 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
322 Log_Warning("Ext2", "Allocating double indirect block (l2) failed");
325 blocks[nBlocks/dwPerBlock] = id1;
326 // Write back indirect 1 block
327 VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
328 memset(blocks, 0, Disk->BlockSize);
331 id1 = blocks[nBlocks / dwPerBlock];
332 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
335 blocks[nBlocks % dwPerBlock] = Block;
337 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
342 nBlocks -= dwPerBlock*dwPerBlock;
344 if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
346 // Allocate/Get Indirect block
348 Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
349 if( !Inode->i_block[14] ) {
350 Log_Warning("Ext2", "Allocating triple indirect block failed");
354 memset(blocks, 0, Disk->BlockSize);
355 Node->Flags |= VFS_FFLAG_DIRTY;
358 VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
360 // Allocate / Get Indirect lvl2 Block
361 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
363 id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
365 Log_Warning("Ext2", "Allocating triple indirect block (l2) failed");
369 blocks[nBlocks/dwPerBlock] = id1;
370 // Write back indirect 1 block
371 VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
372 memset(blocks, 0, Disk->BlockSize);
375 id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
376 VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
379 // Allocate / Get Indirect Level 3 Block
380 if( nBlocks % dwPerBlock == 0 ) {
381 id2 = Ext2_int_AllocateBlock(Disk, id1);
383 Log_Warning("Ext2", "Allocating triple indirect block (l3) failed");
387 blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
388 // Write back indirect 1 block
389 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
390 memset(blocks, 0, Disk->BlockSize);
393 id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
394 VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
397 blocks[nBlocks % dwPerBlock] = Block;
399 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
404 Log_Warning("Ext2", "Inode ?? cannot have a block appended to it, all indirects used");