- Handle the different slashes correctly, also in combination (like "../..\*.txt...
[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(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 unsigned char* Buffer;
1025 unsigned char* 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 = (unsigned char*)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 (_W64 unsigned long)CurrentBuffer, (_W64 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*)((unsigned long *)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*)((unsigned long *)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 *(unsigned char**)&CurrentIBuffer += BytesRead;
1698
1699 CurrentIBufferSize += (USHORT)BytesRead;
1700
1701 if (CurrentIBufferSize == CAB_BLOCKSIZE)
1702 {
1703 Status = WriteDataBlock();
1704 if (Status != CAB_STATUS_SUCCESS)
1705 return Status;
1706 }
1707 TotalBytesLeft -= BytesRead;
1708 } while ((TotalBytesLeft > 0) && (!CreateNewDisk));
1709 }
1710
1711 if (TotalBytesLeft == 0)
1712 {
1713 CloseFile(SourceFile);
1714 FileNode->Delete = true;
1715
1716 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER)
1717 {
1718 FileNode->File.FileControlID = CAB_FILE_CONTINUED;
1719 CurrentFolderNode->Delete = true;
1720
1721 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1722 {
1723 Status = WriteDataBlock();
1724 if (Status != CAB_STATUS_SUCCESS)
1725 return Status;
1726 }
1727
1728 CreateNewFolder = true;
1729 }
1730 }
1731 else
1732 {
1733 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER)
1734 FileNode->File.FileControlID = CAB_FILE_SPLIT;
1735 else
1736 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT;
1737 }
1738
1739 return CAB_STATUS_SUCCESS;
1740 }
1741
1742
1743 ULONG CCabinet::WriteDisk(ULONG MoreDisks)
1744 /*
1745 * FUNCTION: Forces the current disk to be written
1746 * ARGUMENTS:
1747 * MoreDisks = true if there is one or more disks after this disk
1748 * RETURNS:
1749 * Status of operation
1750 */
1751 {
1752 PCFFILE_NODE FileNode;
1753 ULONG Status;
1754
1755 ContinueFile = false;
1756 FileNode = FileListHead;
1757 while (FileNode != NULL)
1758 {
1759 Status = WriteFileToScratchStorage(FileNode);
1760 if (Status != CAB_STATUS_SUCCESS)
1761 return Status;
1762
1763 if (CreateNewDisk)
1764 {
1765 /* A data block could span more than two
1766 disks if MaxDiskSize is very small */
1767 while (CreateNewDisk)
1768 {
1769 DPRINT(MAX_TRACE, ("Creating new disk.\n"));
1770 CommitDisk(true);
1771 CloseDisk();
1772 NewDisk();
1773
1774 ContinueFile = true;
1775 CreateNewDisk = false;
1776
1777 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n",
1778 (UINT)CurrentIBufferSize, (UINT)CurrentOBufferSize));
1779
1780 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1781 {
1782 Status = WriteDataBlock();
1783 if (Status != CAB_STATUS_SUCCESS)
1784 return Status;
1785 }
1786 }
1787 }
1788 else
1789 {
1790 ContinueFile = false;
1791 FileNode = FileNode->Next;
1792 }
1793 }
1794
1795 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1796 {
1797 /* A data block could span more than two
1798 disks if MaxDiskSize is very small */
1799
1800 ASSERT(CreateNewDisk == false);
1801
1802 do
1803 {
1804 if (CreateNewDisk)
1805 {
1806 DPRINT(MID_TRACE, ("Creating new disk 2.\n"));
1807 CommitDisk(true);
1808 CloseDisk();
1809 NewDisk();
1810 CreateNewDisk = false;
1811
1812 ASSERT(FileNode == FileListHead);
1813 }
1814
1815 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0))
1816 {
1817 Status = WriteDataBlock();
1818 if (Status != CAB_STATUS_SUCCESS)
1819 return Status;
1820 }
1821 } while (CreateNewDisk);
1822 }
1823 CommitDisk(MoreDisks);
1824
1825 return CAB_STATUS_SUCCESS;
1826 }
1827
1828
1829 ULONG CCabinet::CommitDisk(ULONG MoreDisks)
1830 /*
1831 * FUNCTION: Commits the current disk
1832 * ARGUMENTS:
1833 * MoreDisks = true if there is one or more disks after this disk
1834 * RETURNS:
1835 * Status of operation
1836 */
1837 {
1838 PCFFOLDER_NODE FolderNode;
1839 ULONG Status;
1840
1841 OnCabinetName(CurrentDiskNumber, CabinetName);
1842
1843 /* Create file, fail if it already exists */
1844 #if defined(WIN32)
1845 FileHandle = CreateFile(CabinetName, // Create this file
1846 GENERIC_WRITE, // Open for writing
1847 0, // No sharing
1848 NULL, // No security
1849 CREATE_NEW, // New file only
1850 FILE_ATTRIBUTE_NORMAL, // Normal file
1851 NULL); // No attribute template
1852 if (FileHandle == INVALID_HANDLE_VALUE)
1853 {
1854 ULONG Status;
1855 /* If file exists, ask to overwrite file */
1856 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) &&
1857 (OnOverwrite(NULL, CabinetName)))
1858 {
1859
1860 /* Create cabinet file, overwrite if it already exists */
1861 FileHandle = CreateFile(CabinetName, // Create this file
1862 GENERIC_WRITE, // Open for writing
1863 0, // No sharing
1864 NULL, // No security
1865 TRUNCATE_EXISTING, // Truncate the file
1866 FILE_ATTRIBUTE_NORMAL, // Normal file
1867 NULL); // No attribute template
1868 if (FileHandle == INVALID_HANDLE_VALUE)
1869 return CAB_STATUS_CANNOT_CREATE;
1870 }
1871 else
1872 {
1873 if (Status == ERROR_FILE_EXISTS)
1874 return CAB_STATUS_FILE_EXISTS;
1875 else
1876 return CAB_STATUS_CANNOT_CREATE;
1877 }
1878 }
1879 #else /* !WIN32 */
1880 FileHandle = fopen(CabinetName, "rb");
1881 if (FileHandle != NULL)
1882 {
1883 fclose(FileHandle);
1884 /* If file exists, ask to overwrite file */
1885 if (OnOverwrite(NULL, CabinetName))
1886 {
1887 FileHandle = fopen(CabinetName, "w+b");
1888 if (FileHandle == NULL)
1889 return CAB_STATUS_CANNOT_CREATE;
1890 }
1891 else
1892 return CAB_STATUS_FILE_EXISTS;
1893
1894 }
1895 else
1896 {
1897 FileHandle = fopen(CabinetName, "w+b");
1898 if (FileHandle == NULL)
1899 return CAB_STATUS_CANNOT_CREATE;
1900 }
1901 #endif
1902
1903 WriteCabinetHeader(MoreDisks != 0);
1904
1905 Status = WriteFolderEntries();
1906 if (Status != CAB_STATUS_SUCCESS)
1907 return Status;
1908
1909 /* Write file entries */
1910 WriteFileEntries();
1911
1912 /* Write data blocks */
1913 FolderNode = FolderListHead;
1914 while (FolderNode != NULL)
1915 {
1916 if (FolderNode->Commit)
1917 {
1918 Status = CommitDataBlocks(FolderNode);
1919 if (Status != CAB_STATUS_SUCCESS)
1920 return Status;
1921 /* Remove data blocks for folder */
1922 DestroyDataNodes(FolderNode);
1923 }
1924 FolderNode = FolderNode->Next;
1925 }
1926
1927 CloseFile(FileHandle);
1928
1929 ScratchFile->Truncate();
1930
1931 return CAB_STATUS_SUCCESS;
1932 }
1933
1934
1935 ULONG CCabinet::CloseDisk()
1936 /*
1937 * FUNCTION: Closes the current disk
1938 * RETURNS:
1939 * Status of operation
1940 */
1941 {
1942 DestroyDeletedFileNodes();
1943
1944 /* Destroy folder nodes that are completely stored */
1945 DestroyDeletedFolderNodes();
1946
1947 CurrentDiskNumber++;
1948
1949 return CAB_STATUS_SUCCESS;
1950 }
1951
1952
1953 ULONG CCabinet::CloseCabinet()
1954 /*
1955 * FUNCTION: Closes the current cabinet
1956 * RETURNS:
1957 * Status of operation
1958 */
1959 {
1960 ULONG Status;
1961
1962 DestroyFileNodes();
1963
1964 DestroyFolderNodes();
1965
1966 if (InputBuffer)
1967 {
1968 FreeMemory(InputBuffer);
1969 InputBuffer = NULL;
1970 }
1971
1972 if (OutputBuffer)
1973 {
1974 FreeMemory(OutputBuffer);
1975 OutputBuffer = NULL;
1976 }
1977
1978 Close();
1979
1980 if (ScratchFile)
1981 {
1982 Status = ScratchFile->Destroy();
1983 delete ScratchFile;
1984 return Status;
1985 }
1986
1987 return CAB_STATUS_SUCCESS;
1988 }
1989
1990
1991 ULONG CCabinet::AddFile(char* FileName)
1992 /*
1993 * FUNCTION: Adds a file to the current disk
1994 * ARGUMENTS:
1995 * FileName = Pointer to string with file name (full path)
1996 * RETURNS:
1997 * Status of operation
1998 */
1999 {
2000 FILEHANDLE SrcFile;
2001 PCFFILE_NODE FileNode;
2002 char* NewFileName;
2003
2004 NewFileName = (char*)AllocateMemory(strlen(FileName) + 1);
2005 if (!NewFileName)
2006 {
2007 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2008 return CAB_STATUS_NOMEMORY;
2009 }
2010 strcpy(NewFileName, FileName);
2011 ConvertPath(NewFileName, false);
2012
2013 /* Try to open file */
2014 #if defined(WIN32)
2015 SrcFile = CreateFile(
2016 NewFileName, // Open this file
2017 GENERIC_READ, // Open for reading
2018 FILE_SHARE_READ, // Share for reading
2019 NULL, // No security
2020 OPEN_EXISTING, // File must exist
2021 FILE_ATTRIBUTE_NORMAL, // Normal file
2022 NULL); // No attribute template
2023 if (SrcFile == INVALID_HANDLE_VALUE)
2024 {
2025 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2026 FreeMemory(NewFileName);
2027 return CAB_STATUS_CANNOT_OPEN;
2028 }
2029 #else /* !WIN32 */
2030 SrcFile = fopen(NewFileName, "rb");
2031 if (SrcFile == NULL)
2032 {
2033 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName));
2034 FreeMemory(NewFileName);
2035 return CAB_STATUS_CANNOT_OPEN;
2036 }
2037 #endif
2038
2039 FileNode = NewFileNode();
2040 if (!FileNode)
2041 {
2042 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2043 FreeMemory(NewFileName);
2044 return CAB_STATUS_NOMEMORY;
2045 }
2046
2047 FileNode->FolderNode = CurrentFolderNode;
2048 FileNode->FileName = NewFileName;
2049
2050 /* FIXME: Check for and handle large files (>= 2GB) */
2051 FileNode->File.FileSize = GetSizeOfFile(SrcFile);
2052 if (FileNode->File.FileSize == (ULONG)-1)
2053 {
2054 DPRINT(MIN_TRACE, ("Cannot read from file.\n"));
2055 FreeMemory(NewFileName);
2056 return CAB_STATUS_CANNOT_READ;
2057 }
2058
2059 GetFileTimes(SrcFile, FileNode);
2060
2061 GetAttributesOnFile(FileNode);
2062
2063 CloseFile(SrcFile);
2064
2065 return CAB_STATUS_SUCCESS;
2066 }
2067
2068 bool CCabinet::CreateSimpleCabinet()
2069 /*
2070 * FUNCTION: Create a simple cabinet based on the files in the criteria list
2071 */
2072 {
2073 bool bRet = false;
2074 char* pszFile;
2075 char szFilePath[MAX_PATH];
2076 char szFile[MAX_PATH];
2077 PSEARCH_CRITERIA Criteria;
2078 ULONG Status;
2079
2080 #if defined(WIN32)
2081 HANDLE hFind;
2082 WIN32_FIND_DATA FindFileData;
2083 #else
2084 DIR* dirp;
2085 struct dirent* dp;
2086 struct stat stbuf;
2087 #endif
2088
2089 // Initialize a new cabinet
2090 Status = NewCabinet();
2091 if (Status != CAB_STATUS_SUCCESS)
2092 {
2093 DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status));
2094 goto cleanup;
2095 }
2096
2097 // Add each file in the criteria list
2098 Criteria = CriteriaListHead;
2099
2100 while(Criteria)
2101 {
2102 // Store the file path with a trailing slash in szFilePath
2103 ConvertPath(Criteria->Search, false);
2104
2105 #if defined(WIN32)
2106 pszFile = strrchr(Criteria->Search, '\\');
2107 #else
2108 pszFile = strrchr(Criteria->Search, '/');
2109 #endif
2110
2111 if(pszFile)
2112 {
2113 // Set the pointer to the start of the file name, not the slash
2114 pszFile++;
2115
2116 strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search);
2117 szFilePath[pszFile - Criteria->Search] = 0;
2118 }
2119 else
2120 {
2121 pszFile = Criteria->Search;
2122
2123 #if defined(WIN32)
2124 szFilePath[0] = 0;
2125 #else
2126 // needed for opendir()
2127 strcpy(szFilePath, "./");
2128 #endif
2129 }
2130
2131 #if defined(WIN32)
2132 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern
2133 hFind = FindFirstFile(Criteria->Search, &FindFileData);
2134
2135 // Don't stop if a search criteria is not found
2136 if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND)
2137 {
2138 DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError()));
2139 goto cleanup;
2140 }
2141
2142 do
2143 {
2144 if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
2145 {
2146 strcpy(szFile, szFilePath);
2147 strcat(szFile, FindFileData.cFileName);
2148
2149 Status = AddFile(szFile);
2150
2151 if(Status != CAB_STATUS_SUCCESS)
2152 {
2153 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2154 goto cleanup;
2155 }
2156 }
2157 }
2158 while(FindNextFile(hFind, &FindFileData));
2159
2160 FindClose(hFind);
2161 #else
2162 // 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
2163 dirp = opendir(szFilePath);
2164
2165 if(dirp)
2166 {
2167 while( (dp = readdir(dirp)) )
2168 {
2169 strcpy(szFile, szFilePath);
2170 strcat(szFile, dp->d_name);
2171
2172 if(stat(szFile, &stbuf) == 0)
2173 {
2174 if(stbuf.st_mode != S_IFDIR)
2175 {
2176 if(MatchFileNamePattern(dp->d_name, pszFile))
2177 {
2178 Status = AddFile(szFile);
2179
2180 if(Status != CAB_STATUS_SUCCESS)
2181 {
2182 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status));
2183 goto cleanup;
2184 }
2185 }
2186 }
2187 }
2188 else
2189 {
2190 DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno));
2191 goto cleanup;
2192 }
2193 }
2194
2195 closedir(dirp);
2196 }
2197 #endif
2198
2199 Criteria = Criteria->Next;
2200 }
2201
2202 Status = WriteDisk(false);
2203 if (Status == CAB_STATUS_SUCCESS)
2204 Status = CloseDisk();
2205 if (Status != CAB_STATUS_SUCCESS)
2206 {
2207 DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status));
2208 goto cleanup;
2209 }
2210
2211 CloseCabinet();
2212 bRet = true;
2213
2214 cleanup:
2215 DestroySearchCriteria();
2216 return bRet;
2217 }
2218
2219 void CCabinet::SetMaxDiskSize(ULONG Size)
2220 /*
2221 * FUNCTION: Sets the maximum size of the current disk
2222 * ARGUMENTS:
2223 * Size = Maximum size of current disk (0 means no maximum size)
2224 */
2225 {
2226 MaxDiskSize = Size;
2227 }
2228
2229 #endif /* CAB_READ_ONLY */
2230
2231
2232 /* Default event handlers */
2233
2234 bool CCabinet::OnOverwrite(PCFFILE File,
2235 char* FileName)
2236 /*
2237 * FUNCTION: Called when extracting a file and it already exists
2238 * ARGUMENTS:
2239 * File = Pointer to CFFILE for file being extracted
2240 * FileName = Pointer to buffer with name of file (full path)
2241 * RETURNS
2242 * true if the file should be overwritten, false if not
2243 */
2244 {
2245 return false;
2246 }
2247
2248
2249 void CCabinet::OnExtract(PCFFILE File,
2250 char* FileName)
2251 /*
2252 * FUNCTION: Called just before extracting a file
2253 * ARGUMENTS:
2254 * File = Pointer to CFFILE for file being extracted
2255 * FileName = Pointer to buffer with name of file (full path)
2256 */
2257 {
2258 }
2259
2260
2261 void CCabinet::OnDiskChange(char* CabinetName,
2262 char* DiskLabel)
2263 /*
2264 * FUNCTION: Called when a new disk is to be processed
2265 * ARGUMENTS:
2266 * CabinetName = Pointer to buffer with name of cabinet
2267 * DiskLabel = Pointer to buffer with label of disk
2268 */
2269 {
2270 }
2271
2272
2273 #ifndef CAB_READ_ONLY
2274
2275 void CCabinet::OnAdd(PCFFILE File,
2276 char* FileName)
2277 /*
2278 * FUNCTION: Called just before adding a file to a cabinet
2279 * ARGUMENTS:
2280 * File = Pointer to CFFILE for file being added
2281 * FileName = Pointer to buffer with name of file (full path)
2282 */
2283 {
2284 }
2285
2286
2287 bool CCabinet::OnDiskLabel(ULONG Number, char* Label)
2288 /*
2289 * FUNCTION: Called when a disk needs a label
2290 * ARGUMENTS:
2291 * Number = Cabinet number that needs a label
2292 * Label = Pointer to buffer to place label of disk
2293 * RETURNS:
2294 * true if a disk label was returned, false if not
2295 */
2296 {
2297 return false;
2298 }
2299
2300
2301 bool CCabinet::OnCabinetName(ULONG Number, char* Name)
2302 /*
2303 * FUNCTION: Called when a cabinet needs a name
2304 * ARGUMENTS:
2305 * Number = Disk number that needs a name
2306 * Name = Pointer to buffer to place name of cabinet
2307 * RETURNS:
2308 * true if a cabinet name was returned, false if not
2309 */
2310 {
2311 return false;
2312 }
2313
2314 #endif /* CAB_READ_ONLY */
2315
2316 PCFFOLDER_NODE CCabinet::LocateFolderNode(ULONG Index)
2317 /*
2318 * FUNCTION: Locates a folder node
2319 * ARGUMENTS:
2320 * Index = Folder index
2321 * RETURNS:
2322 * Pointer to folder node or NULL if the folder node was not found
2323 */
2324 {
2325 PCFFOLDER_NODE Node;
2326
2327 switch (Index)
2328 {
2329 case CAB_FILE_SPLIT:
2330 return FolderListTail;
2331
2332 case CAB_FILE_CONTINUED:
2333 case CAB_FILE_PREV_NEXT:
2334 return FolderListHead;
2335 }
2336
2337 Node = FolderListHead;
2338 while (Node != NULL)
2339 {
2340 if (Node->Index == Index)
2341 return Node;
2342 Node = Node->Next;
2343 }
2344 return NULL;
2345 }
2346
2347
2348 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File)
2349 /*
2350 * FUNCTION: Returns the absolute offset of a file
2351 * ARGUMENTS:
2352 * File = Pointer to CFFILE_NODE structure for file
2353 * RETURNS:
2354 * Status of operation
2355 */
2356 {
2357 PCFDATA_NODE Node;
2358
2359 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n",
2360 File->FileName,
2361 (UINT)File->File.FileOffset,
2362 (UINT)File->File.FileSize));
2363
2364 Node = CurrentFolderNode->DataListHead;
2365 while (Node != NULL)
2366 {
2367 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n",
2368 (UINT)Node->UncompOffset,
2369 (UINT)(Node->UncompOffset + Node->Data.UncompSize),
2370 (UINT)Node->Data.UncompSize));
2371
2372 /* Node->Data.UncompSize will be 0 if the block is split
2373 (ie. it is the last block in this cabinet) */
2374 if ((Node->Data.UncompSize == 0) ||
2375 ((File->File.FileOffset >= Node->UncompOffset) &&
2376 (File->File.FileOffset < Node->UncompOffset +
2377 Node->Data.UncompSize)))
2378 {
2379 File->DataBlock = Node;
2380 return CAB_STATUS_SUCCESS;
2381 }
2382
2383 Node = Node->Next;
2384 }
2385 return CAB_STATUS_INVALID_CAB;
2386 }
2387
2388
2389 ULONG CCabinet::LocateFile(char* FileName,
2390 PCFFILE_NODE *File)
2391 /*
2392 * FUNCTION: Locates a file in the cabinet
2393 * ARGUMENTS:
2394 * FileName = Pointer to string with name of file to locate
2395 * File = Address of pointer to CFFILE_NODE structure to fill
2396 * RETURNS:
2397 * Status of operation
2398 * NOTES:
2399 * Current folder is set to the folder of the file
2400 */
2401 {
2402 PCFFILE_NODE Node;
2403 ULONG Status;
2404
2405 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName));
2406
2407 Node = FileListHead;
2408 while (Node != NULL)
2409 {
2410 if (strcasecmp(FileName, Node->FileName) == 0)
2411 {
2412 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID);
2413 if (!CurrentFolderNode)
2414 {
2415 DPRINT(MID_TRACE, ("Folder with index number (%u) not found.\n",
2416 Node->File.FileControlID));
2417 return CAB_STATUS_INVALID_CAB;
2418 }
2419
2420 if (Node->DataBlock == NULL)
2421 Status = GetAbsoluteOffset(Node);
2422 else
2423 Status = CAB_STATUS_SUCCESS;
2424
2425 *File = Node;
2426 return Status;
2427 }
2428 Node = Node->Next;
2429 }
2430 return CAB_STATUS_NOFILE;
2431 }
2432
2433
2434 ULONG CCabinet::ReadString(char* String, ULONG MaxLength)
2435 /*
2436 * FUNCTION: Reads a NULL-terminated string from the cabinet
2437 * ARGUMENTS:
2438 * String = Pointer to buffer to place string
2439 * MaxLength = Maximum length of string
2440 * RETURNS:
2441 * Status of operation
2442 */
2443 {
2444 ULONG BytesRead;
2445 ULONG Offset;
2446 ULONG Status;
2447 ULONG Size;
2448 bool Found;
2449
2450 Offset = 0;
2451 Found = false;
2452 do
2453 {
2454 Size = ((Offset + 32) <= MaxLength)? 32 : MaxLength - Offset;
2455
2456 if (Size == 0)
2457 {
2458 DPRINT(MIN_TRACE, ("Too long a filename.\n"));
2459 return CAB_STATUS_INVALID_CAB;
2460 }
2461
2462 Status = ReadBlock(&String[Offset], Size, &BytesRead);
2463 if ((Status != CAB_STATUS_SUCCESS) || (BytesRead != Size))
2464 {
2465 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2466 return CAB_STATUS_INVALID_CAB;
2467 }
2468
2469 for (Size = Offset; Size < Offset + BytesRead; Size++)
2470 {
2471 if (String[Size] == '\0')
2472 {
2473 Found = true;
2474 break;
2475 }
2476 }
2477
2478 Offset += BytesRead;
2479
2480 } while (!Found);
2481
2482 /* Back up some bytes */
2483 Size = (BytesRead - Size) - 1;
2484 #if defined(WIN32)
2485 if( SetFilePointer(FileHandle,
2486 -(LONG)Size,
2487 NULL,
2488 FILE_CURRENT) == INVALID_SET_FILE_POINTER )
2489 {
2490 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2491 return CAB_STATUS_INVALID_CAB;
2492 }
2493 #else
2494 if (fseek(FileHandle, (off_t)(-(LONG)Size), SEEK_CUR) != 0)
2495 {
2496 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2497 return CAB_STATUS_INVALID_CAB;
2498 }
2499 #endif
2500 return CAB_STATUS_SUCCESS;
2501 }
2502
2503
2504 ULONG CCabinet::ReadFileTable()
2505 /*
2506 * FUNCTION: Reads the file table from the cabinet file
2507 * RETURNS:
2508 * Status of operation
2509 */
2510 {
2511 ULONG i;
2512 ULONG Status;
2513 ULONG BytesRead;
2514 PCFFILE_NODE File;
2515
2516 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n",
2517 (UINT)CABHeader.FileTableOffset));
2518
2519 /* Seek to file table */
2520 #if defined(WIN32)
2521 if( SetFilePointer(FileHandle,
2522 CABHeader.FileTableOffset,
2523 NULL,
2524 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2525 {
2526 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2527 return CAB_STATUS_INVALID_CAB;
2528 }
2529 #else
2530 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0)
2531 {
2532 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2533 return CAB_STATUS_INVALID_CAB;
2534 }
2535 #endif
2536
2537 for (i = 0; i < CABHeader.FileCount; i++)
2538 {
2539 File = NewFileNode();
2540 if (!File)
2541 {
2542 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2543 return CAB_STATUS_NOMEMORY;
2544 }
2545
2546 if ((Status = ReadBlock(&File->File, sizeof(CFFILE),
2547 &BytesRead)) != CAB_STATUS_SUCCESS)
2548 {
2549 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2550 return CAB_STATUS_INVALID_CAB;
2551 }
2552
2553 File->FileName = (char*)AllocateMemory(MAX_PATH);
2554 if (!File->FileName)
2555 {
2556 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2557 return CAB_STATUS_NOMEMORY;
2558 }
2559
2560 /* Read file name */
2561 Status = ReadString(File->FileName, MAX_PATH);
2562 if (Status != CAB_STATUS_SUCCESS)
2563 return Status;
2564
2565 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n",
2566 File->FileName,
2567 (UINT)File->File.FileOffset,
2568 (UINT)File->File.FileSize,
2569 File->File.FileControlID));
2570
2571 }
2572 return CAB_STATUS_SUCCESS;
2573 }
2574
2575
2576 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode)
2577 /*
2578 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file
2579 * ARGUMENTS:
2580 * FolderNode = Pointer to CFFOLDER_NODE structure for folder
2581 * RETURNS:
2582 * Status of operation
2583 */
2584 {
2585 ULONG AbsoluteOffset;
2586 ULONG UncompOffset;
2587 PCFDATA_NODE Node;
2588 ULONG BytesRead;
2589 ULONG Status;
2590 ULONG i;
2591
2592 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n",
2593 (UINT)FolderNode->Index, (UINT)FolderNode->Folder.DataOffset));
2594
2595 AbsoluteOffset = FolderNode->Folder.DataOffset;
2596 UncompOffset = FolderNode->UncompOffset;
2597
2598 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++)
2599 {
2600 Node = NewDataNode(FolderNode);
2601 if (!Node)
2602 {
2603 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
2604 return CAB_STATUS_NOMEMORY;
2605 }
2606
2607 /* Seek to data block */
2608 #if defined(WIN32)
2609 if( SetFilePointer(FileHandle,
2610 AbsoluteOffset,
2611 NULL,
2612 FILE_BEGIN) == INVALID_SET_FILE_POINTER )
2613 {
2614 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError()));
2615 return CAB_STATUS_INVALID_CAB;
2616 }
2617 #else
2618 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0)
2619 {
2620 DPRINT(MIN_TRACE, ("fseek() failed.\n"));
2621 return CAB_STATUS_INVALID_CAB;
2622 }
2623 #endif
2624
2625 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA),
2626 &BytesRead)) != CAB_STATUS_SUCCESS)
2627 {
2628 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status));
2629 return CAB_STATUS_INVALID_CAB;
2630 }
2631
2632 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
2633 (UINT)AbsoluteOffset,
2634 (UINT)UncompOffset,
2635 (UINT)Node->Data.Checksum,
2636 Node->Data.CompSize,
2637 Node->Data.UncompSize));
2638
2639 Node->AbsoluteOffset = AbsoluteOffset;
2640 Node->UncompOffset = UncompOffset;
2641
2642 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize;
2643 UncompOffset += Node->Data.UncompSize;
2644 }
2645
2646 FolderUncompSize = UncompOffset;
2647
2648 return CAB_STATUS_SUCCESS;
2649 }
2650
2651
2652 PCFFOLDER_NODE CCabinet::NewFolderNode()
2653 /*
2654 * FUNCTION: Creates a new folder node
2655 * RETURNS:
2656 * Pointer to node if there was enough free memory available, otherwise NULL
2657 */
2658 {
2659 PCFFOLDER_NODE Node;
2660
2661 Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE));
2662 if (!Node)
2663 return NULL;
2664
2665 memset(Node, 0, sizeof(CFFOLDER_NODE));
2666
2667 Node->Folder.CompressionType = CAB_COMP_NONE;
2668
2669 Node->Prev = FolderListTail;
2670
2671 if (FolderListTail != NULL)
2672 FolderListTail->Next = Node;
2673 else
2674 FolderListHead = Node;
2675
2676 FolderListTail = Node;
2677
2678 return Node;
2679 }
2680
2681
2682 PCFFILE_NODE CCabinet::NewFileNode()
2683 /*
2684 * FUNCTION: Creates a new file node
2685 * ARGUMENTS:
2686 * FolderNode = Pointer to folder node to bind file to
2687 * RETURNS:
2688 * Pointer to node if there was enough free memory available, otherwise NULL
2689 */
2690 {
2691 PCFFILE_NODE Node;
2692
2693 Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE));
2694 if (!Node)
2695 return NULL;
2696
2697 memset(Node, 0, sizeof(CFFILE_NODE));
2698
2699 Node->Prev = FileListTail;
2700
2701 if (FileListTail != NULL)
2702 FileListTail->Next = Node;
2703 else
2704 FileListHead = Node;
2705
2706 FileListTail = Node;
2707
2708 return Node;
2709 }
2710
2711
2712 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode)
2713 /*
2714 * FUNCTION: Creates a new data block node
2715 * ARGUMENTS:
2716 * FolderNode = Pointer to folder node to bind data block to
2717 * RETURNS:
2718 * Pointer to node if there was enough free memory available, otherwise NULL
2719 */
2720 {
2721 PCFDATA_NODE Node;
2722
2723 Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE));
2724 if (!Node)
2725 return NULL;
2726
2727 memset(Node, 0, sizeof(CFDATA_NODE));
2728
2729 Node->Prev = FolderNode->DataListTail;
2730
2731 if (FolderNode->DataListTail != NULL)
2732 FolderNode->DataListTail->Next = Node;
2733 else
2734 FolderNode->DataListHead = Node;
2735
2736 FolderNode->DataListTail = Node;
2737
2738 return Node;
2739 }
2740
2741
2742 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode)
2743 /*
2744 * FUNCTION: Destroys data block nodes bound to a folder node
2745 * ARGUMENTS:
2746 * FolderNode = Pointer to folder node
2747 */
2748 {
2749 PCFDATA_NODE PrevNode;
2750 PCFDATA_NODE NextNode;
2751
2752 NextNode = FolderNode->DataListHead;
2753 while (NextNode != NULL)
2754 {
2755 PrevNode = NextNode->Next;
2756 FreeMemory(NextNode);
2757 NextNode = PrevNode;
2758 }
2759 FolderNode->DataListHead = NULL;
2760 FolderNode->DataListTail = NULL;
2761 }
2762
2763
2764 void CCabinet::DestroyFileNodes()
2765 /*
2766 * FUNCTION: Destroys file nodes
2767 */
2768 {
2769 PCFFILE_NODE PrevNode;
2770 PCFFILE_NODE NextNode;
2771
2772 NextNode = FileListHead;
2773 while (NextNode != NULL)
2774 {
2775 PrevNode = NextNode->Next;
2776 if (NextNode->FileName)
2777 FreeMemory(NextNode->FileName);
2778 FreeMemory(NextNode);
2779 NextNode = PrevNode;
2780 }
2781 FileListHead = NULL;
2782 FileListTail = NULL;
2783 }
2784
2785
2786 void CCabinet::DestroyDeletedFileNodes()
2787 /*
2788 * FUNCTION: Destroys file nodes that are marked for deletion
2789 */
2790 {
2791 PCFFILE_NODE CurNode;
2792 PCFFILE_NODE NextNode;
2793
2794 CurNode = FileListHead;
2795 while (CurNode != NULL)
2796 {
2797 NextNode = CurNode->Next;
2798
2799 if (CurNode->Delete)
2800 {
2801 if (CurNode->Prev != NULL)
2802 CurNode->Prev->Next = CurNode->Next;
2803 else
2804 {
2805 FileListHead = CurNode->Next;
2806 if (FileListHead)
2807 FileListHead->Prev = NULL;
2808 }
2809
2810 if (CurNode->Next != NULL)
2811 CurNode->Next->Prev = CurNode->Prev;
2812 else
2813 {
2814 FileListTail = CurNode->Prev;
2815 if (FileListTail)
2816 FileListTail->Next = NULL;
2817 }
2818
2819 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName));
2820
2821 TotalFileSize -= (sizeof(CFFILE) + (ULONG)strlen(GetFileName(CurNode->FileName)) + 1);
2822
2823 if (CurNode->FileName)
2824 FreeMemory(CurNode->FileName);
2825 FreeMemory(CurNode);
2826 }
2827 CurNode = NextNode;
2828 }
2829 }
2830
2831
2832 void CCabinet::DestroyFolderNodes()
2833 /*
2834 * FUNCTION: Destroys folder nodes
2835 */
2836 {
2837 PCFFOLDER_NODE PrevNode;
2838 PCFFOLDER_NODE NextNode;
2839
2840 NextNode = FolderListHead;
2841 while (NextNode != NULL)
2842 {
2843 PrevNode = NextNode->Next;
2844 DestroyDataNodes(NextNode);
2845 FreeMemory(NextNode);
2846 NextNode = PrevNode;
2847 }
2848 FolderListHead = NULL;
2849 FolderListTail = NULL;
2850 }
2851
2852
2853 void CCabinet::DestroyDeletedFolderNodes()
2854 /*
2855 * FUNCTION: Destroys folder nodes that are marked for deletion
2856 */
2857 {
2858 PCFFOLDER_NODE CurNode;
2859 PCFFOLDER_NODE NextNode;
2860
2861 CurNode = FolderListHead;
2862 while (CurNode != NULL)
2863 {
2864 NextNode = CurNode->Next;
2865
2866 if (CurNode->Delete)
2867 {
2868 if (CurNode->Prev != NULL)
2869 CurNode->Prev->Next = CurNode->Next;
2870 else
2871 {
2872 FolderListHead = CurNode->Next;
2873 if (FolderListHead)
2874 FolderListHead->Prev = NULL;
2875 }
2876
2877 if (CurNode->Next != NULL)
2878 CurNode->Next->Prev = CurNode->Prev;
2879 else
2880 {
2881 FolderListTail = CurNode->Prev;
2882 if (FolderListTail)
2883 FolderListTail->Next = NULL;
2884 }
2885
2886 DestroyDataNodes(CurNode);
2887 FreeMemory(CurNode);
2888
2889 TotalFolderSize -= sizeof(CFFOLDER);
2890 }
2891 CurNode = NextNode;
2892 }
2893 }
2894
2895
2896 ULONG CCabinet::ComputeChecksum(void* Buffer,
2897 ULONG Size,
2898 ULONG Seed)
2899 /*
2900 * FUNCTION: Computes checksum for data block
2901 * ARGUMENTS:
2902 * Buffer = Pointer to data buffer
2903 * Size = Length of data buffer
2904 * Seed = Previously computed checksum
2905 * RETURNS:
2906 * Checksum of buffer
2907 */
2908 {
2909 int UlongCount; // Number of ULONGs in block
2910 ULONG Checksum; // Checksum accumulator
2911 unsigned char* pb;
2912 ULONG ul;
2913
2914 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE
2915 won't accept checksums computed by this routine */
2916
2917 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer, (UINT)Size));
2918
2919 UlongCount = Size / 4; // Number of ULONGs
2920 Checksum = Seed; // Init checksum
2921 pb = (unsigned char*)Buffer; // Start at front of data block
2922
2923 /* Checksum integral multiple of ULONGs */
2924 while (UlongCount-- > 0)
2925 {
2926 /* NOTE: Build ULONG in big/little-endian independent manner */
2927 ul = *pb++; // Get low-order byte
2928 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2929 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte
2930 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte
2931
2932 Checksum ^= ul; // Update checksum
2933 }
2934
2935 /* Checksum remainder bytes */
2936 ul = 0;
2937 switch (Size % 4)
2938 {
2939 case 3:
2940 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte
2941 case 2:
2942 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte
2943 case 1:
2944 ul |= *pb++; // Get low-order byte
2945 default:
2946 break;
2947 }
2948 Checksum ^= ul; // Update checksum
2949
2950 /* Return computed checksum */
2951 return Checksum;
2952 }
2953
2954
2955 ULONG CCabinet::ReadBlock(void* Buffer,
2956 ULONG Size,
2957 PULONG BytesRead)
2958 /*
2959 * FUNCTION: Read a block of data from file
2960 * ARGUMENTS:
2961 * Buffer = Pointer to data buffer
2962 * Size = Length of data buffer
2963 * BytesRead = Pointer to ULONG that on return will contain
2964 * number of bytes read
2965 * RETURNS:
2966 * Status of operation
2967 */
2968 {
2969 if (!ReadFileData(FileHandle, Buffer, Size, BytesRead))
2970 return CAB_STATUS_INVALID_CAB;
2971 return CAB_STATUS_SUCCESS;
2972 }
2973
2974 bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern)
2975 /*
2976 * FUNCTION: Matches a wildcard character pattern against a file
2977 * ARGUMENTS:
2978 * FileName = The file name to check
2979 * Pattern = The pattern
2980 * RETURNS:
2981 * Whether the pattern matches the file
2982 *
2983 * COPYRIGHT:
2984 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version.
2985 * Adapted from code written by Ingo Wilken.
2986 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup
2987 */
2988 {
2989 char* retryPattern = NULL;
2990 char* retryFileName = NULL;
2991 char ch;
2992
2993 while (*FileName || *Pattern)
2994 {
2995 ch = *Pattern++;
2996
2997 switch (ch)
2998 {
2999 case '*':
3000 retryPattern = Pattern;
3001 retryFileName = FileName;
3002 break;
3003
3004 case '?':
3005 if (*FileName++ == '\0')
3006 return false;
3007
3008 break;
3009
3010 default:
3011 if (*FileName == ch)
3012 {
3013 if (*FileName)
3014 FileName++;
3015 break;
3016 }
3017
3018 if (*FileName)
3019 {
3020 Pattern = retryPattern;
3021 FileName = ++retryFileName;
3022 break;
3023 }
3024
3025 return false;
3026 }
3027
3028 if (!Pattern)
3029 return false;
3030 }
3031
3032 return true;
3033 }
3034
3035 #ifndef CAB_READ_ONLY
3036
3037 ULONG CCabinet::InitCabinetHeader()
3038 /*
3039 * FUNCTION: Initializes cabinet header and optional fields
3040 * RETURNS:
3041 * Status of operation
3042 */
3043 {
3044 ULONG TotalSize;
3045 ULONG Size;
3046
3047 CABHeader.FileTableOffset = 0; // Not known yet
3048 CABHeader.FolderCount = 0; // Not known yet
3049 CABHeader.FileCount = 0; // Not known yet
3050 CABHeader.Flags = 0; // Not known yet
3051
3052 CABHeader.CabinetNumber = (USHORT)CurrentDiskNumber;
3053
3054 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev)))
3055 {
3056 CABHeader.Flags |= CAB_FLAG_HASPREV;
3057 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev))
3058 strcpy(CabinetPrev, "");
3059 }
3060
3061 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext))
3062 {
3063 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3064 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext))
3065 strcpy(DiskNext, "");
3066 }
3067
3068 TotalSize = 0;
3069
3070 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3071 {
3072
3073 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3074
3075 /* Calculate size of name of previous cabinet */
3076 TotalSize += (ULONG)strlen(CabinetPrev) + 1;
3077
3078 /* Calculate size of label of previous disk */
3079 TotalSize += (ULONG)strlen(DiskPrev) + 1;
3080 }
3081
3082 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3083 {
3084
3085 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3086
3087 /* Calculate size of name of next cabinet */
3088 Size = (ULONG)strlen(CabinetNext) + 1;
3089 TotalSize += Size;
3090 NextFieldsSize = Size;
3091
3092 /* Calculate size of label of next disk */
3093 Size = (ULONG)strlen(DiskNext) + 1;
3094 TotalSize += Size;
3095 NextFieldsSize += Size;
3096 }
3097 else
3098 NextFieldsSize = 0;
3099
3100 /* Add cabinet reserved area size if present */
3101 if (CabinetReservedFileSize > 0)
3102 {
3103 CABHeader.Flags |= CAB_FLAG_RESERVE;
3104 TotalSize += CabinetReservedFileSize;
3105 TotalSize += sizeof(ULONG); /* For CabinetResSize, FolderResSize, and FileResSize fields */
3106 }
3107
3108 DiskSize += TotalSize;
3109
3110 TotalHeaderSize = sizeof(CFHEADER) + TotalSize;
3111
3112 return CAB_STATUS_SUCCESS;
3113 }
3114
3115
3116 ULONG CCabinet::WriteCabinetHeader(bool MoreDisks)
3117 /*
3118 * FUNCTION: Writes the cabinet header and optional fields
3119 * ARGUMENTS:
3120 * MoreDisks = true if next cabinet name should be included
3121 * RETURNS:
3122 * Status of operation
3123 */
3124 {
3125 PCFFOLDER_NODE FolderNode;
3126 PCFFILE_NODE FileNode;
3127 ULONG BytesWritten;
3128 ULONG Size;
3129
3130 if (MoreDisks)
3131 {
3132 CABHeader.Flags |= CAB_FLAG_HASNEXT;
3133 Size = TotalHeaderSize;
3134 }
3135 else
3136 {
3137 CABHeader.Flags &= ~CAB_FLAG_HASNEXT;
3138 DiskSize -= NextFieldsSize;
3139 Size = TotalHeaderSize - NextFieldsSize;
3140 }
3141
3142 /* Set absolute folder offsets */
3143 BytesWritten = Size + TotalFolderSize + TotalFileSize;
3144 CABHeader.FolderCount = 0;
3145 FolderNode = FolderListHead;
3146 while (FolderNode != NULL)
3147 {
3148 FolderNode->Folder.DataOffset = BytesWritten;
3149
3150 BytesWritten += FolderNode->TotalFolderSize;
3151
3152 CABHeader.FolderCount++;
3153
3154 FolderNode = FolderNode->Next;
3155 }
3156
3157 /* Set absolute offset of file table */
3158 CABHeader.FileTableOffset = Size + TotalFolderSize;
3159
3160 /* Count number of files to be committed */
3161 CABHeader.FileCount = 0;
3162 FileNode = FileListHead;
3163 while (FileNode != NULL)
3164 {
3165 if (FileNode->Commit)
3166 CABHeader.FileCount++;
3167 FileNode = FileNode->Next;
3168 }
3169
3170 CABHeader.CabinetSize = DiskSize;
3171
3172 /* Write header */
3173 #if defined(WIN32)
3174 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), (LPDWORD)&BytesWritten, NULL))
3175 {
3176 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3177 return CAB_STATUS_CANNOT_WRITE;
3178 }
3179 #else
3180 BytesWritten = sizeof(CFHEADER);
3181 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1)
3182 {
3183 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3184 return CAB_STATUS_CANNOT_WRITE;
3185 }
3186 #endif
3187
3188 /* Write per-cabinet reserved area if present */
3189 if (CABHeader.Flags & CAB_FLAG_RESERVE)
3190 {
3191 ULONG ReservedSize;
3192
3193 ReservedSize = CabinetReservedFileSize & 0xffff;
3194 ReservedSize |= (0 << 16); /* Folder reserved area size */
3195 ReservedSize |= (0 << 24); /* Folder reserved area size */
3196 #if defined(WIN32)
3197 if (!WriteFile(FileHandle, &ReservedSize, sizeof(ULONG), (LPDWORD)&BytesWritten, NULL))
3198 {
3199 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3200 return CAB_STATUS_CANNOT_WRITE;
3201 }
3202 #else
3203 BytesWritten = sizeof(ULONG);
3204 if (fwrite(&ReservedSize, sizeof(ULONG), 1, FileHandle) < 1)
3205 {
3206 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3207 return CAB_STATUS_CANNOT_WRITE;
3208 }
3209 #endif
3210
3211 #if defined(WIN32)
3212 if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, (LPDWORD)&BytesWritten, NULL))
3213 {
3214 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3215 return CAB_STATUS_CANNOT_WRITE;
3216 }
3217 #else
3218 BytesWritten = CabinetReservedFileSize;
3219 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1)
3220 {
3221 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3222 return CAB_STATUS_CANNOT_WRITE;
3223 }
3224 #endif
3225 }
3226
3227 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0)
3228 {
3229 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev));
3230
3231 /* Write name of previous cabinet */
3232 Size = (ULONG)strlen(CabinetPrev) + 1;
3233 #if defined(WIN32)
3234 if (!WriteFile(FileHandle, CabinetPrev, Size, (LPDWORD)&BytesWritten, NULL))
3235 {
3236 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3237 return CAB_STATUS_CANNOT_WRITE;
3238 }
3239 #else
3240 BytesWritten = Size;
3241 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1)
3242 {
3243 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3244 return CAB_STATUS_CANNOT_WRITE;
3245 }
3246 #endif
3247
3248 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev));
3249
3250 /* Write label of previous disk */
3251 Size = (ULONG)strlen(DiskPrev) + 1;
3252 #if defined(WIN32)
3253 if (!WriteFile(FileHandle, DiskPrev, Size, (LPDWORD)&BytesWritten, NULL))
3254 {
3255 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3256 return CAB_STATUS_CANNOT_WRITE;
3257 }
3258 #else
3259 BytesWritten = Size;
3260 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1)
3261 {
3262 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3263 return CAB_STATUS_CANNOT_WRITE;
3264 }
3265 #endif
3266 }
3267
3268 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0)
3269 {
3270 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext));
3271
3272 /* Write name of next cabinet */
3273 Size = (ULONG)strlen(CabinetNext) + 1;
3274 #if defined(WIN32)
3275 if (!WriteFile(FileHandle, CabinetNext, Size, (LPDWORD)&BytesWritten, NULL))
3276 {
3277 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3278 return CAB_STATUS_CANNOT_WRITE;
3279 }
3280 #else
3281 BytesWritten = Size;
3282 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1)
3283 {
3284 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3285 return CAB_STATUS_CANNOT_WRITE;
3286 }
3287 #endif
3288
3289 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext));
3290
3291 /* Write label of next disk */
3292 Size = (ULONG)strlen(DiskNext) + 1;
3293 #if defined(WIN32)
3294 if (!WriteFile(FileHandle, DiskNext, Size, (LPDWORD)&BytesWritten, NULL))
3295 {
3296 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3297 return CAB_STATUS_CANNOT_WRITE;
3298 }
3299 #else
3300 BytesWritten = Size;
3301 if (fwrite(DiskNext, Size, 1, FileHandle) < 1)
3302 {
3303 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3304 return CAB_STATUS_CANNOT_WRITE;
3305 }
3306 #endif
3307 }
3308
3309 return CAB_STATUS_SUCCESS;
3310 }
3311
3312
3313 ULONG CCabinet::WriteFolderEntries()
3314 /*
3315 * FUNCTION: Writes folder entries
3316 * RETURNS:
3317 * Status of operation
3318 */
3319 {
3320 PCFFOLDER_NODE FolderNode;
3321 ULONG BytesWritten;
3322
3323 DPRINT(MAX_TRACE, ("Writing folder table.\n"));
3324
3325 FolderNode = FolderListHead;
3326 while (FolderNode != NULL)
3327 {
3328 if (FolderNode->Commit)
3329 {
3330 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n",
3331 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, (UINT)FolderNode->Folder.DataOffset));
3332
3333 #if defined(WIN32)
3334 if (!WriteFile(FileHandle,
3335 &FolderNode->Folder,
3336 sizeof(CFFOLDER),
3337 (LPDWORD)&BytesWritten,
3338 NULL))
3339 {
3340 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3341 return CAB_STATUS_CANNOT_WRITE;
3342 }
3343 #else
3344 BytesWritten = sizeof(CFFOLDER);
3345 if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1)
3346 {
3347 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3348 return CAB_STATUS_CANNOT_WRITE;
3349 }
3350 #endif
3351 }
3352 FolderNode = FolderNode->Next;
3353 }
3354
3355 return CAB_STATUS_SUCCESS;
3356 }
3357
3358
3359 ULONG CCabinet::WriteFileEntries()
3360 /*
3361 * FUNCTION: Writes file entries for all files
3362 * RETURNS:
3363 * Status of operation
3364 */
3365 {
3366 PCFFILE_NODE File;
3367 ULONG BytesWritten;
3368 bool SetCont = false;
3369
3370 DPRINT(MAX_TRACE, ("Writing file table.\n"));
3371
3372 File = FileListHead;
3373 while (File != NULL)
3374 {
3375 if (File->Commit)
3376 {
3377 /* Remove any continued files that ends in this disk */
3378 if (File->File.FileControlID == CAB_FILE_CONTINUED)
3379 File->Delete = true;
3380
3381 /* The file could end in the last (split) block and should therefore
3382 appear in the next disk too */
3383
3384 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) &&
3385 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit))
3386 {
3387 File->File.FileControlID = CAB_FILE_SPLIT;
3388 File->Delete = false;
3389 SetCont = true;
3390 }
3391
3392 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n",
3393 File->File.FileControlID, (UINT)File->File.FileOffset, (UINT)File->File.FileSize, File->FileName));
3394
3395 #if defined(WIN32)
3396 if (!WriteFile(FileHandle,
3397 &File->File,
3398 sizeof(CFFILE),
3399 (LPDWORD)&BytesWritten,
3400 NULL))
3401 return CAB_STATUS_CANNOT_WRITE;
3402 #else
3403 BytesWritten = sizeof(CFFILE);
3404 if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1)
3405 {
3406 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3407 return CAB_STATUS_CANNOT_WRITE;
3408 }
3409 #endif
3410
3411 #if defined(WIN32)
3412 if (!WriteFile(FileHandle,
3413 GetFileName(File->FileName),
3414 (DWORD)strlen(GetFileName(File->FileName)) + 1,
3415 (LPDWORD)&BytesWritten,
3416 NULL))
3417 return CAB_STATUS_CANNOT_WRITE;
3418 #else
3419 BytesWritten = strlen(GetFileName(File->FileName)) + 1;
3420 if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1)
3421 {
3422 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3423 return CAB_STATUS_CANNOT_WRITE;
3424 }
3425 #endif
3426
3427 if (SetCont)
3428 {
3429 File->File.FileControlID = CAB_FILE_CONTINUED;
3430 SetCont = false;
3431 }
3432 }
3433
3434 File = File->Next;
3435 }
3436 return CAB_STATUS_SUCCESS;
3437 }
3438
3439
3440 ULONG CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode)
3441 /*
3442 * FUNCTION: Writes data blocks to the cabinet
3443 * ARGUMENTS:
3444 * FolderNode = Pointer to folder node containing the data blocks
3445 * RETURNS:
3446 * Status of operation
3447 */
3448 {
3449 PCFDATA_NODE DataNode;
3450 ULONG BytesWritten;
3451 ULONG BytesRead;
3452 ULONG Status;
3453
3454 DataNode = FolderNode->DataListHead;
3455 if (DataNode != NULL)
3456 Status = ScratchFile->Seek(DataNode->ScratchFilePosition);
3457
3458 while (DataNode != NULL)
3459 {
3460 DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n",
3461 (UINT)DataNode->ScratchFilePosition,
3462 DataNode->Data.CompSize,
3463 DataNode->Data.UncompSize));
3464
3465 /* InputBuffer is free for us to use here, so we use it and avoid a
3466 memory allocation. OutputBuffer can't be used here because it may
3467 still contain valid data (if a data block spans two or more disks) */
3468 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead);
3469 if (Status != CAB_STATUS_SUCCESS)
3470 {
3471 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%u).\n", (UINT)Status));
3472 return Status;
3473 }
3474
3475 #if defined(WIN32)
3476 if (!WriteFile(FileHandle, &DataNode->Data,
3477 sizeof(CFDATA), (LPDWORD)&BytesWritten, NULL))
3478 {
3479 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3480 return CAB_STATUS_CANNOT_WRITE;
3481 }
3482 #else
3483 BytesWritten = sizeof(CFDATA);
3484 if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1)
3485 {
3486 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3487 return CAB_STATUS_CANNOT_WRITE;
3488 }
3489 #endif
3490
3491 #if defined(WIN32)
3492 if (!WriteFile(FileHandle, InputBuffer,
3493 DataNode->Data.CompSize, (LPDWORD)&BytesWritten, NULL))
3494 {
3495 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3496 return CAB_STATUS_CANNOT_WRITE;
3497 }
3498 #else
3499 BytesWritten = DataNode->Data.CompSize;
3500 if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1)
3501 {
3502 DPRINT(MIN_TRACE, ("Cannot write to file.\n"));
3503 return CAB_STATUS_CANNOT_WRITE;
3504 }
3505 #endif
3506
3507 DataNode = DataNode->Next;
3508 }
3509 return CAB_STATUS_SUCCESS;
3510 }
3511
3512
3513 ULONG CCabinet::WriteDataBlock()
3514 /*
3515 * FUNCTION: Writes the current data block to the scratch file
3516 * RETURNS:
3517 * Status of operation
3518 */
3519 {
3520 ULONG Status;
3521 ULONG BytesWritten;
3522 PCFDATA_NODE DataNode;
3523
3524 if (!BlockIsSplit)
3525 {
3526 Status = Codec->Compress(OutputBuffer,
3527 InputBuffer,
3528 CurrentIBufferSize,
3529 &TotalCompSize);
3530
3531 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n",
3532 (UINT)CurrentIBufferSize, (UINT)TotalCompSize));
3533
3534 CurrentOBuffer = OutputBuffer;
3535 CurrentOBufferSize = TotalCompSize;
3536 }
3537
3538 DataNode = NewDataNode(CurrentFolderNode);
3539 if (!DataNode)
3540 {
3541 DPRINT(MIN_TRACE, ("Insufficient memory.\n"));
3542 return CAB_STATUS_NOMEMORY;
3543 }
3544
3545 DiskSize += sizeof(CFDATA);
3546
3547 if (MaxDiskSize > 0)
3548 /* Disk size is limited */
3549 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize);
3550 else
3551 BlockIsSplit = false;
3552
3553 if (BlockIsSplit)
3554 {
3555 DataNode->Data.CompSize = (USHORT)(MaxDiskSize - DiskSize);
3556 DataNode->Data.UncompSize = 0;
3557 CreateNewDisk = true;
3558 }
3559 else
3560 {
3561 DataNode->Data.CompSize = (USHORT)CurrentOBufferSize;
3562 DataNode->Data.UncompSize = (USHORT)CurrentIBufferSize;
3563 }
3564
3565 DataNode->Data.Checksum = 0;
3566 DataNode->ScratchFilePosition = ScratchFile->Position();
3567
3568 // FIXME: MAKECAB.EXE does not like this checksum algorithm
3569 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0);
3570
3571 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n",
3572 (UINT)DataNode->Data.Checksum,
3573 DataNode->Data.CompSize,
3574 DataNode->Data.UncompSize));
3575
3576 Status = ScratchFile->WriteBlock(&DataNode->Data,
3577 CurrentOBuffer, &BytesWritten);
3578 if (Status != CAB_STATUS_SUCCESS)
3579 return Status;
3580
3581 DiskSize += BytesWritten;
3582
3583 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA));
3584 CurrentFolderNode->Folder.DataBlockCount++;
3585
3586 *(unsigned char**)&CurrentOBuffer += DataNode->Data.CompSize;
3587 CurrentOBufferSize -= DataNode->Data.CompSize;
3588
3589 LastBlockStart += DataNode->Data.UncompSize;
3590
3591 if (!BlockIsSplit)
3592 {
3593 CurrentIBufferSize = 0;
3594 CurrentIBuffer = InputBuffer;
3595 }
3596
3597 return CAB_STATUS_SUCCESS;
3598 }
3599
3600 #if !defined(WIN32)
3601
3602 void CCabinet::ConvertDateAndTime(time_t* Time,
3603 PUSHORT DosDate,
3604 PUSHORT DosTime)
3605 /*
3606 * FUNCTION: Returns file times of a file
3607 * ARGUMENTS:
3608 * FileHandle = File handle of file to get file times from
3609 * File = Pointer to CFFILE node for file
3610 * RETURNS:
3611 * Status of operation
3612 */
3613 {
3614 struct tm *timedef;
3615
3616 timedef = localtime(Time);
3617
3618 DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n",
3619 timedef->tm_mday, timedef->tm_mon, timedef->tm_year,
3620 timedef->tm_sec, timedef->tm_min, timedef->tm_hour));
3621
3622 *DosDate = ((timedef->tm_mday + 1) << 0)
3623 | ((timedef->tm_mon + 1) << 5)
3624 | (((timedef->tm_year + 1900) - 1980) << 9);
3625
3626 *DosTime = (timedef->tm_sec << 0)
3627 | (timedef->tm_min << 5)
3628 | (timedef->tm_hour << 11);
3629 }
3630
3631 #endif // !WIN32
3632
3633
3634 ULONG CCabinet::GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File)
3635 /*
3636 * FUNCTION: Returns file times of a file
3637 * ARGUMENTS:
3638 * FileHandle = File handle of file to get file times from
3639 * File = Pointer to CFFILE node for file
3640 * RETURNS:
3641 * Status of operation
3642 */
3643 {
3644 #if defined(WIN32)
3645 FILETIME FileTime;
3646
3647 if (GetFileTime(FileHandle, NULL, NULL, &FileTime))
3648 FileTimeToDosDateTime(&FileTime,
3649 &File->File.FileDate,
3650 &File->File.FileTime);
3651 #else
3652 struct stat stbuf;
3653 char buf[MAX_PATH];
3654
3655 // Check for an absolute path
3656 if (IsSeparator(File->FileName[0]))
3657 strcpy(buf, File->FileName);
3658 else
3659 {
3660 getcwd(buf, sizeof(buf));
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 getcwd(buf, sizeof(buf));
3704 strcat(buf, DIR_SEPARATOR_STRING);
3705 strcat(buf, File->FileName);
3706 }
3707
3708 if (stat(buf, &stbuf) == -1)
3709 return CAB_STATUS_CANNOT_READ;
3710
3711 #if 0
3712 File->File.Attributes |= CAB_ATTRIB_READONLY;
3713 File->File.Attributes |= CAB_ATTRIB_HIDDEN;
3714 File->File.Attributes |= CAB_ATTRIB_SYSTEM;
3715 #endif
3716
3717 if (stbuf.st_mode & S_IFDIR)
3718 File->File.Attributes |= CAB_ATTRIB_DIRECTORY;
3719
3720 File->File.Attributes |= CAB_ATTRIB_ARCHIVE;
3721
3722 #endif
3723 return CAB_STATUS_SUCCESS;
3724 }
3725
3726
3727 ULONG CCabinet::SetAttributesOnFile(char* FileName, USHORT FileAttributes)
3728 /*
3729 * FUNCTION: Sets attributes on a file
3730 * ARGUMENTS:
3731 * FileName = File name with path
3732 * FileAttributes = Attributes of that file
3733 * RETURNS:
3734 * Status of operation
3735 */
3736 {
3737 #if defined(WIN32)
3738 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE
3739 // The IDs for these attributes are the same in the CAB file and under Windows
3740 // If the file has any other attributes, strip them off by the logical AND.
3741 SetFileAttributes(FileName, (DWORD)(FileAttributes & 0x37));
3742
3743 return CAB_STATUS_SUCCESS;
3744 #else
3745 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n"));
3746 return CAB_STATUS_SUCCESS;
3747 #endif
3748 }
3749
3750 #endif /* CAB_READ_ONLY */
3751
3752 /* EOF */