1 /*
2  * COPYRIGHT:        See COPYRIGHT.TXT
3  * PROJECT:          Ext2 File System Driver for WinNT/2K/XP
4  * FILE:             indirect.c
5  * PROGRAMMER:       Matt Wu <mattwu@163.com>
6  * HOMEPAGE:         http://www.ext2fsd.com
7  * UPDATE HISTORY:
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "ext2fs.h"
13 
14 /* GLOBALS *****************************************************************/
15 
16 extern PEXT2_GLOBAL Ext2Global;
17 
18 /* DEFINITIONS *************************************************************/
19 
20 #ifdef ALLOC_PRAGMA
21 #endif
22 
23 
24 NTSTATUS
25 Ext2ExpandLast(
26     IN PEXT2_IRP_CONTEXT    IrpContext,
27     IN PEXT2_VCB            Vcb,
28     IN PEXT2_MCB            Mcb,
29     IN ULONG                Base,
30     IN ULONG                Layer,
31     IN PULONG *             Data,
32     IN PULONG               Hint,
33     IN PULONG               Block,
34     IN OUT PULONG           Number
35 )
36 {
37     PULONG      pData = NULL;
38     ULONG       i;
39     NTSTATUS    Status = STATUS_SUCCESS;
40 
41     if (Layer > 0 || IsMcbDirectory(Mcb)) {
42 
43         /* allocate buffer for new block */
44         pData = (ULONG *) Ext2AllocatePool(
45                     PagedPool,
46                     BLOCK_SIZE,
47                     EXT2_DATA_MAGIC
48                 );
49         if (!pData) {
50             DEBUG(DL_ERR, ( "Ex2ExpandBlock: failed to allocate memory for Data.\n"));
51             Status = STATUS_INSUFFICIENT_RESOURCES;
52             goto errorout;
53         }
54         RtlZeroMemory(pData, BLOCK_SIZE);
55         INC_MEM_COUNT(PS_BLOCK_DATA, pData, BLOCK_SIZE);
56     }
57 
58     /* allocate block from disk */
59     Status = Ext2NewBlock(
60                  IrpContext,
61                  Vcb,
62                  (Mcb->Inode.i_ino - 1) / BLOCKS_PER_GROUP,
63                  *Hint,
64                  Block,
65                  Number
66              );
67 
68     if (!NT_SUCCESS(Status)) {
69         goto errorout;
70     }
71 
72     /* increase inode i_blocks */
73     Mcb->Inode.i_blocks += (*Number << (BLOCK_BITS - 9));
74 
75     if (Layer == 0) {
76 
77         if (IsMcbDirectory(Mcb)) {
78             /* for directory we need initialize it's entry structure */
79             PEXT2_DIR_ENTRY2 pEntry;
80             pEntry = (PEXT2_DIR_ENTRY2) pData;
81             pEntry->rec_len = (USHORT)(BLOCK_SIZE);
82             ASSERT(*Number == 1);
83             Ext2SaveBlock(IrpContext, Vcb, *Block, (PVOID)pData);
84         }
85 
86         /* add new Extent into Mcb */
87         if (!Ext2AddBlockExtent(Vcb, Mcb, Base, (*Block), *Number)) {
88             DbgBreak();
89             ClearFlag(Mcb->Flags, MCB_ZONE_INITED);
90             Ext2ClearAllExtents(&Mcb->Extents);
91         }
92 
93     } else {
94 
95         /* zero the content of all meta blocks */
96         for (i = 0; i < *Number; i++) {
97             Ext2SaveBlock(IrpContext, Vcb, *Block + i, (PVOID)pData);
98             /* add block to meta extents */
99             if (!Ext2AddMcbMetaExts(Vcb, Mcb, *Block + i, 1)) {
100                 DbgBreak();
101                 Ext2Sleep(500);
102                 Ext2AddMcbMetaExts(Vcb, Mcb, *Block + i, 1);
103             }
104         }
105     }
106 
107 errorout:
108 
109     if (NT_SUCCESS(Status)) {
110         *Hint = *Block + *Number;
111         if (Data) {
112             *Data = pData;
113             ASSERT(*Number == 1);
114         } else {
115             if (pData) {
116                 Ext2FreePool(pData, EXT2_DATA_MAGIC);
117                 DEC_MEM_COUNT(PS_BLOCK_DATA, pData, BLOCK_SIZE);
118             }
119         }
120     } else {
121         if (pData) {
122             Ext2FreePool(pData, EXT2_DATA_MAGIC);
123             DEC_MEM_COUNT(PS_BLOCK_DATA, pData, BLOCK_SIZE);
124         }
125         if (*Block) {
126             Ext2FreeBlock(IrpContext, Vcb, *Block, *Number);
127             Mcb->Inode.i_blocks -= (*Number << (BLOCK_BITS - 9));
128             *Block = 0;
129         }
130     }
131 
132     return Status;
133 }
134 
135 NTSTATUS
136 Ext2GetBlock(
137     IN PEXT2_IRP_CONTEXT    IrpContext,
138     IN PEXT2_VCB            Vcb,
139     IN PEXT2_MCB            Mcb,
140     IN ULONG                Base,
141     IN ULONG                Layer,
142     IN ULONG                Start,
143     IN ULONG                SizeArray,
144     IN PULONG               BlockArray,
145     IN BOOLEAN              bAlloc,
146     IN OUT PULONG           Hint,
147     OUT PULONG              Block,
148     OUT PULONG              Number
149 )
150 {
151     NTSTATUS    Status = STATUS_SUCCESS;
152     PBCB        Bcb = NULL;
153     PULONG      pData = NULL;
154     ULONG       Slot = 0, i = 0;
155     ULONG       Unit = 1;
156 
157     LARGE_INTEGER Offset;
158 
159     if (Layer == 0) {
160 
161         *Number = 1;
162         if (BlockArray[0] == 0 && bAlloc) {
163 
164             /* now allocate new block */
165             Status = Ext2ExpandLast(
166                          IrpContext,
167                          Vcb,
168                          Mcb,
169                          Base,
170                          Layer,
171                          NULL,
172                          Hint,
173                          &BlockArray[0],
174                          Number
175                      );
176 
177             if (!NT_SUCCESS(Status)) {
178                 goto errorout;
179             }
180         } else {
181             /* check the block is valid or not */
182             if (BlockArray[0] >= TOTAL_BLOCKS) {
183                 DbgBreak();
184                 Status = STATUS_DISK_CORRUPT_ERROR;
185                 goto errorout;
186             }
187         }
188 
189         *Block = BlockArray[0];
190         for (i=1; i < SizeArray; i++) {
191             if (BlockArray[i] == BlockArray[i-1] + 1) {
192                 *Number = *Number + 1;
193             } else {
194                 break;
195             }
196         }
197         *Hint = BlockArray[*Number - 1];
198 
199     } else if (Layer <= 3) {
200 
201         /* check the block is valid or not */
202         if (BlockArray[0] == 0 || BlockArray[0] >= TOTAL_BLOCKS) {
203             DbgBreak();
204             Status = STATUS_DISK_CORRUPT_ERROR;
205             goto errorout;
206         }
207 
208         /* add block to meta extents */
209         if (!Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[0], 1)) {
210             DbgBreak();
211             Ext2Sleep(500);
212             Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[0], 1);
213         }
214 
215         /* map memory in cache for the index block */
216         Offset.QuadPart = ((LONGLONG)BlockArray[0]) << BLOCK_BITS;
217         if ( !CcPinRead( Vcb->Volume,
218                          (PLARGE_INTEGER) (&Offset),
219                          BLOCK_SIZE,
220                          PIN_WAIT,
221                          &Bcb,
222 #ifdef __REACTOS__
223                          (void **)&pData )) {
224 #else
225                          &pData )) {
226 #endif
227 
228             DEBUG(DL_ERR, ( "Ext2GetBlock: Failed to PinLock block: %xh ...\n",
229                             BlockArray[0] ));
230             Status = STATUS_CANT_WAIT;
231             goto errorout;
232         }
233 
234         if (Layer > 1) {
235             Unit = Vcb->max_blocks_per_layer[Layer - 1];
236         } else {
237             Unit = 1;
238         }
239 
240         Slot  = Start / Unit;
241         Start = Start % Unit;
242 
243         if (pData[Slot] == 0) {
244 
245             if (bAlloc) {
246 
247                 /* we need allocate new block and zero all data in case
248                    it's an in-direct block. Index stores the new block no. */
249                 ULONG   Count = 1;
250                 Status = Ext2ExpandLast(
251                              IrpContext,
252                              Vcb,
253                              Mcb,
254                              Base,
255                              Layer,
256                              NULL,
257                              Hint,
258                              &pData[Slot],
259                              &Count
260                          );
261 
262                 if (!NT_SUCCESS(Status)) {
263                     goto errorout;
264                 }
265 
266                 /* refresh hint block */
267                 *Hint = pData[Slot];
268 
269                 /* set dirty bit to notify system to flush */
270                 CcSetDirtyPinnedData(Bcb, NULL );
271                 SetFlag(Vcb->Volume->Flags, FO_FILE_MODIFIED);
272                 if (!Ext2AddVcbExtent(Vcb, Offset.QuadPart,
273                                       (LONGLONG)BLOCK_SIZE)) {
274                     DbgBreak();
275                     Ext2Sleep(100);
276                     if (!Ext2AddVcbExtent(Vcb, Offset.QuadPart,
277                                           (LONGLONG)BLOCK_SIZE)) {
278                         Status = STATUS_INSUFFICIENT_RESOURCES;
279                         goto errorout;
280                     }
281                 }
282 
283                 /* save inode information here */
284                 Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode);
285 
286             } else {
287 
288                 *Number = 1;
289 
290                 if (Layer == 1) {
291                     for (i = Slot + 1; i < BLOCK_SIZE/4; i++) {
292                         if (pData[i] == 0) {
293                             *Number = *Number + 1;
294                         } else {
295                             break;
296                         }
297                     }
298                 } else if (Layer == 2) {
299                     *Number = BLOCK_SIZE/4 - Start;
300                 } else {
301                     *Number = BLOCK_SIZE/4;
302                 }
303 
304                 goto errorout;
305             }
306         }
307 
308         /* transfer to next recursion call */
309         Status = Ext2GetBlock(
310                      IrpContext,
311                      Vcb,
312                      Mcb,
313                      Base,
314                      Layer - 1,
315                      Start,
316                      BLOCK_SIZE/4 - Slot,
317                      &pData[Slot],
318                      bAlloc,
319                      Hint,
320                      Block,
321                      Number
322                  );
323 
324         if (!NT_SUCCESS(Status)) {
325             goto errorout;
326         }
327     }
328 
329 errorout:
330 
331     /* free the memory of pData */
332     if (Bcb) {
333         CcUnpinData(Bcb);
334     }
335 
336     if (!NT_SUCCESS(Status)) {
337         *Block = 0;
338     }
339 
340     return Status;
341 }
342 
343 
344 NTSTATUS
345 Ext2ExpandBlock(
346     IN PEXT2_IRP_CONTEXT IrpContext,
347     IN PEXT2_VCB         Vcb,
348     IN PEXT2_MCB         Mcb,
349     IN ULONG             Base,
350     IN ULONG             Layer,
351     IN ULONG             Start,
352     IN ULONG             SizeArray,
353     IN PULONG            BlockArray,
354     IN PULONG            Hint,
355     IN PULONG            Extra
356 )
357 {
358     ULONG       i = 0;
359     ULONG       j;
360     ULONG       Slot;
361     ULONG       Block = 0;
362     LARGE_INTEGER Offset;
363 
364     PBCB        Bcb = NULL;
365     PULONG      pData = NULL;
366     ULONG       Skip = 0;
367 
368     ULONG       Number;
369     ULONG       Wanted;
370 
371     NTSTATUS    Status = STATUS_SUCCESS;
372 
373     if (Layer == 1) {
374 
375         /*
376          * try to make all leaf block continuous to avoid fragments
377          */
378 
379         Number = min(SizeArray, ((*Extra + (Start & (BLOCK_SIZE/4 - 1))) * 4 / BLOCK_SIZE));
380         Wanted = 0;
381         DEBUG(DL_BLK, ("Ext2ExpandBlock: SizeArray=%xh Extra=%xh Start=%xh %xh\n",
382                        SizeArray, *Extra, Start, Number ));
383 
384         for (i=0; i < Number; i++) {
385             if (BlockArray[i] == 0) {
386                 Wanted += 1;
387             }
388         }
389 
390         i = 0;
391         while (Wanted > 0) {
392 
393             Number = Wanted;
394             Status = Ext2ExpandLast(
395                          IrpContext,
396                          Vcb,
397                          Mcb,
398                          Base,
399                          Layer,
400                          NULL,
401                          Hint,
402                          &Block,
403                          &Number
404                      );
405             if (!NT_SUCCESS(Status)) {
406                 goto errorout;
407             }
408 
409             ASSERT(Number > 0);
410             Wanted -= Number;
411             while (Number) {
412                 if (BlockArray[i] == 0) {
413                     BlockArray[i] = Block++;
414                     Number--;
415                 }
416                 i++;
417             }
418         }
419 
420     } else if (Layer == 0) {
421 
422         /*
423          * bulk allocation for inode data blocks
424          */
425 
426         i = 0;
427 
428         while (*Extra && i < SizeArray) {
429 
430             Wanted = 0;
431             Number = 1;
432 
433             for (j = i; j < SizeArray && j < i + *Extra; j++) {
434 
435                 if (BlockArray[j] >= TOTAL_BLOCKS) {
436                     DbgBreak();
437                     BlockArray[j] = 0;
438                 }
439 
440                 if (BlockArray[j] == 0) {
441                     Wanted += 1;
442                 } else {
443                     break;
444                 }
445             }
446 
447             if (Wanted == 0) {
448 
449                 /* add block extent into Mcb */
450                 ASSERT(BlockArray[i] != 0);
451                 if (!Ext2AddBlockExtent(Vcb, Mcb, Base + i, BlockArray[i], 1)) {
452                     DbgBreak();
453                     ClearFlag(Mcb->Flags, MCB_ZONE_INITED);
454                     Ext2ClearAllExtents(&Mcb->Extents);
455                 }
456 
457             } else {
458 
459                 Number = Wanted;
460                 Status = Ext2ExpandLast(
461                              IrpContext,
462                              Vcb,
463                              Mcb,
464                              Base + i,
465                              0,
466                              NULL,
467                              Hint,
468                              &Block,
469                              &Number
470                          );
471                 if (!NT_SUCCESS(Status)) {
472                     goto errorout;
473                 }
474 
475                 ASSERT(Number > 0);
476                 for (j = 0; j < Number; j++) {
477                     BlockArray[i + j] = Block++;
478                 }
479             }
480 
481             *Extra -= Number;
482             i += Number;
483         }
484 
485         goto errorout;
486     }
487 
488 
489     /*
490      * only for meta blocks allocation
491      */
492 
493     for (i = 0; *Extra && i < SizeArray; i++) {
494 
495         if (Layer <= 3) {
496 
497             if (BlockArray[i] >= TOTAL_BLOCKS) {
498                 DbgBreak();
499                 BlockArray[i] = 0;
500             }
501 
502             if (BlockArray[i] == 0) {
503                 Number = 1;
504                 Status = Ext2ExpandLast(
505                              IrpContext,
506                              Vcb,
507                              Mcb,
508                              Base,
509                              Layer,
510                              &pData,
511                              Hint,
512                              &BlockArray[i],
513                              &Number
514                          );
515                 if (!NT_SUCCESS(Status)) {
516                     goto errorout;
517                 }
518 
519             } else {
520 
521                 Offset.QuadPart = (((LONGLONG)BlockArray[i]) << BLOCK_BITS);
522                 if (!CcPinRead(
523                             Vcb->Volume,
524                             &Offset,
525                             BLOCK_SIZE,
526                             PIN_WAIT,
527                             &Bcb,
528 #ifdef __REACTOS__
529                             (void **)&pData )) {
530 #else
531                             &pData )) {
532 #endif
533 
534                     DEBUG(DL_ERR, ( "Ext2ExpandInode: failed to PinLock offset :%I64xh...\n",
535                                     Offset.QuadPart));
536                     Status = STATUS_CANT_WAIT;
537                     DbgBreak();
538                     goto errorout;
539                 }
540 
541                 /* add block to meta extents */
542                 if (!Ext2AddMcbMetaExts(Vcb, Mcb,  BlockArray[i], 1)) {
543                     DbgBreak();
544                     Ext2Sleep(500);
545                     Ext2AddMcbMetaExts(Vcb, Mcb,  BlockArray[i], 1);
546                 }
547             }
548 
549             Skip = Vcb->max_blocks_per_layer[Layer] * i;
550 
551             if (i == 0) {
552                 if (Layer > 1) {
553                     Slot  = Start / Vcb->max_blocks_per_layer[Layer - 1];
554                     Start = Start % Vcb->max_blocks_per_layer[Layer - 1];
555                     Skip += Slot * Vcb->max_blocks_per_layer[Layer - 1];
556                 } else {
557                     Slot  = Start;
558                     Start = 0;
559                     Skip += Slot;
560                 }
561             } else {
562                 Start = 0;
563                 Slot  = 0;
564             }
565 
566             Status = Ext2ExpandBlock(
567                          IrpContext,
568                          Vcb,
569                          Mcb,
570                          Base + Skip,
571                          Layer - 1,
572                          Start,
573                          BLOCK_SIZE/4 - Slot,
574                          &pData[Slot],
575                          Hint,
576                          Extra
577                      );
578 
579             if (Bcb) {
580                 CcSetDirtyPinnedData(Bcb, NULL);
581                 if (!Ext2AddBlockExtent(Vcb, NULL,
582                                         BlockArray[i],
583                                         BlockArray[i], 1)) {
584                     DbgBreak();
585                     Ext2Sleep(500);
586                     if (!Ext2AddBlockExtent(Vcb, NULL,
587                                             BlockArray[i],
588                                             BlockArray[i], 1)) {
589                     }
590                 }
591             } else {
592                 Ext2SaveBlock(IrpContext, Vcb, BlockArray[i], (PVOID)pData);
593             }
594 
595             if (pData) {
596                 if (Bcb) {
597                     CcUnpinData(Bcb);
598                     Bcb = NULL;
599                 } else {
600                     Ext2FreePool(pData, EXT2_DATA_MAGIC);
601                     DEC_MEM_COUNT(PS_BLOCK_DATA, pData, BLOCK_SIZE);
602                 }
603                 pData = NULL;
604             }
605 
606             if (!NT_SUCCESS(Status)) {
607                 DbgBreak();
608                 break;
609             }
610         }
611     }
612 
613 errorout:
614 
615     return Status;
616 }
617 
618 BOOLEAN
619 Ext2IsBlockEmpty(PULONG BlockArray, ULONG SizeArray)
620 {
621     ULONG i = 0;
622     for (i=0; i < SizeArray; i++) {
623         if (BlockArray[i]) {
624             break;
625         }
626     }
627     return (i == SizeArray);
628 }
629 
630 
631 NTSTATUS
632 Ext2TruncateBlock(
633     IN PEXT2_IRP_CONTEXT IrpContext,
634     IN PEXT2_VCB         Vcb,
635     IN PEXT2_MCB         Mcb,
636     IN ULONG             Base,
637     IN ULONG             Start,
638     IN ULONG             Layer,
639     IN ULONG             SizeArray,
640     IN PULONG            BlockArray,
641     IN PULONG            Extra
642 )
643 {
644     NTSTATUS    Status = STATUS_SUCCESS;
645     ULONG       i = 0;
646     ULONG       Slot = 0;
647     ULONG       Skip = 0;
648 
649     LONGLONG    Offset;
650     PBCB        Bcb = NULL;
651     PULONG      pData = NULL;
652 
653     ASSERT(Mcb != NULL);
654 
655     for (i = 0; i < SizeArray; i++) {
656 
657         if (Layer == 0) {
658 
659             ULONG   Number = 1;
660 
661             while (Extra &&  SizeArray > i + 1 && Number < *Extra) {
662 
663                 if (BlockArray[SizeArray - i - 1] ==
664                     BlockArray[SizeArray - i - 2] + 1) {
665 
666                     BlockArray[SizeArray - i - 1] = 0;
667                     Number++;
668                     SizeArray--;
669 
670                 } else {
671                     break;
672                 }
673             }
674 
675             if (BlockArray[SizeArray - i - 1]) {
676 
677                 Status = Ext2FreeBlock(IrpContext, Vcb, BlockArray[SizeArray - i - 1], Number);
678                 if (NT_SUCCESS(Status)) {
679                     ASSERT(Mcb->Inode.i_blocks >= (Number << (BLOCK_BITS - 9)));
680                     if (Mcb->Inode.i_blocks < (Number << (BLOCK_BITS - 9))) {
681                         Mcb->Inode.i_blocks = 0;
682                         DbgBreak();
683                     } else {
684                         Mcb->Inode.i_blocks -= (Number << (BLOCK_BITS - 9));
685                     }
686                     BlockArray[SizeArray - i - 1] = 0;
687                 }
688             }
689 
690             if (Extra) {
691 
692                 /* dec blocks count */
693                 ASSERT(*Extra >= Number);
694                 *Extra = *Extra - Number;
695 
696                 /* remove block mapping frm Mcb Extents */
697                 if (!Ext2RemoveBlockExtent(Vcb, Mcb, Base + SizeArray - 1 - i, Number)) {
698                     DbgBreak();
699                     ClearFlag(Mcb->Flags, MCB_ZONE_INITED);
700                     Ext2ClearAllExtents(&Mcb->Extents);
701                 }
702             }
703 
704         } else {
705 
706             ASSERT(Layer <= 3);
707 
708             if (BlockArray[SizeArray - i - 1] >= TOTAL_BLOCKS) {
709                 DbgBreak();
710                 BlockArray[SizeArray - i - 1] = 0;
711             }
712 
713             if (i == 0) {
714                 if (Layer > 1) {
715                     Slot  = Start / Vcb->max_blocks_per_layer[Layer - 1];
716                     Start = Start % Vcb->max_blocks_per_layer[Layer - 1];
717                 } else {
718                     Slot  = Start;
719                     Start = (BLOCK_SIZE / 4) - 1;
720                 }
721             } else {
722                 Slot = Start = (BLOCK_SIZE / 4) - 1;
723             }
724 
725             Skip = (SizeArray - i - 1) * Vcb->max_blocks_per_layer[Layer];
726 
727             if (BlockArray[SizeArray - i - 1]) {
728 
729                 Offset = (LONGLONG) (BlockArray[SizeArray - i - 1]);
730                 Offset = Offset << BLOCK_BITS;
731 
732                 if (!CcPinRead( Vcb->Volume,
733                                 (PLARGE_INTEGER) (&Offset),
734                                 BLOCK_SIZE,
735                                 PIN_WAIT,
736                                 &Bcb,
737 #ifdef __REACTOS__
738                                 (void **)&pData )) {
739 #else
740                                 &pData )) {
741 #endif
742 
743                     DEBUG(DL_ERR, ( "Ext2TruncateBlock: PinLock failed on block %xh ...\n",
744                                     BlockArray[SizeArray - i - 1]));
745                     Status = STATUS_CANT_WAIT;
746                     DbgBreak();
747                     goto errorout;
748                 }
749 
750                 Status = Ext2TruncateBlock(
751                              IrpContext,
752                              Vcb,
753                              Mcb,
754                              Base + Skip,
755                              Start,
756                              Layer - 1,
757                              Slot + 1,
758                              &pData[0],
759                              Extra
760                          );
761 
762                 if (!NT_SUCCESS(Status)) {
763                     break;
764                 }
765 
766                 CcSetDirtyPinnedData(Bcb, NULL);
767                 Ext2AddVcbExtent(Vcb, Offset, (LONGLONG)BLOCK_SIZE);
768 
769                 if (*Extra || Ext2IsBlockEmpty(pData, BLOCK_SIZE/4)) {
770 
771                     Ext2TruncateBlock(
772                                  IrpContext,
773                                  Vcb,
774                                  Mcb,
775                                  Base + Skip,    /* base */
776                                  0,              /* start */
777                                  0,              /* layer */
778                                  1,
779                                  &BlockArray[SizeArray - i - 1],
780                                  NULL
781                              );
782 
783                     if (!Ext2RemoveMcbMetaExts(Vcb, Mcb, BlockArray[SizeArray - i - 1], 1)) {
784                         DbgBreak();
785                         Ext2Sleep(500);
786                         Ext2RemoveMcbMetaExts(Vcb, Mcb, BlockArray[SizeArray - i - 1], 1);
787                     }
788                 }
789 
790                 if (pData) {
791                     CcUnpinData(Bcb);
792                     Bcb = NULL;
793                     pData = NULL;
794                 }
795 
796             } else {
797 
798                 if (Layer > 1) {
799                     if (*Extra > Slot * Vcb->max_blocks_per_layer[Layer - 1] + Start + 1) {
800                         *Extra -= (Slot * Vcb->max_blocks_per_layer[Layer - 1] + Start + 1);
801                     } else {
802                         *Extra  = 0;
803                     }
804                 } else {
805                     if (*Extra > Slot + 1) {
806                         *Extra -= (Slot + 1);
807                     } else {
808                         *Extra  = 0;
809                     }
810                 }
811 
812                 if (!Ext2RemoveBlockExtent(Vcb, Mcb, Base + Skip, (Start + 1))) {
813                     DbgBreak();
814                     ClearFlag(Mcb->Flags, MCB_ZONE_INITED);
815                     Ext2ClearAllExtents(&Mcb->Extents);
816                 }
817             }
818         }
819 
820         if (Extra && *Extra == 0) {
821             break;
822         }
823     }
824 
825 errorout:
826 
827     if (pData) {
828         CcUnpinData(Bcb);
829     }
830 
831     return Status;
832 }
833 
834 NTSTATUS
835 Ext2MapIndirect(
836     IN PEXT2_IRP_CONTEXT    IrpContext,
837     IN PEXT2_VCB            Vcb,
838     IN PEXT2_MCB            Mcb,
839     IN ULONG                Index,
840     IN BOOLEAN              bAlloc,
841     OUT PULONG              pBlock,
842     OUT PULONG              Number
843 )
844 {
845     ULONG   Layer;
846     ULONG   Slot;
847 
848     ULONG   Base = Index;
849 
850     NTSTATUS Status = STATUS_SUCCESS;
851 
852     *pBlock = 0;
853     *Number = 0;
854 
855     for (Layer = 0; Layer < EXT2_BLOCK_TYPES; Layer++) {
856 
857         if (Index < Vcb->max_blocks_per_layer[Layer]) {
858 
859             ULONG   dwRet = 0, dwBlk = 0, dwHint = 0, dwArray = 0;
860 
861             Slot = (Layer==0) ? (Index):(Layer + EXT2_NDIR_BLOCKS - 1);
862             dwBlk = Mcb->Inode.i_block[Slot];
863 
864             if (dwBlk == 0) {
865 
866                 if (!bAlloc) {
867 
868                     *Number = 1;
869                     goto errorout;
870 
871                 } else {
872 
873                     if (Slot) {
874                         dwHint = Mcb->Inode.i_block[Slot - 1];
875                     }
876 
877                     /* allocate and zero block if necessary */
878                     *Number = 1;
879                     Status = Ext2ExpandLast(
880                                  IrpContext,
881                                  Vcb,
882                                  Mcb,
883                                  Base,
884                                  Layer,
885                                  NULL,
886                                  &dwHint,
887                                  &dwBlk,
888                                  Number
889                              );
890 
891                     if (!NT_SUCCESS(Status)) {
892                         goto errorout;
893                     }
894 
895                     /* save the it into inode*/
896                     Mcb->Inode.i_block[Slot] = dwBlk;
897 
898                     /* save the inode */
899                     if (!Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode)) {
900                         DbgBreak();
901                         Status = STATUS_UNSUCCESSFUL;
902                         goto errorout;
903                     }
904                 }
905             }
906 
907             if (Layer == 0)
908                 dwArray = Vcb->max_blocks_per_layer[Layer] - Index;
909             else
910                 dwArray = 1;
911 
912             /* querying block number of the index-th file block */
913             Status = Ext2GetBlock(
914                          IrpContext,
915                          Vcb,
916                          Mcb,
917                          Base,
918                          Layer,
919                          Index,
920                          dwArray,
921 #ifdef __REACTOS__
922                         (PULONG)&Mcb->Inode.i_block[Slot],
923 #else
924                         &Mcb->Inode.i_block[Slot],
925 #endif
926                          bAlloc,
927                          &dwHint,
928                          &dwRet,
929                          Number
930                      );
931 
932             if (NT_SUCCESS(Status)) {
933                 *pBlock = dwRet;
934             }
935 
936             break;
937         }
938 
939         Index -= Vcb->max_blocks_per_layer[Layer];
940     }
941 
942 errorout:
943 
944     return Status;
945 }
946 
947 NTSTATUS
948 Ext2ExpandIndirect(
949     PEXT2_IRP_CONTEXT IrpContext,
950     PEXT2_VCB         Vcb,
951     PEXT2_MCB         Mcb,
952     ULONG             Start,
953     ULONG             End,
954     PLARGE_INTEGER    Size
955 )
956 {
957     NTSTATUS Status = STATUS_SUCCESS;
958 
959     ULONG    Layer = 0;
960     ULONG    Extra = 0;
961     ULONG    Hint = 0;
962     ULONG    Slot = 0;
963     ULONG    Base = 0;
964 
965     Extra = End - Start;
966 
967 	/* exceeds the biggest file size (indirect) */
968     if (End > Vcb->max_data_blocks) {
969         return STATUS_INVALID_PARAMETER;
970     }
971 
972     for (Layer = 0; Layer < EXT2_BLOCK_TYPES && Extra; Layer++) {
973 
974         if (Start >= Vcb->max_blocks_per_layer[Layer]) {
975 
976             Base  += Vcb->max_blocks_per_layer[Layer];
977             Start -= Vcb->max_blocks_per_layer[Layer];
978 
979         } else {
980 
981             /* get the slot in i_block array */
982             if (Layer == 0) {
983                 Base = Slot = Start;
984             } else {
985                 Slot = Layer + EXT2_NDIR_BLOCKS - 1;
986             }
987 
988             /* set block hint to avoid fragments */
989             if (Hint == 0) {
990                 if (Mcb->Inode.i_block[Slot] != 0) {
991                     Hint = Mcb->Inode.i_block[Slot];
992                 } else if (Slot > 1) {
993                     Hint = Mcb->Inode.i_block[Slot-1];
994                 }
995             }
996 
997             /* now expand this slot */
998             Status = Ext2ExpandBlock(
999                          IrpContext,
1000                          Vcb,
1001                          Mcb,
1002                          Base,
1003                          Layer,
1004                          Start,
1005                          (Layer == 0) ? (Vcb->max_blocks_per_layer[Layer] - Start) : 1,
1006 #ifdef __REACTOS__
1007                          (PULONG)&Mcb->Inode.i_block[Slot],
1008 #else
1009                          &Mcb->Inode.i_block[Slot],
1010 #endif
1011                          &Hint,
1012                          &Extra
1013                      );
1014             if (!NT_SUCCESS(Status)) {
1015                 break;
1016             }
1017 
1018             Start = 0;
1019             if (Layer == 0) {
1020                 Base = 0;
1021             }
1022             Base += Vcb->max_blocks_per_layer[Layer];
1023         }
1024     }
1025 
1026     Size->QuadPart = ((LONGLONG)(End - Extra)) << BLOCK_BITS;
1027 
1028     /* save inode whatever it succeeds to expand or not */
1029     Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode);
1030 
1031     return Status;
1032 }
1033 
1034 NTSTATUS
1035 Ext2TruncateIndirectFast(
1036     PEXT2_IRP_CONTEXT IrpContext,
1037     PEXT2_VCB         Vcb,
1038     PEXT2_MCB         Mcb
1039     )
1040 {
1041     LONGLONG            Vba;
1042     LONGLONG            Lba;
1043     LONGLONG            Length;
1044     NTSTATUS            Status = STATUS_SUCCESS;
1045     int                 i;
1046 
1047     /* try to load all indirect blocks if mcb zone is not initialized */
1048     if (!IsZoneInited(Mcb)) {
1049         Status = Ext2InitializeZone(IrpContext, Vcb, Mcb);
1050         if (!NT_SUCCESS(Status)) {
1051             DbgBreak();
1052             ClearLongFlag(Mcb->Flags, MCB_ZONE_INITED);
1053             goto errorout;
1054         }
1055     }
1056 
1057     ASSERT (IsZoneInited(Mcb));
1058 
1059     /* delete all data blocks here */
1060     if (FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents) != 0) {
1061         for (i = 0; FsRtlGetNextLargeMcbEntry(&Mcb->Extents, i, &Vba, &Lba, &Length); i++) {
1062             /* ignore the non-existing runs */
1063             if (-1 == Lba || Vba == 0 || Length <= 0)
1064                 continue;
1065             /* now do data block free */
1066             Ext2FreeBlock(IrpContext, Vcb, (ULONG)(Lba - 1), (ULONG)Length);
1067         }
1068     }
1069 
1070     /* delete all meta blocks here */
1071     if (FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts) != 0) {
1072         for (i = 0; FsRtlGetNextLargeMcbEntry(&Mcb->MetaExts, i, &Vba, &Lba, &Length); i++) {
1073             /* ignore the non-existing runs */
1074             if (-1 == Lba || Vba == 0 || Length <= 0)
1075                 continue;
1076             /* now do meta block free */
1077             Ext2FreeBlock(IrpContext, Vcb, (ULONG)(Lba - 1), (ULONG)Length);
1078         }
1079     }
1080 
1081     /* clear data and meta extents */
1082     Ext2ClearAllExtents(&Mcb->Extents);
1083     Ext2ClearAllExtents(&Mcb->MetaExts);
1084     ClearFlag(Mcb->Flags, MCB_ZONE_INITED);
1085 
1086     /* clear inode blocks & sizes */
1087     Mcb->Inode.i_blocks = 0;
1088     Mcb->Inode.i_size = 0;
1089     memset(&Mcb->Inode.i_block[0], 0, sizeof(__u32) * 15);
1090 
1091     /* the caller will do inode save */
1092 
1093 errorout:
1094 
1095     return Status;
1096 }
1097 
1098 NTSTATUS
1099 Ext2TruncateIndirect(
1100     PEXT2_IRP_CONTEXT IrpContext,
1101     PEXT2_VCB         Vcb,
1102     PEXT2_MCB         Mcb,
1103     PLARGE_INTEGER    Size
1104 )
1105 {
1106     NTSTATUS Status = STATUS_SUCCESS;
1107 
1108     ULONG    Layer = 0;
1109 
1110     ULONG    Extra = 0;
1111     ULONG    Wanted = 0;
1112     ULONG    End;
1113     ULONG    Base;
1114 
1115     ULONG    SizeArray = 0;
1116     PULONG   BlockArray = NULL;
1117 
1118     /* translate file size to block */
1119     End = Base = Vcb->max_data_blocks;
1120     Wanted = (ULONG)((Size->QuadPart + BLOCK_SIZE - 1) >> BLOCK_BITS);
1121 
1122     /* do fast deletion here */
1123     if (Wanted == 0) {
1124         Status = Ext2TruncateIndirectFast(IrpContext, Vcb, Mcb);
1125         if (NT_SUCCESS(Status))
1126             goto errorout;
1127     }
1128 
1129     /* calculate blocks to be freed */
1130     Extra = End - Wanted;
1131 
1132     for (Layer = EXT2_BLOCK_TYPES; Layer > 0 && Extra; Layer--) {
1133 
1134         if (Vcb->max_blocks_per_layer[Layer - 1] == 0) {
1135             continue;
1136         }
1137 
1138         Base -= Vcb->max_blocks_per_layer[Layer - 1];
1139 
1140         if (Layer - 1 == 0) {
1141 #ifdef __REACTOS__
1142             BlockArray = (PULONG)&Mcb->Inode.i_block[0];
1143 #else
1144             BlockArray = &Mcb->Inode.i_block[0];
1145 #endif
1146             SizeArray = End;
1147             ASSERT(End == EXT2_NDIR_BLOCKS && Base == 0);
1148         } else {
1149 #ifdef __REACTOS__
1150             BlockArray = (PULONG)&Mcb->Inode.i_block[EXT2_NDIR_BLOCKS - 1 + Layer - 1];
1151 #else
1152             BlockArray = &Mcb->Inode.i_block[EXT2_NDIR_BLOCKS - 1 + Layer - 1];
1153 #endif
1154             SizeArray = 1;
1155         }
1156 
1157         Status = Ext2TruncateBlock(
1158                      IrpContext,
1159                      Vcb,
1160                      Mcb,
1161                      Base,
1162                      End - Base - 1,
1163                      Layer - 1,
1164                      SizeArray,
1165                      BlockArray,
1166                      &Extra
1167                  );
1168         if (!NT_SUCCESS(Status)) {
1169             break;
1170         }
1171 
1172         End = Base;
1173     }
1174 
1175 errorout:
1176 
1177     if (!NT_SUCCESS(Status)) {
1178         Size->QuadPart += ((ULONGLONG)Extra << BLOCK_BITS);
1179     }
1180 
1181     /* save inode */
1182     if (Mcb->Inode.i_size > (loff_t)(Size->QuadPart))
1183         Mcb->Inode.i_size = (loff_t)(Size->QuadPart);
1184     Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode);
1185 
1186     return Status;
1187 }
1188