ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

cabinet.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.