5e951d4968e87e06533fb1ac12b86d9c7e585577
[reactos.git] / reactos / sdk / tools / cabman / cabinet.cxx
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS cabinet manager
4 * FILE: tools/cabman/cabinet.cxx
5 * PURPOSE: Cabinet routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Colin Finck <mail@colinfinck.de>
8 * NOTES: Define CAB_READ_ONLY for read only version
9 * REVISIONS:
10 * CSH 21/03-2001 Created
11 * CSH 15/08-2003 Made it portable
12 * CF 04/05-2007 Made it compatible with 64-bit operating systems
13 * TODO:
14 * - Checksum of datablocks should be calculated
15 * - EXTRACT.EXE complains if a disk is created manually
16 * - Folders that are created manually and span disks will result in a damaged cabinet
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #if !defined(_WIN32)
22 # include <dirent.h>
23 # include <sys/stat.h>
24 # include <sys/types.h>
25 #endif
26 #include "cabinet.h"
27 #include "raw.h"
28 #include "mszip.h"
29
30 #if defined(_WIN32)
31 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
32 static LONG _GetSizeOfFile(FILEHANDLE handle)
33 {
34 ULONG size = GetFileSize(handle, NULL);
35 if (size == INVALID_FILE_SIZE)
36 return -1;
37
38 return size;
39 }
40 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
41 static bool _ReadFileData(FILEHANDLE handle, void* buffer, ULONG size, PULONG bytesread)
42 {
43 return ReadFile(handle, buffer, size, (LPDWORD)bytesread, NULL) != 0;
44 }
45 #else
46 #define GetSizeOfFile(handle) _GetSizeOfFile(handle)
47 static LONG _GetSizeOfFile(FILEHANDLE handle)
48 {
49 LONG size;
50 fseek(handle, 0, SEEK_END);
51 size = ftell(handle);
52 fseek(handle, 0, SEEK_SET);
53 return size;
54 }
55 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread)
56 static bool _ReadFileData(FILEHANDLE handle, void* buffer, ULONG size, PULONG bytesread)
57 {
58 *bytesread = fread(buffer, 1, size, handle);
59 return *bytesread == size;
60 }
61 #endif
62
63 #ifndef CAB_READ_ONLY
64
65 #if 0
66 #if DBG
67
68 void DumpBuffer(void* Buffer, ULONG Size)
69 {
70 FILEHANDLE FileHandle;
71 ULONG BytesWritten;
72
73 /* Create file, overwrite if it already exists */
74 FileHandle = CreateFile("dump.bin", // Create this file
75 GENERIC_WRITE, // Open for writing
76 0, // No sharing
77 NULL, // No security
78 CREATE_ALWAYS, // Create or overwrite
79 FILE_ATTRIBUTE_NORMAL, // Normal file
80 NULL); // No attribute template
81 if (FileHandle == INVALID_HANDLE_VALUE)
82 {
83 DPRINT(MID_TRACE, ("ERROR OPENING '%u'.\n", (UINT)GetLastError()));
84 return;
85 }
86
87 if (!WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL))
88 {
89 DPRINT(MID_TRACE, ("ERROR WRITING '%u'.\n", (UINT)GetLastError()));
90 }
91
92 CloseFile(FileHandle);
93 }
94
95 #endif /* DBG */
96 #endif
97
98 /* CCFDATAStorage */
99
100 CCFDATAStorage::CCFDATAStorage()
101 /*
102 * FUNCTION: Default constructor
103 */
104 {
105 FileCreated = false;
106 }
107
108
109 CCFDATAStorage::~CCFDATAStorage()
110 /*
111 * FUNCTION: Default destructor
112 */
113 {
114 ASSERT(!FileCreated);
115 }
116
117
118 ULONG CCFDATAStorage::Create()
119 /*
120 * FUNCTION: Creates the file
121 * ARGUMENTS:
122 * FileName = Pointer to name of file
123 * RETURNS:
124 * Status of operation
125 */
126 {
127 #if defined(_WIN32)
128 char tmpPath[MAX_PATH];
129 #endif
130 ASSERT(!FileCreated);
131
132 #if defined(_WIN32)
133 if (GetTempPath(MAX_PATH, tmpPath) == 0)
134 return CAB_STATUS_CANNOT_CREATE;
135 if(GetTempFileName(tmpPath, "cab", 0, FullName) == 0)
136 return CAB_STATUS_CANNOT_CREATE;
137
138 /* Create file, overwrite if it already exists */
139 FileHandle = CreateFile(FullName, // Create this file
140 GENERIC_READ | GENERIC_WRITE, // Open for reading/writing
141 0, // No sharing
142 NULL, // No security
143 CREATE_ALWAYS, // Create or overwrite
144 FILE_FLAG_SEQUENTIAL_SCAN | // Optimize for sequential scans
145 FILE_FLAG_DELETE_ON_CLOSE | // Delete file when closed
146 FILE_ATTRIBUTE_TEMPORARY, // Temporary file
147 NULL); // No attribute template
148 if (FileHandle == INVALID_HANDLE_VALUE)
149 {
150 DPRINT(MID_TRACE, ("ERROR '%u'.\n", (UINT)GetLastError()));
151 return CAB_STATUS_CANNOT_CREATE;
152 }
153 #else /* !_WIN32 */
154 /*if (tmpnam(FullName) == NULL)*/
155 if ((FileHandle = tmpfile()) == NULL)
156 return CAB_STATUS_CANNOT_CREATE;
157 /*
158 FileHandle = fopen(FullName, "w+b");
159 if (FileHandle == NULL) {
160 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno));
161 return CAB_STATUS_CANNOT_CREATE;
162 }
163 */
164 #endif
165
166 FileCreated = true;
167
168 return CAB_STATUS_SUCCESS;
169 }
170
171
172 ULONG CCFDATAStorage::Destroy()
173 /*
174 * FUNCTION: Destroys the file
175 * RETURNS:
176 * Status of operation
177 */
178 {
179 ASSERT(FileCreated);
180
181 CloseFile(FileHandle);
182
183 FileCreated = false;
184
185 return CAB_STATUS_SUCCESS;
186 }
187
188
189 ULONG CCFDATAStorage::Truncate()
190 /*
191 * FUNCTION: Truncate the scratch file to zero bytes
192 * RETURNS:
193 * Status of operation
194 */
195 {
196 #if defined(_WIN32)
197 if( SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER )
198 return CAB_STATUS_FAILURE;
199 if (!SetEndOfFile(FileHandle))
200 return CAB_STATUS_FAILURE;
201 #else /* !_WIN32 */
202 fclose(FileHandle);
203 FileHandle = tmpfile();
204 if (FileHandle == NULL)
205 {
206 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno));
207 return CAB_STATUS_FAILURE;
208 }
209 #endif
210 return CAB_STATUS_SUCCESS;
211 }
212
213
214 ULONG CCFDATAStorage::Position()
215 /*
216 * FUNCTION: Returns current position in file
217 * RETURNS:
218 * Current position
219 */
220 {
221 #if defined(_WIN32)
222 return SetFilePointer(FileHandle, 0, NULL, FILE_CURRENT);
223 #else
224 return (ULONG)ftell(FileHandle);
225 #endif
226 }
227
228
229 ULONG CCFDATAStorage::Seek(LONG Position)
230 /*
231 * FUNCTION: Seeks to an absolute position
232 * ARGUMENTS:
233 * Position = Absolute position to seek to
234 * RETURNS:
235 * Status of operation
236 */
237 {
238 #if defined(_WIN32)
239 if( SetFilePointer(FileHandle,
240 Position,
241 NULL,
242 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
243 return CAB_STATUS_FAILURE;
244 else
245 return CAB_STATUS_SUCCESS;
246 #else
247 if (fseek(FileHandle, (off_t)Position, SEEK_SET) != 0)
248 return CAB_STATUS_FAILURE;
249 else
250 return CAB_STATUS_SUCCESS;
251 #endif
252 }
253
254
255 ULONG CCFDATAStorage::ReadBlock(PCFDATA Data, void* Buffer, PULONG BytesRead)
256 /*
257 * FUNCTION: Reads a CFDATA block from the file
258 * ARGUMENTS:
259 * Data = Pointer to CFDATA block for the buffer
260 * Buffer = Pointer to buffer to store data read
261 * BytesWritten = Pointer to buffer to write number of bytes read
262 * RETURNS:
263 * Status of operation
264 */
265 {
266 #if defined(_WIN32)
267 if (!ReadFile(FileHandle, Buffer, Data->CompSize, (LPDWORD)BytesRead, NULL))
268 return CAB_STATUS_CANNOT_READ;
269 #else
270
271 *BytesRead = fread(Buffer, 1, Data->CompSize, FileHandle);
272 if (*BytesRead != Data->CompSize)
273 return CAB_STATUS_CANNOT_READ;
274 #endif
275 return CAB_STATUS_SUCCESS;
276 }
277
278
279 ULONG CCFDATAStorage::WriteBlock(PCFDATA Data, void* Buffer, PULONG BytesWritten)
280 /*
281 * FUNCTION: Writes a CFDATA block to the file
282 * ARGUMENTS:
283 * Data = Pointer to CFDATA block for the buffer
284 * Buffer = Pointer to buffer with data to write
285 * BytesWritten = Pointer to buffer to write number of bytes written
286 * RETURNS:
287 * Status of operation
288 */
289 {
290 #if defined(_WIN32)
291 if (!WriteFile(FileHandle, Buffer, Data->CompSize, (LPDWORD)BytesWritten, NULL))
292 return CAB_STATUS_CANNOT_WRITE;
293 #else
294 *BytesWritten = fwrite(Buffer, 1, Data->CompSize, FileHandle);
295 if (*BytesWritten != Data->CompSize)
296 return CAB_STATUS_CANNOT_WRITE;
297 #endif
298 return CAB_STATUS_SUCCESS;
299 }
300
301 #endif /* CAB_READ_ONLY */
302
303
304 /* CCabinet */
305
306 CCabinet::CCabinet()
307 /*
308 * FUNCTION: Default constructor
309 */
310 {
311 *CabinetName = '\0';
312 *CabinetPrev = '\0';
313 *DiskPrev = '\0';
314 *CabinetNext = '\0';
315 *DiskNext = '\0';
316 *DestPath = '\0';
317 *CabinetReservedFile = '\0';
318
319 FileOpen = false;
320 CabinetReservedFileBuffer = NULL;
321 CabinetReservedFileSize = 0;
322
323 FolderListHead = NULL;
324 FolderListTail = NULL;
325 FileListHead = NULL;
326 FileListTail = NULL;
327 CriteriaListHead = NULL;
328 CriteriaListTail = NULL;
329
330 Codec = NULL;
331 CodecId = -1;
332 CodecSelected = false;
333
334 OutputBuffer = NULL;
335 InputBuffer = NULL;
336 MaxDiskSize = 0;
337 BlockIsSplit = false;
338 ScratchFile = NULL;
339
340 FolderUncompSize = 0;
341 BytesLeftInBlock = 0;
342 ReuseBlock = false;
343 CurrentDataNode = NULL;
344 }
345
346
347 CCabinet::~CCabinet()
348 /*
349 * FUNCTION: Default destructor
350 */
351 {
352 if (CabinetReservedFileBuffer != NULL)
353 {
354 FreeMemory(CabinetReservedFileBuffer);
355 CabinetReservedFileBuffer = NULL;
356 CabinetReservedFileSize = 0;
357 }
358
359 if (CodecSelected)
360 delete Codec;
361 }
362
363 bool CCabinet::IsSeparator(char Char)
364 /*
365 * FUNCTION: Determines if a character is a separator
366 * ARGUMENTS:
367 * Char = Character to check
368 * RETURNS:
369 * Whether it is a separator
370 */
371 {
372 if ((Char == '\\') || (Char == '/'))
373 return true;
374 else
375 return false;
376 }
377
378 char* CCabinet::ConvertPath(char* Path, bool Allocate)
379 /*
380 * FUNCTION: Replaces \ or / with the one used by the host environment
381 * ARGUMENTS:
382 * Path = Pointer to string with pathname
383 * Allocate = Specifies whether to allocate memory for the new
384 * string or to change the existing buffer
385 * RETURNS:
386 * Pointer to new path
387 */
388 {
389 char *newpath;
390 int i;
391
392 if (Allocate)
393 newpath = strdup(Path);
394 else
395 newpath = Path;
396
397 i = 0;
398 while (Path[i] != 0)
399 {
400 #if defined(_WIN32)
401 if (Path[i] == '/')
402 newpath[i] = '\\';
403 else
404 #else
405 if (Path[i] == '\\')
406 newpath[i] = '/';
407 else
408 #endif
409 newpath[i] = Path[i];
410
411 i++;
412 }
413 newpath[i] = 0;
414
415 return(newpath);
416 }
417
418
419 char* CCabinet::GetFileName(char* Path)
420 /*
421 * FUNCTION: Returns a pointer to file name
422 * ARGUMENTS:
423 * Path = Pointer to string with pathname
424 * RETURNS:
425 * Pointer to filename
426 */
427 {
428 ULONG i, j;
429
430 j = i = (Path[0] ? (Path[1] == ':' ? 2 : 0) : 0);
431
432 while (Path [i++])
433 if (IsSeparator(Path [i - 1]))
434 j = i;
435
436 return Path + j;
437 }
438
439
440 void CCabinet::RemoveFileName(char* Path)
441 /*
442 * FUNCTION: Removes a file name from a path
443 * ARGUMENTS:
444 * Path = Pointer to string with path
445 */
446 {
447 char* FileName;
448 ULONG i;
449
450 i = (Path [0] ? (Path[1] == ':' ? 2 : 0) : 0);
451 FileName = GetFileName(Path + i);
452
453 if ((FileName != (Path + i)) && (IsSeparator(FileName [-1])))
454 FileName--;
455 if ((FileName == (Path + i)) && (IsSeparator(FileName [0])))
456 FileName++;
457 FileName[0] = 0;
458 }
459
460
461 bool CCabinet::NormalizePath(char* Path,
462 ULONG Length)
463 /*
464 * FUNCTION: Normalizes a path
465 * ARGUMENTS:
466 * Path = Pointer to string with pathname
467 * Length = Number of bytes in Path
468 * RETURNS:
469 * true if there was enough room in Path, or false
470 */
471 {
472 ULONG n;
473 bool OK = true;
474
475 if ((n = (ULONG)strlen(Path)) &&
476 (!IsSeparator(Path[n - 1])) &&
477 (OK = ((n + 1) < Length)))
478 {
479 Path[n] = DIR_SEPARATOR_CHAR;
480 Path[n + 1] = 0;
481 }
482 return OK;
483 }
484
485
486 char* CCabinet::GetCabinetName()
487 /*
488 * FUNCTION: Returns pointer to cabinet file name
489 * RETURNS:
490 * Pointer to string with name of cabinet
491 */
492 {
493 return CabinetName;
494 }
495
496
497 void CCabinet::SetCabinetName(char* FileName)
498 /*
499 * FUNCTION: Sets cabinet file name
500 * ARGUMENTS:
501 * FileName = Pointer to string with name of cabinet
502 */
503 {
504 strcpy(CabinetName, FileName);
505 }
506
507
508 void CCabinet::SetDestinationPath(char* DestinationPath)
509 /*
510 * FUNCTION: Sets destination path
511 * ARGUMENTS:
512 * DestinationPath = Pointer to string with name of destination path
513 */
514 {
515 strcpy(DestPath, DestinationPath);
516 ConvertPath(DestPath, false);
517 if (strlen(DestPath) > 0)
518 NormalizePath(DestPath, PATH_MAX);
519 }
520
521 ULONG CCabinet::AddSearchCriteria(char* SearchCriteria)
522 /*
523 * FUNCTION: Adds a criteria to the search criteria list
524 * ARGUMENTS:
525 * SearchCriteria = String with the search criteria to add
526 * RETURNS:
527 * Status of operation
528 */
529 {
530 PSEARCH_CRITERIA Criteria;
531
532 // Add the criteria to the list of search criteria
533 Criteria = (PSEARCH_CRITERIA)AllocateMemory(sizeof(SEARCH_CRITERIA));
534 if(!Criteria)
535 {
536 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
537 return CAB_STATUS_NOMEMORY;
538 }
539
540 Criteria->Prev = CriteriaListTail;
541 Criteria->Next = NULL;
542
543 if(CriteriaListTail)
544 CriteriaListTail->Next = Criteria;
545 else
546 CriteriaListHead = Criteria;
547
548 CriteriaListTail = Criteria;
549
550 // Set the actual criteria string
551 Criteria->Search = (char*)AllocateMemory(strlen(SearchCriteria) + 1);
552 if (!Criteria->Search)
553 {
554 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
555 return CAB_STATUS_NOMEMORY;
556 }
557
558 strcpy(Criteria->Search, SearchCriteria);
559
560 return CAB_STATUS_SUCCESS;
561 }
562
563 void CCabinet::DestroySearchCriteria()
564 /*
565 * FUNCTION: Destroys the list with the search criteria
566 */
567 {
568 PSEARCH_CRITERIA Criteria;
569 PSEARCH_CRITERIA NextCriteria;
570
571 Criteria = CriteriaListHead;
572
573 while(Criteria)
574 {
575 NextCriteria = Criteria->Next;
576
577 FreeMemory(Criteria->Search);
578 FreeMemory(Criteria);
579
580 Criteria = NextCriteria;
581 }
582
583 CriteriaListHead = NULL;
584 CriteriaListTail = NULL;
585 }
586
587 bool CCabinet::HasSearchCriteria()
588 /*
589 * FUNCTION: Returns whether we have search criteria
590 * RETURNS:
591 * Whether we have search criteria or not.
592 */
593 {
594 return (CriteriaListHead != NULL);
595 }
596
597 bool CCabinet::SetCompressionCodec(char* CodecName)
598 /*
599 * FUNCTION: Selects the codec to use for compression
600 * ARGUMENTS:
601 * CodecName = Pointer to a string with the name of the codec
602 */
603 {
604 if( !strcasecmp(CodecName, "raw") )
605 SelectCodec(CAB_CODEC_RAW);
606 else if( !strcasecmp(CodecName, "mszip") )
607 SelectCodec(CAB_CODEC_MSZIP);
608 else
609 {
610 printf("ERROR: Invalid codec specified!\n");
611 return false;
612 }
613
614 return true;
615 }
616
617 char* CCabinet::GetDestinationPath()
618 /*
619 * FUNCTION: Returns destination path
620 * RETURNS:
621 * Pointer to string with name of destination path
622 */
623 {
624 return DestPath;
625 }
626
627
628 bool CCabinet::SetCabinetReservedFile(char* FileName)
629 /*
630 * FUNCTION: Sets cabinet reserved file
631 * ARGUMENTS:
632 * FileName = Pointer to string with name of cabinet reserved file
633 */
634 {
635 FILEHANDLE FileHandle;
636 ULONG BytesRead;
637 char* ConvertedFileName;
638
639 ConvertedFileName = ConvertPath(FileName, true);
640 #if defined(_WIN32)
641 FileHandle = CreateFile(ConvertedFileName, // Open this file
642 GENERIC_READ, // Open for reading
643 FILE_SHARE_READ, // Share for reading
644 NULL, // No security
645 OPEN_EXISTING, // Existing file only
646 FILE_ATTRIBUTE_NORMAL, // Normal file
647 NULL); // No attribute template
648 free(ConvertedFileName);
649 if (FileHandle == INVALID_HANDLE_VALUE)
650 {
651 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n"));
652 return false;
653 }
654 #else /* !_WIN32 */
655 FileHandle = fopen(ConvertedFileName, "rb");
656 free(ConvertedFileName);
657 if (FileHandle == NULL)
658 {
659 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n"));
660 return false;
661 }
662 #endif
663
664 CabinetReservedFileSize = GetSizeOfFile(FileHandle);
665 if (CabinetReservedFileSize == (ULONG)-1)
666 {
667 DPRINT(MIN_TRACE, ("Cannot read from cabinet reserved file.\n"));
668 return false;
669 }
670
671 if (CabinetReservedFileSize == 0)
672 {
673 CloseFile(FileHandle);
674 return false;
675 }
676
677 CabinetReservedFileBuffer = AllocateMemory(CabinetReservedFileSize);
678 if (!CabinetReservedFileBuffer)
679 {
680 CloseFile(FileHandle);
681 return false;
682 }
683
684 if (!ReadFileData(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, &BytesRead))
685 {
686 CloseFile(FileHandle);
687 return false;
688 }
689
690 CloseFile(FileHandle);
691
692 strcpy(CabinetReservedFile, FileName);
693
694 return true;
695 }
696
697
698 char* CCabinet::GetCabinetReservedFile()
699 /*
700 * FUNCTION: Returns cabionet reserved file
701 * RETURNS:
702 * Pointer to string with name of cabinet reserved file
703 */
704 {
705 return CabinetReservedFile;
706 }
707
708
709 ULONG CCabinet::GetCurrentDiskNumber()
710 /*
711 * FUNCTION: Returns current disk number
712 * RETURNS:
713 * Current disk number
714 */
715 {
716 return CurrentDiskNumber;
717 }
718
719
720 ULONG CCabinet::Open()
721 /*
722 * FUNCTION: Opens a cabinet file
723 * RETURNS:
724 * Status of operation
725 */
726 {
727 PCFFOLDER_NODE FolderNode;
728 ULONG Status;
729 ULONG Index;
730
731 if (!FileOpen)
732 {
733 ULONG BytesRead;
734 ULONG Size;
735
736 OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough
737 if (!OutputBuffer)
738 return CAB_STATUS_NOMEMORY;
739
740 #if defined(_WIN32)
741 FileHandle = CreateFile(CabinetName, // Open this file
742 GENERIC_READ, // Open for reading
743 FILE_SHARE_READ, // Share for reading
744 NULL, // No security
745 OPEN_EXISTING, // Existing file only
746 FILE_ATTRIBUTE_NORMAL, // Normal file
747 NULL); // No attribute template
748
749 if (FileHandle == INVALID_HANDLE_VALUE)
750 {
751 DPRINT(MID_TRACE, ("Cannot open file.\n"));
752 return CAB_STATUS_CANNOT_OPEN;
753 }
754 #else /* !_WIN32 */
755 FileHandle = fopen(CabinetName, "rb");
756 if (FileHandle == NULL)
757 {
758 DPRINT(MID_TRACE, ("Cannot open file.\n"));
759 return CAB_STATUS_CANNOT_OPEN;
760 }
761 #endif
762
763 FileOpen = true;
764
765 /* Load CAB header */
766 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead))
767 != CAB_STATUS_SUCCESS)
768 {
769 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
770 return CAB_STATUS_INVALID_CAB;
771 }
772
773 /* Check header */
774 if ((BytesRead != sizeof(CFHEADER)) ||
775 (CABHeader.Signature != CAB_SIGNATURE ) ||
776 (CABHeader.Version != CAB_VERSION ) ||
777 (CABHeader.FolderCount == 0 ) ||
778 (CABHeader.FileCount == 0 ) ||
779 (CABHeader.FileTableOffset < sizeof(CFHEADER)))
780 {
781 CloseCabinet();
782 DPRINT(MID_TRACE, ("File has invalid header.\n"));
783 return CAB_STATUS_INVALID_CAB;
784 }
785
786 Size = 0;
787
788 /* Read/skip any reserved bytes */
789 if (CABHeader.Flags & CAB_FLAG_RESERVE)
790 {
791 if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead))
792 != CAB_STATUS_SUCCESS)
793 {
794 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
795 return CAB_STATUS_INVALID_CAB;
796 }
797 CabinetReserved = Size & 0xFFFF;
798 FolderReserved = (Size >> 16) & 0xFF;
799 DataReserved = (Size >> 24) & 0xFF;
800
801 #if defined(_WIN32)
802 if (SetFilePointer(FileHandle, CabinetReserved, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
803 {
804 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
805 return CAB_STATUS_FAILURE;
806 }
807 #else
808 if (fseek(FileHandle, (off_t)CabinetReserved, SEEK_CUR) != 0)
809 {
810 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
811 return CAB_STATUS_FAILURE;
812 }
813 #endif
814 }
815
816 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
817 {
818 /* Read name of previous cabinet */
819 Status = ReadString(CabinetPrev, 256);
820 if (Status != CAB_STATUS_SUCCESS)
821 return Status;
822 /* Read label of previous disk */
823 Status = ReadString(DiskPrev, 256);
824 if (Status != CAB_STATUS_SUCCESS)
825 return Status;
826 }
827 else
828 {
829 strcpy(CabinetPrev, "");
830 strcpy(DiskPrev, "");
831 }
832
833 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
834 {
835 /* Read name of next cabinet */
836 Status = ReadString(CabinetNext, 256);
837 if (Status != CAB_STATUS_SUCCESS)
838 return Status;
839 /* Read label of next disk */
840 Status = ReadString(DiskNext, 256);
841 if (Status != CAB_STATUS_SUCCESS)
842 return Status;
843 }
844 else
845 {
846 strcpy(CabinetNext, "");
847 strcpy(DiskNext, "");
848 }
849
850 /* Read all folders */
851 for (Index = 0; Index < CABHeader.FolderCount; Index++)
852 {
853 FolderNode = NewFolderNode();
854 if (!FolderNode)
855 {
856 DPRINT(MIN_TRACE, ("Insufficient resources.\n"));
857 return CAB_STATUS_NOMEMORY;
858 }
859
860 if (Index == 0)
861 FolderNode->UncompOffset = FolderUncompSize;
862
863 FolderNode->Index = Index;
864
865 if ((Status = ReadBlock(&FolderNode->Folder,
866 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS)
867 {
868 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
869 return CAB_STATUS_INVALID_CAB;
870 }
871 }
872
873 /* Read file entries */
874 Status = ReadFileTable();
875 if (Status != CAB_STATUS_SUCCESS)
876 {
877 DPRINT(MIN_TRACE, ("ReadFileTable() failed (%u).\n", (UINT)Status));
878 return Status;
879 }
880
881 /* Read data blocks for all folders */
882 FolderNode = FolderListHead;
883 while (FolderNode != NULL)
884 {
885 Status = ReadDataBlocks(FolderNode);
886 if (Status != CAB_STATUS_SUCCESS)
887 {
888 DPRINT(MIN_TRACE, ("ReadDataBlocks() failed (%u).\n", (UINT)Status));
889 return Status;
890 }
891 FolderNode = FolderNode->Next;
892 }
893 }
894 return CAB_STATUS_SUCCESS;
895 }
896
897
898 void CCabinet::Close()
899 /*
900 * FUNCTION: Closes the cabinet file
901 */
902 {
903 if (FileOpen)
904 {
905 CloseFile(FileHandle);
906 FileOpen = false;
907 }
908 }
909
910
911 ULONG CCabinet::FindFirst(PCAB_SEARCH Search)
912 /*
913 * FUNCTION: Finds the first file in the cabinet that matches a search criteria
914 * ARGUMENTS:
915 * Search = Pointer to search structure
916 * RETURNS:
917 * Status of operation
918 */
919 {
920 RestartSearch = false;
921 Search->Next = FileListHead;
922 return FindNext(Search);
923 }
924
925
926 ULONG CCabinet::FindNext(PCAB_SEARCH Search)
927 /*
928 * FUNCTION: Finds next file in the cabinet that matches a search criteria
929 * ARGUMENTS:
930 * Search = Pointer to search structure
931 * RETURNS:
932 * Status of operation
933 */
934 {
935 bool bFound = false;
936 PSEARCH_CRITERIA Criteria;
937 ULONG Status;
938
939 if (RestartSearch)
940 {
941 Search->Next = FileListHead;
942
943 /* Skip split files already extracted */
944 while ((Search->Next) &&
945 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) &&
946 (Search->Next->File.FileOffset <= LastFileOffset))
947 {
948 DPRINT(MAX_TRACE, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n",
949 Search->Next->FileName, (UINT)Search->Next->File.FileOffset, (UINT)LastFileOffset));
950 Search->Next = Search->Next->Next;
951 }
952
953 RestartSearch = false;
954 }
955
956 /* Check each search criteria against each file */
957 while(Search->Next)
958 {
959 // Some features (like displaying cabinets) don't require search criteria, so we can just break here.
960 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx".
961 if(!CriteriaListHead)
962 break;
963
964 Criteria = CriteriaListHead;
965
966 while(Criteria)
967 {
968 if(MatchFileNamePattern(Search->Next->FileName, Criteria->Search))
969 {
970 bFound = true;
971 break;
972 }
973
974 Criteria = Criteria->Next;
975 }
976
977 if(bFound)
978 break;
979
980 Search->Next = Search->Next->Next;
981 }
982
983 if (!Search->Next)
984 {
985 if (strlen(DiskNext) > 0)
986 {
987 CloseCabinet();
988
989 SetCabinetName(CabinetNext);
990
991 OnDiskChange(CabinetNext, DiskNext);
992
993 Status = Open();
994 if (Status != CAB_STATUS_SUCCESS)
995 return Status;
996
997 Search->Next = FileListHead;
998 if (!Search->Next)
999 return CAB_STATUS_NOFILE;
1000 }
1001 else
1002 return CAB_STATUS_NOFILE;
1003 }
1004
1005 Search->File = &Search->Next->File;
1006 Search->FileName = Search->Next->FileName;
1007 Search->Next = Search->Next->Next;
1008 return CAB_STATUS_SUCCESS;
1009 }
1010
1011
1012 ULONG CCabinet::ExtractFile(char* FileName)
1013 /*
1014 * FUNCTION: Extracts a file from the cabinet
1015 * ARGUMENTS:
1016 * FileName = Pointer to buffer with name of file
1017 * RETURNS
1018 * Status of operation
1019 */
1020 {
1021 ULONG Size;
1022 ULONG Offset;
1023 ULONG BytesRead;
1024 ULONG BytesToRead;
1025 ULONG BytesWritten;
1026 ULONG BytesSkipped;
1027 ULONG BytesToWrite;
1028 ULONG TotalBytesRead;
1029 ULONG CurrentOffset;
1030 PUCHAR Buffer;
1031 PUCHAR CurrentBuffer;
1032 FILEHANDLE DestFile;
1033 PCFFILE_NODE File;
1034 CFDATA CFData;
1035 ULONG Status;
1036 bool Skip;
1037 #if defined(_WIN32)
1038 FILETIME FileTime;
1039 #endif
1040 CHAR DestName[PATH_MAX];
1041 CHAR TempName[PATH_MAX];
1042
1043 Status = LocateFile(FileName, &File);
1044 if (Status != CAB_STATUS_SUCCESS)
1045 {
1046 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status));
1047 return Status;
1048 }
1049
1050 LastFileOffset = File->File.FileOffset;
1051
1052 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK)
1053 {
1054 case CAB_COMP_NONE:
1055 SelectCodec(CAB_CODEC_RAW);
1056 break;
1057
1058 case CAB_COMP_MSZIP:
1059 SelectCodec(CAB_CODEC_MSZIP);
1060 break;
1061
1062 default:
1063 return CAB_STATUS_UNSUPPCOMP;
1064 }
1065
1066 DPRINT(MAX_TRACE, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1067 (UINT)File->File.FileOffset,
1068 (UINT)File->File.FileSize,
1069 (UINT)File->DataBlock->AbsoluteOffset,
1070 (UINT)File->DataBlock->UncompOffset));
1071
1072 strcpy(DestName, DestPath);
1073 strcat(DestName, FileName);
1074
1075 /* Create destination file, fail if it already exists */
1076 #if defined(_WIN32)
1077 DestFile = CreateFile(DestName, // Create this file
1078 GENERIC_WRITE, // Open for writing
1079 0, // No sharing
1080 NULL, // No security
1081 CREATE_NEW, // New file only
1082 FILE_ATTRIBUTE_NORMAL, // Normal file
1083 NULL); // No attribute template
1084 if (DestFile == INVALID_HANDLE_VALUE)
1085 {
1086 /* If file exists, ask to overwrite file */
1087 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
1088 (OnOverwrite(&File->File, FileName)))
1089 {
1090 /* Create destination file, overwrite if it already exists */
1091 DestFile = CreateFile(DestName, // Create this file
1092 GENERIC_WRITE, // Open for writing
1093 0, // No sharing
1094 NULL, // No security
1095 TRUNCATE_EXISTING, // Truncate the file
1096 FILE_ATTRIBUTE_NORMAL, // Normal file
1097 NULL); // No attribute template
1098 if (DestFile == INVALID_HANDLE_VALUE)
1099 return CAB_STATUS_CANNOT_CREATE;
1100 }
1101 else
1102 {
1103 if (Status == ERROR_FILE_EXISTS)
1104 return CAB_STATUS_FILE_EXISTS;
1105 else
1106 return CAB_STATUS_CANNOT_CREATE;
1107 }
1108 }
1109 #else /* !_WIN32 */
1110 DestFile = fopen(DestName, "rb");
1111 if (DestFile != NULL)
1112 {
1113 fclose(DestFile);
1114 /* If file exists, ask to overwrite file */
1115 if (OnOverwrite(&File->File, FileName))
1116 {
1117 DestFile = fopen(DestName, "w+b");
1118 if (DestFile == NULL)
1119 return CAB_STATUS_CANNOT_CREATE;
1120 }
1121 else
1122 return CAB_STATUS_FILE_EXISTS;
1123 }
1124 else
1125 {
1126 DestFile = fopen(DestName, "w+b");
1127 if (DestFile == NULL)
1128 return CAB_STATUS_CANNOT_CREATE;
1129 }
1130 #endif
1131 #if defined(_WIN32)
1132 if (!DosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime))
1133 {
1134 CloseFile(DestFile);
1135 DPRINT(MIN_TRACE, ("DosDateTimeToFileTime() failed (%u).\n", (UINT)GetLastError()));
1136 return CAB_STATUS_CANNOT_WRITE;
1137 }
1138
1139 SetFileTime(DestFile, NULL, &FileTime, NULL);
1140 #else
1141 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n"));
1142 #endif
1143 SetAttributesOnFile(DestName, File->File.Attributes);
1144
1145 Buffer = (PUCHAR)AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough
1146 if (!Buffer)
1147 {
1148 CloseFile(DestFile);
1149 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1150 return CAB_STATUS_NOMEMORY;
1151 }
1152
1153 /* Call OnExtract event handler */
1154 OnExtract(&File->File, FileName);
1155
1156 /* Search to start of file */
1157 #if defined(_WIN32)
1158 Offset = SetFilePointer(FileHandle,
1159 File->DataBlock->AbsoluteOffset,
1160 NULL,
1161 FILE_BEGIN);
1162 if (Offset == INVALID_SET_FILE_POINTER)
1163 {
1164 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
1165 CloseFile(DestFile);
1166 FreeMemory(Buffer);
1167 return CAB_STATUS_INVALID_CAB;
1168 }
1169 #else
1170 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0)
1171 {
1172 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
1173 CloseFile(DestFile);
1174 FreeMemory(Buffer);
1175 return CAB_STATUS_FAILURE;
1176 }
1177 Offset = ftell(FileHandle);
1178 #endif
1179
1180 Size = File->File.FileSize;
1181 Offset = File->File.FileOffset;
1182 CurrentOffset = File->DataBlock->UncompOffset;
1183
1184 Skip = true;
1185
1186 ReuseBlock = (CurrentDataNode == File->DataBlock);
1187 if (Size > 0)
1188 {
1189 do
1190 {
1191 DPRINT(MAX_TRACE, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n",
1192 (UINT)File->DataBlock->UncompOffset, (UINT)ReuseBlock, (UINT)Offset, (UINT)Size,
1193 (UINT)BytesLeftInBlock));
1194
1195 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0))
1196 {
1197 DPRINT(MAX_TRACE, ("Filling buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock));
1198
1199 CurrentBuffer = Buffer;
1200 TotalBytesRead = 0;
1201 do
1202 {
1203 DPRINT(MAX_TRACE, ("Size (%u bytes).\n", (UINT)Size));
1204
1205 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1206 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1207 {
1208 CloseFile(DestFile);
1209 FreeMemory(Buffer);
1210 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
1211 return CAB_STATUS_INVALID_CAB;
1212 }
1213
1214 DPRINT(MAX_TRACE, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n",
1215 (UINT)CFData.Checksum,
1216 CFData.CompSize,
1217 CFData.UncompSize));
1218
1219 ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12);
1220
1221 BytesToRead = CFData.CompSize;
1222
1223 DPRINT(MAX_TRACE, ("Read: (0x%lX,0x%lX).\n",
1224 (unsigned long)CurrentBuffer, (unsigned long)Buffer));
1225
1226 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) !=
1227 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead))
1228 {
1229 CloseFile(DestFile);
1230 FreeMemory(Buffer);
1231 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
1232 return CAB_STATUS_INVALID_CAB;
1233 }
1234
1235 /* FIXME: Does not work with files generated by makecab.exe */
1236 /*
1237 if (CFData.Checksum != 0)
1238 {
1239 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0);
1240 if (Checksum != CFData.Checksum)
1241 {
1242 CloseFile(DestFile);
1243 FreeMemory(Buffer);
1244 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n",
1245 Checksum, CFData.Checksum));
1246 return CAB_STATUS_INVALID_CAB;
1247 }
1248 }
1249 */
1250 TotalBytesRead += BytesRead;
1251
1252 CurrentBuffer += BytesRead;
1253
1254 if (CFData.UncompSize == 0)
1255 {
1256 if (strlen(DiskNext) == 0)
1257 {
1258 CloseFile(DestFile);
1259 FreeMemory(Buffer);
1260 return CAB_STATUS_NOFILE;
1261 }
1262
1263 /* CloseCabinet() will destroy all file entries so in case
1264 FileName refers to the FileName field of a CFFOLDER_NODE
1265 structure, we have to save a copy of the filename */
1266 strcpy(TempName, FileName);
1267
1268 CloseCabinet();
1269
1270 SetCabinetName(CabinetNext);
1271
1272 OnDiskChange(CabinetNext, DiskNext);
1273
1274 Status = Open();
1275 if (Status != CAB_STATUS_SUCCESS)
1276 {
1277 CloseFile(DestFile);
1278 FreeMemory(Buffer);
1279 return Status;
1280 }
1281
1282 /* The first data block of the file will not be
1283 found as it is located in the previous file */
1284 Status = LocateFile(TempName, &File);
1285 if (Status == CAB_STATUS_NOFILE)
1286 {
1287 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status));
1288 CloseFile(DestFile);
1289 FreeMemory(Buffer);
1290 return Status;
1291 }
1292
1293 /* The file is continued in the first data block in the folder */
1294 File->DataBlock = CurrentFolderNode->DataListHead;
1295
1296 /* Search to start of file */
1297 #if defined(_WIN32)
1298 if( SetFilePointer(FileHandle,
1299 File->DataBlock->AbsoluteOffset,
1300 NULL,
1301 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
1302 {
1303 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
1304 CloseFile(DestFile);
1305 FreeMemory(Buffer);
1306 return CAB_STATUS_INVALID_CAB;
1307 }
1308 #else
1309 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0)
1310 {
1311 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
1312 CloseFile(DestFile);
1313 FreeMemory(Buffer);
1314 return CAB_STATUS_INVALID_CAB;
1315 }
1316 #endif
1317
1318 DPRINT(MAX_TRACE, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n",
1319 (UINT)File->File.FileOffset,
1320 (UINT)File->File.FileSize,
1321 (UINT)File->DataBlock->AbsoluteOffset,
1322 (UINT)File->DataBlock->UncompOffset));
1323
1324 CurrentDataNode = File->DataBlock;
1325 ReuseBlock = true;
1326
1327 RestartSearch = true;
1328 }
1329 } while (CFData.UncompSize == 0);
1330
1331 DPRINT(MAX_TRACE, ("TotalBytesRead (%u).\n", (UINT)TotalBytesRead));
1332
1333 Status = Codec->Uncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite);
1334 if (Status != CS_SUCCESS)
1335 {
1336 CloseFile(DestFile);
1337 FreeMemory(Buffer);
1338 DPRINT(MID_TRACE, ("Cannot uncompress block.\n"));
1339 if (Status == CS_NOMEMORY)
1340 return CAB_STATUS_NOMEMORY;
1341 return CAB_STATUS_INVALID_CAB;
1342 }
1343
1344 if (BytesToWrite != CFData.UncompSize)
1345 {
1346 DPRINT(MID_TRACE, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n",
1347 (UINT)BytesToWrite, CFData.UncompSize));
1348 CloseFile(DestFile);
1349 FreeMemory(Buffer);
1350 return CAB_STATUS_INVALID_CAB;
1351 }
1352
1353 BytesLeftInBlock = BytesToWrite;
1354 }
1355 else
1356 {
1357 DPRINT(MAX_TRACE, ("Using same buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock));
1358
1359 BytesToWrite = BytesLeftInBlock;
1360
1361 DPRINT(MAX_TRACE, ("Seeking to absolute offset 0x%X.\n",
1362 (UINT)(CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + CurrentDataNode->Data.CompSize)));
1363
1364 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) !=
1365 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA)))
1366 {
1367 CloseFile(DestFile);
1368 FreeMemory(Buffer);
1369 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
1370 return CAB_STATUS_INVALID_CAB;
1371 }
1372
1373 DPRINT(MAX_TRACE, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n",
1374 CFData.CompSize, CFData.UncompSize));
1375
1376 /* Go to next data block */
1377 #if defined(_WIN32)
1378 if( SetFilePointer(FileHandle,
1379 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1380 CurrentDataNode->Data.CompSize,
1381 NULL,
1382 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
1383 {
1384 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
1385 CloseFile(DestFile);
1386 FreeMemory(Buffer);
1387 return CAB_STATUS_INVALID_CAB;
1388 }
1389 #else
1390 if (fseek(FileHandle, (off_t)CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) +
1391 CurrentDataNode->Data.CompSize, SEEK_SET) != 0)
1392 {
1393 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
1394 CloseFile(DestFile);
1395 FreeMemory(Buffer);
1396 return CAB_STATUS_INVALID_CAB;
1397 }
1398 #endif
1399
1400 ReuseBlock = false;
1401 }
1402
1403 if (Skip)
1404 BytesSkipped = (Offset - CurrentOffset);
1405 else
1406 BytesSkipped = 0;
1407
1408 BytesToWrite -= BytesSkipped;
1409
1410 if (Size < BytesToWrite)
1411 BytesToWrite = Size;
1412
1413 DPRINT(MAX_TRACE, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n",
1414 (UINT)Offset,
1415 (UINT)CurrentOffset,
1416 (UINT)BytesToWrite,
1417 (UINT)BytesSkipped, (UINT)Skip,
1418 (UINT)Size));
1419
1420 #if defined(_WIN32)
1421 if (!WriteFile(DestFile, (void*)((PUCHAR)OutputBuffer + BytesSkipped),
1422 BytesToWrite, (LPDWORD)&BytesWritten, NULL) ||
1423 (BytesToWrite != BytesWritten))
1424 {
1425 DPRINT(MIN_TRACE, ("Status 0x%X.\n", (UINT)GetLastError()));
1426 #else
1427 BytesWritten = BytesToWrite;
1428 if (fwrite((void*)((PUCHAR)OutputBuffer + BytesSkipped),
1429 BytesToWrite, 1, DestFile) < 1)
1430 {
1431 #endif
1432 CloseFile(DestFile);
1433 FreeMemory(Buffer);
1434 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
1435 return CAB_STATUS_CANNOT_WRITE;
1436 }
1437 Size -= BytesToWrite;
1438
1439 CurrentOffset += BytesToWrite;
1440
1441 /* Don't skip any more bytes */
1442 Skip = false;
1443 } while (Size > 0);
1444 }
1445
1446 CloseFile(DestFile);
1447
1448 FreeMemory(Buffer);
1449
1450 return CAB_STATUS_SUCCESS;
1451 }
1452
1453 bool CCabinet::IsCodecSelected()
1454 /*
1455 * FUNCTION: Returns the value of CodecSelected
1456 * RETURNS:
1457 * Whether a codec is selected
1458 */
1459 {
1460 return CodecSelected;
1461 }
1462
1463 void CCabinet::SelectCodec(LONG Id)
1464 /*
1465 * FUNCTION: Selects codec engine to use
1466 * ARGUMENTS:
1467 * Id = Codec identifier
1468 */
1469 {
1470 if (CodecSelected)
1471 {
1472 if (Id == CodecId)
1473 return;
1474
1475 CodecSelected = false;
1476 delete Codec;
1477 }
1478
1479 switch (Id)
1480 {
1481 case CAB_CODEC_RAW:
1482 Codec = new CRawCodec();
1483 break;
1484
1485 case CAB_CODEC_MSZIP:
1486 Codec = new CMSZipCodec();
1487 break;
1488
1489 default:
1490 return;
1491 }
1492
1493 CodecId = Id;
1494 CodecSelected = true;
1495 }
1496
1497
1498 #ifndef CAB_READ_ONLY
1499
1500 /* CAB write methods */
1501
1502 ULONG CCabinet::NewCabinet()
1503 /*
1504 * FUNCTION: Creates a new cabinet
1505 * RETURNS:
1506 * Status of operation
1507 */
1508 {
1509 ULONG Status;
1510
1511 CurrentDiskNumber = 0;
1512
1513 OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough
1514 InputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough
1515 if ((!OutputBuffer) || (!InputBuffer))
1516 {
1517 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1518 return CAB_STATUS_NOMEMORY;
1519 }
1520 CurrentIBuffer = InputBuffer;
1521 CurrentIBufferSize = 0;
1522
1523 CABHeader.Signature = CAB_SIGNATURE;
1524 CABHeader.Reserved1 = 0; // Not used
1525 CABHeader.CabinetSize = 0; // Not yet known
1526 CABHeader.Reserved2 = 0; // Not used
1527 CABHeader.Reserved3 = 0; // Not used
1528 CABHeader.Version = CAB_VERSION;
1529 CABHeader.FolderCount = 0; // Not yet known
1530 CABHeader.FileCount = 0; // Not yet known
1531 CABHeader.Flags = 0; // Not yet known
1532 // FIXME: Should be random
1533 CABHeader.SetID = 0x534F;
1534 CABHeader.CabinetNumber = 0;
1535
1536
1537 TotalFolderSize = 0;
1538 TotalFileSize = 0;
1539
1540 DiskSize = sizeof(CFHEADER);
1541
1542 InitCabinetHeader();
1543
1544 // NextFolderNumber is 0-based
1545 NextFolderNumber = 0;
1546
1547 CurrentFolderNode = NULL;
1548 Status = NewFolder();
1549 if (Status != CAB_STATUS_SUCCESS)
1550 return Status;
1551
1552 CurrentFolderNode->Folder.DataOffset = DiskSize - TotalHeaderSize;
1553
1554 ScratchFile = new CCFDATAStorage;
1555 if (!ScratchFile)
1556 {
1557 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1558 return CAB_STATUS_NOMEMORY;
1559 }
1560
1561 Status = ScratchFile->Create();
1562
1563 CreateNewFolder = false;
1564
1565 CreateNewDisk = false;
1566
1567 PrevCabinetNumber = 0;
1568
1569 return Status;
1570 }
1571
1572
1573 ULONG CCabinet::NewDisk()
1574 /*
1575 * FUNCTION: Forces a new disk to be created
1576 * RETURNS:
1577 * Status of operation
1578 */
1579 {
1580 // NextFolderNumber is 0-based
1581 NextFolderNumber = 1;
1582
1583 CreateNewDisk = false;
1584
1585 DiskSize = sizeof(CFHEADER) + TotalFolderSize + TotalFileSize;
1586
1587 InitCabinetHeader();
1588
1589 CurrentFolderNode->TotalFolderSize = 0;
1590
1591 CurrentFolderNode->Folder.DataBlockCount = 0;
1592
1593 return CAB_STATUS_SUCCESS;
1594 }
1595
1596
1597 ULONG CCabinet::NewFolder()
1598 /*
1599 * FUNCTION: Forces a new folder to be created
1600 * RETURNS:
1601 * Status of operation
1602 */
1603 {
1604 DPRINT(MAX_TRACE, ("Creating new folder.\n"));
1605
1606 CurrentFolderNode = NewFolderNode();
1607 if (!CurrentFolderNode)
1608 {
1609 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1610 return CAB_STATUS_NOMEMORY;
1611 }
1612
1613 switch (CodecId) {
1614 case CAB_CODEC_RAW:
1615 CurrentFolderNode->Folder.CompressionType = CAB_COMP_NONE;
1616 break;
1617
1618 case CAB_CODEC_MSZIP:
1619 CurrentFolderNode->Folder.CompressionType = CAB_COMP_MSZIP;
1620 break;
1621
1622 default:
1623 return CAB_STATUS_UNSUPPCOMP;
1624 }
1625
1626 /* FIXME: This won't work if no files are added to the new folder */
1627
1628 DiskSize += sizeof(CFFOLDER);
1629
1630 TotalFolderSize += sizeof(CFFOLDER);
1631
1632 NextFolderNumber++;
1633
1634 CABHeader.FolderCount++;
1635
1636 LastBlockStart = 0;
1637
1638 return CAB_STATUS_SUCCESS;
1639 }
1640
1641
1642 ULONG CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode)
1643 /*
1644 * FUNCTION: Writes a file to the scratch file
1645 * ARGUMENTS:
1646 * FileNode = Pointer to file node
1647 * RETURNS:
1648 * Status of operation
1649 */
1650 {
1651 ULONG BytesToRead;
1652 ULONG BytesRead;
1653 ULONG Status;
1654 ULONG Size;
1655
1656 if (!ContinueFile)
1657 {
1658 /* Try to open file */
1659 #if defined(_WIN32)
1660 SourceFile = CreateFile(
1661 FileNode->FileName, // Open this file
1662 GENERIC_READ, // Open for reading
1663 FILE_SHARE_READ, // Share for reading
1664 NULL, // No security
1665 OPEN_EXISTING, // File must exist
1666 FILE_ATTRIBUTE_NORMAL, // Normal file
1667 NULL); // No attribute template
1668 if (SourceFile == INVALID_HANDLE_VALUE)
1669 {
1670 DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName));
1671 return CAB_STATUS_NOFILE;
1672 }
1673 #else /* !_WIN32 */
1674 SourceFile = fopen(FileNode->FileName, "rb");
1675 if (SourceFile == NULL)
1676 {
1677 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n"));
1678 return CAB_STATUS_NOFILE;
1679 }
1680 #endif
1681
1682 if (CreateNewFolder)
1683 {
1684 /* There is always a new folder after
1685 a split file is completely stored */
1686 Status = NewFolder();
1687 if (Status != CAB_STATUS_SUCCESS)
1688 return Status;
1689 CreateNewFolder = false;
1690 }
1691
1692 /* Call OnAdd event handler */
1693 OnAdd(&FileNode->File, FileNode->FileName);
1694
1695 TotalBytesLeft = FileNode->File.FileSize;
1696
1697 FileNode->File.FileOffset = CurrentFolderNode->UncompOffset;
1698 CurrentFolderNode->UncompOffset += TotalBytesLeft;
1699 FileNode->File.FileControlID = (USHORT)(NextFolderNumber - 1);
1700 CurrentFolderNode->Commit = true;
1701 PrevCabinetNumber = CurrentDiskNumber;
1702
1703 Size = sizeof(CFFILE) + (ULONG)strlen(GetFileName(FileNode->FileName)) + 1;
1704 CABHeader.FileTableOffset += Size;
1705 TotalFileSize += Size;
1706 DiskSize += Size;
1707 }
1708
1709 FileNode->Commit = true;
1710
1711 if (TotalBytesLeft > 0)
1712 {
1713 do
1714 {
1715 if (TotalBytesLeft > (ULONG)CAB_BLOCKSIZE - CurrentIBufferSize)
1716 BytesToRead = CAB_BLOCKSIZE - CurrentIBufferSize;
1717 else
1718 BytesToRead = TotalBytesLeft;
1719
1720 if (!ReadFileData(SourceFile, CurrentIBuffer, BytesToRead, &BytesRead) || (BytesToRead != BytesRead))
1721 {
1722 DPRINT(MIN_TRACE, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n",
1723 (UINT)BytesToRead, (UINT)BytesRead, (UINT)CurrentIBufferSize));
1724 return CAB_STATUS_INVALID_CAB;
1725 }
1726
1727 CurrentIBuffer = (unsigned char*)CurrentIBuffer + BytesRead;
1728 CurrentIBufferSize += (USHORT)BytesRead;
1729
1730 if (CurrentIBufferSize == CAB_BLOCKSIZE)
1731 {
1732 Status = WriteDataBlock();
1733 if (Status != CAB_STATUS_SUCCESS)
1734 return Status;
1735 }
1736 TotalBytesLeft -= BytesRead;
1737 } while ((TotalBytesLeft > 0) && (!CreateNewDisk));
1738 }
1739
1740 if (TotalBytesLeft == 0)
1741 {
1742 CloseFile(SourceFile);
1743 FileNode->Delete = true;
1744
1745 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER)
1746 {
1747 FileNode->File.FileControlID = CAB_FILE_CONTINUED;
1748 CurrentFolderNode->Delete = true;
1749
1750 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1751 {
1752 Status = WriteDataBlock();
1753 if (Status != CAB_STATUS_SUCCESS)
1754 return Status;
1755 }
1756
1757 CreateNewFolder = true;
1758 }
1759 }
1760 else
1761 {
1762 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER)
1763 FileNode->File.FileControlID = CAB_FILE_SPLIT;
1764 else
1765 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT;
1766 }
1767
1768 return CAB_STATUS_SUCCESS;
1769 }
1770
1771
1772 ULONG CCabinet::WriteDisk(ULONG MoreDisks)
1773 /*
1774 * FUNCTION: Forces the current disk to be written
1775 * ARGUMENTS:
1776 * MoreDisks = true if there is one or more disks after this disk
1777 * RETURNS:
1778 * Status of operation
1779 */
1780 {
1781 PCFFILE_NODE FileNode;
1782 ULONG Status;
1783
1784 ContinueFile = false;
1785 FileNode = FileListHead;
1786 while (FileNode != NULL)
1787 {
1788 Status = WriteFileToScratchStorage(FileNode);
1789 if (Status != CAB_STATUS_SUCCESS)
1790 return Status;
1791
1792 if (CreateNewDisk)
1793 {
1794 /* A data block could span more than two
1795 disks if MaxDiskSize is very small */
1796 while (CreateNewDisk)
1797 {
1798 DPRINT(MAX_TRACE, ("Creating new disk.\n"));
1799 CommitDisk(true);
1800 CloseDisk();
1801 NewDisk();
1802
1803 ContinueFile = true;
1804 CreateNewDisk = false;
1805
1806 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1807 (UINT)CurrentIBufferSize, (UINT)CurrentOBufferSize));
1808
1809 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1810 {
1811 Status = WriteDataBlock();
1812 if (Status != CAB_STATUS_SUCCESS)
1813 return Status;
1814 }
1815 }
1816 }
1817 else
1818 {
1819 ContinueFile = false;
1820 FileNode = FileNode->Next;
1821 }
1822 }
1823
1824 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1825 {
1826 /* A data block could span more than two
1827 disks if MaxDiskSize is very small */
1828
1829 ASSERT(CreateNewDisk == false);
1830
1831 do
1832 {
1833 if (CreateNewDisk)
1834 {
1835 DPRINT(MID_TRACE, ("Creating new disk 2.\n"));
1836 CommitDisk(true);
1837 CloseDisk();
1838 NewDisk();
1839 CreateNewDisk = false;
1840
1841 ASSERT(FileNode == FileListHead);
1842 }
1843
1844 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1845 {
1846 Status = WriteDataBlock();
1847 if (Status != CAB_STATUS_SUCCESS)
1848 return Status;
1849 }
1850 } while (CreateNewDisk);
1851 }
1852 CommitDisk(MoreDisks);
1853
1854 return CAB_STATUS_SUCCESS;
1855 }
1856
1857
1858 ULONG CCabinet::CommitDisk(ULONG MoreDisks)
1859 /*
1860 * FUNCTION: Commits the current disk
1861 * ARGUMENTS:
1862 * MoreDisks = true if there is one or more disks after this disk
1863 * RETURNS:
1864 * Status of operation
1865 */
1866 {
1867 PCFFOLDER_NODE FolderNode;
1868 ULONG Status;
1869
1870 OnCabinetName(CurrentDiskNumber, CabinetName);
1871
1872 /* Create file, fail if it already exists */
1873 #if defined(_WIN32)
1874 FileHandle = CreateFile(CabinetName, // Create this file
1875 GENERIC_WRITE, // Open for writing
1876 0, // No sharing
1877 NULL, // No security
1878 CREATE_NEW, // New file only
1879 FILE_ATTRIBUTE_NORMAL, // Normal file
1880 NULL); // No attribute template
1881 if (FileHandle == INVALID_HANDLE_VALUE)
1882 {
1883 ULONG Status;
1884 /* If file exists, ask to overwrite file */
1885 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
1886 (OnOverwrite(NULL, CabinetName)))
1887 {
1888
1889 /* Create cabinet file, overwrite if it already exists */
1890 FileHandle = CreateFile(CabinetName, // Create this file
1891 GENERIC_WRITE, // Open for writing
1892 0, // No sharing
1893 NULL, // No security
1894 TRUNCATE_EXISTING, // Truncate the file
1895 FILE_ATTRIBUTE_NORMAL, // Normal file
1896 NULL); // No attribute template
1897 if (FileHandle == INVALID_HANDLE_VALUE)
1898 return CAB_STATUS_CANNOT_CREATE;
1899 }
1900 else
1901 {
1902 if (Status == ERROR_FILE_EXISTS)
1903 return CAB_STATUS_FILE_EXISTS;
1904 else
1905 return CAB_STATUS_CANNOT_CREATE;
1906 }
1907 }
1908 #else /* !_WIN32 */
1909 FileHandle = fopen(CabinetName, "rb");
1910 if (FileHandle != NULL)
1911 {
1912 fclose(FileHandle);
1913 /* If file exists, ask to overwrite file */
1914 if (OnOverwrite(NULL, CabinetName))
1915 {
1916 FileHandle = fopen(CabinetName, "w+b");
1917 if (FileHandle == NULL)
1918 return CAB_STATUS_CANNOT_CREATE;
1919 }
1920 else
1921 return CAB_STATUS_FILE_EXISTS;
1922
1923 }
1924 else
1925 {
1926 FileHandle = fopen(CabinetName, "w+b");
1927 if (FileHandle == NULL)
1928 return CAB_STATUS_CANNOT_CREATE;
1929 }
1930 #endif
1931
1932 WriteCabinetHeader(MoreDisks != 0);
1933
1934 Status = WriteFolderEntries();
1935 if (Status != CAB_STATUS_SUCCESS)
1936 return Status;
1937
1938 /* Write file entries */
1939 WriteFileEntries();
1940
1941 /* Write data blocks */
1942 FolderNode = FolderListHead;
1943 while (FolderNode != NULL)
1944 {
1945 if (FolderNode->Commit)
1946 {
1947 Status = CommitDataBlocks(FolderNode);
1948 if (Status != CAB_STATUS_SUCCESS)
1949 return Status;
1950 /* Remove data blocks for folder */
1951 DestroyDataNodes(FolderNode);
1952 }
1953 FolderNode = FolderNode->Next;
1954 }
1955
1956 CloseFile(FileHandle);
1957
1958 ScratchFile->Truncate();
1959
1960 return CAB_STATUS_SUCCESS;
1961 }
1962
1963
1964 ULONG CCabinet::CloseDisk()
1965 /*
1966 * FUNCTION: Closes the current disk
1967 * RETURNS:
1968 * Status of operation
1969 */
1970 {
1971 DestroyDeletedFileNodes();
1972
1973 /* Destroy folder nodes that are completely stored */
1974 DestroyDeletedFolderNodes();
1975
1976 CurrentDiskNumber++;
1977
1978 return CAB_STATUS_SUCCESS;
1979 }
1980
1981
1982 ULONG CCabinet::CloseCabinet()
1983 /*
1984 * FUNCTION: Closes the current cabinet
1985 * RETURNS:
1986 * Status of operation
1987 */
1988 {
1989 ULONG Status;
1990
1991 DestroyFileNodes();
1992
1993 DestroyFolderNodes();
1994
1995 if (InputBuffer)
1996 {
1997 FreeMemory(InputBuffer);
1998 InputBuffer = NULL;
1999 }
2000
2001 if (OutputBuffer)
2002 {
2003 FreeMemory(OutputBuffer);
2004 OutputBuffer = NULL;
2005 }
2006
2007 Close();
2008
2009 if (ScratchFile)
2010 {
2011 Status = ScratchFile->Destroy();
2012 delete ScratchFile;
2013 return Status;
2014 }
2015
2016 return CAB_STATUS_SUCCESS;
2017 }
2018
2019
2020 ULONG CCabinet::AddFile(char* FileName)
2021 /*
2022 * FUNCTION: Adds a file to the current disk
2023 * ARGUMENTS:
2024 * FileName = Pointer to string with file name (full path)
2025 * RETURNS:
2026 * Status of operation
2027 */
2028 {
2029 FILEHANDLE SrcFile;
2030 PCFFILE_NODE FileNode;
2031 char* NewFileName;
2032
2033 NewFileName = (char*)AllocateMemory(strlen(FileName) + 1);
2034 if (!NewFileName)
2035 {
2036 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2037 return CAB_STATUS_NOMEMORY;
2038 }
2039 strcpy(NewFileName, FileName);
2040 ConvertPath(NewFileName, false);
2041
2042 /* Try to open file */
2043 #if defined(_WIN32)
2044 SrcFile = CreateFile(
2045 NewFileName, // Open this file
2046 GENERIC_READ, // Open for reading
2047 FILE_SHARE_READ, // Share for reading
2048 NULL, // No security
2049 OPEN_EXISTING, // File must exist
2050 FILE_ATTRIBUTE_NORMAL, // Normal file
2051 NULL); // No attribute template
2052 if (SrcFile == INVALID_HANDLE_VALUE)
2053 {
2054 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2055 FreeMemory(NewFileName);
2056 return CAB_STATUS_CANNOT_OPEN;
2057 }
2058 #else /* !_WIN32 */
2059 SrcFile = fopen(NewFileName, "rb");
2060 if (SrcFile == NULL)
2061 {
2062 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2063 FreeMemory(NewFileName);
2064 return CAB_STATUS_CANNOT_OPEN;
2065 }
2066 #endif
2067
2068 FileNode = NewFileNode();
2069 if (!FileNode)
2070 {
2071 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2072 FreeMemory(NewFileName);
2073 CloseFile(SrcFile);
2074 return CAB_STATUS_NOMEMORY;
2075 }
2076
2077 FileNode->FolderNode = CurrentFolderNode;
2078 FileNode->FileName = NewFileName;
2079
2080 /* FIXME: Check for and handle large files (>= 2GB) */
2081 FileNode->File.FileSize = GetSizeOfFile(SrcFile);
2082 if (FileNode->File.FileSize == (ULONG)-1)
2083 {
2084 DPRINT(MIN_TRACE, ("Cannot read from file.\n"));
2085 CloseFile(SrcFile);
2086 return CAB_STATUS_CANNOT_READ;
2087 }
2088
2089 if (GetFileTimes(SrcFile, FileNode) != CAB_STATUS_SUCCESS)
2090 {
2091 DPRINT(MIN_TRACE, ("Cannot read file times.\n"));
2092 CloseFile(SrcFile);
2093 return CAB_STATUS_CANNOT_READ;
2094 }
2095
2096 if (GetAttributesOnFile(FileNode) != CAB_STATUS_SUCCESS)
2097 {
2098 DPRINT(MIN_TRACE, ("Cannot read file attributes.\n"));
2099 CloseFile(SrcFile);
2100 return CAB_STATUS_CANNOT_READ;
2101 }
2102
2103 CloseFile(SrcFile);
2104
2105 return CAB_STATUS_SUCCESS;
2106 }
2107
2108 bool CCabinet::CreateSimpleCabinet()
2109 /*
2110 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2111 */
2112 {
2113 bool bRet = false;
2114 char* pszFile;
2115 char szFilePath[PATH_MAX];
2116 char szFile[PATH_MAX];
2117 PSEARCH_CRITERIA Criteria;
2118 ULONG Status;
2119
2120 #if defined(_WIN32)
2121 HANDLE hFind;
2122 WIN32_FIND_DATA FindFileData;
2123 #else
2124 DIR* dirp;
2125 struct dirent* dp;
2126 struct stat stbuf;
2127 #endif
2128
2129 // Initialize a new cabinet
2130 Status = NewCabinet();
2131 if (Status != CAB_STATUS_SUCCESS)
2132 {
2133 DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status));
2134 goto cleanup2;
2135 }
2136
2137 // Add each file in the criteria list
2138 Criteria = CriteriaListHead;
2139
2140 while(Criteria)
2141 {
2142 // Store the file path with a trailing slash in szFilePath
2143 ConvertPath(Criteria->Search, false);
2144 pszFile = strrchr(Criteria->Search, DIR_SEPARATOR_CHAR);
2145
2146 if(pszFile)
2147 {
2148 // Set the pointer to the start of the file name, not the slash
2149 pszFile++;
2150
2151 strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search);
2152 szFilePath[pszFile - Criteria->Search] = 0;
2153 }
2154 else
2155 {
2156 pszFile = Criteria->Search;
2157
2158 #if defined(_WIN32)
2159 szFilePath[0] = 0;
2160 #else
2161 // needed for opendir()
2162 strcpy(szFilePath, "./");
2163 #endif
2164 }
2165
2166 #if defined(_WIN32)
2167 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2168 hFind = FindFirstFile(Criteria->Search, &FindFileData);
2169
2170 // Don't stop if a search criteria is not found
2171 if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND)
2172 {
2173 DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError()));
2174 goto cleanup;
2175 }
2176
2177 do
2178 {
2179 if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
2180 {
2181 strcpy(szFile, szFilePath);
2182 strcat(szFile, FindFileData.cFileName);
2183
2184 Status = AddFile(szFile);
2185
2186 if(Status != CAB_STATUS_SUCCESS)
2187 {
2188 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2189 goto cleanup;
2190 }
2191 }
2192 }
2193 while(FindNextFile(hFind, &FindFileData));
2194
2195 FindClose(hFind);
2196 #else
2197 // Unix: Use opendir/readdir to loop through all entries, stat to check if it's a file and MatchFileNamePattern to match the file against the pattern
2198 dirp = opendir(szFilePath);
2199
2200 if(dirp)
2201 {
2202 while( (dp = readdir(dirp)) )
2203 {
2204 strcpy(szFile, szFilePath);
2205 strcat(szFile, dp->d_name);
2206
2207 if(stat(szFile, &stbuf) == 0)
2208 {
2209 if(stbuf.st_mode != S_IFDIR)
2210 {
2211 if(MatchFileNamePattern(dp->d_name, pszFile))
2212 {
2213 Status = AddFile(szFile);
2214
2215 if(Status != CAB_STATUS_SUCCESS)
2216 {
2217 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2218 goto cleanup;
2219 }
2220 }
2221 }
2222 }
2223 else
2224 {
2225 DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno));
2226 goto cleanup;
2227 }
2228 }
2229
2230 closedir(dirp);
2231 }
2232 #endif
2233
2234 Criteria = Criteria->Next;
2235 }
2236
2237 Status = WriteDisk(false);
2238 if (Status == CAB_STATUS_SUCCESS)
2239 Status = CloseDisk();
2240 if (Status != CAB_STATUS_SUCCESS)
2241 {
2242 DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status));
2243 goto cleanup;
2244 }
2245
2246 cleanup:
2247 CloseCabinet();
2248 bRet = true;
2249
2250 cleanup2:
2251 DestroySearchCriteria();
2252 return bRet;
2253 }
2254
2255 void CCabinet::SetMaxDiskSize(ULONG Size)
2256 /*
2257 * FUNCTION: Sets the maximum size of the current disk
2258 * ARGUMENTS:
2259 * Size = Maximum size of current disk (0 means no maximum size)
2260 */
2261 {
2262 MaxDiskSize = Size;
2263 }
2264
2265 #endif /* CAB_READ_ONLY */
2266
2267
2268 /* Default event handlers */
2269
2270 bool CCabinet::OnOverwrite(PCFFILE File,
2271 char* FileName)
2272 /*
2273 * FUNCTION: Called when extracting a file and it already exists
2274 * ARGUMENTS:
2275 * File = Pointer to CFFILE for file being extracted
2276 * FileName = Pointer to buffer with name of file (full path)
2277 * RETURNS
2278 * true if the file should be overwritten, false if not
2279 */
2280 {
2281 return false;
2282 }
2283
2284
2285 void CCabinet::OnExtract(PCFFILE File,
2286 char* FileName)
2287 /*
2288 * FUNCTION: Called just before extracting a file
2289 * ARGUMENTS:
2290 * File = Pointer to CFFILE for file being extracted
2291 * FileName = Pointer to buffer with name of file (full path)
2292 */
2293 {
2294 }
2295
2296
2297 void CCabinet::OnDiskChange(char* CabinetName,
2298 char* DiskLabel)
2299 /*
2300 * FUNCTION: Called when a new disk is to be processed
2301 * ARGUMENTS:
2302 * CabinetName = Pointer to buffer with name of cabinet
2303 * DiskLabel = Pointer to buffer with label of disk
2304 */
2305 {
2306 }
2307
2308
2309 #ifndef CAB_READ_ONLY
2310
2311 void CCabinet::OnAdd(PCFFILE File,
2312 char* FileName)
2313 /*
2314 * FUNCTION: Called just before adding a file to a cabinet
2315 * ARGUMENTS:
2316 * File = Pointer to CFFILE for file being added
2317 * FileName = Pointer to buffer with name of file (full path)
2318 */
2319 {
2320 }
2321
2322
2323 bool CCabinet::OnDiskLabel(ULONG Number, char* Label)
2324 /*
2325 * FUNCTION: Called when a disk needs a label
2326 * ARGUMENTS:
2327 * Number = Cabinet number that needs a label
2328 * Label = Pointer to buffer to place label of disk
2329 * RETURNS:
2330 * true if a disk label was returned, false if not
2331 */
2332 {
2333 return false;
2334 }
2335
2336
2337 bool CCabinet::OnCabinetName(ULONG Number, char* Name)
2338 /*
2339 * FUNCTION: Called when a cabinet needs a name
2340 * ARGUMENTS:
2341 * Number = Disk number that needs a name
2342 * Name = Pointer to buffer to place name of cabinet
2343 * RETURNS:
2344 * true if a cabinet name was returned, false if not
2345 */
2346 {
2347 return false;
2348 }
2349
2350 #endif /* CAB_READ_ONLY */
2351
2352 PCFFOLDER_NODE CCabinet::LocateFolderNode(ULONG Index)
2353 /*
2354 * FUNCTION: Locates a folder node
2355 * ARGUMENTS:
2356 * Index = Folder index
2357 * RETURNS:
2358 * Pointer to folder node or NULL if the folder node was not found
2359 */
2360 {
2361 PCFFOLDER_NODE Node;
2362
2363 switch (Index)
2364 {
2365 case CAB_FILE_SPLIT:
2366 return FolderListTail;
2367
2368 case CAB_FILE_CONTINUED:
2369 case CAB_FILE_PREV_NEXT:
2370 return FolderListHead;
2371 }
2372
2373 Node = FolderListHead;
2374 while (Node != NULL)
2375 {
2376 if (Node->Index == Index)
2377 return Node;
2378 Node = Node->Next;
2379 }
2380 return NULL;
2381 }
2382
2383
2384 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File)
2385 /*
2386 * FUNCTION: Returns the absolute offset of a file
2387 * ARGUMENTS:
2388 * File = Pointer to CFFILE_NODE structure for file
2389 * RETURNS:
2390 * Status of operation
2391 */
2392 {
2393 PCFDATA_NODE Node;
2394
2395 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2396 File->FileName,
2397 (UINT)File->File.FileOffset,
2398 (UINT)File->File.FileSize));
2399
2400 Node = CurrentFolderNode->DataListHead;
2401 while (Node != NULL)
2402 {
2403 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2404 (UINT)Node->UncompOffset,
2405 (UINT)(Node->UncompOffset + Node->Data.UncompSize),
2406 (UINT)Node->Data.UncompSize));
2407
2408 /* Node->Data.UncompSize will be 0 if the block is split
2409 (ie. it is the last block in this cabinet) */
2410 if ((Node->Data.UncompSize == 0) ||
2411 ((File->File.FileOffset >= Node->UncompOffset) &&
2412 (File->File.FileOffset < Node->UncompOffset +
2413 Node->Data.UncompSize)))
2414 {
2415 File->DataBlock = Node;
2416 return CAB_STATUS_SUCCESS;
2417 }
2418
2419 Node = Node->Next;
2420 }
2421 return CAB_STATUS_INVALID_CAB;
2422 }
2423
2424
2425 ULONG CCabinet::LocateFile(char* FileName,
2426 PCFFILE_NODE *File)
2427 /*
2428 * FUNCTION: Locates a file in the cabinet
2429 * ARGUMENTS:
2430 * FileName = Pointer to string with name of file to locate
2431 * File = Address of pointer to CFFILE_NODE structure to fill
2432 * RETURNS:
2433 * Status of operation
2434 * NOTES:
2435 * Current folder is set to the folder of the file
2436 */
2437 {
2438 PCFFILE_NODE Node;
2439 ULONG Status;
2440
2441 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName));
2442
2443 Node = FileListHead;
2444 while (Node != NULL)
2445 {
2446 if (strcasecmp(FileName, Node->FileName) == 0)
2447 {
2448 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
2449 if (!CurrentFolderNode)
2450 {
2451 DPRINT(MID_TRACE, ("Folder with index number (%u) not found.\n",
2452 Node->File.FileControlID));
2453 return CAB_STATUS_INVALID_CAB;
2454 }
2455
2456 if (Node->DataBlock == NULL)
2457 Status = GetAbsoluteOffset(Node);
2458 else
2459 Status = CAB_STATUS_SUCCESS;
2460
2461 *File = Node;
2462 return Status;
2463 }
2464 Node = Node->Next;
2465 }
2466 return CAB_STATUS_NOFILE;
2467 }
2468
2469
2470 ULONG CCabinet::ReadString(char* String, LONG MaxLength)
2471 /*
2472 * FUNCTION: Reads a NULL-terminated string from the cabinet
2473 * ARGUMENTS:
2474 * String = Pointer to buffer to place string
2475 * MaxLength = Maximum length of string
2476 * RETURNS:
2477 * Status of operation
2478 */
2479 {
2480 ULONG BytesRead;
2481 ULONG Status;
2482 LONG Size;
2483 bool Found;
2484
2485 Found = false;
2486
2487 Status = ReadBlock(String, MaxLength, &BytesRead);
2488 if (Status != CAB_STATUS_SUCCESS)
2489 {
2490 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2491 return CAB_STATUS_INVALID_CAB;
2492 }
2493
2494 // Find the terminating NULL character
2495 for (Size = 0; Size < MaxLength; Size++)
2496 {
2497 if (String[Size] == '\0')
2498 {
2499 Found = true;
2500 break;
2501 }
2502 }
2503
2504 if (!Found)
2505 {
2506 DPRINT(MIN_TRACE, ("Filename in the cabinet file is too long.\n"));
2507 return CAB_STATUS_INVALID_CAB;
2508 }
2509
2510 // Compute the offset of the next CFFILE.
2511 // We have to subtract from the current offset here, because we read MaxLength characters above and most-probably the file name isn't MaxLength characters long.
2512 // + 1 to skip the terminating NULL character as well.
2513 Size = -(MaxLength - Size) + 1;
2514
2515 #if defined(_WIN32)
2516 if( SetFilePointer(FileHandle,
2517 (LONG)Size,
2518 NULL,
2519 FILE_CURRENT) == INVALID_SET_FILE_POINTER )
2520 {
2521 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2522 return CAB_STATUS_INVALID_CAB;
2523 }
2524 #else
2525 if (fseek(FileHandle, (off_t)Size, SEEK_CUR) != 0)
2526 {
2527 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2528 return CAB_STATUS_INVALID_CAB;
2529 }
2530 #endif
2531 return CAB_STATUS_SUCCESS;
2532 }
2533
2534
2535 ULONG CCabinet::ReadFileTable()
2536 /*
2537 * FUNCTION: Reads the file table from the cabinet file
2538 * RETURNS:
2539 * Status of operation
2540 */
2541 {
2542 ULONG i;
2543 ULONG Status;
2544 ULONG BytesRead;
2545 PCFFILE_NODE File;
2546
2547 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n",
2548 (UINT)CABHeader.FileTableOffset));
2549
2550 /* Seek to file table */
2551 #if defined(_WIN32)
2552 if( SetFilePointer(FileHandle,
2553 CABHeader.FileTableOffset,
2554 NULL,
2555 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2556 {
2557 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2558 return CAB_STATUS_INVALID_CAB;
2559 }
2560 #else
2561 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0)
2562 {
2563 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2564 return CAB_STATUS_INVALID_CAB;
2565 }
2566 #endif
2567
2568 for (i = 0; i < CABHeader.FileCount; i++)
2569 {
2570 File = NewFileNode();
2571 if (!File)
2572 {
2573 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2574 return CAB_STATUS_NOMEMORY;
2575 }
2576
2577 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
2578 &BytesRead)) != CAB_STATUS_SUCCESS)
2579 {
2580 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2581 return CAB_STATUS_INVALID_CAB;
2582 }
2583
2584 File->FileName = (char*)AllocateMemory(PATH_MAX);
2585 if (!File->FileName)
2586 {
2587 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2588 return CAB_STATUS_NOMEMORY;
2589 }
2590
2591 /* Read file name */
2592 Status = ReadString(File->FileName, PATH_MAX);
2593 if (Status != CAB_STATUS_SUCCESS)
2594 return Status;
2595
2596 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2597 File->FileName,
2598 (UINT)File->File.FileOffset,
2599 (UINT)File->File.FileSize,
2600 File->File.FileControlID));
2601
2602 }
2603 return CAB_STATUS_SUCCESS;
2604 }
2605
2606
2607 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode)
2608 /*
2609 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2610 * ARGUMENTS:
2611 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2612 * RETURNS:
2613 * Status of operation
2614 */
2615 {
2616 ULONG AbsoluteOffset;
2617 ULONG UncompOffset;
2618 PCFDATA_NODE Node;
2619 ULONG BytesRead;
2620 ULONG Status;
2621 ULONG i;
2622
2623 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2624 (UINT)FolderNode->Index, (UINT)FolderNode->Folder.DataOffset));
2625
2626 AbsoluteOffset = FolderNode->Folder.DataOffset;
2627 UncompOffset = FolderNode->UncompOffset;
2628
2629 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++)
2630 {
2631 Node = NewDataNode(FolderNode);
2632 if (!Node)
2633 {
2634 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2635 return CAB_STATUS_NOMEMORY;
2636 }
2637
2638 /* Seek to data block */
2639 #if defined(_WIN32)
2640 if( SetFilePointer(FileHandle,
2641 AbsoluteOffset,
2642 NULL,
2643 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2644 {
2645 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2646 return CAB_STATUS_INVALID_CAB;
2647 }
2648 #else
2649 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0)
2650 {
2651 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2652 return CAB_STATUS_INVALID_CAB;
2653 }
2654 #endif
2655
2656 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
2657 &BytesRead)) != CAB_STATUS_SUCCESS)
2658 {
2659 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2660 return CAB_STATUS_INVALID_CAB;
2661 }
2662
2663 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2664 (UINT)AbsoluteOffset,
2665 (UINT)UncompOffset,
2666 (UINT)Node->Data.Checksum,
2667 Node->Data.CompSize,
2668 Node->Data.UncompSize));
2669
2670 Node->AbsoluteOffset = AbsoluteOffset;
2671 Node->UncompOffset = UncompOffset;
2672
2673 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
2674 UncompOffset += Node->Data.UncompSize;
2675 }
2676
2677 FolderUncompSize = UncompOffset;
2678
2679 return CAB_STATUS_SUCCESS;
2680 }
2681
2682
2683 PCFFOLDER_NODE CCabinet::NewFolderNode()
2684 /*
2685 * FUNCTION: Creates a new folder node
2686 * RETURNS:
2687 * Pointer to node if there was enough free memory available, otherwise NULL
2688 */
2689 {
2690 PCFFOLDER_NODE Node;
2691
2692 Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE));
2693 if (!Node)
2694 return NULL;
2695
2696 memset(Node, 0, sizeof(CFFOLDER_NODE));
2697
2698 Node->Folder.CompressionType = CAB_COMP_NONE;
2699
2700 Node->Prev = FolderListTail;
2701
2702 if (FolderListTail != NULL)
2703 FolderListTail->Next = Node;
2704 else
2705 FolderListHead = Node;
2706
2707 FolderListTail = Node;
2708
2709 return Node;
2710 }
2711
2712
2713 PCFFILE_NODE CCabinet::NewFileNode()
2714 /*
2715 * FUNCTION: Creates a new file node
2716 * ARGUMENTS:
2717 * FolderNode = Pointer to folder node to bind file to
2718 * RETURNS:
2719 * Pointer to node if there was enough free memory available, otherwise NULL
2720 */
2721 {
2722 PCFFILE_NODE Node;
2723
2724 Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE));
2725 if (!Node)
2726 return NULL;
2727
2728 memset(Node, 0, sizeof(CFFILE_NODE));
2729
2730 Node->Prev = FileListTail;
2731
2732 if (FileListTail != NULL)
2733 FileListTail->Next = Node;
2734 else
2735 FileListHead = Node;
2736
2737 FileListTail = Node;
2738
2739 return Node;
2740 }
2741
2742
2743 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode)
2744 /*
2745 * FUNCTION: Creates a new data block node
2746 * ARGUMENTS:
2747 * FolderNode = Pointer to folder node to bind data block to
2748 * RETURNS:
2749 * Pointer to node if there was enough free memory available, otherwise NULL
2750 */
2751 {
2752 PCFDATA_NODE Node;
2753
2754 Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE));
2755 if (!Node)
2756 return NULL;
2757
2758 memset(Node, 0, sizeof(CFDATA_NODE));
2759
2760 Node->Prev = FolderNode->DataListTail;
2761
2762 if (FolderNode->DataListTail != NULL)
2763 FolderNode->DataListTail->Next = Node;
2764 else
2765 FolderNode->DataListHead = Node;
2766
2767 FolderNode->DataListTail = Node;
2768
2769 return Node;
2770 }
2771
2772
2773 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode)
2774 /*
2775 * FUNCTION: Destroys data block nodes bound to a folder node
2776 * ARGUMENTS:
2777 * FolderNode = Pointer to folder node
2778 */
2779 {
2780 PCFDATA_NODE PrevNode;
2781 PCFDATA_NODE NextNode;
2782
2783 NextNode = FolderNode->DataListHead;
2784 while (NextNode != NULL)
2785 {
2786 PrevNode = NextNode->Next;
2787 FreeMemory(NextNode);
2788 NextNode = PrevNode;
2789 }
2790 FolderNode->DataListHead = NULL;
2791 FolderNode->DataListTail = NULL;
2792 }
2793
2794
2795 void CCabinet::DestroyFileNodes()
2796 /*
2797 * FUNCTION: Destroys file nodes
2798 */
2799 {
2800 PCFFILE_NODE PrevNode;
2801 PCFFILE_NODE NextNode;
2802
2803 NextNode = FileListHead;
2804 while (NextNode != NULL)
2805 {
2806 PrevNode = NextNode->Next;
2807 if (NextNode->FileName)
2808 FreeMemory(NextNode->FileName);
2809 FreeMemory(NextNode);
2810 NextNode = PrevNode;
2811 }
2812 FileListHead = NULL;
2813 FileListTail = NULL;
2814 }
2815
2816
2817 void CCabinet::DestroyDeletedFileNodes()
2818 /*
2819 * FUNCTION: Destroys file nodes that are marked for deletion
2820 */
2821 {
2822 PCFFILE_NODE CurNode;
2823 PCFFILE_NODE NextNode;
2824
2825 CurNode = FileListHead;
2826 while (CurNode != NULL)
2827 {
2828 NextNode = CurNode->Next;
2829
2830 if (CurNode->Delete)
2831 {
2832 if (CurNode->Prev != NULL)
2833 CurNode->Prev->Next = CurNode->Next;
2834 else
2835 {
2836 FileListHead = CurNode->Next;
2837 if (FileListHead)
2838 FileListHead->Prev = NULL;
2839 }
2840
2841 if (CurNode->Next != NULL)
2842 CurNode->Next->Prev = CurNode->Prev;
2843 else
2844 {
2845 FileListTail = CurNode->Prev;
2846 if (FileListTail)
2847 FileListTail->Next = NULL;
2848 }
2849
2850 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName));
2851
2852 TotalFileSize -= (sizeof(CFFILE) + (ULONG)strlen(GetFileName(CurNode->FileName)) + 1);
2853
2854 if (CurNode->FileName)
2855 FreeMemory(CurNode->FileName);
2856 FreeMemory(CurNode);
2857 }
2858 CurNode = NextNode;
2859 }
2860 }
2861
2862
2863 void CCabinet::DestroyFolderNodes()
2864 /*
2865 * FUNCTION: Destroys folder nodes
2866 */
2867 {
2868 PCFFOLDER_NODE PrevNode;
2869 PCFFOLDER_NODE NextNode;
2870
2871 NextNode = FolderListHead;
2872 while (NextNode != NULL)
2873 {
2874 PrevNode = NextNode->Next;
2875 DestroyDataNodes(NextNode);
2876 FreeMemory(NextNode);
2877 NextNode = PrevNode;
2878 }
2879 FolderListHead = NULL;
2880 FolderListTail = NULL;
2881 }
2882
2883
2884 void CCabinet::DestroyDeletedFolderNodes()
2885 /*
2886 * FUNCTION: Destroys folder nodes that are marked for deletion
2887 */
2888 {
2889 PCFFOLDER_NODE CurNode;
2890 PCFFOLDER_NODE NextNode;
2891
2892 CurNode = FolderListHead;
2893 while (CurNode != NULL)
2894 {
2895 NextNode = CurNode->Next;
2896
2897 if (CurNode->Delete)
2898 {
2899 if (CurNode->Prev != NULL)
2900 CurNode->Prev->Next = CurNode->Next;
2901 else
2902 {
2903 FolderListHead = CurNode->Next;
2904 if (FolderListHead)
2905 FolderListHead->Prev = NULL;
2906 }
2907
2908 if (CurNode->Next != NULL)
2909 CurNode->Next->Prev = CurNode->Prev;
2910 else
2911 {
2912 FolderListTail = CurNode->Prev;
2913 if (FolderListTail)
2914 FolderListTail->Next = NULL;
2915 }
2916
2917 DestroyDataNodes(CurNode);
2918 FreeMemory(CurNode);
2919
2920 TotalFolderSize -= sizeof(CFFOLDER);
2921 }
2922 CurNode = NextNode;
2923 }
2924 }
2925
2926
2927 ULONG CCabinet::ComputeChecksum(void* Buffer,
2928 ULONG Size,
2929 ULONG Seed)
2930 /*
2931 * FUNCTION: Computes checksum for data block
2932 * ARGUMENTS:
2933 * Buffer = Pointer to data buffer
2934 * Size = Length of data buffer
2935 * Seed = Previously computed checksum
2936 * RETURNS:
2937 * Checksum of buffer
2938 */
2939 {
2940 int UlongCount; // Number of ULONGs in block
2941 ULONG Checksum; // Checksum accumulator
2942 unsigned char* pb;
2943 ULONG ul;
2944
2945 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2946 won't accept checksums computed by this routine */
2947
2948 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer, (UINT)Size));
2949
2950 UlongCount = Size / 4; // Number of ULONGs
2951 Checksum = Seed; // Init checksum
2952 pb = (unsigned char*)Buffer; // Start at front of data block
2953
2954 /* Checksum integral multiple of ULONGs */
2955 while (UlongCount-- > 0)
2956 {
2957 /* NOTE: Build ULONG in big/little-endian independent manner */
2958 ul = *pb++; // Get low-order byte
2959 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2960 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
2961 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
2962
2963 Checksum ^= ul; // Update checksum
2964 }
2965
2966 /* Checksum remainder bytes */
2967 ul = 0;
2968 switch (Size % 4)
2969 {
2970 case 3:
2971 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
2972 case 2:
2973 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2974 case 1:
2975 ul |= *pb++; // Get low-order byte
2976 default:
2977 break;
2978 }
2979 Checksum ^= ul; // Update checksum
2980
2981 /* Return computed checksum */
2982 return Checksum;
2983 }
2984
2985
2986 ULONG CCabinet::ReadBlock(void* Buffer,
2987 ULONG Size,
2988 PULONG BytesRead)
2989 /*
2990 * FUNCTION: Read a block of data from file
2991 * ARGUMENTS:
2992 * Buffer = Pointer to data buffer
2993 * Size = Length of data buffer
2994 * BytesRead = Pointer to ULONG that on return will contain
2995 * number of bytes read
2996 * RETURNS:
2997 * Status of operation
2998 */
2999 {
3000 if (!ReadFileData(FileHandle, Buffer, Size, BytesRead))
3001 return CAB_STATUS_INVALID_CAB;
3002 return CAB_STATUS_SUCCESS;
3003 }
3004
3005 bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern)
3006 /*
3007 * FUNCTION: Matches a wildcard character pattern against a file
3008 * ARGUMENTS:
3009 * FileName = The file name to check
3010 * Pattern = The pattern
3011 * RETURNS:
3012 * Whether the pattern matches the file
3013 *
3014 * COPYRIGHT:
3015 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
3016 * Adapted from code written by Ingo Wilken.
3017 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
3018 */
3019 {
3020 char* retryPattern = NULL;
3021 char* retryFileName = NULL;
3022 char ch;
3023
3024 while (*FileName || *Pattern)
3025 {
3026 ch = *Pattern++;
3027
3028 switch (ch)
3029 {
3030 case '*':
3031 retryPattern = Pattern;
3032 retryFileName = FileName;
3033 break;
3034
3035 case '?':
3036 if (*FileName++ == '\0')
3037 return false;
3038
3039 break;
3040
3041 default:
3042 if (*FileName == ch)
3043 {
3044 if (*FileName)
3045 FileName++;
3046 break;
3047 }
3048
3049 if (*FileName)
3050 {
3051 Pattern = retryPattern;
3052 FileName = ++retryFileName;
3053 break;
3054 }
3055
3056 return false;
3057 }
3058
3059 if (!Pattern)
3060 return false;
3061 }
3062
3063 return true;
3064 }
3065
3066 #ifndef CAB_READ_ONLY
3067
3068 ULONG CCabinet::InitCabinetHeader()
3069 /*
3070 * FUNCTION: Initializes cabinet header and optional fields
3071 * RETURNS:
3072 * Status of operation
3073 */
3074 {
3075 ULONG TotalSize;
3076 ULONG Size;
3077
3078 CABHeader.FileTableOffset = 0; // Not known yet
3079 CABHeader.FolderCount = 0; // Not known yet
3080 CABHeader.FileCount = 0; // Not known yet
3081 CABHeader.Flags = 0; // Not known yet
3082
3083 CABHeader.CabinetNumber = (USHORT)CurrentDiskNumber;
3084
3085 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev)))
3086 {
3087 CABHeader.Flags |= CAB_FLAG_HASPREV;
3088 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev))
3089 strcpy(CabinetPrev, "");
3090 }
3091
3092 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext))
3093 {
3094 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3095 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext))
3096 strcpy(DiskNext, "");
3097 }
3098
3099 TotalSize = 0;
3100
3101 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3102 {
3103
3104 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3105
3106 /* Calculate size of name of previous cabinet */
3107 TotalSize += (ULONG)strlen(CabinetPrev) + 1;
3108
3109 /* Calculate size of label of previous disk */
3110 TotalSize += (ULONG)strlen(DiskPrev) + 1;
3111 }
3112
3113 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3114 {
3115
3116 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3117
3118 /* Calculate size of name of next cabinet */
3119 Size = (ULONG)strlen(CabinetNext) + 1;
3120 TotalSize += Size;
3121 NextFieldsSize = Size;
3122
3123 /* Calculate size of label of next disk */
3124 Size = (ULONG)strlen(DiskNext) + 1;
3125 TotalSize += Size;
3126 NextFieldsSize += Size;
3127 }
3128 else
3129 NextFieldsSize = 0;
3130
3131 /* Add cabinet reserved area size if present */
3132 if (CabinetReservedFileSize > 0)
3133 {
3134 CABHeader.Flags |= CAB_FLAG_RESERVE;
3135 TotalSize += CabinetReservedFileSize;
3136 TotalSize += sizeof(ULONG); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3137 }
3138
3139 DiskSize += TotalSize;
3140
3141 TotalHeaderSize = sizeof(CFHEADER) + TotalSize;
3142
3143 return CAB_STATUS_SUCCESS;
3144 }
3145
3146
3147 ULONG CCabinet::WriteCabinetHeader(bool MoreDisks)
3148 /*
3149 * FUNCTION: Writes the cabinet header and optional fields
3150 * ARGUMENTS:
3151 * MoreDisks = true if next cabinet name should be included
3152 * RETURNS:
3153 * Status of operation
3154 */
3155 {
3156 PCFFOLDER_NODE FolderNode;
3157 PCFFILE_NODE FileNode;
3158 ULONG BytesWritten;
3159 ULONG Size;
3160
3161 if (MoreDisks)
3162 {
3163 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3164 Size = TotalHeaderSize;
3165 }
3166 else
3167 {
3168 CABHeader.Flags &= ~CAB_FLAG_HASNEXT;
3169 DiskSize -= NextFieldsSize;
3170 Size = TotalHeaderSize - NextFieldsSize;
3171 }
3172
3173 /* Set absolute folder offsets */
3174 BytesWritten = Size + TotalFolderSize + TotalFileSize;
3175 CABHeader.FolderCount = 0;
3176 FolderNode = FolderListHead;
3177 while (FolderNode != NULL)
3178 {
3179 FolderNode->Folder.DataOffset = BytesWritten;
3180
3181 BytesWritten += FolderNode->TotalFolderSize;
3182
3183 CABHeader.FolderCount++;
3184
3185 FolderNode = FolderNode->Next;
3186 }
3187
3188 /* Set absolute offset of file table */
3189 CABHeader.FileTableOffset = Size + TotalFolderSize;
3190
3191 /* Count number of files to be committed */
3192 CABHeader.FileCount = 0;
3193 FileNode = FileListHead;
3194 while (FileNode != NULL)
3195 {
3196 if (FileNode->Commit)
3197 CABHeader.FileCount++;
3198 FileNode = FileNode->Next;
3199 }
3200
3201 CABHeader.CabinetSize = DiskSize;
3202
3203 /* Write header */
3204 #if defined(_WIN32)
3205 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), (LPDWORD)&BytesWritten, NULL))
3206 {
3207 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3208 return CAB_STATUS_CANNOT_WRITE;
3209 }
3210 #else
3211 BytesWritten = sizeof(CFHEADER);
3212 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1)
3213 {
3214 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3215 return CAB_STATUS_CANNOT_WRITE;
3216 }
3217 #endif
3218
3219 /* Write per-cabinet reserved area if present */
3220 if (CABHeader.Flags & CAB_FLAG_RESERVE)
3221 {
3222 ULONG ReservedSize;
3223
3224 ReservedSize = CabinetReservedFileSize & 0xffff;
3225 ReservedSize |= (0 << 16); /* Folder reserved area size */
3226 ReservedSize |= (0 << 24); /* Folder reserved area size */
3227 #if defined(_WIN32)
3228 if (!WriteFile(FileHandle, &ReservedSize, sizeof(ULONG), (LPDWORD)&BytesWritten, NULL))
3229 {
3230 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3231 return CAB_STATUS_CANNOT_WRITE;
3232 }
3233 #else
3234 BytesWritten = sizeof(ULONG);
3235 if (fwrite(&ReservedSize, sizeof(ULONG), 1, FileHandle) < 1)
3236 {
3237 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3238 return CAB_STATUS_CANNOT_WRITE;
3239 }
3240 #endif
3241
3242 #if defined(_WIN32)
3243 if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, (LPDWORD)&BytesWritten, NULL))
3244 {
3245 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3246 return CAB_STATUS_CANNOT_WRITE;
3247 }
3248 #else
3249 BytesWritten = CabinetReservedFileSize;
3250 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1)
3251 {
3252 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3253 return CAB_STATUS_CANNOT_WRITE;
3254 }
3255 #endif
3256 }
3257
3258 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3259 {
3260 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3261
3262 /* Write name of previous cabinet */
3263 Size = (ULONG)strlen(CabinetPrev) + 1;
3264 #if defined(_WIN32)
3265 if (!WriteFile(FileHandle, CabinetPrev, Size, (LPDWORD)&BytesWritten, NULL))
3266 {
3267 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3268 return CAB_STATUS_CANNOT_WRITE;
3269 }
3270 #else
3271 BytesWritten = Size;
3272 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1)
3273 {
3274 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3275 return CAB_STATUS_CANNOT_WRITE;
3276 }
3277 #endif
3278
3279 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev));
3280
3281 /* Write label of previous disk */
3282 Size = (ULONG)strlen(DiskPrev) + 1;
3283 #if defined(_WIN32)
3284 if (!WriteFile(FileHandle, DiskPrev, Size, (LPDWORD)&BytesWritten, NULL))
3285 {
3286 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3287 return CAB_STATUS_CANNOT_WRITE;
3288 }
3289 #else
3290 BytesWritten = Size;
3291 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1)
3292 {
3293 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3294 return CAB_STATUS_CANNOT_WRITE;
3295 }
3296 #endif
3297 }
3298
3299 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3300 {
3301 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3302
3303 /* Write name of next cabinet */
3304 Size = (ULONG)strlen(CabinetNext) + 1;
3305 #if defined(_WIN32)
3306 if (!WriteFile(FileHandle, CabinetNext, Size, (LPDWORD)&BytesWritten, NULL))
3307 {
3308 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3309 return CAB_STATUS_CANNOT_WRITE;
3310 }
3311 #else
3312 BytesWritten = Size;
3313 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1)
3314 {
3315 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3316 return CAB_STATUS_CANNOT_WRITE;
3317 }
3318 #endif
3319
3320 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext));
3321
3322 /* Write label of next disk */
3323 Size = (ULONG)strlen(DiskNext) + 1;
3324 #if defined(_WIN32)
3325 if (!WriteFile(FileHandle, DiskNext, Size, (LPDWORD)&BytesWritten, NULL))
3326 {
3327 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3328 return CAB_STATUS_CANNOT_WRITE;
3329 }
3330 #else
3331 BytesWritten = Size;
3332 if (fwrite(DiskNext, Size, 1, FileHandle) < 1)
3333 {
3334 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3335 return CAB_STATUS_CANNOT_WRITE;