Bring back ext2 code from branch
[reactos.git] / reactos / lib / fslib / ext2lib / Inode.c
1 /*
2 * PROJECT: Mke2fs
3 * FILE: Inode.c
4 * PROGRAMMER: Matt Wu <mattwu@163.com>
5 * HOMEPAGE: http://ext2.yeah.net
6 */
7
8 /* INCLUDES **************************************************************/
9
10 #include "Mke2fs.h"
11
12 /* DEFINITIONS ***********************************************************/
13
14 extern char *device_name;
15
16 /* FUNCTIONS *************************************************************/
17
18
19 bool ext2_get_inode_lba(PEXT2_FILESYS Ext2Sys, ULONG no, LONGLONG *offset)
20 {
21 LONGLONG loc = 0;
22 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
23
24 if (no < 1 || no > pExt2Sb->s_inodes_count)
25 {
26 KdPrint(("Mke2fs: Inode value %lu was out of range in load_inode.(1-%ld)\n",
27 no, pExt2Sb->s_inodes_count));
28 *offset = 0;
29 return false;
30 }
31
32 loc = (LONGLONG)(Ext2Sys->blocksize) * Ext2Sys->group_desc[(no - 1) / pExt2Sb->s_inodes_per_group].bg_inode_table +
33 ((no - 1) % pExt2Sb->s_inodes_per_group) * sizeof(EXT2_INODE);
34
35 *offset = loc;
36
37 return true;
38 }
39
40 bool ext2_load_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
41 {
42 LONGLONG Offset;
43 bool bRet = false;
44
45 if (ext2_get_inode_lba(Ext2Sys, no, &Offset))
46 {
47 bRet = NT_SUCCESS(Ext2ReadDisk(
48 Ext2Sys,
49 Offset,
50 sizeof(EXT2_INODE),
51 (unsigned char *)pInode));
52 }
53
54 return bRet;
55 }
56
57
58 bool ext2_save_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
59 {
60 LONGLONG offset;
61 bool bRet = false;
62
63 if (ext2_get_inode_lba(Ext2Sys, no, &offset))
64 {
65 bRet = NT_SUCCESS(Ext2WriteDisk(
66 Ext2Sys,
67 offset,
68 sizeof(EXT2_INODE),
69 (unsigned char *)pInode));
70 }
71
72 return bRet;
73 }
74
75
76 /*
77 * Right now, just search forward from the parent directory's block
78 * group to find the next free inode.
79 *
80 * Should have a special policy for directories.
81 */
82
83 bool ext2_new_inode(PEXT2_FILESYS fs, ULONG dir, int mode,
84 PEXT2_INODE_BITMAP map, ULONG *ret)
85 {
86 ULONG dir_group = 0;
87 ULONG i;
88 ULONG start_inode;
89
90 if (!map)
91 map = fs->inode_map;
92
93 if (!map)
94 return false;
95
96 if (dir > 0)
97 dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->ext2_sb);
98
99 start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->ext2_sb)) + 1;
100
101 if (start_inode < EXT2_FIRST_INODE(fs->ext2_sb))
102 start_inode = EXT2_FIRST_INODE(fs->ext2_sb);
103
104 i = start_inode;
105
106 do
107 {
108 if (!ext2_test_inode_bitmap(map, i))
109 break;
110
111 i++;
112
113 if (i > fs->ext2_sb->s_inodes_count)
114 i = EXT2_FIRST_INODE(fs->ext2_sb);
115
116 } while (i != start_inode);
117
118 if (ext2_test_inode_bitmap(map, i))
119 return false;
120
121 *ret = i;
122
123 return true;
124 }
125
126
127 bool ext2_expand_block( PEXT2_FILESYS Ext2Sys, PEXT2_INODE Inode,
128 ULONG dwContent, ULONG Index, int layer,
129 ULONG newBlk, ULONG *dwRet, ULONG *off )
130 {
131 ULONG *pData = NULL;
132 ULONG i = 0, j = 0, temp = 1;
133 ULONG dwBlk;
134 ULONG dwNewBlk = newBlk;
135 bool bDirty = false;
136 bool bRet = true;
137 ULONG Offset = 0;
138
139 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
140
141 pData = (ULONG *)RtlAllocateHeap(GetProcessHeap(), 0, Ext2Sys->blocksize);
142
143 if (!pData)
144 {
145 bRet = false;
146 goto errorout;
147 }
148
149 if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
150 {
151 bRet = false;
152 goto errorout;
153 }
154
155 if (layer == 1)
156 {
157 *dwRet = dwContent;
158 *off = Index;
159 pData[Index] = newBlk;
160
161 bDirty = TRUE;
162 }
163 else if (layer <= 3)
164 {
165 temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
166
167 i = Index / temp;
168 j = Index % temp;
169
170 dwBlk = pData[i];
171
172 if (dwBlk == 0)
173 {
174 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
175 {
176 pData[i] = dwBlk;
177 bDirty = true;
178
179 Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
180 }
181
182 if (!bDirty)
183 goto errorout;
184 }
185
186 if (!ext2_expand_block(Ext2Sys, Inode, dwBlk, j, layer - 1, bDirty, &dwNewBlk, &Offset))
187 {
188 bRet = false;
189 KdPrint(("Mke2fs: ext2_expand_block: ... error recuise...\n"));
190 goto errorout;
191 }
192 }
193
194 if (bDirty)
195 {
196 bRet = ext2_write_block(Ext2Sys, dwContent, (void *)pData);
197 }
198
199
200 errorout:
201
202 if (pData)
203 RtlFreeHeap(GetProcessHeap(), 0, pData);
204
205 if (bRet && dwRet)
206 *dwRet = dwNewBlk;
207
208 return bRet;
209 }
210
211 bool ext2_expand_inode( PEXT2_FILESYS Ext2Sys,
212 PEXT2_INODE Inode,
213 ULONG newBlk )
214 {
215 ULONG dwSizes[4] = {12, 1, 1, 1};
216 ULONG Index = 0;
217 ULONG dwTotal = 0;
218 ULONG dwBlk = 0, dwNewBlk = 0, Offset = 0;
219 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
220 int i = 0;
221 bool bRet = true;
222 bool bDirty = false;
223 ULONG TotalBlocks;
224
225 TotalBlocks = Inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
226 Index = Ext2DataBlocks(Ext2Sys, TotalBlocks);
227
228 for (i = 0; i < 4; i++)
229 {
230 dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
231 dwTotal += dwSizes[i];
232 }
233
234 if (Index >= dwTotal)
235 {
236 KdPrint(("Mke2fs: ext2_expand_inode: beyond the maxinum size of an inode.\n"));
237 return false;
238 }
239
240 for (i = 0; i < 4; i++)
241 {
242 if (Index < dwSizes[i])
243 {
244 if (i == 0)
245 {
246 Inode->i_block[Index] = newBlk;
247 bDirty = true;
248 }
249 else
250 {
251 dwBlk = Inode->i_block[(i + 12 - 1)];
252
253 if (dwBlk == 0)
254 {
255 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk))
256 {
257 Inode->i_block[(i + 12 - 1)] = dwBlk;
258 bDirty = true;
259
260 Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
261 }
262 else
263 {
264 break;
265 }
266 }
267
268 dwNewBlk = 0;
269 bRet = ext2_expand_block(
270 Ext2Sys,
271 Inode,
272 dwBlk,
273 Index,
274 i,
275 newBlk,
276 &dwNewBlk,
277 &Offset );
278 }
279
280 break;
281 }
282
283 Index -= dwSizes[i];
284 }
285
286 return bRet;
287 }
288
289
290 bool ext2_get_block(PEXT2_FILESYS Ext2Sys, ULONG dwContent, ULONG Index, int layer, ULONG *dwRet)
291 {
292 ULONG *pData = NULL;
293 LONGLONG Offset = 0;
294 ULONG i = 0, j = 0, temp = 1;
295 ULONG dwBlk = 0;
296 bool bRet = true;
297
298 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
299
300 Offset = (LONGLONG) dwContent;
301 Offset = Offset * Ext2Sys->blocksize;
302
303 pData = (ULONG *)RtlAllocateHeap(GetProcessHeap(), 0, Ext2Sys->blocksize);
304
305 if (!pData)
306 {
307 return false;
308 }
309 memset(pData, 0, Ext2Sys->blocksize);
310
311 if (layer == 0)
312 {
313 dwBlk = dwContent;
314 }
315 else if (layer <= 3)
316 {
317
318 if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
319 {
320 bRet = false;
321 goto errorout;
322 }
323
324 temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
325
326 i = Index / temp;
327 j = Index % temp;
328
329 if (!ext2_get_block(Ext2Sys, pData[i], j, layer - 1, &dwBlk))
330 {
331 bRet = false;
332 KdPrint(("Mke2fs: ext2_get_block: ... error recuise...\n"));
333 goto errorout;
334 }
335 }
336
337 errorout:
338
339 if (pData)
340 RtlFreeHeap(GetProcessHeap(), 0, pData);
341
342 if (bRet && dwRet)
343 *dwRet = dwBlk;
344
345 return bRet;
346 }
347
348 bool ext2_block_map(PEXT2_FILESYS Ext2Sys, PEXT2_INODE inode, ULONG block, ULONG *dwRet)
349 {
350 ULONG dwSizes[4] = {12, 1, 1, 1};
351 ULONG Index = 0;
352 ULONG dwBlk = 0;
353 PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
354 int i;
355 bool bRet = false;
356
357 Index = block;
358
359 for (i = 0; i < 4; i++)
360 {
361 dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
362 }
363
364 if (Index >= inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE))
365 {
366 KdPrint(("Mke2fs: ext2_block_map: beyond the size of the inode.\n"));
367 return false;
368 }
369
370 for (i = 0; i < 4; i++)
371 {
372 if (Index < dwSizes[i])
373 {
374 dwBlk = inode->i_block[i==0 ? (Index):(i + 12 - 1)];
375
376 bRet = ext2_get_block(Ext2Sys, dwBlk, Index , i, &dwBlk);
377
378 break;
379 }
380 Index -= dwSizes[i];
381 }
382
383 if (bRet && dwBlk)
384 {
385 if (dwRet)
386 *dwRet = dwBlk;
387
388 return true;
389 }
390 else
391 return false;
392 }
393
394 ULONG ext2_build_bdl(PEXT2_FILESYS Ext2Sys,
395 PEXT2_INODE ext2_inode,
396 IN ULONG offset,
397 IN ULONG size,
398 OUT PEXT2_BDL *ext2_bdl )
399 {
400 ULONG nBeg, nEnd, nBlocks;
401 ULONG dwBlk;
402 ULONG i;
403 ULONG dwBytes = 0;
404 LONGLONG lba;
405
406 PEXT2_BDL ext2bdl = NULL;
407
408 *ext2_bdl = NULL;
409
410 if (offset >= ext2_inode->i_size)
411 {
412 KdPrint(("Mke2fs: ext2_build_bdl: beyond the file range.\n"));
413 return 0;
414 }
415
416 /*
417 if (offset + size > ext2_inode->i_size)
418 {
419 size = ext2_inode->i_size - offset;
420 }
421 */
422
423 nBeg = offset / Ext2Sys->blocksize;
424 nEnd = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
425
426 nBlocks = nEnd - nBeg;
427
428 if (nBlocks > 0)
429 {
430 ext2bdl = (PEXT2_BDL)
431 RtlAllocateHeap(GetProcessHeap(), 0, sizeof(EXT2_BDL) * nBlocks);
432
433 if (ext2bdl)
434 {
435
436 memset(ext2bdl, 0, sizeof(EXT2_BDL) * nBlocks);
437
438 for (i = nBeg; i < nEnd; i++)
439 {
440 if (!ext2_block_map(Ext2Sys, ext2_inode, i, &dwBlk))
441 {
442 goto fail;
443 }
444
445 lba = (LONGLONG) dwBlk;
446 lba = lba * Ext2Sys->blocksize;
447
448 if (nBlocks == 1) // ie. (nBeg == nEnd - 1)
449 {
450 dwBytes = size;
451 ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
452 ext2bdl[i - nBeg].Length = dwBytes;
453 ext2bdl[i - nBeg].Offset = 0;
454 }
455 else
456 {
457 if (i == nBeg)
458 {
459 dwBytes = Ext2Sys->blocksize - (offset % (Ext2Sys->blocksize));
460 ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
461 ext2bdl[i - nBeg].Length = dwBytes;
462 ext2bdl[i - nBeg].Offset = 0;
463 }
464 else if (i == nEnd - 1)
465 {
466 ext2bdl[i - nBeg].Lba = lba;
467 ext2bdl[i - nBeg].Length = size - dwBytes;
468 ext2bdl[i - nBeg].Offset = dwBytes;
469 dwBytes = size;
470 }
471 else
472 {
473 ext2bdl[i - nBeg].Lba = lba;
474 ext2bdl[i - nBeg].Length = Ext2Sys->blocksize;
475 ext2bdl[i - nBeg].Offset = dwBytes;
476 dwBytes += Ext2Sys->blocksize;
477 }
478 }
479 }
480
481 *ext2_bdl = ext2bdl;
482 return nBlocks;
483 }
484 }
485
486 fail:
487
488 if (ext2bdl)
489 RtlFreeHeap(GetProcessHeap(), 0, ext2bdl);
490
491 // Error
492 return 0;
493 }
494
495
496 bool ext2_read_inode(PEXT2_FILESYS Ext2Sys,
497 ULONG ino,
498 ULONG offset,
499 PVOID Buffer,
500 ULONG size,
501 PULONG dwReturn)
502 {
503 PEXT2_BDL ext2_bdl = NULL;
504 ULONG blocks, i;
505 bool bRet = true;
506 EXT2_INODE ext2_inode;
507 ULONG dwTotal = 0;
508
509 if (!ext2_load_inode(Ext2Sys, ino, &ext2_inode))
510 {
511 return false;
512 }
513
514 blocks = ext2_build_bdl(Ext2Sys, &ext2_inode, offset, size, &ext2_bdl);
515
516 if (blocks <= 0)
517 return false;
518
519
520 for(i = 0; i < blocks; i++)
521 {
522 bRet = NT_SUCCESS(Ext2ReadDisk(
523 Ext2Sys,
524 ext2_bdl[i].Lba,
525 ext2_bdl[i].Length,
526 (PUCHAR)Buffer + ext2_bdl[i].Offset
527 ));
528
529 if (!bRet)
530 break;
531 dwTotal += ext2_bdl[i].Length;
532 }
533
534 *dwReturn = dwTotal;
535
536 if (ext2_bdl)
537 RtlFreeHeap(GetProcessHeap(), 0, ext2_bdl);
538
539 return bRet;
540 }
541
542
543 bool ext2_write_inode (PEXT2_FILESYS Ext2Sys,
544 ULONG ino,
545 ULONG offset,
546 PVOID Buffer,
547 ULONG size,
548 PULONG dwReturn )
549 {
550 PEXT2_BDL ext2_bdl = NULL;
551 ULONG blocks, i;
552 bool bRet = true;
553 EXT2_INODE inode;
554 ULONG dwTotal = 0;
555 ULONG dwBlk = 0;
556 ULONG TotalBlks;
557
558 blocks = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
559
560 if (!ext2_load_inode(Ext2Sys, ino, &inode))
561 {
562 return false;
563 }
564
565 TotalBlks = inode.i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
566 TotalBlks = Ext2DataBlocks(Ext2Sys, TotalBlks);
567
568 if (blocks > TotalBlks)
569 {
570 for (i=0; i < (blocks - TotalBlks); i++)
571 {
572 if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
573 {
574 ext2_expand_inode(Ext2Sys, &inode, dwBlk);
575 inode.i_blocks += (Ext2Sys->blocksize/SECTOR_SIZE);
576 }
577 }
578 }
579
580 blocks = ext2_build_bdl(Ext2Sys, &inode, offset, size, &ext2_bdl);
581
582 if (blocks <= 0)
583 return false;
584
585 for(i = 0; i < blocks; i++)
586 {
587 bRet = NT_SUCCESS(Ext2WriteDisk(
588 Ext2Sys,
589 ext2_bdl[i].Lba,
590 ext2_bdl[i].Length,
591 (PUCHAR)Buffer + ext2_bdl[i].Offset
592 ));
593
594 if (!bRet)
595 {
596 goto errorout;
597 }
598
599 dwTotal += ext2_bdl[i].Length;
600 }
601
602 *dwReturn = dwTotal;
603
604 if (size + offset > inode.i_size)
605 {
606 inode.i_size = size + offset;
607 }
608
609 ext2_save_inode(Ext2Sys, ino, &inode);
610
611
612 errorout:
613
614 if (ext2_bdl)
615 RtlFreeHeap(GetProcessHeap(), 0, ext2_bdl);
616
617 return bRet;
618 }
619
620 bool
621 ext2_add_entry( PEXT2_FILESYS Ext2Sys,
622 ULONG parent, ULONG inode,
623 int filetype, char *name )
624 {
625 PEXT2_DIR_ENTRY2 dir = NULL, newdir = NULL;
626 EXT2_INODE parent_inode;
627 ULONG dwRet;
628 char *buf;
629 int rec_len;
630 bool bRet = false;
631
632 rec_len = EXT2_DIR_REC_LEN(strlen(name));
633
634 if (!ext2_load_inode(Ext2Sys, parent, &parent_inode))
635 {
636 return false;
637 }
638
639 buf = (char *)RtlAllocateHeap(GetProcessHeap(), 0, parent_inode.i_size);
640
641 if (!ext2_read_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet))
642 {
643 return false;
644 }
645
646 dir = (PEXT2_DIR_ENTRY2) buf;
647
648 while ((char *)dir < buf + parent_inode.i_size)
649 {
650 if ((dir->inode == 0 && dir->rec_len >= rec_len) ||
651 (dir->rec_len >= dir->name_len + rec_len) )
652 {
653 if (dir->inode)
654 {
655 newdir = (PEXT2_DIR_ENTRY2) ((PUCHAR)dir + EXT2_DIR_REC_LEN(dir->name_len));
656 newdir->rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len);
657
658 dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len);
659
660 dir = newdir;
661 }
662
663 dir->file_type = filetype;
664 dir->inode = inode;
665 dir->name_len = strlen(name);
666 memcpy(dir->name, name, strlen(name));
667
668 bRet = true;
669 break;
670 }
671
672 dir = (PEXT2_DIR_ENTRY2) (dir->rec_len + (PUCHAR) dir);
673 }
674
675
676 if (bRet)
677 return ext2_write_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet);
678
679 return bRet;
680 }
681
682
683 bool ext2_reserve_inodes(PEXT2_FILESYS fs)
684 {
685 ULONG i;
686 int group;
687
688 for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->ext2_sb); i++)
689 {
690 ext2_mark_inode_bitmap(fs->inode_map, i);
691 group = ext2_group_of_ino(fs, i);
692 fs->group_desc[group].bg_free_inodes_count--;
693 fs->ext2_sb->s_free_inodes_count--;
694 }
695
696 return true;
697 }