fixed a few warnings
[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 FileStandart;
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 &FileStandart,
244 sizeof(FILE_STANDARD_INFORMATION),
245 FileStandardInformation);
246 FilePosition.CurrentByteOffset.QuadPart =
247 FileStandart.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 static VOID
627 DestroyDeletedFileNodes()
628 /*
629 * FUNCTION: Destroys file nodes that are marked for deletion
630 */
631 {
632 PCFFILE_NODE CurNode;
633 PCFFILE_NODE NextNode;
634
635 CurNode = FileListHead;
636 while (CurNode != NULL)
637 {
638 NextNode = CurNode->Next;
639
640 if (CurNode->Delete)
641 {
642 if (CurNode->Prev != NULL)
643 {
644 CurNode->Prev->Next = CurNode->Next;
645 }
646 else
647 {
648 FileListHead = CurNode->Next;
649 if (FileListHead)
650 FileListHead->Prev = NULL;
651 }
652
653 if (CurNode->Next != NULL)
654 {
655 CurNode->Next->Prev = CurNode->Prev;
656 }
657 else
658 {
659 FileListTail = CurNode->Prev;
660 if (FileListTail)
661 FileListTail->Next = NULL;
662 }
663
664 DPRINT("Deleting file: '%S'\n", CurNode->FileName);
665
666 if (CurNode->FileName)
667 RtlFreeHeap(ProcessHeap, 0, CurNode->FileName);
668 RtlFreeHeap(ProcessHeap, 0, CurNode);
669 }
670 CurNode = NextNode;
671 }
672 }
673
674
675 static VOID
676 DestroyFolderNodes()
677 /*
678 * FUNCTION: Destroys folder nodes
679 */
680 {
681 PCFFOLDER_NODE PrevNode;
682 PCFFOLDER_NODE NextNode;
683
684 NextNode = FolderListHead;
685 while (NextNode != NULL)
686 {
687 PrevNode = NextNode->Next;
688 DestroyDataNodes(NextNode);
689 RtlFreeHeap(ProcessHeap, 0, NextNode);
690 NextNode = PrevNode;
691 }
692 FolderListHead = NULL;
693 FolderListTail = NULL;
694 }
695
696
697 static VOID
698 DestroyDeletedFolderNodes()
699 /*
700 * FUNCTION: Destroys folder nodes that are marked for deletion
701 */
702 {
703 PCFFOLDER_NODE CurNode;
704 PCFFOLDER_NODE NextNode;
705
706 CurNode = FolderListHead;
707 while (CurNode != NULL)
708 {
709 NextNode = CurNode->Next;
710
711 if (CurNode->Delete)
712 {
713 if (CurNode->Prev != NULL)
714 {
715 CurNode->Prev->Next = CurNode->Next;
716 }
717 else
718 {
719 FolderListHead = CurNode->Next;
720 if (FolderListHead)
721 FolderListHead->Prev = NULL;
722 }
723
724 if (CurNode->Next != NULL)
725 {
726 CurNode->Next->Prev = CurNode->Prev;
727 }
728 else
729 {
730 FolderListTail = CurNode->Prev;
731 if (FolderListTail)
732 FolderListTail->Next = NULL;
733 }
734
735 DestroyDataNodes(CurNode);
736 RtlFreeHeap(ProcessHeap, 0, CurNode);
737 }
738 CurNode = NextNode;
739 }
740 }
741
742
743 static PCFFOLDER_NODE
744 LocateFolderNode(ULONG Index)
745 /*
746 * FUNCTION: Locates a folder node
747 * ARGUMENTS:
748 * Index = Folder index
749 * RETURNS:
750 * Pointer to folder node or NULL if the folder node was not found
751 */
752 {
753 PCFFOLDER_NODE Node;
754
755 switch (Index)
756 {
757 case CAB_FILE_SPLIT:
758 return FolderListTail;
759
760 case CAB_FILE_CONTINUED:
761 case CAB_FILE_PREV_NEXT:
762 return FolderListHead;
763 }
764
765 Node = FolderListHead;
766 while (Node != NULL)
767 {
768 if (Node->Index == Index)
769 return Node;
770 Node = Node->Next;
771 }
772 return NULL;
773 }
774
775
776 static ULONG
777 GetAbsoluteOffset(PCFFILE_NODE File)
778 /*
779 * FUNCTION: Returns the absolute offset of a file
780 * ARGUMENTS:
781 * File = Pointer to CFFILE_NODE structure for file
782 * RETURNS:
783 * Status of operation
784 */
785 {
786 PCFDATA_NODE Node;
787
788 DPRINT("FileName '%S' FileOffset (0x%X) FileSize (%d).\n",
789 (PWCHAR)File->FileName,
790 (UINT)File->File.FileOffset,
791 (UINT)File->File.FileSize);
792
793 Node = CurrentFolderNode->DataListHead;
794 while (Node != NULL)
795 {
796 DPRINT("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
797 (UINT)Node->UncompOffset,
798 (UINT)Node->UncompOffset + Node->Data.UncompSize,
799 (UINT)Node->Data.UncompSize);
800
801 /* Node->Data.UncompSize will be 0 if the block is split
802 (ie. it is the last block in this cabinet) */
803 if ((Node->Data.UncompSize == 0) ||
804 ((File->File.FileOffset >= Node->UncompOffset) &&
805 (File->File.FileOffset < Node->UncompOffset +
806 Node->Data.UncompSize)))
807 {
808 File->DataBlock = Node;
809 return CAB_STATUS_SUCCESS;
810 }
811
812 Node = Node->Next;
813 }
814 return CAB_STATUS_INVALID_CAB;
815 }
816
817
818 static ULONG
819 LocateFile(PWCHAR FileName,
820 PCFFILE_NODE *File)
821 /*
822 * FUNCTION: Locates a file in the cabinet
823 * ARGUMENTS:
824 * FileName = Pointer to string with name of file to locate
825 * File = Address of pointer to CFFILE_NODE structure to fill
826 * RETURNS:
827 * Status of operation
828 * NOTES:
829 * Current folder is set to the folder of the file
830 */
831 {
832 PCFFILE_NODE Node;
833 ULONG Status;
834
835 DPRINT("FileName '%S'\n", FileName);
836
837 Node = FileListHead;
838 while (Node != NULL)
839 {
840 if (_wcsicmp(FileName, Node->FileName) == 0)
841 {
842 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
843 if (!CurrentFolderNode)
844 {
845 DPRINT("Folder with index number (%d) not found.\n",
846 (UINT)Node->File.FileControlID);
847 return CAB_STATUS_INVALID_CAB;
848 }
849
850 if (Node->DataBlock == NULL)
851 {
852 Status = GetAbsoluteOffset(Node);
853 }
854 else
855 Status = CAB_STATUS_SUCCESS;
856 *File = Node;
857 return Status;
858 }
859 Node = Node->Next;
860 }
861 return CAB_STATUS_NOFILE;
862 }
863
864
865 static ULONG
866 ReadString(PWCHAR String, ULONG MaxLength)
867 /*
868 * FUNCTION: Reads a NULL-terminated string from the cabinet
869 * ARGUMENTS:
870 * String = Pointer to buffer to place string
871 * MaxLength = Maximum length of string
872 * RETURNS:
873 * Status of operation
874 */
875 {
876 NTSTATUS NtStatus;
877 ULONG BytesRead;
878 ULONG Offset;
879 ULONG Status;
880 ULONG Size;
881 BOOL Found;
882 CHAR buf[MAX_PATH];
883 ANSI_STRING as;
884 UNICODE_STRING us;
885
886 Offset = 0;
887 Found = FALSE;
888 do
889 {
890 Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset;
891
892 if (Size == 0)
893 {
894 DPRINT("Too long a filename.\n");
895 return CAB_STATUS_INVALID_CAB;
896 }
897
898 Status = ReadBlock((PCFDATA)&buf[Offset], Size, &BytesRead);
899 if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size))
900 {
901 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
902 return CAB_STATUS_INVALID_CAB;
903 }
904
905 for (Size = Offset; Size < Offset + BytesRead; Size++)
906 {
907 if (buf[Size] == '\0')
908 {
909 Found = TRUE;
910 break;
911 }
912 }
913
914 Offset += BytesRead;
915 } while (!Found);
916
917 /* Back up some bytes */
918 Size = (BytesRead - Size) - 1;
919 SeekInFile(FileHandle, -(LONG)Size, NULL, SEEK_CURRENT, &NtStatus);
920 if (!NT_SUCCESS(NtStatus))
921 {
922 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
923 return CAB_STATUS_INVALID_CAB;
924 }
925
926 RtlInitAnsiString(&as, buf);
927 us.Buffer = String;
928 us.MaximumLength = MaxLength * sizeof(WCHAR);
929 us.Length = 0;
930
931 RtlAnsiStringToUnicodeString(&us, &as, FALSE);
932
933 return CAB_STATUS_SUCCESS;
934 }
935
936
937 static ULONG
938 ReadFileTable()
939 /*
940 * FUNCTION: Reads the file table from the cabinet file
941 * RETURNS:
942 * Status of operation
943 */
944 {
945 ULONG i;
946 ULONG Status;
947 ULONG BytesRead;
948 PCFFILE_NODE File;
949 NTSTATUS NtStatus;
950
951 DPRINT("Reading file table at absolute offset (0x%X).\n",
952 CABHeader.FileTableOffset);
953
954 /* Seek to file table */
955 SeekInFile(FileHandle, CABHeader.FileTableOffset, NULL, SEEK_BEGIN, &NtStatus);
956 if (!NT_SUCCESS(NtStatus))
957 {
958 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
959 return CAB_STATUS_INVALID_CAB;
960 }
961
962 for (i = 0; i < CABHeader.FileCount; i++)
963 {
964 File = NewFileNode();
965 if (!File)
966 {
967 DPRINT("Insufficient memory.\n");
968 return CAB_STATUS_NOMEMORY;
969 }
970
971 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
972 &BytesRead)) != CAB_STATUS_SUCCESS) {
973 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
974 return CAB_STATUS_INVALID_CAB;
975 }
976
977 File->FileName = (PWCHAR)RtlAllocateHeap(ProcessHeap, 0, MAX_PATH * sizeof(WCHAR));
978 if (!File->FileName)
979 {
980 DPRINT("Insufficient memory.\n");
981 return CAB_STATUS_NOMEMORY;
982 }
983
984 /* Read file name */
985 Status = ReadString(File->FileName, MAX_PATH);
986 if (Status != CAB_STATUS_SUCCESS)
987 return Status;
988
989 DPRINT("Found file '%S' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
990 (PWCHAR)File->FileName,
991 (UINT)File->File.FileOffset,
992 (UINT)File->File.FileSize,
993 (UINT)File->File.FileControlID);
994 }
995 return CAB_STATUS_SUCCESS;
996 }
997
998
999 static ULONG
1000 ReadDataBlocks(PCFFOLDER_NODE FolderNode)
1001 /*
1002 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
1003 * ARGUMENTS:
1004 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
1005 * RETURNS:
1006 * Status of operation
1007 */
1008 {
1009 ULONG AbsoluteOffset;
1010 ULONG UncompOffset;
1011 PCFDATA_NODE Node;
1012 NTSTATUS NtStatus;
1013 ULONG BytesRead;
1014 ULONG Status;
1015 ULONG i;
1016
1017 DPRINT("Reading data blocks for folder (%d) at absolute offset (0x%X).\n",
1018 FolderNode->Index, FolderNode->Folder.DataOffset);
1019
1020 AbsoluteOffset = FolderNode->Folder.DataOffset;
1021 UncompOffset = FolderNode->UncompOffset;
1022
1023 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++)
1024 {
1025 Node = NewDataNode(FolderNode);
1026 if (!Node)
1027 {
1028 DPRINT("Insufficient memory.\n");
1029 return CAB_STATUS_NOMEMORY;
1030 }
1031
1032 /* Seek to data block */
1033 SeekInFile(FileHandle, AbsoluteOffset, NULL, SEEK_BEGIN, &NtStatus);
1034 if (!NT_SUCCESS(NtStatus))
1035 {
1036 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1037 return CAB_STATUS_INVALID_CAB;
1038 }
1039
1040 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
1041 &BytesRead)) != CAB_STATUS_SUCCESS)
1042 {
1043 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1044 return CAB_STATUS_INVALID_CAB;
1045 }
1046
1047 DPRINT("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
1048 (UINT)AbsoluteOffset,
1049 (UINT)UncompOffset,
1050 (UINT)Node->Data.Checksum,
1051 (UINT)Node->Data.CompSize,
1052 (UINT)Node->Data.UncompSize);
1053
1054 Node->AbsoluteOffset = AbsoluteOffset;
1055 Node->UncompOffset = UncompOffset;
1056
1057 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
1058 UncompOffset += Node->Data.UncompSize;
1059 }
1060
1061 FolderUncompSize = UncompOffset;
1062
1063 return CAB_STATUS_SUCCESS;
1064 }
1065
1066
1067 static ULONG
1068 ComputeChecksum(PVOID Buffer,
1069 UINT Size,
1070 ULONG Seed)
1071 /*
1072 * FUNCTION: Computes checksum for data block
1073 * ARGUMENTS:
1074 * Buffer = Pointer to data buffer
1075 * Size = Length of data buffer
1076 * Seed = Previously computed checksum
1077 * RETURNS:
1078 * Checksum of buffer
1079 */
1080 {
1081 INT UlongCount; // Number of ULONGs in block
1082 ULONG Checksum; // Checksum accumulator
1083 PBYTE pb;
1084 ULONG ul;
1085
1086 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
1087 won't accept checksums computed by this routine */
1088
1089 DPRINT("Checksumming buffer (0x%X) Size (%d)\n", (UINT)Buffer, Size);
1090
1091 UlongCount = Size / 4; // Number of ULONGs
1092 Checksum = Seed; // Init checksum
1093 pb = (PBYTE)Buffer; // Start at front of data block
1094
1095 /* Checksum integral multiple of ULONGs */
1096 while (UlongCount-- > 0)
1097 {
1098 /* NOTE: Build ULONG in big/little-endian independent manner */
1099 ul = *pb++; // Get low-order byte
1100 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
1101 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
1102 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
1103
1104 Checksum ^= ul; // Update checksum
1105 }
1106
1107 /* Checksum remainder bytes */
1108 ul = 0;
1109 switch (Size % 4)
1110 {
1111 case 3:
1112 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
1113 case 2:
1114 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
1115 case 1:
1116 ul |= *pb++; // Get low-order byte
1117 default:
1118 break;
1119 }
1120 Checksum ^= ul; // Update checksum
1121
1122 /* Return computed checksum */
1123 return Checksum;
1124 }
1125
1126
1127 static ULONG
1128 CloseCabinet()
1129 /*
1130 * FUNCTION: Closes the current cabinet
1131 * RETURNS:
1132 * Status of operation
1133 */
1134 {
1135 PCFFOLDER_NODE PrevNode;
1136 PCFFOLDER_NODE NextNode;
1137 ULONG Status;
1138
1139 DestroyFileNodes();
1140
1141 DestroyFolderNodes();
1142
1143 if (InputBuffer)
1144 {
1145 RtlFreeHeap(ProcessHeap, 0, InputBuffer);
1146 InputBuffer = NULL;
1147 }
1148
1149 if (OutputBuffer)
1150 {
1151 RtlFreeHeap(ProcessHeap, 0, OutputBuffer);
1152 OutputBuffer = NULL;
1153 }
1154
1155 NtClose(FileHandle);
1156 }
1157
1158
1159 VOID
1160 CabinetInitialize()
1161 /*
1162 * FUNCTION: Initialize archiver
1163 */
1164 {
1165 ZStream.zalloc = MSZipAlloc;
1166 ZStream.zfree = MSZipFree;
1167 ZStream.opaque = (voidpf)0;
1168
1169 FileOpen = FALSE;
1170 wcscpy(DestPath, L"");
1171
1172 FolderListHead = NULL;
1173 FolderListTail = NULL;
1174 FileListHead = NULL;
1175 FileListTail = NULL;
1176
1177 CodecId = CAB_CODEC_RAW;
1178 CodecSelected = TRUE;
1179
1180 OutputBuffer = NULL;
1181 CurrentOBuffer = NULL;
1182 CurrentOBufferSize = 0;
1183 InputBuffer = NULL;
1184 CurrentIBuffer = NULL;
1185 CurrentIBufferSize = 0;
1186
1187 FolderUncompSize = 0;
1188 BytesLeftInBlock = 0;
1189 CabinetReserved = 0;
1190 FolderReserved = 0;
1191 DataReserved = 0;
1192 ReuseBlock = FALSE;
1193 CurrentFolderNode = NULL;
1194 CurrentDataNode = NULL;
1195 CabinetReservedArea = NULL;
1196 RestartSearch = FALSE;
1197 LastFileOffset = 0;
1198 }
1199
1200
1201 VOID
1202 CabinetCleanup()
1203 /*
1204 * FUNCTION: Cleanup archiver
1205 */
1206 {
1207 CabinetClose();
1208 }
1209
1210
1211 BOOL
1212 CabinetNormalizePath(PWCHAR Path,
1213 ULONG Length)
1214 /*
1215 * FUNCTION: Normalizes a path
1216 * ARGUMENTS:
1217 * Path = Pointer to string with pathname
1218 * Length = Number of characters in Path
1219 * RETURNS:
1220 * TRUE if there was enough room in Path, or FALSE
1221 */
1222 {
1223 ULONG n;
1224 BOOL OK = TRUE;
1225
1226 if ((n = wcslen(Path)) &&
1227 (Path[n - 1] != L'\\') &&
1228 (OK = ((n + 1) < Length)))
1229 {
1230 Path[n] = L'\\';
1231 Path[n + 1] = 0;
1232 }
1233 return OK;
1234 }
1235
1236
1237 PWCHAR
1238 CabinetGetCabinetName()
1239 /*
1240 * FUNCTION: Returns pointer to cabinet file name
1241 * RETURNS:
1242 * Pointer to string with name of cabinet
1243 */
1244 {
1245 return CabinetName;
1246 }
1247
1248
1249 VOID
1250 CabinetSetCabinetName(PWCHAR FileName)
1251 /*
1252 * FUNCTION: Sets cabinet file name
1253 * ARGUMENTS:
1254 * FileName = Pointer to string with name of cabinet
1255 */
1256 {
1257 wcscpy(CabinetName, FileName);
1258 }
1259
1260
1261 VOID
1262 CabinetSetDestinationPath(PWCHAR DestinationPath)
1263 /*
1264 * FUNCTION: Sets destination path
1265 * ARGUMENTS:
1266 * DestinationPath = Pointer to string with name of destination path
1267 */
1268 {
1269 wcscpy(DestPath, DestinationPath);
1270 if (wcslen(DestPath) > 0)
1271 CabinetNormalizePath(DestPath, MAX_PATH);
1272 }
1273
1274
1275 PWCHAR
1276 CabinetGetDestinationPath()
1277 /*
1278 * FUNCTION: Returns destination path
1279 * RETURNS:
1280 * Pointer to string with name of destination path
1281 */
1282 {
1283 return DestPath;
1284 }
1285
1286
1287 ULONG
1288 CabinetOpen()
1289 /*
1290 * FUNCTION: Opens a cabinet file
1291 * RETURNS:
1292 * Status of operation
1293 */
1294 {
1295 WCHAR CabinetFileName[256];
1296 PCFFOLDER_NODE FolderNode;
1297 ULONG Status;
1298 ULONG Index;
1299
1300 if (!FileOpen)
1301 {
1302 OBJECT_ATTRIBUTES ObjectAttributes;
1303 IO_STATUS_BLOCK IoStatusBlock;
1304 UNICODE_STRING FileName;
1305 NTSTATUS NtStatus;
1306 ULONG BytesRead;
1307 ULONG Size;
1308
1309 OutputBuffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough
1310 if (!OutputBuffer)
1311 return CAB_STATUS_NOMEMORY;
1312
1313 RtlInitUnicodeString(&FileName,
1314 CabinetName);
1315
1316 InitializeObjectAttributes(&ObjectAttributes,
1317 &FileName,
1318 OBJ_CASE_INSENSITIVE,
1319 NULL,
1320 NULL);
1321
1322 NtStatus = NtOpenFile(&FileHandle,
1323 FILE_READ_ACCESS,
1324 &ObjectAttributes,
1325 &IoStatusBlock,
1326 FILE_SHARE_READ,
1327 FILE_SYNCHRONOUS_IO_ALERT);
1328 if (!NT_SUCCESS(NtStatus))
1329 {
1330 DPRINT("Cannot open file (%S) (%x).\n", CabinetName, NtStatus);
1331 return CAB_STATUS_CANNOT_OPEN;
1332 }
1333
1334 FileOpen = TRUE;
1335
1336 /* Load CAB header */
1337 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) != CAB_STATUS_SUCCESS)
1338 {
1339 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1340 return CAB_STATUS_INVALID_CAB;
1341 }
1342
1343 /* Check header */
1344 if ((BytesRead != sizeof(CFHEADER)) ||
1345 (CABHeader.Signature != CAB_SIGNATURE ) ||
1346 (CABHeader.Version != CAB_VERSION ) ||
1347 (CABHeader.FolderCount == 0 ) ||
1348 (CABHeader.FileCount == 0 ) ||
1349 (CABHeader.FileTableOffset < sizeof(CFHEADER))) {
1350 CloseCabinet();
1351 DPRINT("File has invalid header.\n");
1352 return CAB_STATUS_INVALID_CAB;
1353 }
1354
1355 Size = 0;
1356
1357 /* Read/skip any reserved bytes */
1358 if (CABHeader.Flags & CAB_FLAG_RESERVE)
1359 {
1360 if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) != CAB_STATUS_SUCCESS)
1361 {
1362 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1363 return CAB_STATUS_INVALID_CAB;
1364 }
1365 CabinetReserved = Size & 0xFFFF;
1366 FolderReserved = (Size >> 16) & 0xFF;
1367 DataReserved = (Size >> 24) & 0xFF;
1368
1369 if (CabinetReserved > 0)
1370 {
1371 CabinetReservedArea = RtlAllocateHeap(ProcessHeap, 0, CabinetReserved);
1372 if (!CabinetReservedArea)
1373 {
1374 return CAB_STATUS_NOMEMORY;
1375 }
1376
1377 if ((Status = ReadBlock(CabinetReservedArea, CabinetReserved, &BytesRead)) != CAB_STATUS_SUCCESS)
1378 {
1379 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1380 return CAB_STATUS_INVALID_CAB;
1381 }
1382 }
1383 #if 0
1384 SeekInFile(FileHandle, CabinetReserved, NULL, SEEK_CURRENT, &NtStatus);
1385 if (!NT_SUCCESS(NtStatus))
1386 {
1387 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1388 return CAB_STATUS_INVALID_CAB;
1389 }
1390 #endif
1391 }
1392
1393 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
1394 {
1395 /* Read name of previous cabinet */
1396 Status = ReadString(CabinetFileName, 256);
1397 if (Status != CAB_STATUS_SUCCESS)
1398 return Status;
1399
1400 /* The previous cabinet file is in the same directory as the current */
1401 wcscpy(CabinetPrev, CabinetName);
1402 RemoveFileName(CabinetPrev);
1403 CabinetNormalizePath(CabinetPrev, 256);
1404 wcscat(CabinetPrev, CabinetFileName);
1405
1406 /* Read label of previous disk */
1407 Status = ReadString(DiskPrev, 256);
1408 if (Status != CAB_STATUS_SUCCESS)
1409 return Status;
1410 }
1411 else
1412 {
1413 wcscpy(CabinetPrev, L"");
1414 wcscpy(DiskPrev, L"");
1415 }
1416
1417 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
1418 {
1419 /* Read name of next cabinet */
1420 Status = ReadString(CabinetFileName, 256);
1421 if (Status != CAB_STATUS_SUCCESS)
1422 return Status;
1423
1424 /* The next cabinet file is in the same directory as the previous */
1425 wcscpy(CabinetNext, CabinetName);
1426 RemoveFileName(CabinetNext);
1427 CabinetNormalizePath(CabinetNext, 256);
1428 wcscat(CabinetNext, CabinetFileName);
1429
1430 /* Read label of next disk */
1431 Status = ReadString(DiskNext, 256);
1432 if (Status != CAB_STATUS_SUCCESS)
1433 return Status;
1434 }
1435 else
1436 {
1437 wcscpy(CabinetNext, L"");
1438 wcscpy(DiskNext, L"");
1439 }
1440
1441 /* Read all folders */
1442 for (Index = 0; Index < CABHeader.FolderCount; Index++)
1443 {
1444 FolderNode = NewFolderNode();
1445 if (!FolderNode)
1446 {
1447 DPRINT("Insufficient resources.\n");
1448 return CAB_STATUS_NOMEMORY;
1449 }
1450
1451 if (Index == 0)
1452 FolderNode->UncompOffset = FolderUncompSize;
1453
1454 FolderNode->Index = Index;
1455
1456 if ((Status = ReadBlock(&FolderNode->Folder,
1457 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS)
1458 {
1459 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1460 return CAB_STATUS_INVALID_CAB;
1461 }
1462 }
1463
1464 /* Read file entries */
1465 Status = ReadFileTable();
1466 if (Status != CAB_STATUS_SUCCESS)
1467 {
1468 DPRINT("ReadFileTable() failed (%d).\n", (UINT)Status);
1469 return Status;
1470 }
1471
1472 /* Read data blocks for all folders */
1473 FolderNode = FolderListHead;
1474 while (FolderNode != NULL)
1475 {
1476 Status = ReadDataBlocks(FolderNode);
1477 if (Status != CAB_STATUS_SUCCESS)
1478 {
1479 DPRINT("ReadDataBlocks() failed (%d).\n", (UINT)Status);
1480 return Status;
1481 }
1482 FolderNode = FolderNode->Next;
1483 }
1484 }
1485 return CAB_STATUS_SUCCESS;
1486 }
1487
1488
1489 VOID
1490 CabinetClose()
1491 /*
1492 * FUNCTION: Closes the cabinet file
1493 */
1494 {
1495 if (FileOpen)
1496 {
1497 CloseCabinet();
1498
1499 if (CabinetReservedArea != NULL)
1500 {
1501 RtlFreeHeap(ProcessHeap, 0, CabinetReservedArea);
1502 CabinetReservedArea = NULL;
1503 }
1504
1505 FileOpen = FALSE;
1506 }
1507 }
1508
1509
1510 ULONG
1511 CabinetFindFirst(PWCHAR FileName,
1512 PCAB_SEARCH Search)
1513 /*
1514 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
1515 * ARGUMENTS:
1516 * FileName = Pointer to search criteria
1517 * Search = Pointer to search structure
1518 * RETURNS:
1519 * Status of operation
1520 */
1521 {
1522 RestartSearch = FALSE;
1523 wcsncpy(Search->Search, FileName, MAX_PATH);
1524 Search->Next = FileListHead;
1525 return CabinetFindNext(Search);
1526 }
1527
1528
1529 ULONG
1530 CabinetFindNext(PCAB_SEARCH Search)
1531 /*
1532 * FUNCTION: Finds next file in the cabinet that matches a search criteria
1533 * ARGUMENTS:
1534 * Search = Pointer to search structure
1535 * RETURNS:
1536 * Status of operation
1537 */
1538 {
1539 ULONG Status;
1540
1541 if (RestartSearch)
1542 {
1543 Search->Next = FileListHead;
1544
1545 /* Skip split files already extracted */
1546 while ((Search->Next) &&
1547 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) &&
1548 (Search->Next->File.FileOffset <= LastFileOffset))
1549 {
1550 DPRINT("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
1551 Search->Next->FileName, Search->Next->File.FileOffset, LastFileOffset);
1552 Search->Next = Search->Next->Next;
1553 }
1554
1555 RestartSearch = FALSE;
1556 }
1557
1558 /* FIXME: Check search criteria */
1559
1560 if (!Search->Next)
1561 {
1562 if (wcslen(DiskNext) > 0)
1563 {
1564 CloseCabinet();
1565
1566 CabinetSetCabinetName(CabinetNext);
1567
1568 if (DiskChangeHandler != NULL)
1569 {
1570 DiskChangeHandler(CabinetNext, DiskNext);
1571 }
1572
1573 Status = CabinetOpen();
1574 if (Status != CAB_STATUS_SUCCESS)
1575 return Status;
1576
1577 Search->Next = FileListHead;
1578 if (!Search->Next)
1579 return CAB_STATUS_NOFILE;
1580 }
1581 else
1582 {
1583 return CAB_STATUS_NOFILE;
1584 }
1585 }
1586
1587 Search->File = &Search->Next->File;
1588 Search->FileName = Search->Next->FileName;
1589 Search->Next = Search->Next->Next;
1590 return CAB_STATUS_SUCCESS;
1591 }
1592
1593
1594 ULONG
1595 CabinetExtractFile(PWCHAR FileName)
1596 /*
1597 * FUNCTION: Extracts a file from the cabinet
1598 * ARGUMENTS:
1599 * FileName = Pointer to buffer with name of file
1600 * RETURNS
1601 * Status of operation
1602 */
1603 {
1604 ULONG Size;
1605 ULONG Offset;
1606 ULONG BytesRead;
1607 ULONG BytesToRead;
1608 ULONG BytesWritten;
1609 ULONG BytesSkipped;
1610 ULONG BytesToWrite;
1611 ULONG TotalBytesRead;
1612 ULONG CurrentOffset;
1613 PUCHAR Buffer;
1614 PUCHAR CurrentBuffer;
1615 HANDLE DestFile;
1616 PCFFILE_NODE File;
1617 CFDATA CFData;
1618 ULONG Status;
1619 BOOL Skip;
1620 FILETIME FileTime;
1621 WCHAR DestName[MAX_PATH];
1622 WCHAR TempName[MAX_PATH];
1623 NTSTATUS NtStatus;
1624 UNICODE_STRING UnicodeString;
1625 IO_STATUS_BLOCK IoStatusBlock;
1626 OBJECT_ATTRIBUTES ObjectAttributes;
1627 FILE_BASIC_INFORMATION FileBasic;
1628
1629 Status = LocateFile(FileName, &File);
1630 if (Status != CAB_STATUS_SUCCESS)
1631 {
1632 DPRINT("Cannot locate file (%d).\n", (UINT)Status);
1633 return Status;
1634 }
1635
1636 LastFileOffset = File->File.FileOffset;
1637
1638 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK)
1639 {
1640 case CAB_COMP_NONE:
1641 CabinetSelectCodec(CAB_CODEC_RAW);
1642 break;
1643 case CAB_COMP_MSZIP:
1644 CabinetSelectCodec(CAB_CODEC_MSZIP);
1645 break;
1646 default:
1647 return CAB_STATUS_UNSUPPCOMP;
1648 }
1649
1650 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1651 (UINT)File->File.FileOffset,
1652 (UINT)File->File.FileSize,
1653 (UINT)File->DataBlock->AbsoluteOffset,
1654 (UINT)File->DataBlock->UncompOffset);
1655
1656 wcscpy(DestName, DestPath);
1657 wcscat(DestName, FileName);
1658
1659 /* Create destination file, fail if it already exists */
1660 RtlInitUnicodeString(&UnicodeString,
1661 DestName);
1662
1663 InitializeObjectAttributes(&ObjectAttributes,
1664 &UnicodeString,
1665 OBJ_CASE_INSENSITIVE,
1666 NULL,
1667 NULL);
1668
1669 NtStatus = NtCreateFile(&DestFile,
1670 FILE_WRITE_ACCESS,
1671 &ObjectAttributes,
1672 &IoStatusBlock,
1673 NULL,
1674 FILE_ATTRIBUTE_NORMAL,
1675 0,
1676 FILE_CREATE,
1677 FILE_SYNCHRONOUS_IO_ALERT,
1678 NULL,
1679 0);
1680 if (!NT_SUCCESS(NtStatus))
1681 {
1682 DPRINT("NtCreateFile() failed (%S) (%x).\n", DestName, NtStatus);
1683
1684 /* If file exists, ask to overwrite file */
1685 if (OverwriteHandler == NULL || OverwriteHandler(&File->File, FileName))
1686 {
1687 /* Create destination file, overwrite if it already exists */
1688 NtStatus = NtCreateFile(&DestFile,
1689 FILE_WRITE_ACCESS,
1690 &ObjectAttributes,
1691 &IoStatusBlock,
1692 NULL,
1693 FILE_ATTRIBUTE_NORMAL,
1694 0,
1695 FILE_OVERWRITE,
1696 FILE_SYNCHRONOUS_IO_ALERT,
1697 NULL,
1698 0);
1699 if (!NT_SUCCESS(NtStatus))
1700 {
1701 DPRINT("NtCreateFile() failed 2 (%S) (%x).\n", DestName, NtStatus);
1702 return CAB_STATUS_CANNOT_CREATE;
1703 }
1704 }
1705 else
1706 {
1707 DPRINT("File (%S) exists.\n", DestName);
1708 return CAB_STATUS_FILE_EXISTS;
1709 }
1710 }
1711
1712 if (!ConvertDosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime))
1713 {
1714 NtClose(DestFile);
1715 DPRINT("DosDateTimeToFileTime() failed.\n");
1716 return CAB_STATUS_CANNOT_WRITE;
1717 }
1718
1719 NtStatus = NtQueryInformationFile(DestFile,
1720 &IoStatusBlock,
1721 &FileBasic,
1722 sizeof(FILE_BASIC_INFORMATION),
1723 FileBasicInformation);
1724 if (!NT_SUCCESS(Status))
1725 {
1726 DPRINT("NtQueryInformationFile() failed (%x).\n", NtStatus);
1727 }
1728 else
1729 {
1730 memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME));
1731
1732 NtStatus = NtSetInformationFile(DestFile,
1733 &IoStatusBlock,
1734 &FileBasic,
1735 sizeof(FILE_BASIC_INFORMATION),
1736 FileBasicInformation);
1737 if (!NT_SUCCESS(NtStatus))
1738 {
1739 DPRINT("NtSetInformationFile() failed (%x).\n", NtStatus);
1740 }
1741 }
1742
1743 SetAttributesOnFile(File, DestFile);
1744
1745 Buffer = RtlAllocateHeap(ProcessHeap, 0, CAB_BLOCKSIZE + 12); // This should be enough
1746 if (!Buffer)
1747 {
1748 NtClose(DestFile);
1749 DPRINT("Insufficient memory.\n");
1750 return CAB_STATUS_NOMEMORY;
1751 }
1752
1753 /* Call extract event handler */
1754 if (ExtractHandler != NULL)
1755 {
1756 ExtractHandler(&File->File, FileName);
1757 }
1758
1759 /* Search to start of file */
1760 Offset = SeekInFile(FileHandle,
1761 File->DataBlock->AbsoluteOffset,
1762 NULL,
1763 SEEK_BEGIN,
1764 &NtStatus);
1765 if (!NT_SUCCESS(NtStatus))
1766 {
1767 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1768 return CAB_STATUS_INVALID_CAB;
1769 }
1770
1771 Size = File->File.FileSize;
1772 Offset = File->File.FileOffset;
1773 CurrentOffset = File->DataBlock->UncompOffset;
1774
1775 Skip = TRUE;
1776
1777 ReuseBlock = (CurrentDataNode == File->DataBlock);
1778 if (Size > 0)
1779 {
1780 do
1781 {
1782 DPRINT("CO (0x%X) ReuseBlock (%d) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1783 File->DataBlock->UncompOffset, (UINT)ReuseBlock, Offset, Size,
1784 BytesLeftInBlock);
1785
1786 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0))
1787 {
1788 DPRINT("Filling buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock);
1789
1790 CurrentBuffer = Buffer;
1791 TotalBytesRead = 0;
1792 do
1793 {
1794 DPRINT("Size (%d bytes).\n", Size);
1795
1796 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1797 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1798 {
1799 NtClose(DestFile);
1800 RtlFreeHeap(ProcessHeap, 0, Buffer);
1801 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1802 return CAB_STATUS_INVALID_CAB;
1803 }
1804
1805 DPRINT("Data block: Checksum (0x%X) CompSize (%d bytes) UncompSize (%d bytes) Offset (0x%X).\n",
1806 (UINT)CFData.Checksum,
1807 (UINT)CFData.CompSize,
1808 (UINT)CFData.UncompSize,
1809 (UINT)SeekInFile(FileHandle, 0, NULL, SEEK_CURRENT, &NtStatus));
1810
1811 //ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1812
1813 BytesToRead = CFData.CompSize;
1814
1815 DPRINT("Read: (0x%X,0x%X).\n",
1816 CurrentBuffer, Buffer);
1817
1818 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) !=
1819 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead))
1820 {
1821 NtClose(DestFile);
1822 RtlFreeHeap(ProcessHeap, 0, Buffer);
1823 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1824 return CAB_STATUS_INVALID_CAB;
1825 }
1826
1827 /* FIXME: Does not work with files generated by makecab.exe */
1828 /*
1829 if (CFData.Checksum != 0)
1830 {
1831 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1832 if (Checksum != CFData.Checksum)
1833 {
1834 NtClose(DestFile);
1835 RtlFreeHeap(ProcessHeap, 0, Buffer);
1836 DPRINT("Bad checksum (is 0x%X, should be 0x%X).\n",
1837 Checksum, CFData.Checksum);
1838 return CAB_STATUS_INVALID_CAB;
1839 }
1840 }
1841 */
1842 TotalBytesRead += BytesRead;
1843
1844 CurrentBuffer += BytesRead;
1845
1846 if (CFData.UncompSize == 0)
1847 {
1848 if (wcslen(DiskNext) == 0)
1849 return CAB_STATUS_NOFILE;
1850
1851 /* CloseCabinet() will destroy all file entries so in case
1852 FileName refers to the FileName field of a CFFOLDER_NODE
1853 structure, we have to save a copy of the filename */
1854 wcscpy(TempName, FileName);
1855
1856 CloseCabinet();
1857
1858 CabinetSetCabinetName(CabinetNext);
1859
1860 if (DiskChangeHandler != NULL)
1861 {
1862 DiskChangeHandler(CabinetNext, DiskNext);
1863 }
1864
1865 Status = CabinetOpen();
1866 if (Status != CAB_STATUS_SUCCESS)
1867 return Status;
1868
1869 /* The first data block of the file will not be
1870 found as it is located in the previous file */
1871 Status = LocateFile(TempName, &File);
1872 if (Status == CAB_STATUS_NOFILE)
1873 {
1874 DPRINT("Cannot locate file (%d).\n", (UINT)Status);
1875 return Status;
1876 }
1877
1878 /* The file is continued in the first data block in the folder */
1879 File->DataBlock = CurrentFolderNode->DataListHead;
1880
1881 /* Search to start of file */
1882 SeekInFile(FileHandle,
1883 File->DataBlock->AbsoluteOffset,
1884 NULL,
1885 SEEK_BEGIN,
1886 &NtStatus);
1887 if (!NT_SUCCESS(NtStatus))
1888 {
1889 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1890 return CAB_STATUS_INVALID_CAB;
1891 }
1892
1893 DPRINT("Continuing extraction of file at uncompressed offset (0x%X) Size (%d bytes) AO (0x%X) UO (0x%X).\n",
1894 (UINT)File->File.FileOffset,
1895 (UINT)File->File.FileSize,
1896 (UINT)File->DataBlock->AbsoluteOffset,
1897 (UINT)File->DataBlock->UncompOffset);
1898
1899 CurrentDataNode = File->DataBlock;
1900 ReuseBlock = TRUE;
1901
1902 RestartSearch = TRUE;
1903 }
1904 } while (CFData.UncompSize == 0);
1905
1906 DPRINT("TotalBytesRead (%d).\n", TotalBytesRead);
1907
1908 Status = CodecUncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite);
1909 if (Status != CS_SUCCESS)
1910 {
1911 NtClose(DestFile);
1912 RtlFreeHeap(ProcessHeap, 0, Buffer);
1913 DPRINT("Cannot uncompress block.\n");
1914 if (Status == CS_NOMEMORY)
1915 return CAB_STATUS_NOMEMORY;
1916 return CAB_STATUS_INVALID_CAB;
1917 }
1918
1919 if (BytesToWrite != CFData.UncompSize)
1920 {
1921 DPRINT("BytesToWrite (%d) != CFData.UncompSize (%d)\n",
1922 BytesToWrite, CFData.UncompSize);
1923 return CAB_STATUS_INVALID_CAB;
1924 }
1925
1926 BytesLeftInBlock = BytesToWrite;
1927 }
1928 else
1929 {
1930 DPRINT("Using same buffer. ReuseBlock (%d)\n", (UINT)ReuseBlock);
1931
1932 BytesToWrite = BytesLeftInBlock;
1933
1934 DPRINT("Seeking to absolute offset 0x%X.\n",
1935 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1936 CurrentDataNode->Data.CompSize);
1937
1938 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1939 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1940 {
1941 NtClose(DestFile);
1942 RtlFreeHeap(ProcessHeap, 0, Buffer);
1943 DPRINT("Cannot read from file (%d).\n", (UINT)Status);
1944 return CAB_STATUS_INVALID_CAB;
1945 }
1946
1947 DPRINT("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1948 CFData.CompSize, CFData.UncompSize);
1949
1950 /* Go to next data block */
1951 SeekInFile(FileHandle,
1952 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1953 CurrentDataNode->Data.CompSize,
1954 NULL,
1955 SEEK_BEGIN,
1956 &NtStatus);
1957 if (!NT_SUCCESS(NtStatus))
1958 {
1959 DPRINT("SeekInFile() failed (%x).\n", NtStatus);
1960 return CAB_STATUS_INVALID_CAB;
1961 }
1962
1963 ReuseBlock = FALSE;
1964 }
1965
1966 if (Skip)
1967 BytesSkipped = (Offset - CurrentOffset);
1968 else
1969 BytesSkipped = 0;
1970
1971 BytesToWrite -= BytesSkipped;
1972
1973 if (Size < BytesToWrite)
1974 BytesToWrite = Size;
1975
1976 DPRINT("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%d) Skipped (%d)(%d) Size (%d).\n",
1977 (UINT)Offset,
1978 (UINT)CurrentOffset,
1979 (UINT)BytesToWrite,
1980 (UINT)BytesSkipped, (UINT)Skip,
1981 (UINT)Size);
1982
1983 // if (!WriteFile(DestFile, (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1984 // BytesToWrite, &BytesWritten, NULL) ||
1985 // (BytesToWrite != BytesWritten))
1986
1987 NtStatus = NtWriteFile(DestFile,
1988 NULL,
1989 NULL,
1990 NULL,
1991 &IoStatusBlock,
1992 (PVOID)((ULONG)OutputBuffer + BytesSkipped),
1993 BytesToWrite,
1994 NULL,
1995 NULL);
1996 BytesWritten = BytesToWrite;
1997 if (!NT_SUCCESS(NtStatus))
1998 {
1999 DPRINT("Status 0x%X.\n", NtStatus);
2000
2001 NtClose(DestFile);
2002 RtlFreeHeap(ProcessHeap, 0, Buffer);
2003 DPRINT("Cannot write to file.\n");
2004 return CAB_STATUS_CANNOT_WRITE;
2005 }
2006 Size -= BytesToWrite;
2007
2008 CurrentOffset += BytesToWrite;
2009
2010 /* Don't skip any more bytes */
2011 Skip = FALSE;
2012 } while (Size > 0);
2013 }
2014
2015 NtClose(DestFile);
2016
2017 RtlFreeHeap(ProcessHeap, 0, Buffer);
2018
2019 return CAB_STATUS_SUCCESS;
2020 }
2021
2022
2023 VOID
2024 CabinetSelectCodec(ULONG Id)
2025 /*
2026 * FUNCTION: Selects codec engine to use
2027 * ARGUMENTS:
2028 * Id = Codec identifier
2029 */
2030 {
2031 if (CodecSelected)
2032 {
2033 if (Id == CodecId)
2034 return;
2035
2036 CodecSelected = FALSE;
2037 }
2038
2039 switch (Id)
2040 {
2041 case CAB_CODEC_RAW:
2042 CodecUncompress = RawCodecUncompress;
2043 break;
2044 case CAB_CODEC_MSZIP:
2045 CodecUncompress = MSZipCodecUncompress;
2046 break;
2047 default:
2048 return;
2049 }
2050
2051 CodecId = Id;
2052 CodecSelected = TRUE;
2053 }
2054
2055
2056 VOID
2057 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite,
2058 PCABINET_EXTRACT Extract,
2059 PCABINET_DISK_CHANGE DiskChange)
2060 /*
2061 * FUNCTION: Set event handlers
2062 * ARGUMENTS:
2063 * Overwrite = Handler called when a file is to be overwritten
2064 * Extract = Handler called when a file is to be extracted
2065 * DiskChange = Handler called when changing the disk
2066 */
2067 {
2068 OverwriteHandler = Overwrite;
2069 ExtractHandler = Extract;
2070 DiskChangeHandler = DiskChange;
2071 }
2072
2073
2074 PVOID
2075 CabinetGetCabinetReservedArea(PULONG Size)
2076 /*
2077 * FUNCTION: Get pointer to cabinet reserved area. NULL if none
2078 */
2079 {
2080 if (CabinetReservedArea != NULL)
2081 {
2082 if (Size != NULL)
2083 {
2084 *Size = CabinetReserved;
2085 }
2086 return CabinetReservedArea;
2087 }
2088 else
2089 {
2090 if (Size != NULL)
2091 {
2092 *Size = 0;
2093 }
2094 return NULL;
2095 }
2096 }