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