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