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