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