1252 lines
30 KiB
C
1252 lines
30 KiB
C
|
#include "spprecmp.h"
|
||
|
#pragma hdrstop
|
||
|
#include <diamondd.h>
|
||
|
|
||
|
#define SETUP_FDI_POOL_TAG 0x44465053 // 'SPFD'
|
||
|
|
||
|
#ifdef DeleteFile
|
||
|
#undef DeleteFile // we mean "DeleteFile", not "DeleteFileA"
|
||
|
#endif
|
||
|
|
||
|
HFDI FdiContext;
|
||
|
ERF FdiError;
|
||
|
|
||
|
//
|
||
|
// Gloabls used when copying a file.
|
||
|
// Setup opens the source and target files and maps the source.
|
||
|
// To avoid opening and closing the source and target multiple times
|
||
|
// and to maintain a mapped file inplementation, we'll fake the i/o calls.
|
||
|
// These globals remember state about the source (cabinet) and target
|
||
|
// files currently in use.
|
||
|
//
|
||
|
PUCHAR SpdSourceAddress;
|
||
|
ULONG SpdSourceFileSize;
|
||
|
|
||
|
typedef struct {
|
||
|
PEXPAND_CALLBACK Callback;
|
||
|
PVOID CallbackContext;
|
||
|
LPWSTR DestinationPath;
|
||
|
} EXPAND_CAB_CONTEXT;
|
||
|
|
||
|
typedef struct _DRIVER_CAB_CONTEXT {
|
||
|
PCWSTR FileName;
|
||
|
PCSTR FileNameA;
|
||
|
USHORT FileDate;
|
||
|
USHORT FileTime;
|
||
|
} DRIVER_CAB_CONTEXT, *PDRIVER_CAB_CONTEXT;
|
||
|
|
||
|
DRIVER_CAB_CONTEXT DriverContext;
|
||
|
|
||
|
typedef struct _MY_FILE_STATE {
|
||
|
ULONG Signature;
|
||
|
union {
|
||
|
LONG FileOffset;
|
||
|
HANDLE Handle;
|
||
|
} u;
|
||
|
} MY_FILE_STATE, *PMY_FILE_STATE;
|
||
|
|
||
|
#define SOURCE_FILE_SIGNATURE 0x45f3ec83
|
||
|
#define TARGET_FILE_SIGNATURE 0x46f3ec83
|
||
|
|
||
|
MY_FILE_STATE CurrentTargetFile;
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunction(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Perameters
|
||
|
);
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunctionCabinet(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Parameters
|
||
|
);
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunctionDriverCab(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Perameters
|
||
|
);
|
||
|
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdFdiOpen(
|
||
|
IN PSTR FileName,
|
||
|
IN int oflag,
|
||
|
IN int pmode
|
||
|
);
|
||
|
|
||
|
int
|
||
|
DIAMONDAPI
|
||
|
SpdFdiClose(
|
||
|
IN INT_PTR Handle
|
||
|
);
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pSpdInitGlobals(
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize
|
||
|
)
|
||
|
{
|
||
|
SpdSourceAddress = SourceBaseAddress;
|
||
|
SpdSourceFileSize = SourceFileSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
SpdIsCabinet(
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize,
|
||
|
OUT PBOOLEAN ContainsMultipleFiles
|
||
|
)
|
||
|
{
|
||
|
FDICABINETINFO CabinetInfo;
|
||
|
INT_PTR h;
|
||
|
BOOLEAN b;
|
||
|
|
||
|
*ContainsMultipleFiles = FALSE;
|
||
|
|
||
|
ASSERT(FdiContext);
|
||
|
if(!FdiContext) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Save away globals for later use.
|
||
|
//
|
||
|
pSpdInitGlobals(SourceBaseAddress,SourceFileSize);
|
||
|
|
||
|
//
|
||
|
// 'Open' the file so we can pass a handle that will work
|
||
|
// with SpdFdiRead and SpdFdiWrite.
|
||
|
//
|
||
|
h = SpdFdiOpen("",0,0);
|
||
|
if(h == -1) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We don't trust diamond to be robust.
|
||
|
//
|
||
|
|
||
|
memset(&CabinetInfo, 0, sizeof(CabinetInfo));
|
||
|
|
||
|
try {
|
||
|
b = FDIIsCabinet(FdiContext,h,&CabinetInfo) ? TRUE : FALSE;
|
||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
b = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If spanned or more than one file inside, report it as multiple
|
||
|
//
|
||
|
|
||
|
if (b) {
|
||
|
if ((CabinetInfo.cFolders > 1) || (CabinetInfo.cFiles > 1)) {
|
||
|
*ContainsMultipleFiles = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 'Close' the file.
|
||
|
//
|
||
|
SpdFdiClose(h);
|
||
|
|
||
|
return(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
SpdIsCompressed(
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize
|
||
|
)
|
||
|
{
|
||
|
INT_PTR h;
|
||
|
BOOLEAN b;
|
||
|
BOOLEAN bMultiple;
|
||
|
|
||
|
b = SpdIsCabinet(SourceBaseAddress,SourceFileSize,&bMultiple);
|
||
|
|
||
|
//
|
||
|
// Compressed files with more than one contained file s/b treated as
|
||
|
// an uncompressed file and copied as is. We're not prepared to uncompress
|
||
|
// multiple files from one file.
|
||
|
//
|
||
|
|
||
|
if (bMultiple) {
|
||
|
b = FALSE;
|
||
|
}
|
||
|
|
||
|
return(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
SpdDecompressFile(
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize,
|
||
|
IN HANDLE DestinationHandle
|
||
|
)
|
||
|
{
|
||
|
BOOL b;
|
||
|
|
||
|
ASSERT(FdiContext);
|
||
|
|
||
|
//
|
||
|
// Save away globals for later use.
|
||
|
//
|
||
|
pSpdInitGlobals(SourceBaseAddress,SourceFileSize);
|
||
|
|
||
|
CurrentTargetFile.Signature = TARGET_FILE_SIGNATURE;
|
||
|
CurrentTargetFile.u.Handle = DestinationHandle;
|
||
|
|
||
|
//
|
||
|
// Get the copy going. Note that we pass empty cabinet filenames
|
||
|
// because we've already opened the files.
|
||
|
//
|
||
|
b = FDICopy(FdiContext,"","",0,SpdNotifyFunction,NULL,NULL);
|
||
|
|
||
|
return(b ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
SpdDecompressCabinet(
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize,
|
||
|
IN PWSTR DestinationPath,
|
||
|
IN PEXPAND_CALLBACK Callback,
|
||
|
IN PVOID CallbackContext
|
||
|
)
|
||
|
{
|
||
|
BOOL b;
|
||
|
EXPAND_CAB_CONTEXT NotifyContext;
|
||
|
|
||
|
ASSERT(FdiContext);
|
||
|
|
||
|
//
|
||
|
// Save away globals for later use.
|
||
|
//
|
||
|
pSpdInitGlobals(SourceBaseAddress,SourceFileSize);
|
||
|
|
||
|
CurrentTargetFile.Signature = TARGET_FILE_SIGNATURE;
|
||
|
CurrentTargetFile.u.Handle = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
//
|
||
|
// Tunnel expand context info into SpdNotifyFunctionCabinet
|
||
|
//
|
||
|
NotifyContext.Callback = Callback;
|
||
|
NotifyContext.CallbackContext = CallbackContext;
|
||
|
NotifyContext.DestinationPath = DestinationPath;
|
||
|
|
||
|
//
|
||
|
// Get the copy going. Note that we pass empty cabinet filenames
|
||
|
// because we've already opened the files.
|
||
|
//
|
||
|
b = FDICopy(FdiContext,"","",0,SpdNotifyFunctionCabinet,NULL,&NotifyContext);
|
||
|
|
||
|
if ( CurrentTargetFile.u.Handle != INVALID_HANDLE_VALUE ) {
|
||
|
|
||
|
//
|
||
|
// FDI had some error, so we need to close & destroy the target
|
||
|
// file-in-progress. Note that FDI calls it's FDIClose callback
|
||
|
// but in our implementation, that has no effect on the target
|
||
|
// file.
|
||
|
//
|
||
|
|
||
|
FILE_DISPOSITION_INFORMATION FileDispositionDetails;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
|
||
|
FileDispositionDetails.DeleteFile = TRUE;
|
||
|
|
||
|
ZwSetInformationFile( CurrentTargetFile.u.Handle,
|
||
|
&IoStatusBlock,
|
||
|
&FileDispositionDetails,
|
||
|
sizeof(FileDispositionDetails),
|
||
|
FileDispositionInformation );
|
||
|
|
||
|
ZwClose( CurrentTargetFile.u.Handle );
|
||
|
|
||
|
b = FALSE; // make sure we report failure
|
||
|
}
|
||
|
|
||
|
return(b ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
SpdDecompressFileFromDriverCab(
|
||
|
IN PWSTR SourceFileName,
|
||
|
IN PVOID SourceBaseAddress,
|
||
|
IN ULONG SourceFileSize,
|
||
|
IN HANDLE DestinationHandle,
|
||
|
OUT PUSHORT pDate,
|
||
|
OUT PUSHORT pTime
|
||
|
)
|
||
|
{
|
||
|
BOOL b;
|
||
|
|
||
|
ASSERT(FdiContext);
|
||
|
ASSERT(DriverContext.FileName == NULL);
|
||
|
ASSERT(DriverContext.FileNameA == NULL);
|
||
|
|
||
|
//
|
||
|
// Save away globals for later use.
|
||
|
//
|
||
|
pSpdInitGlobals(SourceBaseAddress,SourceFileSize);
|
||
|
|
||
|
CurrentTargetFile.Signature = TARGET_FILE_SIGNATURE;
|
||
|
CurrentTargetFile.u.Handle = DestinationHandle;
|
||
|
DriverContext.FileName = SpDupStringW(SourceFileName);
|
||
|
|
||
|
if (!DriverContext.FileName) {
|
||
|
return(STATUS_NO_MEMORY);
|
||
|
}
|
||
|
|
||
|
DriverContext.FileNameA = SpToOem((PWSTR)DriverContext.FileName);
|
||
|
|
||
|
//
|
||
|
// Get the copy going. Note that we pass empty cabinet filenames
|
||
|
// because we've already opened the files.
|
||
|
//
|
||
|
b = FDICopy(FdiContext,"","",0,SpdNotifyFunctionDriverCab,NULL,NULL);
|
||
|
|
||
|
ASSERT(DriverContext.FileName != NULL);
|
||
|
SpMemFree( (PWSTR)DriverContext.FileName );
|
||
|
DriverContext.FileName = NULL;
|
||
|
|
||
|
if (DriverContext.FileNameA) {
|
||
|
SpMemFree( (PSTR)DriverContext.FileNameA );
|
||
|
DriverContext.FileNameA = NULL;
|
||
|
}
|
||
|
|
||
|
*pDate = DriverContext.FileDate;
|
||
|
*pTime = DriverContext.FileTime;
|
||
|
|
||
|
|
||
|
return(b ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunction(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Parameters
|
||
|
)
|
||
|
{
|
||
|
switch(Operation) {
|
||
|
|
||
|
case fdintCABINET_INFO:
|
||
|
case fdintNEXT_CABINET:
|
||
|
case fdintPARTIAL_FILE:
|
||
|
|
||
|
//
|
||
|
// Cabinet management functions which we don't use.
|
||
|
// Return success.
|
||
|
//
|
||
|
return(0);
|
||
|
|
||
|
case fdintCOPY_FILE:
|
||
|
|
||
|
//
|
||
|
// Diamond is asking us whether we want to copy the file.
|
||
|
// We need to return a file handle to indicate that we do.
|
||
|
//
|
||
|
return((INT_PTR)&CurrentTargetFile);
|
||
|
|
||
|
case fdintCLOSE_FILE_INFO:
|
||
|
|
||
|
//
|
||
|
// Diamond is done with the target file and wants us to close it.
|
||
|
// (ie, this is the counterpart to fdint_COPY_FILE).
|
||
|
// We manage our own file i/o so ignore this.
|
||
|
//
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunctionCabinet(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Parameters
|
||
|
)
|
||
|
{
|
||
|
EXPAND_CAB_CONTEXT * Context = (EXPAND_CAB_CONTEXT *) Parameters->pv;
|
||
|
NTSTATUS Status;
|
||
|
ULONG FileNameLength;
|
||
|
ULONG Disposition;
|
||
|
OBJECT_ATTRIBUTES Obja;
|
||
|
UNICODE_STRING UnicodeString;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
union {
|
||
|
FILE_BASIC_INFORMATION FileBasicDetails;
|
||
|
FILE_RENAME_INFORMATION FileRenameDetails;
|
||
|
FILE_DISPOSITION_INFORMATION FileDispositionDetails;
|
||
|
WCHAR PathName[CB_MAX_FILENAME * 2];
|
||
|
} U;
|
||
|
HANDLE TempHandle;
|
||
|
|
||
|
//
|
||
|
// These values are retained between fdintCOPY_FILE and fdintCLOSE_FILE_INFO
|
||
|
//
|
||
|
static WCHAR FileName[CB_MAX_FILENAME];
|
||
|
static LARGE_INTEGER FileSize;
|
||
|
static LARGE_INTEGER FileTime;
|
||
|
static ULONG FileAttributes;
|
||
|
|
||
|
|
||
|
switch ( Operation ) {
|
||
|
|
||
|
case fdintCOPY_FILE:
|
||
|
|
||
|
//
|
||
|
// Diamond is asking us whether we want to copy the file.
|
||
|
// Convert everything we're given to the form needed to
|
||
|
// call the client back to ask it about this file.
|
||
|
// We need to return a file handle to indicate that we do.
|
||
|
//
|
||
|
|
||
|
Status = RtlMultiByteToUnicodeN (
|
||
|
FileName,
|
||
|
sizeof(FileName),
|
||
|
&FileNameLength,
|
||
|
Parameters->psz1,
|
||
|
strlen(Parameters->psz1)
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
//
|
||
|
// failed to translate, ignore file
|
||
|
//
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
FileName[ FileNameLength / sizeof(WCHAR) ] = L'\0';
|
||
|
|
||
|
FileSize.LowPart = Parameters->cb;
|
||
|
FileSize.HighPart = 0;
|
||
|
|
||
|
SpTimeFromDosTime( Parameters->date,
|
||
|
Parameters->time,
|
||
|
&FileTime );
|
||
|
|
||
|
FileAttributes = Parameters->attribs &
|
||
|
(FILE_ATTRIBUTE_ARCHIVE |
|
||
|
FILE_ATTRIBUTE_READONLY |
|
||
|
FILE_ATTRIBUTE_HIDDEN |
|
||
|
FILE_ATTRIBUTE_SYSTEM);
|
||
|
|
||
|
Disposition = Context->Callback( EXPAND_COPY_FILE,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition == EXPAND_ABORT ) {
|
||
|
return(-1); // tell FDI to abort
|
||
|
} else if ( Disposition != EXPAND_COPY_THIS_FILE ) {
|
||
|
return(0); // tell FDI to skip this file
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// see if target file already exists
|
||
|
//
|
||
|
wcscpy( U.PathName, Context->DestinationPath );
|
||
|
SpConcatenatePaths( U.PathName, FileName );
|
||
|
|
||
|
INIT_OBJA( &Obja, &UnicodeString, U.PathName );
|
||
|
|
||
|
Status = ZwCreateFile( &TempHandle,
|
||
|
FILE_GENERIC_READ,
|
||
|
&Obja,
|
||
|
&IoStatusBlock,
|
||
|
NULL,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
0, // no sharing
|
||
|
FILE_OPEN, // fail if not existing
|
||
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
|
||
|
NULL,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if ( NT_SUCCESS(Status) ) {
|
||
|
|
||
|
//
|
||
|
// Target file already exists. Check for over-write.
|
||
|
//
|
||
|
Status = ZwQueryInformationFile( TempHandle,
|
||
|
&IoStatusBlock,
|
||
|
&U.FileBasicDetails,
|
||
|
sizeof(FILE_BASIC_INFORMATION),
|
||
|
FileBasicInformation );
|
||
|
|
||
|
ZwClose( TempHandle );
|
||
|
|
||
|
if ( NT_SUCCESS(Status) &&
|
||
|
( U.FileBasicDetails.FileAttributes & FILE_ATTRIBUTE_READONLY )) {
|
||
|
|
||
|
//
|
||
|
// target file is read-only: report error
|
||
|
//
|
||
|
Disposition = Context->Callback( EXPAND_NOTIFY_CREATE_FAILED,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition != EXPAND_CONTINUE ) {
|
||
|
return(-1); // tell FDI to abort
|
||
|
}
|
||
|
|
||
|
return (0); // tell FDI to just skip this target file
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// ask client about overwrite
|
||
|
//
|
||
|
Disposition = Context->Callback( EXPAND_QUERY_OVERWRITE,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition == EXPAND_ABORT ) {
|
||
|
return(-1); // tell FDI to abort
|
||
|
} else if ( Disposition != EXPAND_COPY_THIS_FILE ) {
|
||
|
return(0); // tell FDI to skip this file
|
||
|
}
|
||
|
} // end if target file already exists
|
||
|
|
||
|
//
|
||
|
// create temporary target file
|
||
|
//
|
||
|
wcscpy( U.PathName, Context->DestinationPath );
|
||
|
SpConcatenatePaths( U.PathName, L"$$TEMP$$.~~~" );
|
||
|
|
||
|
//
|
||
|
// see if target file exists
|
||
|
//
|
||
|
INIT_OBJA( &Obja, &UnicodeString, U.PathName );
|
||
|
|
||
|
Status = ZwCreateFile( &CurrentTargetFile.u.Handle,
|
||
|
FILE_GENERIC_WRITE,
|
||
|
&Obja,
|
||
|
&IoStatusBlock,
|
||
|
NULL,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
0, // no sharing
|
||
|
FILE_OVERWRITE_IF, // allow overwrite
|
||
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
|
||
|
NULL,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if ( !NT_SUCCESS(Status) ) {
|
||
|
|
||
|
//
|
||
|
// advise client we can't create this file
|
||
|
//
|
||
|
Disposition = Context->Callback( EXPAND_NOTIFY_CREATE_FAILED,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition != EXPAND_CONTINUE ) {
|
||
|
return(-1); // tell FDI to abort
|
||
|
}
|
||
|
|
||
|
return (0); // tell FDI to just skip this target file
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// target file created: give the handle to FDI to expand
|
||
|
//
|
||
|
|
||
|
return( (INT_PTR) &CurrentTargetFile ); // target "handle"
|
||
|
|
||
|
case fdintCLOSE_FILE_INFO:
|
||
|
|
||
|
//
|
||
|
// Diamond is done with the target file and wants us to close it.
|
||
|
//
|
||
|
|
||
|
ASSERT( CurrentTargetFile.Signature == TARGET_FILE_SIGNATURE );
|
||
|
ASSERT( CurrentTargetFile.u.Handle != INVALID_HANDLE_VALUE );
|
||
|
|
||
|
if (( CurrentTargetFile.Signature == TARGET_FILE_SIGNATURE ) &&
|
||
|
( CurrentTargetFile.u.Handle != INVALID_HANDLE_VALUE )) {
|
||
|
|
||
|
//
|
||
|
// set target file's true name (overwriting old file)
|
||
|
//
|
||
|
U.FileRenameDetails.ReplaceIfExists = TRUE;
|
||
|
U.FileRenameDetails.RootDirectory = NULL;
|
||
|
U.FileRenameDetails.FileNameLength = wcslen( FileName ) * sizeof(WCHAR);
|
||
|
wcscpy( U.FileRenameDetails.FileName, FileName );
|
||
|
|
||
|
Status = ZwSetInformationFile( CurrentTargetFile.u.Handle,
|
||
|
&IoStatusBlock,
|
||
|
&U.FileRenameDetails,
|
||
|
sizeof(U.FileRenameDetails) +
|
||
|
U.FileRenameDetails.FileNameLength,
|
||
|
FileRenameInformation );
|
||
|
|
||
|
if ( !NT_SUCCESS(Status) ) {
|
||
|
|
||
|
//
|
||
|
// Unable to change temp name to true name. Change to delete
|
||
|
// on close, close it, and tell the user it didn't work.
|
||
|
//
|
||
|
|
||
|
U.FileDispositionDetails.DeleteFile = TRUE;
|
||
|
|
||
|
ZwSetInformationFile( CurrentTargetFile.u.Handle,
|
||
|
&IoStatusBlock,
|
||
|
&U.FileDispositionDetails,
|
||
|
sizeof(U.FileDispositionDetails),
|
||
|
FileDispositionInformation );
|
||
|
|
||
|
ZwClose( CurrentTargetFile.u.Handle );
|
||
|
|
||
|
CurrentTargetFile.u.Handle = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
Disposition = Context->Callback( EXPAND_NOTIFY_CREATE_FAILED,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition != EXPAND_CONTINUE ) {
|
||
|
return(-1); // tell FDI to abort
|
||
|
}
|
||
|
|
||
|
return (TRUE); // keep FDI going
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// try to set file's last-modifed time
|
||
|
//
|
||
|
Status = ZwQueryInformationFile( CurrentTargetFile.u.Handle,
|
||
|
&IoStatusBlock,
|
||
|
&U.FileBasicDetails,
|
||
|
sizeof(U.FileBasicDetails),
|
||
|
FileBasicInformation );
|
||
|
|
||
|
if (NT_SUCCESS(Status) ) {
|
||
|
|
||
|
U.FileBasicDetails.LastWriteTime = FileTime;
|
||
|
|
||
|
ZwSetInformationFile( CurrentTargetFile.u.Handle,
|
||
|
&IoStatusBlock,
|
||
|
&U.FileBasicDetails,
|
||
|
sizeof(U.FileBasicDetails),
|
||
|
FileBasicInformation );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Note that we did not put any attributes on this file.
|
||
|
// The client callback code may do that if it so desires.
|
||
|
//
|
||
|
|
||
|
ZwClose( CurrentTargetFile.u.Handle );
|
||
|
|
||
|
CurrentTargetFile.u.Handle = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
//
|
||
|
// Tell client it has been done
|
||
|
//
|
||
|
Disposition = Context->Callback( EXPAND_COPIED_FILE,
|
||
|
FileName,
|
||
|
&FileSize,
|
||
|
&FileTime,
|
||
|
FileAttributes,
|
||
|
Context->CallbackContext);
|
||
|
|
||
|
if ( Disposition == EXPAND_ABORT ) {
|
||
|
|
||
|
return(-1); // tell FDI to abort now
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
//
|
||
|
// Cabinet management functions which we don't use.
|
||
|
// Return success.
|
||
|
//
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdNotifyFunctionDriverCab(
|
||
|
IN FDINOTIFICATIONTYPE Operation,
|
||
|
IN PFDINOTIFICATION Parameters
|
||
|
)
|
||
|
{
|
||
|
BOOLEAN extract;
|
||
|
PWSTR CabNameW;
|
||
|
ULONG Size;
|
||
|
ULONG StringSize;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
switch(Operation) {
|
||
|
|
||
|
case fdintCABINET_INFO:
|
||
|
case fdintNEXT_CABINET:
|
||
|
case fdintPARTIAL_FILE:
|
||
|
|
||
|
//
|
||
|
// Cabinet management functions which we don't use.
|
||
|
// Return success.
|
||
|
//
|
||
|
return(0);
|
||
|
|
||
|
case fdintCOPY_FILE:
|
||
|
|
||
|
//
|
||
|
// Diamond is asking us whether we want to copy the file.
|
||
|
// We need to return a file handle to indicate that we do.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// diamond is an ansi API -- we need to convert to unicode string
|
||
|
//
|
||
|
|
||
|
extract = FALSE;
|
||
|
if (DriverContext.FileNameA) {
|
||
|
if (_stricmp(DriverContext.FileNameA, Parameters->psz1) == 0) {
|
||
|
extract = TRUE;
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
StringSize = strlen(Parameters->psz1);
|
||
|
CabNameW = SpMemAlloc ((StringSize+1) * sizeof(WCHAR));
|
||
|
if (!CabNameW) {
|
||
|
//
|
||
|
// we're out of memory, abort
|
||
|
//
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
Status = RtlMultiByteToUnicodeN (
|
||
|
CabNameW,
|
||
|
StringSize * sizeof(WCHAR),
|
||
|
&Size,
|
||
|
Parameters->psz1,
|
||
|
StringSize
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
//
|
||
|
// failed to translate, abort
|
||
|
//
|
||
|
SpMemFree(CabNameW);
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
extract = FALSE;
|
||
|
|
||
|
//
|
||
|
// null terminate
|
||
|
//
|
||
|
CabNameW[StringSize] = 0;
|
||
|
if (_wcsicmp(DriverContext.FileName, CabNameW) == 0) {
|
||
|
extract = TRUE;
|
||
|
}
|
||
|
|
||
|
SpMemFree( CabNameW );
|
||
|
}
|
||
|
|
||
|
if (extract) {
|
||
|
return((INT_PTR)&CurrentTargetFile);
|
||
|
} else {
|
||
|
return (INT_PTR)NULL;
|
||
|
}
|
||
|
|
||
|
case fdintCLOSE_FILE_INFO:
|
||
|
|
||
|
//
|
||
|
// Diamond is done with the target file and wants us to close it.
|
||
|
// (ie, this is the counterpart to fdint_COPY_FILE).
|
||
|
// We manage our own file i/o so ignore this
|
||
|
// (first we grab the file date and time)
|
||
|
//
|
||
|
DriverContext.FileDate = Parameters->date;
|
||
|
DriverContext.FileTime = Parameters->time;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
PVOID
|
||
|
DIAMONDAPI
|
||
|
SpdFdiAlloc(
|
||
|
IN ULONG NumberOfBytes
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to allocate memory.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NumberOfBytes - supplies desired size of block.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns pointer to a block of cache-aligned memory.
|
||
|
Does not return if memory cannot be allocated.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PVOID p;
|
||
|
|
||
|
p = ExAllocatePoolWithTag(PagedPoolCacheAligned,NumberOfBytes,SETUP_FDI_POOL_TAG);
|
||
|
|
||
|
if(!p) {
|
||
|
SpOutOfMemory();
|
||
|
}
|
||
|
|
||
|
return(p);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DIAMONDAPI
|
||
|
SpdFdiFree(
|
||
|
IN PVOID Block
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to free a memory block.
|
||
|
The block must have been allocated with SpdFdiAlloc().
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Block - supplies pointer to block of memory to be freed.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ExFreePool(Block);
|
||
|
}
|
||
|
|
||
|
|
||
|
INT_PTR
|
||
|
DIAMONDAPI
|
||
|
SpdFdiOpen(
|
||
|
IN PSTR FileName,
|
||
|
IN int oflag,
|
||
|
IN int pmode
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to open files.
|
||
|
|
||
|
In our implementation, the source and target files are already opened
|
||
|
by the time we can get to this point so we don'tt ever actually open
|
||
|
anything here.
|
||
|
|
||
|
However diamond may 'open' the source file more than once because it
|
||
|
wants 2 different states. We support that here by using our own
|
||
|
'handles' with special meaning to us.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FileName - supplies name of file to be opened. Ignored.
|
||
|
|
||
|
oflag - supplies flags for open. Ignored.
|
||
|
|
||
|
pmode - supplies additional flags for open. Ignored.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PMY_FILE_STATE State;
|
||
|
|
||
|
UNREFERENCED_PARAMETER(FileName);
|
||
|
UNREFERENCED_PARAMETER(oflag);
|
||
|
UNREFERENCED_PARAMETER(pmode);
|
||
|
|
||
|
//
|
||
|
// Note: we only support opening the source (cabinet) file, which we
|
||
|
// carefully pass in to FDICopy() as the empty string.
|
||
|
//
|
||
|
ASSERT(*FileName == 0);
|
||
|
if(*FileName) {
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
State = SpMemAlloc(sizeof(MY_FILE_STATE));
|
||
|
|
||
|
State->u.FileOffset = 0;
|
||
|
State->Signature = SOURCE_FILE_SIGNATURE;
|
||
|
|
||
|
return((INT_PTR)State);
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT
|
||
|
DIAMONDAPI
|
||
|
SpdFdiRead(
|
||
|
IN INT_PTR Handle,
|
||
|
OUT PVOID pv,
|
||
|
IN UINT ByteCount
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to read from a file.
|
||
|
|
||
|
We assume that diamond is going to read only from the cabinet file.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Handle - supplies handle to open file to be read from.
|
||
|
|
||
|
pv - supplies pointer to buffer to receive bytes we read.
|
||
|
|
||
|
ByteCount - supplies number of bytes to read.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Number of bytes read or -1 if an error occurs.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
UINT rc;
|
||
|
PMY_FILE_STATE State;
|
||
|
LONG RealByteCount;
|
||
|
|
||
|
State = (PMY_FILE_STATE)Handle;
|
||
|
|
||
|
//
|
||
|
// Assume failure.
|
||
|
//
|
||
|
rc = (UINT)(-1);
|
||
|
|
||
|
//
|
||
|
// Only read the source with this routine.
|
||
|
//
|
||
|
ASSERT(State->Signature == SOURCE_FILE_SIGNATURE);
|
||
|
if(State->Signature == SOURCE_FILE_SIGNATURE) {
|
||
|
|
||
|
RealByteCount = (LONG)ByteCount;
|
||
|
if(State->u.FileOffset + RealByteCount > (LONG)SpdSourceFileSize) {
|
||
|
RealByteCount = (LONG)SpdSourceFileSize - State->u.FileOffset;
|
||
|
}
|
||
|
if(RealByteCount < 0) {
|
||
|
RealByteCount = 0;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
pv,
|
||
|
SpdSourceAddress + State->u.FileOffset,
|
||
|
(ULONG)RealByteCount
|
||
|
);
|
||
|
|
||
|
State->u.FileOffset += RealByteCount;
|
||
|
|
||
|
rc = RealByteCount;
|
||
|
|
||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(rc);
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT
|
||
|
DIAMONDAPI
|
||
|
SpdFdiWrite(
|
||
|
IN INT_PTR Handle,
|
||
|
IN PVOID pv,
|
||
|
IN UINT ByteCount
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to write to a file.
|
||
|
|
||
|
We assume that diamond is going to write only to the target file.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Handle - supplies handle to open file to be written to.
|
||
|
|
||
|
pv - supplies pointer to buffer containing bytes to write.
|
||
|
|
||
|
ByteCount - supplies number of bytes to write.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Number of bytes written (ByteCount) or -1 if an error occurs.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
UINT rc;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
NTSTATUS Status;
|
||
|
PMY_FILE_STATE State;
|
||
|
|
||
|
State = (PMY_FILE_STATE)Handle;
|
||
|
|
||
|
//
|
||
|
// Assume failure.
|
||
|
//
|
||
|
rc = (UINT)(-1);
|
||
|
|
||
|
//
|
||
|
// Only write the target with this routine.
|
||
|
//
|
||
|
ASSERT(State->Signature == TARGET_FILE_SIGNATURE);
|
||
|
if(State->Signature == TARGET_FILE_SIGNATURE) {
|
||
|
|
||
|
Status = ZwWriteFile(
|
||
|
(HANDLE)State->u.Handle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
pv,
|
||
|
ByteCount,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if(NT_SUCCESS(Status)) {
|
||
|
rc = ByteCount;
|
||
|
} else {
|
||
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpdFdiWrite: Status %lx writing to target file\n",Status));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(rc);
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
DIAMONDAPI
|
||
|
SpdFdiClose(
|
||
|
IN INT_PTR Handle
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to close files.
|
||
|
|
||
|
In our implementation, the source and target files are managed
|
||
|
elsewhere so we don't actually need to close any files.
|
||
|
However we may need to free some state information.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Handle - handle of file to close.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
0 (success).
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PMY_FILE_STATE State = (PMY_FILE_STATE)Handle;
|
||
|
|
||
|
//
|
||
|
// Only 'close' the source file.
|
||
|
//
|
||
|
if(State->Signature == SOURCE_FILE_SIGNATURE) {
|
||
|
SpMemFree(State);
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
DIAMONDAPI
|
||
|
SpdFdiSeek(
|
||
|
IN INT_PTR Handle,
|
||
|
IN long Distance,
|
||
|
IN int SeekType
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Callback used by FDICopy to seek files.
|
||
|
|
||
|
We assume that we can seek only in the source file.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Handle - handle of file to close.
|
||
|
|
||
|
Distance - supplies distance to seek. Interpretation of this
|
||
|
parameter depends on the value of SeekType.
|
||
|
|
||
|
SeekType - supplies a value indicating how Distance is to be
|
||
|
interpreted; one of SEEK_SET, SEEK_CUR, SEEK_END.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
New file offset.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PMY_FILE_STATE State = (PMY_FILE_STATE)Handle;
|
||
|
LONG rc;
|
||
|
|
||
|
//
|
||
|
// Assume failure.
|
||
|
//
|
||
|
rc = -1L;
|
||
|
|
||
|
//
|
||
|
// Only allow seeking in the source.
|
||
|
//
|
||
|
ASSERT(State->Signature == SOURCE_FILE_SIGNATURE);
|
||
|
|
||
|
if(State->Signature == SOURCE_FILE_SIGNATURE) {
|
||
|
|
||
|
switch(SeekType) {
|
||
|
|
||
|
case SEEK_CUR:
|
||
|
|
||
|
//
|
||
|
// Distance is an offset from the current file position.
|
||
|
//
|
||
|
State->u.FileOffset += Distance;
|
||
|
break;
|
||
|
|
||
|
case SEEK_END:
|
||
|
|
||
|
//
|
||
|
// Distance is an offset from the end of file.
|
||
|
//
|
||
|
State->u.FileOffset = SpdSourceFileSize - Distance;
|
||
|
break;
|
||
|
|
||
|
case SEEK_SET:
|
||
|
|
||
|
//
|
||
|
// Distance is the new absolute offset.
|
||
|
//
|
||
|
State->u.FileOffset = (ULONG)Distance;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(State->u.FileOffset < 0) {
|
||
|
State->u.FileOffset = 0;
|
||
|
}
|
||
|
|
||
|
if(State->u.FileOffset > (LONG)SpdSourceFileSize) {
|
||
|
State->u.FileOffset = SpdSourceFileSize;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return successful status.
|
||
|
//
|
||
|
rc = State->u.FileOffset;
|
||
|
}
|
||
|
|
||
|
return(rc);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SpdInitialize(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
FdiContext = FDICreate(
|
||
|
SpdFdiAlloc,
|
||
|
SpdFdiFree,
|
||
|
SpdFdiOpen,
|
||
|
SpdFdiRead,
|
||
|
SpdFdiWrite,
|
||
|
SpdFdiClose,
|
||
|
SpdFdiSeek,
|
||
|
cpuUNKNOWN,
|
||
|
&FdiError
|
||
|
);
|
||
|
|
||
|
if(FdiContext == NULL) {
|
||
|
SpOutOfMemory();
|
||
|
}
|
||
|
|
||
|
RtlZeroMemory(&DriverContext, sizeof(DriverContext) );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SpdTerminate(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
FDIDestroy(FdiContext);
|
||
|
|
||
|
FdiContext = NULL;
|
||
|
}
|