Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencabinet.cxx
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS cabinet manager 00004 * FILE: tools/cabman/cabinet.cpp 00005 * PURPOSE: Cabinet routines 00006 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 00007 * Colin Finck <mail@colinfinck.de> 00008 * NOTES: Define CAB_READ_ONLY for read only version 00009 * REVISIONS: 00010 * CSH 21/03-2001 Created 00011 * CSH 15/08-2003 Made it portable 00012 * CF 04/05-2007 Made it compatible with 64-bit operating systems 00013 * TODO: 00014 * - Checksum of datablocks should be calculated 00015 * - EXTRACT.EXE complains if a disk is created manually 00016 * - Folders that are created manually and span disks will result in a damaged cabinet 00017 */ 00018 #include <stdio.h> 00019 #include <stdlib.h> 00020 #include <string.h> 00021 #if !defined(_WIN32) 00022 # include <dirent.h> 00023 # include <sys/stat.h> 00024 # include <sys/types.h> 00025 #endif 00026 #include "cabinet.h" 00027 #include "raw.h" 00028 #include "mszip.h" 00029 00030 #if defined(_WIN32) 00031 #define GetSizeOfFile(handle) _GetSizeOfFile(handle) 00032 static LONG _GetSizeOfFile(FILEHANDLE handle) 00033 { 00034 ULONG size = GetFileSize(handle, NULL); 00035 if (size == INVALID_FILE_SIZE) 00036 return -1; 00037 00038 return size; 00039 } 00040 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread) 00041 static bool _ReadFileData(FILEHANDLE handle, void* buffer, ULONG size, PULONG bytesread) 00042 { 00043 return ReadFile(handle, buffer, size, (LPDWORD)bytesread, NULL) != 0; 00044 } 00045 #else 00046 #define GetSizeOfFile(handle) _GetSizeOfFile(handle) 00047 static LONG _GetSizeOfFile(FILEHANDLE handle) 00048 { 00049 LONG size; 00050 fseek(handle, 0, SEEK_END); 00051 size = ftell(handle); 00052 fseek(handle, 0, SEEK_SET); 00053 return size; 00054 } 00055 #define ReadFileData(handle, buffer, size, bytesread) _ReadFileData(handle, buffer, size, bytesread) 00056 static bool _ReadFileData(FILEHANDLE handle, void* buffer, ULONG size, PULONG bytesread) 00057 { 00058 *bytesread = fread(buffer, 1, size, handle); 00059 return *bytesread == size; 00060 } 00061 #endif 00062 00063 #ifndef CAB_READ_ONLY 00064 00065 #if 0 00066 #if DBG 00067 00068 void DumpBuffer(void* Buffer, ULONG Size) 00069 { 00070 FILEHANDLE FileHandle; 00071 ULONG BytesWritten; 00072 00073 /* Create file, overwrite if it already exists */ 00074 FileHandle = CreateFile("dump.bin", // Create this file 00075 GENERIC_WRITE, // Open for writing 00076 0, // No sharing 00077 NULL, // No security 00078 CREATE_ALWAYS, // Create or overwrite 00079 FILE_ATTRIBUTE_NORMAL, // Normal file 00080 NULL); // No attribute template 00081 if (FileHandle == INVALID_HANDLE_VALUE) 00082 { 00083 DPRINT(MID_TRACE, ("ERROR OPENING '%u'.\n", (UINT)GetLastError())); 00084 return; 00085 } 00086 00087 if (!WriteFile(FileHandle, Buffer, Size, &BytesWritten, NULL)) 00088 { 00089 DPRINT(MID_TRACE, ("ERROR WRITING '%u'.\n", (UINT)GetLastError())); 00090 } 00091 00092 CloseFile(FileHandle); 00093 } 00094 00095 #endif /* DBG */ 00096 #endif 00097 00098 /* CCFDATAStorage */ 00099 00100 CCFDATAStorage::CCFDATAStorage() 00101 /* 00102 * FUNCTION: Default constructor 00103 */ 00104 { 00105 FileCreated = false; 00106 } 00107 00108 00109 CCFDATAStorage::~CCFDATAStorage() 00110 /* 00111 * FUNCTION: Default destructor 00112 */ 00113 { 00114 ASSERT(!FileCreated); 00115 } 00116 00117 00118 ULONG CCFDATAStorage::Create() 00119 /* 00120 * FUNCTION: Creates the file 00121 * ARGUMENTS: 00122 * FileName = Pointer to name of file 00123 * RETURNS: 00124 * Status of operation 00125 */ 00126 { 00127 #if defined(_WIN32) 00128 char tmpPath[MAX_PATH]; 00129 #endif 00130 ASSERT(!FileCreated); 00131 00132 #if defined(_WIN32) 00133 if (GetTempPath(MAX_PATH, tmpPath) == 0) 00134 return CAB_STATUS_CANNOT_CREATE; 00135 if(GetTempFileName(tmpPath, "cab", 0, FullName) == 0) 00136 return CAB_STATUS_CANNOT_CREATE; 00137 00138 /* Create file, overwrite if it already exists */ 00139 FileHandle = CreateFile(FullName, // Create this file 00140 GENERIC_READ | GENERIC_WRITE, // Open for reading/writing 00141 0, // No sharing 00142 NULL, // No security 00143 CREATE_ALWAYS, // Create or overwrite 00144 FILE_FLAG_SEQUENTIAL_SCAN | // Optimize for sequential scans 00145 FILE_FLAG_DELETE_ON_CLOSE | // Delete file when closed 00146 FILE_ATTRIBUTE_TEMPORARY, // Temporary file 00147 NULL); // No attribute template 00148 if (FileHandle == INVALID_HANDLE_VALUE) 00149 { 00150 DPRINT(MID_TRACE, ("ERROR '%u'.\n", (UINT)GetLastError())); 00151 return CAB_STATUS_CANNOT_CREATE; 00152 } 00153 #else /* !_WIN32 */ 00154 /*if (tmpnam(FullName) == NULL)*/ 00155 if ((FileHandle = tmpfile()) == NULL) 00156 return CAB_STATUS_CANNOT_CREATE; 00157 /* 00158 FileHandle = fopen(FullName, "w+b"); 00159 if (FileHandle == NULL) { 00160 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno)); 00161 return CAB_STATUS_CANNOT_CREATE; 00162 } 00163 */ 00164 #endif 00165 00166 FileCreated = true; 00167 00168 return CAB_STATUS_SUCCESS; 00169 } 00170 00171 00172 ULONG CCFDATAStorage::Destroy() 00173 /* 00174 * FUNCTION: Destroys the file 00175 * RETURNS: 00176 * Status of operation 00177 */ 00178 { 00179 ASSERT(FileCreated); 00180 00181 CloseFile(FileHandle); 00182 00183 FileCreated = false; 00184 00185 return CAB_STATUS_SUCCESS; 00186 } 00187 00188 00189 ULONG CCFDATAStorage::Truncate() 00190 /* 00191 * FUNCTION: Truncate the scratch file to zero bytes 00192 * RETURNS: 00193 * Status of operation 00194 */ 00195 { 00196 #if defined(_WIN32) 00197 if( SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 00198 return CAB_STATUS_FAILURE; 00199 if (!SetEndOfFile(FileHandle)) 00200 return CAB_STATUS_FAILURE; 00201 #else /* !_WIN32 */ 00202 fclose(FileHandle); 00203 FileHandle = tmpfile(); 00204 if (FileHandle == NULL) 00205 { 00206 DPRINT(MID_TRACE, ("ERROR '%i'.\n", errno)); 00207 return CAB_STATUS_FAILURE; 00208 } 00209 #endif 00210 return CAB_STATUS_SUCCESS; 00211 } 00212 00213 00214 ULONG CCFDATAStorage::Position() 00215 /* 00216 * FUNCTION: Returns current position in file 00217 * RETURNS: 00218 * Current position 00219 */ 00220 { 00221 #if defined(_WIN32) 00222 return SetFilePointer(FileHandle, 0, NULL, FILE_CURRENT); 00223 #else 00224 return (ULONG)ftell(FileHandle); 00225 #endif 00226 } 00227 00228 00229 ULONG CCFDATAStorage::Seek(LONG Position) 00230 /* 00231 * FUNCTION: Seeks to an absolute position 00232 * ARGUMENTS: 00233 * Position = Absolute position to seek to 00234 * RETURNS: 00235 * Status of operation 00236 */ 00237 { 00238 #if defined(_WIN32) 00239 if( SetFilePointer(FileHandle, 00240 Position, 00241 NULL, 00242 FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 00243 return CAB_STATUS_FAILURE; 00244 else 00245 return CAB_STATUS_SUCCESS; 00246 #else 00247 if (fseek(FileHandle, (off_t)Position, SEEK_SET) != 0) 00248 return CAB_STATUS_FAILURE; 00249 else 00250 return CAB_STATUS_SUCCESS; 00251 #endif 00252 } 00253 00254 00255 ULONG CCFDATAStorage::ReadBlock(PCFDATA Data, void* Buffer, PULONG BytesRead) 00256 /* 00257 * FUNCTION: Reads a CFDATA block from the file 00258 * ARGUMENTS: 00259 * Data = Pointer to CFDATA block for the buffer 00260 * Buffer = Pointer to buffer to store data read 00261 * BytesWritten = Pointer to buffer to write number of bytes read 00262 * RETURNS: 00263 * Status of operation 00264 */ 00265 { 00266 #if defined(_WIN32) 00267 if (!ReadFile(FileHandle, Buffer, Data->CompSize, (LPDWORD)BytesRead, NULL)) 00268 return CAB_STATUS_CANNOT_READ; 00269 #else 00270 00271 *BytesRead = fread(Buffer, 1, Data->CompSize, FileHandle); 00272 if (*BytesRead != Data->CompSize) 00273 return CAB_STATUS_CANNOT_READ; 00274 #endif 00275 return CAB_STATUS_SUCCESS; 00276 } 00277 00278 00279 ULONG CCFDATAStorage::WriteBlock(PCFDATA Data, void* Buffer, PULONG BytesWritten) 00280 /* 00281 * FUNCTION: Writes a CFDATA block to the file 00282 * ARGUMENTS: 00283 * Data = Pointer to CFDATA block for the buffer 00284 * Buffer = Pointer to buffer with data to write 00285 * BytesWritten = Pointer to buffer to write number of bytes written 00286 * RETURNS: 00287 * Status of operation 00288 */ 00289 { 00290 #if defined(_WIN32) 00291 if (!WriteFile(FileHandle, Buffer, Data->CompSize, (LPDWORD)BytesWritten, NULL)) 00292 return CAB_STATUS_CANNOT_WRITE; 00293 #else 00294 *BytesWritten = fwrite(Buffer, 1, Data->CompSize, FileHandle); 00295 if (*BytesWritten != Data->CompSize) 00296 return CAB_STATUS_CANNOT_WRITE; 00297 #endif 00298 return CAB_STATUS_SUCCESS; 00299 } 00300 00301 #endif /* CAB_READ_ONLY */ 00302 00303 00304 /* CCabinet */ 00305 00306 CCabinet::CCabinet() 00307 /* 00308 * FUNCTION: Default constructor 00309 */ 00310 { 00311 *CabinetName = '\0'; 00312 *CabinetPrev = '\0'; 00313 *DiskPrev = '\0'; 00314 *CabinetNext = '\0'; 00315 *DiskNext = '\0'; 00316 *DestPath = '\0'; 00317 *CabinetReservedFile = '\0'; 00318 00319 FileOpen = false; 00320 CabinetReservedFileBuffer = NULL; 00321 CabinetReservedFileSize = 0; 00322 00323 FolderListHead = NULL; 00324 FolderListTail = NULL; 00325 FileListHead = NULL; 00326 FileListTail = NULL; 00327 CriteriaListHead = NULL; 00328 CriteriaListTail = NULL; 00329 00330 Codec = NULL; 00331 CodecId = -1; 00332 CodecSelected = false; 00333 00334 OutputBuffer = NULL; 00335 InputBuffer = NULL; 00336 MaxDiskSize = 0; 00337 BlockIsSplit = false; 00338 ScratchFile = NULL; 00339 00340 FolderUncompSize = 0; 00341 BytesLeftInBlock = 0; 00342 ReuseBlock = false; 00343 CurrentDataNode = NULL; 00344 } 00345 00346 00347 CCabinet::~CCabinet() 00348 /* 00349 * FUNCTION: Default destructor 00350 */ 00351 { 00352 if (CabinetReservedFileBuffer != NULL) 00353 { 00354 FreeMemory(CabinetReservedFileBuffer); 00355 CabinetReservedFileBuffer = NULL; 00356 CabinetReservedFileSize = 0; 00357 } 00358 00359 if (CodecSelected) 00360 delete Codec; 00361 } 00362 00363 bool CCabinet::IsSeparator(char Char) 00364 /* 00365 * FUNCTION: Determines if a character is a separator 00366 * ARGUMENTS: 00367 * Char = Character to check 00368 * RETURNS: 00369 * Whether it is a separator 00370 */ 00371 { 00372 if ((Char == '\\') || (Char == '/')) 00373 return true; 00374 else 00375 return false; 00376 } 00377 00378 char* CCabinet::ConvertPath(char* Path, bool Allocate) 00379 /* 00380 * FUNCTION: Replaces \ or / with the one used by the host environment 00381 * ARGUMENTS: 00382 * Path = Pointer to string with pathname 00383 * Allocate = Specifies whether to allocate memory for the new 00384 * string or to change the existing buffer 00385 * RETURNS: 00386 * Pointer to new path 00387 */ 00388 { 00389 char *newpath; 00390 int i; 00391 00392 if (Allocate) 00393 newpath = strdup(Path); 00394 else 00395 newpath = Path; 00396 00397 i = 0; 00398 while (Path[i] != 0) 00399 { 00400 #if defined(_WIN32) 00401 if (Path[i] == '/') 00402 newpath[i] = '\\'; 00403 else 00404 #else 00405 if (Path[i] == '\\') 00406 newpath[i] = '/'; 00407 else 00408 #endif 00409 newpath[i] = Path[i]; 00410 00411 i++; 00412 } 00413 newpath[i] = 0; 00414 00415 return(newpath); 00416 } 00417 00418 00419 char* CCabinet::GetFileName(char* Path) 00420 /* 00421 * FUNCTION: Returns a pointer to file name 00422 * ARGUMENTS: 00423 * Path = Pointer to string with pathname 00424 * RETURNS: 00425 * Pointer to filename 00426 */ 00427 { 00428 ULONG i, j; 00429 00430 j = i = (Path[0] ? (Path[1] == ':' ? 2 : 0) : 0); 00431 00432 while (Path [i++]) 00433 if (IsSeparator(Path [i - 1])) 00434 j = i; 00435 00436 return Path + j; 00437 } 00438 00439 00440 void CCabinet::RemoveFileName(char* Path) 00441 /* 00442 * FUNCTION: Removes a file name from a path 00443 * ARGUMENTS: 00444 * Path = Pointer to string with path 00445 */ 00446 { 00447 char* FileName; 00448 ULONG i; 00449 00450 i = (Path [0] ? (Path[1] == ':' ? 2 : 0) : 0); 00451 FileName = GetFileName(Path + i); 00452 00453 if ((FileName != (Path + i)) && (IsSeparator(FileName [-1]))) 00454 FileName--; 00455 if ((FileName == (Path + i)) && (IsSeparator(FileName [0]))) 00456 FileName++; 00457 FileName[0] = 0; 00458 } 00459 00460 00461 bool CCabinet::NormalizePath(char* Path, 00462 ULONG Length) 00463 /* 00464 * FUNCTION: Normalizes a path 00465 * ARGUMENTS: 00466 * Path = Pointer to string with pathname 00467 * Length = Number of bytes in Path 00468 * RETURNS: 00469 * true if there was enough room in Path, or false 00470 */ 00471 { 00472 ULONG n; 00473 bool OK = true; 00474 00475 if ((n = (ULONG)strlen(Path)) && 00476 (!IsSeparator(Path[n - 1])) && 00477 (OK = ((n + 1) < Length))) 00478 { 00479 Path[n] = DIR_SEPARATOR_CHAR; 00480 Path[n + 1] = 0; 00481 } 00482 return OK; 00483 } 00484 00485 00486 char* CCabinet::GetCabinetName() 00487 /* 00488 * FUNCTION: Returns pointer to cabinet file name 00489 * RETURNS: 00490 * Pointer to string with name of cabinet 00491 */ 00492 { 00493 return CabinetName; 00494 } 00495 00496 00497 void CCabinet::SetCabinetName(char* FileName) 00498 /* 00499 * FUNCTION: Sets cabinet file name 00500 * ARGUMENTS: 00501 * FileName = Pointer to string with name of cabinet 00502 */ 00503 { 00504 strcpy(CabinetName, FileName); 00505 } 00506 00507 00508 void CCabinet::SetDestinationPath(char* DestinationPath) 00509 /* 00510 * FUNCTION: Sets destination path 00511 * ARGUMENTS: 00512 * DestinationPath = Pointer to string with name of destination path 00513 */ 00514 { 00515 strcpy(DestPath, DestinationPath); 00516 ConvertPath(DestPath, false); 00517 if (strlen(DestPath) > 0) 00518 NormalizePath(DestPath, PATH_MAX); 00519 } 00520 00521 ULONG CCabinet::AddSearchCriteria(char* SearchCriteria) 00522 /* 00523 * FUNCTION: Adds a criteria to the search criteria list 00524 * ARGUMENTS: 00525 * SearchCriteria = String with the search criteria to add 00526 * RETURNS: 00527 * Status of operation 00528 */ 00529 { 00530 PSEARCH_CRITERIA Criteria; 00531 00532 // Add the criteria to the list of search criteria 00533 Criteria = (PSEARCH_CRITERIA)AllocateMemory(sizeof(SEARCH_CRITERIA)); 00534 if(!Criteria) 00535 { 00536 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 00537 return CAB_STATUS_NOMEMORY; 00538 } 00539 00540 Criteria->Prev = CriteriaListTail; 00541 Criteria->Next = NULL; 00542 00543 if(CriteriaListTail) 00544 CriteriaListTail->Next = Criteria; 00545 else 00546 CriteriaListHead = Criteria; 00547 00548 CriteriaListTail = Criteria; 00549 00550 // Set the actual criteria string 00551 Criteria->Search = (char*)AllocateMemory(strlen(SearchCriteria) + 1); 00552 if (!Criteria->Search) 00553 { 00554 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 00555 return CAB_STATUS_NOMEMORY; 00556 } 00557 00558 strcpy(Criteria->Search, SearchCriteria); 00559 00560 return CAB_STATUS_SUCCESS; 00561 } 00562 00563 void CCabinet::DestroySearchCriteria() 00564 /* 00565 * FUNCTION: Destroys the list with the search criteria 00566 */ 00567 { 00568 PSEARCH_CRITERIA Criteria; 00569 PSEARCH_CRITERIA NextCriteria; 00570 00571 Criteria = CriteriaListHead; 00572 00573 while(Criteria) 00574 { 00575 NextCriteria = Criteria->Next; 00576 00577 FreeMemory(Criteria->Search); 00578 FreeMemory(Criteria); 00579 00580 Criteria = NextCriteria; 00581 } 00582 00583 CriteriaListHead = NULL; 00584 CriteriaListTail = NULL; 00585 } 00586 00587 bool CCabinet::HasSearchCriteria() 00588 /* 00589 * FUNCTION: Returns whether we have search criteria 00590 * RETURNS: 00591 * Whether we have search criteria or not. 00592 */ 00593 { 00594 return (CriteriaListHead != NULL); 00595 } 00596 00597 bool CCabinet::SetCompressionCodec(char* CodecName) 00598 /* 00599 * FUNCTION: Selects the codec to use for compression 00600 * ARGUMENTS: 00601 * CodecName = Pointer to a string with the name of the codec 00602 */ 00603 { 00604 if( !strcasecmp(CodecName, "raw") ) 00605 SelectCodec(CAB_CODEC_RAW); 00606 else if( !strcasecmp(CodecName, "mszip") ) 00607 SelectCodec(CAB_CODEC_MSZIP); 00608 else 00609 { 00610 printf("Invalid codec specified!\n"); 00611 return false; 00612 } 00613 00614 return true; 00615 } 00616 00617 char* CCabinet::GetDestinationPath() 00618 /* 00619 * FUNCTION: Returns destination path 00620 * RETURNS: 00621 * Pointer to string with name of destination path 00622 */ 00623 { 00624 return DestPath; 00625 } 00626 00627 00628 bool CCabinet::SetCabinetReservedFile(char* FileName) 00629 /* 00630 * FUNCTION: Sets cabinet reserved file 00631 * ARGUMENTS: 00632 * FileName = Pointer to string with name of cabinet reserved file 00633 */ 00634 { 00635 FILEHANDLE FileHandle; 00636 ULONG BytesRead; 00637 00638 #if defined(_WIN32) 00639 FileHandle = CreateFile(ConvertPath(FileName, true), // Open this file 00640 GENERIC_READ, // Open for reading 00641 FILE_SHARE_READ, // Share for reading 00642 NULL, // No security 00643 OPEN_EXISTING, // Existing file only 00644 FILE_ATTRIBUTE_NORMAL, // Normal file 00645 NULL); // No attribute template 00646 if (FileHandle == INVALID_HANDLE_VALUE) 00647 { 00648 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); 00649 return false; 00650 } 00651 #else /* !_WIN32 */ 00652 FileHandle = fopen(ConvertPath(FileName, true), "rb"); 00653 if (FileHandle == NULL) 00654 { 00655 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); 00656 return false; 00657 } 00658 #endif 00659 00660 CabinetReservedFileSize = GetSizeOfFile(FileHandle); 00661 if (CabinetReservedFileSize == (ULONG)-1) 00662 { 00663 DPRINT(MIN_TRACE, ("Cannot read from cabinet reserved file.\n")); 00664 return false; 00665 } 00666 00667 if (CabinetReservedFileSize == 0) 00668 { 00669 CloseFile(FileHandle); 00670 return false; 00671 } 00672 00673 CabinetReservedFileBuffer = AllocateMemory(CabinetReservedFileSize); 00674 if (!CabinetReservedFileBuffer) 00675 { 00676 CloseFile(FileHandle); 00677 return false; 00678 } 00679 00680 if (!ReadFileData(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, &BytesRead)) 00681 { 00682 CloseFile(FileHandle); 00683 return false; 00684 } 00685 00686 CloseFile(FileHandle); 00687 00688 strcpy(CabinetReservedFile, FileName); 00689 00690 return true; 00691 } 00692 00693 00694 char* CCabinet::GetCabinetReservedFile() 00695 /* 00696 * FUNCTION: Returns cabionet reserved file 00697 * RETURNS: 00698 * Pointer to string with name of cabinet reserved file 00699 */ 00700 { 00701 return CabinetReservedFile; 00702 } 00703 00704 00705 ULONG CCabinet::GetCurrentDiskNumber() 00706 /* 00707 * FUNCTION: Returns current disk number 00708 * RETURNS: 00709 * Current disk number 00710 */ 00711 { 00712 return CurrentDiskNumber; 00713 } 00714 00715 00716 ULONG CCabinet::Open() 00717 /* 00718 * FUNCTION: Opens a cabinet file 00719 * RETURNS: 00720 * Status of operation 00721 */ 00722 { 00723 PCFFOLDER_NODE FolderNode; 00724 ULONG Status; 00725 ULONG Index; 00726 00727 if (!FileOpen) 00728 { 00729 ULONG BytesRead; 00730 ULONG Size; 00731 00732 OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough 00733 if (!OutputBuffer) 00734 return CAB_STATUS_NOMEMORY; 00735 00736 #if defined(_WIN32) 00737 FileHandle = CreateFile(CabinetName, // Open this file 00738 GENERIC_READ, // Open for reading 00739 FILE_SHARE_READ, // Share for reading 00740 NULL, // No security 00741 OPEN_EXISTING, // Existing file only 00742 FILE_ATTRIBUTE_NORMAL, // Normal file 00743 NULL); // No attribute template 00744 00745 if (FileHandle == INVALID_HANDLE_VALUE) 00746 { 00747 DPRINT(MID_TRACE, ("Cannot open file.\n")); 00748 return CAB_STATUS_CANNOT_OPEN; 00749 } 00750 #else /* !_WIN32 */ 00751 FileHandle = fopen(CabinetName, "rb"); 00752 if (FileHandle == NULL) 00753 { 00754 DPRINT(MID_TRACE, ("Cannot open file.\n")); 00755 return CAB_STATUS_CANNOT_OPEN; 00756 } 00757 #endif 00758 00759 FileOpen = true; 00760 00761 /* Load CAB header */ 00762 if ((Status = ReadBlock(&CABHeader, sizeof(CFHEADER), &BytesRead)) 00763 != CAB_STATUS_SUCCESS) 00764 { 00765 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 00766 return CAB_STATUS_INVALID_CAB; 00767 } 00768 00769 /* Check header */ 00770 if ((BytesRead != sizeof(CFHEADER)) || 00771 (CABHeader.Signature != CAB_SIGNATURE ) || 00772 (CABHeader.Version != CAB_VERSION ) || 00773 (CABHeader.FolderCount == 0 ) || 00774 (CABHeader.FileCount == 0 ) || 00775 (CABHeader.FileTableOffset < sizeof(CFHEADER))) 00776 { 00777 CloseCabinet(); 00778 DPRINT(MID_TRACE, ("File has invalid header.\n")); 00779 return CAB_STATUS_INVALID_CAB; 00780 } 00781 00782 Size = 0; 00783 00784 /* Read/skip any reserved bytes */ 00785 if (CABHeader.Flags & CAB_FLAG_RESERVE) 00786 { 00787 if ((Status = ReadBlock(&Size, sizeof(ULONG), &BytesRead)) 00788 != CAB_STATUS_SUCCESS) 00789 { 00790 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 00791 return CAB_STATUS_INVALID_CAB; 00792 } 00793 CabinetReserved = Size & 0xFFFF; 00794 FolderReserved = (Size >> 16) & 0xFF; 00795 DataReserved = (Size >> 24) & 0xFF; 00796 00797 #if defined(_WIN32) 00798 if (SetFilePointer(FileHandle, CabinetReserved, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) 00799 { 00800 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 00801 return CAB_STATUS_FAILURE; 00802 } 00803 #else 00804 if (fseek(FileHandle, (off_t)CabinetReserved, SEEK_CUR) != 0) 00805 { 00806 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 00807 return CAB_STATUS_FAILURE; 00808 } 00809 #endif 00810 } 00811 00812 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 00813 { 00814 /* Read name of previous cabinet */ 00815 Status = ReadString(CabinetPrev, 256); 00816 if (Status != CAB_STATUS_SUCCESS) 00817 return Status; 00818 /* Read label of previous disk */ 00819 Status = ReadString(DiskPrev, 256); 00820 if (Status != CAB_STATUS_SUCCESS) 00821 return Status; 00822 } 00823 else 00824 { 00825 strcpy(CabinetPrev, ""); 00826 strcpy(DiskPrev, ""); 00827 } 00828 00829 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 00830 { 00831 /* Read name of next cabinet */ 00832 Status = ReadString(CabinetNext, 256); 00833 if (Status != CAB_STATUS_SUCCESS) 00834 return Status; 00835 /* Read label of next disk */ 00836 Status = ReadString(DiskNext, 256); 00837 if (Status != CAB_STATUS_SUCCESS) 00838 return Status; 00839 } 00840 else 00841 { 00842 strcpy(CabinetNext, ""); 00843 strcpy(DiskNext, ""); 00844 } 00845 00846 /* Read all folders */ 00847 for (Index = 0; Index < CABHeader.FolderCount; Index++) 00848 { 00849 FolderNode = NewFolderNode(); 00850 if (!FolderNode) 00851 { 00852 DPRINT(MIN_TRACE, ("Insufficient resources.\n")); 00853 return CAB_STATUS_NOMEMORY; 00854 } 00855 00856 if (Index == 0) 00857 FolderNode->UncompOffset = FolderUncompSize; 00858 00859 FolderNode->Index = Index; 00860 00861 if ((Status = ReadBlock(&FolderNode->Folder, 00862 sizeof(CFFOLDER), &BytesRead)) != CAB_STATUS_SUCCESS) 00863 { 00864 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 00865 return CAB_STATUS_INVALID_CAB; 00866 } 00867 } 00868 00869 /* Read file entries */ 00870 Status = ReadFileTable(); 00871 if (Status != CAB_STATUS_SUCCESS) 00872 { 00873 DPRINT(MIN_TRACE, ("ReadFileTable() failed (%u).\n", (UINT)Status)); 00874 return Status; 00875 } 00876 00877 /* Read data blocks for all folders */ 00878 FolderNode = FolderListHead; 00879 while (FolderNode != NULL) 00880 { 00881 Status = ReadDataBlocks(FolderNode); 00882 if (Status != CAB_STATUS_SUCCESS) 00883 { 00884 DPRINT(MIN_TRACE, ("ReadDataBlocks() failed (%u).\n", (UINT)Status)); 00885 return Status; 00886 } 00887 FolderNode = FolderNode->Next; 00888 } 00889 } 00890 return CAB_STATUS_SUCCESS; 00891 } 00892 00893 00894 void CCabinet::Close() 00895 /* 00896 * FUNCTION: Closes the cabinet file 00897 */ 00898 { 00899 if (FileOpen) 00900 { 00901 CloseFile(FileHandle); 00902 FileOpen = false; 00903 } 00904 } 00905 00906 00907 ULONG CCabinet::FindFirst(PCAB_SEARCH Search) 00908 /* 00909 * FUNCTION: Finds the first file in the cabinet that matches a search criteria 00910 * ARGUMENTS: 00911 * Search = Pointer to search structure 00912 * RETURNS: 00913 * Status of operation 00914 */ 00915 { 00916 RestartSearch = false; 00917 Search->Next = FileListHead; 00918 return FindNext(Search); 00919 } 00920 00921 00922 ULONG CCabinet::FindNext(PCAB_SEARCH Search) 00923 /* 00924 * FUNCTION: Finds next file in the cabinet that matches a search criteria 00925 * ARGUMENTS: 00926 * Search = Pointer to search structure 00927 * RETURNS: 00928 * Status of operation 00929 */ 00930 { 00931 bool bFound = false; 00932 PSEARCH_CRITERIA Criteria; 00933 ULONG Status; 00934 00935 if (RestartSearch) 00936 { 00937 Search->Next = FileListHead; 00938 00939 /* Skip split files already extracted */ 00940 while ((Search->Next) && 00941 (Search->Next->File.FileControlID > CAB_FILE_MAX_FOLDER) && 00942 (Search->Next->File.FileOffset <= LastFileOffset)) 00943 { 00944 DPRINT(MAX_TRACE, ("Skipping file (%s) FileOffset (0x%X) LastFileOffset (0x%X).\n", 00945 Search->Next->FileName, (UINT)Search->Next->File.FileOffset, (UINT)LastFileOffset)); 00946 Search->Next = Search->Next->Next; 00947 } 00948 00949 RestartSearch = false; 00950 } 00951 00952 /* Check each search criteria against each file */ 00953 while(Search->Next) 00954 { 00955 // Some features (like displaying cabinets) don't require search criteria, so we can just break here. 00956 // If a feature requires it, handle this in the ParseCmdline() function in "main.cxx". 00957 if(!CriteriaListHead) 00958 break; 00959 00960 Criteria = CriteriaListHead; 00961 00962 while(Criteria) 00963 { 00964 if(MatchFileNamePattern(Search->Next->FileName, Criteria->Search)) 00965 { 00966 bFound = true; 00967 break; 00968 } 00969 00970 Criteria = Criteria->Next; 00971 } 00972 00973 if(bFound) 00974 break; 00975 00976 Search->Next = Search->Next->Next; 00977 } 00978 00979 if (!Search->Next) 00980 { 00981 if (strlen(DiskNext) > 0) 00982 { 00983 CloseCabinet(); 00984 00985 SetCabinetName(CabinetNext); 00986 00987 OnDiskChange(CabinetNext, DiskNext); 00988 00989 Status = Open(); 00990 if (Status != CAB_STATUS_SUCCESS) 00991 return Status; 00992 00993 Search->Next = FileListHead; 00994 if (!Search->Next) 00995 return CAB_STATUS_NOFILE; 00996 } 00997 else 00998 return CAB_STATUS_NOFILE; 00999 } 01000 01001 Search->File = &Search->Next->File; 01002 Search->FileName = Search->Next->FileName; 01003 Search->Next = Search->Next->Next; 01004 return CAB_STATUS_SUCCESS; 01005 } 01006 01007 01008 ULONG CCabinet::ExtractFile(char* FileName) 01009 /* 01010 * FUNCTION: Extracts a file from the cabinet 01011 * ARGUMENTS: 01012 * FileName = Pointer to buffer with name of file 01013 * RETURNS 01014 * Status of operation 01015 */ 01016 { 01017 ULONG Size; 01018 ULONG Offset; 01019 ULONG BytesRead; 01020 ULONG BytesToRead; 01021 ULONG BytesWritten; 01022 ULONG BytesSkipped; 01023 ULONG BytesToWrite; 01024 ULONG TotalBytesRead; 01025 ULONG CurrentOffset; 01026 PUCHAR Buffer; 01027 PUCHAR CurrentBuffer; 01028 FILEHANDLE DestFile; 01029 PCFFILE_NODE File; 01030 CFDATA CFData; 01031 ULONG Status; 01032 bool Skip; 01033 #if defined(_WIN32) 01034 FILETIME FileTime; 01035 #endif 01036 CHAR DestName[PATH_MAX]; 01037 CHAR TempName[PATH_MAX]; 01038 01039 Status = LocateFile(FileName, &File); 01040 if (Status != CAB_STATUS_SUCCESS) 01041 { 01042 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status)); 01043 return Status; 01044 } 01045 01046 LastFileOffset = File->File.FileOffset; 01047 01048 switch (CurrentFolderNode->Folder.CompressionType & CAB_COMP_MASK) 01049 { 01050 case CAB_COMP_NONE: 01051 SelectCodec(CAB_CODEC_RAW); 01052 break; 01053 01054 case CAB_COMP_MSZIP: 01055 SelectCodec(CAB_CODEC_MSZIP); 01056 break; 01057 01058 default: 01059 return CAB_STATUS_UNSUPPCOMP; 01060 } 01061 01062 DPRINT(MAX_TRACE, ("Extracting file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n", 01063 (UINT)File->File.FileOffset, 01064 (UINT)File->File.FileSize, 01065 (UINT)File->DataBlock->AbsoluteOffset, 01066 (UINT)File->DataBlock->UncompOffset)); 01067 01068 strcpy(DestName, DestPath); 01069 strcat(DestName, FileName); 01070 01071 /* Create destination file, fail if it already exists */ 01072 #if defined(_WIN32) 01073 DestFile = CreateFile(DestName, // Create this file 01074 GENERIC_WRITE, // Open for writing 01075 0, // No sharing 01076 NULL, // No security 01077 CREATE_NEW, // New file only 01078 FILE_ATTRIBUTE_NORMAL, // Normal file 01079 NULL); // No attribute template 01080 if (DestFile == INVALID_HANDLE_VALUE) 01081 { 01082 /* If file exists, ask to overwrite file */ 01083 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) && 01084 (OnOverwrite(&File->File, FileName))) 01085 { 01086 /* Create destination file, overwrite if it already exists */ 01087 DestFile = CreateFile(DestName, // Create this file 01088 GENERIC_WRITE, // Open for writing 01089 0, // No sharing 01090 NULL, // No security 01091 TRUNCATE_EXISTING, // Truncate the file 01092 FILE_ATTRIBUTE_NORMAL, // Normal file 01093 NULL); // No attribute template 01094 if (DestFile == INVALID_HANDLE_VALUE) 01095 return CAB_STATUS_CANNOT_CREATE; 01096 } 01097 else 01098 { 01099 if (Status == ERROR_FILE_EXISTS) 01100 return CAB_STATUS_FILE_EXISTS; 01101 else 01102 return CAB_STATUS_CANNOT_CREATE; 01103 } 01104 } 01105 #else /* !_WIN32 */ 01106 DestFile = fopen(DestName, "rb"); 01107 if (DestFile != NULL) 01108 { 01109 fclose(DestFile); 01110 /* If file exists, ask to overwrite file */ 01111 if (OnOverwrite(&File->File, FileName)) 01112 { 01113 DestFile = fopen(DestName, "w+b"); 01114 if (DestFile == NULL) 01115 return CAB_STATUS_CANNOT_CREATE; 01116 } 01117 else 01118 return CAB_STATUS_FILE_EXISTS; 01119 } 01120 else 01121 { 01122 DestFile = fopen(DestName, "w+b"); 01123 if (DestFile == NULL) 01124 return CAB_STATUS_CANNOT_CREATE; 01125 } 01126 #endif 01127 #if defined(_WIN32) 01128 if (!DosDateTimeToFileTime(File->File.FileDate, File->File.FileTime, &FileTime)) 01129 { 01130 CloseFile(DestFile); 01131 DPRINT(MIN_TRACE, ("DosDateTimeToFileTime() failed (%u).\n", (UINT)GetLastError())); 01132 return CAB_STATUS_CANNOT_WRITE; 01133 } 01134 01135 SetFileTime(DestFile, NULL, &FileTime, NULL); 01136 #else 01137 //DPRINT(MIN_TRACE, ("FIXME: DosDateTimeToFileTime\n")); 01138 #endif 01139 SetAttributesOnFile(DestName, File->File.Attributes); 01140 01141 Buffer = (PUCHAR)AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough 01142 if (!Buffer) 01143 { 01144 CloseFile(DestFile); 01145 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 01146 return CAB_STATUS_NOMEMORY; 01147 } 01148 01149 /* Call OnExtract event handler */ 01150 OnExtract(&File->File, FileName); 01151 01152 /* Search to start of file */ 01153 #if defined(_WIN32) 01154 Offset = SetFilePointer(FileHandle, 01155 File->DataBlock->AbsoluteOffset, 01156 NULL, 01157 FILE_BEGIN); 01158 if (Offset == INVALID_SET_FILE_POINTER) 01159 { 01160 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 01161 CloseFile(DestFile); 01162 return CAB_STATUS_INVALID_CAB; 01163 } 01164 #else 01165 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) 01166 { 01167 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 01168 CloseFile(DestFile); 01169 return CAB_STATUS_FAILURE; 01170 } 01171 Offset = ftell(FileHandle); 01172 #endif 01173 01174 Size = File->File.FileSize; 01175 Offset = File->File.FileOffset; 01176 CurrentOffset = File->DataBlock->UncompOffset; 01177 01178 Skip = true; 01179 01180 ReuseBlock = (CurrentDataNode == File->DataBlock); 01181 if (Size > 0) 01182 { 01183 do 01184 { 01185 DPRINT(MAX_TRACE, ("CO (0x%X) ReuseBlock (%u) Offset (0x%X) Size (%d) BytesLeftInBlock (%d)\n", 01186 (UINT)File->DataBlock->UncompOffset, (UINT)ReuseBlock, (UINT)Offset, (UINT)Size, 01187 (UINT)BytesLeftInBlock)); 01188 01189 if (/*(CurrentDataNode != File->DataBlock) &&*/ (!ReuseBlock) || (BytesLeftInBlock <= 0)) 01190 { 01191 DPRINT(MAX_TRACE, ("Filling buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock)); 01192 01193 CurrentBuffer = Buffer; 01194 TotalBytesRead = 0; 01195 do 01196 { 01197 DPRINT(MAX_TRACE, ("Size (%u bytes).\n", (UINT)Size)); 01198 01199 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != 01200 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) 01201 { 01202 CloseFile(DestFile); 01203 FreeMemory(Buffer); 01204 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 01205 return CAB_STATUS_INVALID_CAB; 01206 } 01207 01208 DPRINT(MAX_TRACE, ("Data block: Checksum (0x%X) CompSize (%u bytes) UncompSize (%u bytes)\n", 01209 (UINT)CFData.Checksum, 01210 CFData.CompSize, 01211 CFData.UncompSize)); 01212 01213 ASSERT(CFData.CompSize <= CAB_BLOCKSIZE + 12); 01214 01215 BytesToRead = CFData.CompSize; 01216 01217 DPRINT(MAX_TRACE, ("Read: (0x%lX,0x%lX).\n", 01218 (unsigned long)CurrentBuffer, (unsigned long)Buffer)); 01219 01220 if (((Status = ReadBlock(CurrentBuffer, BytesToRead, &BytesRead)) != 01221 CAB_STATUS_SUCCESS) || (BytesToRead != BytesRead)) 01222 { 01223 CloseFile(DestFile); 01224 FreeMemory(Buffer); 01225 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 01226 return CAB_STATUS_INVALID_CAB; 01227 } 01228 01229 /* FIXME: Does not work with files generated by makecab.exe */ 01230 /* 01231 if (CFData.Checksum != 0) 01232 { 01233 ULONG Checksum = ComputeChecksum(CurrentBuffer, BytesRead, 0); 01234 if (Checksum != CFData.Checksum) 01235 { 01236 CloseFile(DestFile); 01237 FreeMemory(Buffer); 01238 DPRINT(MIN_TRACE, ("Bad checksum (is 0x%X, should be 0x%X).\n", 01239 Checksum, CFData.Checksum)); 01240 return CAB_STATUS_INVALID_CAB; 01241 } 01242 } 01243 */ 01244 TotalBytesRead += BytesRead; 01245 01246 CurrentBuffer += BytesRead; 01247 01248 if (CFData.UncompSize == 0) 01249 { 01250 if (strlen(DiskNext) == 0) 01251 return CAB_STATUS_NOFILE; 01252 01253 /* CloseCabinet() will destroy all file entries so in case 01254 FileName refers to the FileName field of a CFFOLDER_NODE 01255 structure, we have to save a copy of the filename */ 01256 strcpy(TempName, FileName); 01257 01258 CloseCabinet(); 01259 01260 SetCabinetName(CabinetNext); 01261 01262 OnDiskChange(CabinetNext, DiskNext); 01263 01264 Status = Open(); 01265 if (Status != CAB_STATUS_SUCCESS) 01266 return Status; 01267 01268 /* The first data block of the file will not be 01269 found as it is located in the previous file */ 01270 Status = LocateFile(TempName, &File); 01271 if (Status == CAB_STATUS_NOFILE) 01272 { 01273 DPRINT(MID_TRACE, ("Cannot locate file (%u).\n", (UINT)Status)); 01274 return Status; 01275 } 01276 01277 /* The file is continued in the first data block in the folder */ 01278 File->DataBlock = CurrentFolderNode->DataListHead; 01279 01280 /* Search to start of file */ 01281 #if defined(_WIN32) 01282 if( SetFilePointer(FileHandle, 01283 File->DataBlock->AbsoluteOffset, 01284 NULL, 01285 FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 01286 { 01287 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 01288 return CAB_STATUS_INVALID_CAB; 01289 } 01290 #else 01291 if (fseek(FileHandle, (off_t)File->DataBlock->AbsoluteOffset, SEEK_SET) != 0) 01292 { 01293 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 01294 return CAB_STATUS_INVALID_CAB; 01295 } 01296 #endif 01297 01298 DPRINT(MAX_TRACE, ("Continuing extraction of file at uncompressed offset (0x%X) Size (%u bytes) AO (0x%X) UO (0x%X).\n", 01299 (UINT)File->File.FileOffset, 01300 (UINT)File->File.FileSize, 01301 (UINT)File->DataBlock->AbsoluteOffset, 01302 (UINT)File->DataBlock->UncompOffset)); 01303 01304 CurrentDataNode = File->DataBlock; 01305 ReuseBlock = true; 01306 01307 RestartSearch = true; 01308 } 01309 } while (CFData.UncompSize == 0); 01310 01311 DPRINT(MAX_TRACE, ("TotalBytesRead (%u).\n", (UINT)TotalBytesRead)); 01312 01313 Status = Codec->Uncompress(OutputBuffer, Buffer, TotalBytesRead, &BytesToWrite); 01314 if (Status != CS_SUCCESS) 01315 { 01316 CloseFile(DestFile); 01317 FreeMemory(Buffer); 01318 DPRINT(MID_TRACE, ("Cannot uncompress block.\n")); 01319 if (Status == CS_NOMEMORY) 01320 return CAB_STATUS_NOMEMORY; 01321 return CAB_STATUS_INVALID_CAB; 01322 } 01323 01324 if (BytesToWrite != CFData.UncompSize) 01325 { 01326 DPRINT(MID_TRACE, ("BytesToWrite (%u) != CFData.UncompSize (%d)\n", 01327 (UINT)BytesToWrite, CFData.UncompSize)); 01328 return CAB_STATUS_INVALID_CAB; 01329 } 01330 01331 BytesLeftInBlock = BytesToWrite; 01332 } 01333 else 01334 { 01335 DPRINT(MAX_TRACE, ("Using same buffer. ReuseBlock (%u)\n", (UINT)ReuseBlock)); 01336 01337 BytesToWrite = BytesLeftInBlock; 01338 01339 DPRINT(MAX_TRACE, ("Seeking to absolute offset 0x%X.\n", 01340 (UINT)(CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + CurrentDataNode->Data.CompSize))); 01341 01342 if (((Status = ReadBlock(&CFData, sizeof(CFDATA), &BytesRead)) != 01343 CAB_STATUS_SUCCESS) || (BytesRead != sizeof(CFDATA))) 01344 { 01345 CloseFile(DestFile); 01346 FreeMemory(Buffer); 01347 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 01348 return CAB_STATUS_INVALID_CAB; 01349 } 01350 01351 DPRINT(MAX_TRACE, ("CFData.CompSize 0x%X CFData.UncompSize 0x%X.\n", 01352 CFData.CompSize, CFData.UncompSize)); 01353 01354 /* Go to next data block */ 01355 #if defined(_WIN32) 01356 if( SetFilePointer(FileHandle, 01357 CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + 01358 CurrentDataNode->Data.CompSize, 01359 NULL, 01360 FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 01361 { 01362 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 01363 return CAB_STATUS_INVALID_CAB; 01364 } 01365 #else 01366 if (fseek(FileHandle, (off_t)CurrentDataNode->AbsoluteOffset + sizeof(CFDATA) + 01367 CurrentDataNode->Data.CompSize, SEEK_SET) != 0) 01368 { 01369 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 01370 return CAB_STATUS_INVALID_CAB; 01371 } 01372 #endif 01373 01374 ReuseBlock = false; 01375 } 01376 01377 if (Skip) 01378 BytesSkipped = (Offset - CurrentOffset); 01379 else 01380 BytesSkipped = 0; 01381 01382 BytesToWrite -= BytesSkipped; 01383 01384 if (Size < BytesToWrite) 01385 BytesToWrite = Size; 01386 01387 DPRINT(MAX_TRACE, ("Offset (0x%X) CurrentOffset (0x%X) ToWrite (%u) Skipped (%u)(%u) Size (%u).\n", 01388 (UINT)Offset, 01389 (UINT)CurrentOffset, 01390 (UINT)BytesToWrite, 01391 (UINT)BytesSkipped, (UINT)Skip, 01392 (UINT)Size)); 01393 01394 #if defined(_WIN32) 01395 if (!WriteFile(DestFile, (void*)((PUCHAR)OutputBuffer + BytesSkipped), 01396 BytesToWrite, (LPDWORD)&BytesWritten, NULL) || 01397 (BytesToWrite != BytesWritten)) 01398 { 01399 DPRINT(MIN_TRACE, ("Status 0x%X.\n", (UINT)GetLastError())); 01400 #else 01401 BytesWritten = BytesToWrite; 01402 if (fwrite((void*)((PUCHAR)OutputBuffer + BytesSkipped), 01403 BytesToWrite, 1, DestFile) < 1) 01404 { 01405 #endif 01406 CloseFile(DestFile); 01407 FreeMemory(Buffer); 01408 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 01409 return CAB_STATUS_CANNOT_WRITE; 01410 } 01411 Size -= BytesToWrite; 01412 01413 CurrentOffset += BytesToWrite; 01414 01415 /* Don't skip any more bytes */ 01416 Skip = false; 01417 } while (Size > 0); 01418 } 01419 01420 CloseFile(DestFile); 01421 01422 FreeMemory(Buffer); 01423 01424 return CAB_STATUS_SUCCESS; 01425 } 01426 01427 bool CCabinet::IsCodecSelected() 01428 /* 01429 * FUNCTION: Returns the value of CodecSelected 01430 * RETURNS: 01431 * Whether a codec is selected 01432 */ 01433 { 01434 return CodecSelected; 01435 } 01436 01437 void CCabinet::SelectCodec(LONG Id) 01438 /* 01439 * FUNCTION: Selects codec engine to use 01440 * ARGUMENTS: 01441 * Id = Codec identifier 01442 */ 01443 { 01444 if (CodecSelected) 01445 { 01446 if (Id == CodecId) 01447 return; 01448 01449 CodecSelected = false; 01450 delete Codec; 01451 } 01452 01453 switch (Id) 01454 { 01455 case CAB_CODEC_RAW: 01456 Codec = new CRawCodec(); 01457 break; 01458 01459 case CAB_CODEC_MSZIP: 01460 Codec = new CMSZipCodec(); 01461 break; 01462 01463 default: 01464 return; 01465 } 01466 01467 CodecId = Id; 01468 CodecSelected = true; 01469 } 01470 01471 01472 #ifndef CAB_READ_ONLY 01473 01474 /* CAB write methods */ 01475 01476 ULONG CCabinet::NewCabinet() 01477 /* 01478 * FUNCTION: Creates a new cabinet 01479 * RETURNS: 01480 * Status of operation 01481 */ 01482 { 01483 ULONG Status; 01484 01485 CurrentDiskNumber = 0; 01486 01487 OutputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough 01488 InputBuffer = AllocateMemory(CAB_BLOCKSIZE + 12); // This should be enough 01489 if ((!OutputBuffer) || (!InputBuffer)) 01490 { 01491 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 01492 return CAB_STATUS_NOMEMORY; 01493 } 01494 CurrentIBuffer = InputBuffer; 01495 CurrentIBufferSize = 0; 01496 01497 CABHeader.Signature = CAB_SIGNATURE; 01498 CABHeader.Reserved1 = 0; // Not used 01499 CABHeader.CabinetSize = 0; // Not yet known 01500 CABHeader.Reserved2 = 0; // Not used 01501 CABHeader.Reserved3 = 0; // Not used 01502 CABHeader.Version = CAB_VERSION; 01503 CABHeader.FolderCount = 0; // Not yet known 01504 CABHeader.FileCount = 0; // Not yet known 01505 CABHeader.Flags = 0; // Not yet known 01506 // FIXME: Should be random 01507 CABHeader.SetID = 0x534F; 01508 CABHeader.CabinetNumber = 0; 01509 01510 01511 TotalFolderSize = 0; 01512 TotalFileSize = 0; 01513 01514 DiskSize = sizeof(CFHEADER); 01515 01516 InitCabinetHeader(); 01517 01518 // NextFolderNumber is 0-based 01519 NextFolderNumber = 0; 01520 01521 CurrentFolderNode = NULL; 01522 Status = NewFolder(); 01523 if (Status != CAB_STATUS_SUCCESS) 01524 return Status; 01525 01526 CurrentFolderNode->Folder.DataOffset = DiskSize - TotalHeaderSize; 01527 01528 ScratchFile = new CCFDATAStorage; 01529 if (!ScratchFile) 01530 { 01531 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 01532 return CAB_STATUS_NOMEMORY; 01533 } 01534 01535 Status = ScratchFile->Create(); 01536 01537 CreateNewFolder = false; 01538 01539 CreateNewDisk = false; 01540 01541 PrevCabinetNumber = 0; 01542 01543 return Status; 01544 } 01545 01546 01547 ULONG CCabinet::NewDisk() 01548 /* 01549 * FUNCTION: Forces a new disk to be created 01550 * RETURNS: 01551 * Status of operation 01552 */ 01553 { 01554 // NextFolderNumber is 0-based 01555 NextFolderNumber = 1; 01556 01557 CreateNewDisk = false; 01558 01559 DiskSize = sizeof(CFHEADER) + TotalFolderSize + TotalFileSize; 01560 01561 InitCabinetHeader(); 01562 01563 CurrentFolderNode->TotalFolderSize = 0; 01564 01565 CurrentFolderNode->Folder.DataBlockCount = 0; 01566 01567 return CAB_STATUS_SUCCESS; 01568 } 01569 01570 01571 ULONG CCabinet::NewFolder() 01572 /* 01573 * FUNCTION: Forces a new folder to be created 01574 * RETURNS: 01575 * Status of operation 01576 */ 01577 { 01578 DPRINT(MAX_TRACE, ("Creating new folder.\n")); 01579 01580 CurrentFolderNode = NewFolderNode(); 01581 if (!CurrentFolderNode) 01582 { 01583 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 01584 return CAB_STATUS_NOMEMORY; 01585 } 01586 01587 switch (CodecId) { 01588 case CAB_CODEC_RAW: 01589 CurrentFolderNode->Folder.CompressionType = CAB_COMP_NONE; 01590 break; 01591 01592 case CAB_CODEC_MSZIP: 01593 CurrentFolderNode->Folder.CompressionType = CAB_COMP_MSZIP; 01594 break; 01595 01596 default: 01597 return CAB_STATUS_UNSUPPCOMP; 01598 } 01599 01600 /* FIXME: This won't work if no files are added to the new folder */ 01601 01602 DiskSize += sizeof(CFFOLDER); 01603 01604 TotalFolderSize += sizeof(CFFOLDER); 01605 01606 NextFolderNumber++; 01607 01608 CABHeader.FolderCount++; 01609 01610 LastBlockStart = 0; 01611 01612 return CAB_STATUS_SUCCESS; 01613 } 01614 01615 01616 ULONG CCabinet::WriteFileToScratchStorage(PCFFILE_NODE FileNode) 01617 /* 01618 * FUNCTION: Writes a file to the scratch file 01619 * ARGUMENTS: 01620 * FileNode = Pointer to file node 01621 * RETURNS: 01622 * Status of operation 01623 */ 01624 { 01625 ULONG BytesToRead; 01626 ULONG BytesRead; 01627 ULONG Status; 01628 ULONG Size; 01629 01630 if (!ContinueFile) 01631 { 01632 /* Try to open file */ 01633 #if defined(_WIN32) 01634 SourceFile = CreateFile( 01635 FileNode->FileName, // Open this file 01636 GENERIC_READ, // Open for reading 01637 FILE_SHARE_READ, // Share for reading 01638 NULL, // No security 01639 OPEN_EXISTING, // File must exist 01640 FILE_ATTRIBUTE_NORMAL, // Normal file 01641 NULL); // No attribute template 01642 if (SourceFile == INVALID_HANDLE_VALUE) 01643 { 01644 DPRINT(MID_TRACE, ("File not found (%s).\n", FileNode->FileName)); 01645 return CAB_STATUS_NOFILE; 01646 } 01647 #else /* !_WIN32 */ 01648 SourceFile = fopen(FileNode->FileName, "rb"); 01649 if (SourceFile == NULL) 01650 { 01651 DPRINT(MID_TRACE, ("Cannot open cabinet reserved file.\n")); 01652 return CAB_STATUS_NOFILE; 01653 } 01654 #endif 01655 01656 if (CreateNewFolder) 01657 { 01658 /* There is always a new folder after 01659 a split file is completely stored */ 01660 Status = NewFolder(); 01661 if (Status != CAB_STATUS_SUCCESS) 01662 return Status; 01663 CreateNewFolder = false; 01664 } 01665 01666 /* Call OnAdd event handler */ 01667 OnAdd(&FileNode->File, FileNode->FileName); 01668 01669 TotalBytesLeft = FileNode->File.FileSize; 01670 01671 FileNode->File.FileOffset = CurrentFolderNode->UncompOffset; 01672 CurrentFolderNode->UncompOffset += TotalBytesLeft; 01673 FileNode->File.FileControlID = (USHORT)(NextFolderNumber - 1); 01674 CurrentFolderNode->Commit = true; 01675 PrevCabinetNumber = CurrentDiskNumber; 01676 01677 Size = sizeof(CFFILE) + (ULONG)strlen(GetFileName(FileNode->FileName)) + 1; 01678 CABHeader.FileTableOffset += Size; 01679 TotalFileSize += Size; 01680 DiskSize += Size; 01681 } 01682 01683 FileNode->Commit = true; 01684 01685 if (TotalBytesLeft > 0) 01686 { 01687 do 01688 { 01689 if (TotalBytesLeft > (ULONG)CAB_BLOCKSIZE - CurrentIBufferSize) 01690 BytesToRead = CAB_BLOCKSIZE - CurrentIBufferSize; 01691 else 01692 BytesToRead = TotalBytesLeft; 01693 01694 if (!ReadFileData(SourceFile, CurrentIBuffer, BytesToRead, &BytesRead) || (BytesToRead != BytesRead)) 01695 { 01696 DPRINT(MIN_TRACE, ("Cannot read from file. BytesToRead (%u) BytesRead (%u) CurrentIBufferSize (%u).\n", 01697 (UINT)BytesToRead, (UINT)BytesRead, (UINT)CurrentIBufferSize)); 01698 return CAB_STATUS_INVALID_CAB; 01699 } 01700 01701 CurrentIBuffer = (unsigned char*)CurrentIBuffer + BytesRead; 01702 CurrentIBufferSize += (USHORT)BytesRead; 01703 01704 if (CurrentIBufferSize == CAB_BLOCKSIZE) 01705 { 01706 Status = WriteDataBlock(); 01707 if (Status != CAB_STATUS_SUCCESS) 01708 return Status; 01709 } 01710 TotalBytesLeft -= BytesRead; 01711 } while ((TotalBytesLeft > 0) && (!CreateNewDisk)); 01712 } 01713 01714 if (TotalBytesLeft == 0) 01715 { 01716 CloseFile(SourceFile); 01717 FileNode->Delete = true; 01718 01719 if (FileNode->File.FileControlID > CAB_FILE_MAX_FOLDER) 01720 { 01721 FileNode->File.FileControlID = CAB_FILE_CONTINUED; 01722 CurrentFolderNode->Delete = true; 01723 01724 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 01725 { 01726 Status = WriteDataBlock(); 01727 if (Status != CAB_STATUS_SUCCESS) 01728 return Status; 01729 } 01730 01731 CreateNewFolder = true; 01732 } 01733 } 01734 else 01735 { 01736 if (FileNode->File.FileControlID <= CAB_FILE_MAX_FOLDER) 01737 FileNode->File.FileControlID = CAB_FILE_SPLIT; 01738 else 01739 FileNode->File.FileControlID = CAB_FILE_PREV_NEXT; 01740 } 01741 01742 return CAB_STATUS_SUCCESS; 01743 } 01744 01745 01746 ULONG CCabinet::WriteDisk(ULONG MoreDisks) 01747 /* 01748 * FUNCTION: Forces the current disk to be written 01749 * ARGUMENTS: 01750 * MoreDisks = true if there is one or more disks after this disk 01751 * RETURNS: 01752 * Status of operation 01753 */ 01754 { 01755 PCFFILE_NODE FileNode; 01756 ULONG Status; 01757 01758 ContinueFile = false; 01759 FileNode = FileListHead; 01760 while (FileNode != NULL) 01761 { 01762 Status = WriteFileToScratchStorage(FileNode); 01763 if (Status != CAB_STATUS_SUCCESS) 01764 return Status; 01765 01766 if (CreateNewDisk) 01767 { 01768 /* A data block could span more than two 01769 disks if MaxDiskSize is very small */ 01770 while (CreateNewDisk) 01771 { 01772 DPRINT(MAX_TRACE, ("Creating new disk.\n")); 01773 CommitDisk(true); 01774 CloseDisk(); 01775 NewDisk(); 01776 01777 ContinueFile = true; 01778 CreateNewDisk = false; 01779 01780 DPRINT(MAX_TRACE, ("First on new disk. CurrentIBufferSize (%u) CurrentOBufferSize (%u).\n", 01781 (UINT)CurrentIBufferSize, (UINT)CurrentOBufferSize)); 01782 01783 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 01784 { 01785 Status = WriteDataBlock(); 01786 if (Status != CAB_STATUS_SUCCESS) 01787 return Status; 01788 } 01789 } 01790 } 01791 else 01792 { 01793 ContinueFile = false; 01794 FileNode = FileNode->Next; 01795 } 01796 } 01797 01798 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 01799 { 01800 /* A data block could span more than two 01801 disks if MaxDiskSize is very small */ 01802 01803 ASSERT(CreateNewDisk == false); 01804 01805 do 01806 { 01807 if (CreateNewDisk) 01808 { 01809 DPRINT(MID_TRACE, ("Creating new disk 2.\n")); 01810 CommitDisk(true); 01811 CloseDisk(); 01812 NewDisk(); 01813 CreateNewDisk = false; 01814 01815 ASSERT(FileNode == FileListHead); 01816 } 01817 01818 if ((CurrentIBufferSize > 0) || (CurrentOBufferSize > 0)) 01819 { 01820 Status = WriteDataBlock(); 01821 if (Status != CAB_STATUS_SUCCESS) 01822 return Status; 01823 } 01824 } while (CreateNewDisk); 01825 } 01826 CommitDisk(MoreDisks); 01827 01828 return CAB_STATUS_SUCCESS; 01829 } 01830 01831 01832 ULONG CCabinet::CommitDisk(ULONG MoreDisks) 01833 /* 01834 * FUNCTION: Commits the current disk 01835 * ARGUMENTS: 01836 * MoreDisks = true if there is one or more disks after this disk 01837 * RETURNS: 01838 * Status of operation 01839 */ 01840 { 01841 PCFFOLDER_NODE FolderNode; 01842 ULONG Status; 01843 01844 OnCabinetName(CurrentDiskNumber, CabinetName); 01845 01846 /* Create file, fail if it already exists */ 01847 #if defined(_WIN32) 01848 FileHandle = CreateFile(CabinetName, // Create this file 01849 GENERIC_WRITE, // Open for writing 01850 0, // No sharing 01851 NULL, // No security 01852 CREATE_NEW, // New file only 01853 FILE_ATTRIBUTE_NORMAL, // Normal file 01854 NULL); // No attribute template 01855 if (FileHandle == INVALID_HANDLE_VALUE) 01856 { 01857 ULONG Status; 01858 /* If file exists, ask to overwrite file */ 01859 if (((Status = GetLastError()) == ERROR_FILE_EXISTS) && 01860 (OnOverwrite(NULL, CabinetName))) 01861 { 01862 01863 /* Create cabinet file, overwrite if it already exists */ 01864 FileHandle = CreateFile(CabinetName, // Create this file 01865 GENERIC_WRITE, // Open for writing 01866 0, // No sharing 01867 NULL, // No security 01868 TRUNCATE_EXISTING, // Truncate the file 01869 FILE_ATTRIBUTE_NORMAL, // Normal file 01870 NULL); // No attribute template 01871 if (FileHandle == INVALID_HANDLE_VALUE) 01872 return CAB_STATUS_CANNOT_CREATE; 01873 } 01874 else 01875 { 01876 if (Status == ERROR_FILE_EXISTS) 01877 return CAB_STATUS_FILE_EXISTS; 01878 else 01879 return CAB_STATUS_CANNOT_CREATE; 01880 } 01881 } 01882 #else /* !_WIN32 */ 01883 FileHandle = fopen(CabinetName, "rb"); 01884 if (FileHandle != NULL) 01885 { 01886 fclose(FileHandle); 01887 /* If file exists, ask to overwrite file */ 01888 if (OnOverwrite(NULL, CabinetName)) 01889 { 01890 FileHandle = fopen(CabinetName, "w+b"); 01891 if (FileHandle == NULL) 01892 return CAB_STATUS_CANNOT_CREATE; 01893 } 01894 else 01895 return CAB_STATUS_FILE_EXISTS; 01896 01897 } 01898 else 01899 { 01900 FileHandle = fopen(CabinetName, "w+b"); 01901 if (FileHandle == NULL) 01902 return CAB_STATUS_CANNOT_CREATE; 01903 } 01904 #endif 01905 01906 WriteCabinetHeader(MoreDisks != 0); 01907 01908 Status = WriteFolderEntries(); 01909 if (Status != CAB_STATUS_SUCCESS) 01910 return Status; 01911 01912 /* Write file entries */ 01913 WriteFileEntries(); 01914 01915 /* Write data blocks */ 01916 FolderNode = FolderListHead; 01917 while (FolderNode != NULL) 01918 { 01919 if (FolderNode->Commit) 01920 { 01921 Status = CommitDataBlocks(FolderNode); 01922 if (Status != CAB_STATUS_SUCCESS) 01923 return Status; 01924 /* Remove data blocks for folder */ 01925 DestroyDataNodes(FolderNode); 01926 } 01927 FolderNode = FolderNode->Next; 01928 } 01929 01930 CloseFile(FileHandle); 01931 01932 ScratchFile->Truncate(); 01933 01934 return CAB_STATUS_SUCCESS; 01935 } 01936 01937 01938 ULONG CCabinet::CloseDisk() 01939 /* 01940 * FUNCTION: Closes the current disk 01941 * RETURNS: 01942 * Status of operation 01943 */ 01944 { 01945 DestroyDeletedFileNodes(); 01946 01947 /* Destroy folder nodes that are completely stored */ 01948 DestroyDeletedFolderNodes(); 01949 01950 CurrentDiskNumber++; 01951 01952 return CAB_STATUS_SUCCESS; 01953 } 01954 01955 01956 ULONG CCabinet::CloseCabinet() 01957 /* 01958 * FUNCTION: Closes the current cabinet 01959 * RETURNS: 01960 * Status of operation 01961 */ 01962 { 01963 ULONG Status; 01964 01965 DestroyFileNodes(); 01966 01967 DestroyFolderNodes(); 01968 01969 if (InputBuffer) 01970 { 01971 FreeMemory(InputBuffer); 01972 InputBuffer = NULL; 01973 } 01974 01975 if (OutputBuffer) 01976 { 01977 FreeMemory(OutputBuffer); 01978 OutputBuffer = NULL; 01979 } 01980 01981 Close(); 01982 01983 if (ScratchFile) 01984 { 01985 Status = ScratchFile->Destroy(); 01986 delete ScratchFile; 01987 return Status; 01988 } 01989 01990 return CAB_STATUS_SUCCESS; 01991 } 01992 01993 01994 ULONG CCabinet::AddFile(char* FileName) 01995 /* 01996 * FUNCTION: Adds a file to the current disk 01997 * ARGUMENTS: 01998 * FileName = Pointer to string with file name (full path) 01999 * RETURNS: 02000 * Status of operation 02001 */ 02002 { 02003 FILEHANDLE SrcFile; 02004 PCFFILE_NODE FileNode; 02005 char* NewFileName; 02006 02007 NewFileName = (char*)AllocateMemory(strlen(FileName) + 1); 02008 if (!NewFileName) 02009 { 02010 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 02011 return CAB_STATUS_NOMEMORY; 02012 } 02013 strcpy(NewFileName, FileName); 02014 ConvertPath(NewFileName, false); 02015 02016 /* Try to open file */ 02017 #if defined(_WIN32) 02018 SrcFile = CreateFile( 02019 NewFileName, // Open this file 02020 GENERIC_READ, // Open for reading 02021 FILE_SHARE_READ, // Share for reading 02022 NULL, // No security 02023 OPEN_EXISTING, // File must exist 02024 FILE_ATTRIBUTE_NORMAL, // Normal file 02025 NULL); // No attribute template 02026 if (SrcFile == INVALID_HANDLE_VALUE) 02027 { 02028 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName)); 02029 FreeMemory(NewFileName); 02030 return CAB_STATUS_CANNOT_OPEN; 02031 } 02032 #else /* !_WIN32 */ 02033 SrcFile = fopen(NewFileName, "rb"); 02034 if (SrcFile == NULL) 02035 { 02036 DPRINT(MID_TRACE, ("File not found (%s).\n", NewFileName)); 02037 FreeMemory(NewFileName); 02038 return CAB_STATUS_CANNOT_OPEN; 02039 } 02040 #endif 02041 02042 FileNode = NewFileNode(); 02043 if (!FileNode) 02044 { 02045 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 02046 FreeMemory(NewFileName); 02047 CloseFile(SrcFile); 02048 return CAB_STATUS_NOMEMORY; 02049 } 02050 02051 FileNode->FolderNode = CurrentFolderNode; 02052 FileNode->FileName = NewFileName; 02053 02054 /* FIXME: Check for and handle large files (>= 2GB) */ 02055 FileNode->File.FileSize = GetSizeOfFile(SrcFile); 02056 if (FileNode->File.FileSize == (ULONG)-1) 02057 { 02058 DPRINT(MIN_TRACE, ("Cannot read from file.\n")); 02059 FreeMemory(NewFileName); 02060 CloseFile(SrcFile); 02061 return CAB_STATUS_CANNOT_READ; 02062 } 02063 02064 if (GetFileTimes(SrcFile, FileNode) != CAB_STATUS_SUCCESS) 02065 { 02066 DPRINT(MIN_TRACE, ("Cannot read file times.\n")); 02067 FreeMemory(NewFileName); 02068 CloseFile(SrcFile); 02069 return CAB_STATUS_CANNOT_READ; 02070 } 02071 02072 if (GetAttributesOnFile(FileNode) != CAB_STATUS_SUCCESS) 02073 { 02074 DPRINT(MIN_TRACE, ("Cannot read file attributes.\n")); 02075 FreeMemory(NewFileName); 02076 CloseFile(SrcFile); 02077 return CAB_STATUS_CANNOT_READ; 02078 } 02079 02080 CloseFile(SrcFile); 02081 02082 return CAB_STATUS_SUCCESS; 02083 } 02084 02085 bool CCabinet::CreateSimpleCabinet() 02086 /* 02087 * FUNCTION: Create a simple cabinet based on the files in the criteria list 02088 */ 02089 { 02090 bool bRet = false; 02091 char* pszFile; 02092 char szFilePath[PATH_MAX]; 02093 char szFile[PATH_MAX]; 02094 PSEARCH_CRITERIA Criteria; 02095 ULONG Status; 02096 02097 #if defined(_WIN32) 02098 HANDLE hFind; 02099 WIN32_FIND_DATA FindFileData; 02100 #else 02101 DIR* dirp; 02102 struct dirent* dp; 02103 struct stat stbuf; 02104 #endif 02105 02106 // Initialize a new cabinet 02107 Status = NewCabinet(); 02108 if (Status != CAB_STATUS_SUCCESS) 02109 { 02110 DPRINT(MIN_TRACE, ("Cannot create cabinet (%u).\n", (UINT)Status)); 02111 goto cleanup2; 02112 } 02113 02114 // Add each file in the criteria list 02115 Criteria = CriteriaListHead; 02116 02117 while(Criteria) 02118 { 02119 // Store the file path with a trailing slash in szFilePath 02120 ConvertPath(Criteria->Search, false); 02121 pszFile = strrchr(Criteria->Search, DIR_SEPARATOR_CHAR); 02122 02123 if(pszFile) 02124 { 02125 // Set the pointer to the start of the file name, not the slash 02126 pszFile++; 02127 02128 strncpy(szFilePath, Criteria->Search, pszFile - Criteria->Search); 02129 szFilePath[pszFile - Criteria->Search] = 0; 02130 } 02131 else 02132 { 02133 pszFile = Criteria->Search; 02134 02135 #if defined(_WIN32) 02136 szFilePath[0] = 0; 02137 #else 02138 // needed for opendir() 02139 strcpy(szFilePath, "./"); 02140 #endif 02141 } 02142 02143 #if defined(_WIN32) 02144 // Windows: Use the easy FindFirstFile/FindNextFile API for getting all files and checking them against the pattern 02145 hFind = FindFirstFile(Criteria->Search, &FindFileData); 02146 02147 // Don't stop if a search criteria is not found 02148 if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND) 02149 { 02150 DPRINT(MIN_TRACE, ("FindFirstFile failed, Criteria: %s, error code is %u\n", Criteria->Search, (UINT)GetLastError())); 02151 goto cleanup; 02152 } 02153 02154 do 02155 { 02156 if(!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 02157 { 02158 strcpy(szFile, szFilePath); 02159 strcat(szFile, FindFileData.cFileName); 02160 02161 Status = AddFile(szFile); 02162 02163 if(Status != CAB_STATUS_SUCCESS) 02164 { 02165 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); 02166 goto cleanup; 02167 } 02168 } 02169 } 02170 while(FindNextFile(hFind, &FindFileData)); 02171 02172 FindClose(hFind); 02173 #else 02174 // 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 02175 dirp = opendir(szFilePath); 02176 02177 if(dirp) 02178 { 02179 while( (dp = readdir(dirp)) ) 02180 { 02181 strcpy(szFile, szFilePath); 02182 strcat(szFile, dp->d_name); 02183 02184 if(stat(szFile, &stbuf) == 0) 02185 { 02186 if(stbuf.st_mode != S_IFDIR) 02187 { 02188 if(MatchFileNamePattern(dp->d_name, pszFile)) 02189 { 02190 Status = AddFile(szFile); 02191 02192 if(Status != CAB_STATUS_SUCCESS) 02193 { 02194 DPRINT(MIN_TRACE, ("Cannot add file to cabinet (%u).\n", (UINT)Status)); 02195 goto cleanup; 02196 } 02197 } 02198 } 02199 } 02200 else 02201 { 02202 DPRINT(MIN_TRACE, ("stat failed, error code is %i\n", errno)); 02203 goto cleanup; 02204 } 02205 } 02206 02207 closedir(dirp); 02208 } 02209 #endif 02210 02211 Criteria = Criteria->Next; 02212 } 02213 02214 Status = WriteDisk(false); 02215 if (Status == CAB_STATUS_SUCCESS) 02216 Status = CloseDisk(); 02217 if (Status != CAB_STATUS_SUCCESS) 02218 { 02219 DPRINT(MIN_TRACE, ("Cannot write disk (%u).\n", (UINT)Status)); 02220 goto cleanup; 02221 } 02222 02223 cleanup: 02224 CloseCabinet(); 02225 bRet = true; 02226 02227 cleanup2: 02228 DestroySearchCriteria(); 02229 return bRet; 02230 } 02231 02232 void CCabinet::SetMaxDiskSize(ULONG Size) 02233 /* 02234 * FUNCTION: Sets the maximum size of the current disk 02235 * ARGUMENTS: 02236 * Size = Maximum size of current disk (0 means no maximum size) 02237 */ 02238 { 02239 MaxDiskSize = Size; 02240 } 02241 02242 #endif /* CAB_READ_ONLY */ 02243 02244 02245 /* Default event handlers */ 02246 02247 bool CCabinet::OnOverwrite(PCFFILE File, 02248 char* FileName) 02249 /* 02250 * FUNCTION: Called when extracting a file and it already exists 02251 * ARGUMENTS: 02252 * File = Pointer to CFFILE for file being extracted 02253 * FileName = Pointer to buffer with name of file (full path) 02254 * RETURNS 02255 * true if the file should be overwritten, false if not 02256 */ 02257 { 02258 return false; 02259 } 02260 02261 02262 void CCabinet::OnExtract(PCFFILE File, 02263 char* FileName) 02264 /* 02265 * FUNCTION: Called just before extracting a file 02266 * ARGUMENTS: 02267 * File = Pointer to CFFILE for file being extracted 02268 * FileName = Pointer to buffer with name of file (full path) 02269 */ 02270 { 02271 } 02272 02273 02274 void CCabinet::OnDiskChange(char* CabinetName, 02275 char* DiskLabel) 02276 /* 02277 * FUNCTION: Called when a new disk is to be processed 02278 * ARGUMENTS: 02279 * CabinetName = Pointer to buffer with name of cabinet 02280 * DiskLabel = Pointer to buffer with label of disk 02281 */ 02282 { 02283 } 02284 02285 02286 #ifndef CAB_READ_ONLY 02287 02288 void CCabinet::OnAdd(PCFFILE File, 02289 char* FileName) 02290 /* 02291 * FUNCTION: Called just before adding a file to a cabinet 02292 * ARGUMENTS: 02293 * File = Pointer to CFFILE for file being added 02294 * FileName = Pointer to buffer with name of file (full path) 02295 */ 02296 { 02297 } 02298 02299 02300 bool CCabinet::OnDiskLabel(ULONG Number, char* Label) 02301 /* 02302 * FUNCTION: Called when a disk needs a label 02303 * ARGUMENTS: 02304 * Number = Cabinet number that needs a label 02305 * Label = Pointer to buffer to place label of disk 02306 * RETURNS: 02307 * true if a disk label was returned, false if not 02308 */ 02309 { 02310 return false; 02311 } 02312 02313 02314 bool CCabinet::OnCabinetName(ULONG Number, char* Name) 02315 /* 02316 * FUNCTION: Called when a cabinet needs a name 02317 * ARGUMENTS: 02318 * Number = Disk number that needs a name 02319 * Name = Pointer to buffer to place name of cabinet 02320 * RETURNS: 02321 * true if a cabinet name was returned, false if not 02322 */ 02323 { 02324 return false; 02325 } 02326 02327 #endif /* CAB_READ_ONLY */ 02328 02329 PCFFOLDER_NODE CCabinet::LocateFolderNode(ULONG Index) 02330 /* 02331 * FUNCTION: Locates a folder node 02332 * ARGUMENTS: 02333 * Index = Folder index 02334 * RETURNS: 02335 * Pointer to folder node or NULL if the folder node was not found 02336 */ 02337 { 02338 PCFFOLDER_NODE Node; 02339 02340 switch (Index) 02341 { 02342 case CAB_FILE_SPLIT: 02343 return FolderListTail; 02344 02345 case CAB_FILE_CONTINUED: 02346 case CAB_FILE_PREV_NEXT: 02347 return FolderListHead; 02348 } 02349 02350 Node = FolderListHead; 02351 while (Node != NULL) 02352 { 02353 if (Node->Index == Index) 02354 return Node; 02355 Node = Node->Next; 02356 } 02357 return NULL; 02358 } 02359 02360 02361 ULONG CCabinet::GetAbsoluteOffset(PCFFILE_NODE File) 02362 /* 02363 * FUNCTION: Returns the absolute offset of a file 02364 * ARGUMENTS: 02365 * File = Pointer to CFFILE_NODE structure for file 02366 * RETURNS: 02367 * Status of operation 02368 */ 02369 { 02370 PCFDATA_NODE Node; 02371 02372 DPRINT(MAX_TRACE, ("FileName '%s' FileOffset (0x%X) FileSize (%u).\n", 02373 File->FileName, 02374 (UINT)File->File.FileOffset, 02375 (UINT)File->File.FileSize)); 02376 02377 Node = CurrentFolderNode->DataListHead; 02378 while (Node != NULL) 02379 { 02380 DPRINT(MAX_TRACE, ("GetAbsoluteOffset(): Comparing (0x%X, 0x%X) (%u).\n", 02381 (UINT)Node->UncompOffset, 02382 (UINT)(Node->UncompOffset + Node->Data.UncompSize), 02383 (UINT)Node->Data.UncompSize)); 02384 02385 /* Node->Data.UncompSize will be 0 if the block is split 02386 (ie. it is the last block in this cabinet) */ 02387 if ((Node->Data.UncompSize == 0) || 02388 ((File->File.FileOffset >= Node->UncompOffset) && 02389 (File->File.FileOffset < Node->UncompOffset + 02390 Node->Data.UncompSize))) 02391 { 02392 File->DataBlock = Node; 02393 return CAB_STATUS_SUCCESS; 02394 } 02395 02396 Node = Node->Next; 02397 } 02398 return CAB_STATUS_INVALID_CAB; 02399 } 02400 02401 02402 ULONG CCabinet::LocateFile(char* FileName, 02403 PCFFILE_NODE *File) 02404 /* 02405 * FUNCTION: Locates a file in the cabinet 02406 * ARGUMENTS: 02407 * FileName = Pointer to string with name of file to locate 02408 * File = Address of pointer to CFFILE_NODE structure to fill 02409 * RETURNS: 02410 * Status of operation 02411 * NOTES: 02412 * Current folder is set to the folder of the file 02413 */ 02414 { 02415 PCFFILE_NODE Node; 02416 ULONG Status; 02417 02418 DPRINT(MAX_TRACE, ("FileName '%s'\n", FileName)); 02419 02420 Node = FileListHead; 02421 while (Node != NULL) 02422 { 02423 if (strcasecmp(FileName, Node->FileName) == 0) 02424 { 02425 CurrentFolderNode = LocateFolderNode(Node->File.FileControlID); 02426 if (!CurrentFolderNode) 02427 { 02428 DPRINT(MID_TRACE, ("Folder with index number (%u) not found.\n", 02429 Node->File.FileControlID)); 02430 return CAB_STATUS_INVALID_CAB; 02431 } 02432 02433 if (Node->DataBlock == NULL) 02434 Status = GetAbsoluteOffset(Node); 02435 else 02436 Status = CAB_STATUS_SUCCESS; 02437 02438 *File = Node; 02439 return Status; 02440 } 02441 Node = Node->Next; 02442 } 02443 return CAB_STATUS_NOFILE; 02444 } 02445 02446 02447 ULONG CCabinet::ReadString(char* String, LONG MaxLength) 02448 /* 02449 * FUNCTION: Reads a NULL-terminated string from the cabinet 02450 * ARGUMENTS: 02451 * String = Pointer to buffer to place string 02452 * MaxLength = Maximum length of string 02453 * RETURNS: 02454 * Status of operation 02455 */ 02456 { 02457 ULONG BytesRead; 02458 ULONG Status; 02459 LONG Size; 02460 bool Found; 02461 02462 Found = false; 02463 02464 Status = ReadBlock(String, MaxLength, &BytesRead); 02465 if (Status != CAB_STATUS_SUCCESS) 02466 { 02467 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 02468 return CAB_STATUS_INVALID_CAB; 02469 } 02470 02471 // Find the terminating NULL character 02472 for (Size = 0; Size < MaxLength; Size++) 02473 { 02474 if (String[Size] == '\0') 02475 { 02476 Found = true; 02477 break; 02478 } 02479 } 02480 02481 if (!Found) 02482 { 02483 DPRINT(MIN_TRACE, ("Filename in the cabinet file is too long.\n")); 02484 return CAB_STATUS_INVALID_CAB; 02485 } 02486 02487 // Compute the offset of the next CFFILE. 02488 // We have to subtract from the current offset here, because we read MaxLength characters above and most-probably the file name isn't MaxLength characters long. 02489 // + 1 to skip the terminating NULL character as well. 02490 Size = -(MaxLength - Size) + 1; 02491 02492 #if defined(_WIN32) 02493 if( SetFilePointer(FileHandle, 02494 (LONG)Size, 02495 NULL, 02496 FILE_CURRENT) == INVALID_SET_FILE_POINTER ) 02497 { 02498 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 02499 return CAB_STATUS_INVALID_CAB; 02500 } 02501 #else 02502 if (fseek(FileHandle, (off_t)Size, SEEK_CUR) != 0) 02503 { 02504 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 02505 return CAB_STATUS_INVALID_CAB; 02506 } 02507 #endif 02508 return CAB_STATUS_SUCCESS; 02509 } 02510 02511 02512 ULONG CCabinet::ReadFileTable() 02513 /* 02514 * FUNCTION: Reads the file table from the cabinet file 02515 * RETURNS: 02516 * Status of operation 02517 */ 02518 { 02519 ULONG i; 02520 ULONG Status; 02521 ULONG BytesRead; 02522 PCFFILE_NODE File; 02523 02524 DPRINT(MAX_TRACE, ("Reading file table at absolute offset (0x%X).\n", 02525 (UINT)CABHeader.FileTableOffset)); 02526 02527 /* Seek to file table */ 02528 #if defined(_WIN32) 02529 if( SetFilePointer(FileHandle, 02530 CABHeader.FileTableOffset, 02531 NULL, 02532 FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 02533 { 02534 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 02535 return CAB_STATUS_INVALID_CAB; 02536 } 02537 #else 02538 if (fseek(FileHandle, (off_t)CABHeader.FileTableOffset, SEEK_SET) != 0) 02539 { 02540 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 02541 return CAB_STATUS_INVALID_CAB; 02542 } 02543 #endif 02544 02545 for (i = 0; i < CABHeader.FileCount; i++) 02546 { 02547 File = NewFileNode(); 02548 if (!File) 02549 { 02550 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 02551 return CAB_STATUS_NOMEMORY; 02552 } 02553 02554 if ((Status = ReadBlock(&File->File, sizeof(CFFILE), 02555 &BytesRead)) != CAB_STATUS_SUCCESS) 02556 { 02557 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 02558 return CAB_STATUS_INVALID_CAB; 02559 } 02560 02561 File->FileName = (char*)AllocateMemory(PATH_MAX); 02562 if (!File->FileName) 02563 { 02564 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 02565 return CAB_STATUS_NOMEMORY; 02566 } 02567 02568 /* Read file name */ 02569 Status = ReadString(File->FileName, PATH_MAX); 02570 if (Status != CAB_STATUS_SUCCESS) 02571 return Status; 02572 02573 DPRINT(MAX_TRACE, ("Found file '%s' at uncompressed offset (0x%X). Size (%u bytes) ControlId (0x%X).\n", 02574 File->FileName, 02575 (UINT)File->File.FileOffset, 02576 (UINT)File->File.FileSize, 02577 File->File.FileControlID)); 02578 02579 } 02580 return CAB_STATUS_SUCCESS; 02581 } 02582 02583 02584 ULONG CCabinet::ReadDataBlocks(PCFFOLDER_NODE FolderNode) 02585 /* 02586 * FUNCTION: Reads all CFDATA blocks for a folder from the cabinet file 02587 * ARGUMENTS: 02588 * FolderNode = Pointer to CFFOLDER_NODE structure for folder 02589 * RETURNS: 02590 * Status of operation 02591 */ 02592 { 02593 ULONG AbsoluteOffset; 02594 ULONG UncompOffset; 02595 PCFDATA_NODE Node; 02596 ULONG BytesRead; 02597 ULONG Status; 02598 ULONG i; 02599 02600 DPRINT(MAX_TRACE, ("Reading data blocks for folder (%u) at absolute offset (0x%X).\n", 02601 (UINT)FolderNode->Index, (UINT)FolderNode->Folder.DataOffset)); 02602 02603 AbsoluteOffset = FolderNode->Folder.DataOffset; 02604 UncompOffset = FolderNode->UncompOffset; 02605 02606 for (i = 0; i < FolderNode->Folder.DataBlockCount; i++) 02607 { 02608 Node = NewDataNode(FolderNode); 02609 if (!Node) 02610 { 02611 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 02612 return CAB_STATUS_NOMEMORY; 02613 } 02614 02615 /* Seek to data block */ 02616 #if defined(_WIN32) 02617 if( SetFilePointer(FileHandle, 02618 AbsoluteOffset, 02619 NULL, 02620 FILE_BEGIN) == INVALID_SET_FILE_POINTER ) 02621 { 02622 DPRINT(MIN_TRACE, ("SetFilePointer() failed, error code is %u.\n", (UINT)GetLastError())); 02623 return CAB_STATUS_INVALID_CAB; 02624 } 02625 #else 02626 if (fseek(FileHandle, (off_t)AbsoluteOffset, SEEK_SET) != 0) 02627 { 02628 DPRINT(MIN_TRACE, ("fseek() failed.\n")); 02629 return CAB_STATUS_INVALID_CAB; 02630 } 02631 #endif 02632 02633 if ((Status = ReadBlock(&Node->Data, sizeof(CFDATA), 02634 &BytesRead)) != CAB_STATUS_SUCCESS) 02635 { 02636 DPRINT(MIN_TRACE, ("Cannot read from file (%u).\n", (UINT)Status)); 02637 return CAB_STATUS_INVALID_CAB; 02638 } 02639 02640 DPRINT(MAX_TRACE, ("AbsOffset (0x%X) UncompOffset (0x%X) Checksum (0x%X) CompSize (%u) UncompSize (%u).\n", 02641 (UINT)AbsoluteOffset, 02642 (UINT)UncompOffset, 02643 (UINT)Node->Data.Checksum, 02644 Node->Data.CompSize, 02645 Node->Data.UncompSize)); 02646 02647 Node->AbsoluteOffset = AbsoluteOffset; 02648 Node->UncompOffset = UncompOffset; 02649 02650 AbsoluteOffset += sizeof(CFDATA) + Node->Data.CompSize; 02651 UncompOffset += Node->Data.UncompSize; 02652 } 02653 02654 FolderUncompSize = UncompOffset; 02655 02656 return CAB_STATUS_SUCCESS; 02657 } 02658 02659 02660 PCFFOLDER_NODE CCabinet::NewFolderNode() 02661 /* 02662 * FUNCTION: Creates a new folder node 02663 * RETURNS: 02664 * Pointer to node if there was enough free memory available, otherwise NULL 02665 */ 02666 { 02667 PCFFOLDER_NODE Node; 02668 02669 Node = (PCFFOLDER_NODE)AllocateMemory(sizeof(CFFOLDER_NODE)); 02670 if (!Node) 02671 return NULL; 02672 02673 memset(Node, 0, sizeof(CFFOLDER_NODE)); 02674 02675 Node->Folder.CompressionType = CAB_COMP_NONE; 02676 02677 Node->Prev = FolderListTail; 02678 02679 if (FolderListTail != NULL) 02680 FolderListTail->Next = Node; 02681 else 02682 FolderListHead = Node; 02683 02684 FolderListTail = Node; 02685 02686 return Node; 02687 } 02688 02689 02690 PCFFILE_NODE CCabinet::NewFileNode() 02691 /* 02692 * FUNCTION: Creates a new file node 02693 * ARGUMENTS: 02694 * FolderNode = Pointer to folder node to bind file to 02695 * RETURNS: 02696 * Pointer to node if there was enough free memory available, otherwise NULL 02697 */ 02698 { 02699 PCFFILE_NODE Node; 02700 02701 Node = (PCFFILE_NODE)AllocateMemory(sizeof(CFFILE_NODE)); 02702 if (!Node) 02703 return NULL; 02704 02705 memset(Node, 0, sizeof(CFFILE_NODE)); 02706 02707 Node->Prev = FileListTail; 02708 02709 if (FileListTail != NULL) 02710 FileListTail->Next = Node; 02711 else 02712 FileListHead = Node; 02713 02714 FileListTail = Node; 02715 02716 return Node; 02717 } 02718 02719 02720 PCFDATA_NODE CCabinet::NewDataNode(PCFFOLDER_NODE FolderNode) 02721 /* 02722 * FUNCTION: Creates a new data block node 02723 * ARGUMENTS: 02724 * FolderNode = Pointer to folder node to bind data block to 02725 * RETURNS: 02726 * Pointer to node if there was enough free memory available, otherwise NULL 02727 */ 02728 { 02729 PCFDATA_NODE Node; 02730 02731 Node = (PCFDATA_NODE)AllocateMemory(sizeof(CFDATA_NODE)); 02732 if (!Node) 02733 return NULL; 02734 02735 memset(Node, 0, sizeof(CFDATA_NODE)); 02736 02737 Node->Prev = FolderNode->DataListTail; 02738 02739 if (FolderNode->DataListTail != NULL) 02740 FolderNode->DataListTail->Next = Node; 02741 else 02742 FolderNode->DataListHead = Node; 02743 02744 FolderNode->DataListTail = Node; 02745 02746 return Node; 02747 } 02748 02749 02750 void CCabinet::DestroyDataNodes(PCFFOLDER_NODE FolderNode) 02751 /* 02752 * FUNCTION: Destroys data block nodes bound to a folder node 02753 * ARGUMENTS: 02754 * FolderNode = Pointer to folder node 02755 */ 02756 { 02757 PCFDATA_NODE PrevNode; 02758 PCFDATA_NODE NextNode; 02759 02760 NextNode = FolderNode->DataListHead; 02761 while (NextNode != NULL) 02762 { 02763 PrevNode = NextNode->Next; 02764 FreeMemory(NextNode); 02765 NextNode = PrevNode; 02766 } 02767 FolderNode->DataListHead = NULL; 02768 FolderNode->DataListTail = NULL; 02769 } 02770 02771 02772 void CCabinet::DestroyFileNodes() 02773 /* 02774 * FUNCTION: Destroys file nodes 02775 */ 02776 { 02777 PCFFILE_NODE PrevNode; 02778 PCFFILE_NODE NextNode; 02779 02780 NextNode = FileListHead; 02781 while (NextNode != NULL) 02782 { 02783 PrevNode = NextNode->Next; 02784 if (NextNode->FileName) 02785 FreeMemory(NextNode->FileName); 02786 FreeMemory(NextNode); 02787 NextNode = PrevNode; 02788 } 02789 FileListHead = NULL; 02790 FileListTail = NULL; 02791 } 02792 02793 02794 void CCabinet::DestroyDeletedFileNodes() 02795 /* 02796 * FUNCTION: Destroys file nodes that are marked for deletion 02797 */ 02798 { 02799 PCFFILE_NODE CurNode; 02800 PCFFILE_NODE NextNode; 02801 02802 CurNode = FileListHead; 02803 while (CurNode != NULL) 02804 { 02805 NextNode = CurNode->Next; 02806 02807 if (CurNode->Delete) 02808 { 02809 if (CurNode->Prev != NULL) 02810 CurNode->Prev->Next = CurNode->Next; 02811 else 02812 { 02813 FileListHead = CurNode->Next; 02814 if (FileListHead) 02815 FileListHead->Prev = NULL; 02816 } 02817 02818 if (CurNode->Next != NULL) 02819 CurNode->Next->Prev = CurNode->Prev; 02820 else 02821 { 02822 FileListTail = CurNode->Prev; 02823 if (FileListTail) 02824 FileListTail->Next = NULL; 02825 } 02826 02827 DPRINT(MAX_TRACE, ("Deleting file: '%s'\n", CurNode->FileName)); 02828 02829 TotalFileSize -= (sizeof(CFFILE) + (ULONG)strlen(GetFileName(CurNode->FileName)) + 1); 02830 02831 if (CurNode->FileName) 02832 FreeMemory(CurNode->FileName); 02833 FreeMemory(CurNode); 02834 } 02835 CurNode = NextNode; 02836 } 02837 } 02838 02839 02840 void CCabinet::DestroyFolderNodes() 02841 /* 02842 * FUNCTION: Destroys folder nodes 02843 */ 02844 { 02845 PCFFOLDER_NODE PrevNode; 02846 PCFFOLDER_NODE NextNode; 02847 02848 NextNode = FolderListHead; 02849 while (NextNode != NULL) 02850 { 02851 PrevNode = NextNode->Next; 02852 DestroyDataNodes(NextNode); 02853 FreeMemory(NextNode); 02854 NextNode = PrevNode; 02855 } 02856 FolderListHead = NULL; 02857 FolderListTail = NULL; 02858 } 02859 02860 02861 void CCabinet::DestroyDeletedFolderNodes() 02862 /* 02863 * FUNCTION: Destroys folder nodes that are marked for deletion 02864 */ 02865 { 02866 PCFFOLDER_NODE CurNode; 02867 PCFFOLDER_NODE NextNode; 02868 02869 CurNode = FolderListHead; 02870 while (CurNode != NULL) 02871 { 02872 NextNode = CurNode->Next; 02873 02874 if (CurNode->Delete) 02875 { 02876 if (CurNode->Prev != NULL) 02877 CurNode->Prev->Next = CurNode->Next; 02878 else 02879 { 02880 FolderListHead = CurNode->Next; 02881 if (FolderListHead) 02882 FolderListHead->Prev = NULL; 02883 } 02884 02885 if (CurNode->Next != NULL) 02886 CurNode->Next->Prev = CurNode->Prev; 02887 else 02888 { 02889 FolderListTail = CurNode->Prev; 02890 if (FolderListTail) 02891 FolderListTail->Next = NULL; 02892 } 02893 02894 DestroyDataNodes(CurNode); 02895 FreeMemory(CurNode); 02896 02897 TotalFolderSize -= sizeof(CFFOLDER); 02898 } 02899 CurNode = NextNode; 02900 } 02901 } 02902 02903 02904 ULONG CCabinet::ComputeChecksum(void* Buffer, 02905 ULONG Size, 02906 ULONG Seed) 02907 /* 02908 * FUNCTION: Computes checksum for data block 02909 * ARGUMENTS: 02910 * Buffer = Pointer to data buffer 02911 * Size = Length of data buffer 02912 * Seed = Previously computed checksum 02913 * RETURNS: 02914 * Checksum of buffer 02915 */ 02916 { 02917 int UlongCount; // Number of ULONGs in block 02918 ULONG Checksum; // Checksum accumulator 02919 unsigned char* pb; 02920 ULONG ul; 02921 02922 /* FIXME: Doesn't seem to be correct. EXTRACT.EXE 02923 won't accept checksums computed by this routine */ 02924 02925 DPRINT(MIN_TRACE, ("Checksumming buffer (0x%p) Size (%u)\n", Buffer, (UINT)Size)); 02926 02927 UlongCount = Size / 4; // Number of ULONGs 02928 Checksum = Seed; // Init checksum 02929 pb = (unsigned char*)Buffer; // Start at front of data block 02930 02931 /* Checksum integral multiple of ULONGs */ 02932 while (UlongCount-- > 0) 02933 { 02934 /* NOTE: Build ULONG in big/little-endian independent manner */ 02935 ul = *pb++; // Get low-order byte 02936 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte 02937 ul |= (((ULONG)(*pb++)) << 16); // Add 3nd byte 02938 ul |= (((ULONG)(*pb++)) << 24); // Add 4th byte 02939 02940 Checksum ^= ul; // Update checksum 02941 } 02942 02943 /* Checksum remainder bytes */ 02944 ul = 0; 02945 switch (Size % 4) 02946 { 02947 case 3: 02948 ul |= (((ULONG)(*pb++)) << 16); // Add 3rd byte 02949 case 2: 02950 ul |= (((ULONG)(*pb++)) << 8); // Add 2nd byte 02951 case 1: 02952 ul |= *pb++; // Get low-order byte 02953 default: 02954 break; 02955 } 02956 Checksum ^= ul; // Update checksum 02957 02958 /* Return computed checksum */ 02959 return Checksum; 02960 } 02961 02962 02963 ULONG CCabinet::ReadBlock(void* Buffer, 02964 ULONG Size, 02965 PULONG BytesRead) 02966 /* 02967 * FUNCTION: Read a block of data from file 02968 * ARGUMENTS: 02969 * Buffer = Pointer to data buffer 02970 * Size = Length of data buffer 02971 * BytesRead = Pointer to ULONG that on return will contain 02972 * number of bytes read 02973 * RETURNS: 02974 * Status of operation 02975 */ 02976 { 02977 if (!ReadFileData(FileHandle, Buffer, Size, BytesRead)) 02978 return CAB_STATUS_INVALID_CAB; 02979 return CAB_STATUS_SUCCESS; 02980 } 02981 02982 bool CCabinet::MatchFileNamePattern(char* FileName, char* Pattern) 02983 /* 02984 * FUNCTION: Matches a wildcard character pattern against a file 02985 * ARGUMENTS: 02986 * FileName = The file name to check 02987 * Pattern = The pattern 02988 * RETURNS: 02989 * Whether the pattern matches the file 02990 * 02991 * COPYRIGHT: 02992 * This function is based on Busybox code, Copyright (C) 1998 by Erik Andersen, released under GPL2 or any later version. 02993 * Adapted from code written by Ingo Wilken. 02994 * Original location: http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/utility.c?rev=5&view=markup 02995 */ 02996 { 02997 char* retryPattern = NULL; 02998 char* retryFileName = NULL; 02999 char ch; 03000 03001 while (*FileName || *Pattern) 03002 { 03003 ch = *Pattern++; 03004 03005 switch (ch) 03006 { 03007 case '*': 03008 retryPattern = Pattern; 03009 retryFileName = FileName; 03010 break; 03011 03012 case '?': 03013 if (*FileName++ == '\0') 03014 return false; 03015 03016 break; 03017 03018 default: 03019 if (*FileName == ch) 03020 { 03021 if (*FileName) 03022 FileName++; 03023 break; 03024 } 03025 03026 if (*FileName) 03027 { 03028 Pattern = retryPattern; 03029 FileName = ++retryFileName; 03030 break; 03031 } 03032 03033 return false; 03034 } 03035 03036 if (!Pattern) 03037 return false; 03038 } 03039 03040 return true; 03041 } 03042 03043 #ifndef CAB_READ_ONLY 03044 03045 ULONG CCabinet::InitCabinetHeader() 03046 /* 03047 * FUNCTION: Initializes cabinet header and optional fields 03048 * RETURNS: 03049 * Status of operation 03050 */ 03051 { 03052 ULONG TotalSize; 03053 ULONG Size; 03054 03055 CABHeader.FileTableOffset = 0; // Not known yet 03056 CABHeader.FolderCount = 0; // Not known yet 03057 CABHeader.FileCount = 0; // Not known yet 03058 CABHeader.Flags = 0; // Not known yet 03059 03060 CABHeader.CabinetNumber = (USHORT)CurrentDiskNumber; 03061 03062 if ((CurrentDiskNumber > 0) && (OnCabinetName(PrevCabinetNumber, CabinetPrev))) 03063 { 03064 CABHeader.Flags |= CAB_FLAG_HASPREV; 03065 if (!OnDiskLabel(PrevCabinetNumber, DiskPrev)) 03066 strcpy(CabinetPrev, ""); 03067 } 03068 03069 if (OnCabinetName(CurrentDiskNumber + 1, CabinetNext)) 03070 { 03071 CABHeader.Flags |= CAB_FLAG_HASNEXT; 03072 if (!OnDiskLabel(CurrentDiskNumber + 1, DiskNext)) 03073 strcpy(DiskNext, ""); 03074 } 03075 03076 TotalSize = 0; 03077 03078 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 03079 { 03080 03081 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); 03082 03083 /* Calculate size of name of previous cabinet */ 03084 TotalSize += (ULONG)strlen(CabinetPrev) + 1; 03085 03086 /* Calculate size of label of previous disk */ 03087 TotalSize += (ULONG)strlen(DiskPrev) + 1; 03088 } 03089 03090 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 03091 { 03092 03093 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); 03094 03095 /* Calculate size of name of next cabinet */ 03096 Size = (ULONG)strlen(CabinetNext) + 1; 03097 TotalSize += Size; 03098 NextFieldsSize = Size; 03099 03100 /* Calculate size of label of next disk */ 03101 Size = (ULONG)strlen(DiskNext) + 1; 03102 TotalSize += Size; 03103 NextFieldsSize += Size; 03104 } 03105 else 03106 NextFieldsSize = 0; 03107 03108 /* Add cabinet reserved area size if present */ 03109 if (CabinetReservedFileSize > 0) 03110 { 03111 CABHeader.Flags |= CAB_FLAG_RESERVE; 03112 TotalSize += CabinetReservedFileSize; 03113 TotalSize += sizeof(ULONG); /* For CabinetResSize, FolderResSize, and FileResSize fields */ 03114 } 03115 03116 DiskSize += TotalSize; 03117 03118 TotalHeaderSize = sizeof(CFHEADER) + TotalSize; 03119 03120 return CAB_STATUS_SUCCESS; 03121 } 03122 03123 03124 ULONG CCabinet::WriteCabinetHeader(bool MoreDisks) 03125 /* 03126 * FUNCTION: Writes the cabinet header and optional fields 03127 * ARGUMENTS: 03128 * MoreDisks = true if next cabinet name should be included 03129 * RETURNS: 03130 * Status of operation 03131 */ 03132 { 03133 PCFFOLDER_NODE FolderNode; 03134 PCFFILE_NODE FileNode; 03135 ULONG BytesWritten; 03136 ULONG Size; 03137 03138 if (MoreDisks) 03139 { 03140 CABHeader.Flags |= CAB_FLAG_HASNEXT; 03141 Size = TotalHeaderSize; 03142 } 03143 else 03144 { 03145 CABHeader.Flags &= ~CAB_FLAG_HASNEXT; 03146 DiskSize -= NextFieldsSize; 03147 Size = TotalHeaderSize - NextFieldsSize; 03148 } 03149 03150 /* Set absolute folder offsets */ 03151 BytesWritten = Size + TotalFolderSize + TotalFileSize; 03152 CABHeader.FolderCount = 0; 03153 FolderNode = FolderListHead; 03154 while (FolderNode != NULL) 03155 { 03156 FolderNode->Folder.DataOffset = BytesWritten; 03157 03158 BytesWritten += FolderNode->TotalFolderSize; 03159 03160 CABHeader.FolderCount++; 03161 03162 FolderNode = FolderNode->Next; 03163 } 03164 03165 /* Set absolute offset of file table */ 03166 CABHeader.FileTableOffset = Size + TotalFolderSize; 03167 03168 /* Count number of files to be committed */ 03169 CABHeader.FileCount = 0; 03170 FileNode = FileListHead; 03171 while (FileNode != NULL) 03172 { 03173 if (FileNode->Commit) 03174 CABHeader.FileCount++; 03175 FileNode = FileNode->Next; 03176 } 03177 03178 CABHeader.CabinetSize = DiskSize; 03179 03180 /* Write header */ 03181 #if defined(_WIN32) 03182 if (!WriteFile(FileHandle, &CABHeader, sizeof(CFHEADER), (LPDWORD)&BytesWritten, NULL)) 03183 { 03184 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03185 return CAB_STATUS_CANNOT_WRITE; 03186 } 03187 #else 03188 BytesWritten = sizeof(CFHEADER); 03189 if (fwrite(&CABHeader, sizeof(CFHEADER), 1, FileHandle) < 1) 03190 { 03191 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03192 return CAB_STATUS_CANNOT_WRITE; 03193 } 03194 #endif 03195 03196 /* Write per-cabinet reserved area if present */ 03197 if (CABHeader.Flags & CAB_FLAG_RESERVE) 03198 { 03199 ULONG ReservedSize; 03200 03201 ReservedSize = CabinetReservedFileSize & 0xffff; 03202 ReservedSize |= (0 << 16); /* Folder reserved area size */ 03203 ReservedSize |= (0 << 24); /* Folder reserved area size */ 03204 #if defined(_WIN32) 03205 if (!WriteFile(FileHandle, &ReservedSize, sizeof(ULONG), (LPDWORD)&BytesWritten, NULL)) 03206 { 03207 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03208 return CAB_STATUS_CANNOT_WRITE; 03209 } 03210 #else 03211 BytesWritten = sizeof(ULONG); 03212 if (fwrite(&ReservedSize, sizeof(ULONG), 1, FileHandle) < 1) 03213 { 03214 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03215 return CAB_STATUS_CANNOT_WRITE; 03216 } 03217 #endif 03218 03219 #if defined(_WIN32) 03220 if (!WriteFile(FileHandle, CabinetReservedFileBuffer, CabinetReservedFileSize, (LPDWORD)&BytesWritten, NULL)) 03221 { 03222 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03223 return CAB_STATUS_CANNOT_WRITE; 03224 } 03225 #else 03226 BytesWritten = CabinetReservedFileSize; 03227 if (fwrite(CabinetReservedFileBuffer, CabinetReservedFileSize, 1, FileHandle) < 1) 03228 { 03229 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03230 return CAB_STATUS_CANNOT_WRITE; 03231 } 03232 #endif 03233 } 03234 03235 if ((CABHeader.Flags & CAB_FLAG_HASPREV) > 0) 03236 { 03237 DPRINT(MAX_TRACE, ("CabinetPrev '%s'.\n", CabinetPrev)); 03238 03239 /* Write name of previous cabinet */ 03240 Size = (ULONG)strlen(CabinetPrev) + 1; 03241 #if defined(_WIN32) 03242 if (!WriteFile(FileHandle, CabinetPrev, Size, (LPDWORD)&BytesWritten, NULL)) 03243 { 03244 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03245 return CAB_STATUS_CANNOT_WRITE; 03246 } 03247 #else 03248 BytesWritten = Size; 03249 if (fwrite(CabinetPrev, Size, 1, FileHandle) < 1) 03250 { 03251 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03252 return CAB_STATUS_CANNOT_WRITE; 03253 } 03254 #endif 03255 03256 DPRINT(MAX_TRACE, ("DiskPrev '%s'.\n", DiskPrev)); 03257 03258 /* Write label of previous disk */ 03259 Size = (ULONG)strlen(DiskPrev) + 1; 03260 #if defined(_WIN32) 03261 if (!WriteFile(FileHandle, DiskPrev, Size, (LPDWORD)&BytesWritten, NULL)) 03262 { 03263 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03264 return CAB_STATUS_CANNOT_WRITE; 03265 } 03266 #else 03267 BytesWritten = Size; 03268 if (fwrite(DiskPrev, Size, 1, FileHandle) < 1) 03269 { 03270 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03271 return CAB_STATUS_CANNOT_WRITE; 03272 } 03273 #endif 03274 } 03275 03276 if ((CABHeader.Flags & CAB_FLAG_HASNEXT) > 0) 03277 { 03278 DPRINT(MAX_TRACE, ("CabinetNext '%s'.\n", CabinetNext)); 03279 03280 /* Write name of next cabinet */ 03281 Size = (ULONG)strlen(CabinetNext) + 1; 03282 #if defined(_WIN32) 03283 if (!WriteFile(FileHandle, CabinetNext, Size, (LPDWORD)&BytesWritten, NULL)) 03284 { 03285 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03286 return CAB_STATUS_CANNOT_WRITE; 03287 } 03288 #else 03289 BytesWritten = Size; 03290 if (fwrite(CabinetNext, Size, 1, FileHandle) < 1) 03291 { 03292 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03293 return CAB_STATUS_CANNOT_WRITE; 03294 } 03295 #endif 03296 03297 DPRINT(MAX_TRACE, ("DiskNext '%s'.\n", DiskNext)); 03298 03299 /* Write label of next disk */ 03300 Size = (ULONG)strlen(DiskNext) + 1; 03301 #if defined(_WIN32) 03302 if (!WriteFile(FileHandle, DiskNext, Size, (LPDWORD)&BytesWritten, NULL)) 03303 { 03304 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03305 return CAB_STATUS_CANNOT_WRITE; 03306 } 03307 #else 03308 BytesWritten = Size; 03309 if (fwrite(DiskNext, Size, 1, FileHandle) < 1) 03310 { 03311 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03312 return CAB_STATUS_CANNOT_WRITE; 03313 } 03314 #endif 03315 } 03316 03317 return CAB_STATUS_SUCCESS; 03318 } 03319 03320 03321 ULONG CCabinet::WriteFolderEntries() 03322 /* 03323 * FUNCTION: Writes folder entries 03324 * RETURNS: 03325 * Status of operation 03326 */ 03327 { 03328 PCFFOLDER_NODE FolderNode; 03329 ULONG BytesWritten; 03330 03331 DPRINT(MAX_TRACE, ("Writing folder table.\n")); 03332 03333 FolderNode = FolderListHead; 03334 while (FolderNode != NULL) 03335 { 03336 if (FolderNode->Commit) 03337 { 03338 DPRINT(MAX_TRACE, ("Writing folder entry. CompressionType (0x%X) DataBlockCount (%d) DataOffset (0x%X).\n", 03339 FolderNode->Folder.CompressionType, FolderNode->Folder.DataBlockCount, (UINT)FolderNode->Folder.DataOffset)); 03340 03341 #if defined(_WIN32) 03342 if (!WriteFile(FileHandle, 03343 &FolderNode->Folder, 03344 sizeof(CFFOLDER), 03345 (LPDWORD)&BytesWritten, 03346 NULL)) 03347 { 03348 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03349 return CAB_STATUS_CANNOT_WRITE; 03350 } 03351 #else 03352 BytesWritten = sizeof(CFFOLDER); 03353 if (fwrite(&FolderNode->Folder, sizeof(CFFOLDER), 1, FileHandle) < 1) 03354 { 03355 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03356 return CAB_STATUS_CANNOT_WRITE; 03357 } 03358 #endif 03359 } 03360 FolderNode = FolderNode->Next; 03361 } 03362 03363 return CAB_STATUS_SUCCESS; 03364 } 03365 03366 03367 ULONG CCabinet::WriteFileEntries() 03368 /* 03369 * FUNCTION: Writes file entries for all files 03370 * RETURNS: 03371 * Status of operation 03372 */ 03373 { 03374 PCFFILE_NODE File; 03375 ULONG BytesWritten; 03376 bool SetCont = false; 03377 03378 DPRINT(MAX_TRACE, ("Writing file table.\n")); 03379 03380 File = FileListHead; 03381 while (File != NULL) 03382 { 03383 if (File->Commit) 03384 { 03385 /* Remove any continued files that ends in this disk */ 03386 if (File->File.FileControlID == CAB_FILE_CONTINUED) 03387 File->Delete = true; 03388 03389 /* The file could end in the last (split) block and should therefore 03390 appear in the next disk too */ 03391 03392 if ((File->File.FileOffset + File->File.FileSize >= LastBlockStart) && 03393 (File->File.FileControlID <= CAB_FILE_MAX_FOLDER) && (BlockIsSplit)) 03394 { 03395 File->File.FileControlID = CAB_FILE_SPLIT; 03396 File->Delete = false; 03397 SetCont = true; 03398 } 03399 03400 DPRINT(MAX_TRACE, ("Writing file entry. FileControlID (0x%X) FileOffset (0x%X) FileSize (%u) FileName (%s).\n", 03401 File->File.FileControlID, (UINT)File->File.FileOffset, (UINT)File->File.FileSize, File->FileName)); 03402 03403 #if defined(_WIN32) 03404 if (!WriteFile(FileHandle, 03405 &File->File, 03406 sizeof(CFFILE), 03407 (LPDWORD)&BytesWritten, 03408 NULL)) 03409 return CAB_STATUS_CANNOT_WRITE; 03410 #else 03411 BytesWritten = sizeof(CFFILE); 03412 if (fwrite(&File->File, sizeof(CFFILE), 1, FileHandle) < 1) 03413 { 03414 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03415 return CAB_STATUS_CANNOT_WRITE; 03416 } 03417 #endif 03418 03419 #if defined(_WIN32) 03420 if (!WriteFile(FileHandle, 03421 GetFileName(File->FileName), 03422 (DWORD)strlen(GetFileName(File->FileName)) + 1, 03423 (LPDWORD)&BytesWritten, 03424 NULL)) 03425 return CAB_STATUS_CANNOT_WRITE; 03426 #else 03427 BytesWritten = strlen(GetFileName(File->FileName)) + 1; 03428 if (fwrite(GetFileName(File->FileName), strlen(GetFileName(File->FileName)) + 1, 1, FileHandle) < 1) 03429 { 03430 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03431 return CAB_STATUS_CANNOT_WRITE; 03432 } 03433 #endif 03434 03435 if (SetCont) 03436 { 03437 File->File.FileControlID = CAB_FILE_CONTINUED; 03438 SetCont = false; 03439 } 03440 } 03441 03442 File = File->Next; 03443 } 03444 return CAB_STATUS_SUCCESS; 03445 } 03446 03447 03448 ULONG CCabinet::CommitDataBlocks(PCFFOLDER_NODE FolderNode) 03449 /* 03450 * FUNCTION: Writes data blocks to the cabinet 03451 * ARGUMENTS: 03452 * FolderNode = Pointer to folder node containing the data blocks 03453 * RETURNS: 03454 * Status of operation 03455 */ 03456 { 03457 PCFDATA_NODE DataNode; 03458 ULONG BytesWritten; 03459 ULONG BytesRead; 03460 ULONG Status; 03461 03462 DataNode = FolderNode->DataListHead; 03463 if (DataNode != NULL) 03464 Status = ScratchFile->Seek(DataNode->ScratchFilePosition); 03465 03466 while (DataNode != NULL) 03467 { 03468 DPRINT(MAX_TRACE, ("Reading block at (0x%X) CompSize (%u) UncompSize (%u).\n", 03469 (UINT)DataNode->ScratchFilePosition, 03470 DataNode->Data.CompSize, 03471 DataNode->Data.UncompSize)); 03472 03473 /* InputBuffer is free for us to use here, so we use it and avoid a 03474 memory allocation. OutputBuffer can't be used here because it may 03475 still contain valid data (if a data block spans two or more disks) */ 03476 Status = ScratchFile->ReadBlock(&DataNode->Data, InputBuffer, &BytesRead); 03477 if (Status != CAB_STATUS_SUCCESS) 03478 { 03479 DPRINT(MIN_TRACE, ("Cannot read from scratch file (%u).\n", (UINT)Status)); 03480 return Status; 03481 } 03482 03483 #if defined(_WIN32) 03484 if (!WriteFile(FileHandle, &DataNode->Data, 03485 sizeof(CFDATA), (LPDWORD)&BytesWritten, NULL)) 03486 { 03487 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03488 return CAB_STATUS_CANNOT_WRITE; 03489 } 03490 #else 03491 BytesWritten = sizeof(CFDATA); 03492 if (fwrite(&DataNode->Data, sizeof(CFDATA), 1, FileHandle) < 1) 03493 { 03494 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03495 return CAB_STATUS_CANNOT_WRITE; 03496 } 03497 #endif 03498 03499 #if defined(_WIN32) 03500 if (!WriteFile(FileHandle, InputBuffer, 03501 DataNode->Data.CompSize, (LPDWORD)&BytesWritten, NULL)) 03502 { 03503 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03504 return CAB_STATUS_CANNOT_WRITE; 03505 } 03506 #else 03507 BytesWritten = DataNode->Data.CompSize; 03508 if (fwrite(InputBuffer, DataNode->Data.CompSize, 1, FileHandle) < 1) 03509 { 03510 DPRINT(MIN_TRACE, ("Cannot write to file.\n")); 03511 return CAB_STATUS_CANNOT_WRITE; 03512 } 03513 #endif 03514 03515 DataNode = DataNode->Next; 03516 } 03517 return CAB_STATUS_SUCCESS; 03518 } 03519 03520 03521 ULONG CCabinet::WriteDataBlock() 03522 /* 03523 * FUNCTION: Writes the current data block to the scratch file 03524 * RETURNS: 03525 * Status of operation 03526 */ 03527 { 03528 ULONG Status; 03529 ULONG BytesWritten; 03530 PCFDATA_NODE DataNode; 03531 03532 if (!BlockIsSplit) 03533 { 03534 Status = Codec->Compress(OutputBuffer, 03535 InputBuffer, 03536 CurrentIBufferSize, 03537 &TotalCompSize); 03538 03539 DPRINT(MAX_TRACE, ("Block compressed. CurrentIBufferSize (%u) TotalCompSize(%u).\n", 03540 (UINT)CurrentIBufferSize, (UINT)TotalCompSize)); 03541 03542 CurrentOBuffer = OutputBuffer; 03543 CurrentOBufferSize = TotalCompSize; 03544 } 03545 03546 DataNode = NewDataNode(CurrentFolderNode); 03547 if (!DataNode) 03548 { 03549 DPRINT(MIN_TRACE, ("Insufficient memory.\n")); 03550 return CAB_STATUS_NOMEMORY; 03551 } 03552 03553 DiskSize += sizeof(CFDATA); 03554 03555 if (MaxDiskSize > 0) 03556 /* Disk size is limited */ 03557 BlockIsSplit = (DiskSize + CurrentOBufferSize > MaxDiskSize); 03558 else 03559 BlockIsSplit = false; 03560 03561 if (BlockIsSplit) 03562 { 03563 DataNode->Data.CompSize = (USHORT)(MaxDiskSize - DiskSize); 03564 DataNode->Data.UncompSize = 0; 03565 CreateNewDisk = true; 03566 } 03567 else 03568 { 03569 DataNode->Data.CompSize = (USHORT)CurrentOBufferSize; 03570 DataNode->Data.UncompSize = (USHORT)CurrentIBufferSize; 03571 } 03572 03573 DataNode->Data.Checksum = 0; 03574 DataNode->ScratchFilePosition = ScratchFile->Position(); 03575 03576 // FIXME: MAKECAB.EXE does not like this checksum algorithm 03577 //DataNode->Data.Checksum = ComputeChecksum(CurrentOBuffer, DataNode->Data.CompSize, 0); 03578 03579 DPRINT(MAX_TRACE, ("Writing block. Checksum (0x%X) CompSize (%u) UncompSize (%u).\n", 03580 (UINT)DataNode->Data.Checksum, 03581 DataNode->Data.CompSize, 03582 DataNode->Data.UncompSize)); 03583 03584 Status = ScratchFile->WriteBlock(&DataNode->Data, 03585 CurrentOBuffer, &BytesWritten); 03586 if (Status != CAB_STATUS_SUCCESS) 03587 return Status; 03588 03589 DiskSize += BytesWritten; 03590 03591 CurrentFolderNode->TotalFolderSize += (BytesWritten + sizeof(CFDATA)); 03592 CurrentFolderNode->Folder.DataBlockCount++; 03593 03594 CurrentOBuffer = (unsigned char*)CurrentOBuffer + DataNode->Data.CompSize; 03595 CurrentOBufferSize -= DataNode->Data.CompSize; 03596 03597 LastBlockStart += DataNode->Data.UncompSize; 03598 03599 if (!BlockIsSplit) 03600 { 03601 CurrentIBufferSize = 0; 03602 CurrentIBuffer = InputBuffer; 03603 } 03604 03605 return CAB_STATUS_SUCCESS; 03606 } 03607 03608 #if !defined(_WIN32) 03609 03610 void CCabinet::ConvertDateAndTime(time_t* Time, 03611 PUSHORT DosDate, 03612 PUSHORT DosTime) 03613 /* 03614 * FUNCTION: Returns file times of a file 03615 * ARGUMENTS: 03616 * FileHandle = File handle of file to get file times from 03617 * File = Pointer to CFFILE node for file 03618 * RETURNS: 03619 * Status of operation 03620 */ 03621 { 03622 struct tm *timedef; 03623 03624 timedef = localtime(Time); 03625 03626 DPRINT(MAX_TRACE, ("day: %d, mon: %d, year:%d, hour: %d, min: %d, sec: %d\n", 03627 timedef->tm_mday, timedef->tm_mon, timedef->tm_year, 03628 timedef->tm_sec, timedef->tm_min, timedef->tm_hour)); 03629 03630 *DosDate = ((timedef->tm_mday + 1) << 0) 03631 | ((timedef->tm_mon + 1) << 5) 03632 | (((timedef->tm_year + 1900) - 1980) << 9); 03633 03634 *DosTime = (timedef->tm_sec << 0) 03635 | (timedef->tm_min << 5) 03636 | (timedef->tm_hour << 11); 03637 } 03638 03639 #endif // !_WIN32 03640 03641 03642 ULONG CCabinet::GetFileTimes(FILEHANDLE FileHandle, PCFFILE_NODE File) 03643 /* 03644 * FUNCTION: Returns file times of a file 03645 * ARGUMENTS: 03646 * FileHandle = File handle of file to get file times from 03647 * File = Pointer to CFFILE node for file 03648 * RETURNS: 03649 * Status of operation 03650 */ 03651 { 03652 #if defined(_WIN32) 03653 FILETIME FileTime; 03654 03655 if (GetFileTime(FileHandle, NULL, NULL, &FileTime)) 03656 FileTimeToDosDateTime(&FileTime, 03657 &File->File.FileDate, 03658 &File->File.FileTime); 03659 #else 03660 struct stat stbuf; 03661 char buf[PATH_MAX]; 03662 03663 // Check for an absolute path 03664 if (IsSeparator(File->FileName[0])) 03665 strcpy(buf, File->FileName); 03666 else 03667 { 03668 if (!getcwd(buf, sizeof(buf))) 03669 return CAB_STATUS_CANNOT_READ; 03670 strcat(buf, DIR_SEPARATOR_STRING); 03671 strcat(buf, File->FileName); 03672 } 03673 03674 if (stat(buf, &stbuf) == -1) 03675 return CAB_STATUS_CANNOT_READ; 03676 03677 ConvertDateAndTime(&stbuf.st_mtime, &File->File.FileDate, &File->File.FileTime); 03678 #endif 03679 return CAB_STATUS_SUCCESS; 03680 } 03681 03682 03683 ULONG CCabinet::GetAttributesOnFile(PCFFILE_NODE File) 03684 /* 03685 * FUNCTION: Returns attributes on a file 03686 * ARGUMENTS: 03687 * File = Pointer to CFFILE node for file 03688 * RETURNS: 03689 * Status of operation 03690 */ 03691 { 03692 #if defined(_WIN32) 03693 LONG Attributes; 03694 03695 Attributes = GetFileAttributes(File->FileName); 03696 if (Attributes == -1) 03697 return CAB_STATUS_CANNOT_READ; 03698 03699 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE 03700 // The IDs for these attributes are the same in the CAB file and under Windows 03701 // If the file has any other attributes, strip them off by the logical AND. 03702 File->File.Attributes = (USHORT)(Attributes & 0x37); 03703 #else 03704 struct stat stbuf; 03705 char buf[PATH_MAX]; 03706 03707 // Check for an absolute path 03708 if (IsSeparator(File->FileName[0])) 03709 strcpy(buf, File->FileName); 03710 else 03711 { 03712 if (!getcwd(buf, sizeof(buf))) 03713 return CAB_STATUS_CANNOT_READ; 03714 strcat(buf, DIR_SEPARATOR_STRING); 03715 strcat(buf, File->FileName); 03716 } 03717 03718 if (stat(buf, &stbuf) == -1) 03719 return CAB_STATUS_CANNOT_READ; 03720 03721 #if 0 03722 File->File.Attributes |= CAB_ATTRIB_READONLY; 03723 File->File.Attributes |= CAB_ATTRIB_HIDDEN; 03724 File->File.Attributes |= CAB_ATTRIB_SYSTEM; 03725 #endif 03726 03727 if (stbuf.st_mode & S_IFDIR) 03728 File->File.Attributes |= CAB_ATTRIB_DIRECTORY; 03729 03730 File->File.Attributes |= CAB_ATTRIB_ARCHIVE; 03731 03732 #endif 03733 return CAB_STATUS_SUCCESS; 03734 } 03735 03736 03737 ULONG CCabinet::SetAttributesOnFile(char* FileName, USHORT FileAttributes) 03738 /* 03739 * FUNCTION: Sets attributes on a file 03740 * ARGUMENTS: 03741 * FileName = File name with path 03742 * FileAttributes = Attributes of that file 03743 * RETURNS: 03744 * Status of operation 03745 */ 03746 { 03747 #if defined(_WIN32) 03748 // 0x37 = READONLY | HIDDEN | SYSTEM | DIRECTORY | ARCHIVE 03749 // The IDs for these attributes are the same in the CAB file and under Windows 03750 // If the file has any other attributes, strip them off by the logical AND. 03751 SetFileAttributes(FileName, (DWORD)(FileAttributes & 0x37)); 03752 03753 return CAB_STATUS_SUCCESS; 03754 #else 03755 //DPRINT(MIN_TRACE, ("FIXME: SetAttributesOnFile() is unimplemented\n")); 03756 return CAB_STATUS_SUCCESS; 03757 #endif 03758 } 03759 03760 #endif /* CAB_READ_ONLY */ 03761 03762 /* EOF */ Generated on Sun May 27 2012 04:37:44 for ReactOS by
1.7.6.1
|