Various changes, most of them involving the FAT and Ext2 Drivers, adding write support
[tpg/acess2.git] / Modules / Filesystems / Ext2 / write.c
1 /*
2  * Acess OS
3  * Ext2 Driver Version 1
4  */
5 /**
6  * \file write.c
7  * \brief Second Extended Filesystem Driver
8  * \todo Implement file full write support
9  */
10 #define DEBUG   1
11 #define VERBOSE 0
12 #include "ext2_common.h"
13
14 // === PROTOYPES ===
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);
19
20 // === CODE ===
21 /**
22  * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
23  * \brief Write to a file
24  */
25 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
26 {
27         tExt2_Disk      *disk = Node->ImplPtr;
28         tExt2_Inode     inode;
29         Uint64  base;
30         Uint64  retLen;
31         Uint    block;
32         Uint64  allocSize;
33          int    bNewBlocks = 0;
34         
35         Debug_HexDump("Ext2_Write", Buffer, Length);
36         
37         Ext2_int_ReadInode(disk, Node->Inode, &inode);
38         
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);
43         
44         // Are we writing to inside the allocated space?
45         if( Offset > allocSize )        return 0;
46         
47         if( Offset < allocSize )
48         {
49                 // Will we go out of it?
50                 if(Offset + Length > allocSize) {
51                         bNewBlocks = 1;
52                         retLen = allocSize - Offset;
53                 } else
54                         retLen = Length;
55                 
56                 // Within the allocated space
57                 block = Offset / disk->BlockSize;
58                 Offset %= disk->BlockSize;
59                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
60                 
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
66                 }
67                 
68                 // Write First Block
69                 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
70                 Buffer += disk->BlockSize-Offset;
71                 retLen -= disk->BlockSize-Offset;
72                 block ++;
73                 
74                 // Write middle blocks
75                 while(retLen > disk->BlockSize)
76                 {
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;
81                         block ++;
82                 }
83                 
84                 // Write last block
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
88         }
89         else
90                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
91         
92 addBlocks:
93         Warning("[EXT2 ] File extending is untested");
94         
95         // Allocate blocks and copy data to them
96         retLen = Length - (allocSize-Offset);
97         while( retLen > disk->BlockSize )
98         {
99                 // Allocate a block
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);
105                         goto ret;
106                 }
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;
114         }
115         // Last block :D
116         block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
117         if(!block)      goto ret;
118         if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
119                 Ext2_int_DeallocateBlock(disk, block);
120                 goto ret;
121         }
122         base = block * disk->BlockSize;
123         VFS_WriteAt(disk->FD, base, retLen, Buffer);
124         inode.i_size += retLen;
125         retLen = 0;
126
127 ret:    // Makes sure the changes to the inode are committed
128         Ext2_int_WriteInode(disk, Node->Inode, &inode);
129         return Length - retLen;
130 }
131
132 /**
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
137  */
138 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
139 {
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;
144          int    i, j = 0;
145         Uint    block;
146         
147         // Are there any free blocks?
148         if(Disk->SuperBlock.s_free_blocks_count == 0)   return 0;
149         
150         if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
151         {
152                 // Search block group's bitmap
153                 for(i = 0; i < bpg; i++)
154                 {
155                         // Get the block in the bitmap block
156                         j = i & (bitsperblock-1);
157                         
158                         // Read in if needed
159                         if(j == 0) {
160                                 VFS_ReadAt(
161                                         Disk->FD,
162                                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
163                                         Disk->BlockSize,
164                                         bitmap
165                                         );
166                         }
167                         
168                         // Fast Check
169                         if( bitmap[j/32] == -1 ) {
170                                 j = (j + 31) & ~31;
171                                 continue;
172                         }
173                         
174                         // Is the bit set?
175                         if( bitmap[j/32] & (1 << (j%32)) )
176                                 continue;
177                         
178                         // Ooh! We found one
179                         break;
180                 }
181                 if( i < bpg ) {
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
185                 }
186                 
187                 // Mark as used
188                 bitmap[j/32] |= (1 << (j%32));
189                 VFS_WriteAt(
190                         Disk->FD,
191                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
192                         Disk->BlockSize,
193                         bitmap
194                         );
195                 block = i;
196                 Disk->Groups[blockgroup].bg_free_blocks_count --;
197                 #if EXT2_UPDATE_WRITEBACK
198                 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
199                 #endif
200         }
201         else
202         {
203         checkAll:
204                 Warning("[EXT2 ] TODO - Implement using blocks outside the current block group");
205                 return 0;
206         }
207         
208         // Reduce global count
209         Disk->SuperBlock.s_free_blocks_count --;
210         #if EXT2_UPDATE_WRITEBACK
211         Ext2_int_UpdateSuperblock(Disk);
212         #endif
213         
214         return block;
215 }
216
217 /**
218  * \brief Deallocates a block
219  */
220 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
221 {
222 }
223
224 /**
225  * \brief Append a block to an inode
226  */
227 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
228 {
229          int    nBlocks;
230          int    dwPerBlock = Disk->BlockSize / 4;
231         Uint32  *blocks;
232         Uint32  id1, id2;
233         
234         nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
235         
236         // Direct Blocks
237         if( nBlocks < 12 ) {
238                 Inode->i_block[nBlocks] = Block;
239                 return 0;
240         }
241         
242         blocks = malloc( Disk->BlockSize );
243         if(!blocks)     return 1;
244         
245         nBlocks -= 12;
246         // Single Indirect
247         if( nBlocks < dwPerBlock)
248         {
249                 // Allocate/Get Indirect block
250                 if( nBlocks == 0 ) {
251                         Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
252                         if( !Inode->i_block[12] ) {
253                                 free(blocks);
254                                 return 1;
255                         }
256                         memset(blocks, 0, Disk->BlockSize); 
257                 }
258                 else
259                         VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
260                 
261                 blocks[nBlocks] = Block;
262                 
263                 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
264                 free(blocks);
265                 return 0;
266         }
267         
268         nBlocks += dwPerBlock;
269         // Double Indirect
270         if( nBlocks < dwPerBlock*dwPerBlock )
271         {
272                 // Allocate/Get Indirect block
273                 if( nBlocks == 0 ) {
274                         Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
275                         if( !Inode->i_block[13] ) {
276                                 free(blocks);
277                                 return 1;
278                         }
279                         memset(blocks, 0, Disk->BlockSize);
280                 }
281                 else
282                         VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
283                 
284                 // Allocate / Get Indirect lvl2 Block
285                 if( nBlocks % dwPerBlock == 0 ) {
286                         id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
287                         if( !id1 ) {
288                                 free(blocks);
289                                 return 1;
290                         }
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);
295                 }
296                 else {
297                         id1 = blocks[nBlocks / dwPerBlock];
298                         VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
299                 }
300                 
301                 blocks[nBlocks % dwPerBlock] = Block;
302                 
303                 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
304                 free(blocks);
305                 return 0;
306         }
307         
308         nBlocks -= dwPerBlock*dwPerBlock;
309         // Triple Indirect
310         if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
311         {
312                 // Allocate/Get Indirect block
313                 if( nBlocks == 0 ) {
314                         Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
315                         if( !Inode->i_block[14] ) {
316                                 free(blocks);
317                                 return 1;
318                         }
319                         memset(blocks, 0, Disk->BlockSize);
320                 }
321                 else
322                         VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
323                 
324                 // Allocate / Get Indirect lvl2 Block
325                 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
326                 {
327                         id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
328                         if( !id1 ) {
329                                 free(blocks);
330                                 return 1;
331                         }
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);
336                 }
337                 else {
338                         id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
339                         VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
340                 }
341                 
342                 // Allocate / Get Indirect Level 3 Block
343                 if( nBlocks % dwPerBlock == 0 ) {
344                         id2 = Ext2_int_AllocateBlock(Disk, id1);
345                         if( !id2 ) {
346                                 free(blocks);
347                                 return 1;
348                         }
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);
353                 }
354                 else {
355                         id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
356                         VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
357                 }
358                 
359                 blocks[nBlocks % dwPerBlock] = Block;
360                 
361                 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
362                 free(blocks);
363                 return 0;
364         }
365         
366         Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");
367         free(blocks);
368         return 1;
369 }

UCC git Repository :: git.ucc.asn.au