OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
Compression.c
Go to the documentation of this file.
1
9#include "NTFS.h"
10#include "Helper.h"
11
12#define IS_COMPRESSED_BLOCK 0x8000
13#define BLOCK_LENGTH_BITS 0xFFF
14#define UNIT_MASK 0xF
15
16extern UINT64 mUnitSize;
17STATIC UINT64 mBufferSize;
18
19STATIC
20EFI_STATUS
22 IN OUT COMPRESSED *Clusters
23 )
24{
25 EFI_STATUS Status;
26 UINTN ClusterSize;
27
28 ClusterSize = Clusters->FileSystem->ClusterSize;
29
30 if (Clusters->Head >= Clusters->Tail) {
31 DEBUG ((DEBUG_INFO, "NTFS: Compression block overflown\n"));
32 return EFI_VOLUME_CORRUPTED;
33 }
34
35 Status = DiskRead (
36 Clusters->FileSystem,
37 (Clusters->Elements[Clusters->Head].Lcn - Clusters->Elements[Clusters->Head].Vcn + Clusters->CurrentVcn) * ClusterSize,
38 ClusterSize,
39 Clusters->Cluster
40 );
41 if (EFI_ERROR (Status)) {
42 return Status;
43 }
44
45 ++Clusters->CurrentVcn;
46
47 if (Clusters->CurrentVcn >= Clusters->Elements[Clusters->Head].Vcn) {
48 ++Clusters->Head;
49 }
50
51 Clusters->ClusterOffset = 0;
52
53 return EFI_SUCCESS;
54}
55
56STATIC
57EFI_STATUS
59 IN COMPRESSED *Clusters,
60 OUT UINT8 *Result
61 )
62{
63 EFI_STATUS Status;
64
65 ASSERT (Clusters != NULL);
66 ASSERT (Result != NULL);
67
68 if (Clusters->ClusterOffset >= Clusters->FileSystem->ClusterSize) {
69 Status = GetNextCluster (Clusters);
70 if (EFI_ERROR (Status)) {
71 return Status;
72 }
73 }
74
75 *Result = Clusters->Cluster[Clusters->ClusterOffset++];
76
77 return EFI_SUCCESS;
78}
79
80STATIC
81EFI_STATUS
83 IN COMPRESSED *Clusters,
84 OUT UINT16 *Result
85 )
86{
87 EFI_STATUS Status;
88 UINT8 ByteLow;
89 UINT8 ByteHigh;
90
91 ASSERT (Clusters != NULL);
92 ASSERT (Result != NULL);
93
94 Status = GetDataRunByte (Clusters, &ByteLow);
95 if (EFI_ERROR (Status)) {
96 return Status;
97 }
98
99 Status = GetDataRunByte (Clusters, &ByteHigh);
100 if (EFI_ERROR (Status)) {
101 return Status;
102 }
103
104 *Result = (((UINT16)ByteHigh) << 8U) | ByteLow;
105
106 return EFI_SUCCESS;
107}
108
160STATIC
161EFI_STATUS
163 IN COMPRESSED *Clusters,
164 OUT UINT8 *Dest OPTIONAL
165 )
166{
167 EFI_STATUS Status;
168 UINT16 BlockParameters;
169 UINTN BlockLength;
170 UINT8 TagsByte;
171 UINT8 Tokens;
172 UINT16 ClearTextPointer;
173 UINT8 PlainText;
174 UINT16 Index;
175 UINT16 Length;
176 UINT16 Lmask;
177 UINT16 Delta;
178 UINT16 Dshift;
179 UINT16 BackReference;
180 UINTN SpareBytes;
181
182 ASSERT (Clusters != NULL);
183
184 Status = GetTwoDataRunBytes (Clusters, &BlockParameters);
185 if (EFI_ERROR (Status)) {
186 return Status;
187 }
188
189 BlockLength = (BlockParameters & BLOCK_LENGTH_BITS) + 1U;
190
191 if (Dest != NULL) {
192 if ((BlockParameters & IS_COMPRESSED_BLOCK) != 0) {
193 ClearTextPointer = 0;
194 Tokens = 0;
195 TagsByte = 0;
196 while (BlockLength > 0) {
197 if (ClearTextPointer > COMPRESSION_BLOCK) {
198 DEBUG ((DEBUG_INFO, "NTFS: Compression block too large\n"));
199 return EFI_VOLUME_CORRUPTED;
200 }
201
202 if (Tokens == 0) {
203 Status = GetDataRunByte (Clusters, &TagsByte);
204 if (EFI_ERROR (Status)) {
205 return Status;
206 }
207
208 Tokens = 8U;
209 --BlockLength;
210 if (BlockLength == 0) {
211 break;
212 }
213 }
214
215 if ((TagsByte & 1U) != 0) {
216 //
217 // Back-reference
218 //
219 Status = GetTwoDataRunBytes (Clusters, &BackReference);
220 if (EFI_ERROR (Status)) {
221 return Status;
222 }
223
224 BlockLength -= 2U;
225
226 if (ClearTextPointer == 0) {
227 DEBUG ((DEBUG_INFO, "NTFS: Nontext window empty\n"));
228 return EFI_VOLUME_CORRUPTED;
229 }
230
231 Lmask = BLOCK_LENGTH_BITS;
232 Dshift = 12U;
233 for (Index = ClearTextPointer - 1U; Index >= 0x10U; Index >>= 1U) {
234 Lmask >>= 1U;
235 --Dshift;
236 }
237
238 Delta = BackReference >> Dshift;
239 Length = (BackReference & Lmask) + 3U;
240
241 if ((Delta > (ClearTextPointer - 1U)) || (Length >= COMPRESSION_BLOCK)) {
242 DEBUG ((DEBUG_INFO, "NTFS: Invalid back-reference.\n"));
243 return EFI_VOLUME_CORRUPTED;
244 }
245
246 if (mBufferSize < Length) {
247 DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #1) Buffer overflow.\n"));
248 return EFI_VOLUME_CORRUPTED;
249 }
250
251 for (Index = 0; Index < Length; ++Index) {
252 Dest[ClearTextPointer] = Dest[ClearTextPointer - Delta - 1U];
253 ++ClearTextPointer;
254 }
255
257 } else {
258 //
259 // Plain text
260 //
261 Status = GetDataRunByte (Clusters, &PlainText);
262 if (EFI_ERROR (Status)) {
263 return Status;
264 }
265
266 if (mBufferSize == 0) {
267 DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #2) Buffer overflow.\n"));
268 return EFI_VOLUME_CORRUPTED;
269 }
270
271 Dest[ClearTextPointer++] = PlainText;
272
273 --BlockLength;
274 --mBufferSize;
275 }
276
277 TagsByte >>= 1U;
278 --Tokens;
279 }
280
281 return EFI_SUCCESS;
282 }
283
284 if (BlockLength != COMPRESSION_BLOCK) {
285 DEBUG ((DEBUG_INFO, "NTFS: Invalid compression block size %d\n", BlockLength));
286 return EFI_VOLUME_CORRUPTED;
287 }
288 }
289
290 while (BlockLength > 0) {
291 SpareBytes = Clusters->FileSystem->ClusterSize - Clusters->ClusterOffset;
292 if (SpareBytes > BlockLength) {
293 SpareBytes = BlockLength;
294 }
295
296 if ((Dest != NULL) && (SpareBytes != 0)) {
297 if (mBufferSize < SpareBytes) {
298 DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #3) Buffer overflow.\n"));
299 return EFI_VOLUME_CORRUPTED;
300 }
301
302 CopyMem (Dest, &Clusters->Cluster[Clusters->ClusterOffset], SpareBytes);
303 Dest += SpareBytes;
304 mBufferSize -= SpareBytes;
305 }
306
307 BlockLength -= SpareBytes;
308 Clusters->ClusterOffset += SpareBytes;
309 if (BlockLength != 0) {
310 Status = GetNextCluster (Clusters);
311 if (EFI_ERROR (Status)) {
312 return Status;
313 }
314 }
315 }
316
317 return EFI_SUCCESS;
318}
319
374STATIC
375EFI_STATUS
377 IN RUNLIST *Runlist,
378 OUT UINT8 *Dest OPTIONAL,
379 IN UINTN BlocksTotal
380 )
381{
382 EFI_STATUS Status;
383 UINTN SpareBlocks;
384 UINT64 SpareClusters;
385 UINT64 BlocksPerCluster;
386 UINT64 ClustersPerBlock;
387 UINT64 ClearTextClusters;
388 UINTN ClusterSize;
389
390 ASSERT (Runlist != NULL);
391
392 BlocksPerCluster = 0;
393 ClustersPerBlock = 0;
394 ClusterSize = Runlist->Unit.FileSystem->ClusterSize;
395
396 mBufferSize = BlocksTotal * COMPRESSION_BLOCK;
397
398 if (ClusterSize >= COMPRESSION_BLOCK) {
399 BlocksPerCluster = DivU64x64Remainder (ClusterSize, COMPRESSION_BLOCK, NULL);
400 } else {
401 ClustersPerBlock = DivU64x64Remainder (COMPRESSION_BLOCK, ClusterSize, NULL);
402 }
403
404 while (BlocksTotal != 0) {
405 if ((Runlist->TargetVcn & UNIT_MASK) == 0) {
406 if ((Runlist->Unit.Head != Runlist->Unit.Tail) && (Runlist->IsSparse == FALSE)) {
407 DEBUG ((DEBUG_INFO, "NTFS: Invalid compression block\n"));
408 return EFI_VOLUME_CORRUPTED;
409 }
410
411 Runlist->Unit.Head = Runlist->Unit.Tail = 0;
412 Runlist->Unit.CurrentVcn = Runlist->TargetVcn;
413 Runlist->Unit.ClusterOffset = ClusterSize;
414 if (Runlist->TargetVcn >= Runlist->NextVcn) {
415 Status = ReadRunListElement (Runlist);
416 if (EFI_ERROR (Status)) {
417 return Status;
418 }
419 }
420
421 while ((Runlist->TargetVcn + mUnitSize) > Runlist->NextVcn) {
422 if (Runlist->IsSparse) {
423 break;
424 }
425
426 Runlist->Unit.Elements[Runlist->Unit.Tail].Vcn = Runlist->NextVcn;
427 Runlist->Unit.Elements[Runlist->Unit.Tail].Lcn = Runlist->CurrentLcn + Runlist->NextVcn - Runlist->CurrentVcn;
428 ++Runlist->Unit.Tail;
429
430 Status = ReadRunListElement (Runlist);
431 if (EFI_ERROR (Status)) {
432 return Status;
433 }
434 }
435 }
436
437 if (ClusterSize >= COMPRESSION_BLOCK) {
438 SpareBlocks = (UINTN)((mUnitSize - (Runlist->TargetVcn & UNIT_MASK)) * BlocksPerCluster);
439 } else {
440 SpareBlocks = (UINTN)DivU64x64Remainder (mUnitSize - (Runlist->TargetVcn & UNIT_MASK), ClustersPerBlock, NULL);
441 }
442
443 if (SpareBlocks > BlocksTotal) {
444 SpareBlocks = BlocksTotal;
445 }
446
447 BlocksTotal -= SpareBlocks;
448
449 if (Runlist->IsSparse) {
450 if (ClusterSize >= COMPRESSION_BLOCK) {
451 Runlist->TargetVcn += DivU64x64Remainder (SpareBlocks, BlocksPerCluster, NULL);
452 } else {
453 Runlist->TargetVcn += SpareBlocks * ClustersPerBlock;
454 }
455
456 if (Runlist->Unit.Tail == 0) {
457 if (Dest != NULL) {
458 if (mBufferSize < (SpareBlocks * COMPRESSION_BLOCK)) {
459 DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #1) Buffer overflow.\n"));
460 return EFI_VOLUME_CORRUPTED;
461 }
462
463 ZeroMem (Dest, SpareBlocks * COMPRESSION_BLOCK);
464 Dest += SpareBlocks * COMPRESSION_BLOCK;
465 mBufferSize -= SpareBlocks * COMPRESSION_BLOCK;
466 }
467 } else {
468 while (SpareBlocks != 0) {
469 Status = DecompressBlock (&Runlist->Unit, Dest);
470 if (EFI_ERROR (Status)) {
471 return Status;
472 }
473
474 if (Dest != NULL) {
475 Dest += COMPRESSION_BLOCK;
476 }
477
478 --SpareBlocks;
479 }
480 }
481 } else {
482 if (ClusterSize >= COMPRESSION_BLOCK) {
483 SpareClusters = DivU64x64Remainder (SpareBlocks, BlocksPerCluster, NULL);
484 } else {
485 SpareClusters = SpareBlocks * ClustersPerBlock;
486 }
487
488 while ((Runlist->Unit.Head < Runlist->Unit.Tail) && (SpareClusters != 0)) {
489 ClearTextClusters = Runlist->Unit.Elements[Runlist->Unit.Head].Vcn - Runlist->TargetVcn;
490 if (ClearTextClusters > SpareClusters) {
491 ClearTextClusters = SpareClusters;
492 }
493
494 Runlist->TargetVcn += ClearTextClusters;
495 if (Dest != NULL) {
496 if (mBufferSize < (ClearTextClusters * ClusterSize)) {
497 DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #2) Buffer overflow.\n"));
498 return EFI_VOLUME_CORRUPTED;
499 }
500
501 Status = DiskRead (
502 Runlist->Unit.FileSystem,
503 Runlist->Unit.Elements[Runlist->Unit.Head].Lcn * ClusterSize,
504 (UINTN)(ClearTextClusters * ClusterSize),
505 Dest
506 );
507 if (EFI_ERROR (Status)) {
508 return Status;
509 }
510
511 Dest += ClearTextClusters * ClusterSize;
512 mBufferSize -= ClearTextClusters * ClusterSize;
513 }
514
515 SpareClusters -= ClearTextClusters;
516 ++Runlist->Unit.Head;
517 }
518
519 if (SpareClusters != 0) {
520 if (Dest != NULL) {
521 if (mBufferSize < (SpareClusters * ClusterSize)) {
522 DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #3) Buffer overflow.\n"));
523 return EFI_VOLUME_CORRUPTED;
524 }
525
526 Status = DiskRead (
527 Runlist->Unit.FileSystem,
528 (Runlist->TargetVcn - Runlist->CurrentVcn + Runlist->CurrentLcn) * ClusterSize,
529 (UINTN)(SpareClusters * ClusterSize),
530 Dest
531 );
532 if (EFI_ERROR (Status)) {
533 return Status;
534 }
535
536 Dest += SpareClusters * ClusterSize;
537 mBufferSize -= SpareClusters * ClusterSize;
538 }
539
540 Runlist->TargetVcn += SpareClusters;
541 }
542 }
543 }
544
545 return EFI_SUCCESS;
546}
547
548EFI_STATUS
550 IN RUNLIST *Runlist,
551 IN UINT64 Offset,
552 IN UINTN Length,
553 OUT UINT8 *Dest
554 )
555{
556 EFI_STATUS Status;
557 UINT64 Vcn;
558 UINT64 Target;
559 UINTN SpareBytes;
560 UINTN Residual;
561 UINT64 BlocksPerCluster;
562 UINT64 ClustersPerBlock;
563 UINTN ClusterSize;
564
565 ASSERT (Runlist != NULL);
566 ASSERT (Dest != NULL);
567
568 ClusterSize = Runlist->Unit.FileSystem->ClusterSize;
569
570 if (Runlist->Unit.ClearTextBlock != NULL) {
571 if ((Offset & (~(COMPRESSION_BLOCK - 1U))) == Runlist->Unit.SavedPosition) {
572 Residual = (UINTN)(COMPRESSION_BLOCK - (Offset - Runlist->Unit.SavedPosition));
573 if (Residual > Length) {
574 Residual = Length;
575 }
576
577 CopyMem (Dest, Runlist->Unit.ClearTextBlock + Offset - Runlist->Unit.SavedPosition, Residual);
578 if (Residual == Length) {
579 return EFI_SUCCESS;
580 }
581
582 Dest += Residual;
583 Length -= Residual;
584 Offset += Residual;
585 }
586 } else {
587 Runlist->Unit.ClearTextBlock = AllocateZeroPool (COMPRESSION_BLOCK);
588 if (Runlist->Unit.ClearTextBlock == NULL) {
589 return EFI_OUT_OF_RESOURCES;
590 }
591
592 Runlist->Unit.SavedPosition = 1U;
593 }
594
595 Vcn = Runlist->TargetVcn;
596 Runlist->TargetVcn &= ~(mUnitSize - 1U);
597 while (Runlist->NextVcn <= Runlist->TargetVcn) {
598 Status = ReadRunListElement (Runlist);
599 if (EFI_ERROR (Status)) {
600 FreePool (Runlist->Unit.ClearTextBlock);
601 return Status;
602 }
603 }
604
605 Runlist->Unit.Head = Runlist->Unit.Tail = 0;
606 Runlist->Unit.Cluster = AllocateZeroPool (ClusterSize);
607 if (Runlist->Unit.Cluster == NULL) {
608 FreePool (Runlist->Unit.ClearTextBlock);
609 return EFI_OUT_OF_RESOURCES;
610 }
611
612 if (Vcn > Runlist->TargetVcn) {
613 if (ClusterSize >= COMPRESSION_BLOCK) {
614 BlocksPerCluster = DivU64x64Remainder (ClusterSize, COMPRESSION_BLOCK, NULL);
615 Status = ReadCompressedBlock (
616 Runlist,
617 NULL,
618 (UINTN)((Vcn - Runlist->TargetVcn) * BlocksPerCluster)
619 );
620 } else {
621 ClustersPerBlock = DivU64x64Remainder (COMPRESSION_BLOCK, ClusterSize, NULL);
622 Status = ReadCompressedBlock (
623 Runlist,
624 NULL,
625 (UINTN)DivU64x64Remainder (Vcn - Runlist->TargetVcn, ClustersPerBlock, NULL)
626 );
627 }
628
629 if (EFI_ERROR (Status)) {
630 FreePool (Runlist->Unit.ClearTextBlock);
631 FreePool (Runlist->Unit.Cluster);
632 return Status;
633 }
634 }
635
636 if ((Offset % COMPRESSION_BLOCK) != 0) {
637 Target = Runlist->TargetVcn * ClusterSize;
638
639 Status = ReadCompressedBlock (Runlist, Runlist->Unit.ClearTextBlock, 1U);
640 if (EFI_ERROR (Status)) {
641 FreePool (Runlist->Unit.ClearTextBlock);
642 FreePool (Runlist->Unit.Cluster);
643 return Status;
644 }
645
646 Runlist->Unit.SavedPosition = Target;
647
648 Residual = (UINTN)(Offset % COMPRESSION_BLOCK);
649 SpareBytes = COMPRESSION_BLOCK - Residual;
650 if (SpareBytes > Length) {
651 SpareBytes = Length;
652 }
653
654 CopyMem (Dest, &Runlist->Unit.ClearTextBlock[Residual], SpareBytes);
655 if (SpareBytes == Length) {
656 FreePool (Runlist->Unit.ClearTextBlock);
657 FreePool (Runlist->Unit.Cluster);
658 return EFI_SUCCESS;
659 }
660
661 Dest += SpareBytes;
662 Length -= SpareBytes;
663 }
664
665 Status = ReadCompressedBlock (Runlist, Dest, Length / COMPRESSION_BLOCK);
666 if (EFI_ERROR (Status)) {
667 FreePool (Runlist->Unit.ClearTextBlock);
668 FreePool (Runlist->Unit.Cluster);
669 return Status;
670 }
671
674 if (Length != 0) {
675 Target = Runlist->TargetVcn * ClusterSize;
676
677 Status = ReadCompressedBlock (Runlist, Runlist->Unit.ClearTextBlock, 1U);
678 if (EFI_ERROR (Status)) {
679 FreePool (Runlist->Unit.ClearTextBlock);
680 FreePool (Runlist->Unit.Cluster);
681 return Status;
682 }
683
684 Runlist->Unit.SavedPosition = Target;
685
686 CopyMem (Dest, Runlist->Unit.ClearTextBlock, Length);
687 }
688
689 FreePool (Runlist->Unit.ClearTextBlock);
690 FreePool (Runlist->Unit.Cluster);
691
692 return EFI_SUCCESS;
693}
UINT64 Length
#define BLOCK_LENGTH_BITS
Definition Compression.c:13
STATIC EFI_STATUS GetNextCluster(IN OUT COMPRESSED *Clusters)
Definition Compression.c:21
STATIC UINT64 mBufferSize
Definition Compression.c:17
#define IS_COMPRESSED_BLOCK
Definition Compression.c:12
STATIC EFI_STATUS ReadCompressedBlock(IN RUNLIST *Runlist, OUT UINT8 *Dest OPTIONAL, IN UINTN BlocksTotal)
#define UNIT_MASK
Definition Compression.c:14
STATIC EFI_STATUS DecompressBlock(IN COMPRESSED *Clusters, OUT UINT8 *Dest OPTIONAL)
UINT64 mUnitSize
Definition Data.c:12
STATIC EFI_STATUS GetTwoDataRunBytes(IN COMPRESSED *Clusters, OUT UINT16 *Result)
Definition Compression.c:82
EFI_STATUS Decompress(IN RUNLIST *Runlist, IN UINT64 Offset, IN UINTN Length, OUT UINT8 *Dest)
STATIC EFI_STATUS GetDataRunByte(IN COMPRESSED *Clusters, OUT UINT8 *Result)
Definition Compression.c:58
EFI_STATUS EFIAPI DiskRead(IN EFI_FS *FileSystem, IN UINT64 Offset, IN UINTN Size, IN OUT VOID *Buffer)
Definition Data.c:116
EFI_STATUS EFIAPI ReadRunListElement(IN OUT RUNLIST *Runlist)
Definition Data.c:468
#define COMPRESSION_BLOCK
Definition Driver.h:14
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
UINT64 EFIAPI DivU64x64Remainder(IN UINT64 Dividend, IN UINT64 Divisor, OUT UINT64 *Remainder OPTIONAL)
Definition UserMath.c:59
#define ASSERT(x)
Definition coder.h:55