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