c09eea97f176cb1a49d2d4d1002349534bd3a87a
[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 CurrentIBuffer = (unsigned char*)CurrentIBuffer + BytesRead;
1698 CurrentIBufferSize += (USHORT)BytesRead;
1699
1700 if (CurrentIBufferSize == CAB_BLOCKSIZE)
1701 {
1702 Status = WriteDataBlock();
1703 if (Status != CAB_STATUS_SUCCESS)
1704 return Status;
1705 }
1706 TotalBytesLeft -= BytesRead;
1707 } while ((TotalBytesLeft > 0) && (!CreateNewDisk));
1708 }
1709
1710 if (TotalBytesLeft == 0)
1711 {
1712 CloseFile(SourceFile);
1713 FileNode->Delete = true;
1714
1715 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER)
1716 {
1717 FileNode->File.FileControlID = CAB_FILE_CONTINUED;
1718 CurrentFolderNode->Delete = true;
1719
1720 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1721 {
1722 Status = WriteDataBlock();
1723 if (Status != CAB_STATUS_SUCCESS)
1724 return Status;
1725 }
1726
1727 CreateNewFolder = true;
1728 }
1729 }
1730 else
1731 {
1732 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER)
1733 FileNode->File.FileControlID = CAB_FILE_SPLIT;
1734 else
1735 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT;
1736 }
1737
1738 return CAB_STATUS_SUCCESS;
1739 }
1740
1741
1742 ULONG CCabinet::WriteDisk(ULONG MoreDisks)
1743 /*
1744 * FUNCTION: Forces the current disk to be written
1745 * ARGUMENTS:
1746 * MoreDisks = true if there is one or more disks after this disk
1747 * RETURNS:
1748 * Status of operation
1749 */
1750 {
1751 PCFFILE_NODE FileNode;
1752 ULONG Status;
1753
1754 ContinueFile = false;
1755 FileNode = FileListHead;
1756 while (FileNode != NULL)
1757 {
1758 Status = WriteFileToScratchStorage(FileNode);
1759 if (Status != CAB_STATUS_SUCCESS)
1760 return Status;
1761
1762 if (CreateNewDisk)
1763 {
1764 /* A data block could span more than two
1765 disks if MaxDiskSize is very small */
1766 while (CreateNewDisk)
1767 {
1768 DPRINT(MAX_TRACE, ("Creating new disk.\n"));
1769 CommitDisk(true);
1770 CloseDisk();
1771 NewDisk();
1772
1773 ContinueFile = true;
1774 CreateNewDisk = false;
1775
1776 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1777 (UINT)CurrentIBufferSize, (UINT)CurrentOBufferSize));
1778
1779 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1780 {
1781 Status = WriteDataBlock();
1782 if (Status != CAB_STATUS_SUCCESS)
1783 return Status;
1784 }
1785 }
1786 }
1787 else
1788 {
1789 ContinueFile = false;
1790 FileNode = FileNode->Next;
1791 }
1792 }
1793
1794 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1795 {
1796 /* A data block could span more than two
1797 disks if MaxDiskSize is very small */
1798
1799 ASSERT(CreateNewDisk == false);
1800
1801 do
1802 {
1803 if (CreateNewDisk)
1804 {
1805 DPRINT(MID_TRACE, ("Creating new disk 2.\n"));
1806 CommitDisk(true);
1807 CloseDisk();
1808 NewDisk();
1809 CreateNewDisk = false;
1810
1811 ASSERT(FileNode == FileListHead);
1812 }
1813
1814 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1815 {
1816 Status = WriteDataBlock();
1817 if (Status != CAB_STATUS_SUCCESS)
1818 return Status;
1819 }
1820 } while (CreateNewDisk);
1821 }
1822 CommitDisk(MoreDisks);
1823
1824 return CAB_STATUS_SUCCESS;
1825 }
1826
1827
1828 ULONG CCabinet::CommitDisk(ULONG MoreDisks)
1829 /*
1830 * FUNCTION: Commits the current disk
1831 * ARGUMENTS:
1832 * MoreDisks = true if there is one or more disks after this disk
1833 * RETURNS:
1834 * Status of operation
1835 */
1836 {
1837 PCFFOLDER_NODE FolderNode;
1838 ULONG Status;
1839
1840 OnCabinetName(CurrentDiskNumber, CabinetName);
1841
1842 /* Create file, fail if it already exists */
1843 #if defined(WIN32)
1844 FileHandle = CreateFile(CabinetName, // Create this file
1845 GENERIC_WRITE, // Open for writing
1846 0, // No sharing
1847 NULL, // No security
1848 CREATE_NEW, // New file only
1849 FILE_ATTRIBUTE_NORMAL, // Normal file
1850 NULL); // No attribute template
1851 if (FileHandle == INVALID_HANDLE_VALUE)
1852 {
1853 ULONG Status;
1854 /* If file exists, ask to overwrite file */
1855 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
1856 (OnOverwrite(NULL, CabinetName)))
1857 {
1858
1859 /* Create cabinet file, overwrite if it already exists */
1860 FileHandle = CreateFile(CabinetName, // Create this file
1861 GENERIC_WRITE, // Open for writing
1862 0, // No sharing
1863 NULL, // No security
1864 TRUNCATE_EXISTING, // Truncate the file
1865 FILE_ATTRIBUTE_NORMAL, // Normal file
1866 NULL); // No attribute template
1867 if (FileHandle == INVALID_HANDLE_VALUE)
1868 return CAB_STATUS_CANNOT_CREATE;
1869 }
1870 else
1871 {
1872 if (Status == ERROR_FILE_EXISTS)
1873 return CAB_STATUS_FILE_EXISTS;
1874 else
1875 return CAB_STATUS_CANNOT_CREATE;
1876 }
1877 }
1878 #else /* !WIN32 */
1879 FileHandle = fopen(CabinetName, "rb");
1880 if (FileHandle != NULL)
1881 {
1882 fclose(FileHandle);
1883 /* If file exists, ask to overwrite file */
1884 if (OnOverwrite(NULL, CabinetName))
1885 {
1886 FileHandle = fopen(CabinetName, "w+b");
1887 if (FileHandle == NULL)
1888 return CAB_STATUS_CANNOT_CREATE;
1889 }
1890 else
1891 return CAB_STATUS_FILE_EXISTS;
1892
1893 }
1894 else
1895 {
1896 FileHandle = fopen(CabinetName, "w+b");
1897 if (FileHandle == NULL)
1898 return CAB_STATUS_CANNOT_CREATE;
1899 }
1900 #endif
1901
1902 WriteCabinetHeader(MoreDisks != 0);
1903
1904 Status = WriteFolderEntries();
1905 if (Status != CAB_STATUS_SUCCESS)
1906 return Status;
1907
1908 /* Write file entries */
1909 WriteFileEntries();
1910
1911 /* Write data blocks */
1912 FolderNode = FolderListHead;
1913 while (FolderNode != NULL)
1914 {
1915 if (FolderNode->Commit)
1916 {
1917 Status = CommitDataBlocks(FolderNode);
1918 if (Status != CAB_STATUS_SUCCESS)
1919 return Status;
1920 /* Remove data blocks for folder */
1921 DestroyDataNodes(FolderNode);
1922 }
1923 FolderNode = FolderNode->Next;
1924 }
1925
1926 CloseFile(FileHandle);
1927
1928 ScratchFile->Truncate();
1929
1930 return CAB_STATUS_SUCCESS;
1931 }
1932
1933
1934 ULONG CCabinet::CloseDisk()
1935 /*
1936 * FUNCTION: Closes the current disk
1937 * RETURNS:
1938 * Status of operation
1939 */
1940 {
1941 DestroyDeletedFileNodes();
1942
1943 /* Destroy folder nodes that are completely stored */
1944 DestroyDeletedFolderNodes();
1945
1946 CurrentDiskNumber++;
1947
1948 return CAB_STATUS_SUCCESS;
1949 }
1950
1951
1952 ULONG CCabinet::CloseCabinet()
1953 /*
1954 * FUNCTION: Closes the current cabinet
1955 * RETURNS:
1956 * Status of operation
1957 */
1958 {
1959 ULONG Status;
1960
1961 DestroyFileNodes();
1962
1963 DestroyFolderNodes();
1964
1965 if (InputBuffer)
1966 {
1967 FreeMemory(InputBuffer);
1968 InputBuffer = NULL;
1969 }
1970
1971 if (OutputBuffer)
1972 {
1973 FreeMemory(OutputBuffer);
1974 OutputBuffer = NULL;
1975 }
1976
1977 Close();
1978
1979 if (ScratchFile)
1980 {
1981 Status = ScratchFile->Destroy();
1982 delete ScratchFile;
1983 return Status;
1984 }
1985
1986 return CAB_STATUS_SUCCESS;
1987 }
1988
1989
1990 ULONG CCabinet::AddFile(char* FileName)
1991 /*
1992 * FUNCTION: Adds a file to the current disk
1993 * ARGUMENTS:
1994 * FileName = Pointer to string with file name (full path)
1995 * RETURNS:
1996 * Status of operation
1997 */
1998 {
1999 FILEHANDLE SrcFile;
2000 PCFFILE_NODE FileNode;
2001 char* NewFileName;
2002
2003 NewFileName = (char*)AllocateMemory(strlen(FileName) + 1);
2004 if (!NewFileName)
2005 {
2006 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2007 return CAB_STATUS_NOMEMORY;
2008 }
2009 strcpy(NewFileName, FileName);
2010 ConvertPath(NewFileName, false);
2011
2012 /* Try to open file */
2013 #if defined(WIN32)
2014 SrcFile = CreateFile(
2015 NewFileName, // Open this file
2016 GENERIC_READ, // Open for reading
2017 FILE_SHARE_READ, // Share for reading
2018 NULL, // No security
2019 OPEN_EXISTING, // File must exist
2020 FILE_ATTRIBUTE_NORMAL, // Normal file
2021 NULL); // No attribute template
2022 if (SrcFile == INVALID_HANDLE_VALUE)
2023 {
2024 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2025 FreeMemory(NewFileName);
2026 return CAB_STATUS_CANNOT_OPEN;
2027 }
2028 #else /* !WIN32 */
2029 SrcFile = fopen(NewFileName, "rb");
2030 if (SrcFile == NULL)
2031 {
2032 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2033 FreeMemory(NewFileName);
2034 return CAB_STATUS_CANNOT_OPEN;
2035 }
2036 #endif
2037
2038 FileNode = NewFileNode();
2039 if (!FileNode)
2040 {
2041 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2042 FreeMemory(NewFileName);
2043 return CAB_STATUS_NOMEMORY;
2044 }
2045
2046 FileNode->FolderNode = CurrentFolderNode;
2047 FileNode->FileName = NewFileName;
2048
2049 /* FIXME: Check for and handle large files (>= 2GB) */
2050 FileNode->File.FileSize = GetSizeOfFile(SrcFile);
2051 if (FileNode->File.FileSize == (ULONG)-1)
2052 {
2053 DPRINT(MIN_TRACE, ("Cannot read from file.\n"));
2054 FreeMemory(NewFileName);
2055 return CAB_STATUS_CANNOT_READ;
2056 }
2057
2058 GetFileTimes(SrcFile, FileNode);
2059
2060 GetAttributesOnFile(FileNode);
2061
2062 CloseFile(SrcFile);
2063
2064 return CAB_STATUS_SUCCESS;
2065 }
2066
2067 bool CCabinet::CreateSimpleCabinet()
2068 /*
2069 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2070 */
2071 {
2072 bool bRet = false;
2073 char* pszFile;
2074 char szFilePath[MAX_PATH];
2075 char szFile[MAX_PATH];
2076 PSEARCH_CRITERIA Criteria;
2077 ULONG Status;
2078
2079 #if defined(WIN32)
2080 HANDLE hFind;
2081 WIN32_FIND_DATA FindFileData;
2082 #else
2083 DIR* dirp;
2084 struct dirent* dp;
2085 struct stat stbuf;
2086 #endif
2087
2088 // Initialize a new cabinet
2089 Status = NewCabinet();
2090 if (Status != CAB_STATUS_SUCCESS)
2091 {
2092 DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status));
2093 goto cleanup;
2094 }
2095
2096 // Add each file in the criteria list
2097 Criteria = CriteriaListHead;
2098
2099 while(Criteria)
2100 {
2101 // Store the file path with a trailing slash in szFilePath
2102 ConvertPath(Criteria->Search, false);
2103 pszFile = strrchr(Criteria->Search, DIR_SEPARATOR_CHAR);
2104
2105 if(pszFile)
2106 {
2107 // Set the pointer to the start of the file name, not the slash
2108 pszFile++;
2109
2110 strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search);
2111 szFilePath[pszFile - Criteria->Search] = 0;
2112 }
2113 else
2114 {
2115 pszFile = Criteria->Search;
2116
2117 #if defined(WIN32)
2118 szFilePath[0] = 0;
2119 #else
2120 // needed for opendir()
2121 strcpy(szFilePath, "./");
2122 #endif
2123 }
2124
2125 #if defined(WIN32)
2126 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2127 hFind = FindFirstFile(Criteria->Search, &FindFileData);
2128
2129 // Don't stop if a search criteria is not found
2130 if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND)
2131 {
2132 DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError()));
2133 goto cleanup;
2134 }
2135
2136 do
2137 {
2138 if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
2139 {
2140 strcpy(szFile, szFilePath);
2141 strcat(szFile, FindFileData.cFileName);
2142
2143 Status = AddFile(szFile);
2144
2145 if(Status != CAB_STATUS_SUCCESS)
2146 {
2147 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2148 goto cleanup;
2149 }
2150 }
2151 }
2152 while(FindNextFile(hFind, &FindFileData));
2153
2154 FindClose(hFind);
2155 #else
2156 // 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
2157 dirp = opendir(szFilePath);
2158
2159 if(dirp)
2160 {
2161 while( (dp = readdir(dirp)) )
2162 {
2163 strcpy(szFile, szFilePath);
2164 strcat(szFile, dp->d_name);
2165
2166 if(stat(szFile, &stbuf) == 0)
2167 {
2168 if(stbuf.st_mode != S_IFDIR)
2169 {
2170 if(MatchFileNamePattern(dp->d_name, pszFile))
2171 {
2172 Status = AddFile(szFile);
2173
2174 if(Status != CAB_STATUS_SUCCESS)
2175 {
2176 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2177 goto cleanup;
2178 }
2179 }
2180 }
2181 }
2182 else
2183 {
2184 DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno));
2185 goto cleanup;
2186 }
2187 }
2188
2189 closedir(dirp);
2190 }
2191 #endif
2192
2193 Criteria = Criteria->Next;
2194 }
2195
2196 Status = WriteDisk(false);
2197 if (Status == CAB_STATUS_SUCCESS)
2198 Status = CloseDisk();
2199 if (Status != CAB_STATUS_SUCCESS)
2200 {
2201 DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status));
2202 goto cleanup;
2203 }
2204
2205 CloseCabinet();
2206 bRet = true;
2207
2208 cleanup:
2209 DestroySearchCriteria();
2210 return bRet;
2211 }
2212
2213 void CCabinet::SetMaxDiskSize(ULONG Size)
2214 /*
2215 * FUNCTION: Sets the maximum size of the current disk
2216 * ARGUMENTS:
2217 * Size = Maximum size of current disk (0 means no maximum size)
2218 */
2219 {
2220 MaxDiskSize = Size;
2221 }
2222
2223 #endif /* CAB_READ_ONLY */
2224
2225
2226 /* Default event handlers */
2227
2228 bool CCabinet::OnOverwrite(PCFFILE File,
2229 char* FileName)
2230 /*
2231 * FUNCTION: Called when extracting a file and it already exists
2232 * ARGUMENTS:
2233 * File = Pointer to CFFILE for file being extracted
2234 * FileName = Pointer to buffer with name of file (full path)
2235 * RETURNS
2236 * true if the file should be overwritten, false if not
2237 */
2238 {
2239 return false;
2240 }
2241
2242
2243 void CCabinet::OnExtract(PCFFILE File,
2244 char* FileName)
2245 /*
2246 * FUNCTION: Called just before extracting a file
2247 * ARGUMENTS:
2248 * File = Pointer to CFFILE for file being extracted
2249 * FileName = Pointer to buffer with name of file (full path)
2250 */
2251 {
2252 }
2253
2254
2255 void CCabinet::OnDiskChange(char* CabinetName,
2256 char* DiskLabel)
2257 /*
2258 * FUNCTION: Called when a new disk is to be processed
2259 * ARGUMENTS:
2260 * CabinetName = Pointer to buffer with name of cabinet
2261 * DiskLabel = Pointer to buffer with label of disk
2262 */
2263 {
2264 }
2265
2266
2267 #ifndef CAB_READ_ONLY
2268
2269 void CCabinet::OnAdd(PCFFILE File,
2270 char* FileName)
2271 /*
2272 * FUNCTION: Called just before adding a file to a cabinet
2273 * ARGUMENTS:
2274 * File = Pointer to CFFILE for file being added
2275 * FileName = Pointer to buffer with name of file (full path)
2276 */
2277 {
2278 }
2279
2280
2281 bool CCabinet::OnDiskLabel(ULONG Number, char* Label)
2282 /*
2283 * FUNCTION: Called when a disk needs a label
2284 * ARGUMENTS:
2285 * Number = Cabinet number that needs a label
2286 * Label = Pointer to buffer to place label of disk
2287 * RETURNS:
2288 * true if a disk label was returned, false if not
2289 */
2290 {
2291 return false;
2292 }
2293
2294
2295 bool CCabinet::OnCabinetName(ULONG Number, char* Name)
2296 /*
2297 * FUNCTION: Called when a cabinet needs a name
2298 * ARGUMENTS:
2299 * Number = Disk number that needs a name
2300 * Name = Pointer to buffer to place name of cabinet
2301 * RETURNS:
2302 * true if a cabinet name was returned, false if not
2303 */
2304 {
2305 return false;
2306 }
2307
2308 #endif /* CAB_READ_ONLY */
2309
2310 PCFFOLDER_NODE CCabinet::LocateFolderNode(ULONG Index)
2311 /*
2312 * FUNCTION: Locates a folder node
2313 * ARGUMENTS:
2314 * Index = Folder index
2315 * RETURNS:
2316 * Pointer to folder node or NULL if the folder node was not found
2317 */
2318 {
2319 PCFFOLDER_NODE Node;
2320
2321 switch (Index)
2322 {
2323 case CAB_FILE_SPLIT:
2324 return FolderListTail;
2325
2326 case CAB_FILE_CONTINUED:
2327 case CAB_FILE_PREV_NEXT:
2328 return FolderListHead;
2329 }
2330
2331 Node = FolderListHead;
2332 while (Node != NULL)
2333 {
2334 if (Node->Index == Index)
2335 return Node;
2336 Node = Node->Next;
2337 }
2338 return NULL;
2339 }
2340
2341
2342 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File)
2343 /*
2344 * FUNCTION: Returns the absolute offset of a file
2345 * ARGUMENTS:
2346 * File = Pointer to CFFILE_NODE structure for file
2347 * RETURNS:
2348 * Status of operation
2349 */
2350 {
2351 PCFDATA_NODE Node;
2352
2353 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2354 File->FileName,
2355 (UINT)File->File.FileOffset,
2356 (UINT)File->File.FileSize));
2357
2358 Node = CurrentFolderNode->DataListHead;
2359 while (Node != NULL)
2360 {
2361 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2362 (UINT)Node->UncompOffset,
2363 (UINT)(Node->UncompOffset + Node->Data.UncompSize),
2364 (UINT)Node->Data.UncompSize));
2365
2366 /* Node->Data.UncompSize will be 0 if the block is split
2367 (ie. it is the last block in this cabinet) */
2368 if ((Node->Data.UncompSize == 0) ||
2369 ((File->File.FileOffset >= Node->UncompOffset) &&
2370 (File->File.FileOffset < Node->UncompOffset +
2371 Node->Data.UncompSize)))
2372 {
2373 File->DataBlock = Node;
2374 return CAB_STATUS_SUCCESS;
2375 }
2376
2377 Node = Node->Next;
2378 }
2379 return CAB_STATUS_INVALID_CAB;
2380 }
2381
2382
2383 ULONG CCabinet::LocateFile(char* FileName,
2384 PCFFILE_NODE *File)
2385 /*
2386 * FUNCTION: Locates a file in the cabinet
2387 * ARGUMENTS:
2388 * FileName = Pointer to string with name of file to locate
2389 * File = Address of pointer to CFFILE_NODE structure to fill
2390 * RETURNS:
2391 * Status of operation
2392 * NOTES:
2393 * Current folder is set to the folder of the file
2394 */
2395 {
2396 PCFFILE_NODE Node;
2397 ULONG Status;
2398
2399 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName));
2400
2401 Node = FileListHead;
2402 while (Node != NULL)
2403 {
2404 if (strcasecmp(FileName, Node->FileName) == 0)
2405 {
2406 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
2407 if (!CurrentFolderNode)
2408 {
2409 DPRINT(MID_TRACE, ("Folder with index number (%u) not found.\n",
2410 Node->File.FileControlID));
2411 return CAB_STATUS_INVALID_CAB;
2412 }
2413
2414 if (Node->DataBlock == NULL)
2415 Status = GetAbsoluteOffset(Node);
2416 else
2417 Status = CAB_STATUS_SUCCESS;
2418
2419 *File = Node;
2420 return Status;
2421 }
2422 Node = Node->Next;
2423 }
2424 return CAB_STATUS_NOFILE;
2425 }
2426
2427
2428 ULONG CCabinet::ReadString(char* String, LONG MaxLength)
2429 /*
2430 * FUNCTION: Reads a NULL-terminated string from the cabinet
2431 * ARGUMENTS:
2432 * String = Pointer to buffer to place string
2433 * MaxLength = Maximum length of string
2434 * RETURNS:
2435 * Status of operation
2436 */
2437 {
2438 ULONG BytesRead;
2439 ULONG Status;
2440 LONG Size;
2441 bool Found;
2442
2443 Found = false;
2444
2445 Status = ReadBlock(String, MaxLength, &BytesRead);
2446 if (Status != CAB_STATUS_SUCCESS)
2447 {
2448 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2449 return CAB_STATUS_INVALID_CAB;
2450 }
2451
2452 // Find the terminating NULL character
2453 for (Size = 0; Size < MaxLength; Size++)
2454 {
2455 if (String[Size] == '\0')
2456 {
2457 Found = true;
2458 break;
2459 }
2460 }
2461
2462 if (!Found)
2463 {
2464 DPRINT(MIN_TRACE, ("Filename in the cabinet file is too long.\n"));
2465 return CAB_STATUS_INVALID_CAB;
2466 }
2467
2468 // Compute the offset of the next CFFILE.
2469 // 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.
2470 // + 1 to skip the terminating NULL character as well.
2471 Size = -(MaxLength - Size) + 1;
2472
2473 #if defined(WIN32)
2474 if( SetFilePointer(FileHandle,
2475 (LONG)Size,
2476 NULL,
2477 FILE_CURRENT) == INVALID_SET_FILE_POINTER )
2478 {
2479 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2480 return CAB_STATUS_INVALID_CAB;
2481 }
2482 #else
2483 if (fseek(FileHandle, (off_t)Size, SEEK_CUR) != 0)
2484 {
2485 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2486 return CAB_STATUS_INVALID_CAB;
2487 }
2488 #endif
2489 return CAB_STATUS_SUCCESS;
2490 }
2491
2492
2493 ULONG CCabinet::ReadFileTable()
2494 /*
2495 * FUNCTION: Reads the file table from the cabinet file
2496 * RETURNS:
2497 * Status of operation
2498 */
2499 {
2500 ULONG i;
2501 ULONG Status;
2502 ULONG BytesRead;
2503 PCFFILE_NODE File;
2504
2505 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n",
2506 (UINT)CABHeader.FileTableOffset));
2507
2508 /* Seek to file table */
2509 #if defined(WIN32)
2510 if( SetFilePointer(FileHandle,
2511 CABHeader.FileTableOffset,
2512 NULL,
2513 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2514 {
2515 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2516 return CAB_STATUS_INVALID_CAB;
2517 }
2518 #else
2519 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0)
2520 {
2521 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2522 return CAB_STATUS_INVALID_CAB;
2523 }
2524 #endif
2525
2526 for (i = 0; i < CABHeader.FileCount; i++)
2527 {
2528 File = NewFileNode();
2529 if (!File)
2530 {
2531 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2532 return CAB_STATUS_NOMEMORY;
2533 }
2534
2535 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
2536 &BytesRead)) != CAB_STATUS_SUCCESS)
2537 {
2538 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2539 return CAB_STATUS_INVALID_CAB;
2540 }
2541
2542 File->FileName = (char*)AllocateMemory(MAX_PATH);
2543 if (!File->FileName)
2544 {
2545 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2546 return CAB_STATUS_NOMEMORY;
2547 }
2548
2549 /* Read file name */
2550 Status = ReadString(File->FileName, MAX_PATH);
2551 if (Status != CAB_STATUS_SUCCESS)
2552 return Status;
2553
2554 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2555 File->FileName,
2556 (UINT)File->File.FileOffset,
2557 (UINT)File->File.FileSize,
2558 File->File.FileControlID));
2559
2560 }
2561 return CAB_STATUS_SUCCESS;
2562 }
2563
2564
2565 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode)
2566 /*
2567 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2568 * ARGUMENTS:
2569 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2570 * RETURNS:
2571 * Status of operation
2572 */
2573 {
2574 ULONG AbsoluteOffset;
2575 ULONG UncompOffset;
2576 PCFDATA_NODE Node;
2577 ULONG BytesRead;
2578 ULONG Status;
2579 ULONG i;
2580
2581 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2582 (UINT)FolderNode->Index, (UINT)FolderNode->Folder.DataOffset));
2583
2584 AbsoluteOffset = FolderNode->Folder.DataOffset;
2585 UncompOffset = FolderNode->UncompOffset;
2586
2587 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++)
2588 {
2589 Node = NewDataNode(FolderNode);
2590 if (!Node)
2591 {
2592 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2593 return CAB_STATUS_NOMEMORY;
2594 }
2595
2596 /* Seek to data block */
2597 #if defined(WIN32)
2598 if( SetFilePointer(FileHandle,
2599 AbsoluteOffset,
2600 NULL,
2601 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2602 {
2603 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2604 return CAB_STATUS_INVALID_CAB;
2605 }
2606 #else
2607 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0)
2608 {
2609 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2610 return CAB_STATUS_INVALID_CAB;
2611 }
2612 #endif
2613
2614 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
2615 &BytesRead)) != CAB_STATUS_SUCCESS)
2616 {
2617 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2618 return CAB_STATUS_INVALID_CAB;
2619 }
2620
2621 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2622 (UINT)AbsoluteOffset,
2623 (UINT)UncompOffset,
2624 (UINT)Node->Data.Checksum,
2625 Node->Data.CompSize,
2626 Node->Data.UncompSize));
2627
2628 Node->AbsoluteOffset = AbsoluteOffset;
2629 Node->UncompOffset = UncompOffset;
2630
2631 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
2632 UncompOffset += Node->Data.UncompSize;
2633 }
2634
2635 FolderUncompSize = UncompOffset;
2636
2637 return CAB_STATUS_SUCCESS;
2638 }
2639
2640
2641 PCFFOLDER_NODE CCabinet::NewFolderNode()
2642 /*
2643 * FUNCTION: Creates a new folder node
2644 * RETURNS:
2645 * Pointer to node if there was enough free memory available, otherwise NULL
2646 */
2647 {
2648 PCFFOLDER_NODE Node;
2649
2650 Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE));
2651 if (!Node)
2652 return NULL;
2653
2654 memset(Node, 0, sizeof(CFFOLDER_NODE));
2655
2656 Node->Folder.CompressionType = CAB_COMP_NONE;
2657
2658 Node->Prev = FolderListTail;
2659
2660 if (FolderListTail != NULL)
2661 FolderListTail->Next = Node;
2662 else
2663 FolderListHead = Node;
2664
2665 FolderListTail = Node;
2666
2667 return Node;
2668 }
2669
2670
2671 PCFFILE_NODE CCabinet::NewFileNode()
2672 /*
2673 * FUNCTION: Creates a new file node
2674 * ARGUMENTS:
2675 * FolderNode = Pointer to folder node to bind file to
2676 * RETURNS:
2677 * Pointer to node if there was enough free memory available, otherwise NULL
2678 */
2679 {
2680 PCFFILE_NODE Node;
2681
2682 Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE));
2683 if (!Node)
2684 return NULL;
2685
2686 memset(Node, 0, sizeof(CFFILE_NODE));
2687
2688 Node->Prev = FileListTail;
2689
2690 if (FileListTail != NULL)
2691 FileListTail->Next = Node;
2692 else
2693 FileListHead = Node;
2694
2695 FileListTail = Node;
2696
2697 return Node;
2698 }
2699
2700
2701 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode)
2702 /*
2703 * FUNCTION: Creates a new data block node
2704 * ARGUMENTS:
2705 * FolderNode = Pointer to folder node to bind data block to
2706 * RETURNS:
2707 * Pointer to node if there was enough free memory available, otherwise NULL
2708 */
2709 {
2710 PCFDATA_NODE Node;
2711
2712 Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE));
2713 if (!Node)
2714 return NULL;
2715
2716 memset(Node, 0, sizeof(CFDATA_NODE));
2717
2718 Node->Prev = FolderNode->DataListTail;
2719
2720 if (FolderNode->DataListTail != NULL)
2721 FolderNode->DataListTail->Next = Node;
2722 else
2723 FolderNode->DataListHead = Node;
2724
2725 FolderNode->DataListTail = Node;
2726
2727 return Node;
2728 }
2729
2730
2731 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode)
2732 /*
2733 * FUNCTION: Destroys data block nodes bound to a folder node
2734 * ARGUMENTS:
2735 * FolderNode = Pointer to folder node
2736 */
2737 {
2738 PCFDATA_NODE PrevNode;
2739 PCFDATA_NODE NextNode;
2740
2741 NextNode = FolderNode->DataListHead;
2742 while (NextNode != NULL)
2743 {
2744 PrevNode = NextNode->Next;
2745 FreeMemory(NextNode);
2746 NextNode = PrevNode;
2747 }
2748 FolderNode->DataListHead = NULL;
2749 FolderNode->DataListTail = NULL;
2750 }
2751
2752
2753 void CCabinet::DestroyFileNodes()
2754 /*
2755 * FUNCTION: Destroys file nodes
2756 */
2757 {
2758 PCFFILE_NODE PrevNode;
2759 PCFFILE_NODE NextNode;
2760
2761 NextNode = FileListHead;
2762 while (NextNode != NULL)
2763 {
2764 PrevNode = NextNode->Next;
2765 if (NextNode->FileName)
2766 FreeMemory(NextNode->FileName);
2767 FreeMemory(NextNode);
2768 NextNode = PrevNode;
2769 }
2770 FileListHead = NULL;
2771 FileListTail = NULL;
2772 }
2773
2774
2775 void CCabinet::DestroyDeletedFileNodes()
2776 /*
2777 * FUNCTION: Destroys file nodes that are marked for deletion
2778 */
2779 {
2780 PCFFILE_NODE CurNode;
2781 PCFFILE_NODE NextNode;
2782
2783 CurNode = FileListHead;
2784 while (CurNode != NULL)
2785 {
2786 NextNode = CurNode->Next;
2787
2788 if (CurNode->Delete)
2789 {
2790 if (CurNode->Prev != NULL)
2791 CurNode->Prev->Next = CurNode->Next;
2792 else
2793 {
2794 FileListHead = CurNode->Next;
2795 if (FileListHead)
2796 FileListHead->Prev = NULL;
2797 }
2798
2799 if (CurNode->Next != NULL)
2800 CurNode->Next->Prev = CurNode->Prev;
2801 else
2802 {
2803 FileListTail = CurNode->Prev;
2804 if (FileListTail)
2805 FileListTail->Next = NULL;
2806 }
2807
2808 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName));
2809
2810 TotalFileSize -= (sizeof(CFFILE) + (ULONG)strlen(GetFileName(CurNode->FileName)) + 1);
2811
2812 if (CurNode->FileName)
2813 FreeMemory(CurNode->FileName);
2814 FreeMemory(CurNode);
2815 }
2816 CurNode = NextNode;
2817 }
2818 }
2819
2820
2821 void CCabinet::DestroyFolderNodes()
2822 /*
2823 * FUNCTION: Destroys folder nodes
2824 */
2825 {
2826 PCFFOLDER_NODE PrevNode;
2827 PCFFOLDER_NODE NextNode;
2828
2829 NextNode = FolderListHead;
2830 while (NextNode != NULL)
2831 {
2832 PrevNode = NextNode->Next;
2833 DestroyDataNodes(NextNode);
2834 FreeMemory(NextNode);
2835 NextNode = PrevNode;
2836 }
2837 FolderListHead = NULL;
2838 FolderListTail = NULL;
2839 }
2840
2841
2842 void CCabinet::DestroyDeletedFolderNodes()
2843 /*
2844 * FUNCTION: Destroys folder nodes that are marked for deletion
2845 */
2846 {
2847 PCFFOLDER_NODE CurNode;
2848 PCFFOLDER_NODE NextNode;
2849
2850 CurNode = FolderListHead;
2851 while (CurNode != NULL)
2852 {
2853 NextNode = CurNode->Next;
2854
2855 if (CurNode->Delete)
2856 {
2857 if (CurNode->Prev != NULL)
2858 CurNode->Prev->Next = CurNode->Next;
2859 else
2860 {
2861 FolderListHead = CurNode->Next;
2862 if (FolderListHead)
2863 FolderListHead->Prev = NULL;
2864 }
2865
2866 if (CurNode->Next != NULL)
2867 CurNode->Next->Prev = CurNode->Prev;
2868 else
2869 {
2870 FolderListTail = CurNode->Prev;
2871 if (FolderListTail)
2872 FolderListTail->Next = NULL;
2873 }
2874
2875 DestroyDataNodes(CurNode);
2876 FreeMemory(CurNode);
2877
2878 TotalFolderSize -= sizeof(CFFOLDER);
2879 }
2880 CurNode = NextNode;
2881 }
2882 }
2883
2884
2885 ULONG CCabinet::ComputeChecksum(void* Buffer,
2886 ULONG Size,
2887 ULONG Seed)
2888 /*
2889 * FUNCTION: Computes checksum for data block
2890 * ARGUMENTS:
2891 * Buffer = Pointer to data buffer
2892 * Size = Length of data buffer
2893 * Seed = Previously computed checksum
2894 * RETURNS:
2895 * Checksum of buffer
2896 */
2897 {
2898 int UlongCount; // Number of ULONGs in block
2899 ULONG Checksum; // Checksum accumulator
2900 unsigned char* pb;
2901 ULONG ul;
2902
2903 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2904 won't accept checksums computed by this routine */
2905
2906 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer, (UINT)Size));
2907
2908 UlongCount = Size / 4; // Number of ULONGs
2909 Checksum = Seed; // Init checksum
2910 pb = (unsigned char*)Buffer; // Start at front of data block
2911
2912 /* Checksum integral multiple of ULONGs */
2913 while (UlongCount-- > 0)
2914 {
2915 /* NOTE: Build ULONG in big/little-endian independent manner */
2916 ul = *pb++; // Get low-order byte
2917 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2918 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
2919 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
2920
2921 Checksum ^= ul; // Update checksum
2922 }
2923
2924 /* Checksum remainder bytes */
2925 ul = 0;
2926 switch (Size % 4)
2927 {
2928 case 3:
2929 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
2930 case 2:
2931 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2932 case 1:
2933 ul |= *pb++; // Get low-order byte
2934 default:
2935 break;
2936 }
2937 Checksum ^= ul; // Update checksum
2938
2939 /* Return computed checksum */
2940 return Checksum;
2941 }
2942
2943
2944 ULONG CCabinet::ReadBlock(void* Buffer,
2945 ULONG Size,
2946 PULONG BytesRead)
2947 /*
2948 * FUNCTION: Read a block of data from file
2949 * ARGUMENTS:
2950 * Buffer = Pointer to data buffer
2951 * Size = Length of data buffer
2952 * BytesRead = Pointer to ULONG that on return will contain
2953 * number of bytes read
2954 * RETURNS:
2955 * Status of operation
2956 */
2957 {
2958 if (!ReadFileData(FileHandle, Buffer, Size, BytesRead))
2959 return CAB_STATUS_INVALID_CAB;
2960 return CAB_STATUS_SUCCESS;
2961 }
2962
2963 bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern)
2964 /*
2965 * FUNCTION: Matches a wildcard character pattern against a file
2966 * ARGUMENTS:
2967 * FileName = The file name to check
2968 * Pattern = The pattern
2969 * RETURNS:
2970 * Whether the pattern matches the file
2971 *
2972 * COPYRIGHT:
2973 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
2974 * Adapted from code written by Ingo Wilken.
2975 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
2976 */
2977 {
2978 char* retryPattern = NULL;
2979 char* retryFileName = NULL;
2980 char ch;
2981
2982 while (*FileName || *Pattern)
2983 {
2984 ch = *Pattern++;
2985
2986 switch (ch)
2987 {
2988 case '*':
2989 retryPattern = Pattern;
2990 retryFileName = FileName;
2991 break;
2992
2993 case '?':
2994 if (*FileName++ == '\0')
2995 return false;
2996
2997 break;
2998
2999 default:
3000 if (*FileName == ch)
3001 {
3002 if (*FileName)
3003 FileName++;
3004 break;
3005 }
3006
3007 if (*FileName)
3008 {
3009 Pattern = retryPattern;
3010 FileName = ++retryFileName;
3011 break;
3012 }
3013
3014 return false;
3015 }
3016
3017 if (!Pattern)
3018 return false;
3019 }
3020
3021 return true;
3022 }
3023
3024 #ifndef CAB_READ_ONLY
3025
3026 ULONG CCabinet::InitCabinetHeader()
3027 /*
3028 * FUNCTION: Initializes cabinet header and optional fields
3029 * RETURNS:
3030 * Status of operation
3031 */
3032 {
3033 ULONG TotalSize;
3034 ULONG Size;
3035
3036 CABHeader.FileTableOffset = 0; // Not known yet
3037 CABHeader.FolderCount = 0; // Not known yet
3038 CABHeader.FileCount = 0; // Not known yet
3039 CABHeader.Flags = 0; // Not known yet
3040
3041 CABHeader.CabinetNumber = (USHORT)CurrentDiskNumber;
3042
3043 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev)))
3044 {
3045 CABHeader.Flags |= CAB_FLAG_HASPREV;
3046 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev))
3047 strcpy(CabinetPrev, "");
3048 }
3049
3050 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext))
3051 {
3052 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3053 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext))
3054 strcpy(DiskNext, "");
3055 }
3056
3057 TotalSize = 0;
3058
3059 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3060 {
3061
3062 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3063
3064 /* Calculate size of name of previous cabinet */
3065 TotalSize += (ULONG)strlen(CabinetPrev) + 1;
3066
3067 /* Calculate size of label of previous disk */
3068 TotalSize += (ULONG)strlen(DiskPrev) + 1;
3069 }
3070
3071 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3072 {
3073
3074 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3075
3076 /* Calculate size of name of next cabinet */
3077 Size = (ULONG)strlen(CabinetNext) + 1;
3078 TotalSize += Size;
3079 NextFieldsSize = Size;
3080
3081 /* Calculate size of label of next disk */
3082 Size = (ULONG)strlen(DiskNext) + 1;
3083 TotalSize += Size;
3084 NextFieldsSize += Size;
3085 }
3086 else
3087 NextFieldsSize = 0;
3088
3089 /* Add cabinet reserved area size if present */
3090 if (CabinetReservedFileSize > 0)
3091 {
3092 CABHeader.Flags |= CAB_FLAG_RESERVE;
3093 TotalSize += CabinetReservedFileSize;
3094 TotalSize += sizeof(ULONG); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3095 }
3096
3097 DiskSize += TotalSize;
3098
3099 TotalHeaderSize = sizeof(CFHEADER) + TotalSize;
3100
3101 return CAB_STATUS_SUCCESS;
3102 }
3103
3104
3105 ULONG CCabinet::WriteCabinetHeader(bool MoreDisks)
3106 /*
3107 * FUNCTION: Writes the cabinet header and optional fields
3108 * ARGUMENTS:
3109 * MoreDisks = true if next cabinet name should be included
3110 * RETURNS:
3111 * Status of operation
3112 */
3113 {
3114 PCFFOLDER_NODE FolderNode;
3115 PCFFILE_NODE FileNode;
3116 ULONG BytesWritten;
3117 ULONG Size;
3118
3119 if (MoreDisks)
3120 {
3121 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3122 Size = TotalHeaderSize;
3123 }
3124 else
3125 {
3126 CABHeader.Flags &= ~CAB_FLAG_HASNEXT;
3127 DiskSize -= NextFieldsSize;
3128 Size = TotalHeaderSize - NextFieldsSize;
3129 }
3130
3131 /* Set absolute folder offsets */
3132 BytesWritten = Size + TotalFolderSize + TotalFileSize;
3133 CABHeader.FolderCount = 0;
3134 FolderNode = FolderListHead;
3135 while (FolderNode != NULL)
3136 {
3137 FolderNode->Folder.DataOffset = BytesWritten;
3138
3139 BytesWritten += FolderNode->TotalFolderSize;
3140
3141 CABHeader.FolderCount++;
3142
3143 FolderNode = FolderNode->Next;
3144 }
3145
3146 /* Set absolute offset of file table */
3147 CABHeader.FileTableOffset = Size + TotalFolderSize;
3148
3149 /* Count number of files to be committed */
3150 CABHeader.FileCount = 0;
3151 FileNode = FileListHead;
3152 while (FileNode != NULL)
3153 {
3154 if (FileNode->Commit)
3155 CABHeader.FileCount++;
3156 FileNode = FileNode->Next;
3157 }
3158
3159 CABHeader.CabinetSize = DiskSize;
3160
3161 /* Write header */
3162 #if defined(WIN32)
3163 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), (LPDWORD)&BytesWritten, NULL))
3164 {
3165 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3166 return CAB_STATUS_CANNOT_WRITE;
3167 }
3168 #else
3169 BytesWritten = sizeof(CFHEADER);
3170 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1)
3171 {
3172 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3173 return CAB_STATUS_CANNOT_WRITE;
3174 }
3175 #endif
3176
3177 /* Write per-cabinet reserved area if present */
3178 if (CABHeader.Flags & CAB_FLAG_RESERVE)
3179 {
3180 ULONG ReservedSize;
3181
3182 ReservedSize = CabinetReservedFileSize & 0xffff;
3183 ReservedSize |= (0 << 16); /* Folder reserved area size */
3184 ReservedSize |= (0 << 24); /* Folder reserved area size */
3185 #if defined(WIN32)
3186 if (!WriteFile(FileHandle, &ReservedSize, sizeof(ULONG), (LPDWORD)&BytesWritten, NULL))
3187 {
3188 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3189 return CAB_STATUS_CANNOT_WRITE;
3190 }
3191 #else
3192 BytesWritten = sizeof(ULONG);
3193 if (fwrite(&ReservedSize, sizeof(ULONG), 1, FileHandle) < 1)
3194 {
3195 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3196 return CAB_STATUS_CANNOT_WRITE;
3197 }
3198 #endif
3199
3200 #if defined(WIN32)
3201 if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, (LPDWORD)&BytesWritten, NULL))
3202 {
3203 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3204 return CAB_STATUS_CANNOT_WRITE;
3205 }
3206 #else
3207 BytesWritten = CabinetReservedFileSize;
3208 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1)
3209 {
3210 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3211 return CAB_STATUS_CANNOT_WRITE;
3212 }
3213 #endif
3214 }
3215
3216 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3217 {
3218 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3219
3220 /* Write name of previous cabinet */
3221 Size = (ULONG)strlen(CabinetPrev) + 1;
3222 #if defined(WIN32)
3223 if (!WriteFile(FileHandle, CabinetPrev, Size, (LPDWORD)&BytesWritten, NULL))
3224 {
3225 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3226 return CAB_STATUS_CANNOT_WRITE;
3227 }
3228 #else
3229 BytesWritten = Size;
3230 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1)
3231 {
3232 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3233 return CAB_STATUS_CANNOT_WRITE;
3234 }
3235 #endif
3236
3237 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev));
3238
3239 /* Write label of previous disk */
3240 Size = (ULONG)strlen(DiskPrev) + 1;
3241 #if defined(WIN32)
3242 if (!WriteFile(FileHandle, DiskPrev, Size, (LPDWORD)&BytesWritten, NULL))
3243 {
3244 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3245 return CAB_STATUS_CANNOT_WRITE;
3246 }
3247 #else
3248 BytesWritten = Size;
3249 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1)
3250 {
3251 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3252 return CAB_STATUS_CANNOT_WRITE;
3253 }
3254 #endif
3255 }
3256
3257 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3258 {
3259 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3260
3261 /* Write name of next cabinet */
3262 Size = (ULONG)strlen(CabinetNext) + 1;
3263 #if defined(WIN32)
3264 if (!WriteFile(FileHandle, CabinetNext, Size, (LPDWORD)&BytesWritten, NULL))
3265 {
3266 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3267 return CAB_STATUS_CANNOT_WRITE;
3268 }
3269 #else
3270 BytesWritten = Size;
3271 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1)
3272 {
3273 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3274 return CAB_STATUS_CANNOT_WRITE;
3275 }
3276 #endif
3277
3278 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext));
3279
3280 /* Write label of next disk */
3281 Size = (ULONG)strlen(DiskNext) + 1;
3282 #if defined(WIN32)
3283 if (!WriteFile(FileHandle, DiskNext, Size, (LPDWORD)&BytesWritten, NULL))
3284 {
3285 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3286 return CAB_STATUS_CANNOT_WRITE;
3287 }
3288 #else
3289 BytesWritten = Size;
3290 if (fwrite(DiskNext, Size, 1, FileHandle) < 1)
3291 {
3292 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3293 return CAB_STATUS_CANNOT_WRITE;
3294 }
3295 #endif
3296 }
3297
3298 return CAB_STATUS_SUCCESS;
3299 }
3300
3301
3302 ULONG CCabinet::WriteFolderEntries()
3303 /*
3304 * FUNCTION: Writes folder entries
3305 * RETURNS:
3306 * Status of operation
3307 */
3308 {
3309 PCFFOLDER_NODE FolderNode;
3310 ULONG BytesWritten;
3311
3312 DPRINT(MAX_TRACE, ("Writing folder table.\n"));
3313
3314 FolderNode = FolderListHead;
3315 while (FolderNode != NULL)
3316 {
3317 if (FolderNode->Commit)
3318 {
3319 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3320 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, (UINT)FolderNode->Folder.DataOffset));
3321
3322 #if defined(WIN32)
3323 if (!WriteFile(FileHandle,
3324 &FolderNode->Folder,
3325 sizeof(CFFOLDER),
3326 (LPDWORD)&BytesWritten,
3327 NULL))
3328 {
3329 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3330 return CAB_STATUS_CANNOT_WRITE;
3331 }
3332 #else
3333 BytesWritten = sizeof(CFFOLDER);
3334 if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1)
3335 {
3336 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3337 return CAB_STATUS_CANNOT_WRITE;
3338 }
3339 #endif
3340 }
3341 FolderNode = FolderNode->Next;
3342 }
3343
3344 return CAB_STATUS_SUCCESS;
3345 }
3346
3347
3348 ULONG CCabinet::WriteFileEntries()
3349 /*
3350 * FUNCTION: Writes file entries for all files
3351 * RETURNS:
3352 * Status of operation
3353 */
3354 {
3355 PCFFILE_NODE File;
3356 ULONG BytesWritten;
3357 bool SetCont = false;
3358
3359 DPRINT(MAX_TRACE, ("Writing file table.\n"));
3360
3361 File = FileListHead;
3362 while (File != NULL)
3363 {
3364 if (File->Commit)
3365 {
3366 /* Remove any continued files that ends in this disk */
3367 if (File->File.FileControlID == CAB_FILE_CONTINUED)
3368 File->Delete = true;
3369
3370 /* The file could end in the last (split) block and should therefore
3371 appear in the next disk too */
3372
3373 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) &&
3374 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit))
3375 {
3376 File->File.FileControlID = CAB_FILE_SPLIT;
3377 File->Delete = false;
3378 SetCont = true;
3379 }
3380
3381 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3382 File->File.FileControlID, (UINT)File->File.FileOffset, (UINT)File->File.FileSize, File->FileName));
3383
3384 #if defined(WIN32)
3385 if (!WriteFile(FileHandle,
3386 &File->File,
3387 sizeof(CFFILE),
3388 (LPDWORD)&BytesWritten,
3389 NULL))
3390 return CAB_STATUS_CANNOT_WRITE;
3391 #else
3392 BytesWritten = sizeof(CFFILE);
3393 if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1)
3394 {
3395 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3396 return CAB_STATUS_CANNOT_WRITE;
3397 }
3398 #endif
3399
3400 #if defined(WIN32)
3401 if (!WriteFile(FileHandle,
3402 GetFileName(File->FileName),
3403 (DWORD)strlen(GetFileName(File->FileName)) + 1,
3404 (LPDWORD)&BytesWritten,
3405 NULL))
3406 return CAB_STATUS_CANNOT_WRITE;
3407 #else
3408 BytesWritten = strlen(GetFileName(File->FileName)) + 1;
3409 if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1)
3410 {
3411 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3412 return CAB_STATUS_CANNOT_WRITE;
3413 }
3414 #endif
3415
3416 if (SetCont)
3417 {
3418 File->File.FileControlID = CAB_FILE_CONTINUED;
3419 SetCont = false;
3420 }
3421 }
3422
3423 File = File->Next;
3424 }
3425 return CAB_STATUS_SUCCESS;
3426 }
3427
3428
3429 ULONG CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode)
3430 /*
3431 * FUNCTION: Writes data blocks to the cabinet
3432 * ARGUMENTS:
3433 * FolderNode = Pointer to folder node containing the data blocks
3434 * RETURNS:
3435 * Status of operation
3436 */
3437 {
3438 PCFDATA_NODE DataNode;
3439 ULONG BytesWritten;
3440 ULONG BytesRead;
3441 ULONG Status;
3442
3443 DataNode = FolderNode->DataListHead;
3444 if (DataNode != NULL)
3445 Status = ScratchFile->Seek(DataNode->ScratchFilePosition);
3446
3447 while (DataNode != NULL)
3448 {
3449 DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3450 (UINT)DataNode->ScratchFilePosition,
3451 DataNode->Data.CompSize,
3452 DataNode->Data.UncompSize));
3453
3454 /* InputBuffer is free for us to use here, so we use it and avoid a
3455 memory allocation. OutputBuffer can't be used here because it may
3456 still contain valid data (if a data block spans two or more disks) */
3457 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead);
3458 if (Status != CAB_STATUS_SUCCESS)
3459 {
3460 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%u).\n", (UINT)Status));
3461 return Status;
3462 }
3463
3464 #if defined(WIN32)
3465 if (!WriteFile(FileHandle, &DataNode->Data,
3466 sizeof(CFDATA), (LPDWORD)&BytesWritten, NULL))
3467 {
3468 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3469 return CAB_STATUS_CANNOT_WRITE;
3470 }
3471 #else
3472 BytesWritten = sizeof(CFDATA);
3473 if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1)
3474 {
3475 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3476 return CAB_STATUS_CANNOT_WRITE;
3477 }
3478 #endif
3479
3480 #if defined(WIN32)
3481 if (!WriteFile(FileHandle, InputBuffer,
3482 DataNode->Data.CompSize, (LPDWORD)&BytesWritten, NULL))
3483 {
3484 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3485 return CAB_STATUS_CANNOT_WRITE;
3486 }
3487 #else
3488 BytesWritten = DataNode->Data.CompSize;
3489 if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1)
3490 {
3491 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3492 return CAB_STATUS_CANNOT_WRITE;
3493 }
3494 #endif
3495
3496 DataNode = DataNode->Next;
3497 }
3498 return CAB_STATUS_SUCCESS;
3499 }
3500
3501
3502 ULONG CCabinet::WriteDataBlock()
3503 /*
3504 * FUNCTION: Writes the current data block to the scratch file
3505 * RETURNS:
3506 * Status of operation
3507 */
3508 {
3509 ULONG Status;
3510 ULONG BytesWritten;
3511 PCFDATA_NODE DataNode;
3512
3513 if (!BlockIsSplit)
3514 {
3515 Status = Codec->Compress(OutputBuffer,
3516 InputBuffer,
3517 CurrentIBufferSize,
3518 &TotalCompSize);
3519
3520 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3521 (UINT)CurrentIBufferSize, (UINT)TotalCompSize));
3522
3523 CurrentOBuffer = OutputBuffer;
3524 CurrentOBufferSize = TotalCompSize;
3525 }
3526
3527 DataNode = NewDataNode(CurrentFolderNode);
3528 if (!DataNode)
3529 {
3530 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
3531 return CAB_STATUS_NOMEMORY;
3532 }
3533
3534 DiskSize += sizeof(CFDATA);
3535
3536 if (MaxDiskSize > 0)
3537 /* Disk size is limited */
3538 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize);
3539 else
3540 BlockIsSplit = false;
3541
3542 if (BlockIsSplit)
3543 {
3544 DataNode->Data.CompSize = (USHORT)(MaxDiskSize - DiskSize);
3545 DataNode->Data.UncompSize = 0;
3546 CreateNewDisk = true;
3547 }
3548 else
3549 {
3550 DataNode->Data.CompSize = (USHORT)CurrentOBufferSize;
3551 DataNode->Data.UncompSize = (USHORT)CurrentIBufferSize;
3552 }
3553
3554 DataNode->Data.Checksum = 0;
3555 DataNode->ScratchFilePosition = ScratchFile->Position();
3556
3557 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3558 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3559
3560 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3561 (UINT)DataNode->Data.Checksum,
3562 DataNode->Data.CompSize,
3563 DataNode->Data.UncompSize));
3564
3565 Status = ScratchFile->WriteBlock(&DataNode->Data,
3566 CurrentOBuffer, &BytesWritten);
3567 if (Status != CAB_STATUS_SUCCESS)
3568 return Status;
3569
3570 DiskSize += BytesWritten;
3571
3572 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA));
3573 CurrentFolderNode->Folder.DataBlockCount++;
3574
3575 CurrentOBuffer = (unsigned char*)CurrentOBuffer + DataNode->Data.CompSize;
3576 CurrentOBufferSize -= DataNode->Data.CompSize;
3577
3578 LastBlockStart += DataNode->Data.UncompSize;
3579
3580 if (!BlockIsSplit)
3581 {
3582 CurrentIBufferSize = 0;
3583 CurrentIBuffer = InputBuffer;
3584 }
3585
3586 return CAB_STATUS_SUCCESS;
3587 }
3588
3589 #if !defined(WIN32)
3590
3591 void CCabinet::ConvertDateAndTime(time_t* Time,
3592 PUSHORT DosDate,
3593 PUSHORT DosTime)
3594 /*
3595 * FUNCTION: Returns file times of a file
3596 * ARGUMENTS:
3597 * FileHandle = File handle of file to get file times from
3598 * File = Pointer to CFFILE node for file
3599 * RETURNS:
3600 * Status of operation
3601 */
3602 {
3603 struct tm *timedef;
3604
3605 timedef = localtime(Time);
3606
3607 DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3608 timedef->tm_mday, timedef->tm_mon, timedef->tm_year,
3609 timedef->tm_sec, timedef->tm_min, timedef->tm_hour));
3610
3611 *DosDate = ((timedef->tm_mday + 1) << 0)
3612 | ((timedef->tm_mon + 1) << 5)
3613 | (((timedef->tm_year + 1900) - 1980) << 9);
3614
3615 *DosTime = (timedef->tm_sec << 0)
3616 | (timedef->tm_min << 5)
3617 | (timedef->tm_hour << 11);
3618 }
3619
3620 #endif // !WIN32
3621
3622
3623 ULONG CCabinet::GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File)
3624 /*
3625 * FUNCTION: Returns file times of a file
3626 * ARGUMENTS:
3627 * FileHandle = File handle of file to get file times from
3628 * File = Pointer to CFFILE node for file
3629 * RETURNS:
3630 * Status of operation
3631 */
3632 {
3633 #if defined(WIN32)
3634 FILETIME FileTime;
3635
3636 if (GetFileTime(FileHandle, NULL, NULL, &FileTime))
3637 FileTimeToDosDateTime(&FileTime,
3638 &File->File.FileDate,
3639 &File->File.FileTime);
3640 #else
3641 struct stat stbuf;
3642 char buf[MAX_PATH];
3643
3644 // Check for an absolute path
3645 if (IsSeparator(File->FileName[0]))
3646 strcpy(buf, File->FileName);
3647 else
3648 {
3649 getcwd(buf, sizeof(buf));
3650 strcat(buf, DIR_SEPARATOR_STRING);
3651 strcat(buf, File->FileName);
3652 }
3653
3654 if (stat(buf, &stbuf) == -1)
3655 return CAB_STATUS_CANNOT_READ;
3656
3657 ConvertDateAndTime(&stbuf.st_mtime, &File->File.FileDate, &File->File.FileTime);
3658 #endif
3659 return CAB_STATUS_SUCCESS;
3660 }
3661
3662
3663 ULONG CCabinet::GetAttributesOnFile(PCFFILE_NODE File)
3664 /*
3665 * FUNCTION: Returns attributes on a file
3666 * ARGUMENTS:
3667 * File = Pointer to CFFILE node for file
3668 * RETURNS:
3669 * Status of operation
3670 */
3671 {
3672 #if defined(WIN32)
3673 LONG Attributes;
3674
3675 Attributes = GetFileAttributes(File->FileName);
3676 if (Attributes == -1)
3677 return CAB_STATUS_CANNOT_READ;
3678
3679 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3680 // The IDs for these attributes are the same in the CAB file and under Windows
3681 // If the file has any other attributes, strip them off by the logical AND.
3682 File->File.Attributes = (USHORT)(Attributes & 0x37);
3683 #else
3684 struct stat stbuf;
3685 char buf[MAX_PATH];
3686
3687 // Check for an absolute path
3688 if (IsSeparator(File->FileName[0]))
3689 strcpy(buf, File->FileName);
3690 else
3691 {
3692 getcwd(buf, sizeof(buf));
3693 strcat(buf, DIR_SEPARATOR_STRING);
3694 strcat(buf, File->FileName);
3695 }
3696
3697 if (stat(buf, &stbuf) == -1)
3698 return CAB_STATUS_CANNOT_READ;
3699
3700 #if 0
3701 File->File.Attributes |= CAB_ATTRIB_READONLY;
3702 File->File.Attributes |= CAB_ATTRIB_HIDDEN;
3703 File->File.Attributes |= CAB_ATTRIB_SYSTEM;
3704 #endif
3705
3706 if (stbuf.st_mode & S_IFDIR)
3707 File->File.Attributes |= CAB_ATTRIB_DIRECTORY;
3708
3709 File->File.Attributes |= CAB_ATTRIB_ARCHIVE;
3710
3711 #endif
3712 return CAB_STATUS_SUCCESS;
3713 }
3714
3715
3716 ULONG CCabinet::SetAttributesOnFile(char* FileName, USHORT FileAttributes)
3717 /*
3718 * FUNCTION: Sets attributes on a file
3719 * ARGUMENTS:
3720 * FileName = File name with path
3721 * FileAttributes = Attributes of that file
3722 * RETURNS:
3723 * Status of operation
3724 */
3725 {
3726 #if defined(WIN32)
3727 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3728 // The IDs for these attributes are the same in the CAB file and under Windows
3729 // If the file has any other attributes, strip them off by the logical AND.
3730 SetFileAttributes(FileName, (DWORD)(FileAttributes & 0x37));
3731
3732 return CAB_STATUS_SUCCESS;
3733 #else
3734 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3735 return CAB_STATUS_SUCCESS;
3736 #endif
3737 }
3738
3739 #endif /* CAB_READ_ONLY */
3740
3741 /* EOF */