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