Merge 13831:14550 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 char* NewFileName;
1759
1760 NewFileName = (char*)AllocateMemory(strlen(FileName) + 1);
1761 if (!NewFileName) {
1762 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1763 return CAB_STATUS_NOMEMORY;
1764 }
1765 strcpy(NewFileName, FileName);
1766 ConvertPath(NewFileName, false);
1767
1768 /* Try to open file */
1769 #if defined(WIN32)
1770 SrcFile = CreateFile(
1771 NewFileName, // Open this file
1772 GENERIC_READ, // Open for reading
1773 FILE_SHARE_READ, // Share for reading
1774 NULL, // No security
1775 OPEN_EXISTING, // File must exist
1776 FILE_ATTRIBUTE_NORMAL, // Normal file
1777 NULL); // No attribute template
1778 if (SrcFile == INVALID_HANDLE_VALUE) {
1779 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
1780 FreeMemory(NewFileName);
1781 return CAB_STATUS_CANNOT_OPEN;
1782 }
1783 #else /* !WIN32 */
1784 SrcFile = fopen(NewFileName, "rb");
1785 if (SrcFile == NULL) {
1786 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
1787 FreeMemory(NewFileName);
1788 return CAB_STATUS_CANNOT_OPEN;
1789 }
1790 #endif
1791
1792 FileNode = NewFileNode();
1793 if (!FileNode) {
1794 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
1795 FreeMemory(NewFileName);
1796 return CAB_STATUS_NOMEMORY;
1797 }
1798
1799 FileNode->FolderNode = CurrentFolderNode;
1800 FileNode->FileName = NewFileName;
1801
1802 /* FIXME: Check for and handle large files (>= 2GB) */
1803 FileNode->File.FileSize = GetSizeOfFile(SrcFile);
1804 if (FileNode->File.FileSize == (unsigned long)-1) {
1805 DPRINT(MIN_TRACE, ("Cannot read from file.\n"));
1806 FreeMemory(NewFileName);
1807 return CAB_STATUS_CANNOT_READ;
1808 }
1809
1810 GetFileTimes(SrcFile, FileNode);
1811
1812 GetAttributesOnFile(FileNode);
1813
1814 CloseFile(SrcFile);
1815
1816 return CAB_STATUS_SUCCESS;
1817 }
1818
1819
1820 void CCabinet::SetMaxDiskSize(unsigned long Size)
1821 /*
1822 * FUNCTION: Sets the maximum size of the current disk
1823 * ARGUMENTS:
1824 * Size = Maximum size of current disk (0 means no maximum size)
1825 */
1826 {
1827 MaxDiskSize = Size;
1828 }
1829
1830 #endif /* CAB_READ_ONLY */
1831
1832
1833 /* Default event handlers */
1834
1835 bool CCabinet::OnOverwrite(PCFFILE File,
1836 char* FileName)
1837 /*
1838 * FUNCTION: Called when extracting a file and it already exists
1839 * ARGUMENTS:
1840 * File = Pointer to CFFILE for file being extracted
1841 * FileName = Pointer to buffer with name of file (full path)
1842 * RETURNS
1843 * true if the file should be overwritten, false if not
1844 */
1845 {
1846 return false;
1847 }
1848
1849
1850 void CCabinet::OnExtract(PCFFILE File,
1851 char* FileName)
1852 /*
1853 * FUNCTION: Called just before extracting a file
1854 * ARGUMENTS:
1855 * File = Pointer to CFFILE for file being extracted
1856 * FileName = Pointer to buffer with name of file (full path)
1857 */
1858 {
1859 }
1860
1861
1862 void CCabinet::OnDiskChange(char* CabinetName,
1863 char* DiskLabel)
1864 /*
1865 * FUNCTION: Called when a new disk is to be processed
1866 * ARGUMENTS:
1867 * CabinetName = Pointer to buffer with name of cabinet
1868 * DiskLabel = Pointer to buffer with label of disk
1869 */
1870 {
1871 }
1872
1873
1874 #ifndef CAB_READ_ONLY
1875
1876 void CCabinet::OnAdd(PCFFILE File,
1877 char* FileName)
1878 /*
1879 * FUNCTION: Called just before adding a file to a cabinet
1880 * ARGUMENTS:
1881 * File = Pointer to CFFILE for file being added
1882 * FileName = Pointer to buffer with name of file (full path)
1883 */
1884 {
1885 }
1886
1887
1888 bool CCabinet::OnDiskLabel(unsigned long Number, char* Label)
1889 /*
1890 * FUNCTION: Called when a disk needs a label
1891 * ARGUMENTS:
1892 * Number = Cabinet number that needs a label
1893 * Label = Pointer to buffer to place label of disk
1894 * RETURNS:
1895 * true if a disk label was returned, false if not
1896 */
1897 {
1898 return false;
1899 }
1900
1901
1902 bool CCabinet::OnCabinetName(unsigned long Number, char* Name)
1903 /*
1904 * FUNCTION: Called when a cabinet needs a name
1905 * ARGUMENTS:
1906 * Number = Disk number that needs a name
1907 * Name = Pointer to buffer to place name of cabinet
1908 * RETURNS:
1909 * true if a cabinet name was returned, false if not
1910 */
1911 {
1912 return false;
1913 }
1914
1915 #endif /* CAB_READ_ONLY */
1916
1917 PCFFOLDER_NODE CCabinet::LocateFolderNode(unsigned long Index)
1918 /*
1919 * FUNCTION: Locates a folder node
1920 * ARGUMENTS:
1921 * Index = Folder index
1922 * RETURNS:
1923 * Pointer to folder node or NULL if the folder node was not found
1924 */
1925 {
1926 PCFFOLDER_NODE Node;
1927
1928 switch (Index) {
1929 case CAB_FILE_SPLIT:
1930 return FolderListTail;
1931
1932 case CAB_FILE_CONTINUED:
1933 case CAB_FILE_PREV_NEXT:
1934 return FolderListHead;
1935 }
1936
1937 Node = FolderListHead;
1938 while (Node != NULL) {
1939 if (Node->Index == Index)
1940 return Node;
1941 Node = Node->Next;
1942 }
1943 return NULL;
1944 }
1945
1946
1947 unsigned long CCabinet::GetAbsoluteOffset(PCFFILE_NODE File)
1948 /*
1949 * FUNCTION: Returns the absolute offset of a file
1950 * ARGUMENTS:
1951 * File = Pointer to CFFILE_NODE structure for file
1952 * RETURNS:
1953 * Status of operation
1954 */
1955 {
1956 PCFDATA_NODE Node;
1957
1958 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%d).\n",
1959 (char*)File->FileName,
1960 (unsigned int)File->File.FileOffset,
1961 (unsigned int)File->File.FileSize));
1962
1963 Node = CurrentFolderNode->DataListHead;
1964 while (Node != NULL) {
1965
1966 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%d).\n",
1967 (unsigned int)Node->UncompOffset,
1968 (unsigned int)Node->UncompOffset + Node->Data.UncompSize,
1969 (unsigned int)Node->Data.UncompSize));
1970
1971 /* Node->Data.UncompSize will be 0 if the block is split
1972 (ie. it is the last block in this cabinet) */
1973 if ((Node->Data.UncompSize == 0) ||
1974 ((File->File.FileOffset >= Node->UncompOffset) &&
1975 (File->File.FileOffset < Node->UncompOffset +
1976 Node->Data.UncompSize))) {
1977 File->DataBlock = Node;
1978 return CAB_STATUS_SUCCESS;
1979 }
1980
1981 Node = Node->Next;
1982 }
1983 return CAB_STATUS_INVALID_CAB;
1984 }
1985
1986
1987 unsigned long CCabinet::LocateFile(char* FileName,
1988 PCFFILE_NODE *File)
1989 /*
1990 * FUNCTION: Locates a file in the cabinet
1991 * ARGUMENTS:
1992 * FileName = Pointer to string with name of file to locate
1993 * File = Address of pointer to CFFILE_NODE structure to fill
1994 * RETURNS:
1995 * Status of operation
1996 * NOTES:
1997 * Current folder is set to the folder of the file
1998 */
1999 {
2000 PCFFILE_NODE Node;
2001 unsigned long Status;
2002
2003 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName));
2004
2005 Node = FileListHead;
2006 while (Node != NULL) {
2007 if (strcasecmp(FileName, Node->FileName) == 0) {
2008
2009 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
2010 if (!CurrentFolderNode) {
2011 DPRINT(MID_TRACE, ("Folder with index number (%d) not found.\n",
2012 (unsigned int)Node->File.FileControlID));
2013 return CAB_STATUS_INVALID_CAB;
2014 }
2015
2016 if (Node->DataBlock == NULL) {
2017 Status = GetAbsoluteOffset(Node);
2018 } else
2019 Status = CAB_STATUS_SUCCESS;
2020 *File = Node;
2021 return Status;
2022 }
2023 Node = Node->Next;
2024 }
2025 return CAB_STATUS_NOFILE;
2026 }
2027
2028
2029 unsigned long CCabinet::ReadString(char* String, unsigned long MaxLength)
2030 /*
2031 * FUNCTION: Reads a NULL-terminated string from the cabinet
2032 * ARGUMENTS:
2033 * String = Pointer to buffer to place string
2034 * MaxLength = Maximum length of string
2035 * RETURNS:
2036 * Status of operation
2037 */
2038 {
2039 unsigned long BytesRead;
2040 unsigned long Offset;
2041 unsigned long Status;
2042 unsigned long Size;
2043 bool Found;
2044
2045 Offset = 0;
2046 Found = false;
2047 do {
2048 Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset;
2049
2050 if (Size == 0) {
2051 DPRINT(MIN_TRACE, ("Too long a filename.\n"));
2052 return CAB_STATUS_INVALID_CAB;
2053 }
2054
2055 Status = ReadBlock(&String[Offset], Size, &BytesRead);
2056 if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size)) {
2057 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status));
2058 return CAB_STATUS_INVALID_CAB;
2059 }
2060
2061 for (Size = Offset; Size < Offset + BytesRead; Size++) {
2062 if (String[Size] == '\0') {
2063 Found = true;
2064 break;
2065 }
2066 }
2067
2068 Offset += BytesRead;
2069
2070 } while (!Found);
2071
2072 /* Back up some bytes */
2073 Size = (BytesRead - Size) - 1;
2074 #if defined(WIN32)
2075 SetLastError(NO_ERROR);
2076 (unsigned int)SetFilePointer(FileHandle,
2077 -(long)Size,
2078 NULL,
2079 FILE_CURRENT);
2080 if (GetLastError() != NO_ERROR) {
2081 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
2082 return CAB_STATUS_INVALID_CAB;
2083 }
2084 #else
2085 if (fseek(FileHandle, (off_t)(-(long)Size), SEEK_CUR) != 0) {
2086 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2087 return CAB_STATUS_INVALID_CAB;
2088 }
2089 #endif
2090 return CAB_STATUS_SUCCESS;
2091 }
2092
2093
2094 unsigned long CCabinet::ReadFileTable()
2095 /*
2096 * FUNCTION: Reads the file table from the cabinet file
2097 * RETURNS:
2098 * Status of operation
2099 */
2100 {
2101 unsigned long i;
2102 unsigned long Status;
2103 unsigned long BytesRead;
2104 PCFFILE_NODE File;
2105
2106 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%lX).\n",
2107 CABHeader.FileTableOffset));
2108
2109 /* Seek to file table */
2110 #if defined(WIN32)
2111 SetLastError(NO_ERROR);
2112 (unsigned int)SetFilePointer(FileHandle,
2113 CABHeader.FileTableOffset,
2114 NULL,
2115 FILE_BEGIN);
2116 if (GetLastError() != NO_ERROR) {
2117 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
2118 DPRINT(MIN_TRACE, ("Error: %lu\n", GetLastError()));
2119 return CAB_STATUS_INVALID_CAB;
2120 }
2121 #else
2122 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0) {
2123 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2124 return CAB_STATUS_INVALID_CAB;
2125 }
2126 #endif
2127
2128 for (i = 0; i < CABHeader.FileCount; i++) {
2129 File = NewFileNode();
2130 if (!File) {
2131 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2132 return CAB_STATUS_NOMEMORY;
2133 }
2134
2135 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
2136 &BytesRead)) != CAB_STATUS_SUCCESS) {
2137 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status));
2138 return CAB_STATUS_INVALID_CAB;
2139 }
2140
2141 File->FileName = (char*)AllocateMemory(MAX_PATH);
2142 if (!File->FileName) {
2143 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2144 return CAB_STATUS_NOMEMORY;
2145 }
2146
2147 /* Read file name */
2148 Status = ReadString(File->FileName, MAX_PATH);
2149 if (Status != CAB_STATUS_SUCCESS)
2150 return Status;
2151
2152 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%d bytes) ControlId (0x%X).\n",
2153 (char*)File->FileName,
2154 (unsigned int)File->File.FileOffset,
2155 (unsigned int)File->File.FileSize,
2156 (unsigned int)File->File.FileControlID));
2157
2158 }
2159 return CAB_STATUS_SUCCESS;
2160 }
2161
2162
2163 unsigned long CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode)
2164 /*
2165 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2166 * ARGUMENTS:
2167 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2168 * RETURNS:
2169 * Status of operation
2170 */
2171 {
2172 unsigned long AbsoluteOffset;
2173 unsigned long UncompOffset;
2174 PCFDATA_NODE Node;
2175 unsigned long BytesRead;
2176 unsigned long Status;
2177 unsigned long i;
2178
2179 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%lu) at absolute offset (0x%lX).\n",
2180 FolderNode->Index, FolderNode->Folder.DataOffset));
2181
2182 AbsoluteOffset = FolderNode->Folder.DataOffset;
2183 UncompOffset = FolderNode->UncompOffset;
2184
2185 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) {
2186 Node = NewDataNode(FolderNode);
2187 if (!Node) {
2188 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2189 return CAB_STATUS_NOMEMORY;
2190 }
2191
2192 /* Seek to data block */
2193 #if defined(WIN32)
2194 SetLastError(NO_ERROR);
2195 (unsigned int)SetFilePointer(FileHandle,
2196 AbsoluteOffset,
2197 NULL,
2198 FILE_BEGIN);
2199 if (GetLastError() != NO_ERROR) {
2200 DPRINT(MIN_TRACE, ("SetFilePointer() failed.\n"));
2201 return CAB_STATUS_INVALID_CAB;
2202 }
2203 #else
2204 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0) {
2205 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2206 return CAB_STATUS_INVALID_CAB;
2207 }
2208 #endif
2209
2210 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
2211 &BytesRead)) != CAB_STATUS_SUCCESS) {
2212 DPRINT(MIN_TRACE, ("Cannot read from file (%d).\n", (unsigned int)Status));
2213 return CAB_STATUS_INVALID_CAB;
2214 }
2215
2216 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
2217 (unsigned int)AbsoluteOffset,
2218 (unsigned int)UncompOffset,
2219 (unsigned int)Node->Data.Checksum,
2220 (unsigned int)Node->Data.CompSize,
2221 (unsigned int)Node->Data.UncompSize));
2222
2223 Node->AbsoluteOffset = AbsoluteOffset;
2224 Node->UncompOffset = UncompOffset;
2225
2226 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
2227 UncompOffset += Node->Data.UncompSize;
2228 }
2229
2230 FolderUncompSize = UncompOffset;
2231
2232 return CAB_STATUS_SUCCESS;
2233 }
2234
2235
2236 PCFFOLDER_NODE CCabinet::NewFolderNode()
2237 /*
2238 * FUNCTION: Creates a new folder node
2239 * RETURNS:
2240 * Pointer to node if there was enough free memory available, otherwise NULL
2241 */
2242 {
2243 PCFFOLDER_NODE Node;
2244
2245 Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE));
2246 if (!Node)
2247 return NULL;
2248
2249 memset(Node, 0, sizeof(CFFOLDER_NODE));
2250
2251 Node->Folder.CompressionType = CAB_COMP_NONE;
2252
2253 Node->Prev = FolderListTail;
2254
2255 if (FolderListTail != NULL) {
2256 FolderListTail->Next = Node;
2257 } else {
2258 FolderListHead = Node;
2259 }
2260 FolderListTail = Node;
2261
2262 return Node;
2263 }
2264
2265
2266 PCFFILE_NODE CCabinet::NewFileNode()
2267 /*
2268 * FUNCTION: Creates a new file node
2269 * ARGUMENTS:
2270 * FolderNode = Pointer to folder node to bind file to
2271 * RETURNS:
2272 * Pointer to node if there was enough free memory available, otherwise NULL
2273 */
2274 {
2275 PCFFILE_NODE Node;
2276
2277 Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE));
2278 if (!Node)
2279 return NULL;
2280
2281 memset(Node, 0, sizeof(CFFILE_NODE));
2282
2283 Node->Prev = FileListTail;
2284
2285 if (FileListTail != NULL) {
2286 FileListTail->Next = Node;
2287 } else {
2288 FileListHead = Node;
2289 }
2290 FileListTail = Node;
2291
2292 return Node;
2293 }
2294
2295
2296 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode)
2297 /*
2298 * FUNCTION: Creates a new data block node
2299 * ARGUMENTS:
2300 * FolderNode = Pointer to folder node to bind data block to
2301 * RETURNS:
2302 * Pointer to node if there was enough free memory available, otherwise NULL
2303 */
2304 {
2305 PCFDATA_NODE Node;
2306
2307 Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE));
2308 if (!Node)
2309 return NULL;
2310
2311 memset(Node, 0, sizeof(CFDATA_NODE));
2312
2313 Node->Prev = FolderNode->DataListTail;
2314
2315 if (FolderNode->DataListTail != NULL) {
2316 FolderNode->DataListTail->Next = Node;
2317 } else {
2318 FolderNode->DataListHead = Node;
2319 }
2320 FolderNode->DataListTail = Node;
2321
2322 return Node;
2323 }
2324
2325
2326 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode)
2327 /*
2328 * FUNCTION: Destroys data block nodes bound to a folder node
2329 * ARGUMENTS:
2330 * FolderNode = Pointer to folder node
2331 */
2332 {
2333 PCFDATA_NODE PrevNode;
2334 PCFDATA_NODE NextNode;
2335
2336 NextNode = FolderNode->DataListHead;
2337 while (NextNode != NULL) {
2338 PrevNode = NextNode->Next;
2339 FreeMemory(NextNode);
2340 NextNode = PrevNode;
2341 }
2342 FolderNode->DataListHead = NULL;
2343 FolderNode->DataListTail = NULL;
2344 }
2345
2346
2347 void CCabinet::DestroyFileNodes()
2348 /*
2349 * FUNCTION: Destroys file nodes
2350 * ARGUMENTS:
2351 * FolderNode = Pointer to folder node
2352 */
2353 {
2354 PCFFILE_NODE PrevNode;
2355 PCFFILE_NODE NextNode;
2356
2357 NextNode = FileListHead;
2358 while (NextNode != NULL) {
2359 PrevNode = NextNode->Next;
2360 if (NextNode->FileName)
2361 FreeMemory(NextNode->FileName);
2362 FreeMemory(NextNode);
2363 NextNode = PrevNode;
2364 }
2365 FileListHead = NULL;
2366 FileListTail = NULL;
2367 }
2368
2369
2370 void CCabinet::DestroyDeletedFileNodes()
2371 /*
2372 * FUNCTION: Destroys file nodes that are marked for deletion
2373 */
2374 {
2375 PCFFILE_NODE CurNode;
2376 PCFFILE_NODE NextNode;
2377
2378 CurNode = FileListHead;
2379 while (CurNode != NULL) {
2380 NextNode = CurNode->Next;
2381
2382 if (CurNode->Delete) {
2383 if (CurNode->Prev != NULL) {
2384 CurNode->Prev->Next = CurNode->Next;
2385 } else {
2386 FileListHead = CurNode->Next;
2387 if (FileListHead)
2388 FileListHead->Prev = NULL;
2389 }
2390
2391 if (CurNode->Next != NULL) {
2392 CurNode->Next->Prev = CurNode->Prev;
2393 } else {
2394 FileListTail = CurNode->Prev;
2395 if (FileListTail)
2396 FileListTail->Next = NULL;
2397 }
2398
2399 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName));
2400
2401 TotalFileSize -= (sizeof(CFFILE) + strlen(GetFileName(CurNode->FileName)) + 1);
2402
2403 if (CurNode->FileName)
2404 FreeMemory(CurNode->FileName);
2405 FreeMemory(CurNode);
2406 }
2407 CurNode = NextNode;
2408 }
2409 }
2410
2411
2412 void CCabinet::DestroyFolderNodes()
2413 /*
2414 * FUNCTION: Destroys folder nodes
2415 */
2416 {
2417 PCFFOLDER_NODE PrevNode;
2418 PCFFOLDER_NODE NextNode;
2419
2420 NextNode = FolderListHead;
2421 while (NextNode != NULL) {
2422 PrevNode = NextNode->Next;
2423 DestroyDataNodes(NextNode);
2424 FreeMemory(NextNode);
2425 NextNode = PrevNode;
2426 }
2427 FolderListHead = NULL;
2428 FolderListTail = NULL;
2429 }
2430
2431
2432 void CCabinet::DestroyDeletedFolderNodes()
2433 /*
2434 * FUNCTION: Destroys folder nodes that are marked for deletion
2435 */
2436 {
2437 PCFFOLDER_NODE CurNode;
2438 PCFFOLDER_NODE NextNode;
2439
2440 CurNode = FolderListHead;
2441 while (CurNode != NULL) {
2442 NextNode = CurNode->Next;
2443
2444 if (CurNode->Delete) {
2445 if (CurNode->Prev != NULL) {
2446 CurNode->Prev->Next = CurNode->Next;
2447 } else {
2448 FolderListHead = CurNode->Next;
2449 if (FolderListHead)
2450 FolderListHead->Prev = NULL;
2451 }
2452
2453 if (CurNode->Next != NULL) {
2454 CurNode->Next->Prev = CurNode->Prev;
2455 } else {
2456 FolderListTail = CurNode->Prev;
2457 if (FolderListTail)
2458 FolderListTail->Next = NULL;
2459 }
2460
2461 DestroyDataNodes(CurNode);
2462 FreeMemory(CurNode);
2463
2464 TotalFolderSize -= sizeof(CFFOLDER);
2465 }
2466 CurNode = NextNode;
2467 }
2468 }
2469
2470
2471 unsigned long CCabinet::ComputeChecksum(void* Buffer,
2472 unsigned int Size,
2473 unsigned long Seed)
2474 /*
2475 * FUNCTION: Computes checksum for data block
2476 * ARGUMENTS:
2477 * Buffer = Pointer to data buffer
2478 * Size = Length of data buffer
2479 * Seed = Previously computed checksum
2480 * RETURNS:
2481 * Checksum of buffer
2482 */
2483 {
2484 int UlongCount; // Number of ULONGs in block
2485 unsigned long Checksum; // Checksum accumulator
2486 unsigned char* pb;
2487 unsigned long ul;
2488
2489 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2490 won't accept checksums computed by this routine */
2491
2492 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%X) Size (%d)\n", (unsigned int)Buffer, Size));
2493
2494 UlongCount = Size / 4; // Number of ULONGs
2495 Checksum = Seed; // Init checksum
2496 pb = (unsigned char*)Buffer; // Start at front of data block
2497
2498 /* Checksum integral multiple of ULONGs */
2499 while (UlongCount-- > 0) {
2500 /* NOTE: Build unsigned long in big/little-endian independent manner */
2501 ul = *pb++; // Get low-order byte
2502 ul |= (((unsigned long)(*pb++)) << 8); // Add 2nd byte
2503 ul |= (((unsigned long)(*pb++)) << 16); // Add 3nd byte
2504 ul |= (((unsigned long)(*pb++)) << 24); // Add 4th byte
2505
2506 Checksum ^= ul; // Update checksum
2507 }
2508
2509 /* Checksum remainder bytes */
2510 ul = 0;
2511 switch (Size % 4) {
2512 case 3:
2513 ul |= (((unsigned long)(*pb++)) << 16); // Add 3rd byte
2514 case 2:
2515 ul |= (((unsigned long)(*pb++)) << 8); // Add 2nd byte
2516 case 1:
2517 ul |= *pb++; // Get low-order byte
2518 default:
2519 break;
2520 }
2521 Checksum ^= ul; // Update checksum
2522
2523 /* Return computed checksum */
2524 return Checksum;
2525 }
2526
2527
2528 unsigned long CCabinet::ReadBlock(void* Buffer,
2529 unsigned long Size,
2530 unsigned long* BytesRead)
2531 /*
2532 * FUNCTION: Read a block of data from file
2533 * ARGUMENTS:
2534 * Buffer = Pointer to data buffer
2535 * Size = Length of data buffer
2536 * BytesRead = Pointer to unsigned long that on return will contain
2537 * number of bytes read
2538 * RETURNS:
2539 * Status of operation
2540 */
2541 {
2542 if (!ReadFileData(FileHandle, Buffer, Size, BytesRead))
2543 return CAB_STATUS_INVALID_CAB;
2544 return CAB_STATUS_SUCCESS;
2545 }
2546
2547 #ifndef CAB_READ_ONLY
2548
2549 unsigned long CCabinet::InitCabinetHeader()
2550 /*
2551 * FUNCTION: Initializes cabinet header and optional fields
2552 * RETURNS:
2553 * Status of operation
2554 */
2555 {
2556 unsigned long TotalSize;
2557 unsigned long Size;
2558
2559 CABHeader.FileTableOffset = 0; // Not known yet
2560 CABHeader.FolderCount = 0; // Not known yet
2561 CABHeader.FileCount = 0; // Not known yet
2562 CABHeader.Flags = 0; // Not known yet
2563
2564 CABHeader.CabinetNumber = (unsigned short)CurrentDiskNumber;
2565
2566 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev))) {
2567 CABHeader.Flags |= CAB_FLAG_HASPREV;
2568 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev))
2569 strcpy(CabinetPrev, "");
2570 }
2571
2572 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext)) {
2573 CABHeader.Flags |= CAB_FLAG_HASNEXT;
2574 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext))
2575 strcpy(DiskNext, "");
2576 }
2577
2578 TotalSize = 0;
2579
2580 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) {
2581
2582 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
2583
2584 /* Calculate size of name of previous cabinet */
2585 TotalSize += strlen(CabinetPrev) + 1;
2586
2587 /* Calculate size of label of previous disk */
2588 TotalSize += strlen(DiskPrev) + 1;
2589 }
2590
2591 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) {
2592
2593 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
2594
2595 /* Calculate size of name of next cabinet */
2596 Size = strlen(CabinetNext) + 1;
2597 TotalSize += Size;
2598 NextFieldsSize = Size;
2599
2600 /* Calculate size of label of next disk */
2601 Size = strlen(DiskNext) + 1;
2602 TotalSize += Size;
2603 NextFieldsSize += Size;
2604 } else
2605 NextFieldsSize = 0;
2606
2607 /* Add cabinet reserved area size if present */
2608 if (CabinetReservedFileSize > 0)
2609 {
2610 CABHeader.Flags |= CAB_FLAG_RESERVE;
2611 TotalSize += CabinetReservedFileSize;
2612 TotalSize += sizeof(unsigned long); /* For CabinetResSize, FolderResSize, and FileResSize fields */
2613 }
2614
2615 DiskSize += TotalSize;
2616
2617 TotalHeaderSize = sizeof(CFHEADER) + TotalSize;
2618
2619 return CAB_STATUS_SUCCESS;
2620 }
2621
2622
2623 unsigned long CCabinet::WriteCabinetHeader(bool MoreDisks)
2624 /*
2625 * FUNCTION: Writes the cabinet header and optional fields
2626 * ARGUMENTS:
2627 * MoreDisks = true if next cabinet name should be included
2628 * RETURNS:
2629 * Status of operation
2630 */
2631 {
2632 PCFFOLDER_NODE FolderNode;
2633 PCFFILE_NODE FileNode;
2634 unsigned long BytesWritten;
2635 unsigned long Size;
2636
2637 if (MoreDisks) {
2638 CABHeader.Flags |= CAB_FLAG_HASNEXT;
2639 Size = TotalHeaderSize;
2640 } else {
2641 CABHeader.Flags &= ~CAB_FLAG_HASNEXT;
2642 DiskSize -= NextFieldsSize;
2643 Size = TotalHeaderSize - NextFieldsSize;
2644 }
2645
2646 /* Set absolute folder offsets */
2647 BytesWritten = Size + TotalFolderSize + TotalFileSize;
2648 CABHeader.FolderCount = 0;
2649 FolderNode = FolderListHead;
2650 while (FolderNode != NULL) {
2651 FolderNode->Folder.DataOffset = BytesWritten;
2652
2653 BytesWritten += FolderNode->TotalFolderSize;
2654
2655 CABHeader.FolderCount++;
2656
2657 FolderNode = FolderNode->Next;
2658 }
2659
2660 /* Set absolute offset of file table */
2661 CABHeader.FileTableOffset = Size + TotalFolderSize;
2662
2663 /* Count number of files to be committed */
2664 CABHeader.FileCount = 0;
2665 FileNode = FileListHead;
2666 while (FileNode != NULL) {
2667 if (FileNode->Commit)
2668 CABHeader.FileCount++;
2669 FileNode = FileNode->Next;
2670 }
2671
2672 CABHeader.CabinetSize = DiskSize;
2673
2674 /* Write header */
2675 #if defined(WIN32)
2676 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), &BytesWritten, NULL)) {
2677 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2678 return CAB_STATUS_CANNOT_WRITE;
2679 }
2680 #else
2681 BytesWritten = sizeof(CFHEADER);
2682 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1) {
2683 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2684 return CAB_STATUS_CANNOT_WRITE;
2685 }
2686 #endif
2687
2688 /* Write per-cabinet reserved area if present */
2689 if (CABHeader.Flags & CAB_FLAG_RESERVE) {
2690 unsigned long ReservedSize;
2691
2692 ReservedSize = CabinetReservedFileSize & 0xffff;
2693 ReservedSize |= (0 << 16); /* Folder reserved area size */
2694 ReservedSize |= (0 << 24); /* Folder reserved area size */
2695 #if defined(WIN32)
2696 if (!WriteFile(FileHandle, &ReservedSize, sizeof(unsigned long), &BytesWritten, NULL)) {
2697 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2698 return CAB_STATUS_CANNOT_WRITE;
2699 }
2700 #else
2701 BytesWritten = sizeof(unsigned long);
2702 if (fwrite(&ReservedSize, sizeof(unsigned long), 1, FileHandle) < 1) {
2703 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2704 return CAB_STATUS_CANNOT_WRITE;
2705 }
2706 #endif
2707
2708 #if defined(WIN32)
2709 if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, &BytesWritten, NULL)) {
2710 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2711 return CAB_STATUS_CANNOT_WRITE;
2712 }
2713 #else
2714 BytesWritten = CabinetReservedFileSize;
2715 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1) {
2716 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2717 return CAB_STATUS_CANNOT_WRITE;
2718 }
2719 #endif
2720 }
2721
2722 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) {
2723
2724 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
2725
2726 /* Write name of previous cabinet */
2727 Size = strlen(CabinetPrev) + 1;
2728 #if defined(WIN32)
2729 if (!WriteFile(FileHandle, CabinetPrev, Size, &BytesWritten, NULL)) {
2730 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2731 return CAB_STATUS_CANNOT_WRITE;
2732 }
2733 #else
2734 BytesWritten = Size;
2735 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1) {
2736 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2737 return CAB_STATUS_CANNOT_WRITE;
2738 }
2739 #endif
2740
2741 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev));
2742
2743 /* Write label of previous disk */
2744 Size = strlen(DiskPrev) + 1;
2745 #if defined(WIN32)
2746 if (!WriteFile(FileHandle, DiskPrev, Size, &BytesWritten, NULL)) {
2747 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2748 return CAB_STATUS_CANNOT_WRITE;
2749 }
2750 #else
2751 BytesWritten = Size;
2752 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1) {
2753 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2754 return CAB_STATUS_CANNOT_WRITE;
2755 }
2756 #endif
2757 }
2758
2759 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) {
2760
2761 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
2762
2763 /* Write name of next cabinet */
2764 Size = strlen(CabinetNext) + 1;
2765 #if defined(WIN32)
2766 if (!WriteFile(FileHandle, CabinetNext, Size, &BytesWritten, NULL)) {
2767 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2768 return CAB_STATUS_CANNOT_WRITE;
2769 }
2770 #else
2771 BytesWritten = Size;
2772 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1) {
2773 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2774 return CAB_STATUS_CANNOT_WRITE;
2775 }
2776 #endif
2777
2778 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext));
2779
2780 /* Write label of next disk */
2781 Size = strlen(DiskNext) + 1;
2782 #if defined(WIN32)
2783 if (!WriteFile(FileHandle, DiskNext, Size, &BytesWritten, NULL)) {
2784 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2785 return CAB_STATUS_CANNOT_WRITE;
2786 }
2787 #else
2788 BytesWritten = Size;
2789 if (fwrite(DiskNext, Size, 1, FileHandle) < 1) {
2790 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2791 return CAB_STATUS_CANNOT_WRITE;
2792 }
2793 #endif
2794 }
2795
2796 return CAB_STATUS_SUCCESS;
2797 }
2798
2799
2800 unsigned long CCabinet::WriteFolderEntries()
2801 /*
2802 * FUNCTION: Writes folder entries
2803 * RETURNS:
2804 * Status of operation
2805 */
2806 {
2807 PCFFOLDER_NODE FolderNode;
2808 unsigned long BytesWritten;
2809
2810 DPRINT(MAX_TRACE, ("Writing folder table.\n"));
2811
2812 FolderNode = FolderListHead;
2813 while (FolderNode != NULL) {
2814 if (FolderNode->Commit) {
2815
2816 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%lX).\n",
2817 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, FolderNode->Folder.DataOffset));
2818
2819 #if defined(WIN32)
2820 if (!WriteFile(FileHandle,
2821 &FolderNode->Folder,
2822 sizeof(CFFOLDER),
2823 &BytesWritten,
2824 NULL)) {
2825 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2826 return CAB_STATUS_CANNOT_WRITE;
2827 }
2828 #else
2829 BytesWritten = sizeof(CFFOLDER);
2830 if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1) {
2831 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2832 return CAB_STATUS_CANNOT_WRITE;
2833 }
2834 #endif
2835 }
2836 FolderNode = FolderNode->Next;
2837 }
2838
2839 return CAB_STATUS_SUCCESS;
2840 }
2841
2842
2843 unsigned long CCabinet::WriteFileEntries()
2844 /*
2845 * FUNCTION: Writes file entries for all files
2846 * RETURNS:
2847 * Status of operation
2848 */
2849 {
2850 PCFFILE_NODE File;
2851 unsigned long BytesWritten;
2852 bool SetCont;
2853
2854 DPRINT(MAX_TRACE, ("Writing file table.\n"));
2855
2856 File = FileListHead;
2857 while (File != NULL) {
2858 if (File->Commit) {
2859 /* Remove any continued files that ends in this disk */
2860 if (File->File.FileControlID == CAB_FILE_CONTINUED)
2861 File->Delete = true;
2862
2863 /* The file could end in the last (split) block and should therefore
2864 appear in the next disk too */
2865
2866 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) &&
2867 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit)) {
2868 File->File.FileControlID = CAB_FILE_SPLIT;
2869 File->Delete = false;
2870 SetCont = true;
2871 }
2872
2873 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%lX) FileSize (%lu) FileName (%s).\n",
2874 File->File.FileControlID, File->File.FileOffset, File->File.FileSize, File->FileName));
2875
2876 #if defined(WIN32)
2877 if (!WriteFile(FileHandle,
2878 &File->File,
2879 sizeof(CFFILE),
2880 &BytesWritten,
2881 NULL))
2882 return CAB_STATUS_CANNOT_WRITE;
2883 #else
2884 BytesWritten = sizeof(CFFILE);
2885 if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1) {
2886 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2887 return CAB_STATUS_CANNOT_WRITE;
2888 }
2889 #endif
2890
2891 #if defined(WIN32)
2892 if (!WriteFile(FileHandle,
2893 GetFileName(File->FileName),
2894 strlen(GetFileName(File->FileName)) + 1, &BytesWritten, NULL))
2895 return CAB_STATUS_CANNOT_WRITE;
2896 #else
2897 BytesWritten = strlen(GetFileName(File->FileName)) + 1;
2898 if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1) {
2899 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2900 return CAB_STATUS_CANNOT_WRITE;
2901 }
2902 #endif
2903
2904 if (SetCont) {
2905 File->File.FileControlID = CAB_FILE_CONTINUED;
2906 SetCont = false;
2907 }
2908 }
2909
2910 File = File->Next;
2911 }
2912 return CAB_STATUS_SUCCESS;
2913 }
2914
2915
2916 unsigned long CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode)
2917 /*
2918 * FUNCTION: Writes data blocks to the cabinet
2919 * ARGUMENTS:
2920 * FolderNode = Pointer to folder node containing the data blocks
2921 * RETURNS:
2922 * Status of operation
2923 */
2924 {
2925 PCFDATA_NODE DataNode;
2926 unsigned long BytesWritten;
2927 unsigned long BytesRead;
2928 unsigned long Status;
2929
2930 DataNode = FolderNode->DataListHead;
2931 if (DataNode != NULL)
2932 Status = ScratchFile->Seek(DataNode->ScratchFilePosition);
2933
2934 while (DataNode != NULL) {
2935 DPRINT(MAX_TRACE, ("Reading block at (0x%lX) CompSize (%d) UncompSize (%d).\n",
2936 DataNode->ScratchFilePosition,
2937 DataNode->Data.CompSize,
2938 DataNode->Data.UncompSize));
2939
2940 /* InputBuffer is free for us to use here, so we use it and avoid a
2941 memory allocation. OutputBuffer can't be used here because it may
2942 still contain valid data (if a data block spans two or more disks) */
2943 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead);
2944 if (Status != CAB_STATUS_SUCCESS) {
2945 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%d).\n", (unsigned int)Status));
2946 return Status;
2947 }
2948
2949 #if defined(WIN32)
2950 if (!WriteFile(FileHandle, &DataNode->Data,
2951 sizeof(CFDATA), &BytesWritten, NULL)) {
2952 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2953 return CAB_STATUS_CANNOT_WRITE;
2954 }
2955 #else
2956 BytesWritten = sizeof(CFDATA);
2957 if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1) {
2958 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2959 return CAB_STATUS_CANNOT_WRITE;
2960 }
2961 #endif
2962
2963 #if defined(WIN32)
2964 if (!WriteFile(FileHandle, InputBuffer,
2965 DataNode->Data.CompSize, &BytesWritten, NULL)) {
2966 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2967 return CAB_STATUS_CANNOT_WRITE;
2968 }
2969 #else
2970 BytesWritten = DataNode->Data.CompSize;
2971 if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1) {
2972 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
2973 return CAB_STATUS_CANNOT_WRITE;
2974 }
2975 #endif
2976
2977 DataNode = DataNode->Next;
2978 }
2979 return CAB_STATUS_SUCCESS;
2980 }
2981
2982
2983 unsigned long CCabinet::WriteDataBlock()
2984 /*
2985 * FUNCTION: Writes the current data block to the scratch file
2986 * RETURNS:
2987 * Status of operation
2988 */
2989 {
2990 unsigned long Status;
2991 unsigned long BytesWritten;
2992 PCFDATA_NODE DataNode;
2993
2994 if (!BlockIsSplit) {
2995 Status = Codec->Compress(OutputBuffer,
2996 InputBuffer,
2997 CurrentIBufferSize,
2998 &TotalCompSize);
2999
3000 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%lu) TotalCompSize(%lu).\n",
3001 CurrentIBufferSize, TotalCompSize));
3002
3003 CurrentOBuffer = OutputBuffer;
3004 CurrentOBufferSize = TotalCompSize;
3005 }
3006
3007 DataNode = NewDataNode(CurrentFolderNode);
3008 if (!DataNode) {
3009 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
3010 return CAB_STATUS_NOMEMORY;
3011 }
3012
3013 DiskSize += sizeof(CFDATA);
3014
3015 if (MaxDiskSize > 0)
3016 /* Disk size is limited */
3017 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize);
3018 else
3019 BlockIsSplit = false;
3020
3021 if (BlockIsSplit) {
3022 DataNode->Data.CompSize = (unsigned short)(MaxDiskSize - DiskSize);
3023 DataNode->Data.UncompSize = 0;
3024 CreateNewDisk = true;
3025 } else {
3026 DataNode->Data.CompSize = (unsigned short)CurrentOBufferSize;
3027 DataNode->Data.UncompSize = (unsigned short)CurrentIBufferSize;
3028 }
3029
3030 DataNode->Data.Checksum = 0;
3031 DataNode->ScratchFilePosition = ScratchFile->Position();
3032
3033 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3034 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3035
3036 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%d) UncompSize (%d).\n",
3037 (unsigned int)DataNode->Data.Checksum,
3038 (unsigned int)DataNode->Data.CompSize,
3039 (unsigned int)DataNode->Data.UncompSize));
3040
3041 Status = ScratchFile->WriteBlock(&DataNode->Data,
3042 CurrentOBuffer, &BytesWritten);
3043 if (Status != CAB_STATUS_SUCCESS)
3044 return Status;
3045
3046 DiskSize += BytesWritten;
3047
3048 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA));
3049 CurrentFolderNode->Folder.DataBlockCount++;
3050
3051 *(unsigned char**)&CurrentOBuffer += DataNode->Data.CompSize;
3052 CurrentOBufferSize -= DataNode->Data.CompSize;
3053
3054 LastBlockStart += DataNode->Data.UncompSize;
3055
3056 if (!BlockIsSplit) {
3057 CurrentIBufferSize = 0;
3058 CurrentIBuffer = InputBuffer;
3059 }
3060
3061 return CAB_STATUS_SUCCESS;
3062 }
3063
3064
3065 #if !defined(WIN32)
3066
3067 void CCabinet::ConvertDateAndTime(time_t* Time,
3068 unsigned short* DosDate,
3069 unsigned short* DosTime)
3070 /*
3071 * FUNCTION: Returns file times of a file
3072 * ARGUMENTS:
3073 * FileHandle = File handle of file to get file times from
3074 * File = Pointer to CFFILE node for file
3075 * RETURNS:
3076 * Status of operation
3077 */
3078 {
3079 struct tm *timedef;
3080
3081 timedef = localtime(Time);
3082
3083 DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3084 timedef->tm_mday, timedef->tm_mon, timedef->tm_year,
3085 timedef->tm_sec, timedef->tm_min, timedef->tm_hour));
3086
3087 *DosDate = ((timedef->tm_mday + 1) << 0)
3088 | ((timedef->tm_mon + 1) << 5)
3089 | (((timedef->tm_year + 1900) - 1980) << 9);
3090
3091 *DosTime = (timedef->tm_sec << 0)
3092 | (timedef->tm_min << 5)
3093 | (timedef->tm_hour << 11);
3094 }
3095
3096 #endif // !WIN32
3097
3098
3099 unsigned long CCabinet::GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File)
3100 /*
3101 * FUNCTION: Returns file times of a file
3102 * ARGUMENTS:
3103 * FileHandle = File handle of file to get file times from
3104 * File = Pointer to CFFILE node for file
3105 * RETURNS:
3106 * Status of operation
3107 */
3108 {
3109 #if defined(WIN32)
3110 FILETIME FileTime;
3111
3112 if (GetFileTime(FileHandle, NULL, NULL, &FileTime))
3113 FileTimeToDosDateTime(&FileTime,
3114 &File->File.FileDate,
3115 &File->File.FileTime);
3116 #else
3117 struct stat stbuf;
3118 char buf[MAX_PATH];
3119
3120 // Check for an absolute path
3121 if (IsSeparator(File->FileName[0]))
3122 {
3123 strcpy(buf, File->FileName);
3124 }
3125 else
3126 {
3127 getcwd(buf, sizeof(buf));
3128 strcat(buf, DIR_SEPARATOR_STRING);
3129 strcat(buf, File->FileName);
3130 }
3131
3132 if (stat(buf, &stbuf) == -1)
3133 {
3134 return CAB_STATUS_CANNOT_READ;
3135 }
3136
3137 ConvertDateAndTime(&stbuf.st_mtime, &File->File.FileDate, &File->File.FileTime);
3138 #endif
3139 return CAB_STATUS_SUCCESS;
3140 }
3141
3142
3143 unsigned long CCabinet::GetAttributesOnFile(PCFFILE_NODE File)
3144 /*
3145 * FUNCTION: Returns attributes on a file
3146 * ARGUMENTS:
3147 * File = Pointer to CFFILE node for file
3148 * RETURNS:
3149 * Status of operation
3150 */
3151 {
3152 #if defined(WIN32)
3153 long Attributes;
3154
3155 Attributes = GetFileAttributes(File->FileName);
3156 if (Attributes == -1)
3157 return CAB_STATUS_CANNOT_READ;
3158
3159 if (Attributes & FILE_ATTRIBUTE_READONLY)
3160 File->File.Attributes |= CAB_ATTRIB_READONLY;
3161
3162 if (Attributes & FILE_ATTRIBUTE_HIDDEN)
3163 File->File.Attributes |= CAB_ATTRIB_HIDDEN;
3164
3165 if (Attributes & FILE_ATTRIBUTE_SYSTEM)
3166 File->File.Attributes |= CAB_ATTRIB_SYSTEM;
3167
3168 if (Attributes & FILE_ATTRIBUTE_DIRECTORY)
3169 File->File.Attributes |= CAB_ATTRIB_DIRECTORY;
3170
3171 if (Attributes & FILE_ATTRIBUTE_ARCHIVE)
3172 File->File.Attributes |= CAB_ATTRIB_ARCHIVE;
3173 #else
3174 struct stat stbuf;
3175 char buf[MAX_PATH];
3176
3177 // Check for an absolute path
3178 if (IsSeparator(File->FileName[0]))
3179 {
3180 strcpy(buf, File->FileName);
3181 }
3182 else
3183 {
3184 getcwd(buf, sizeof(buf));
3185 strcat(buf, DIR_SEPARATOR_STRING);
3186 strcat(buf, File->FileName);
3187 }
3188
3189 if (stat(buf, &stbuf) == -1)
3190 {
3191 return CAB_STATUS_CANNOT_READ;
3192 }
3193
3194 #if 0
3195 File->File.Attributes |= CAB_ATTRIB_READONLY;
3196 File->File.Attributes |= CAB_ATTRIB_HIDDEN;
3197 File->File.Attributes |= CAB_ATTRIB_SYSTEM;
3198 #endif
3199
3200 if (stbuf.st_mode & S_IFDIR)
3201 File->File.Attributes |= CAB_ATTRIB_DIRECTORY;
3202
3203 File->File.Attributes |= CAB_ATTRIB_ARCHIVE;
3204
3205 #endif
3206 return CAB_STATUS_SUCCESS;
3207 }
3208
3209
3210 unsigned long CCabinet::SetAttributesOnFile(PCFFILE_NODE File)
3211 /*
3212 * FUNCTION: Sets attributes on a file
3213 * ARGUMENTS:
3214 * File = Pointer to CFFILE node for file
3215 * RETURNS:
3216 * Status of operation
3217 */
3218 {
3219 #if defined(WIN32)
3220 unsigned long Attributes = 0;
3221
3222 if (File->File.Attributes & CAB_ATTRIB_READONLY)
3223 Attributes |= FILE_ATTRIBUTE_READONLY;
3224
3225 if (File->File.Attributes & CAB_ATTRIB_HIDDEN)
3226 Attributes |= FILE_ATTRIBUTE_HIDDEN;
3227
3228 if (File->File.Attributes & CAB_ATTRIB_SYSTEM)
3229 Attributes |= FILE_ATTRIBUTE_SYSTEM;
3230
3231 if (File->File.Attributes & CAB_ATTRIB_DIRECTORY)
3232 Attributes |= FILE_ATTRIBUTE_DIRECTORY;
3233
3234 if (File->File.Attributes & CAB_ATTRIB_ARCHIVE)
3235 Attributes |= FILE_ATTRIBUTE_ARCHIVE;
3236
3237 SetFileAttributes(File->FileName, Attributes);
3238
3239 return CAB_STATUS_SUCCESS;
3240 #else
3241 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3242 return CAB_STATUS_SUCCESS;
3243 #endif
3244 }
3245
3246 #endif /* CAB_READ_ONLY */
3247
3248 /* EOF */