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