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