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