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

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