Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencabinet.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS text-mode setup 00004 * FILE: subsys/system/usetup/cabinet.c 00005 * PURPOSE: Cabinet routines 00006 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 00007 * REVISIONS: 00008 * CSH 15/08-2003 Created 00009 */ 00010 00011 #include "usetup.h" 00012 00013 #define NDEBUG 00014 #include <debug.h> 00015 00016 #define SEEK_BEGIN 0 00017 #define SEEK_CURRENT 1 00018 #ifndef SEEK_END 00019 #define SEEK_END 2 00020 #endif 00021 00022 typedef struct _DOSTIME 00023 { 00024 WORD Second:5; 00025 WORD Minute:6; 00026 WORD Hour:5; 00027 } DOSTIME, *PDOSTIME; 00028 00029 00030 typedef struct _DOSDATE 00031 { 00032 WORD Day:5; 00033 WORD Month:4; 00034 WORD Year:5; 00035 } DOSDATE, *PDOSDATE; 00036 00037 static WCHAR CabinetName[256]; // Filename of current cabinet 00038 static WCHAR CabinetPrev[256]; // Filename of previous cabinet 00039 static WCHAR DiskPrev[256]; // Label of cabinet in file CabinetPrev 00040 static WCHAR CabinetNext[256]; // Filename of next cabinet 00041 static WCHAR DiskNext[256]; // Label of cabinet in file CabinetNext 00042 static ULONG FolderUncompSize = 0; // Uncompressed size of folder 00043 static ULONG BytesLeftInBlock = 0; // Number of bytes left in current block 00044 static WCHAR DestPath[MAX_PATH]; 00045 static HANDLE FileHandle; 00046 static HANDLE FileSectionHandle; 00047 static PUCHAR FileBuffer; 00048 static SIZE_T DestFileSize; 00049 static SIZE_T FileSize; 00050 static BOOL FileOpen = FALSE; 00051 static PCFHEADER PCABHeader; 00052 static PCFFOLDER CabinetFolders; 00053 static ULONG CabinetReserved = 0; 00054 static ULONG FolderReserved = 0; 00055 static ULONG DataReserved = 0; 00056 static ULONG CodecId; 00057 static PCABINET_CODEC_UNCOMPRESS CodecUncompress = NULL; 00058 static BOOL CodecSelected = FALSE; 00059 static ULONG LastFileOffset = 0; // Uncompressed offset of last extracted file 00060 static PCABINET_OVERWRITE OverwriteHandler = NULL; 00061 static PCABINET_EXTRACT ExtractHandler = NULL; 00062 static PCABINET_DISK_CHANGE DiskChangeHandler = NULL; 00063 static z_stream ZStream; 00064 static PVOID CabinetReservedArea = NULL; 00065 00066 00067 /* Needed by zlib, but we don't want the dependency on msvcrt.dll */ 00068 void *__cdecl 00069 malloc(size_t size) 00070 { 00071 return RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, size); 00072 } 00073 00074 void __cdecl 00075 free(void *ptr) 00076 { 00077 RtlFreeHeap(ProcessHeap, 0, ptr); 00078 } 00079 00080 void *__cdecl 00081 calloc(size_t nmemb, size_t size) 00082 { 00083 return (void *)RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, nmemb * size); 00084 } 00085 00086 /* RAW codec */ 00087 00088 /* 00089 * FUNCTION: Uncompresses data in a buffer 00090 * ARGUMENTS: 00091 * OutputBuffer = Pointer to buffer to place uncompressed data 00092 * InputBuffer = Pointer to buffer with data to be uncompressed 00093 * InputLength = Length of input buffer before, and amount consumed after 00094 * Negative to indicate that this is not the start of a new block 00095 * OutputLength = Length of output buffer before, amount filled after 00096 * Negative to indicate that this is not the end of the block 00097 */ 00098 ULONG 00099 RawCodecUncompress(PVOID OutputBuffer, 00100 PVOID InputBuffer, 00101 PLONG InputLength, 00102 PLONG OutputLength) 00103 { 00104 LONG Len = min(abs(*InputLength), abs(*OutputLength)); 00105 00106 memcpy(OutputBuffer, InputBuffer, Len); 00107 *InputLength = *OutputLength = Len; 00108 00109 return CS_SUCCESS; 00110 } 00111 00112 /* MSZIP codec */ 00113 00114 /* 00115 * FUNCTION: Uncompresses data in a buffer 00116 * ARGUMENTS: 00117 * OutputBuffer = Pointer to buffer to place uncompressed data 00118 * InputBuffer = Pointer to buffer with data to be uncompressed 00119 * InputLength = Length of input buffer before, and amount consumed after 00120 * Negative to indicate that this is not the start of a new block 00121 * OutputLength = Length of output buffer before, amount filled after 00122 * Negative to indicate that this is not the end of the block 00123 */ 00124 ULONG 00125 MSZipCodecUncompress(PVOID OutputBuffer, 00126 PVOID InputBuffer, 00127 PLONG InputLength, 00128 PLONG OutputLength) 00129 { 00130 USHORT Magic; 00131 INT Status; 00132 00133 DPRINT("MSZipCodecUncompress(OutputBuffer = %x, InputBuffer = %x, " 00134 "InputLength = %d, OutputLength = %d)\n", OutputBuffer, 00135 InputBuffer, *InputLength, *OutputLength); 00136 if (*InputLength > 0) 00137 { 00138 Magic = *(PUSHORT)InputBuffer; 00139 00140 if (Magic != MSZIP_MAGIC) 00141 { 00142 DPRINT("Bad MSZIP block header magic (0x%X)\n", Magic); 00143 return CS_BADSTREAM; 00144 } 00145 00146 ZStream.next_in = (PUCHAR)InputBuffer + 2; 00147 ZStream.avail_in = *InputLength - 2; 00148 ZStream.next_out = (PUCHAR)OutputBuffer; 00149 ZStream.avail_out = abs(*OutputLength); 00150 00151 /* WindowBits is passed < 0 to tell that there is no zlib header. 00152 * Note that in this case inflate *requires* an extra "dummy" byte 00153 * after the compressed stream in order to complete decompression and 00154 * return Z_STREAM_END. 00155 */ 00156 Status = inflateInit2(&ZStream, -MAX_WBITS); 00157 if (Status != Z_OK) 00158 { 00159 DPRINT("inflateInit2() returned (%d)\n", Status); 00160 return CS_BADSTREAM; 00161 } 00162 ZStream.total_in = 2; 00163 } 00164 else 00165 { 00166 ZStream.avail_in = -*InputLength; 00167 ZStream.next_in = (PUCHAR)InputBuffer; 00168 ZStream.next_out = (PUCHAR)OutputBuffer; 00169 ZStream.avail_out = abs(*OutputLength); 00170 ZStream.total_in = 0; 00171 } 00172 00173 ZStream.total_out = 0; 00174 Status = inflate(&ZStream, Z_SYNC_FLUSH); 00175 if (Status != Z_OK && Status != Z_STREAM_END) 00176 { 00177 DPRINT("inflate() returned (%d) (%s)\n", Status, ZStream.msg); 00178 if (Status == Z_MEM_ERROR) 00179 return CS_NOMEMORY; 00180 return CS_BADSTREAM; 00181 } 00182 00183 if (*OutputLength > 0) 00184 { 00185 Status = inflateEnd(&ZStream); 00186 if (Status != Z_OK) 00187 { 00188 DPRINT("inflateEnd() returned (%d)\n", Status); 00189 return CS_BADSTREAM; 00190 } 00191 } 00192 00193 *OutputLength = ZStream.total_out; 00194 *InputLength = ZStream.total_in; 00195 00196 return CS_SUCCESS; 00197 } 00198 00199 /* Memory functions */ 00200 00201 voidpf 00202 MSZipAlloc(voidpf opaque, uInt items, uInt size) 00203 { 00204 return (voidpf)RtlAllocateHeap(ProcessHeap, 0, items * size); 00205 } 00206 00207 void 00208 MSZipFree(voidpf opaque, voidpf address) 00209 { 00210 RtlFreeHeap(ProcessHeap, 0, address); 00211 } 00212 00213 static BOOL 00214 ConvertSystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, 00215 LPFILETIME lpFileTime) 00216 { 00217 TIME_FIELDS TimeFields; 00218 LARGE_INTEGER liTime; 00219 00220 TimeFields.Year = lpSystemTime->wYear; 00221 TimeFields.Month = lpSystemTime->wMonth; 00222 TimeFields.Day = lpSystemTime->wDay; 00223 TimeFields.Hour = lpSystemTime->wHour; 00224 TimeFields.Minute = lpSystemTime->wMinute; 00225 TimeFields.Second = lpSystemTime->wSecond; 00226 TimeFields.Milliseconds = lpSystemTime->wMilliseconds; 00227 00228 if (RtlTimeFieldsToTime(&TimeFields, &liTime)) 00229 { 00230 lpFileTime->dwLowDateTime = liTime.u.LowPart; 00231 lpFileTime->dwHighDateTime = liTime.u.HighPart; 00232 return TRUE; 00233 } 00234 00235 return FALSE; 00236 } 00237 00238 static BOOL 00239 ConvertDosDateTimeToFileTime(WORD wFatDate, 00240 WORD wFatTime, 00241 LPFILETIME lpFileTime) 00242 { 00243 PDOSTIME pdtime = (PDOSTIME)&wFatTime; 00244 PDOSDATE pddate = (PDOSDATE)&wFatDate; 00245 SYSTEMTIME SystemTime; 00246 00247 if (lpFileTime == NULL) 00248 return FALSE; 00249 00250 SystemTime.wMilliseconds = 0; 00251 SystemTime.wSecond = pdtime->Second; 00252 SystemTime.wMinute = pdtime->Minute; 00253 SystemTime.wHour = pdtime->Hour; 00254 00255 SystemTime.wDay = pddate->Day; 00256 SystemTime.wMonth = pddate->Month; 00257 SystemTime.wYear = 1980 + pddate->Year; 00258 00259 ConvertSystemTimeToFileTime(&SystemTime, lpFileTime); 00260 00261 return TRUE; 00262 } 00263 00264 /* 00265 * FUNCTION: Returns a pointer to file name 00266 * ARGUMENTS: 00267 * Path = Pointer to string with pathname 00268 * RETURNS: 00269 * Pointer to filename 00270 */ 00271 static PWCHAR 00272 GetFileName(PWCHAR Path) 00273 { 00274 ULONG i, j; 00275 00276 j = i = 0; 00277 00278 while (Path[i++]) 00279 { 00280 if (Path[i - 1] == L'\\') 00281 j = i; 00282 } 00283 00284 return Path + j; 00285 } 00286 00287 /* 00288 * FUNCTION: Removes a file name from a path 00289 * ARGUMENTS: 00290 * Path = Pointer to string with path 00291 */ 00292 static VOID 00293 RemoveFileName(PWCHAR Path) 00294 { 00295 PWCHAR FileName; 00296 DWORD i; 00297 00298 i = 0; 00299 FileName = GetFileName(Path + i); 00300 00301 if (FileName != Path + i && FileName[-1] == L'\\') 00302 FileName--; 00303 00304 if (FileName == Path + i && FileName[0] == L'\\') 00305 FileName++; 00306 00307 FileName[0] = 0; 00308 } 00309 00310 /* 00311 * FUNCTION: Sets attributes on a file 00312 * ARGUMENTS: 00313 * File = Pointer to CFFILE node for file 00314 * RETURNS: 00315 * Status of operation 00316 */ 00317 static BOOL 00318 SetAttributesOnFile(PCFFILE File, 00319 HANDLE hFile) 00320 { 00321 FILE_BASIC_INFORMATION FileBasic; 00322 IO_STATUS_BLOCK IoStatusBlock; 00323 NTSTATUS NtStatus; 00324 ULONG Attributes = 0; 00325 00326 if (File->Attributes & CAB_ATTRIB_READONLY) 00327 Attributes |= FILE_ATTRIBUTE_READONLY; 00328 00329 if (File->Attributes & CAB_ATTRIB_HIDDEN) 00330 Attributes |= FILE_ATTRIBUTE_HIDDEN; 00331 00332 if (File->Attributes & CAB_ATTRIB_SYSTEM) 00333 Attributes |= FILE_ATTRIBUTE_SYSTEM; 00334 00335 if (File->Attributes & CAB_ATTRIB_DIRECTORY) 00336 Attributes |= FILE_ATTRIBUTE_DIRECTORY; 00337 00338 if (File->Attributes & CAB_ATTRIB_ARCHIVE) 00339 Attributes |= FILE_ATTRIBUTE_ARCHIVE; 00340 00341 NtStatus = NtQueryInformationFile(hFile, 00342 &IoStatusBlock, 00343 &FileBasic, 00344 sizeof(FILE_BASIC_INFORMATION), 00345 FileBasicInformation); 00346 if (!NT_SUCCESS(NtStatus)) 00347 { 00348 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus); 00349 } 00350 else 00351 { 00352 FileBasic.FileAttributes = Attributes; 00353 00354 NtStatus = NtSetInformationFile(hFile, 00355 &IoStatusBlock, 00356 &FileBasic, 00357 sizeof(FILE_BASIC_INFORMATION), 00358 FileBasicInformation); 00359 if (!NT_SUCCESS(NtStatus)) 00360 { 00361 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus); 00362 } 00363 } 00364 00365 return NT_SUCCESS(NtStatus); 00366 } 00367 00368 /* 00369 * FUNCTION: Closes the current cabinet 00370 * RETURNS: 00371 * Status of operation 00372 */ 00373 static ULONG 00374 CloseCabinet(VOID) 00375 { 00376 if (FileBuffer) 00377 { 00378 NtUnmapViewOfSection(NtCurrentProcess(), FileBuffer); 00379 NtClose(FileSectionHandle); 00380 NtClose(FileHandle); 00381 FileBuffer = NULL; 00382 } 00383 00384 return 0; 00385 } 00386 00387 /* 00388 * FUNCTION: Initialize archiver 00389 */ 00390 VOID 00391 CabinetInitialize(VOID) 00392 { 00393 ZStream.zalloc = MSZipAlloc; 00394 ZStream.zfree = MSZipFree; 00395 ZStream.opaque = (voidpf)0; 00396 00397 FileOpen = FALSE; 00398 wcscpy(DestPath, L""); 00399 00400 CodecId = CAB_CODEC_RAW; 00401 CodecSelected = TRUE; 00402 00403 FolderUncompSize = 0; 00404 BytesLeftInBlock = 0; 00405 CabinetReserved = 0; 00406 FolderReserved = 0; 00407 DataReserved = 0; 00408 CabinetReservedArea = NULL; 00409 LastFileOffset = 0; 00410 } 00411 00412 /* 00413 * FUNCTION: Cleanup archiver 00414 */ 00415 VOID 00416 CabinetCleanup(VOID) 00417 { 00418 CabinetClose(); 00419 } 00420 00421 /* 00422 * FUNCTION: Normalizes a path 00423 * ARGUMENTS: 00424 * Path = Pointer to string with pathname 00425 * Length = Number of characters in Path 00426 * RETURNS: 00427 * TRUE if there was enough room in Path, or FALSE 00428 */ 00429 BOOL 00430 CabinetNormalizePath(PWCHAR Path, 00431 ULONG Length) 00432 { 00433 ULONG n; 00434 BOOL Ok; 00435 00436 n = wcslen(Path); 00437 Ok = (n + 1) < Length; 00438 00439 if (n != 0 && Path[n - 1] != L'\\' && Ok) 00440 { 00441 Path[n] = L'\\'; 00442 Path[n + 1] = 0; 00443 } 00444 00445 return Ok; 00446 } 00447 00448 /* 00449 * FUNCTION: Returns pointer to cabinet file name 00450 * RETURNS: 00451 * Pointer to string with name of cabinet 00452 */ 00453 PWCHAR 00454 CabinetGetCabinetName(VOID) 00455 { 00456 return CabinetName; 00457 } 00458 00459 /* 00460 * FUNCTION: Sets cabinet file name 00461 * ARGUMENTS: 00462 * FileName = Pointer to string with name of cabinet 00463 */ 00464 VOID 00465 CabinetSetCabinetName(PWCHAR FileName) 00466 { 00467 wcscpy(CabinetName, FileName); 00468 } 00469 00470 /* 00471 * FUNCTION: Sets destination path 00472 * ARGUMENTS: 00473 * DestinationPath = Pointer to string with name of destination path 00474 */ 00475 VOID 00476 CabinetSetDestinationPath(PWCHAR DestinationPath) 00477 { 00478 wcscpy(DestPath, DestinationPath); 00479 00480 if (wcslen(DestPath) > 0) 00481 CabinetNormalizePath(DestPath, MAX_PATH); 00482 } 00483 00484 /* 00485 * FUNCTION: Returns destination path 00486 * RETURNS: 00487 * Pointer to string with name of destination path 00488 */ 00489 PWCHAR 00490 CabinetGetDestinationPath(VOID) 00491 { 00492 return DestPath; 00493 } 00494 00495 /* 00496 * FUNCTION: Opens a cabinet file 00497 * RETURNS: 00498 * Status of operation 00499 */ 00500 ULONG 00501 CabinetOpen(VOID) 00502 { 00503 PUCHAR Buffer; 00504 UNICODE_STRING ustring; 00505 ANSI_STRING astring; 00506 00507 if (!FileOpen) 00508 { 00509 OBJECT_ATTRIBUTES ObjectAttributes; 00510 IO_STATUS_BLOCK IoStatusBlock; 00511 UNICODE_STRING FileName; 00512 NTSTATUS NtStatus; 00513 00514 RtlInitUnicodeString(&FileName, CabinetName); 00515 00516 InitializeObjectAttributes(&ObjectAttributes, 00517 &FileName, 00518 OBJ_CASE_INSENSITIVE, 00519 NULL, NULL); 00520 00521 NtStatus = NtOpenFile(&FileHandle, 00522 GENERIC_READ | SYNCHRONIZE, 00523 &ObjectAttributes, 00524 &IoStatusBlock, 00525 FILE_SHARE_READ, 00526 FILE_SYNCHRONOUS_IO_NONALERT); 00527 00528 if (!NT_SUCCESS(NtStatus)) 00529 { 00530 DPRINT("Cannot open file (%S) (%x)\n", CabinetName, NtStatus); 00531 return CAB_STATUS_CANNOT_OPEN; 00532 } 00533 00534 FileOpen = TRUE; 00535 00536 NtStatus = NtCreateSection(&FileSectionHandle, 00537 SECTION_ALL_ACCESS, 00538 0, 0, 00539 PAGE_READONLY, 00540 SEC_COMMIT, 00541 FileHandle); 00542 00543 if (!NT_SUCCESS(NtStatus)) 00544 { 00545 DPRINT("NtCreateSection failed: %x\n", NtStatus); 00546 return CAB_STATUS_NOMEMORY; 00547 } 00548 00549 FileBuffer = 0; 00550 FileSize = 0; 00551 00552 NtStatus = NtMapViewOfSection(FileSectionHandle, 00553 NtCurrentProcess(), 00554 (PVOID *)&FileBuffer, 00555 0, 0, 0, 00556 &FileSize, 00557 ViewUnmap, 00558 0, 00559 PAGE_READONLY); 00560 00561 if (!NT_SUCCESS(NtStatus)) 00562 { 00563 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus); 00564 return CAB_STATUS_NOMEMORY; 00565 } 00566 00567 DPRINT("Cabinet file %S opened and mapped to %x\n", CabinetName, FileBuffer); 00568 PCABHeader = (PCFHEADER) FileBuffer; 00569 00570 /* Check header */ 00571 if (FileSize <= sizeof(CFHEADER) || 00572 PCABHeader->Signature != CAB_SIGNATURE || 00573 PCABHeader->Version != CAB_VERSION || 00574 PCABHeader->FolderCount == 0 || 00575 PCABHeader->FileCount == 0 || 00576 PCABHeader->FileTableOffset < sizeof(CFHEADER)) 00577 { 00578 CloseCabinet(); 00579 DPRINT("File has invalid header\n"); 00580 return CAB_STATUS_INVALID_CAB; 00581 } 00582 00583 Buffer = (PUCHAR)(PCABHeader + 1); 00584 00585 /* Read/skip any reserved bytes */ 00586 if (PCABHeader->Flags & CAB_FLAG_RESERVE) 00587 { 00588 CabinetReserved = *(PUSHORT)Buffer; 00589 Buffer += 2; 00590 FolderReserved = *Buffer; 00591 Buffer++; 00592 DataReserved = *Buffer; 00593 Buffer++; 00594 00595 if (CabinetReserved > 0) 00596 { 00597 CabinetReservedArea = Buffer; 00598 Buffer += CabinetReserved; 00599 } 00600 } 00601 00602 if (PCABHeader->Flags & CAB_FLAG_HASPREV) 00603 { 00604 /* The previous cabinet file is in 00605 the same directory as the current */ 00606 wcscpy(CabinetPrev, CabinetName); 00607 RemoveFileName(CabinetPrev); 00608 CabinetNormalizePath(CabinetPrev, 256); 00609 RtlInitAnsiString(&astring, (LPSTR)Buffer); 00610 ustring.Length = wcslen(CabinetPrev); 00611 ustring.Buffer = CabinetPrev + ustring.Length; 00612 ustring.MaximumLength = sizeof(CabinetPrev) - ustring.Length; 00613 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 00614 Buffer += astring.Length + 1; 00615 00616 /* Read label of prev disk */ 00617 RtlInitAnsiString(&astring, (LPSTR)Buffer); 00618 ustring.Length = 0; 00619 ustring.Buffer = DiskPrev; 00620 ustring.MaximumLength = sizeof(DiskPrev); 00621 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 00622 Buffer += astring.Length + 1; 00623 } 00624 else 00625 { 00626 wcscpy(CabinetPrev, L""); 00627 wcscpy(DiskPrev, L""); 00628 } 00629 00630 if (PCABHeader->Flags & CAB_FLAG_HASNEXT) 00631 { 00632 /* The next cabinet file is in 00633 the same directory as the previous */ 00634 wcscpy(CabinetNext, CabinetName); 00635 RemoveFileName(CabinetNext); 00636 CabinetNormalizePath(CabinetNext, 256); 00637 RtlInitAnsiString(&astring, (LPSTR)Buffer); 00638 ustring.Length = wcslen(CabinetNext); 00639 ustring.Buffer = CabinetNext + ustring.Length; 00640 ustring.MaximumLength = sizeof(CabinetNext) - ustring.Length; 00641 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 00642 Buffer += astring.Length + 1; 00643 00644 /* Read label of next disk */ 00645 RtlInitAnsiString(&astring, (LPSTR)Buffer); 00646 ustring.Length = 0; 00647 ustring.Buffer = DiskNext; 00648 ustring.MaximumLength = sizeof(DiskNext); 00649 RtlAnsiStringToUnicodeString(&ustring, &astring, FALSE); 00650 Buffer += astring.Length + 1; 00651 } 00652 else 00653 { 00654 wcscpy(CabinetNext, L""); 00655 wcscpy(DiskNext, L""); 00656 } 00657 CabinetFolders = (PCFFOLDER)Buffer; 00658 } 00659 00660 DPRINT("CabinetOpen returning SUCCESS\n"); 00661 return CAB_STATUS_SUCCESS; 00662 } 00663 00664 /* 00665 * FUNCTION: Closes the cabinet file 00666 */ 00667 VOID 00668 CabinetClose(VOID) 00669 { 00670 if (FileOpen) 00671 { 00672 CloseCabinet(); 00673 FileOpen = FALSE; 00674 } 00675 } 00676 00677 /* 00678 * FUNCTION: Finds the first file in the cabinet that matches a search criteria 00679 * ARGUMENTS: 00680 * FileName = Pointer to search criteria 00681 * Search = Pointer to search structure 00682 * RETURNS: 00683 * Status of operation 00684 */ 00685 ULONG 00686 CabinetFindFirst(PWCHAR FileName, 00687 PCAB_SEARCH Search) 00688 { 00689 DPRINT("CabinetFindFirst( FileName = %S )\n", FileName); 00690 wcsncpy(Search->Search, FileName, MAX_PATH); 00691 wcsncpy(Search->Cabinet, CabinetName, MAX_PATH); 00692 Search->File = 0; 00693 return CabinetFindNext(Search); 00694 } 00695 00696 /* 00697 * FUNCTION: Finds the next file in the cabinet that matches a search criteria 00698 * ARGUMENTS: 00699 * FileName = Pointer to search criteria 00700 * Search = Pointer to search structure 00701 * RETURNS: 00702 * Status of operation 00703 */ 00704 ULONG 00705 CabinetFindNextFileSequential(PWCHAR FileName, 00706 PCAB_SEARCH Search) 00707 { 00708 DPRINT("CabinetFindNextFileSequential( FileName = %S )\n", FileName); 00709 wcsncpy(Search->Search, FileName, MAX_PATH); 00710 return CabinetFindNext(Search); 00711 } 00712 00713 /* 00714 * FUNCTION: Finds next file in the cabinet that matches a search criteria 00715 * ARGUMENTS: 00716 * Search = Pointer to search structure 00717 * RETURNS: 00718 * Status of operation 00719 */ 00720 ULONG 00721 CabinetFindNext(PCAB_SEARCH Search) 00722 { 00723 PCFFILE Prev; 00724 ANSI_STRING AnsiString; 00725 UNICODE_STRING UnicodeString; 00726 WCHAR FileName[MAX_PATH]; 00727 00728 if (wcscmp(Search->Cabinet, CabinetName) != 0) 00729 { 00730 /* restart search of cabinet has changed since last find */ 00731 Search->File = 0; 00732 } 00733 00734 if (!Search->File) 00735 { 00736 /* starting new search or cabinet */ 00737 Search->File = (PCFFILE)(FileBuffer + PCABHeader->FileTableOffset); 00738 Search->Index = 0; 00739 Prev = 0; 00740 } 00741 else 00742 Prev = Search->File; 00743 00744 while (TRUE) 00745 { 00746 /* look at each file in the archive and see if we found a match */ 00747 if (Search->File->FolderIndex == 0xFFFD || 00748 Search->File->FolderIndex == 0xFFFF) 00749 { 00750 /* skip files continued from previous cab */ 00751 DPRINT("Skipping file (%s): FileOffset (0x%X), " 00752 "LastFileOffset (0x%X)\n", (char *)(Search->File + 1), 00753 Search->File->FileOffset, LastFileOffset); 00754 } 00755 else 00756 { 00757 // FIXME: check for match against search criteria 00758 if (Search->File != Prev) 00759 { 00760 if (Prev == NULL || Search->File->FolderIndex != Prev->FolderIndex) 00761 { 00762 Search->CFData = NULL; 00763 Search->Offset = 0; 00764 } 00765 00766 /* don't match the file we started with */ 00767 if (wcscmp(Search->Search, L"*") == 0) 00768 { 00769 /* take any file */ 00770 break; 00771 } 00772 else 00773 { 00774 /* otherwise, try to match the exact file name */ 00775 RtlInitAnsiString(&AnsiString, Search->File->FileName); 00776 UnicodeString.Buffer = FileName; 00777 UnicodeString.Buffer[0] = 0; 00778 UnicodeString.Length = 0; 00779 UnicodeString.MaximumLength = sizeof(FileName); 00780 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 00781 if (wcscmp(Search->Search, UnicodeString.Buffer) == 0) 00782 break; 00783 } 00784 } 00785 } 00786 00787 /* if we make it here we found no match, so move to the next file */ 00788 Search->Index++; 00789 if (Search->Index >= PCABHeader->FileCount) 00790 { 00791 /* we have reached the end of this cabinet */ 00792 DPRINT("End of cabinet reached\n"); 00793 return CAB_STATUS_NOFILE; 00794 } 00795 else 00796 Search->File = (PCFFILE)(strchr((char *)(Search->File + 1), 0) + 1); 00797 } 00798 00799 DPRINT("Found file %s\n", Search->File->FileName); 00800 return CAB_STATUS_SUCCESS; 00801 } 00802 00803 #if 0 00804 int 00805 Validate() 00806 { 00807 return (int)RtlValidateHeap(ProcessHeap, 0, 0); 00808 } 00809 #endif 00810 00811 /* 00812 * FUNCTION: Extracts a file from the cabinet 00813 * ARGUMENTS: 00814 * Search = Pointer to PCAB_SEARCH structure used to locate the file 00815 * RETURNS 00816 * Status of operation 00817 */ 00818 ULONG 00819 CabinetExtractFile(PCAB_SEARCH Search) 00820 { 00821 ULONG Size; // remaining file bytes to decompress 00822 ULONG CurrentOffset; // current uncompressed offset within the folder 00823 PUCHAR CurrentBuffer; // current pointer to compressed data in the block 00824 LONG RemainingBlock; // remaining comp data in the block 00825 HANDLE DestFile; 00826 HANDLE DestFileSection; 00827 PVOID DestFileBuffer; // mapped view of dest file 00828 PVOID CurrentDestBuffer; // pointer to the current position in the dest view 00829 PCFDATA CFData; // current data block 00830 ULONG Status; 00831 FILETIME FileTime; 00832 WCHAR DestName[MAX_PATH]; 00833 NTSTATUS NtStatus; 00834 UNICODE_STRING UnicodeString; 00835 ANSI_STRING AnsiString; 00836 IO_STATUS_BLOCK IoStatusBlock; 00837 OBJECT_ATTRIBUTES ObjectAttributes; 00838 FILE_BASIC_INFORMATION FileBasic; 00839 PCFFOLDER CurrentFolder; 00840 LARGE_INTEGER MaxDestFileSize; 00841 LONG InputLength, OutputLength; 00842 char Junk[512]; 00843 00844 if (wcscmp(Search->Cabinet, CabinetName) != 0) 00845 { 00846 /* the file is not in the current cabinet */ 00847 DPRINT("File is not in this cabinet (%S != %S)\n", 00848 Search->Cabinet, CabinetName); 00849 return CAB_STATUS_NOFILE; 00850 } 00851 00852 /* look up the folder that the file specifies */ 00853 if (Search->File->FolderIndex == 0xFFFD || 00854 Search->File->FolderIndex == 0xFFFF) 00855 { 00856 /* folder is continued from previous cabinet, 00857 that shouldn't happen here */ 00858 return CAB_STATUS_NOFILE; 00859 } 00860 else if (Search->File->FolderIndex == 0xFFFE) 00861 { 00862 /* folder is the last in this cabinet and continues into next */ 00863 CurrentFolder = &CabinetFolders[PCABHeader->FolderCount - 1]; 00864 } 00865 else 00866 { 00867 /* folder is completely contained within this cabinet */ 00868 CurrentFolder = &CabinetFolders[Search->File->FolderIndex]; 00869 } 00870 00871 switch (CurrentFolder->CompressionType & CAB_COMP_MASK) 00872 { 00873 case CAB_COMP_NONE: 00874 CabinetSelectCodec(CAB_CODEC_RAW); 00875 break; 00876 case CAB_COMP_MSZIP: 00877 CabinetSelectCodec(CAB_CODEC_MSZIP); 00878 break; 00879 default: 00880 return CAB_STATUS_UNSUPPCOMP; 00881 } 00882 00883 DPRINT("Extracting file at uncompressed offset (0x%X) Size (%d bytes)\n", 00884 (UINT)Search->File->FileOffset, (UINT)Search->File->FileSize); 00885 00886 RtlInitAnsiString(&AnsiString, Search->File->FileName); 00887 wcscpy(DestName, DestPath); 00888 UnicodeString.MaximumLength = sizeof(DestName) - wcslen(DestName) * sizeof(WCHAR); 00889 UnicodeString.Buffer = DestName + wcslen(DestName); 00890 UnicodeString.Length = 0; 00891 RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); 00892 00893 /* Create destination file, fail if it already exists */ 00894 RtlInitUnicodeString(&UnicodeString, DestName); 00895 00896 InitializeObjectAttributes(&ObjectAttributes, 00897 &UnicodeString, 00898 OBJ_CASE_INSENSITIVE, 00899 NULL, NULL); 00900 00901 NtStatus = NtCreateFile(&DestFile, 00902 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 00903 &ObjectAttributes, 00904 &IoStatusBlock, 00905 NULL, 00906 FILE_ATTRIBUTE_NORMAL, 00907 0, 00908 FILE_CREATE, 00909 FILE_SYNCHRONOUS_IO_NONALERT, 00910 NULL, 0); 00911 00912 if (!NT_SUCCESS(NtStatus)) 00913 { 00914 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 00915 00916 /* If file exists, ask to overwrite file */ 00917 if (OverwriteHandler == NULL || OverwriteHandler(Search->File, DestName)) 00918 { 00919 /* Create destination file, overwrite if it already exists */ 00920 NtStatus = NtCreateFile(&DestFile, 00921 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 00922 &ObjectAttributes, 00923 &IoStatusBlock, 00924 NULL, 00925 FILE_ATTRIBUTE_NORMAL, 00926 0, 00927 FILE_OVERWRITE, 00928 FILE_SYNCHRONOUS_IO_ALERT, 00929 NULL, 0); 00930 00931 if (!NT_SUCCESS(NtStatus)) 00932 { 00933 DPRINT("NtCreateFile() failed (%S) (%x)\n", DestName, NtStatus); 00934 return CAB_STATUS_CANNOT_CREATE; 00935 } 00936 } 00937 else 00938 { 00939 DPRINT("File (%S) exists\n", DestName); 00940 return CAB_STATUS_FILE_EXISTS; 00941 } 00942 } 00943 00944 MaxDestFileSize.QuadPart = Search->File->FileSize; 00945 NtStatus = NtCreateSection(&DestFileSection, 00946 SECTION_ALL_ACCESS, 00947 0, 00948 &MaxDestFileSize, 00949 PAGE_READWRITE, 00950 SEC_COMMIT, 00951 DestFile); 00952 00953 if (!NT_SUCCESS(NtStatus)) 00954 { 00955 DPRINT("NtCreateSection failed: %x\n", NtStatus); 00956 Status = CAB_STATUS_NOMEMORY; 00957 goto CloseDestFile; 00958 } 00959 00960 DestFileBuffer = 0; 00961 DestFileSize = 0; 00962 NtStatus = NtMapViewOfSection(DestFileSection, 00963 NtCurrentProcess(), 00964 &DestFileBuffer, 00965 0, 0, 0, 00966 &DestFileSize, 00967 ViewUnmap, 00968 0, 00969 PAGE_READWRITE); 00970 00971 if (!NT_SUCCESS(NtStatus)) 00972 { 00973 DPRINT("NtMapViewOfSection failed: %x\n", NtStatus); 00974 Status = CAB_STATUS_NOMEMORY; 00975 goto CloseDestFileSection; 00976 } 00977 00978 CurrentDestBuffer = DestFileBuffer; 00979 if (!ConvertDosDateTimeToFileTime(Search->File->FileDate, 00980 Search->File->FileTime, 00981 &FileTime)) 00982 { 00983 DPRINT("DosDateTimeToFileTime() failed\n"); 00984 Status = CAB_STATUS_CANNOT_WRITE; 00985 goto UnmapDestFile; 00986 } 00987 00988 NtStatus = NtQueryInformationFile(DestFile, 00989 &IoStatusBlock, 00990 &FileBasic, 00991 sizeof(FILE_BASIC_INFORMATION), 00992 FileBasicInformation); 00993 if (!NT_SUCCESS(NtStatus)) 00994 { 00995 DPRINT("NtQueryInformationFile() failed (%x)\n", NtStatus); 00996 } 00997 else 00998 { 00999 memcpy(&FileBasic.LastAccessTime, &FileTime, sizeof(FILETIME)); 01000 01001 NtStatus = NtSetInformationFile(DestFile, 01002 &IoStatusBlock, 01003 &FileBasic, 01004 sizeof(FILE_BASIC_INFORMATION), 01005 FileBasicInformation); 01006 if (!NT_SUCCESS(NtStatus)) 01007 { 01008 DPRINT("NtSetInformationFile() failed (%x)\n", NtStatus); 01009 } 01010 } 01011 01012 SetAttributesOnFile(Search->File, DestFile); 01013 01014 /* Call extract event handler */ 01015 if (ExtractHandler != NULL) 01016 { 01017 ExtractHandler(Search->File, DestName); 01018 } 01019 01020 if (Search->CFData) 01021 CFData = Search->CFData; 01022 else 01023 CFData = (PCFDATA)(CabinetFolders[Search->File->FolderIndex].DataOffset + FileBuffer); 01024 01025 CurrentOffset = Search->Offset; 01026 while (CurrentOffset + CFData->UncompSize <= Search->File->FileOffset) 01027 { 01028 /* walk the data blocks until we reach 01029 the one containing the start of the file */ 01030 CurrentOffset += CFData->UncompSize; 01031 CFData = (PCFDATA)((char *)(CFData + 1) + DataReserved + CFData->CompSize); 01032 } 01033 01034 Search->CFData = CFData; 01035 Search->Offset = CurrentOffset; 01036 01037 /* now decompress and discard any data in 01038 the block before the start of the file */ 01039 01040 /* start of comp data */ 01041 CurrentBuffer = ((unsigned char *)(CFData + 1)) + DataReserved; 01042 RemainingBlock = CFData->CompSize; 01043 InputLength = RemainingBlock; 01044 01045 while (CurrentOffset < Search->File->FileOffset) 01046 { 01047 /* compute remaining uncomp bytes to start 01048 of file, bounded by sizeof junk */ 01049 OutputLength = Search->File->FileOffset - CurrentOffset; 01050 if (OutputLength > (LONG)sizeof(Junk)) 01051 OutputLength = sizeof (Junk); 01052 01053 /* negate to signal NOT end of block */ 01054 OutputLength = -OutputLength; 01055 CodecUncompress(Junk, CurrentBuffer, &InputLength, &OutputLength); 01056 /* add the uncomp bytes extracted to current folder offset */ 01057 CurrentOffset += OutputLength; 01058 /* add comp bytes consumed to CurrentBuffer */ 01059 CurrentBuffer += InputLength; 01060 /* subtract bytes consumed from bytes remaining in block */ 01061 RemainingBlock -= InputLength; 01062 /* neg for resume decompression of the same block */ 01063 InputLength = -RemainingBlock; 01064 } 01065 01066 /* now CurrentBuffer points to the first comp byte 01067 of the file, so we can begin decompressing */ 01068 01069 /* Size = remaining uncomp bytes of the file to decompress */ 01070 Size = Search->File->FileSize; 01071 while (Size > 0) 01072 { 01073 OutputLength = Size; 01074 DPRINT("Decompressing block at %x with RemainingBlock = %d, Size = %d\n", 01075 CurrentBuffer, RemainingBlock, Size); 01076 01077 Status = CodecUncompress(CurrentDestBuffer, 01078 CurrentBuffer, 01079 &InputLength, 01080 &OutputLength); 01081 01082 if (Status != CS_SUCCESS) 01083 { 01084 DPRINT("Cannot uncompress block\n"); 01085 if (Status == CS_NOMEMORY) 01086 Status = CAB_STATUS_NOMEMORY; 01087 Status = CAB_STATUS_INVALID_CAB; 01088 goto UnmapDestFile; 01089 } 01090 01091 /* advance dest buffer by bytes produced */ 01092 CurrentDestBuffer = (PVOID)((ULONG_PTR)CurrentDestBuffer + OutputLength); 01093 /* advance src buffer by bytes consumed */ 01094 CurrentBuffer += InputLength; 01095 /* reduce remaining file bytes by bytes produced */ 01096 Size -= OutputLength; 01097 /* reduce remaining block size by bytes consumed */ 01098 RemainingBlock -= InputLength; 01099 if (RemainingBlock == 0) 01100 { 01101 /* used up this block, move on to the next */ 01102 DPRINT("Out of block data\n"); 01103 CFData = (PCFDATA)CurrentBuffer; 01104 RemainingBlock = CFData->CompSize; 01105 CurrentBuffer = (unsigned char *)(CFData + 1) + DataReserved; 01106 InputLength = RemainingBlock; 01107 } 01108 } 01109 01110 Status = CAB_STATUS_SUCCESS; 01111 01112 UnmapDestFile: 01113 NtUnmapViewOfSection(NtCurrentProcess(), DestFileBuffer); 01114 01115 CloseDestFileSection: 01116 NtClose(DestFileSection); 01117 01118 CloseDestFile: 01119 NtClose(DestFile); 01120 01121 return Status; 01122 } 01123 01124 /* 01125 * FUNCTION: Selects codec engine to use 01126 * ARGUMENTS: 01127 * Id = Codec identifier 01128 */ 01129 VOID 01130 CabinetSelectCodec(ULONG Id) 01131 { 01132 if (CodecSelected) 01133 { 01134 if (Id == CodecId) 01135 return; 01136 01137 CodecSelected = FALSE; 01138 } 01139 01140 switch (Id) 01141 { 01142 case CAB_CODEC_RAW: 01143 CodecUncompress = RawCodecUncompress; 01144 break; 01145 case CAB_CODEC_MSZIP: 01146 CodecUncompress = MSZipCodecUncompress; 01147 break; 01148 default: 01149 return; 01150 } 01151 01152 CodecId = Id; 01153 CodecSelected = TRUE; 01154 } 01155 01156 /* 01157 * FUNCTION: Set event handlers 01158 * ARGUMENTS: 01159 * Overwrite = Handler called when a file is to be overwritten 01160 * Extract = Handler called when a file is to be extracted 01161 * DiskChange = Handler called when changing the disk 01162 */ 01163 VOID 01164 CabinetSetEventHandlers(PCABINET_OVERWRITE Overwrite, 01165 PCABINET_EXTRACT Extract, 01166 PCABINET_DISK_CHANGE DiskChange) 01167 { 01168 OverwriteHandler = Overwrite; 01169 ExtractHandler = Extract; 01170 DiskChangeHandler = DiskChange; 01171 } 01172 01173 /* 01174 * FUNCTION: Get pointer to cabinet reserved area. NULL if none 01175 */ 01176 PVOID 01177 CabinetGetCabinetReservedArea(PULONG Size) 01178 { 01179 if (CabinetReservedArea != NULL) 01180 { 01181 if (Size != NULL) 01182 { 01183 *Size = CabinetReserved; 01184 } 01185 01186 return CabinetReservedArea; 01187 } 01188 else 01189 { 01190 if (Size != NULL) 01191 { 01192 *Size = 0; 01193 } 01194 01195 return NULL; 01196 } 01197 } Generated on Sun May 27 2012 04:17:57 for ReactOS by
1.7.6.1
|