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