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