Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1650 lines
48 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
fileq4.c
Abstract:
Setup file queue routines for commit (ie, performing enqueued actions).
Author:
Ted Miller (tedm) 15-Feb-1995
Revision History:
--*/
#include "setupntp.h"
#pragma hdrstop
typedef struct _Q_CAB_CB_DATA {
PSP_FILE_QUEUE Queue;
PSOURCE_MEDIA_INFO SourceMedia;
PSP_FILE_QUEUE_NODE CurrentFirstNode;
PVOID MsgHandler;
PVOID Context;
BOOL IsMsgHandlerNativeCharWidth;
} Q_CAB_CB_DATA, *PQ_CAB_CB_DATA;
DWORD
pCommitCopyQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
UINT
pSetupCabinetQueueCallback(
IN PVOID Context,
IN UINT Notification,
IN UINT Param1,
IN UINT Param2
);
DWORD
pSetupCopySingleQueuedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
OUT PTSTR NewSourcePath,
IN BOOL IsMsgHandlerNativeCharWidth
);
DWORD
pSetupCopySingleQueuedFileCabCase(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR CabinetName,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
);
VOID
pSetupSetPathOverrides(
IN PVOID StringTable,
IN OUT PTSTR RootPath,
IN OUT PTSTR SubPath,
IN LONG RootPathId,
IN LONG SubPathId,
IN PTSTR NewPath
);
VOID
pSetupBuildSourceForCopy(
IN PCTSTR UserRoot,
IN PCTSTR UserPath,
IN LONG MediaRoot,
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
OUT PTSTR FullPath
);
PTSTR
pSetupFormFullPath(
IN PVOID StringTable,
IN LONG PathPart1,
IN LONG PathPart2, OPTIONAL
IN LONG PathPart3 OPTIONAL
);
BOOL
_SetupCommitFileQueue(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
/*++
Routine Description:
Implementation for SetupCommitFileQueue; handles ANSI and Unicode
callback routines.
Arguments:
Same as for SetupCommitFileQueue().
IsMsgHandlerNativeCharWidth - indicates whether the MsgHandler callback
expects native char width args (or ansi ones, in the unicode build
of this dll).
Return Value:
Boolean value indicating outcome.
--*/
{
PSP_FILE_QUEUE Queue;
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
UINT u;
BOOL b;
DWORD rc;
PCTSTR FullSourcePath,FullTargetPath;
FILEPATHS FilePaths;
#define BAIL(rc) pSetupCallMsgHandler( \
MsgHandler, \
IsMsgHandlerNativeCharWidth,\
Context, \
SPFILENOTIFY_ENDQUEUE, \
FALSE, \
0 \
); \
\
SetLastError((rc)); \
return(FALSE)
//
// Queue handle is actually a pointer to the queue structure.
//
Queue = (PSP_FILE_QUEUE)QueueHandle;
//
// If there's nothing to do, bail now. This prevents an empty
// progress dialog from flashing on the screen. Don't return out
// of the body of the try -- that is bad news performance-wise.
//
try {
b = (!Queue->DeleteNodeCount && !Queue->RenameNodeCount && !Queue->CopyNodeCount);
} except (EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
if(b) {
return(TRUE);
}
b = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTQUEUE,
(UINT)Owner,
0
);
if(!b) {
BAIL(GetLastError());
}
//
// Handle deletes first.
//
if(Queue->DeleteNodeCount) {
b = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_DELETE,
Queue->DeleteNodeCount
);
if(!b) {
BAIL(GetLastError());
}
for(QueueNode=Queue->DeleteQueue; QueueNode; QueueNode=QueueNode->Next) {
//
// Form the full path of the file to be deleted.
//
FullTargetPath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetPath) {
BAIL(ERROR_NOT_ENOUGH_MEMORY);
}
FilePaths.Source = NULL;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
//
// Inform the callback that we are about to start a delete operation.
//
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTDELETE,
(UINT)&FilePaths,
FILEOP_DELETE
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
MyFree(FullTargetPath);
BAIL(rc);
}
if(u == FILEOP_DOIT) {
//
// Attempt the delete. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
SetFileAttributes(FullTargetPath,FILE_ATTRIBUTE_NORMAL);
do {
rc = DeleteFile(FullTargetPath) ? NO_ERROR : GetLastError();
if(rc == ERROR_ACCESS_DENIED) {
//
// The file is probably in use.
//
if(QueueNode->InternalFlags & IQF_DELAYED_DELETE_OK) {
//
// Inf wanted delete on next reboot.
//
QueueNode->InternalFlags |= INUSE_IN_USE;
if(b = DelayedMove(FullTargetPath,NULL)) {
//
// Tell the callback.
//
FilePaths.Source = NULL;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
FilePaths.Flags = FILEOP_DELETE;
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_FILEOPDELAYED,
(UINT)&FilePaths,
0
);
}
} else {
//
// Just skip this file.
//
b = TRUE;
}
rc = b ? NO_ERROR : GetLastError();
}
if(rc != NO_ERROR) {
FilePaths.Win32Error = rc;
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_DELETEERROR,
(UINT)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
MyFree(FullTargetPath);
BAIL(rc);
}
if(u == FILEOP_SKIP) {
break;
}
}
} while(rc != NO_ERROR);
} else {
rc = NO_ERROR;
}
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDDELETE,
(UINT)&FilePaths,
0
);
MyFree(FullTargetPath);
}
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_DELETE,
0
);
}
//
// Handle renames next.
//
if(Queue->RenameNodeCount) {
b = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_RENAME,
Queue->RenameNodeCount
);
if(!b) {
BAIL(GetLastError());
}
for(QueueNode=Queue->RenameQueue; QueueNode; QueueNode=QueueNode->Next) {
//
// Form the full source path of the file to be renamed.
//
FullSourcePath = pSetupFormFullPath(
Queue->StringTable,
QueueNode->SourcePath,
QueueNode->SourceFilename,
-1
);
if(!FullSourcePath) {
BAIL(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Form the full target path of the file to be renamed.
//
FullTargetPath = pSetupFormFullPath(
Queue->StringTable,
//BUGBUG need to pull out only path part of SourcePath
QueueNode->TargetDirectory == -1 ? QueueNode->SourcePath : QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetPath) {
MyFree(FullSourcePath);
BAIL(ERROR_NOT_ENOUGH_MEMORY);
}
FilePaths.Source = FullSourcePath;
FilePaths.Target = FullTargetPath;
FilePaths.Win32Error = NO_ERROR;
//
// Inform the callback that we are about to start a rename operation.
//
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTRENAME,
(UINT)&FilePaths,
FILEOP_RENAME
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
MyFree(FullSourcePath);
MyFree(FullTargetPath);
BAIL(rc);
}
if(u == FILEOP_DOIT) {
//
// Attempt the rename. If it fails inform the callback,
// which may decide to abort, retry. or skip the file.
//
do {
rc = MoveFile(FullSourcePath,FullTargetPath) ? NO_ERROR : GetLastError();
if((rc == ERROR_FILE_NOT_FOUND) || (rc == ERROR_PATH_NOT_FOUND)) {
rc = NO_ERROR;
}
if(rc != NO_ERROR) {
FilePaths.Win32Error = rc;
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_RENAMEERROR,
(UINT)&FilePaths,
0
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
MyFree(FullSourcePath);
MyFree(FullTargetPath);
BAIL(rc);
}
if(u == FILEOP_SKIP) {
break;
}
}
} while(rc != NO_ERROR);
} else {
rc = NO_ERROR;
}
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDRENAME,
(UINT)&FilePaths,
0
);
MyFree(FullSourcePath);
MyFree(FullTargetPath);
}
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_RENAME,
0
);
}
//
// Handle copies last. Don't bother calling the copy commit routine
// if there are no files to copy.
//
rc = Queue->CopyNodeCount
? pCommitCopyQueue(Queue,MsgHandler,Context,IsMsgHandlerNativeCharWidth)
: NO_ERROR;
b = (rc == NO_ERROR);
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDQUEUE,
b,
0
);
if(!b) {
SetLastError(rc);
}
return(b);
}
#ifdef UNICODE
//
// ANSI version. Also need undecorated (Unicode) version for compatibility
// with apps that were linked before we had A and W versions.
//
BOOL
SetupCommitFileQueueA(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_A MsgHandler,
IN PVOID Context
)
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,FALSE));
}
#undef SetupCommitFileQueue
SetupCommitFileQueue(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_W MsgHandler,
IN PVOID Context
)
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,TRUE));
}
#else
//
// Unicode stub. Also need undecorated (ANSI) version for compatibility
// with apps that were linked before we had A and W versions.
//
BOOL
SetupCommitFileQueueW(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_W MsgHandler,
IN PVOID Context
)
{
UNREFERENCED_PARAMETER(Owner);
UNREFERENCED_PARAMETER(QueueHandle);
UNREFERENCED_PARAMETER(MsgHandler);
UNREFERENCED_PARAMETER(Context);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#undef SetupCommitFileQueue
SetupCommitFileQueue(
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK_A MsgHandler,
IN PVOID Context
)
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,TRUE));
}
#endif
BOOL
#ifdef UNICODE
SetupCommitFileQueueW(
#else
SetupCommitFileQueueA(
#endif
IN HWND Owner, OPTIONAL
IN HSPFILEQ QueueHandle,
IN PSP_FILE_CALLBACK MsgHandler,
IN PVOID Context
)
/*++
Routine Description:
Perform file operations enqueued on a setup file queue.
Arguments:
OwnerWindow - if specified, supplies the window handle of a window
that is to be used as the parent of any progress dialogs.
QueueHandle - supplies a handle to a setup file queue, as returned
by SetupOpenFileQueue.
MsgHandler - Supplies a callback routine to be notified
of various significant events in the queue processing.
Context - Supplies a value that is passed to the MsgHandler
callback function.
Return Value:
Boolean value indicating outcome.
--*/
{
return(_SetupCommitFileQueue(Owner,QueueHandle,MsgHandler,Context,TRUE));
}
DWORD
pCommitCopyQueue(
IN PSP_FILE_QUEUE Queue,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
{
PSOURCE_MEDIA_INFO SourceMediaInfo;
SOURCE_MEDIA SourceMedia;
PTCHAR p;
UINT SourcePathLen;
UINT u;
DWORD rc;
Q_CAB_CB_DATA QData;
BOOL b;
BOOL FirstIteration;
PSP_FILE_QUEUE_NODE QueueNode,queueNode;
TCHAR UserSourceRoot[MAX_PATH];
TCHAR UserSourcePath[MAX_PATH];
TCHAR FullSourcePath[MAX_PATH];
TCHAR UserOverride[MAX_PATH];
//
// The caller is supposed to skip calling us if there are no files
// to be copied.
//
MYASSERT(Queue->CopyNodeCount);
//
// Inform the callback that we are starting.
//
b = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTSUBQUEUE,
FILEOP_COPY,
Queue->CopyNodeCount
);
if(!b) {
return(GetLastError());
}
//
// Initially, no user-specified override path exists.
//
UserSourceRoot[0] = 0;
UserSourcePath[0] = 0;
//
// The outermost loop iterates through all the source media descriptors.
//
for(SourceMediaInfo=Queue->SourceMediaList; SourceMediaInfo; SourceMediaInfo=SourceMediaInfo->Next) {
//
// If there are no files on this particular media, skip it.
// Otherwise get pointer to queue node for first file on this media.
//
if(!SourceMediaInfo->CopyNodeCount) {
continue;
}
MYASSERT(SourceMediaInfo->CopyQueue);
//
// We will need to prompt for media, which requires some preparation.
// We need to get the first file in the queue for this media, because
// its path is where we will expect to find it or its cabinet or tag file.
// If there is no tag file, then we will look for the file itself.
//
QueueNode = SourceMediaInfo->CopyQueue;
FirstIteration = TRUE;
RepromptMedia:
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
QueueNode,
FullSourcePath
);
p = _tcsrchr(FullSourcePath,TEXT('\\'));
*p++ = 0;
//
// Now FullSourcePath has the path part and p has the file part
// for the first file in the queue for this media.
// Get the media in the drive by calling the callback function.
//
SourceMedia.Tagfile = (SourceMediaInfo->Tagfile != -1 && FirstIteration)
? StringTableStringFromId(
Queue->StringTable,
SourceMediaInfo->Tagfile
)
: NULL;
SourceMedia.Description = (SourceMediaInfo->Description != -1)
? StringTableStringFromId(
Queue->StringTable,
SourceMediaInfo->DescriptionDisplayName
)
: NULL;
SourceMedia.SourcePath = FullSourcePath;
SourceMedia.SourceFile = p;
SourceMedia.Flags = (QueueNode->StyleFlags & (SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP | SP_COPY_NOBROWSE));
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_NEEDMEDIA,
(UINT)&SourceMedia,
(UINT)UserOverride
);
if(u == FILEOP_ABORT) {
return(GetLastError());
}
if(u == FILEOP_SKIP) {
//
// If there are more files on this media, then try another one.
// Otherwise we're done with this media.
//
QueueNode->InternalFlags |= IQF_PROCESSED;
for(QueueNode=QueueNode->Next; QueueNode; QueueNode=QueueNode->Next) {
if(!(QueueNode->InternalFlags & IQF_PROCESSED)) {
FirstIteration = FALSE;
goto RepromptMedia;
}
}
continue;
}
if(u == FILEOP_NEWPATH) {
//
// User gave us a new source path. See which parts of the new path
// match the existing path/overrides we are using.
//
pSetupSetPathOverrides(
Queue->StringTable,
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
QueueNode->SourcePath,
UserOverride
);
}
//
// If we get here, the media is now accessible.
// Some or all of the files might be in a cabinet whose name is the tagfile.
//
// NOTE: Win95 used the tagfile field to be the cabinet name instead.
// If present it is used as a tagfile of sorts. The absence of a tagfile
// means the files are not in cabinets. For NT, we don't bother
// with all of this but instead try to be a little smarter.
//
// Scan the media for all source files we expect to find on it.
// If we find a file, process it. Later we hit the cabinet and only
// process the files we didn't already find outside the cabinet.
//
for(queueNode=QueueNode; queueNode; queueNode=queueNode->Next) {
if(queueNode->InternalFlags & IQF_PROCESSED) {
//
// Already processed. Skip to next file.
//
continue;
}
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
queueNode,
FullSourcePath
);
rc = SetupDetermineSourceFileName(FullSourcePath,&b,&p,NULL);
if(rc == NO_ERROR) {
//
// Found the file outside a cabinet. Process it now.
//
rc = pSetupCopySingleQueuedFile(
Queue,
queueNode,
p,
MsgHandler,
Context,
UserOverride,
IsMsgHandlerNativeCharWidth
);
MyFree(p);
if(rc != NO_ERROR) {
return(rc);
}
//
// See if we have a new source path.
//
if(UserOverride[0]) {
pSetupSetPathOverrides(
Queue->StringTable,
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
queueNode->SourcePath,
UserOverride
);
}
}
}
//
// See if any files still need to be processed.
//
for(b=FALSE,queueNode=QueueNode; queueNode; queueNode=queueNode->Next) {
if(!(queueNode->InternalFlags & IQF_PROCESSED)) {
b = TRUE;
break;
}
}
//
// If any files still need to be processed and we have a potential
// cabinet file, go try to extract them from a cabinet.
//
if(b && (SourceMediaInfo->Tagfile != -1) && FirstIteration) {
pSetupBuildSourceForCopy(
UserSourceRoot,
UserSourcePath,
SourceMediaInfo->SourceRootPath,
Queue,
queueNode,
FullSourcePath
);
lstrcpy(
_tcsrchr(FullSourcePath,TEXT('\\'))+1,
StringTableStringFromId(Queue->StringTable,SourceMediaInfo->Tagfile)
);
if(DiamondIsCabinet(FullSourcePath)) {
QData.Queue = Queue;
QData.SourceMedia = SourceMediaInfo;
QData.MsgHandler = MsgHandler;
QData.IsMsgHandlerNativeCharWidth = IsMsgHandlerNativeCharWidth;
QData.Context = Context;
rc = DiamondProcessCabinet(
FullSourcePath,
0,
pSetupCabinetQueueCallback,
&QData,
TRUE
);
if(rc != NO_ERROR) {
return(rc);
}
//
// Now reset the tagfile to indicate that there is no cabinet.
// If we don't do this and there are still files that have not
// been processed, we'll end up in an infinite loop -- the prompt
// will come back successfully, and we'll just keep going around
// and around looking through the cabinet, etc.
//
SourceMediaInfo->Tagfile = -1;
}
}
//
// If we get here and files *still* need to be processed,
// assume the files are in a different directory somewhere
// and start all over with this media.
//
for( ; QueueNode; QueueNode=QueueNode->Next) {
if(!(QueueNode->InternalFlags & IQF_PROCESSED)) {
FirstIteration = FALSE;
goto RepromptMedia;
}
}
}
//
// Tell handler we're done with the copy queue and return.
//
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDSUBQUEUE,
FILEOP_COPY,
0
);
return(NO_ERROR);
}
VOID
pSetupBuildSourceForCopy(
IN PCTSTR UserRoot,
IN PCTSTR UserPath,
IN LONG MediaRoot,
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
OUT PTSTR FullPath
)
{
PCTSTR p;
//
// If there is a user-specified override root path, use that instead of
// the root path specified in the source media descriptor.
//
p = UserRoot[0]
? UserRoot
: StringTableStringFromId(Queue->StringTable,MediaRoot);
lstrcpyn(FullPath,p,MAX_PATH);
//
// If there is a user-specified override path, use that instead of any
// path specified in the copy node.
//
if(UserPath[0]) {
p = UserPath;
} else {
if(QueueNode->SourcePath == -1) {
p = NULL;
} else {
p = StringTableStringFromId(Queue->StringTable,QueueNode->SourcePath);
}
}
if(p) {
ConcatenatePaths(FullPath,p,MAX_PATH,NULL);
}
//
// Fetch the filename and append.
//
p = StringTableStringFromId(Queue->StringTable,QueueNode->SourceFilename),
ConcatenatePaths(FullPath,p,MAX_PATH,NULL);
}
VOID
pSetupSetPathOverrides(
IN PVOID StringTable,
IN OUT PTSTR RootPath,
IN OUT PTSTR SubPath,
IN LONG RootPathId,
IN LONG SubPathId,
IN PTSTR NewPath
)
{
PCTSTR root,path;
UINT u,l;
//
// See if the existing root override or root path is a prefix
// of the path the user gave us.
//
root = RootPath[0] ? RootPath : StringTableStringFromId(StringTable,RootPathId);
u = lstrlen(root);
path = SubPath[0]
? SubPath
: ((SubPathId == -1) ? NULL : StringTableStringFromId(StringTable,SubPathId));
if(path && (*path == TEXT('\\'))) {
path++;
}
if(_tcsnicmp(NewPath,root,u)) {
//
// Root path does not match what we're currently using, ie, the user
// supplied a new path. In this case, we will see if the currently in-use
// subpath matches the suffix of the new path, and if so, we'll assume
// that is the override subpath and shorten the override root path.
//
lstrcpy(RootPath,NewPath);
if(path) {
u = lstrlen(NewPath);
l = lstrlen(path);
if((u > l) && (NewPath[(u-l)-1] == TEXT('\\')) && !lstrcmpi(NewPath+u-l,path)) {
//
// Subpath tail matches. Truncate the root override and
// leave the subpath override alone.
//
RootPath[(u-l)-1] = 0;
} else {
//
// In this case, we need to indicate an override subpath of the root,
// or else all subsequent accesses will still try to append the subpath
// specified in the copy node, which is not what we want.
//
SubPath[0] = TEXT('\\');
SubPath[1] = 0;
}
}
} else {
//
// Root path matches what we are currently using.
//
// See if the tail of the user-specified path matches the existing
// subpath. If not, then use the rest of the root path as the subpath
// override. If the tail matches, then extend the user override root.
//
// Examples:
//
// File was queued with root = f:\, subpath = \mips
//
// User override path is f:\alpha
//
// The new status will be leave override root alone;
// override subpath = \alpha
//
// File was queued with root = \\foo\bar, subpath = \i386
//
// User override path is \\foo\bar\new\i386
//
// The new status will be a root override of \\foo\bar\new;
// no override subpath.
//
NewPath += u;
if(*NewPath == TEXT('\\')) {
NewPath++;
}
if(path) {
u = lstrlen(NewPath);
l = lstrlen(path);
if((u >= l) && !lstrcmpi(NewPath+u-l,path)) {
//
// Change root override and indicate no override subpath.
//
SubPath[0] = 0;
NewPath[u-l] = 0;
lstrcpy(RootPath,root);
ConcatenatePaths(RootPath,NewPath,MAX_PATH,NULL);
u = lstrlen(RootPath);
if(u && (RootPath[u-1] == TEXT('\\'))) {
RootPath[u-1] = 0;
}
} else {
//
// Leave override root alone but change subpath.
//
lstrcpy(SubPath,NewPath);
if(!SubPath[0]) {
SubPath[0] = TEXT('\\');
SubPath[1] = 0;
}
}
}
}
}
UINT
pSetupCabinetQueueCallback(
IN PVOID Context,
IN UINT Notification,
IN UINT Param1,
IN UINT Param2
)
{
UINT rc;
PCABINET_INFO CabinetInfo;
PFILE_IN_CABINET_INFO FileInfo;
TCHAR TempPath[MAX_PATH];
PTSTR CabinetFile;
PTSTR QueuedFile;
PTSTR FilePart1,FilePart2;
PTSTR FullTargetPath;
PFILEPATHS FilePaths;
PSP_FILE_QUEUE_NODE QueueNode,FirstNode,LastNode;
PQ_CAB_CB_DATA QData;
UINT h;
SOURCE_MEDIA SourceMedia;
QData = 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.
//
// Determine whether we want to copy this file.
// The context we get has all the stuff we need in it
// to make this determination.
//
// Note that the queue could contain multiple copy operations
// involving this file, but we only want to extract it once!
//
FileInfo = (PFILE_IN_CABINET_INFO)Param1;
CabinetFile = (PTSTR)Param2;
if(FilePart1 = _tcsrchr(FileInfo->NameInCabinet,TEXT('\\'))) {
FilePart1++;
} else {
FilePart1 = (PTSTR)FileInfo->NameInCabinet;
}
rc = FILEOP_SKIP;
FileInfo->Win32Error = NO_ERROR;
FirstNode = NULL;
//
// Find ALL instances of this file in the queue and mark them.
//
for(QueueNode=QData->SourceMedia->CopyQueue; QueueNode; QueueNode=QueueNode->Next) {
if(QueueNode->InternalFlags & IQF_PROCESSED) {
//
// This file was already processed. Ignore it.
//
continue;
}
//
// Check the filename in the cabinet against the file
// in the media's copy queue.
//
QueuedFile = StringTableStringFromId(
QData->Queue->StringTable,
QueueNode->SourceFilename
);
if(FilePart2 = _tcsrchr(QueuedFile,TEXT('\\'))) {
FilePart2++;
} else {
FilePart2 = QueuedFile;
}
if(!lstrcmpi(FilePart1,FilePart2)) {
//
// We want this file.
//
rc = FILEOP_DOIT;
QueueNode->InternalFlags |= IQF_PROCESSED | IQF_MATCH;
if(!FirstNode) {
FirstNode = QueueNode;
}
LastNode = QueueNode;
}
}
if(rc == FILEOP_DOIT) {
//
// We want this file. Tell the caller the full target pathname
// to be used, which is a temporary file in the directory
// where the first instance of the file will ultimately go.
// We do this so we can call SetupInstallFile later (perhaps
// multiple times), which will handle version checks, etc.
//
// Before attempting to create a temp file make sure the path exists.
//
lstrcpyn(
TempPath,
StringTableStringFromId(QData->Queue->StringTable,FirstNode->TargetDirectory),
MAX_PATH
);
ConcatenatePaths(TempPath,TEXT("x"),MAX_PATH,NULL); // last component ignored
h = pSetupMakeSurePathExists(TempPath);
if(h == NO_ERROR) {
LastNode->InternalFlags |= IQF_LAST_MATCH;
h = GetTempFileName(
StringTableStringFromId(QData->Queue->StringTable,FirstNode->TargetDirectory),
TEXT("SETP"),
0,
FileInfo->FullTargetName
);
if(h) {
QData->CurrentFirstNode = FirstNode;
} else {
FileInfo->Win32Error = GetLastError();
rc = FILEOP_ABORT;
}
} else {
FileInfo->Win32Error = GetLastError();
rc = FILEOP_ABORT;
}
}
break;
case SPFILENOTIFY_FILEEXTRACTED:
FilePaths = (PFILEPATHS)Param1;
//
// The current file was extracted. If this was successful,
// then we need to call SetupInstallFile on it to perform version
// checks and move it into its final location or locations.
//
// The .Source member of FilePaths is the cabinet file.
//
// The .Target member is the name of the temporary file, which is
// very useful, as it is the name if the file to use as the source
// in copy operations.
//
// Process each file in the queue that we care about.
//
if((rc = FilePaths->Win32Error) == NO_ERROR) {
for(QueueNode=QData->CurrentFirstNode; QueueNode && (rc==NO_ERROR); QueueNode=QueueNode->Next) {
//
// If we don't care about this file, skip it.
//
if(!(QueueNode->InternalFlags & IQF_MATCH)) {
continue;
}
QueueNode->InternalFlags &= ~IQF_MATCH;
rc = pSetupCopySingleQueuedFileCabCase(
QData->Queue,
QueueNode,
FilePaths->Source,
FilePaths->Target,
QData->MsgHandler,
QData->Context,
QData->IsMsgHandlerNativeCharWidth
);
//
// If this was the last file that matched, break out.
//
if(QueueNode->InternalFlags & IQF_LAST_MATCH) {
QueueNode->InternalFlags &= ~IQF_LAST_MATCH;
break;
}
}
}
//
// Delete the temporary file we extracted -- we don't need it any more.
//
DeleteFile(FilePaths->Target);
break;
case SPFILENOTIFY_NEEDNEWCABINET:
//
// Need a new cabinet.
//
CabinetInfo = (PCABINET_INFO)Param1;
SourceMedia.Tagfile = NULL;
SourceMedia.Description = CabinetInfo->DiskName;
SourceMedia.SourcePath = CabinetInfo->CabinetPath;
SourceMedia.SourceFile = CabinetInfo->CabinetFile;
SourceMedia.Flags = SP_FLAG_CABINETCONTINUATION | SP_COPY_NOSKIP;
h = pSetupCallMsgHandler(
QData->MsgHandler,
QData->IsMsgHandlerNativeCharWidth,
QData->Context,
SPFILENOTIFY_NEEDMEDIA,
(UINT)&SourceMedia,
Param2
);
switch(h) {
case FILEOP_NEWPATH:
case FILEOP_DOIT:
rc = NO_ERROR;
break;
default:
rc = GetLastError();
break;
}
break;
default:
MYASSERT(0);
rc = FALSE;
}
return(rc);
}
DWORD
pSetupCopySingleQueuedFile(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
OUT PTSTR NewSourcePath,
IN BOOL IsMsgHandlerNativeCharWidth
)
{
PTSTR FullTargetName;
FILEPATHS FilePaths;
UINT u;
BOOL InUse;
TCHAR source[MAX_PATH],PathBuffer[MAX_PATH];
DWORD rc;
BOOL b;
NewSourcePath[0] = 0;
QueueNode->InternalFlags |= IQF_PROCESSED;
//
// Form the full target path of the file.
//
FullTargetName = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetName) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
lstrcpyn(source,FullSourceName,MAX_PATH);
do {
//
// Form the full source name.
//
FilePaths.Source = FullSourceName;
FilePaths.Target = FullTargetName;
FilePaths.Win32Error = NO_ERROR;
//
// Notify the callback that the copy is starting.
//
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTCOPY,
(UINT)&FilePaths,
FILEOP_COPY
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
break;
}
if(u == FILEOP_DOIT) {
//
// Attempt the copy.
//
b = SetupInstallFileEx(
NULL, // no inf handle
NULL, // no inf context
FullSourceName,
NULL, // source path root is part of FullSourcePath
FullTargetName,
QueueNode->StyleFlags | SP_COPY_SOURCE_ABSOLUTE,
MsgHandler,
Context,
&InUse
);
if(b || ((rc = GetLastError()) == NO_ERROR)) {
//
// File was copied or not copied, but it if was not copied
// the callback funtcion was already notified about why
// (version check failed, etc).
//
if(InUse) {
QueueNode->InternalFlags |= (QueueNode->StyleFlags & SP_COPY_IN_USE_NEEDS_REBOOT)
? INUSE_INF_WANTS_REBOOT
: INUSE_IN_USE;
}
rc = NO_ERROR;
} else {
//
// File was not copied and a real error occurred.
// Notify the callback. Disallow skip if that is specified
// in the node's flags.
//
FilePaths.Win32Error = rc;
FilePaths.Flags = QueueNode->StyleFlags & (SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP | SP_COPY_NOBROWSE);
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_COPYERROR,
(UINT)&FilePaths,
(UINT)PathBuffer
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
break;
} else {
if(u == FILEOP_SKIP) {
//
// Force termination of processing for this file.
//
rc = NO_ERROR;
break;
} else {
if(u == FILEOP_NEWPATH) {
//
// Note that rc is already set to something other than
// NO_ERROR or we wouldn't be here.
//
lstrcpyn(NewSourcePath,PathBuffer,MAX_PATH);
lstrcpyn(source,NewSourcePath,MAX_PATH);
ConcatenatePaths(
source,
StringTableStringFromId(Queue->StringTable,QueueNode->SourceFilename),
MAX_PATH,
NULL
);
}
//
// Else we don't have a new path.
// Just keep using the one we had.
//
}
}
}
} else {
//
// skip file
//
rc = NO_ERROR;
}
} while(rc != NO_ERROR);
//
// Notify the callback that the copy is done.
//
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDCOPY,
(UINT)&FilePaths,
0
);
MyFree(FullTargetName);
return(rc);
}
DWORD
pSetupCopySingleQueuedFileCabCase(
IN PSP_FILE_QUEUE Queue,
IN PSP_FILE_QUEUE_NODE QueueNode,
IN PCTSTR CabinetName,
IN PCTSTR FullSourceName,
IN PVOID MsgHandler,
IN PVOID Context,
IN BOOL IsMsgHandlerNativeCharWidth
)
{
PTSTR FullTargetName;
FILEPATHS FilePaths;
UINT u;
BOOL InUse;
TCHAR PathBuffer[MAX_PATH];
DWORD rc;
BOOL b;
//
// Form the full target path of the file.
//
FullTargetName = pSetupFormFullPath(
Queue->StringTable,
QueueNode->TargetDirectory,
QueueNode->TargetFilename,
-1
);
if(!FullTargetName) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// We use the cabinet name as the source name so the display looks right
// to the user. Otherwise he sees the name of some temp file in the
// source field.
//
FilePaths.Source = CabinetName;
FilePaths.Target = FullTargetName;
FilePaths.Win32Error = NO_ERROR;
do {
//
// Notify the callback that the copy is starting.
//
u = pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_STARTCOPY,
(UINT)&FilePaths,
FILEOP_COPY
);
if(u == FILEOP_ABORT) {
rc = GetLastError();
break;
}
if(u == FILEOP_DOIT) {
//
// Attempt the copy.
//
b = SetupInstallFileEx(
NULL, // no inf handle
NULL, // no inf context
FullSourceName,
NULL, // source path root is part of FullSourcePath
FullTargetName,
QueueNode->StyleFlags | SP_COPY_SOURCE_ABSOLUTE,
MsgHandler,
Context,
&InUse
);
if(b || ((rc = GetLastError()) == NO_ERROR)) {
//
// File was copied or not copied, but it if was not copied
// the callback funtcion was already notified about why
// (version check failed, etc).
//
if(InUse) {
QueueNode->InternalFlags |= (QueueNode->StyleFlags & SP_COPY_IN_USE_NEEDS_REBOOT)
? INUSE_INF_WANTS_REBOOT
: INUSE_IN_USE;
}
rc = NO_ERROR;
} else {
//
// File was not copied and a real error occurred.
// Break out and return the error.
//
break;
}
} else {
//
// skip file
//
rc = NO_ERROR;
}
} while(rc != NO_ERROR);
//
// Notify the callback that the copy is done.
//
FilePaths.Win32Error = rc;
pSetupCallMsgHandler(
MsgHandler,
IsMsgHandlerNativeCharWidth,
Context,
SPFILENOTIFY_ENDCOPY,
(UINT)&FilePaths,
0
);
MyFree(FullTargetName);
return(rc);
}
PTSTR
pSetupFormFullPath(
IN PVOID StringTable,
IN LONG PathPart1,
IN LONG PathPart2, OPTIONAL
IN LONG PathPart3 OPTIONAL
)
/*++
Routine Description:
Form a full path based on components whose strings are in a string
table.
Arguments:
StringTable - supplies handle to string table.
PathPart1 - Supplies first part of path
PathPart2 - if specified, supplies second part of path
PathPart3 - if specified, supplies third part of path
Return Value:
Pointer to buffer containing full path. Caller can free with MyFree().
NULL if out of memory.
--*/
{
UINT RequiredSize;
PCTSTR p1,p2,p3;
TCHAR Buffer[MAX_PATH];
p1 = StringTableStringFromId(StringTable,PathPart1);
p2 = (PathPart2 == -1) ? NULL : StringTableStringFromId(StringTable,PathPart2);
p3 = (PathPart3 == -1) ? NULL : StringTableStringFromId(StringTable,PathPart3);
lstrcpy(Buffer,p1);
if(!p2 || ConcatenatePaths(Buffer,p2,MAX_PATH,NULL)) {
if(p3) {
ConcatenatePaths(Buffer,p3,MAX_PATH,NULL);
}
}
return(DuplicateString(Buffer));
}