xref: /reactos/sdk/tools/cabman/cabinet.cxx (revision 70193adc)
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