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