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