fixed typo
[reactos.git] / reactos / subsys / system / usetup / cabinet.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS text-mode setup
4 * FILE: subsys/system/usetup/cabinet.c
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISIONS:
8 * CSH 15/08-2003 Created
9 */
10 #include <ntos.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <zlib.h>
14 #include "cabinet.h"
15 #include "usetup.h"
16
17 #define NDEBUG
18 #include <debug.h>
19
20 #define SEEK_BEGIN 0
21 #define SEEK_CURRENT 1
22 #ifndef SEEK_END
23 #define SEEK_END 2
24 #endif
25
26 typedef struct __DOSTIME
27 {
28 WORD Second:5;
29 WORD Minute:6;
30 WORD Hour:5;
31 } DOSTIME, *PDOSTIME;
32
33
34 typedef struct __DOSDATE
35 {
36 WORD Day:5;
37 WORD Month:4;
38 WORD Year:5;
39 } DOSDATE, *PDOSDATE;
40
41 static WCHAR CabinetName[256]; // Filename of current cabinet
42 static WCHAR CabinetPrev[256]; // Filename of previous cabinet
43 static WCHAR DiskPrev[256]; // Label of cabinet in file CabinetPrev
44 static WCHAR CabinetNext[256]; // Filename of next cabinet
45 static WCHAR DiskNext[256]; // Label of cabinet in file CabinetNext
46 static ULONG FolderUncompSize = 0; // Uncompressed size of folder
47 static ULONG BytesLeftInBlock = 0; // Number of bytes left in current block
48 static BOOL ReuseBlock = FALSE;
49 static WCHAR DestPath[MAX_PATH];
50 static HANDLE FileHandle;
51 static BOOL FileOpen = FALSE;
52 static CFHEADER CABHeader;
53 static ULONG CabinetReserved = 0;
54 static ULONG FolderReserved = 0;
55 static ULONG DataReserved = 0;
56 static PCFFOLDER_NODE FolderListHead = NULL;
57 static PCFFOLDER_NODE FolderListTail = NULL;
58 static PCFFOLDER_NODE CurrentFolderNode = NULL;
59 static PCFDATA_NODE CurrentDataNode = NULL;
60 static PCFFILE_NODE FileListHead = NULL;
61 static PCFFILE_NODE FileListTail = NULL;
62 static ULONG CodecId;
63 static PCABINET_CODEC_UNCOMPRESS CodecUncompress = NULL;
64 static BOOL CodecSelected = FALSE;
65 static PVOID InputBuffer = NULL;
66 static PVOID CurrentIBuffer = NULL; // Current offset in input buffer
67 static ULONG CurrentIBufferSize = 0; // Bytes left in input buffer
68 static PVOID OutputBuffer = NULL;
69 static PVOID CurrentOBuffer = NULL; // Current offset in output buffer
70 static ULONG CurrentOBufferSize = 0; // Bytes left in output buffer
71 static BOOL RestartSearch = FALSE;
72 static ULONG LastFileOffset = 0; // Uncompressed offset of last extracted file
73 static PCABINET_OVERWRITE OverwriteHandler = NULL;
74 static PCABINET_EXTRACT ExtractHandler = NULL;
75 static PCABINET_DISK_CHANGE DiskChangeHandler = NULL;
76 static z_stream ZStream;
77 static PVOID CabinetReservedArea = NULL;
78
79
80 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */
81
82 void free(void* _ptr)
83 {
84 RtlFreeHeap(ProcessHeap, 0, _ptr);
85 }
86
87 void* calloc(size_t _nmemb, size_t _size)
88 {
89 return (void*)RtlAllocateHeap (ProcessHeap, HEAP_ZERO_MEMORY, _size);
90 }
91
92 /* RAW codec */
93
94 ULONG
95 RawCodecUncompress(PVOID OutputBuffer,
96 PVOID InputBuffer,
97 ULONG InputLength,
98 PULONG OutputLength)
99 /*
100 * FUNCTION: Uncompresses data in a buffer
101 * ARGUMENTS:
102 * OutputBuffer = Pointer to buffer to place uncompressed data
103 * InputBuffer = Pointer to buffer with data to be uncompressed
104 * InputLength = Length of input buffer
105 * OutputLength = Address of buffer to place size of uncompressed data
106 */
107 {
108 memcpy(OutputBuffer, InputBuffer, InputLength);
109 *OutputLength = InputLength;
110 return CS_SUCCESS;
111 }
112
113
114 /* MSZIP codec */
115
116 ULONG
117 MSZipCodecUncompress(PVOID OutputBuffer,
118 PVOID InputBuffer,
119 ULONG InputLength,
120 PULONG OutputLength)
121 /*
122 * FUNCTION: Uncompresses data in a buffer
123 * ARGUMENTS:
124 * OutputBuffer = Pointer to buffer to place uncompressed data
125 * InputBuffer = Pointer to buffer with data to be uncompressed
126 * InputLength = Length of input buffer
127 * OutputLength = Address of buffer to place size of uncompressed data
128 */
129 {
130 USHORT Magic;
131 INT Status;
132
133 DPRINT("InputLength (%d).\n", InputLength);
134
135 Magic = *((PUSHORT)InputBuffer);
136
137 if (Magic != MSZIP_MAGIC)
138 {
139 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic);
140 return CS_BADSTREAM;
141 }
142
143 ZStream.next_in = (PUCHAR)((ULONG)InputBuffer + 2);
144 ZStream.avail_in = InputLength - 2;
145 ZStream.next_out = (PUCHAR)OutputBuffer;
146 ZStream.avail_out = CAB_BLOCKSIZE + 12;
147
148 /* WindowBits is passed < 0 to tell that there is no zlib header.
149 * Note that in this case inflate *requires* an extra "dummy" byte
150 * after the compressed stream in order to complete decompression and
151 * return Z_STREAM_END.
152 */
153 Status = inflateInit2(&ZStream, -MAX_WBITS);
154 if (Status != Z_OK)
155 {
156 DPRINT("inflateInit2() returned (%d).\n", Status);
157 return CS_BADSTREAM;
158 }
159
160 while ((ZStream.total_out < CAB_BLOCKSIZE + 12) &&
161 (ZStream.total_in < InputLength - 2))
162 {
163 Status = inflate(&ZStream, Z_NO_FLUSH);
164 if (Status == Z_STREAM_END) break;
165 if (Status != Z_OK)
166 {
167 DPRINT("inflate() returned (%d) (%s).\n", Status, ZStream.msg);
168 if (Status == Z_MEM_ERROR)
169 return CS_NOMEMORY;
170 return CS_BADSTREAM;
171 }
172 }
173
174 *OutputLength = ZStream.total_out;
175
176 Status = inflateEnd(&ZStream);
177 if (Status != Z_OK)
178 {
179 DPRINT("inflateEnd() returned (%d).\n", Status);
180 return CS_BADSTREAM;
181 }
182 return CS_SUCCESS;
183 }
184
185
186
187 /* Memory functions */
188
189 voidpf MSZipAlloc(voidpf opaque, uInt items, uInt size)
190 {
191 return (voidpf)RtlAllocateHeap (ProcessHeap, 0, items * size);
192 }
193
194 void MSZipFree (voidpf opaque, voidpf address)
195 {
196 RtlFreeHeap(ProcessHeap, 0, address);
197 }
198
199
200 static DWORD
201 SeekInFile(HANDLE hFile,
202 LONG lDistanceToMove,
203 PLONG lpDistanceToMoveHigh,
204 DWORD dwMoveMethod,
205 PNTSTATUS Status)
206 {
207 FILE_POSITION_INFORMATION FilePosition;
208 FILE_STANDARD_INFORMATION FileStandard;
209 NTSTATUS errCode;
210 IO_STATUS_BLOCK IoStatusBlock;
211 LARGE_INTEGER Distance;
212
213 DPRINT("SeekInFile(hFile %x, lDistanceToMove %d, dwMoveMethod %d)\n",
214 hFile,lDistanceToMove,dwMoveMethod);
215
216 Distance.u.LowPart = lDistanceToMove;
217 if (lpDistanceToMoveHigh)
218 {
219 Distance.u.HighPart = *lpDistanceToMoveHigh;
220 }
221 else if (lDistanceToMove >= 0)
222 {
223 Distance.u.HighPart = 0;
224 }
225 else
226 {
227 Distance.u.HighPart = -1;
228 }
229
230 if (dwMoveMethod == SEEK_CURRENT)
231 {
232 NtQueryInformationFile(hFile,
233 &IoStatusBlock,
234 &FilePosition,
235 sizeof(FILE_POSITION_INFORMATION),
236 FilePositionInformation);
237 FilePosition.CurrentByteOffset.QuadPart += Distance.QuadPart;
238 }
239 else if (dwMoveMethod == SEEK_END)
240 {
241 NtQueryInformationFile(hFile,
242 &IoStatusBlock,
243 &FileStandard,
244 sizeof(FILE_STANDARD_INFORMATION),
245 FileStandardInformation);
246 FilePosition.CurrentByteOffset.QuadPart =
247 FileStandard.EndOfFile.QuadPart + Distance.QuadPart;
248 }
249 else if ( dwMoveMethod == SEEK_BEGIN )
250 {
251 FilePosition.CurrentByteOffset.QuadPart = Distance.QuadPart;
252 }
253
254 // DPRINT1("GOTO FILE OFFSET: %I64d\n", FilePosition.CurrentByteOffset.QuadPart);
255
256 errCode = NtSetInformationFile(hFile,
257 &IoStatusBlock,
258 &FilePosition,
259 sizeof(FILE_POSITION_INFORMATION),
260 FilePositionInformation);
261 if (!NT_SUCCESS(errCode))
262 {
263 if (Status != NULL)
264 {
265 *Status = errCode;
266 }
267 return -1;
268 }
269
270 if (lpDistanceToMoveHigh != NULL)
271 {
272 *lpDistanceToMoveHigh = FilePosition.CurrentByteOffset.u.HighPart;
273 }
274 if (Status != NULL)
275 {
276 *Status = STATUS_SUCCESS;
277 }
278 return FilePosition.CurrentByteOffset.u.LowPart;
279 }
280
281
282 static BOOL
283 ConvertSystemTimeToFileTime(
284 CONST SYSTEMTIME * lpSystemTime,
285 LPFILETIME lpFileTime)
286 {
287 TIME_FIELDS TimeFields;
288 LARGE_INTEGER liTime;
289
290 TimeFields.Year = lpSystemTime->wYear;
291 TimeFields.Month = lpSystemTime->wMonth;
292 TimeFields.Day = lpSystemTime->wDay;
293 TimeFields.Hour = lpSystemTime->wHour;
294 TimeFields.Minute = lpSystemTime->wMinute;
295 TimeFields.Second = lpSystemTime->wSecond;
296 TimeFields.Milliseconds = lpSystemTime->wMilliseconds;
297
298 if (RtlTimeFieldsToTime(&TimeFields, &liTime))
299 {
300 lpFileTime->dwLowDateTime = liTime.u.LowPart;
301 lpFileTime->dwHighDateTime = liTime.u.HighPart;
302 return TRUE;
303 }
304 return FALSE;
305 }
306
307
308 static BOOL
309 ConvertDosDateTimeToFileTime(
310 WORD wFatDate,
311 WORD wFatTime,
312 LPFILETIME lpFileTime)
313 {
314 PDOSTIME pdtime = (PDOSTIME) &wFatTime;
315 PDOSDATE pddate = (PDOSDATE) &wFatDate;
316 SYSTEMTIME SystemTime;
317
318 if (lpFileTime == NULL)
319 return FALSE;
320
321 SystemTime.wMilliseconds = 0;
322 SystemTime.wSecond = pdtime->Second;
323 SystemTime.wMinute = pdtime->Minute;
324 SystemTime.wHour = pdtime->Hour;
325
326 SystemTime.wDay = pddate->Day;
327 SystemTime.wMonth = pddate->Month;
328 SystemTime.wYear = 1980 + pddate->Year;
329
330 ConvertSystemTimeToFileTime(&SystemTime,lpFileTime);
331
332 return TRUE;
333 }
334
335
336 static PWCHAR
337 GetFileName(PWCHAR Path)
338 /*
339 * FUNCTION: Returns a pointer to file name
340 * ARGUMENTS:
341 * Path = Pointer to string with pathname
342 * RETURNS:
343 * Pointer to filename
344 */
345 {
346 ULONG i, j;
347
348 j = i = 0;
349
350 while (Path [i++])
351 {
352 if (Path[i - 1] == L'\\') j = i;
353 }
354 return Path + j;
355 }
356
357
358 static VOID
359 RemoveFileName(PWCHAR Path)
360 /*
361 * FUNCTION: Removes a file name from a path
362 * ARGUMENTS:
363 * Path = Pointer to string with path
364 */
365 {
366 PWCHAR FileName;
367 DWORD i;
368
369 i = 0;
370 FileName = GetFileName(Path + i);
371
372 if ((FileName != (Path + i)) && (FileName [-1] == L'\\'))
373 FileName--;
374 if ((FileName == (Path + i)) && (FileName [0] == L'\\'))
375 FileName++;
376 FileName[0] = 0;
377 }
378
379
380 static BOOL
381 SetAttributesOnFile(PCFFILE_NODE File, HANDLE hFile)
382 /*
383 * FUNCTION: Sets attributes on a file
384 * ARGUMENTS:
385 * File = Pointer to CFFILE node for file
386 * RETURNS:
387 * Status of operation
388 */
389 {
390 FILE_BASIC_INFORMATION FileBasic;
391 IO_STATUS_BLOCK IoStatusBlock;
392 NTSTATUS NtStatus;
393 ULONG Attributes = 0;
394
395 if (File->File.Attributes & CAB_ATTRIB_READONLY)
396 Attributes |= FILE_ATTRIBUTE_READONLY;
397
398 if (File->File.Attributes & CAB_ATTRIB_HIDDEN)
399 Attributes |= FILE_ATTRIBUTE_HIDDEN;
400
401 if (File->File.Attributes & CAB_ATTRIB_SYSTEM)
402 Attributes |= FILE_ATTRIBUTE_SYSTEM;
403
404 if (File->File.Attributes & CAB_ATTRIB_DIRECTORY)
405 Attributes |= FILE_ATTRIBUTE_DIRECTORY;
406
407 if (File->File.Attributes & CAB_ATTRIB_ARCHIVE)
408 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
409
410 NtStatus = NtQueryInformationFile(hFile,
411 &IoStatusBlock,
412 &FileBasic,
413 sizeof(FILE_BASIC_INFORMATION),
414 FileBasicInformation);
415 if (!NT_SUCCESS(NtStatus))
416 {
417 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus);
418 }
419 else
420 {
421 FileBasic.FileAttributes = Attributes;
422
423 NtStatus = NtSetInformationFile(hFile,
424 &IoStatusBlock,
425 &FileBasic,
426 sizeof(FILE_BASIC_INFORMATION),
427 FileBasicInformation);
428 if (!NT_SUCCESS(NtStatus))
429 {
430 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus);
431 }
432 }
433
434 return NT_SUCCESS(NtStatus);
435 }
436
437
438 static ULONG
439 ReadBlock(PVOID Buffer,
440 ULONG Size,
441 PULONG BytesRead)
442 /*
443 * FUNCTION: Read a block of data from file
444 * ARGUMENTS:
445 * Buffer = Pointer to data buffer
446 * Size = Length of data buffer
447 * BytesRead = Pointer to ULONG that on return will contain
448 * number of bytes read
449 * RETURNS:
450 * Status of operation
451 */
452 {
453 IO_STATUS_BLOCK IoStatusBlock;
454 NTSTATUS NtStatus;
455
456 NtStatus = NtReadFile(FileHandle,
457 NULL,
458 NULL,
459 NULL,
460 &IoStatusBlock,
461 Buffer,
462 Size,
463 NULL,
464 NULL);
465 if (!NT_SUCCESS(NtStatus))
466 {
467 DPRINT("ReadBlock for %d bytes failed (%x)\n", Size, NtStatus);
468 *BytesRead = 0;
469 return CAB_STATUS_INVALID_CAB;
470 }
471 *BytesRead = Size;
472 return CAB_STATUS_SUCCESS;
473 }
474
475
476 static PCFFOLDER_NODE
477 NewFolderNode()
478 /*
479 * FUNCTION: Creates a new folder node
480 * RETURNS:
481 * Pointer to node if there was enough free memory available, otherwise NULL
482 */
483 {
484 PCFFOLDER_NODE Node;
485
486 Node = (PCFFOLDER_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFOLDER_NODE));
487 if (!Node)
488 return NULL;
489
490 RtlZeroMemory(Node, sizeof(CFFOLDER_NODE));
491
492 Node->Folder.CompressionType = CAB_COMP_NONE;
493
494 Node->Prev = FolderListTail;
495
496 if (FolderListTail != NULL)
497 {
498 FolderListTail->Next = Node;
499 }
500 else
501 {
502 FolderListHead = Node;
503 }
504 FolderListTail = Node;
505
506 return Node;
507 }
508
509
510 static PCFFILE_NODE
511 NewFileNode()
512 /*
513 * FUNCTION: Creates a new file node
514 * ARGUMENTS:
515 * FolderNode = Pointer to folder node to bind file to
516 * RETURNS:
517 * Pointer to node if there was enough free memory available, otherwise NULL
518 */
519 {
520 PCFFILE_NODE Node;
521
522 Node = (PCFFILE_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFFILE_NODE));
523 if (!Node)
524 return NULL;
525
526 RtlZeroMemory(Node, sizeof(CFFILE_NODE));
527
528 Node->Prev = FileListTail;
529
530 if (FileListTail != NULL)
531 {
532 FileListTail->Next = Node;
533 }
534 else
535 {
536 FileListHead = Node;
537 }
538 FileListTail = Node;
539
540 return Node;
541 }
542
543
544 static PCFDATA_NODE
545 NewDataNode(PCFFOLDER_NODE FolderNode)
546 /*
547 * FUNCTION: Creates a new data block node
548 * ARGUMENTS:
549 * FolderNode = Pointer to folder node to bind data block to
550 * RETURNS:
551 * Pointer to node if there was enough free memory available, otherwise NULL
552 */
553 {
554 PCFDATA_NODE Node;
555
556 Node = (PCFDATA_NODE)RtlAllocateHeap (ProcessHeap, 0, sizeof(CFDATA_NODE));
557 if (!Node)
558 return NULL;
559
560 RtlZeroMemory(Node, sizeof(CFDATA_NODE));
561
562 Node->Prev = FolderNode->DataListTail;
563
564 if (FolderNode->DataListTail != NULL)
565 {
566 FolderNode->DataListTail->Next = Node;
567 }
568 else
569 {
570 FolderNode->DataListHead = Node;
571 }
572 FolderNode->DataListTail = Node;
573
574 return Node;
575 }
576
577
578 static VOID
579 DestroyDataNodes(PCFFOLDER_NODE FolderNode)
580 /*
581 * FUNCTION: Destroys data block nodes bound to a folder node
582 * ARGUMENTS:
583 * FolderNode = Pointer to folder node
584 */
585 {
586 PCFDATA_NODE PrevNode;
587 PCFDATA_NODE NextNode;
588
589 NextNode = FolderNode->DataListHead;
590 while (NextNode != NULL)
591 {
592 PrevNode = NextNode->Next;
593 RtlFreeHeap(ProcessHeap, 0, NextNode);
594 NextNode = PrevNode;
595 }
596 FolderNode->DataListHead = NULL;
597 FolderNode->DataListTail = NULL;
598 }
599
600
601 static VOID
602 DestroyFileNodes()
603 /*
604 * FUNCTION: Destroys file nodes
605 * ARGUMENTS:
606 * FolderNode = Pointer to folder node
607 */
608 {
609 PCFFILE_NODE PrevNode;
610 PCFFILE_NODE NextNode;
611
612 NextNode = FileListHead;
613 while (NextNode != NULL)
614 {
615 PrevNode = NextNode->Next;
616 if (NextNode->FileName)
617 RtlFreeHeap(ProcessHeap, 0, NextNode->FileName);
618 RtlFreeHeap(ProcessHeap, 0, NextNode);
619 NextNode = PrevNode;
620 }
621 FileListHead = NULL;
622 FileListTail = NULL;
623 }
624
625
626 #if 0
627 static VOID
628 DestroyDeletedFileNodes()
629 /*
630 * FUNCTION: Destroys file nodes that are marked for deletion
631 */
632 {
633 PCFFILE_NODE CurNode;
634 PCFFILE_NODE NextNode;
635
636 CurNode = FileListHead;
637 while (CurNode != NULL)
638 {
639 NextNode = CurNode->Next;
640
641 if (CurNode->Delete)
642 {
643 if (CurNode->Prev != NULL)
644 {
645 CurNode->Prev->Next = CurNode->Next;
646 }
647 else
648 {
649 FileListHead = CurNode->Next;
650 if (FileListHead)
651 FileListHead->Prev = NULL;
652 }
653
654 if (CurNode->Next != NULL)
655 {
656 CurNode->Next->Prev = CurNode->Prev;
657 }
658 else
659 {
660 FileListTail = CurNode->Prev;
661 if (FileListTail)
662 FileListTail->Next = NULL;
663 }
664
665 DPRINT("Deleting file: '%S'\n", CurNode->FileName);
666
667 if (CurNode->FileName)
668 RtlFreeHeap(ProcessHeap, 0, CurNode->FileName);
669 RtlFreeHeap(ProcessHeap, 0, CurNode);
670 }
671 CurNode = NextNode;
672 }
673 }
674 #endif
675
676 static VOID
677 DestroyFolderNodes()
678 /*
679 * FUNCTION: Destroys folder nodes
680 */
681 {
682 PCFFOLDER_NODE PrevNode;
683 PCFFOLDER_NODE NextNode;
684
685 NextNode = FolderListHead;
686 while (NextNode != NULL)
687 {
688 PrevNode = NextNode->Next;
689 DestroyDataNodes(NextNode);
690 RtlFreeHeap(ProcessHeap, 0, NextNode);
691 NextNode = PrevNode;
692 }
693 FolderListHead = NULL;
694 FolderListTail = NULL;
695 }
696
697 #if 0
698 static VOID
699 DestroyDeletedFolderNodes()
700 /*
701 * FUNCTION: Destroys folder nodes that are marked for deletion
702 */
703 {
704 PCFFOLDER_NODE CurNode;
705 PCFFOLDER_NODE NextNode;
706
707 CurNode = FolderListHead;
708 while (CurNode != NULL)
709 {
710 NextNode = CurNode->Next;
711
712 if (CurNode->Delete)
713 {
714 if (CurNode->Prev != NULL)
715 {
716 CurNode->Prev->Next = CurNode->Next;
717 }
718 else
719 {
720 FolderListHead = CurNode->Next;
721 if (FolderListHead)
722 FolderListHead->Prev = NULL;
723 }
724
725 if (CurNode->Next != NULL)
726 {
727 CurNode->Next->Prev = CurNode->Prev;
728 }
729 else
730 {
731 FolderListTail = CurNode->Prev;
732 if (FolderListTail)
733 FolderListTail->Next = NULL;
734 }
735
736 DestroyDataNodes(CurNode);
737 RtlFreeHeap(ProcessHeap, 0, CurNode);
738 }
739 CurNode = NextNode;
740 }
741 }
742 #endif
743
744 static PCFFOLDER_NODE
745 LocateFolderNode(ULONG Index)
746 /*
747 * FUNCTION: Locates a folder node
748 * ARGUMENTS:
749 * Index = Folder index
750 * RETURNS:
751 * Pointer to folder node or NULL if the folder node was not found
752 */
753 {
754 PCFFOLDER_NODE Node;
755
756 switch (Index)
757 {
758 case CAB_FILE_SPLIT:
759 return FolderListTail;
760
761 case CAB_FILE_CONTINUED:
762 case CAB_FILE_PREV_NEXT:
763 return FolderListHead;
764 }
765
766 Node = FolderListHead;
767 while (Node != NULL)
768 {
769 if (Node->Index == Index)
770 return Node;
771 Node = Node->Next;
772 }
773 return NULL;
774 }
775
776
777 static ULONG
778 GetAbsoluteOffset(PCFFILE_NODE File)
779 /*
780 * FUNCTION: Returns the absolute offset of a file
781 * ARGUMENTS:
782 * File = Pointer to CFFILE_NODE structure for file
783 * RETURNS:
784 * Status of operation
785 */
786 {
787 PCFDATA_NODE Node;
788
789 DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n",
790 (PWCHAR)File->FileName,
791 (UINT)File->File.FileOffset,
792 (UINT)File->File.FileSize);
793
794 Node = CurrentFolderNode->DataListHead;
795 while (Node != NULL)
796 {
797 DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
798 (UINT)Node->UncompOffset,
799 (UINT)Node->UncompOffset + Node->Data.UncompSize,
800 (UINT)Node->Data.UncompSize);
801
802 /* Node->Data.UncompSize will be 0 if the block is split
803 (ie. it is the last block in this cabinet) */
804 if ((Node->Data.UncompSize == 0) ||
805 ((File->File.FileOffset >= Node->UncompOffset) &&
806 (File->File.FileOffset < Node->UncompOffset +
807 Node->Data.UncompSize)))
808 {
809 File->DataBlock = Node;
810 return CAB_STATUS_SUCCESS;
811 }
812
813 Node = Node->Next;
814 }
815 return CAB_STATUS_INVALID_CAB;
816 }
817
818
819 static ULONG
820 LocateFile(PWCHAR FileName,
821 PCFFILE_NODE *File)
822 /*
823 * FUNCTION: Locates a file in the cabinet
824 * ARGUMENTS:
825 * FileName = Pointer to string with name of file to locate
826 * File = Address of pointer to CFFILE_NODE structure to fill
827 * RETURNS:
828 * Status of operation
829 * NOTES:
830 * Current folder is set to the folder of the file
831 */
832 {
833 PCFFILE_NODE Node;
834 ULONG Status;
835
836 DPRINT("FileName '%S'\n", FileName);
837
838 Node = FileListHead;
839 while (Node != NULL)
840 {
841 if (_wcsicmp(FileName, Node->FileName) == 0)
842 {
843 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
844 if (!CurrentFolderNode)
845 {
846 DPRINT("Folder with index number (%d) not found.\n",
847 (UINT)Node->File.FileControlID);
848 return CAB_STATUS_INVALID_CAB;
849 }
850
851 if (Node->DataBlock == NULL)
852 {
853 Status = GetAbsoluteOffset(Node);
854 }
855 else
856 Status = CAB_STATUS_SUCCESS;
857 *File = Node;
858 return Status;
859 }
860 Node = Node->Next;
861 }
862 return CAB_STATUS_NOFILE;
863 }
864
865
866 static ULONG
867 ReadString(PWCHAR String, ULONG MaxLength)
868 /*
869 * FUNCTION: Reads a NULL-terminated string from the cabinet
870 * ARGUMENTS:
871 * String = Pointer to buffer to place string
872 * MaxLength = Maximum length of string
873 * RETURNS:
874 * Status of operation
875 */
876 {
877 NTSTATUS NtStatus;
878 ULONG BytesRead;
879 ULONG Offset;
880 ULONG Status;
881 ULONG Size;
882 BOOL Found;
883 CHAR buf[MAX_PATH];
884 ANSI_STRING as;
885 UNICODE_STRING us;
886
887 Offset = 0;
888 Found = FALSE;
889 do
890 {
891 Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset;
892
893 if (Size == 0)
894 {
895 DPRINT("Too long a filename.\n");
896 return CAB_STATUS_INVALID_CAB;
897 }
898
899 Status = ReadBlock((PCFDATA)&buf[Offset], Size, &BytesRead);
900 if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size))
901 {
902 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
903 return CAB_STATUS_INVALID_CAB;
904 }
905
906 for (Size = Offset; Size < Offset + BytesRead; Size++)
907 {
908 if (buf[Size] == '\0')
909 {
910 Found = TRUE;
911 break;
912 }
913 }
914
915 Offset += BytesRead;
916 } while (!Found);
917
918 /* Back up some bytes */
919 Size = (BytesRead - Size) - 1;
920 SeekInFile(FileHandle, -(LONG)Size, NULL, SEEK_CURRENT, &NtStatus);
921 if (!NT_SUCCESS(NtStatus))
922 {
923 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
924 return CAB_STATUS_INVALID_CAB;
925 }
926
927 RtlInitAnsiString(&as, buf);
928 us.Buffer = String;
929 us.MaximumLength = MaxLength * sizeof(WCHAR);
930 us.Length = 0;
931
932 RtlAnsiStringToUnicodeString(&us, &as, FALSE);
933
934 return CAB_STATUS_SUCCESS;
935 }
936
937
938 static ULONG
939 ReadFileTable(VOID)
940 /*
941 * FUNCTION: Reads the file table from the cabinet file
942 * RETURNS:
943 * Status of operation
944 */
945 {
946 ULONG i;
947 ULONG Status;
948 ULONG BytesRead;
949 PCFFILE_NODE File;
950 NTSTATUS NtStatus;
951
952 DPRINT("Reading file table at absolute offset (0x%X).\n",
953 CABHeader.FileTableOffset);
954
955 /* Seek to file table */
956 SeekInFile(FileHandle, CABHeader.FileTableOffset, NULL, SEEK_BEGIN, &NtStatus);
957 if (!NT_SUCCESS(NtStatus))
958 {
959 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
960 return CAB_STATUS_INVALID_CAB;
961 }
962
963 for (i = 0; i < CABHeader.FileCount; i++)
964 {
965 File = NewFileNode();
966 if (!File)
967 {
968 DPRINT("Insufficient memory.\n");
969 return CAB_STATUS_NOMEMORY;
970 }
971
972 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
973 &BytesRead)) != CAB_STATUS_SUCCESS) {
974 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
975 return CAB_STATUS_INVALID_CAB;
976 }
977
978 File->FileName = (PWCHAR)RtlAllocateHeap(ProcessHeap, 0, MAX_PATH * sizeof(WCHAR));
979 if (!File->FileName)
980 {
981 DPRINT("Insufficient memory.\n");
982 return CAB_STATUS_NOMEMORY;
983 }
984
985 /* Read file name */
986 Status = ReadString(File->FileName, MAX_PATH);
987 if (Status != CAB_STATUS_SUCCESS)
988 return Status;
989
990 DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
991 (PWCHAR)File->FileName,
992 (UINT)File->File.FileOffset,
993 (UINT)File->File.FileSize,
994 (UINT)File->File.FileControlID);
995 }
996 return CAB_STATUS_SUCCESS;
997 }
998
999
1000 static ULONG
1001 ReadDataBlocks(PCFFOLDER_NODE FolderNode)
1002 /*
1003 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1004 * ARGUMENTS:
1005 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1006 * RETURNS:
1007 * Status of operation
1008 */
1009 {
1010 ULONG AbsoluteOffset;
1011 ULONG UncompOffset;
1012 PCFDATA_NODE Node;
1013 NTSTATUS NtStatus;
1014 ULONG BytesRead;
1015 ULONG Status;
1016 ULONG i;
1017
1018 DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1019 FolderNode->Index, FolderNode->Folder.DataOffset);
1020
1021 AbsoluteOffset = FolderNode->Folder.DataOffset;
1022 UncompOffset = FolderNode->UncompOffset;
1023
1024 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++)
1025 {
1026 Node = NewDataNode(FolderNode);
1027 if (!Node)
1028 {
1029 DPRINT("Insufficient memory.\n");
1030 return CAB_STATUS_NOMEMORY;
1031 }
1032
1033 /* Seek to data block */
1034 SeekInFile(FileHandle, AbsoluteOffset, NULL, SEEK_BEGIN, &NtStatus);
1035 if (!NT_SUCCESS(NtStatus))
1036 {
1037 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1038 return CAB_STATUS_INVALID_CAB;
1039 }
1040
1041 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
1042 &BytesRead)) != CAB_STATUS_SUCCESS)
1043 {
1044 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1045 return CAB_STATUS_INVALID_CAB;
1046 }
1047
1048 DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1049 (UINT)AbsoluteOffset,
1050 (UINT)UncompOffset,
1051 (UINT)Node->Data.Checksum,
1052 (UINT)Node->Data.CompSize,
1053 (UINT)Node->Data.UncompSize);
1054
1055 Node->AbsoluteOffset = AbsoluteOffset;
1056 Node->UncompOffset = UncompOffset;
1057
1058 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
1059 UncompOffset += Node->Data.UncompSize;
1060 }
1061
1062 FolderUncompSize = UncompOffset;
1063
1064 return CAB_STATUS_SUCCESS;
1065 }
1066
1067 #if 0
1068 static ULONG
1069 ComputeChecksum(PVOID Buffer,
1070 UINT Size,
1071 ULONG Seed)
1072 /*
1073 * FUNCTION: Computes checksum for data block
1074 * ARGUMENTS:
1075 * Buffer = Pointer to data buffer
1076 * Size = Length of data buffer
1077 * Seed = Previously computed checksum
1078 * RETURNS:
1079 * Checksum of buffer
1080 */
1081 {
1082 INT UlongCount; // Number of ULONGs in block
1083 ULONG Checksum; // Checksum accumulator
1084 PBYTE pb;
1085 ULONG ul;
1086
1087 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
1088 won't accept checksums computed by this routine */
1089
1090 DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT)Buffer, Size);
1091
1092 UlongCount = Size / 4; // Number of ULONGs
1093 Checksum = Seed; // Init checksum
1094 pb = (PBYTE)Buffer; // Start at front of data block
1095
1096 /* Checksum integral multiple of ULONGs */
1097 while (UlongCount-- > 0)
1098 {
1099 /* NOTE: Build ULONG in big/little-endian independent manner */
1100 ul = *pb++; // Get low-order byte
1101 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
1102 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
1103 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
1104
1105 Checksum ^= ul; // Update checksum
1106 }
1107
1108 /* Checksum remainder bytes */
1109 ul = 0;
1110 switch (Size % 4)
1111 {
1112 case 3:
1113 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
1114 case 2:
1115 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
1116 case 1:
1117 ul |= *pb++; // Get low-order byte
1118 default:
1119 break;
1120 }
1121 Checksum ^= ul; // Update checksum
1122
1123 /* Return computed checksum */
1124 return Checksum;
1125 }
1126 #endif
1127
1128 static ULONG
1129 CloseCabinet(VOID)
1130 /*
1131 * FUNCTION: Closes the current cabinet
1132 * RETURNS:
1133 * Status of operation
1134 */
1135 {
1136 DestroyFileNodes();
1137
1138 DestroyFolderNodes();
1139
1140 if (InputBuffer)
1141 {
1142 RtlFreeHeap(ProcessHeap, 0, InputBuffer);
1143 InputBuffer = NULL;
1144 }
1145
1146 if (OutputBuffer)
1147 {
1148 RtlFreeHeap(ProcessHeap, 0, OutputBuffer);
1149 OutputBuffer = NULL;
1150 }
1151
1152 NtClose(FileHandle);
1153 return 0;
1154 }
1155
1156
1157 VOID
1158 CabinetInitialize(VOID)
1159 /*
1160 * FUNCTION: Initialize archiver
1161 */
1162 {
1163 ZStream.zalloc = MSZipAlloc;
1164 ZStream.zfree = MSZipFree;
1165 ZStream.opaque = (voidpf)0;
1166
1167 FileOpen = FALSE;
1168 wcscpy(DestPath, L"");
1169
1170 FolderListHead = NULL;
1171 FolderListTail = NULL;
1172 FileListHead = NULL;
1173 FileListTail = NULL;
1174
1175 CodecId = CAB_CODEC_RAW;
1176 CodecSelected = TRUE;
1177
1178 OutputBuffer = NULL;
1179 CurrentOBuffer = NULL;
1180 CurrentOBufferSize = 0;
1181 InputBuffer = NULL;
1182 CurrentIBuffer = NULL;
1183 CurrentIBufferSize = 0;
1184
1185 FolderUncompSize = 0;
1186 BytesLeftInBlock = 0;
1187 CabinetReserved = 0;
1188 FolderReserved = 0;
1189 DataReserved = 0;
1190 ReuseBlock = FALSE;
1191 CurrentFolderNode = NULL;
1192 CurrentDataNode = NULL;
1193 CabinetReservedArea = NULL;
1194 RestartSearch = FALSE;
1195 LastFileOffset = 0;
1196 }
1197
1198
1199 VOID
1200 CabinetCleanup(VOID)
1201 /*
1202 * FUNCTION: Cleanup archiver
1203 */
1204 {
1205 CabinetClose();
1206 }
1207
1208
1209 BOOL
1210 CabinetNormalizePath(PWCHAR Path,
1211 ULONG Length)
1212 /*
1213 * FUNCTION: Normalizes a path
1214 * ARGUMENTS:
1215 * Path = Pointer to string with pathname
1216 * Length = Number of characters in Path
1217 * RETURNS:
1218 * TRUE if there was enough room in Path, or FALSE
1219 */
1220 {
1221 ULONG n;
1222 BOOL OK = TRUE;
1223
1224 if ((n = wcslen(Path)) &&
1225 (Path[n - 1] != L'\\') &&
1226 (OK = ((n + 1) < Length)))
1227 {
1228 Path[n] = L'\\';
1229 Path[n + 1] = 0;
1230 }
1231 return OK;
1232 }
1233
1234
1235 PWCHAR
1236 CabinetGetCabinetName()
1237 /*
1238 * FUNCTION: Returns pointer to cabinet file name
1239 * RETURNS:
1240 * Pointer to string with name of cabinet
1241 */
1242 {
1243 return CabinetName;
1244 }
1245
1246
1247 VOID
1248 CabinetSetCabinetName(PWCHAR FileName)
1249 /*
1250 * FUNCTION: Sets cabinet file name
1251 * ARGUMENTS:
1252 * FileName = Pointer to string with name of cabinet
1253 */
1254 {
1255 wcscpy(CabinetName, FileName);
1256 }
1257
1258
1259 VOID
1260 CabinetSetDestinationPath(PWCHAR DestinationPath)
1261 /*
1262 * FUNCTION: Sets destination path
1263 * ARGUMENTS:
1264 * DestinationPath = Pointer to string with name of destination path
1265 */
1266 {
1267 wcscpy(DestPath, DestinationPath);
1268 if (wcslen(DestPath) > 0)
1269 CabinetNormalizePath(DestPath, MAX_PATH);
1270 }
1271
1272
1273 PWCHAR
1274 CabinetGetDestinationPath()
1275 /*
1276 * FUNCTION: Returns destination path
1277 * RETURNS:
1278 * Pointer to string with name of destination path
1279 */
1280 {
1281 return DestPath;
1282 }
1283
1284
1285 ULONG
1286 CabinetOpen(VOID)
1287 /*
1288 * FUNCTION: Opens a cabinet file
1289 * RETURNS:
1290 * Status of operation
1291 */
1292 {
1293 WCHAR CabinetFileName[256];
1294 PCFFOLDER_NODE FolderNode;
1295 ULONG Status;
1296 ULONG Index;
1297
1298 if (!FileOpen)
1299 {
1300 OBJECT_ATTRIBUTES ObjectAttributes;
1301 IO_STATUS_BLOCK IoStatusBlock;
1302 UNICODE_STRING FileName;
1303 NTSTATUS NtStatus;
1304 ULONG BytesRead;
1305 ULONG Size;
1306
1307 OutputBuffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough
1308 if (!OutputBuffer)
1309 return CAB_STATUS_NOMEMORY;
1310
1311 RtlInitUnicodeString(&FileName,
1312 CabinetName);
1313
1314 InitializeObjectAttributes(&ObjectAttributes,
1315 &FileName,
1316 OBJ_CASE_INSENSITIVE,
1317 NULL,
1318 NULL);
1319
1320 NtStatus = NtOpenFile(&FileHandle,
1321 FILE_READ_ACCESS,
1322 &ObjectAttributes,
1323 &IoStatusBlock,
1324 FILE_SHARE_READ,
1325 FILE_SYNCHRONOUS_IO_NONALERT);
1326 if (!NT_SUCCESS(NtStatus))
1327 {
1328 DPRINT("Cannot open file (%S) (%x).\n", CabinetName, NtStatus);
1329 return CAB_STATUS_CANNOT_OPEN;
1330 }
1331
1332 FileOpen = TRUE;
1333
1334 /* Load CAB header */
1335 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) != CAB_STATUS_SUCCESS)
1336 {
1337 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1338 return CAB_STATUS_INVALID_CAB;
1339 }
1340
1341 /* Check header */
1342 if ((BytesRead != sizeof(CFHEADER)) ||
1343 (CABHeader.Signature != CAB_SIGNATURE ) ||
1344 (CABHeader.Version != CAB_VERSION ) ||
1345 (CABHeader.FolderCount == 0 ) ||
1346 (CABHeader.FileCount == 0 ) ||
1347 (CABHeader.FileTableOffset < sizeof(CFHEADER))) {
1348 CloseCabinet();
1349 DPRINT("File has invalid header.\n");
1350 return CAB_STATUS_INVALID_CAB;
1351 }
1352
1353 Size = 0;
1354
1355 /* Read/skip any reserved bytes */
1356 if (CABHeader.Flags & CAB_FLAG_RESERVE)
1357 {
1358 if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) != CAB_STATUS_SUCCESS)
1359 {
1360 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1361 return CAB_STATUS_INVALID_CAB;
1362 }
1363 CabinetReserved = Size & 0xFFFF;
1364 FolderReserved = (Size >> 16) & 0xFF;
1365 DataReserved = (Size >> 24) & 0xFF;
1366
1367 if (CabinetReserved > 0)
1368 {
1369 CabinetReservedArea = RtlAllocateHeap(ProcessHeap, 0, CabinetReserved);
1370 if (!CabinetReservedArea)
1371 {
1372 return CAB_STATUS_NOMEMORY;
1373 }
1374
1375 if ((Status = ReadBlock(CabinetReservedArea, CabinetReserved, &BytesRead)) != CAB_STATUS_SUCCESS)
1376 {
1377 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1378 return CAB_STATUS_INVALID_CAB;
1379 }
1380 }
1381 #if 0
1382 SeekInFile(FileHandle, CabinetReserved, NULL, SEEK_CURRENT, &NtStatus);
1383 if (!NT_SUCCESS(NtStatus))
1384 {
1385 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1386 return CAB_STATUS_INVALID_CAB;
1387 }
1388 #endif
1389 }
1390
1391 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
1392 {
1393 /* Read name of previous cabinet */
1394 Status = ReadString(CabinetFileName, 256);
1395 if (Status != CAB_STATUS_SUCCESS)
1396 return Status;
1397
1398 /* The previous cabinet file is in the same directory as the current */
1399 wcscpy(CabinetPrev, CabinetName);
1400 RemoveFileName(CabinetPrev);
1401 CabinetNormalizePath(CabinetPrev, 256);
1402 wcscat(CabinetPrev, CabinetFileName);
1403
1404 /* Read label of previous disk */
1405 Status = ReadString(DiskPrev, 256);
1406 if (Status != CAB_STATUS_SUCCESS)
1407 return Status;
1408 }
1409 else
1410 {
1411 wcscpy(CabinetPrev, L"");
1412 wcscpy(DiskPrev, L"");
1413 }
1414
1415 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
1416 {
1417 /* Read name of next cabinet */
1418 Status = ReadString(CabinetFileName, 256);
1419 if (Status != CAB_STATUS_SUCCESS)
1420 return Status;
1421
1422 /* The next cabinet file is in the same directory as the previous */
1423 wcscpy(CabinetNext, CabinetName);
1424 RemoveFileName(CabinetNext);
1425 CabinetNormalizePath(CabinetNext, 256);
1426 wcscat(CabinetNext, CabinetFileName);
1427
1428 /* Read label of next disk */
1429 Status = ReadString(DiskNext, 256);
1430 if (Status != CAB_STATUS_SUCCESS)
1431 return Status;
1432 }
1433 else
1434 {
1435 wcscpy(CabinetNext, L"");
1436 wcscpy(DiskNext, L"");
1437 }
1438
1439 /* Read all folders */
1440 for (Index = 0; Index < CABHeader.FolderCount; Index++)
1441 {
1442 FolderNode = NewFolderNode();
1443 if (!FolderNode)
1444 {
1445 DPRINT("Insufficient resources.\n");
1446 return CAB_STATUS_NOMEMORY;
1447 }
1448
1449 if (Index == 0)
1450 FolderNode->UncompOffset = FolderUncompSize;
1451
1452 FolderNode->Index = Index;
1453
1454 if ((Status = ReadBlock(&FolderNode->Folder,
1455 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS)
1456 {
1457 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1458 return CAB_STATUS_INVALID_CAB;
1459 }
1460 }
1461
1462 /* Read file entries */
1463 Status = ReadFileTable();
1464 if (Status != CAB_STATUS_SUCCESS)
1465 {
1466 DPRINT("ReadFileTable() failed (%d).\n", (UINT)Status);
1467 return Status;
1468 }
1469
1470 /* Read data blocks for all folders */
1471 FolderNode = FolderListHead;
1472 while (FolderNode != NULL)
1473 {
1474 Status = ReadDataBlocks(FolderNode);
1475 if (Status != CAB_STATUS_SUCCESS)
1476 {
1477 DPRINT("ReadDataBlocks() failed (%d).\n", (UINT)Status);
1478 return Status;
1479 }
1480 FolderNode = FolderNode->Next;
1481 }
1482 }
1483 return CAB_STATUS_SUCCESS;
1484 }
1485
1486
1487 VOID
1488 CabinetClose(VOID)
1489 /*
1490 * FUNCTION: Closes the cabinet file
1491 */
1492 {
1493 if (FileOpen)
1494 {
1495 CloseCabinet();
1496
1497 if (CabinetReservedArea != NULL)
1498 {
1499 RtlFreeHeap(ProcessHeap, 0, CabinetReservedArea);
1500 CabinetReservedArea = NULL;
1501 }
1502
1503 FileOpen = FALSE;
1504 }
1505 }
1506
1507
1508 ULONG
1509 CabinetFindFirst(PWCHAR FileName,
1510 PCAB_SEARCH Search)
1511 /*
1512 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
1513 * ARGUMENTS:
1514 * FileName = Pointer to search criteria
1515 * Search = Pointer to search structure
1516 * RETURNS:
1517 * Status of operation
1518 */
1519 {
1520 RestartSearch = FALSE;
1521 wcsncpy(Search->Search, FileName, MAX_PATH);
1522 Search->Next = FileListHead;
1523 return CabinetFindNext(Search);
1524 }
1525
1526
1527 ULONG
1528 CabinetFindNext(PCAB_SEARCH Search)
1529 /*
1530 * FUNCTION: Finds next file in the cabinet that matches a search criteria
1531 * ARGUMENTS:
1532 * Search = Pointer to search structure
1533 * RETURNS:
1534 * Status of operation
1535 */
1536 {
1537 ULONG Status;
1538
1539 if (RestartSearch)
1540 {
1541 Search->Next = FileListHead;
1542
1543 /* Skip split files already extracted */
1544 while ((Search->Next) &&
1545 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) &&
1546 (Search->Next->File.FileOffset <= LastFileOffset))
1547 {
1548 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
1549 Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset);
1550 Search->Next = Search->Next->Next;
1551 }
1552
1553 RestartSearch = FALSE;
1554 }
1555
1556 /* FIXME: Check search criteria */
1557
1558 if (!Search->Next)
1559 {
1560 if (wcslen(DiskNext) > 0)
1561 {
1562 CloseCabinet();
1563
1564 CabinetSetCabinetName(CabinetNext);
1565
1566 if (DiskChangeHandler != NULL)
1567 {
1568 DiskChangeHandler(CabinetNext, DiskNext);
1569 }
1570
1571 Status = CabinetOpen();
1572 if (Status != CAB_STATUS_SUCCESS)
1573 return Status;
1574
1575 Search->Next = FileListHead;
1576 if (!Search->Next)
1577 return CAB_STATUS_NOFILE;
1578 }
1579 else
1580 {
1581 return CAB_STATUS_NOFILE;
1582 }
1583 }
1584
1585 Search->File = &Search->Next->File;
1586 Search->FileName = Search->Next->FileName;
1587 Search->Next = Search->Next->Next;
1588 return CAB_STATUS_SUCCESS;
1589 }
1590
1591
1592 ULONG
1593 CabinetExtractFile(PWCHAR FileName)
1594 /*
1595 * FUNCTION: Extracts a file from the cabinet
1596 * ARGUMENTS:
1597 * FileName = Pointer to buffer with name of file
1598 * RETURNS
1599 * Status of operation
1600 */
1601 {
1602 ULONG Size;
1603 ULONG Offset;
1604 ULONG BytesRead;
1605 ULONG BytesToRead;
1606 ULONG BytesWritten;
1607 ULONG BytesSkipped;
1608 ULONG BytesToWrite;
1609 ULONG TotalBytesRead;
1610 ULONG CurrentOffset;
1611 PUCHAR Buffer;
1612 PUCHAR CurrentBuffer;
1613 HANDLE DestFile;
1614 PCFFILE_NODE File;
1615 CFDATA CFData;
1616 ULONG Status;
1617 BOOL Skip;
1618 FILETIME FileTime;
1619 WCHAR DestName[MAX_PATH];
1620 WCHAR TempName[MAX_PATH];
1621 PWCHAR s;
1622 NTSTATUS NtStatus;
1623 UNICODE_STRING UnicodeString;
1624 IO_STATUS_BLOCK IoStatusBlock;
1625 OBJECT_ATTRIBUTES ObjectAttributes;
1626 FILE_BASIC_INFORMATION FileBasic;
1627
1628 Status = LocateFile(FileName, &File);
1629 if (Status != CAB_STATUS_SUCCESS)
1630 {
1631 DPRINT("Cannot locate file (%d).\n", (UINT)Status);
1632 return Status;
1633 }
1634
1635 LastFileOffset = File->File.FileOffset;
1636
1637 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK)
1638 {
1639 case CAB_COMP_NONE:
1640 CabinetSelectCodec(CAB_CODEC_RAW);
1641 break;
1642 case CAB_COMP_MSZIP:
1643 CabinetSelectCodec(CAB_CODEC_MSZIP);
1644 break;
1645 default:
1646 return CAB_STATUS_UNSUPPCOMP;
1647 }
1648
1649 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1650 (UINT)File->File.FileOffset,
1651 (UINT)File->File.FileSize,
1652 (UINT)File->DataBlock->AbsoluteOffset,
1653 (UINT)File->DataBlock->UncompOffset);
1654
1655 wcscpy(DestName, DestPath);
1656 wcscat(DestName, FileName);
1657
1658 while (NULL != (s = wcsstr(DestName, L"\\.\\")))
1659 {
1660 memmove(s, s + 2, (wcslen(s + 2) + 1) *sizeof(WCHAR));
1661 }
1662
1663 /* Create destination file, fail if it already exists */
1664 RtlInitUnicodeString(&UnicodeString,
1665 DestName);
1666
1667
1668 InitializeObjectAttributes(&ObjectAttributes,
1669 &UnicodeString,
1670 OBJ_CASE_INSENSITIVE,
1671 NULL,
1672 NULL);
1673
1674 NtStatus = NtCreateFile(&DestFile,
1675 FILE_WRITE_ACCESS,
1676 &ObjectAttributes,
1677 &IoStatusBlock,
1678 NULL,
1679 FILE_ATTRIBUTE_NORMAL,
1680 0,
1681 FILE_CREATE,
1682 FILE_SYNCHRONOUS_IO_NONALERT,
1683 NULL,
1684 0);
1685 if (!NT_SUCCESS(NtStatus))
1686 {
1687 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName, NtStatus);
1688
1689 /* If file exists, ask to overwrite file */
1690 if (OverwriteHandler == NULL || OverwriteHandler(&File->File, FileName))
1691 {
1692 /* Create destination file, overwrite if it already exists */
1693 NtStatus = NtCreateFile(&DestFile,
1694 FILE_WRITE_ACCESS,
1695 &ObjectAttributes,
1696 &IoStatusBlock,
1697 NULL,
1698 FILE_ATTRIBUTE_NORMAL,
1699 0,
1700 FILE_OVERWRITE,
1701 FILE_SYNCHRONOUS_IO_ALERT,
1702 NULL,
1703 0);
1704 if (!NT_SUCCESS(NtStatus))
1705 {
1706 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName, NtStatus);
1707 return CAB_STATUS_CANNOT_CREATE;
1708 }
1709 }
1710 else
1711 {
1712 DPRINT("File (%S) exists.\n", DestName);
1713 return CAB_STATUS_FILE_EXISTS;
1714 }
1715 }
1716
1717 if (!ConvertDosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime))
1718 {
1719 NtClose(DestFile);
1720 DPRINT("DosDateTimeToFileTime() failed.\n");
1721 return CAB_STATUS_CANNOT_WRITE;
1722 }
1723
1724 NtStatus = NtQueryInformationFile(DestFile,
1725 &IoStatusBlock,
1726 &FileBasic,
1727 sizeof(FILE_BASIC_INFORMATION),
1728 FileBasicInformation);
1729 if (!NT_SUCCESS(Status))
1730 {
1731 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus);
1732 }
1733 else
1734 {
1735 memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME));
1736
1737 NtStatus = NtSetInformationFile(DestFile,
1738 &IoStatusBlock,
1739 &FileBasic,
1740 sizeof(FILE_BASIC_INFORMATION),
1741 FileBasicInformation);
1742 if (!NT_SUCCESS(NtStatus))
1743 {
1744 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus);
1745 }
1746 }
1747
1748 SetAttributesOnFile(File, DestFile);
1749
1750 Buffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough
1751 if (!Buffer)
1752 {
1753 NtClose(DestFile);
1754 DPRINT("Insufficient memory.\n");
1755 return CAB_STATUS_NOMEMORY;
1756 }
1757
1758 /* Call extract event handler */
1759 if (ExtractHandler != NULL)
1760 {
1761 ExtractHandler(&File->File, FileName);
1762 }
1763
1764 /* Search to start of file */
1765 Offset = SeekInFile(FileHandle,
1766 File->DataBlock->AbsoluteOffset,
1767 NULL,
1768 SEEK_BEGIN,
1769 &NtStatus);
1770 if (!NT_SUCCESS(NtStatus))
1771 {
1772 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1773 return CAB_STATUS_INVALID_CAB;
1774 }
1775
1776 Size = File->File.FileSize;
1777 Offset = File->File.FileOffset;
1778 CurrentOffset = File->DataBlock->UncompOffset;
1779
1780 Skip = TRUE;
1781
1782 ReuseBlock = (CurrentDataNode == File->DataBlock);
1783 if (Size > 0)
1784 {
1785 do
1786 {
1787 DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1788 File->DataBlock->UncompOffset, (UINT)ReuseBlock, Offset, Size,
1789 BytesLeftInBlock);
1790
1791 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0))
1792 {
1793 DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock);
1794
1795 CurrentBuffer = Buffer;
1796 TotalBytesRead = 0;
1797 do
1798 {
1799 DPRINT("Size (%d bytes).\n", Size);
1800
1801 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1802 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1803 {
1804 NtClose(DestFile);
1805 RtlFreeHeap(ProcessHeap, 0, Buffer);
1806 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1807 return CAB_STATUS_INVALID_CAB;
1808 }
1809
1810 DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
1811 (UINT)CFData.Checksum,
1812 (UINT)CFData.CompSize,
1813 (UINT)CFData.UncompSize,
1814 (UINT)SeekInFile(FileHandle, 0, NULL, SEEK_CURRENT, &NtStatus));
1815
1816 //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1817
1818 BytesToRead = CFData.CompSize;
1819
1820 DPRINT("Read: (0x%X,0x%X).\n",
1821 CurrentBuffer, Buffer);
1822
1823 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) !=
1824 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead))
1825 {
1826 NtClose(DestFile);
1827 RtlFreeHeap(ProcessHeap, 0, Buffer);
1828 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1829 return CAB_STATUS_INVALID_CAB;
1830 }
1831
1832 /* FIXME: Does not work with files generated by makecab.exe */
1833 /*
1834 if (CFData.Checksum != 0)
1835 {
1836 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1837 if (Checksum != CFData.Checksum)
1838 {
1839 NtClose(DestFile);
1840 RtlFreeHeap(ProcessHeap, 0, Buffer);
1841 DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n",
1842 Checksum, CFData.Checksum);
1843 return CAB_STATUS_INVALID_CAB;
1844 }
1845 }
1846 */
1847 TotalBytesRead += BytesRead;
1848
1849 CurrentBuffer += BytesRead;
1850
1851 if (CFData.UncompSize == 0)
1852 {
1853 if (wcslen(DiskNext) == 0)
1854 return CAB_STATUS_NOFILE;
1855
1856 /* CloseCabinet() will destroy all file entries so in case
1857 FileName refers to the FileName field of a CFFOLDER_NODE
1858 structure, we have to save a copy of the filename */
1859 wcscpy(TempName, FileName);
1860
1861 CloseCabinet();
1862
1863 CabinetSetCabinetName(CabinetNext);
1864
1865 if (DiskChangeHandler != NULL)
1866 {
1867 DiskChangeHandler(CabinetNext, DiskNext);
1868 }
1869
1870 Status = CabinetOpen();
1871 if (Status != CAB_STATUS_SUCCESS)
1872 return Status;
1873
1874 /* The first data block of the file will not be
1875 found as it is located in the previous file */
1876 Status = LocateFile(TempName, &File);
1877 if (Status == CAB_STATUS_NOFILE)
1878 {
1879 DPRINT("Cannot locate file (%d).\n", (UINT)Status);
1880 return Status;
1881 }
1882
1883 /* The file is continued in the first data block in the folder */
1884 File->DataBlock = CurrentFolderNode->DataListHead;
1885
1886 /* Search to start of file */
1887 SeekInFile(FileHandle,
1888 File->DataBlock->AbsoluteOffset,
1889 NULL,
1890 SEEK_BEGIN,
1891 &NtStatus);
1892 if (!NT_SUCCESS(NtStatus))
1893 {
1894 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1895 return CAB_STATUS_INVALID_CAB;
1896 }
1897
1898 DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1899 (UINT)File->File.FileOffset,
1900 (UINT)File->File.FileSize,
1901 (UINT)File->DataBlock->AbsoluteOffset,
1902 (UINT)File->DataBlock->UncompOffset);
1903
1904 CurrentDataNode = File->DataBlock;
1905 ReuseBlock = TRUE;
1906
1907 RestartSearch = TRUE;
1908 }
1909 } while (CFData.UncompSize == 0);
1910
1911 DPRINT("TotalBytesRead (%d).\n", TotalBytesRead);
1912
1913 Status = CodecUncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite);
1914 if (Status != CS_SUCCESS)
1915 {
1916 NtClose(DestFile);
1917 RtlFreeHeap(ProcessHeap, 0, Buffer);
1918 DPRINT("Cannot uncompress block.\n");
1919 if (Status == CS_NOMEMORY)
1920 return CAB_STATUS_NOMEMORY;
1921 return CAB_STATUS_INVALID_CAB;
1922 }
1923
1924 if (BytesToWrite != CFData.UncompSize)
1925 {
1926 DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
1927 BytesToWrite, CFData.UncompSize);
1928 return CAB_STATUS_INVALID_CAB;
1929 }
1930
1931 BytesLeftInBlock = BytesToWrite;
1932 }
1933 else
1934 {
1935 DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock);
1936
1937 BytesToWrite = BytesLeftInBlock;
1938
1939 DPRINT("Seeking to absolute offset 0x%X.\n",
1940 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1941 CurrentDataNode->Data.CompSize);
1942
1943 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1944 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1945 {
1946 NtClose(DestFile);
1947 RtlFreeHeap(ProcessHeap, 0, Buffer);
1948 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1949 return CAB_STATUS_INVALID_CAB;
1950 }
1951
1952 DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1953 CFData.CompSize, CFData.UncompSize);
1954
1955 /* Go to next data block */
1956 SeekInFile(FileHandle,
1957 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1958 CurrentDataNode->Data.CompSize,
1959 NULL,
1960 SEEK_BEGIN,
1961 &NtStatus);
1962 if (!NT_SUCCESS(NtStatus))
1963 {
1964 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1965 return CAB_STATUS_INVALID_CAB;
1966 }
1967
1968 ReuseBlock = FALSE;
1969 }
1970
1971 if (Skip)
1972 BytesSkipped = (Offset - CurrentOffset);
1973 else
1974 BytesSkipped = 0;
1975
1976 BytesToWrite -= BytesSkipped;
1977
1978 if (Size < BytesToWrite)
1979 BytesToWrite = Size;
1980
1981 DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1982 (UINT)Offset,
1983 (UINT)CurrentOffset,
1984 (UINT)BytesToWrite,
1985 (UINT)BytesSkipped, (UINT)Skip,
1986 (UINT)Size);
1987
1988 // if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1989 // BytesToWrite, &BytesWritten, NULL) ||
1990 // (BytesToWrite != BytesWritten))
1991
1992 NtStatus = NtWriteFile(DestFile,
1993 NULL,
1994 NULL,
1995 NULL,
1996 &IoStatusBlock,
1997 (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1998 BytesToWrite,
1999 NULL,
2000 NULL);
2001 BytesWritten = BytesToWrite;
2002 if (!NT_SUCCESS(NtStatus))
2003 {
2004 DPRINT("Status 0x%X.\n", NtStatus);
2005
2006 NtClose(DestFile);
2007 RtlFreeHeap(ProcessHeap, 0, Buffer);
2008 DPRINT("Cannot write to file.\n");
2009 return CAB_STATUS_CANNOT_WRITE;
2010 }
2011 Size -= BytesToWrite;
2012
2013 CurrentOffset += BytesToWrite;
2014
2015 /* Don't skip any more bytes */
2016 Skip = FALSE;
2017 } while (Size > 0);
2018 }
2019
2020 NtClose(DestFile);
2021
2022 RtlFreeHeap(ProcessHeap, 0, Buffer);
2023
2024 return CAB_STATUS_SUCCESS;
2025 }
2026
2027
2028 VOID
2029 CabinetSelectCodec(ULONG Id)
2030 /*
2031 * FUNCTION: Selects codec engine to use
2032 * ARGUMENTS:
2033 * Id = Codec identifier
2034 */
2035 {
2036 if (CodecSelected)
2037 {
2038 if (Id == CodecId)
2039 return;
2040
2041 CodecSelected = FALSE;
2042 }
2043
2044 switch (Id)
2045 {
2046 case CAB_CODEC_RAW:
2047 CodecUncompress = RawCodecUncompress;
2048 break;
2049 case CAB_CODEC_MSZIP:
2050 CodecUncompress = MSZipCodecUncompress;
2051 break;
2052 default:
2053 return;
2054 }
2055
2056 CodecId = Id;
2057 CodecSelected = TRUE;
2058 }
2059
2060
2061 VOID
2062 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite,
2063 PCABINET_EXTRACT Extract,
2064 PCABINET_DISK_CHANGE DiskChange)
2065 /*
2066 * FUNCTION: Set event handlers
2067 * ARGUMENTS:
2068 * Overwrite = Handler called when a file is to be overwritten
2069 * Extract = Handler called when a file is to be extracted
2070 * DiskChange = Handler called when changing the disk
2071 */
2072 {
2073 OverwriteHandler = Overwrite;
2074 ExtractHandler = Extract;
2075 DiskChangeHandler = DiskChange;
2076 }
2077
2078
2079 PVOID
2080 CabinetGetCabinetReservedArea(PULONG Size)
2081 /*
2082 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
2083 */
2084 {
2085 if (CabinetReservedArea != NULL)
2086 {
2087 if (Size != NULL)
2088 {
2089 *Size = CabinetReserved;
2090 }
2091 return CabinetReservedArea;
2092 }
2093 else
2094 {
2095 if (Size != NULL)
2096 {
2097 *Size = 0;
2098 }
2099 return NULL;
2100 }
2101 }