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