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