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