Kernel - Slight reworks to timer code
[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 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);
18
19 // === CODE ===
20 /**
21  * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
22  * \brief Write to a file
23  */
24 Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
25 {
26         tExt2_Disk      *disk = Node->ImplPtr;
27         tExt2_Inode     inode;
28         Uint64  base;
29         Uint64  retLen;
30         Uint    block;
31         Uint64  allocSize;
32          int    bNewBlocks = 0;
33         
34         Debug_HexDump("Ext2_Write", Buffer, Length);
35         
36         Ext2_int_ReadInode(disk, Node->Inode, &inode);
37         
38         // Get the ammount of space already allocated
39         // - Round size up to block size
40         // - block size is a power of two, so this will work
41         allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1);
42         
43         // Are we writing to inside the allocated space?
44         if( Offset > allocSize )        return 0;
45         
46         if( Offset < allocSize )
47         {
48                 // Will we go out of it?
49                 if(Offset + Length > allocSize) {
50                         bNewBlocks = 1;
51                         retLen = allocSize - Offset;
52                 } else
53                         retLen = Length;
54                 
55                 // Within the allocated space
56                 block = Offset / disk->BlockSize;
57                 Offset %= disk->BlockSize;
58                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
59                 
60                 // Write only block (if only one)
61                 if(Offset + retLen <= disk->BlockSize) {
62                         VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer);
63                         if(!bNewBlocks) return Length;
64                         goto addBlocks; // Ugh! A goto, but it seems unavoidable
65                 }
66                 
67                 // Write First Block
68                 VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer);
69                 Buffer += disk->BlockSize-Offset;
70                 retLen -= disk->BlockSize-Offset;
71                 block ++;
72                 
73                 // Write middle blocks
74                 while(retLen > disk->BlockSize)
75                 {
76                         base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
77                         VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
78                         Buffer += disk->BlockSize;
79                         retLen -= disk->BlockSize;
80                         block ++;
81                 }
82                 
83                 // Write last block
84                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, block);
85                 VFS_WriteAt(disk->FD, base, retLen, Buffer);
86                 if(!bNewBlocks) return Length;  // Writing in only allocated space
87         }
88         else
89                 base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1);
90         
91 addBlocks:
92         Log_Notice("EXT2", "File extending is untested");
93         
94         // Allocate blocks and copy data to them
95         retLen = Length - (allocSize-Offset);
96         while( retLen > disk->BlockSize )
97         {
98                 // Allocate a block
99                 block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
100                 if(!block)      return Length - retLen;
101                 // Add it to this inode
102                 if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
103                         Ext2_int_DeallocateBlock(disk, block);
104                         goto ret;
105                 }
106                 // Copy data to the node
107                 base = block * disk->BlockSize;
108                 VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer);
109                 // Update pointer and size remaining
110                 inode.i_size += disk->BlockSize;
111                 Buffer += disk->BlockSize;
112                 retLen -= disk->BlockSize;
113         }
114         // Last block :D
115         block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize);
116         if(!block)      goto ret;
117         if( !Ext2_int_AppendBlock(disk, &inode, block) ) {
118                 Ext2_int_DeallocateBlock(disk, block);
119                 goto ret;
120         }
121         base = block * disk->BlockSize;
122         VFS_WriteAt(disk->FD, base, retLen, Buffer);
123         inode.i_size += retLen;
124         retLen = 0;
125
126 ret:    // Makes sure the changes to the inode are committed
127         Ext2_int_WriteInode(disk, Node->Inode, &inode);
128         return Length - retLen;
129 }
130
131 /**
132  * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
133  * \brief Allocate a block from the best possible location
134  * \param Disk  EXT2 Disk Information Structure
135  * \param PrevBlock     Previous block ID in the file
136  */
137 Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock)
138 {
139          int    bpg = Disk->SuperBlock.s_blocks_per_group;
140         Uint    blockgroup = PrevBlock / bpg;
141         Uint    bitmap[Disk->BlockSize/sizeof(Uint)];
142         Uint    bitsperblock = 8*Disk->BlockSize;
143          int    i, j = 0;
144         Uint    block;
145         
146         // Are there any free blocks?
147         if(Disk->SuperBlock.s_free_blocks_count == 0)   return 0;
148         
149         if(Disk->Groups[blockgroup].bg_free_blocks_count > 0)
150         {
151                 // Search block group's bitmap
152                 for(i = 0; i < bpg; i++)
153                 {
154                         // Get the block in the bitmap block
155                         j = i & (bitsperblock-1);
156                         
157                         // Read in if needed
158                         if(j == 0) {
159                                 VFS_ReadAt(
160                                         Disk->FD,
161                                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
162                                         Disk->BlockSize,
163                                         bitmap
164                                         );
165                         }
166                         
167                         // Fast Check
168                         if( bitmap[j/32] == 0xFFFFFFFF ) {
169                                 j = (j + 31) & ~31;
170                                 continue;
171                         }
172                         
173                         // Is the bit set?
174                         if( bitmap[j/32] & (1 << (j%32)) )
175                                 continue;
176                         
177                         // Ooh! We found one
178                         break;
179                 }
180                 if( i < bpg ) {
181                         Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist");
182                         goto    checkAll;       // Search the entire filesystem for a free block
183                         // Goto needed for neatness
184                 }
185                 
186                 // Mark as used
187                 bitmap[j/32] |= (1 << (j%32));
188                 VFS_WriteAt(
189                         Disk->FD,
190                         (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock,
191                         Disk->BlockSize,
192                         bitmap
193                         );
194                 block = i;
195                 Disk->Groups[blockgroup].bg_free_blocks_count --;
196                 #if EXT2_UPDATE_WRITEBACK
197                 //Ext2_int_UpdateBlockGroup(Disk, blockgroup);
198                 #endif
199         }
200         else
201         {
202         checkAll:
203                 Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group");
204                 return 0;
205         }
206         
207         // Reduce global count
208         Disk->SuperBlock.s_free_blocks_count --;
209         #if EXT2_UPDATE_WRITEBACK
210         Ext2_int_UpdateSuperblock(Disk);
211         #endif
212         
213         return block;
214 }
215
216 /**
217  * \brief Deallocates a block
218  */
219 void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block)
220 {
221 }
222
223 /**
224  * \brief Append a block to an inode
225  */
226 int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block)
227 {
228          int    nBlocks;
229          int    dwPerBlock = Disk->BlockSize / 4;
230         Uint32  *blocks;
231         Uint32  id1, id2;
232         
233         nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize;
234         
235         // Direct Blocks
236         if( nBlocks < 12 ) {
237                 Inode->i_block[nBlocks] = Block;
238                 return 0;
239         }
240         
241         blocks = malloc( Disk->BlockSize );
242         if(!blocks)     return 1;
243         
244         nBlocks -= 12;
245         // Single Indirect
246         if( nBlocks < dwPerBlock)
247         {
248                 // Allocate/Get Indirect block
249                 if( nBlocks == 0 ) {
250                         Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
251                         if( !Inode->i_block[12] ) {
252                                 free(blocks);
253                                 return 1;
254                         }
255                         memset(blocks, 0, Disk->BlockSize); 
256                 }
257                 else
258                         VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
259                 
260                 blocks[nBlocks] = Block;
261                 
262                 VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks);
263                 free(blocks);
264                 return 0;
265         }
266         
267         nBlocks += dwPerBlock;
268         // Double Indirect
269         if( nBlocks < dwPerBlock*dwPerBlock )
270         {
271                 // Allocate/Get Indirect block
272                 if( nBlocks == 0 ) {
273                         Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
274                         if( !Inode->i_block[13] ) {
275                                 free(blocks);
276                                 return 1;
277                         }
278                         memset(blocks, 0, Disk->BlockSize);
279                 }
280                 else
281                         VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
282                 
283                 // Allocate / Get Indirect lvl2 Block
284                 if( nBlocks % dwPerBlock == 0 ) {
285                         id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
286                         if( !id1 ) {
287                                 free(blocks);
288                                 return 1;
289                         }
290                         blocks[nBlocks/dwPerBlock] = id1;
291                         // Write back indirect 1 block
292                         VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks);
293                         memset(blocks, 0, Disk->BlockSize);
294                 }
295                 else {
296                         id1 = blocks[nBlocks / dwPerBlock];
297                         VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
298                 }
299                 
300                 blocks[nBlocks % dwPerBlock] = Block;
301                 
302                 VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
303                 free(blocks);
304                 return 0;
305         }
306         
307         nBlocks -= dwPerBlock*dwPerBlock;
308         // Triple Indirect
309         if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock )
310         {
311                 // Allocate/Get Indirect block
312                 if( nBlocks == 0 ) {
313                         Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
314                         if( !Inode->i_block[14] ) {
315                                 free(blocks);
316                                 return 1;
317                         }
318                         memset(blocks, 0, Disk->BlockSize);
319                 }
320                 else
321                         VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
322                 
323                 // Allocate / Get Indirect lvl2 Block
324                 if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 )
325                 {
326                         id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]);
327                         if( !id1 ) {
328                                 free(blocks);
329                                 return 1;
330                         }
331                         blocks[nBlocks/dwPerBlock] = id1;
332                         // Write back indirect 1 block
333                         VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks);
334                         memset(blocks, 0, Disk->BlockSize);
335                 }
336                 else {
337                         id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)];
338                         VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
339                 }
340                 
341                 // Allocate / Get Indirect Level 3 Block
342                 if( nBlocks % dwPerBlock == 0 ) {
343                         id2 = Ext2_int_AllocateBlock(Disk, id1);
344                         if( !id2 ) {
345                                 free(blocks);
346                                 return 1;
347                         }
348                         blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2;
349                         // Write back indirect 1 block
350                         VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks);
351                         memset(blocks, 0, Disk->BlockSize);
352                 }
353                 else {
354                         id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock];
355                         VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
356                 }
357                 
358                 blocks[nBlocks % dwPerBlock] = Block;
359                 
360                 VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks);
361                 free(blocks);
362                 return 0;
363         }
364         
365         Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used");
366         free(blocks);
367         return 1;
368 }

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