1557 lines
38 KiB
C
1557 lines
38 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
decomp.c
|
|
|
|
Abstract:
|
|
|
|
File decompression support routines.
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) 1-Feb-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <pshpack1.h>
|
|
struct LZINFO;
|
|
typedef struct LZINFO *PLZINFO;
|
|
#include <lz_header.h>
|
|
#include <poppack.h>
|
|
|
|
|
|
typedef struct _SFD_INFO {
|
|
unsigned FileCount;
|
|
PCTSTR TargetFile;
|
|
BOOL GotTimestamp;
|
|
FILETIME FileTime;
|
|
} SFD_INFO, *PSFD_INFO;
|
|
|
|
|
|
UINT
|
|
pGetCompressInfoCB(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
);
|
|
|
|
UINT
|
|
pSingleFileDecompCB(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
);
|
|
|
|
//
|
|
// OldMyMalloc/OldMyFree used by SetupGetFileCompressionInfo
|
|
// for app-compat
|
|
//
|
|
PVOID
|
|
OldMyMalloc(
|
|
IN DWORD Size
|
|
);
|
|
|
|
VOID
|
|
OldMyFree(
|
|
IN PVOID Block
|
|
);
|
|
|
|
PTSTR
|
|
SetupGenerateCompressedName(
|
|
IN PCTSTR Filename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a filename, generate the compressed form of the name.
|
|
The compressed form is generated as follows:
|
|
|
|
Look backwards for a dot. If there is no dot, append "._" to the name.
|
|
If there is a dot followed by 0, 1, or 2 charcaters, append "_".
|
|
Otherwise there is a 3-character or greater extension and we replace
|
|
the last character with "_".
|
|
|
|
Arguments:
|
|
|
|
Filename - supplies filename whose compressed form is desired.
|
|
|
|
Return Value:
|
|
|
|
Pointer to buffer containing nul-terminated compressed-form filename.
|
|
The caller must free this buffer via MyFree().
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR CompressedName,p,q;
|
|
UINT u;
|
|
|
|
//
|
|
// The maximum length of the compressed filename is the length of the
|
|
// original name plus 2 (for ._).
|
|
//
|
|
if(CompressedName = MyMalloc((lstrlen(Filename)+3)*sizeof(TCHAR))) {
|
|
|
|
lstrcpy(CompressedName,Filename);
|
|
|
|
p = _tcsrchr(CompressedName,TEXT('.'));
|
|
q = _tcsrchr(CompressedName,TEXT('\\'));
|
|
if(q < p) {
|
|
|
|
//
|
|
// If there are 0, 1, or 2 characters after the dot, just append
|
|
// the underscore. p points to the dot so include that in the length.
|
|
//
|
|
u = lstrlen(p);
|
|
if(u < 4) {
|
|
lstrcat(CompressedName,TEXT("_"));
|
|
} else {
|
|
//
|
|
// There are at least 3 characters in the extension.
|
|
// Replace the final one with an underscore.
|
|
//
|
|
p[u-1] = TEXT('_');
|
|
}
|
|
} else {
|
|
//
|
|
// No dot, just add ._.
|
|
//
|
|
lstrcat(CompressedName,TEXT("._"));
|
|
}
|
|
}
|
|
|
|
return(CompressedName);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupAttemptLocate(
|
|
IN PCTSTR FileName,
|
|
OUT PBOOL Found,
|
|
OUT PWIN32_FIND_DATA FindData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to locate a source file via FindFirstFile().
|
|
|
|
Errors of the 'file not found' type are not considered errors
|
|
and result in NO_ERROR. Any non-NO_ERROR return indicates that
|
|
we could not determine whether the file is present or not
|
|
because of some hardware or system problem, etc.
|
|
|
|
Arguments:
|
|
|
|
FileName - supplies filename of the file to be located.
|
|
|
|
Found - receives a value indicating whether the file was found.
|
|
This value is only valid when the function returns NO_ERROR.
|
|
|
|
FindData - if found, returns win32 find data for the file.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome. If NO_ERROR, check
|
|
the Found return value to see whether the file was found.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
|
|
if(*Found = FileExists(FileName,FindData)) {
|
|
d = NO_ERROR;
|
|
} else {
|
|
//
|
|
// We didn't find the file. See whether that was because
|
|
// the file wasn't there or because some other error occured.
|
|
//
|
|
d = GetLastError();
|
|
|
|
if((d == ERROR_NO_MORE_FILES)
|
|
|| (d == ERROR_FILE_NOT_FOUND)
|
|
|| (d == ERROR_PATH_NOT_FOUND)
|
|
|| (d == ERROR_BAD_NETPATH))
|
|
{
|
|
d = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetupDetermineSourceFileName(
|
|
IN PCTSTR FileName,
|
|
OUT PBOOL UsedCompressedName,
|
|
OUT PTSTR *FileNameLocated,
|
|
OUT PWIN32_FIND_DATA FindData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to locate a source file whose name can be compressed
|
|
or uncompressed.
|
|
|
|
The order of attempt is
|
|
|
|
- the name as given (should be the uncompressed name)
|
|
- the compressed form, using _ as the compression char
|
|
- the compressed form, using $ as the compression char
|
|
|
|
Arguments:
|
|
|
|
FileName - supplies filename of the file to be located.
|
|
|
|
UsedCompressedName - receives a boolean indicating whether
|
|
the filename we located seems to indicate that the file
|
|
is compressed.
|
|
|
|
FileNameLocated - receives a pointer to the filename actually
|
|
located. The caller must free with MyFree().
|
|
|
|
FindData - if found, returns win32 find data for the file.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located; check UsedCompressedName and FileNameOpened.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
PTSTR TryName;
|
|
BOOL Found;
|
|
|
|
|
|
TryName = DuplicateString(FileName);
|
|
if(!TryName) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
*UsedCompressedName = FALSE;
|
|
*FileNameLocated = TryName;
|
|
|
|
d = pSetupAttemptLocate(TryName,&Found,FindData);
|
|
if(d != NO_ERROR) {
|
|
MyFree(TryName);
|
|
*FileNameLocated = NULL;
|
|
return(d);
|
|
}
|
|
|
|
if(Found) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
MyFree(TryName);
|
|
*UsedCompressedName = TRUE;
|
|
*FileNameLocated = NULL;
|
|
|
|
TryName = SetupGenerateCompressedName(FileName);
|
|
if(!TryName) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
*FileNameLocated = TryName;
|
|
|
|
d = pSetupAttemptLocate(TryName,&Found,FindData);
|
|
if(d != NO_ERROR) {
|
|
MyFree(TryName);
|
|
*FileNameLocated = NULL;
|
|
return(d);
|
|
}
|
|
|
|
if(Found) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_'));
|
|
TryName[lstrlen(TryName)-1] = TEXT('$');
|
|
|
|
d = pSetupAttemptLocate(TryName,&Found,FindData);
|
|
|
|
if((d != NO_ERROR) || !Found) {
|
|
*FileNameLocated = NULL;
|
|
MyFree(TryName);
|
|
}
|
|
|
|
return(Found ? NO_ERROR : ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
BOOL
|
|
pSetupDoesFileMatch(
|
|
IN PCTSTR InputName,
|
|
IN PCTSTR CompareName,
|
|
OUT PBOOL UsedCompressedName,
|
|
OUT PTSTR *FileNameLocated
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
determine if the specified input file matches the
|
|
name to compare it with. We try the undecorated name
|
|
as well as the compressed versions of the file name.
|
|
|
|
The order of attempt is
|
|
|
|
- the name as given (should be the uncompressed name)
|
|
- the compressed form, using _ as the compression char
|
|
- the compressed form, using $ as the compression char
|
|
|
|
Arguments:
|
|
|
|
FileName - supplies filename we're looking at.
|
|
|
|
CompareName -supplies the filename we're comparing against
|
|
|
|
UsedCompressedName - receives a boolean indicating whether
|
|
the filename we located seems to indicate that the file
|
|
is compressed.
|
|
|
|
FileNameLocated - receives a pointer to the filename actually
|
|
located. The caller must free with MyFree().
|
|
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located; check UsedCompressedName and FileNameOpened.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
PTSTR TryName,TargetName,src,dst;
|
|
BOOL Found;
|
|
|
|
|
|
TryName = DuplicateString(InputName);
|
|
if(!TryName) {
|
|
return(FALSE);
|
|
}
|
|
|
|
TargetName = DuplicateString(CompareName);
|
|
if(!TargetName) {
|
|
MyFree(TryName);
|
|
return(FALSE);
|
|
}
|
|
|
|
dst = _tcsrchr(TryName,TEXT('.'));
|
|
if (dst) {
|
|
*dst = 0;
|
|
}
|
|
src = _tcsrchr(TargetName,TEXT('.'));
|
|
if (src) {
|
|
*src = 0;
|
|
}
|
|
|
|
if (lstrcmpi(TargetName,TryName)) {
|
|
// the "surnames" do not match, so none of the other comparisons will work.
|
|
MyFree(TryName);
|
|
MyFree(TargetName);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (dst) {
|
|
*dst = TEXT('.');
|
|
}
|
|
|
|
if (src) {
|
|
*src = TEXT('.');
|
|
}
|
|
|
|
*UsedCompressedName = FALSE;
|
|
*FileNameLocated = TryName;
|
|
|
|
if (!lstrcmpi(TryName,TargetName)) {
|
|
// we matched
|
|
MyFree(TargetName);
|
|
return(TRUE);
|
|
}
|
|
|
|
MyFree(TryName);
|
|
*UsedCompressedName = TRUE;
|
|
|
|
TryName = SetupGenerateCompressedName(TargetName);
|
|
if(!TryName) {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
*FileNameLocated = TryName;
|
|
|
|
if (!lstrcmpi(TryName,InputName)) {
|
|
// we matched
|
|
MyFree(TargetName);
|
|
return(TRUE);
|
|
}
|
|
|
|
MYASSERT(TryName[lstrlen(TryName)-1] == TEXT('_'));
|
|
TryName[lstrlen(TryName)-1] = TEXT('$');
|
|
|
|
if (!lstrcmpi(TryName,InputName)) {
|
|
// we matched
|
|
MyFree(TargetName);
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// no match
|
|
//
|
|
MyFree(TargetName);
|
|
MyFree(TryName);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
pSetupDecompressWinLzFile(
|
|
IN PTSTR SourceFileName,
|
|
IN PTSTR TargetFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a file is compressed, and retreive additional
|
|
information about it.
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be checked.
|
|
This filename is used as a base; if not found then we look
|
|
for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
|
|
|
|
ActualSourceFileName - receives a pointer to the filename
|
|
that was actually located. Caller can free with MyFree().
|
|
Valid only if the return code from this routine is NO_ERROR.
|
|
|
|
SourceFileSize - receives the size of the located file in its
|
|
current (ie, compressed) form. Valid only if this routine
|
|
returns NO_ERROR.
|
|
|
|
TargetFileSize - receives the uncompressed size of the file.
|
|
If the file is not compressed this will be the same as
|
|
SourceFileSize. Valid only if this routine returns NO_ERROR.
|
|
|
|
CompressionType - receives a value indicating the compression type.
|
|
Valid only if this routine returns NO_ERROR.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located and output params are filled in.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT hSrc,hDst;
|
|
OFSTRUCT ofSrc,ofDst;
|
|
LONG l;
|
|
DWORD d;
|
|
FILETIME CreateTime,AccessTime,WriteTime;
|
|
|
|
//
|
|
// Get the timestamp of the source.
|
|
//
|
|
d = GetSetFileTimestamp(
|
|
SourceFileName,
|
|
&CreateTime,
|
|
&AccessTime,
|
|
&WriteTime,
|
|
FALSE
|
|
);
|
|
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
|
|
hSrc = LZOpenFile(SourceFileName,&ofSrc,OF_READ|OF_SHARE_DENY_WRITE);
|
|
if(hSrc >= 0) {
|
|
|
|
hDst = LZOpenFile(TargetFileName,&ofSrc,OF_CREATE|OF_WRITE|OF_SHARE_EXCLUSIVE);
|
|
if(hDst >= 0) {
|
|
|
|
l = LZCopy(hSrc,hDst);
|
|
if(l >= 0) {
|
|
l = 0;
|
|
|
|
//
|
|
// Set the timestamp of the target. The file is already there
|
|
// so just ignore errors.
|
|
//
|
|
GetSetFileTimestamp(
|
|
TargetFileName,
|
|
&CreateTime,
|
|
&AccessTime,
|
|
&WriteTime,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
LZClose(hDst);
|
|
|
|
} else {
|
|
l = hDst;
|
|
}
|
|
|
|
LZClose(hSrc);
|
|
|
|
} else {
|
|
l = hSrc;
|
|
}
|
|
|
|
//
|
|
// lz error to win32 error
|
|
//
|
|
switch(l) {
|
|
|
|
case 0:
|
|
return(NO_ERROR);
|
|
|
|
case LZERROR_BADINHANDLE:
|
|
case LZERROR_READ:
|
|
return(ERROR_READ_FAULT);
|
|
|
|
case LZERROR_BADOUTHANDLE:
|
|
case LZERROR_WRITE:
|
|
return(ERROR_WRITE_FAULT);
|
|
|
|
case LZERROR_GLOBALLOC:
|
|
case LZERROR_GLOBLOCK:
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
case LZERROR_BADVALUE:
|
|
case LZERROR_UNKNOWNALG:
|
|
return(ERROR_INVALID_DATA);
|
|
|
|
default:
|
|
return(ERROR_INVALID_FUNCTION);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetupInternalGetFileCompressionInfo(
|
|
IN PCTSTR SourceFileName,
|
|
OUT PTSTR *ActualSourceFileName,
|
|
OUT PWIN32_FIND_DATA SourceFindData,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a file is compressed, and retreive additional
|
|
information about it.
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be checked.
|
|
This filename is used as a base; if not found then we look
|
|
for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
|
|
|
|
ActualSourceFileName - receives a pointer to the filename
|
|
that was actually located. Caller can free with MyFree().
|
|
Valid only if the return code from this routine is NO_ERROR.
|
|
|
|
SourceFindData - receives win32 find data for the located file in its
|
|
current (ie, compressed) form. Valid only if this routine
|
|
returns NO_ERROR.
|
|
|
|
TargetFileSize - receives the uncompressed size of the file.
|
|
If the file is not compressed this will be the same as
|
|
SourceFileSize. Valid only if this routine returns NO_ERROR.
|
|
|
|
CompressionType - receives a value indicating the compression type.
|
|
Valid only if this routine returns NO_ERROR.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located and output params are filled in.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
DWORD caberr = NO_ERROR;
|
|
BOOL b;
|
|
HANDLE hFile,hMapping;
|
|
DWORD size;
|
|
FH UNALIGNED *LZHeader;
|
|
|
|
d = SetupDetermineSourceFileName(
|
|
SourceFileName,
|
|
&b,
|
|
ActualSourceFileName,
|
|
SourceFindData
|
|
);
|
|
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
|
|
//
|
|
// If the file is 0-length it isn't compressed;
|
|
// trying to map it in below will fail in this case.
|
|
//
|
|
if(SourceFindData->nFileSizeLow) {
|
|
|
|
//
|
|
// See if it's a diamond file.
|
|
//
|
|
d = DiamondProcessCabinet(
|
|
*ActualSourceFileName,
|
|
0,
|
|
pGetCompressInfoCB,
|
|
&size,
|
|
TRUE
|
|
);
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
*TargetFileSize = size;
|
|
*CompressionType = FILE_COMPRESSION_MSZIP;
|
|
return(NO_ERROR);
|
|
} else if (d != ERROR_INVALID_DATA) {
|
|
//
|
|
// general problems not specific to the file format itself
|
|
// however if this might be a plain file, ignore it
|
|
//
|
|
size_t len1 = lstrlen(SourceFileName);
|
|
size_t len2 = lstrlen(*ActualSourceFileName);
|
|
TCHAR c1 = *CharPrev(SourceFileName,SourceFileName+len1);
|
|
TCHAR c2 = *CharPrev(*ActualSourceFileName,*ActualSourceFileName+len2);
|
|
if(((c2 == TEXT('_')) || (c2 == TEXT('$'))) && ((len1 != len2) || (c1 != c2))) {
|
|
//
|
|
// ActualSourceFileName ends in '_' or '$' and is a modification of SourceFileName
|
|
// don't let us try and parse this as a plain file
|
|
//
|
|
caberr = d;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if it's a WINLZ file.
|
|
//
|
|
d = pSetupOpenAndMapFileForRead(
|
|
*ActualSourceFileName,
|
|
&SourceFindData->nFileSizeLow,
|
|
&hFile,
|
|
&hMapping,
|
|
(PVOID *)&LZHeader
|
|
);
|
|
|
|
if(d != NO_ERROR) {
|
|
MyFree(*ActualSourceFileName);
|
|
return(d);
|
|
}
|
|
|
|
b = FALSE;
|
|
try {
|
|
if((SourceFindData->nFileSizeLow >= HEADER_LEN)
|
|
&& !memcmp(LZHeader->rgbyteMagic,COMP_SIG,COMP_SIG_LEN)
|
|
&& RecognizeCompAlg(LZHeader->byteAlgorithm))
|
|
{
|
|
*TargetFileSize = LZHeader->cbulUncompSize;
|
|
b = TRUE;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
;
|
|
}
|
|
|
|
pSetupUnmapAndCloseFile(hFile,hMapping,LZHeader);
|
|
|
|
if(b) {
|
|
*CompressionType = FILE_COMPRESSION_WINLZA;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
if(caberr) {
|
|
//
|
|
// looks like a compressed file and DiamondProcessCabinet
|
|
// returned a suspicious error
|
|
//
|
|
return(caberr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// File is not compressed.
|
|
//
|
|
*CompressionType = FILE_COMPRESSION_NONE;
|
|
*TargetFileSize = SourceFindData->nFileSizeLow;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
UINT
|
|
pGetCompressInfoCB(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
{
|
|
PFILE_IN_CABINET_INFO FileInfo;
|
|
DWORD rc;
|
|
|
|
switch(Notification) {
|
|
|
|
case SPFILENOTIFY_CABINETINFO:
|
|
//
|
|
// We don't do anything with this.
|
|
//
|
|
rc = NO_ERROR;
|
|
break;
|
|
|
|
case SPFILENOTIFY_FILEINCABINET:
|
|
//
|
|
// New file within a cabinet.
|
|
//
|
|
// We don't ever want to copy the file. Save size info
|
|
// and abort.
|
|
//
|
|
FileInfo = (PFILE_IN_CABINET_INFO)Param1;
|
|
|
|
*((PDWORD)Context) = FileInfo->FileSize;
|
|
|
|
FileInfo->Win32Error = NO_ERROR;
|
|
rc = FILEOP_ABORT;
|
|
SetLastError(NO_ERROR);
|
|
break;
|
|
|
|
//case SPFILENOTIFY_FILEEXTRACTED:
|
|
//case SPFILENOTIFY_NEEDNEWCABINET:
|
|
default:
|
|
//
|
|
// We should never get these.
|
|
//
|
|
MYASSERT(0);
|
|
rc = ERROR_INVALID_FUNCTION;
|
|
break;
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
DWORD
|
|
SetupGetFileCompressionInfoA(
|
|
IN PCSTR SourceFileName,
|
|
OUT PSTR *ActualSourceFileName,
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
{
|
|
WIN32_FIND_DATA FindData;
|
|
DWORD d;
|
|
PCWSTR source;
|
|
PWSTR actualsource = NULL;
|
|
PSTR actualsourceansi = NULL;
|
|
PSTR la_actualsourceansi = NULL;
|
|
DWORD targetsize;
|
|
UINT type;
|
|
|
|
d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source);
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
|
|
d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
|
|
|
|
if(d == NO_ERROR) {
|
|
|
|
MYASSERT(actualsource);
|
|
|
|
if((actualsourceansi = pSetupUnicodeToAnsi(actualsource))==NULL) {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean0;
|
|
}
|
|
if((la_actualsourceansi = (PSTR)OldMyMalloc(1+strlen(actualsourceansi)))==NULL) {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean0;
|
|
}
|
|
strcpy(la_actualsourceansi,actualsourceansi);
|
|
try {
|
|
*SourceFileSize = FindData.nFileSizeLow;
|
|
*ActualSourceFileName = la_actualsourceansi; // free using LocalFree
|
|
*TargetFileSize = targetsize;
|
|
*CompressionType = type;
|
|
la_actualsourceansi = NULL;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
d = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
clean0:
|
|
if(actualsource) {
|
|
MyFree(actualsource);
|
|
}
|
|
if(actualsourceansi) {
|
|
MyFree(actualsourceansi);
|
|
}
|
|
if(la_actualsourceansi) {
|
|
OldMyFree(la_actualsourceansi);
|
|
}
|
|
|
|
MyFree(source);
|
|
|
|
return(d);
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
DWORD
|
|
SetupGetFileCompressionInfoW(
|
|
IN PCWSTR SourceFileName,
|
|
OUT PWSTR *ActualSourceFileName,
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(SourceFileName);
|
|
UNREFERENCED_PARAMETER(ActualSourceFileName);
|
|
UNREFERENCED_PARAMETER(SourceFileSize);
|
|
UNREFERENCED_PARAMETER(TargetFileSize);
|
|
UNREFERENCED_PARAMETER(CompressionType);
|
|
return(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
#endif
|
|
|
|
DWORD
|
|
SetupGetFileCompressionInfo(
|
|
IN PCTSTR SourceFileName,
|
|
OUT PTSTR *ActualSourceFileName,
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Here for App-Compat only
|
|
Replaced by SetupGetFileCompressionInfoEx
|
|
|
|
Return pointer is allocated by OldMyMalloc,
|
|
it can be freed by (*cough*) OldMyFree (exported as MyFree)
|
|
This is because there are apps out there that use this,
|
|
and run-time link to setupapi!MyFree to release memory
|
|
|
|
!!!! DO NOT USE THIS API !!!!
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be checked.
|
|
This filename is used as a base; if not found then we look
|
|
for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
|
|
|
|
ActualSourceFileName - receives a pointer to the filename
|
|
that was actually located. Caller can free with exported MyFree().
|
|
Valid only if the return code from this routine is NO_ERROR.
|
|
|
|
SourceFileSize - receives the size of the located file in its
|
|
current (ie, compressed) form. Valid only if this routine
|
|
returns NO_ERROR.
|
|
|
|
TargetFileSize - receives the uncompressed size of the file.
|
|
If the file is not compressed this will be the same as
|
|
SourceFileSize. Valid only if this routine returns NO_ERROR.
|
|
|
|
CompressionType - receives a value indicating the compression type.
|
|
Valid only if this routine returns NO_ERROR.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located and output params are filled in.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
WIN32_FIND_DATA FindData;
|
|
DWORD d;
|
|
PCTSTR source;
|
|
PTSTR actualsource = NULL;
|
|
PTSTR la_actualsource = NULL;
|
|
DWORD targetsize;
|
|
UINT type;
|
|
|
|
d = CaptureStringArg(SourceFileName,&source);
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
|
|
d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
|
|
|
|
if(d == NO_ERROR) {
|
|
MYASSERT(actualsource);
|
|
la_actualsource = (PTSTR)OldMyMalloc(sizeof(TCHAR)*(1+lstrlen(actualsource)));
|
|
if (la_actualsource == NULL) {
|
|
MyFree(actualsource);
|
|
MyFree(source);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
lstrcpy(la_actualsource,actualsource);
|
|
try {
|
|
*SourceFileSize = FindData.nFileSizeLow;
|
|
*ActualSourceFileName = la_actualsource; // free using LocalFree
|
|
*TargetFileSize = targetsize;
|
|
*CompressionType = type;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
d = ERROR_INVALID_PARAMETER;
|
|
}
|
|
if(d != NO_ERROR) {
|
|
OldMyFree(la_actualsource);
|
|
}
|
|
MyFree(actualsource);
|
|
}
|
|
|
|
MyFree(source);
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
BOOL
|
|
WINAPI
|
|
SetupGetFileCompressionInfoExA(
|
|
IN PCSTR SourceFileName,
|
|
IN PSTR ActualSourceFileNameBuffer,
|
|
IN DWORD ActualSourceFileNameBufferLen,
|
|
OUT PDWORD RequiredBufferLen, OPTIONAL
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
{
|
|
WIN32_FIND_DATA FindData;
|
|
DWORD d;
|
|
PCWSTR source;
|
|
PWSTR actualsource = NULL;
|
|
PSTR actualsourceansi = NULL;
|
|
DWORD targetsize;
|
|
DWORD reqbufsize;
|
|
UINT type;
|
|
|
|
d = pSetupCaptureAndConvertAnsiArg(SourceFileName,&source);
|
|
if(d != NO_ERROR) {
|
|
SetLastError(d);
|
|
return (d==NO_ERROR);
|
|
}
|
|
|
|
d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
|
|
|
|
if(d == NO_ERROR) {
|
|
MYASSERT(actualsource);
|
|
actualsourceansi = pSetupUnicodeToAnsi(actualsource);
|
|
if(actualsourceansi != NULL) {
|
|
try {
|
|
reqbufsize = strlen(actualsourceansi)+1;
|
|
if (RequiredBufferLen) {
|
|
*RequiredBufferLen = reqbufsize;
|
|
}
|
|
if(ActualSourceFileNameBuffer) {
|
|
if((ActualSourceFileNameBufferLen < reqbufsize)) {
|
|
d = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
strcpy(ActualSourceFileNameBuffer,actualsourceansi);
|
|
}
|
|
} else if(ActualSourceFileNameBufferLen) {
|
|
d = ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
*SourceFileSize = FindData.nFileSizeLow;
|
|
*TargetFileSize = targetsize;
|
|
*CompressionType = type;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
d = ERROR_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if(actualsource) {
|
|
MyFree(actualsource);
|
|
}
|
|
if(actualsourceansi) {
|
|
MyFree(actualsourceansi);
|
|
}
|
|
MyFree(source);
|
|
|
|
SetLastError(d);
|
|
return (d==NO_ERROR);
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
BOOL
|
|
WINAPI
|
|
SetupGetFileCompressionInfoExW(
|
|
IN PCWSTR SourceFileName,
|
|
IN PWSTR ActualSourceFileNameBuffer,
|
|
IN DWORD ActualSourceFileNameBufferLen,
|
|
OUT PDWORD RequiredBufferLen, OPTIONAL
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(SourceFileName);
|
|
UNREFERENCED_PARAMETER(ActualSourceFileNameBuffer);
|
|
UNREFERENCED_PARAMETER(ActualSourceFileNameBufferLen);
|
|
UNREFERENCED_PARAMETER(RequiredBufferLen);
|
|
UNREFERENCED_PARAMETER(SourceFileSize);
|
|
UNREFERENCED_PARAMETER(TargetFileSize);
|
|
UNREFERENCED_PARAMETER(CompressionType);
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetupGetFileCompressionInfoEx(
|
|
IN PCTSTR SourceFileName,
|
|
IN PTSTR ActualSourceFileNameBuffer,
|
|
IN DWORD ActualSourceFileNameBufferLen,
|
|
OUT PDWORD RequiredBufferLen, OPTIONAL
|
|
OUT PDWORD SourceFileSize,
|
|
OUT PDWORD TargetFileSize,
|
|
OUT PUINT CompressionType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a file is compressed, and retreive additional
|
|
information about it.
|
|
This is the replacement for the very broken SetupGetFileCompressionInfo
|
|
Caller must pass in a buffer
|
|
If the buffer is NULL, return size
|
|
and all other parameters filled out (unless some other error occurred)
|
|
note however, you would typically call this with a buffer size of MAX_PATH.
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be checked.
|
|
This filename is used as a base; if not found then we look
|
|
for the 2 compressed forms (ie, foo.ex_, foo.ex$) as well.
|
|
|
|
ActualSourceFileNameBuffer - if not NULL, receives actual filename
|
|
Valid only if the return code from this routine is NO_ERROR.
|
|
|
|
ActualSourceFileNameBufferLen - pass in length (characters) of
|
|
ActualSourceFileNameBuffer. must be 0 if ActualSourceFileNameBuffer
|
|
is NULL.
|
|
|
|
RequiredBufferLen - if not NULL, filled with length of actual filename
|
|
including terminating NULL.
|
|
Valid only if the return code from this routine is NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
|
|
|
|
SourceFileSize - receives the size of the located file in its
|
|
current (ie, compressed) form. Valid only if this routine
|
|
returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
|
|
|
|
TargetFileSize - receives the uncompressed size of the file.
|
|
If the file is not compressed this will be the same as
|
|
SourceFileSize. Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
|
|
|
|
CompressionType - receives a value indicating the compression type.
|
|
Valid only if this routine returns NO_ERROR or ERROR_INSUFFICIENT_BUFFER.
|
|
|
|
Return Value:
|
|
|
|
TRUE indicating success (NO_ERROR)
|
|
FALSE indicating failure
|
|
GetLastError() provides Win32 error code indicating the outcome.
|
|
|
|
ERROR_FILE_NOT_FOUND - normal code indicating everything is ok
|
|
but we can't find the file
|
|
|
|
NO_ERROR - file was located and all output params are filled in including ActualSourceFileNameBuffer.
|
|
also returned if ActualSourceFileNameBuffer is NULL
|
|
|
|
ERROR_INSUFFICIENT_BUFFER - file was located and output params are filled in, excluding
|
|
ActualSourceFileNameBuffer.
|
|
|
|
Others - something is wrong with the hardware or system.
|
|
|
|
--*/
|
|
|
|
{
|
|
WIN32_FIND_DATA FindData;
|
|
DWORD d;
|
|
PCTSTR source;
|
|
PTSTR actualsource = NULL;
|
|
DWORD targetsize;
|
|
UINT type;
|
|
DWORD reqbufsize;
|
|
|
|
d = CaptureStringArg(SourceFileName,&source);
|
|
if(d != NO_ERROR) {
|
|
SetLastError(d);
|
|
return (d==NO_ERROR);
|
|
}
|
|
|
|
d = SetupInternalGetFileCompressionInfo(source,&actualsource,&FindData,&targetsize,&type);
|
|
|
|
if(d == NO_ERROR) {
|
|
MYASSERT(actualsource);
|
|
try {
|
|
reqbufsize = lstrlen(actualsource)+1;
|
|
if (RequiredBufferLen) {
|
|
*RequiredBufferLen = reqbufsize;
|
|
}
|
|
if(ActualSourceFileNameBuffer) {
|
|
if(ActualSourceFileNameBufferLen < reqbufsize) {
|
|
d = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
lstrcpy(ActualSourceFileNameBuffer,actualsource);
|
|
}
|
|
} else if(ActualSourceFileNameBufferLen) {
|
|
d = ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
*SourceFileSize = FindData.nFileSizeLow;
|
|
*TargetFileSize = targetsize;
|
|
*CompressionType = type;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
d = ERROR_INVALID_PARAMETER;
|
|
}
|
|
MyFree(actualsource);
|
|
}
|
|
|
|
MyFree(source);
|
|
|
|
SetLastError(d);
|
|
return (d==NO_ERROR);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// ANSI version
|
|
//
|
|
DWORD
|
|
SetupDecompressOrCopyFileA(
|
|
IN PCSTR SourceFileName,
|
|
OUT PCSTR TargetFileName,
|
|
OUT PUINT CompressionType OPTIONAL
|
|
)
|
|
{
|
|
DWORD rc;
|
|
PCWSTR s,t;
|
|
|
|
rc = pSetupCaptureAndConvertAnsiArg(SourceFileName,&s);
|
|
if(rc == NO_ERROR) {
|
|
|
|
rc = pSetupCaptureAndConvertAnsiArg(TargetFileName,&t);
|
|
if(rc == NO_ERROR) {
|
|
|
|
rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL);
|
|
MyFree(t);
|
|
}
|
|
|
|
MyFree(s);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
#else
|
|
//
|
|
// Unicode stub
|
|
//
|
|
DWORD
|
|
SetupDecompressOrCopyFileW(
|
|
IN PCWSTR SourceFileName,
|
|
OUT PCWSTR TargetFileName,
|
|
OUT PUINT CompressionType OPTIONAL
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(SourceFileName);
|
|
UNREFERENCED_PARAMETER(TargetFileName);
|
|
UNREFERENCED_PARAMETER(CompressionType);
|
|
return(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
#endif
|
|
|
|
DWORD
|
|
SetupDecompressOrCopyFile(
|
|
IN PCTSTR SourceFileName,
|
|
IN PCTSTR TargetFileName,
|
|
IN PUINT CompressionType OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decompress or copy a file.
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be decompressed.
|
|
If CompressionType is specified, no additional processing is
|
|
performed on this name -- the caller is responsible for determining
|
|
the actual file name (ie, foo.ex_ instead of foo.exe) before calling
|
|
this routine. If CompressionType is not specified, then this routine
|
|
attempts to locate the compressed form of the filename if the file
|
|
is not found with the name given.
|
|
|
|
TargetFileName - supplies filename of target file.
|
|
|
|
CompressionType - if specified, supplies type of compression in use
|
|
on the source. This can be determined by calling
|
|
SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE
|
|
results in the file being copied and not decompressed,
|
|
regardless of the type of compression that may be in use on the source.
|
|
If this value is not specified then this routine attempts to determine
|
|
the compression type and decompresses/copies accordingly.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
PCTSTR s,t;
|
|
|
|
rc = CaptureStringArg(SourceFileName,&s);
|
|
if(rc == NO_ERROR) {
|
|
|
|
rc = CaptureStringArg(TargetFileName,&t);
|
|
if(rc == NO_ERROR) {
|
|
|
|
rc = pSetupDecompressOrCopyFile(s,t,CompressionType,FALSE,NULL);
|
|
MyFree(t);
|
|
}
|
|
|
|
MyFree(s);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupDecompressOrCopyFile(
|
|
IN PCTSTR SourceFileName,
|
|
IN PCTSTR TargetFileName,
|
|
IN PUINT CompressionType, OPTIONAL
|
|
IN BOOL AllowMove,
|
|
OUT PBOOL Moved OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decompress or copy a file.
|
|
|
|
Arguments:
|
|
|
|
SourceFileName - supplies filename of the file to be decompressed.
|
|
If CompressionType is specified, no additional processing is
|
|
performed on this name -- the caller is responsible for determining
|
|
the actual file name (ie, foo.ex_ instead of foo.exe) before calling
|
|
this routine. If CompressionType is not specified, then this routine
|
|
attempts to locate the compressed form of the filename if the file
|
|
is not found with the name given.
|
|
|
|
TargetFileName - supplies filename of target file.
|
|
|
|
CompressionType - if specified, supplies type of compression in use
|
|
on the source. This can be determined by calling
|
|
SetupGetFileCompressionInfo(). Specifying FILE_COMPRESSION_NONE
|
|
results in the file being copied and not decompressed,
|
|
regardless of the type of compression that may be in use on the source.
|
|
If this value is not specified then this routine attempts to determine
|
|
the compression type and decompresses/copies accordingly.
|
|
|
|
AllowMove - if specified, then files that do not require decompression
|
|
will be moved instead of copied.
|
|
|
|
Moved - if specified receives a boolean indicating whether the file was
|
|
moved (as opposed to copied or decompressed).
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating the outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD d;
|
|
UINT ComprType;
|
|
PTSTR ActualName;
|
|
DWORD TargetSize;
|
|
FILETIME CreateTime,AccessTime,WriteTime;
|
|
SFD_INFO CBData;
|
|
BOOL moved;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
if(Moved) {
|
|
*Moved = FALSE;
|
|
}
|
|
|
|
if(CompressionType) {
|
|
ComprType = *CompressionType;
|
|
ActualName = (PTSTR)SourceFileName;
|
|
} else {
|
|
//
|
|
// Need to determine compresison type.
|
|
//
|
|
d = SetupInternalGetFileCompressionInfo(
|
|
SourceFileName,
|
|
&ActualName,
|
|
&FindData,
|
|
&TargetSize,
|
|
&ComprType
|
|
);
|
|
|
|
if(d != NO_ERROR) {
|
|
return(d);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Blast the target file. Ignore if failure -- it'll be caught later.
|
|
//
|
|
SetFileAttributes(TargetFileName,FILE_ATTRIBUTE_NORMAL);
|
|
DeleteFile(TargetFileName);
|
|
|
|
switch(ComprType) {
|
|
|
|
case FILE_COMPRESSION_NONE:
|
|
moved = (AllowMove ? MoveFile(ActualName,TargetFileName) : FALSE);
|
|
if(moved) {
|
|
d = NO_ERROR;
|
|
if(Moved) {
|
|
*Moved = TRUE;
|
|
}
|
|
} else {
|
|
d = GetSetFileTimestamp(ActualName,&CreateTime,&AccessTime,&WriteTime,FALSE);
|
|
if(d == NO_ERROR) {
|
|
d = CopyFile(ActualName,TargetFileName,FALSE) ? NO_ERROR : GetLastError();
|
|
if(d == NO_ERROR) {
|
|
GetSetFileTimestamp(TargetFileName,&CreateTime,&AccessTime,&WriteTime,TRUE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FILE_COMPRESSION_WINLZA:
|
|
d = pSetupDecompressWinLzFile(ActualName,(PTSTR)TargetFileName);
|
|
break;
|
|
|
|
case FILE_COMPRESSION_MSZIP:
|
|
|
|
CBData.FileCount = 0;
|
|
CBData.TargetFile = TargetFileName;
|
|
CBData.GotTimestamp = FALSE;
|
|
|
|
d = DiamondProcessCabinet(
|
|
ActualName,
|
|
0,
|
|
pSingleFileDecompCB,
|
|
&CBData,
|
|
TRUE
|
|
);
|
|
break;
|
|
|
|
default:
|
|
d = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if(!CompressionType) {
|
|
MyFree(ActualName);
|
|
}
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
UINT
|
|
pSingleFileDecompCB(
|
|
IN PVOID Context,
|
|
IN UINT Notification,
|
|
IN UINT_PTR Param1,
|
|
IN UINT_PTR Param2
|
|
)
|
|
{
|
|
PSFD_INFO Data;
|
|
PFILE_IN_CABINET_INFO FileInfo;
|
|
PFILEPATHS FilePaths;
|
|
DWORD rc;
|
|
HANDLE h;
|
|
|
|
Data = Context;
|
|
|
|
switch(Notification) {
|
|
|
|
case SPFILENOTIFY_CABINETINFO:
|
|
//
|
|
// We don't do anything with this.
|
|
//
|
|
rc = NO_ERROR;
|
|
break;
|
|
|
|
case SPFILENOTIFY_FILEINCABINET:
|
|
//
|
|
// New file within a cabinet.
|
|
//
|
|
FileInfo = (PFILE_IN_CABINET_INFO)Param1;
|
|
FileInfo->Win32Error = NO_ERROR;
|
|
|
|
//
|
|
// We only want the first file. If this is a subsequent file,
|
|
// bail out.
|
|
//
|
|
if(Data->FileCount++) {
|
|
|
|
rc = FILEOP_ABORT;
|
|
SetLastError(NO_ERROR);
|
|
|
|
} else {
|
|
//
|
|
// We want the file. Ignore the names in the cabinet and
|
|
// use the name given to us. Also, we want to preserve
|
|
// the timestamp of the cabinet, not of the file within it.
|
|
//
|
|
lstrcpyn(FileInfo->FullTargetName,Data->TargetFile,MAX_PATH);
|
|
|
|
h = CreateFile(
|
|
(PCTSTR)Param2, // cabinet filename
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(h != INVALID_HANDLE_VALUE) {
|
|
if(GetFileTime(h,NULL,NULL,&Data->FileTime)) {
|
|
Data->GotTimestamp = TRUE;
|
|
}
|
|
CloseHandle(h);
|
|
}
|
|
|
|
rc = FILEOP_DOIT;
|
|
}
|
|
|
|
break;
|
|
|
|
case SPFILENOTIFY_FILEEXTRACTED:
|
|
//
|
|
// File was successfully extracted.
|
|
// Preserve timestamp.
|
|
//
|
|
FilePaths = (PFILEPATHS)Param1;
|
|
|
|
if(Data->GotTimestamp) {
|
|
|
|
h = CreateFile(
|
|
FilePaths->Target,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(h != INVALID_HANDLE_VALUE) {
|
|
SetFileTime(h,NULL,NULL,&Data->FileTime);
|
|
CloseHandle(h);
|
|
}
|
|
}
|
|
|
|
rc = NO_ERROR;
|
|
break;
|
|
|
|
//case SPFILENOTIFY_NEEDNEWCABINET:
|
|
default:
|
|
//
|
|
// We should never get this.
|
|
//
|
|
MYASSERT(0);
|
|
rc = ERROR_INVALID_FUNCTION;
|
|
break;
|
|
}
|
|
|
|
return(rc);
|
|
}
|