2020-09-30 17:12:32 +02:00

2963 lines
86 KiB
C

/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
backup.c
Abstract:
Routines to control backup during install process
And restore of an old install process
Author:
Jamie Hunter (jamiehun) 13-Jan-1997
--*/
#include "precomp.h"
#pragma hdrstop
DWORD
pSetupQueueBackupCopy(
IN HSPFILEQ QueueHandle,
IN LONG TargetRootPath,
IN LONG TargetSubDir, OPTIONAL
IN LONG TargetFilename,
IN LONG BackupRootPath,
IN LONG BackupSubDir, OPTIONAL
IN LONG BackupFilename
)
/*++
Routine Description:
Place a backup copy operation on a setup file queue.
Target is to be backed up at Backup location
Arguments:
QueueHandle - supplies a handle to a setup file queue, as returned
by SetupOpenFileQueue.
TargetRootPath - Supplies the source directory, eg C:\WINNT\
TargetSubDir - Supplies the optional sub-directory (eg WINNT if RootPath = c:\ )
TargetFilename - supplies the filename part of the file to be copied.
BackupRootPath - supplies the directory where the file is to be copied.
BackupSubDir - supplies the optional sub-directory
BackupFilename - supplies the name of the target file.
Return Value:
same value as GetLastError() indicating error, or NO_ERROR
--*/
{
PSP_FILE_QUEUE Queue;
PSP_FILE_QUEUE_NODE QueueNode,TempNode;
int Size;
DWORD Err;
PVOID StringTable;
PTSTR FullRootName;
Queue = (PSP_FILE_QUEUE)QueueHandle;
Err = NO_ERROR;
try {
StringTable = Queue->StringTable; // used for strings in source queue
} except (EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// Allocate a queue structure.
QueueNode = MyMalloc(sizeof(SP_FILE_QUEUE_NODE));
if (!QueueNode) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
// Operation is backup.
QueueNode->Operation = FILEOP_BACKUP;
QueueNode->InternalFlags = 0;
QueueNode->SourceRootPath = BackupRootPath;
QueueNode->SourcePath = BackupSubDir;
QueueNode->SourceFilename = BackupFilename;
// if target has a sub-dir, we have to combine root and subdir into one string
if (TargetSubDir != -1) {
FullRootName = pSetupFormFullPath(
StringTable,
TargetRootPath,
TargetSubDir,
-1);
if (!FullRootName) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
TargetRootPath = StringTableAddString(StringTable,
FullRootName,
STRTAB_CASE_SENSITIVE
);
MyFree(FullRootName);
if (TargetRootPath == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
// now combined into TargetRootPath
TargetSubDir = -1;
}
QueueNode->TargetDirectory = TargetRootPath;
QueueNode->TargetFilename = TargetFilename;
QueueNode->Next = NULL;
// Link the node onto the end of the backup queue
if (Queue->BackupQueue) {
for (TempNode = Queue->BackupQueue; TempNode->Next; TempNode=TempNode->Next) /* blank */ ;
TempNode->Next = QueueNode;
} else {
Queue->BackupQueue = QueueNode;
}
Queue->BackupNodeCount++;
Err = NO_ERROR;
goto clean0;
clean1:
MyFree(QueueNode);
clean0:
SetLastError(Err);
return Err;
}
BOOL
pSetupGetFullBackupPath(
OUT PTSTR FullPath,
IN PCTSTR Path, OPTIONAL
IN UINT TargetBufferSize,
OUT PUINT RequiredSize OPTIONAL
)
/*++
Routine Description:
This routine takes a potentially relative path
and concatinates it to the base path
Arguments:
FullPath - Destination for full path
Path - Relative source path to backup directory if specified.
If NULL, generates a temporary path
TargetBufferSize - Size of buffer (characters)
RequiredSize - Filled in with size required to contain full path
Return Value:
If the function succeeds, return TRUE
If there was an error, return FALSE
--*/
{
UINT PathLen;
LPCTSTR Base = WindowsBackupDirectory;
if(!Path) {
// temporary location
Path = SP_BACKUP_OLDFILES;
Base = WindowsDirectory;
}
// Backup directory is stored in "WindowsBackupDirectory" for permanent backups
// and WindowsDirectory\SP_BACKUP_OLDFILES for temporary backups
PathLen = lstrlen(Base);
if ( FullPath == NULL || TargetBufferSize <= PathLen ) {
// just calculate required path len
FullPath = (PTSTR) Base;
TargetBufferSize = 0;
} else {
// calculate and copy
lstrcpy(FullPath, Base);
}
return ConcatenatePaths(FullPath, Path, TargetBufferSize, RequiredSize);
}
DWORD
pSetupBackupCopyString(
IN PVOID DestStringTable,
OUT PLONG DestStringID,
IN PVOID SrcStringTable,
IN LONG SrcStringID
)
/*++
Routine Description:
Gets a string from source string table, adds it to destination string table with new ID.
Arguments:
DestStringTable - Where string has to go
DestStringID - pointer, set to string ID in respect to DestStringTable
SrcStringTable - Where string is coming from
StringID - string ID in respect to SrcStringTable
Return Value:
Returns error code (LastError is also set)
If the function succeeds, returns NO_ERROR
--*/
{
DWORD Err = NO_ERROR;
LONG DestID;
PTSTR String;
if (DestStringID == NULL) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if (SrcStringID == -1) {
// "not supplied"
DestID = -1;
} else {
// actually need to copy
String = StringTableStringFromId( SrcStringTable, SrcStringID );
if (String == NULL) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
DestID = StringTableAddString( DestStringTable, String, STRTAB_CASE_SENSITIVE );
if (DestID == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
*DestStringID = DestID;
}
Err = NO_ERROR;
clean0:
SetLastError(Err);
return Err;
}
DWORD
pSetupBackupGetTargetByPath(
IN HSPFILEQ QueueHandle,
IN PVOID PathStringTable, OPTIONAL
IN PCTSTR TargetPath, OPTIONAL
IN LONG TargetRoot,
IN LONG TargetSubDir, OPTIONAL
IN LONG TargetFilename,
OUT PLONG TableID, OPTIONAL
OUT PSP_TARGET_ENT TargetInfo
)
/*++
Routine Description:
Given a pathname, obtains/creates target info
Arguments:
QueueHandle - Queue we're looking at
PathStringTable - String table used for the Target Root/SubDir/Filename strings, NULL if same as QueueHandle's
TargetPath - if given, is the full path, previously generated
TargetRoot - root portion, eg c:\winnt
TargetSubDir - optional sub-directory portion, -1 if not provided
TargetFilename - filename , eg readme.txt
TableID - filled with ID for future use in pSetupBackupGetTargetByID or pSetupBackupSetTargetByID
TargetInfo - Filled with information about target
Return Value:
Returns error code (LastError is also set)
If the function succeeds, returns NO_ERROR
--*/
{
LONG PathID;
TCHAR PathBuffer[MAX_PATH];
PTSTR TmpPtr;
PVOID LookupTable = NULL;
PVOID QueueStringTable = NULL;
PTSTR FullTargetPath = NULL;
DWORD Err = NO_ERROR;
PSP_FILE_QUEUE Queue;
DWORD RequiredSize;
Queue = (PSP_FILE_QUEUE)QueueHandle;
try {
LookupTable = Queue->TargetLookupTable; // used for path lookup in source queue
QueueStringTable = Queue->StringTable; // used for strings in source queue
} except (EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if (PathStringTable == NULL) {
// default string table is that of queue's
PathStringTable = QueueStringTable;
}
if (TargetPath == NULL) {
// obtain the complete target path and filename (Duplicated String)
FullTargetPath = pSetupFormFullPath(
PathStringTable,
TargetRoot,
TargetSubDir,
TargetFilename);
if (!FullTargetPath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
TargetPath = FullTargetPath;
}
// normalize path
RequiredSize = GetFullPathName(TargetPath,
SIZECHARS(PathBuffer),
PathBuffer,
&TmpPtr
);
// This call should always succeed.
MYASSERT((RequiredSize > 0) &&
(RequiredSize < SIZECHARS(PathBuffer)) // RequiredSize doesn't include terminating NULL char
);
// Even though we asserted that this should not be the case above,
// we should handle failure in case asserts are turned off.
if(!RequiredSize) {
Err = GetLastError();
goto clean0;
} else if(RequiredSize >= SIZECHARS(PathBuffer)) {
Err = ERROR_BUFFER_OVERFLOW;
goto clean0;
}
PathID = StringTableLookUpStringEx(LookupTable, PathBuffer, 0, TargetInfo, sizeof(SP_TARGET_ENT));
if (PathID == -1) {
ZeroMemory(TargetInfo, sizeof(SP_TARGET_ENT));
if (PathStringTable != QueueStringTable) {
// need to add entries to Queue's string table if we're using another
Err = pSetupBackupCopyString(QueueStringTable, &TargetRoot, PathStringTable, TargetRoot);
if (Err != NO_ERROR) {
goto clean0;
}
Err = pSetupBackupCopyString(QueueStringTable, &TargetSubDir, PathStringTable, TargetSubDir);
if (Err != NO_ERROR) {
goto clean0;
}
Err = pSetupBackupCopyString(QueueStringTable, &TargetFilename, PathStringTable, TargetFilename);
if (Err != NO_ERROR) {
goto clean0;
}
PathStringTable = QueueStringTable;
}
TargetInfo->TargetRoot = TargetRoot;
TargetInfo->TargetSubDir = TargetSubDir;
TargetInfo->TargetFilename = TargetFilename;
TargetInfo->BackupRoot = -1;
TargetInfo->BackupSubDir = -1;
TargetInfo->BackupFilename = -1;
TargetInfo->NewTargetFilename = -1;
TargetInfo->InternalFlags = 0;
PathID = StringTableAddStringEx(LookupTable, PathBuffer, 0, TargetInfo, sizeof(SP_TARGET_ENT));
if (PathID == -1)
{
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
if (TableID != NULL) {
*TableID = PathID;
}
Err = NO_ERROR;
clean0:
if (FullTargetPath != NULL) {
MyFree(FullTargetPath);
}
SetLastError(Err);
return Err;
}
DWORD
pSetupBackupGetTargetByID(
IN HSPFILEQ QueueHandle,
IN LONG TableID,
OUT PSP_TARGET_ENT TargetInfo
)
/*++
Routine Description:
Given an entry in the LookupTable, gets info
Arguments:
QueueHandle - Queue we're looking at
TableID - ID relating to string entry we've found (via pSetupBackupGetTargetByPath)
TargetInfo - Filled with information about target
Return Value:
Returns error code (LastError is also set)
If the function succeeds, returns NO_ERROR
--*/
{
PVOID LookupTable = NULL;
DWORD Err = NO_ERROR;
PSP_FILE_QUEUE Queue;
Queue = (PSP_FILE_QUEUE)QueueHandle;
try {
LookupTable = Queue->TargetLookupTable; // used for strings in source queue
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if (StringTableGetExtraData(LookupTable, TableID, TargetInfo, sizeof(SP_TARGET_ENT)) == FALSE) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
Err = NO_ERROR;
clean0:
SetLastError(Err);
return Err;
}
DWORD
pSetupBackupSetTargetByID(
IN HSPFILEQ QueueHandle,
IN LONG TableID,
IN PSP_TARGET_ENT TargetInfo
)
/*++
Routine Description:
Given an entry in the LookupTable, sets info
Arguments:
QueueHandle - Queue we're looking at
TableID - ID relating to string entry we've found (via pSetupBackupGetTargetByPath)
TargetInfo - Filled with information about target
Return Value:
Returns error code (LastError is also set)
If the function succeeds, returns NO_ERROR
--*/
{
PVOID LookupTable = NULL;
DWORD Err = NO_ERROR;
PSP_FILE_QUEUE Queue;
Queue = (PSP_FILE_QUEUE)QueueHandle;
try {
LookupTable = Queue->TargetLookupTable; // used for strings in source queue
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if ( StringTableSetExtraData(LookupTable, TableID, TargetInfo, sizeof(SP_TARGET_ENT)) == FALSE) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
Err = NO_ERROR;
clean0:
SetLastError(Err);
return Err;
}
DWORD
pSetupBackupAppendFiles(
IN HSPFILEQ TargetQueueHandle,
IN PCTSTR BackupSubDir,
IN DWORD BackupFlags,
IN HSPFILEQ SourceQueueHandle OPTIONAL
)
/*++
Routine Description:
This routine will take a list of files from SourceQueueHandle Copy sub-queue's
These files will appear in the Target Queue's target cache
And may be placed into the Target Backup Queue
Typically the copy queue is entries of..
<oldsrc-root>\<oldsrc-sub>\<oldsrc-name> copied to
<olddest-path>\<olddest-name>
Arguments:
TargetQueueHandle - Where Backups are queued to
BackupSubDir - Directory to backup to, relative to backup root
BackupFlags - How backup should occur
SourceQueueHandle - Handle that has a series of copy operations (backup hint)
created, say, by pretending to do the re-install
If not specified, only flags are passed
Return Value:
Returns error code (LastError is also set)
If the function succeeds, returns NO_ERROR
--*/
{
TCHAR BackupPath[MAX_PATH];
PSP_FILE_QUEUE SourceQueue = NULL;
PSP_FILE_QUEUE TargetQueue = NULL;
PSP_FILE_QUEUE_NODE QueueNode = NULL;
PSOURCE_MEDIA_INFO SourceMediaInfo = NULL;
BOOL b = TRUE;
PVOID SourceStringTable = NULL;
PVOID TargetStringTable = NULL;
LONG BackupRootID = -1;
DWORD Err = NO_ERROR;
LONG PathID = -1;
SP_TARGET_ENT TargetInfo;
SourceQueue = (PSP_FILE_QUEUE)SourceQueueHandle; // optional
TargetQueue = (PSP_FILE_QUEUE)TargetQueueHandle;
b=TRUE; // set if we can skip this routine
try {
TargetStringTable = TargetQueue->StringTable; // used for strings in target queue
if (SourceQueue == NULL) {
b = TRUE; // nothing to do
} else {
SourceStringTable = SourceQueue->StringTable; // used for strings in source queue
b = (!SourceQueue->CopyNodeCount);
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// these are backup flags to be passed into the queue
if (BackupFlags & SP_BKFLG_CALLBACK) {
TargetQueue->Flags |= FQF_BACKUP_AWARE;
}
if (b) {
// nothing to do
goto clean0;
}
// get full directory path of backup - this appears as the "dest" for any backup entries
if ( BackupSubDir == NULL ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if ( pSetupGetFullBackupPath(BackupPath, BackupSubDir, MAX_PATH,NULL) == FALSE ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// Target will often use this, so we create the ID now instead of later
BackupRootID = StringTableAddString(TargetStringTable,
BackupPath,
STRTAB_CASE_SENSITIVE);
if (BackupRootID == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
// CopyQueue is split over a number of media's
// we're not (currently) bothered about media
// iterate through all the copy sub-queue's
// and (1) add them to the target lookup table
// (2) if wanted, add them into the backup queue
for (SourceMediaInfo=SourceQueue->SourceMediaList; SourceMediaInfo!=NULL ; SourceMediaInfo=SourceMediaInfo->Next) {
if (!SourceMediaInfo->CopyNodeCount) {
continue;
}
MYASSERT(SourceMediaInfo->CopyQueue);
for (QueueNode = SourceMediaInfo->CopyQueue; QueueNode!=NULL; QueueNode = QueueNode->Next) {
// for each "Copy"
// we want information about the destination path
Err = pSetupBackupGetTargetByPath(TargetQueueHandle,
SourceStringTable,
NULL, // precalculated string
QueueNode->TargetDirectory,
-1,
QueueNode->TargetFilename,
&PathID,
&TargetInfo);
if (Err != NO_ERROR) {
goto clean0;
}
// we now have a created (or obtained) TargetInfo, and PathID
// provide a source name for backup
TargetInfo.BackupRoot = BackupRootID;
Err = pSetupBackupCopyString(TargetStringTable, &TargetInfo.BackupSubDir, SourceStringTable, QueueNode->SourcePath);
if (Err != NO_ERROR) {
goto clean0;
}
Err = pSetupBackupCopyString(TargetStringTable, &TargetInfo.BackupFilename, SourceStringTable, QueueNode->SourceFilename);
if (Err != NO_ERROR) {
goto clean0;
}
if ((BackupFlags & SP_BKFLG_LATEBACKUP) == FALSE) {
// we need to add this item to the backup queue
Err = pSetupQueueBackupCopy(TargetQueueHandle,
// source
TargetInfo.TargetRoot,
TargetInfo.TargetSubDir,
TargetInfo.TargetFilename,
TargetInfo.BackupRoot,
TargetInfo.BackupSubDir,
TargetInfo.BackupFilename);
if (Err != NO_ERROR) {
goto clean0;
}
// flag that we've added it to the pre-copy backup sub-queue
TargetInfo.InternalFlags |= SP_TEFLG_BACKUPQUEUE;
}
// any backups should go to this specified directory
TargetInfo.InternalFlags |= SP_TEFLG_ORIGNAME;
Err = pSetupBackupSetTargetByID(TargetQueueHandle, PathID, &TargetInfo);
if (Err != NO_ERROR) {
goto clean0;
}
}
}
Err = NO_ERROR;
clean0:
SetLastError(Err);
return (Err);
}
DWORD
pSetupBackupFile(
IN HSPFILEQ QueueHandle,
IN PCTSTR TargetPath,
IN PCTSTR BackupPath,
IN LONG TargetID, OPTIONAL
IN LONG TargetRootPath,
IN LONG TargetSubDir,
IN LONG TargetFilename,
IN LONG BackupRootPath,
IN LONG BackupSubDir,
IN LONG BackupFilename,
OUT BOOL *InUseFlag
)
/*++
Routine Description:
If BackupFilename not supplied, it is obtained/created
Will either
1) copy a file to the backup directory, or
2) queue that a file is backed up on reboot
The latter occurs if the file was locked.
Arguments:
HSPFILEQ - QueueHandle - specifies Queue
LONG - TargetID - if specified (not -1), use for target
LONG - TargetRootPath - used if TargetID == -1
LONG - TargetSubDir - used if TargetID == -1
LONG - TargetFilename - used if TargetID == -1
LONG - BackupRootPath - alternate root (valid if BackupFilename != -1)
LONG - BackupSubDir - alternate directory (valid if BackupFilename != -1)
LONG - BackupFilename - alternate filename
Return Value:
If the function succeeds, return value is TRUE
If the function fails, return value is FALSE
--*/
{
PSP_FILE_QUEUE Queue = NULL;
PVOID StringTable = NULL;
PVOID LookupTable = NULL;
DWORD Err = NO_ERROR;
SP_TARGET_ENT TargetInfo;
PTSTR FullTargetPath = NULL;
PTSTR FullBackupPath = NULL;
BOOL InUse = FALSE;
PTSTR TempNamePtr = NULL, DirTruncPos;
TCHAR TempPath[MAX_PATH];
TCHAR TempFilename[MAX_PATH];
TCHAR ParsedPath[MAX_PATH];
UINT OldMode;
LONG NewTargetFilename;
BOOL DoRename = FALSE;
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
Queue = (PSP_FILE_QUEUE)QueueHandle;
try {
StringTable = Queue->StringTable; // used for strings in source queue
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
if(TargetPath == NULL && TargetID == -1) {
if (TargetRootPath == -1 || TargetFilename == -1) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// complete target path
FullTargetPath = pSetupFormFullPath(
StringTable,
TargetRootPath,
TargetSubDir,
TargetFilename
);
if (!FullTargetPath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
TargetPath = FullTargetPath;
}
if (TargetID == -1) {
Err = pSetupBackupGetTargetByPath(QueueHandle,
NULL, // string table
TargetPath, // precalculated string
TargetRootPath,
TargetSubDir,
TargetFilename,
&TargetID,
&TargetInfo);
} else {
Err = pSetupBackupGetTargetByID(QueueHandle,
TargetID,
&TargetInfo);
}
if(Err != NO_ERROR) {
goto clean0;
}
// if we're not interested in backing up (global flag) we can skip
// but it's only safe to do so if we'd copy & then later throw the copy away on success
if ((TargetInfo.InternalFlags & SP_TEFLG_RENAMEEXISTING) == 0 && (GlobalSetupFlags & PSPGF_NO_BACKUP)!=0) {
goto clean0;
}
// Figure out whether we've been asked to rename the existing file to a
// temp name in the same directory, but haven't yet done so.
DoRename = ((TargetInfo.InternalFlags & (SP_TEFLG_RENAMEEXISTING | SP_TEFLG_MOVED)) == SP_TEFLG_RENAMEEXISTING);
if(BackupFilename == -1) {
// non-specific backup
if((TargetInfo.InternalFlags & SP_TEFLG_SAVED) && !DoRename) {
// Already backed up, and we don't need to rename the existing file.
// Nothing to do.
Err = NO_ERROR;
goto clean0;
}
if(TargetInfo.InternalFlags & SP_TEFLG_INUSE) {
// Previously marked as INUSE, not allowed to change it. If we
// were asked to rename the existing file, then we need to return
// failure, otherwise, we can report success.
InUse = TRUE;
Err = DoRename ? ERROR_SHARING_VIOLATION : NO_ERROR;
goto clean0;
}
if(TargetInfo.InternalFlags & SP_TEFLG_ORIGNAME) {
// original name given, use that
BackupRootPath = TargetInfo.BackupRoot;
BackupSubDir = TargetInfo.BackupSubDir;
BackupFilename = TargetInfo.BackupFilename;
}
} else {
// We should never be called if the file has already been
// saved.
MYASSERT(!(TargetInfo.InternalFlags & SP_TEFLG_SAVED));
// Even if the above assert fires, we should still deal with
// the case where this occurs. Also, we should deal with the
// case where a backup was previously attempted but failed due
// to the file being in-use.
if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
Err = ERROR_INVALID_DATA;
goto clean0;
} else if(TargetInfo.InternalFlags & SP_TEFLG_INUSE) {
// force the issue of InUse
InUse = TRUE;
Err = ERROR_SHARING_VIOLATION;
goto clean0;
}
TargetInfo.BackupRoot = BackupRootPath;
TargetInfo.BackupSubDir = BackupSubDir;
TargetInfo.BackupFilename = BackupFilename;
TargetInfo.InternalFlags |= SP_TEFLG_ORIGNAME;
TargetInfo.InternalFlags &= ~SP_TEFLG_TEMPNAME;
}
if(TargetPath == NULL) {
// must have looked up using TargetID, use TargetInfo to generate TargetPath
// complete target path
FullTargetPath = pSetupFormFullPath(StringTable,
TargetInfo.TargetRoot,
TargetInfo.TargetSubDir,
TargetInfo.TargetFilename
);
if(!FullTargetPath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
TargetPath = FullTargetPath;
}
if(DoRename) {
// We'd better not already have a temp filename stored in our TargetInfo.
MYASSERT(TargetInfo.NewTargetFilename == -1);
// First, strip the filename off the path.
_tcscpy(TempPath, TargetPath);
TempNamePtr = (PTSTR)MyGetFileTitle(TempPath);
*TempNamePtr = TEXT('\0');
// Now get a temp filename within that directory...
if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
Err = GetLastError();
goto clean0;
}
// ...and store this path's string ID in our TargetInfo
NewTargetFilename = StringTableAddString(StringTable,
TempFilename,
STRTAB_CASE_SENSITIVE
);
if(NewTargetFilename == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
if(!(TargetInfo.InternalFlags & (SP_TEFLG_ORIGNAME | SP_TEFLG_TEMPNAME))) {
// If we don't yet have a name to use in backing up this file, then
// generate one now. If we are doing a rename, we can use that name.
if(DoRename) {
// Make sure that all flags agree on the fact that we need to back
// up this file.
MYASSERT(!(TargetInfo.InternalFlags & SP_TEFLG_SAVED));
// Temp filename was stored in TempFilename buffer above.
TempNamePtr = (PTSTR)MyGetFileTitle(TempFilename);
BackupFilename = StringTableAddString(StringTable, TempNamePtr, STRTAB_CASE_SENSITIVE);
if(BackupFilename == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
DirTruncPos = CharPrev(TempFilename, TempNamePtr);
// (We know MyGetFileTitle will never return a pointer to a path
// separator character, so the following check is valid.)
if(*DirTruncPos == TEXT('\\')) {
// If this is in a root directory (e.g., "A:\"), then we don't want to strip off
// the trailing backslash.
if(((DirTruncPos - TempFilename) != 2) || (*CharNext(TempFilename) != TEXT(':'))) {
TempNamePtr = DirTruncPos;
}
}
lstrcpyn(TempPath, TempFilename, (int)(TempNamePtr - TempFilename) + 1);
BackupRootPath = StringTableAddString(StringTable, TempPath, STRTAB_CASE_SENSITIVE);
if(BackupRootPath == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
} else {
// specify "NULL" as the sub-directory, since all we want is a temporary location
if(pSetupGetFullBackupPath(TempPath, NULL, MAX_PATH,NULL) == FALSE ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
_tcscpy(TempFilename,TempPath);
// Note: In the code below, we employ a "trick" to get the
// pSetupMakeSurePathExists API to make sure that a directory
// exists. Since we don't yet know the filename (we need to call
// GetTempFileName against an existing directory to find that out),
// we just use a dummy placeholder filename ("OLD") so that it can
// be discarded by the pSetupMakeSurePathExists API.
if(ConcatenatePaths(TempFilename, TEXT("OLD"), MAX_PATH, NULL) == FALSE ) {
Err = GetLastError();
goto clean0;
}
pSetupMakeSurePathExists(TempFilename);
if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
Err = GetLastError();
goto clean0;
}
TempNamePtr = TempFilename + _tcslen(TempPath) + 1 /* 1 to skip past \ */;
BackupRootPath = StringTableAddString( StringTable, TempPath, STRTAB_CASE_SENSITIVE );
if(BackupRootPath == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
BackupFilename = StringTableAddString( StringTable, TempNamePtr, STRTAB_CASE_SENSITIVE );
if(BackupFilename == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
BackupPath = TempFilename;
TargetInfo.BackupRoot = BackupRootPath;
TargetInfo.BackupSubDir = BackupSubDir = -1;
TargetInfo.BackupFilename = BackupFilename;
TargetInfo.InternalFlags |= SP_TEFLG_TEMPNAME;
}
if(BackupPath == NULL) {
// make a complete path from this source
FullBackupPath = pSetupFormFullPath(StringTable,
BackupRootPath,
BackupSubDir,
BackupFilename
);
if (!FullBackupPath) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
BackupPath = FullBackupPath;
}
// If we need to make a copy of the existing file, do so now.
if(!DoRename || (TargetInfo.InternalFlags & SP_TEFLG_ORIGNAME)) {
SetFileAttributes(BackupPath, FILE_ATTRIBUTE_NORMAL);
pSetupMakeSurePathExists(BackupPath);
Err = CopyFile(TargetPath, BackupPath, FALSE) ? NO_ERROR : GetLastError();
if(Err == NO_ERROR) {
TargetInfo.InternalFlags |= SP_TEFLG_SAVED;
} else {
// Delete placeholder file created by GetTempFileName.
DeleteFile(BackupPath);
if(Err == ERROR_SHARING_VIOLATION) {
// Unless we were also going to attempt a rename, don't
// consider sharing violations to be errors.
InUse = TRUE;
TargetInfo.InternalFlags |= SP_TEFLG_INUSE;
if(!DoRename) {
Err = NO_ERROR;
}
}
}
}
// OK, now rename the existing file, if necessary.
if(DoRename && (Err == NO_ERROR)) {
if(DoMove(TargetPath, TempFilename)) {
TargetInfo.InternalFlags |= SP_TEFLG_MOVED;
TargetInfo.NewTargetFilename = NewTargetFilename;
// Post a delayed deletion for this temp filename so it'll get
// cleaned up after reboot.
if(!PostDelayedMove(Queue, TempFilename, NULL, -1, FALSE)) {
// Don't abort just because we couldn't schedule a delayed
// delete. If this fails, the only bad thing that will happen
// is a turd will get left over after reboot.
// We should log an event about this, however.
Err = GetLastError();
WriteLogEntry(Queue->LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_RENAME_EXISTING_DELAYED_DELETE_FAILED,
NULL,
TargetPath,
TempFilename
);
WriteLogError(Queue->LogContext,
SETUP_LOG_WARNING,
Err
);
Err = NO_ERROR;
}
} else {
Err = GetLastError();
DeleteFile(TempFilename);
if(Err == ERROR_SHARING_VIOLATION) {
InUse = TRUE;
TargetInfo.InternalFlags |= SP_TEFLG_INUSE;
}
}
}
// update internal info (this call should never fail)
pSetupBackupSetTargetByID(QueueHandle,
TargetID,
&TargetInfo
);
clean0:
if(InUseFlag) {
*InUseFlag = InUse;
}
if (FullTargetPath != NULL) {
MyFree(FullTargetPath);
}
if (FullBackupPath != NULL) {
MyFree(FullBackupPath);
}
SetErrorMode(OldMode);
SetLastError(Err);
return Err;
}
BOOL pSetupRemoveBackupDirectory(IN PCTSTR BackupDir)
/*++
Routine Description:
Deletes all the backups out of a directory
Where BackupDir is the same format as in pSetupOpenBackup
Arguments:
BackupDir - Directory relative to the backup directory, or absolute
Return Value:
If the function succeeds, return value is TRUE
If the function fails, return value is FALSE
--*/
{
TCHAR Buffer[MAX_PATH];
// get full directory path
if ( BackupDir == NULL ) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if ( pSetupGetFullBackupPath(Buffer, BackupDir, MAX_PATH,NULL) == FALSE ) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
return FALSE;
}
DWORD
pSetupGetCurrentlyInstalledDriverNode(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Get driver node that relates to current INF file of device
Arguments:
DeviceInfoSet
DeviceInfoData
Return Value:
Error Status
--*/
{
HKEY hKey = NULL;
DWORD Err;
DWORD RegDataType, RegDataLength;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
SP_DRVINFO_DATA DriverInfoData;
// Retrieve the current device install parameters, in preparation for modifying them to
// target driver search at a particular INF.
ZeroMemory(&DeviceInstallParams, sizeof(DeviceInstallParams));
ZeroMemory(&DriverInfoData, sizeof(DriverInfoData));
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams)) {
return GetLastError();
}
// Open the device's driver key and retrieve the INF from which the device was installed.
hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ
);
if(hKey == INVALID_HANDLE_VALUE) {
return GetLastError();
}
RegDataLength = sizeof(DeviceInstallParams.DriverPath); // want in bytes, not chars
Err = RegQueryValueEx(hKey,
REGSTR_VAL_INFPATH,
NULL,
&RegDataType,
(PBYTE)DeviceInstallParams.DriverPath,
&RegDataLength
);
if((Err == ERROR_SUCCESS) && (RegDataType != REG_SZ)) {
Err = ERROR_INVALID_DATA;
}
if(Err != ERROR_SUCCESS) {
goto clean0;
}
// set the flag that indicates DriverPath represents a single INF to be searched (and
// not a directory path). Then store the parameters back to the device information element.
DeviceInstallParams.Flags |= DI_ENUMSINGLEINF;
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
if(!SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &DeviceInstallParams)) {
Err = GetLastError();
goto clean0;
}
// Now build a class driver list from this INF.
if(!SetupDiBuildDriverInfoList(DeviceInfoSet, DeviceInfoData, SPDIT_CLASSDRIVER)) {
Err = GetLastError();
goto clean0;
}
// OK, now select the driver node from that INF that was used to install this device.
// The three parameters that uniquely identify a driver node are INF Provider,
// Device Manufacturer, and Device Description. Retrieve these three pieces of information
// in preparation for selecting the proper driver node in the class list we just built.
// First, retrieve the INF Provider. This is stored in the device's driver key (which we
// already have open). The provider name is the only one of the three parameters that the
// INF may omit (in that case, the ProviderName value entry won't be present in the driver
// key).
RegDataLength = sizeof(DriverInfoData.ProviderName); // want in bytes, not chars
Err = RegQueryValueEx(hKey,
REGSTR_VAL_PROVIDER_NAME,
NULL,
&RegDataType,
(PBYTE)DriverInfoData.ProviderName,
&RegDataLength
);
if(Err == ERROR_SUCCESS) {
if(RegDataType != REG_SZ) {
Err = ERROR_INVALID_DATA;
goto clean0;
}
} else {
// Assume there is no provider specified. If it turns out that the registry query
// really failed for some other reason, we'll discover that soon enough when we attempt
// to select the driver node.
Err = ERROR_SUCCESS;
*DriverInfoData.ProviderName = TEXT('\0');
}
// Next, retrieve the manufacturer (stored in the Mfg device property).
if(!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_MFG,
NULL, // datatype is guaranteed to always be REG_SZ.
(PBYTE)DriverInfoData.MfgName,
sizeof(DriverInfoData.MfgName), // in bytes
NULL)) {
Err = GetLastError();
goto clean0;
}
// Finally, retrieve the device description (stored in the DeviceDesc device property).
if(!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_DEVICEDESC,
NULL, // datatype is guaranteed to always be REG_SZ.
(PBYTE)DriverInfoData.Description,
sizeof(DriverInfoData.Description), // in bytes
NULL)) {
Err = GetLastError();
goto clean0;
}
// Do the final initialization on the driver info data 'template' buffer used in doing the
// selection search.
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
DriverInfoData.DriverType = SPDIT_CLASSDRIVER;
DriverInfoData.Reserved = 0; // Search for the driver matching the specified criteria and
// select it if found.
if(!SetupDiSetSelectedDriver(DeviceInfoSet, DeviceInfoData, &DriverInfoData)) {
Err = GetLastError();
goto clean0;
}
// At this point, we've successfully selected the currently installed driver for the specified
// device information element. We're done!
Err = NO_ERROR;
clean0:
RegCloseKey(hKey);
SetLastError(Err);
return Err;
}
DWORD
pSetupGetBackupQueue(
IN PCTSTR DeviceID,
IN OUT HSPFILEQ FileQueue,
IN DWORD BackupFlags
)
/*++
Routine Description:
Creates a backup Queue for current device (DeviceID)
Also makes sure that the INF file is backed up
Arguments:
DeviceID String ID of device
FileQueue Backup queue is filled with files that need copying
BackupFlags Various flags
Return Value:
Error Status
--*/
{
// we want to obtain a copy/move list of device associated with DeviceID
PSP_FILE_QUEUE FileQ = (PSP_FILE_QUEUE)FileQueue;
HDEVINFO TempInfoSet = (HDEVINFO)INVALID_HANDLE_VALUE;
HSPFILEQ TempQueueHandle = (HSPFILEQ)INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA TempInfoData;
SP_DEVINSTALL_PARAMS TempParams;
TCHAR SubDir[MAX_DEVICE_ID_LEN + sizeof(SP_BACKUP_DRIVERFILES) + 16];
LONG Instance;
PDEVINFO_ELEM DevInfoElem = NULL;
PTSTR szInfFileName = NULL;
TCHAR BackupPath[MAX_PATH];
TCHAR OemOrigName[MAX_PATH];
TCHAR CatBackupPath[MAX_PATH];
TCHAR CatSourcePath[MAX_PATH];
DWORD Err;
PDEVICE_INFO_SET pDeviceInfoSet = NULL;
int c;
DWORD BackupInfID = -1;
PSP_INF_INFORMATION pInfInformation = NULL;
DWORD InfInformationSize;
SP_ORIGINAL_FILE_INFO InfOriginalFileInformation;
BOOL success;
CatBackupPath[0] = 0; // by default, don't bother with a catalog
CatSourcePath[0] = 0;
// pretend we're installing old INF
// this gives us a list of files we need
TempInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL);
if ( TempInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE ) {
Err = GetLastError();
goto clean0;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(TempInfoSet))) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// Open the driver info, related to DeviceID I was given
TempInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if ( SetupDiOpenDeviceInfo(TempInfoSet ,DeviceID, NULL, 0, &TempInfoData) == FALSE ) {
Err = GetLastError();
goto clean0;
}
// Get the currently-installed driver node selected for this element.
if ( pSetupGetCurrentlyInstalledDriverNode(TempInfoSet, &TempInfoData) != NO_ERROR ) {
Err = GetLastError();
goto clean0;
}
// Now queue all files to be copied by this driver node into our own file queue.
TempQueueHandle = SetupOpenFileQueue();
if ( TempQueueHandle == (HSPFILEQ)INVALID_HANDLE_VALUE ) {
// SetupOpenFileQueue modified to return error
Err = GetLastError();
goto clean0;
}
TempParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if ( !SetupDiGetDeviceInstallParams(TempInfoSet, &TempInfoData, &TempParams) ) {
Err = GetLastError();
goto clean0;
}
TempParams.FileQueue = TempQueueHandle;
TempParams.Flags |= DI_NOVCP;
if ( !SetupDiSetDeviceInstallParams(TempInfoSet, &TempInfoData, &TempParams) ) {
Err = GetLastError();
goto clean0;
}
if ( !SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, TempInfoSet, &TempInfoData) ) {
Err = GetLastError();
goto clean0;
}
// need a directory - replace "bad" characters with others
for(c=0; DeviceID[c]; c++)
{
if(DeviceID[c] == TEXT('\\') || DeviceID[c] == TEXT('/'))
{
SubDir[c] = TEXT('#');
}
else if(DeviceID[c] == TEXT('*'))
{
SubDir[c] = TEXT('~');
}
else
{
SubDir[c] = DeviceID[c];
}
}
// insert into this path an instance number and INF dir
Instance = SP_BACKUP_INSTANCE0;
wsprintf(SubDir+c, TEXT("\\%04d\\%s"), (LONG) Instance, (PCTSTR) SP_BACKUP_DRIVERFILES );
// get the path of the INF file, we will need to back it up
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
&TempInfoData,
NULL))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
if (szInfFileName == NULL) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
// we want to get the "real" name of the INF - we may have a precompiled inf
// BUGBUG (jamiehun) we could check the tail-name first for OEMxxx.INF
ZeroMemory(&InfOriginalFileInformation, sizeof(InfOriginalFileInformation));
// if nothing else, use same name as is in the INF directory
lstrcpy(OemOrigName,MyGetFileTitle(szInfFileName));
// create the path of the new inf we want to copy to - hopefully we've managed to preserve the original inf name
if ( pSetupGetFullBackupPath(BackupPath, SubDir, MAX_PATH,NULL) == FALSE ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
// but use the original name if available
InfInformationSize = 8192; // I'd rather have this too big and succeed first time, than read the INF twice
pInfInformation = (PSP_INF_INFORMATION)MyMalloc(InfInformationSize);
if (pInfInformation != NULL) {
success = SetupGetInfInformation(szInfFileName,INFINFO_INF_NAME_IS_ABSOLUTE,pInfInformation,InfInformationSize,&InfInformationSize);
if (!success && GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
PVOID newbuff = MyRealloc(pInfInformation,InfInformationSize);
if (!newbuff) {
MyFree(pInfInformation);
pInfInformation = NULL;
} else {
pInfInformation = (PSP_INF_INFORMATION)newbuff;
success = SetupGetInfInformation(szInfFileName,INFINFO_INF_NAME_IS_ABSOLUTE,pInfInformation,InfInformationSize,&InfInformationSize);
}
}
if (success) {
InfOriginalFileInformation.cbSize = sizeof(InfOriginalFileInformation);
if (SetupQueryInfOriginalFileInformation(pInfInformation,0,NULL,&InfOriginalFileInformation)) {
if (InfOriginalFileInformation.OriginalInfName[0]) {
// we have a "real" inf name
lstrcpy(OemOrigName,MyGetFileTitle(InfOriginalFileInformation.OriginalInfName));
} else {
MYASSERT(InfOriginalFileInformation.OriginalInfName[0]);
}
if (InfOriginalFileInformation.OriginalCatalogName[0]) {
TCHAR CurrentCatName[MAX_PATH];
// given that the file is ....\OEMx.INF the catalog is "OEMx.CAT"
// we key off OemOrigName (eg mydisk.inf )
// and we won't bother copying the catalog if we can't verify the inf
lstrcpy(CurrentCatName,MyGetFileTitle(szInfFileName));
lstrcpy(_tcsrchr(CurrentCatName, TEXT('.')), pszCatSuffix);
// we have a catalog name
// now consider making a copy of the cached catalog into our backup
// we get out CatProblem and szCatFileName
// if all seems ok, copy file from szCatFileName to backupdir\OriginalCatalogName
Err = VerifyFile(FileQ->LogContext,
CurrentCatName, // eg "OEMx.CAT"
NULL,0, // we're not verifying against another catalog image
OemOrigName, // eg "mydisk.inf"
szInfFileName, // eg "....\OEMx.INF"
NULL, // return: problem info
NULL, // return: problem file
TRUE, // assume catalog is self-verified
NULL, // alt platform info
CatSourcePath, // return: catalog file, full path
NULL // return: number of catalogs considered
);
if (Err == NO_ERROR && CatSourcePath[0]) {
// we have a catalog file of interest to copy
lstrcpy(CatBackupPath,BackupPath);
if (!ConcatenatePaths(CatBackupPath, InfOriginalFileInformation.OriginalCatalogName, MAX_PATH, NULL)) {
// non-fatal
CatSourcePath[0]=0;
CatBackupPath[0]=0;
}
}
}
}
}
if (pInfInformation != NULL) {
MyFree(pInfInformation);
pInfInformation = NULL;
}
}
if ( ConcatenatePaths(BackupPath, OemOrigName, MAX_PATH, NULL) == FALSE ) {
Err = ERROR_INVALID_HANDLE;
goto clean0;
}
SetFileAttributes(BackupPath,FILE_ATTRIBUTE_NORMAL);
pSetupMakeSurePathExists(BackupPath);
Err = CopyFile(szInfFileName, BackupPath ,FALSE) ? NO_ERROR : GetLastError();
if (Err != NO_ERROR) {
goto clean0;
}
if(CatSourcePath[0] && CatBackupPath[0]) {
// if we copied Inf file, try to copy catalog file
// if we don't succeed, don't consider this a fatal error
CopyFile(CatSourcePath, CatBackupPath ,FALSE);
}
// Add string indicating Backup INF location to file Queue
// for later retrieval
BackupInfID = StringTableAddString(FileQ->StringTable,
BackupPath,
STRTAB_CASE_SENSITIVE);
if (BackupInfID == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
// Also backup the PNF file
// WARNING: We reuse the szInfFileName and BackupPath variables at this point
// so if you add code that needs them then add it above.
if ((lstrlen(szInfFileName) > lstrlen(DISTR_INF_PNF_SUFFIX)) &&
(lstrlen(BackupPath) > lstrlen(DISTR_INF_PNF_SUFFIX))) {
// Just replace the szInfFileName and BackupPath .INF suffix with .PNF and call
// CopyFile.
lstrcpy(&szInfFileName[lstrlen(szInfFileName) - lstrlen(DISTR_INF_PNF_SUFFIX)],
DISTR_INF_PNF_SUFFIX);
lstrcpy(&BackupPath[lstrlen(BackupPath) - lstrlen(DISTR_INF_PNF_SUFFIX)],
DISTR_INF_PNF_SUFFIX);
CopyFile(szInfFileName, BackupPath, FALSE);
}
// update BackupInfID so that INF name can be queried later
FileQ->BackupInfID = BackupInfID;
// add items we may need to backup
// (ie the copy queue of TempQueueHandle is converted to a backup queue of FileQueue)
if ( pSetupBackupAppendFiles(FileQueue, SubDir, BackupFlags, TempQueueHandle) != NO_ERROR ) {
Err = GetLastError();
goto clean0;
}
Err = NO_ERROR;
clean0:
// delete temporary structures used
if (pDeviceInfoSet != NULL ) {
UnlockDeviceInfoSet(pDeviceInfoSet);
}
if ( TempInfoSet != (HDEVINFO)INVALID_HANDLE_VALUE ) {
SetupDiDestroyDeviceInfoList(TempInfoSet);
}
if ( TempQueueHandle != (HSPFILEQ)INVALID_HANDLE_VALUE ) {
SetupCloseFileQueue(TempQueueHandle);
}
SetLastError(Err);
return Err;
}
BOOL
PostDelayedMove(
IN PSP_FILE_QUEUE Queue,
IN PCTSTR CurrentName,
IN PCTSTR NewName, OPTIONAL
IN DWORD SecurityDesc,
IN BOOL TargetIsProtected
)
/*++
Routine Description:
Helper for DelayedMove
We don't do any delayed Moves until we know all else succeeded
Arguments:
Queue Queue that the move is applied to
CurrentName Name of file we want to move
NewName Name we want to move to
SecurityDesc Index in string table of Security Descriptor string or -1 if not present
TargetIsProtected Indicates whether target file is a protected system file
Return Value:
FALSE if error
--*/
{
PSP_DELAYMOVE_NODE DelayMoveNode;
LONG SourceFilename;
LONG TargetFilename;
DWORD Err;
if (CurrentName == NULL) {
SourceFilename = -1;
} else {
SourceFilename = StringTableAddString(Queue->StringTable,
(PTSTR)CurrentName,
STRTAB_CASE_SENSITIVE
);
if (SourceFilename == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
if (NewName == NULL) {
TargetFilename = -1;
} else {
TargetFilename = StringTableAddString(Queue->StringTable,
(PTSTR)NewName,
STRTAB_CASE_SENSITIVE
);
if (TargetFilename == -1) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
DelayMoveNode = MyMalloc(sizeof(SP_DELAYMOVE_NODE));
if (DelayMoveNode == NULL) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
DelayMoveNode->NextNode = NULL;
DelayMoveNode->SourceFilename = SourceFilename;
DelayMoveNode->TargetFilename = TargetFilename;
DelayMoveNode->SecurityDesc = SecurityDesc;
DelayMoveNode->TargetIsProtected = TargetIsProtected;
if (Queue->DelayMoveQueueTail == NULL) {
Queue->DelayMoveQueue = DelayMoveNode;
} else {
Queue->DelayMoveQueueTail->NextNode = DelayMoveNode;
}
Queue->DelayMoveQueueTail = DelayMoveNode;
Err = NO_ERROR;
clean0:
SetLastError(Err);
return (Err == NO_ERROR);
}
DWORD
DoAllDelayedMoves(
IN PSP_FILE_QUEUE Queue
)
/*++
Routine Description:
Execute the Delayed Moves previously posted
Arguments:
Queue Queue that has the list in
Return Value:
Error Status
--*/
{
PSP_DELAYMOVE_NODE DelayMoveNode;
PTSTR CurrentName;
PTSTR TargetName;
BOOL b = TRUE;
PSP_DELAYMOVE_NODE DoneQueue = NULL;
PSP_DELAYMOVE_NODE NextNode = NULL;
DWORD Err = NO_ERROR;
BOOL EnableProtectedRenames = FALSE;
for (DelayMoveNode = Queue->DelayMoveQueue ; DelayMoveNode ; DelayMoveNode = NextNode ) {
NextNode = DelayMoveNode->NextNode;
MYASSERT(DelayMoveNode->SourceFilename != -1);
CurrentName = StringTableStringFromId(Queue->StringTable, DelayMoveNode->SourceFilename);
MYASSERT(CurrentName);
if (DelayMoveNode->TargetFilename == -1) {
TargetName = NULL;
} else {
TargetName = StringTableStringFromId( Queue->StringTable, DelayMoveNode->TargetFilename );
MYASSERT(TargetName);
}
// Keep track of whether we've encountered any protected system files.
EnableProtectedRenames |= DelayMoveNode->TargetIsProtected;
#ifdef UNICODE
// If this is a move (instead of a delete), then set security (letting
// SCE know what the file's final name will be.
if((DelayMoveNode->SecurityDesc != -1) && TargetName) {
Err = pSetupCallSCE(ST_SCE_RENAME,
CurrentName,
Queue,
TargetName,
DelayMoveNode->SecurityDesc,
NULL
);
if(Err != NO_ERROR ){
// If we're on the first delay-move node, then we can abort.
// However, if we've already processed one or more nodes, then
// we can't abort--we must simply log an error indicating what
// happened and keep on going.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELAYED_MOVE_SCE_FAILED,
NULL,
CurrentName,
TargetName
);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
Err
);
if(DelayMoveNode == Queue->DelayMoveQueue) {
// Failure occurred on 1st node--we can abort.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_OPERATION_CANCELLED,
NULL
);
break;
} else {
// There's no turning back--log an error and keep on going.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_ERROR_IGNORED,
NULL
);
Err = NO_ERROR;
}
}
} else
#endif
{
Err = NO_ERROR;
}
// finally delay the move
if(!DelayedMove(CurrentName, TargetName)) {
Err = GetLastError();
// Same deal as above with SCE call--if we've already processed one
// or more delay-move nodes, we can't abort.
if(TargetName) {
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELAYED_MOVE_SCE_FAILED,
NULL,
CurrentName,
TargetName
);
} else {
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_DELAYED_DELETE_FAILED,
NULL,
CurrentName
);
}
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
Err
);
if(DelayMoveNode == Queue->DelayMoveQueue) {
// Failure occurred on 1st node--we can abort.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_OPERATION_CANCELLED,
NULL
);
break;
} else {
// There's no turning back--log an error and keep on going.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR,
MSG_LOG_ERROR_IGNORED,
NULL
);
Err = NO_ERROR;
}
}
// Move node to queue containing nodes that have already been processed
DelayMoveNode->NextNode = DoneQueue;
DoneQueue = DelayMoveNode;
}
// If we have any replacement of protected system files, then we need to
// inform session manager so that it allows the replacement to occur upon
// reboot.
// NOTE: We don't have to worry about enabling replacement of system files
// with unsigned (hence, untrusted) versions. We only queue up unsigned
// system files for replacement if the user was explicitly warned of (and
// agreed to) the consequences.
// BugBug: the session manager only allows the granularity of "allow all
// renames" or "allow no renames". If Err != NO_ERROR, then we
// might want to clear out this flag, but that means we'd negate
// any renames that were previously allowed. Yuck. So we flip a
// coin, decide to do nothing, and hope for the best if an error
// occurred. We have similar situation above -- it's all or
// nothing.
if((Err == NO_ERROR) && EnableProtectedRenames) {
pSetupProtectedRenamesFlag(TRUE);
}
// any nodes that are left are dropped
for ( ; DelayMoveNode ; DelayMoveNode = NextNode ) {
NextNode = DelayMoveNode->NextNode;
MyFree(DelayMoveNode);
}
Queue->DelayMoveQueue = NULL;
Queue->DelayMoveQueueTail = NULL;
// delete all nodes we queue'd
for ( ; DoneQueue ; DoneQueue = NextNode ) {
NextNode = DoneQueue->NextNode;
// done with node
MyFree(DoneQueue);
}
return Err;
}
VOID
pSetupUnwindAll(
IN PSP_FILE_QUEUE Queue,
IN BOOL Succeeded
)
/*++
Routine Description:
Processes the Unwind Queue. If Succeeded is FALSE, restores any data that was backed up
Arguments:
Queue Queue to be unwound
Succeeded Indicates if we should treat the whole operation as succeeded or failed
Return Value:
None--this routine should always succeed. (Any file errors encountered
along the way are logged in the setupapi logfile.)
--*/
{
// if Succeeded, we need to delete Temp files
// if we didn't succeed, we need to restore backups
PSP_UNWIND_NODE UnwindNode;
PSP_UNWIND_NODE ThisNode;
SP_TARGET_ENT TargetInfo;
PTSTR BackupFilename;
PTSTR TargetFilename;
PTSTR RenamedFilename;
DWORD Err = NO_ERROR;
TCHAR TempPath[MAX_PATH];
PTSTR TempNamePtr;
TCHAR TempFilename[MAX_PATH];
BOOL RestoreByRenaming;
BOOL OkToDeleteBackup;
if (Succeeded == FALSE) {
// we need to restore backups
WriteLogEntry(
Queue->LogContext,
SETUP_LOG_WARNING,
MSG_LOG_UNWIND,
NULL);
for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
ThisNode = UnwindNode;
UnwindNode = UnwindNode->NextNode;
if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
BackupFilename = NULL;
TargetFilename = NULL;
RenamedFilename = NULL;
// restore backup
if(!(TargetInfo.InternalFlags & SP_TEFLG_RESTORED)) {
// get target name
TargetFilename = pSetupFormFullPath(
Queue->StringTable,
TargetInfo.TargetRoot,
TargetInfo.TargetSubDir,
TargetInfo.TargetFilename);
if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
// Get renamed filename
RenamedFilename = StringTableStringFromId(Queue->StringTable,
TargetInfo.NewTargetFilename
);
}
if(TargetInfo.InternalFlags & SP_TEFLG_SAVED) {
// get backup name
BackupFilename = pSetupFormFullPath(
Queue->StringTable,
TargetInfo.BackupRoot,
TargetInfo.BackupSubDir,
TargetInfo.BackupFilename);
}
}
if(TargetFilename && (RenamedFilename || BackupFilename)) {
// We either renamed the original file or we backed it up.
// We need to put it back.
RestoreByRenaming = RenamedFilename ? TRUE : FALSE;
RestoreRenamedOrBackedUpFile(TargetFilename,
(RestoreByRenaming
? RenamedFilename
: BackupFilename),
RestoreByRenaming,
Queue->LogContext
);
// If we were doing a copy (i.e., from a backup) as opposed
// to a rename, then we need to reapply timestamp and
// security.
if(!RestoreByRenaming) {
Err = GetSetFileTimestamp(TargetFilename,
&(ThisNode->CreateTime),
&(ThisNode->AccessTime),
&(ThisNode->WriteTime),
TRUE
);
if(Err != NO_ERROR) {
// We just blew away the timestamp on the file--log
// an error entry to that effect.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BACKUP_EXISTING_RESTORE_FILETIME_FAILED,
NULL,
TargetFilename
);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
Err
);
}
if(ThisNode->SecurityDesc != NULL){
Err = StampFileSecurity(TargetFilename, ThisNode->SecurityDesc);
if(Err != NO_ERROR) {
// We just blew away the existing security on
// the file--log an error entry to that effect.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BACKUP_EXISTING_RESTORE_SECURITY_FAILED,
NULL,
TargetFilename
);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
Err
);
}
#ifdef UNICODE
Err = pSetupCallSCE(ST_SCE_UNWIND,
TargetFilename,
NULL,
NULL,
-1,
ThisNode->SecurityDesc
);
if(Err != NO_ERROR) {
// We just blew away the existing security on
// the file--log an error entry to that effect.
WriteLogEntry(Queue->LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
MSG_LOG_BACKUP_EXISTING_RESTORE_SCE_FAILED,
NULL,
TargetFilename
);
WriteLogError(Queue->LogContext,
SETUP_LOG_ERROR,
Err
);
}
#endif
}
}
// Now mark that we've restored this file. We'll delete
// tempfiles later
TargetInfo.InternalFlags |= SP_TEFLG_RESTORED;
pSetupBackupSetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo);
}
if(BackupFilename) {
MyFree(BackupFilename);
}
if(TargetFilename) {
MyFree(TargetFilename);
}
}
}
}
// cleanup - remove temporary files
for ( UnwindNode = Queue->UnwindQueue; UnwindNode != NULL; ) {
ThisNode = UnwindNode;
UnwindNode = UnwindNode->NextNode;
if (pSetupBackupGetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo) == NO_ERROR) {
// delete temporary file
if (TargetInfo.InternalFlags & SP_TEFLG_TEMPNAME) {
// get name of file that was used for backup
BackupFilename = pSetupFormFullPath(
Queue->StringTable,
TargetInfo.BackupRoot,
TargetInfo.BackupSubDir,
TargetInfo.BackupFilename);
if(BackupFilename) {
// If this operation was a bootfile replacement, then we
// don't want to delete the backup (if we used the renamed
// file for the backup as well). A delayed delete will
// have been queued to get rid of the file after a reboot.
OkToDeleteBackup = TRUE;
if(TargetInfo.InternalFlags & SP_TEFLG_MOVED) {
// Retrieve the renamed filename to see if it's the
// same as the backup filename.
RenamedFilename = StringTableStringFromId(Queue->StringTable,
TargetInfo.NewTargetFilename
);
if(!lstrcmpi(BackupFilename, RenamedFilename)) {
OkToDeleteBackup = FALSE;
}
}
if(OkToDeleteBackup) {
// since it was temporary, delete it
if(!DeleteFile(BackupFilename)) {
// Alright, see if we can set it up for delayed delete
// instead.
if(!DelayedMove(BackupFilename, NULL)) {
// Oh well, just write a log entry indicating that
// this file turd was left on the user's disk.
Err = GetLastError();
WriteLogEntry(Queue->LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_BACKUP_DELAYED_DELETE_FAILED,
NULL,
BackupFilename
);
WriteLogError(Queue->LogContext,
SETUP_LOG_WARNING,
Err
);
}
}
}
MyFree(BackupFilename);
}
TargetInfo.InternalFlags = 0; // entry is now invalid
pSetupBackupSetTargetByID((HSPFILEQ)Queue, ThisNode->TargetID, &TargetInfo);
}
}
// cleanup node
if (ThisNode->SecurityDesc != NULL) {
MyFree(ThisNode->SecurityDesc);
}
MyFree(ThisNode);
}
Queue->UnwindQueue = NULL;
}
DWORD _SetupGetBackupInformation(
IN PSP_FILE_QUEUE Queue,
OUT PSP_BACKUP_QUEUE_PARAMS BackupParams
)
/*++
Routine Description:
Get Backup INF path - Internal version
Arguments:
Queue - pointer to queue structure (validated)
BackupParams OUT - filled with INF file path
Return Value:
TRUE if success, else FALSE
--*/
{
// Queue is assumed to have been validated
// BackupParams is in Native format
LONG BackupInfID;
ULONG BufSize = MAX_PATH;
BOOL b;
DWORD err = NO_ERROR;
LPCTSTR filename;
INT offset;
BackupInfID = Queue->BackupInfID;
if (BackupInfID>=0) {
// get inf from stringtable
b = StringTableStringFromIdEx(Queue->StringTable,
BackupInfID,
BackupParams->FullInfPath,
&BufSize);
if (b == FALSE) {
if (BufSize == 0) {
err = ERROR_NO_BACKUP;
} else {
err = ERROR_INSUFFICIENT_BUFFER;
}
goto Clean0;
}
// find index of filename
filename = MyGetFileTitle(BackupParams->FullInfPath);
offset = (INT)(filename - BackupParams->FullInfPath);
BackupParams->FilenameOffset = offset;
} else {
// no backup path
err = ERROR_NO_BACKUP;
}
Clean0:
return err;
}
#ifdef UNICODE
// ANSI version in UNICODE
BOOL
WINAPI
SetupGetBackupInformationA(
IN HSPFILEQ QueueHandle,
OUT PSP_BACKUP_QUEUE_PARAMS_A BackupParams
)
{
BOOL b;
int i;
INT c;
LPCSTR p;
SP_BACKUP_QUEUE_PARAMS_W BackupParamsW;
// confirm structure size
try {
if(BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS_A)) {
SetLastError(ERROR_INVALID_PARAMETER);
b = FALSE;
leave; // exit try block
}
// call Unicode version of API
ZeroMemory( &BackupParamsW, sizeof(BackupParamsW) );
BackupParamsW.cbSize = sizeof(BackupParamsW);
b = SetupGetBackupInformationW(QueueHandle,&BackupParamsW);
if (b) {
// success, convert structure from UNICODE to ANSI
i = WideCharToMultiByte(CP_ACP, 0, BackupParamsW.FullInfPath, MAX_PATH, BackupParams->FullInfPath, MAX_PATH, NULL, NULL);
if (i==0) {
// error occurred (LastError set to error)
b = FALSE;
leave; // exit try block
}
// we need to recalc the offset of INF filename
// taking care of internationalization
p = BackupParams->FullInfPath;
for(c = 0; c < BackupParamsW.FilenameOffset; c++) {
p = CharNextA(p);
}
BackupParams->FilenameOffset = (int)(p-(BackupParams->FullInfPath)); // new offset in ANSI
}
} except(EXCEPTION_EXECUTE_HANDLER) {
// if we except, assume it's due to invalid parameter
SetLastError(ERROR_INVALID_PARAMETER);
b = FALSE;
}
return b;
}
#else
// Unicode version in ANSI
BOOL
WINAPI
SetupGetBackupInformationW(
IN HSPFILEQ QueueHandle,
OUT PSP_BACKUP_QUEUE_PARAMS_W BackupParams
)
{
UNREFERENCED_PARAMETER(QueueHandle);
UNREFERENCED_PARAMETER(BackupParams);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
// Native version
BOOL
WINAPI
SetupGetBackupInformation(
IN HSPFILEQ QueueHandle,
OUT PSP_BACKUP_QUEUE_PARAMS BackupParams
)
/*++
Routine Description:
Get Backup INF path
Arguments:
QueueHandle - handle of queue to retrieve backup INF file from
BackupParams - IN - has cbSize set, OUT - filled with INF file path
Return Value:
TRUE if success, else FALSE
--*/
{
BOOL b = TRUE;
PSP_FILE_QUEUE Queue = (PSP_FILE_QUEUE)QueueHandle;
DWORD res;
// first validate QueueHandle
try {
if(Queue->Signature != SP_FILE_QUEUE_SIG) {
b = FALSE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
}
if(!b) {
SetLastError(ERROR_INVALID_HANDLE);
goto Clean0;
}
// now fill in structure
// if we except, assume bad pointer
try {
if(BackupParams->cbSize != sizeof(SP_BACKUP_QUEUE_PARAMS)) {
SetLastError(ERROR_INVALID_PARAMETER);
b = FALSE;
leave; // exit try block
}
res = _SetupGetBackupInformation(Queue,BackupParams);
if (res == NO_ERROR) {
b = TRUE;
} else {
SetLastError(res);
b = FALSE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
// if we except, assume it's due to invalid parameter
SetLastError(ERROR_INVALID_PARAMETER);
b = FALSE;
}
Clean0:
return b;
}
VOID
RestoreRenamedOrBackedUpFile(
IN PCTSTR TargetFilename,
IN PCTSTR CurrentFilename,
IN BOOL RenameFile,
IN PSETUP_LOG_CONTEXT LogContext OPTIONAL
)
/*++
Routine Description:
This routine does its best to restore a backed-up or renamed file back to
its original name.
Arguments:
TargetFilename - filename to be restored to
CurrentFilename - file to restore
RenameFile - if TRUE, CurrentFilename was previously renamed from
TargetFilename (hence should be renamed back). If FALSE,
CurrentFilename is merely a copy, and should be copied back.
LogContext - supplies a log context used if errors are encountered.
Return Value:
None.
--*/
{
DWORD Err;
TCHAR TempPath[MAX_PATH];
PTSTR TempNamePtr;
TCHAR TempFilename[MAX_PATH];
DWORD LogTag = AllocLogInfoSlotOrLevel(LogContext,SETUP_LOG_INFO,FALSE);
WriteLogEntry(
LogContext,
LogTag,
MSG_LOG_UNWIND_FILE,
NULL,
CurrentFilename,
TargetFilename
);
// First, clear target attributes...
SetFileAttributes(TargetFilename, FILE_ATTRIBUTE_NORMAL);
if(RenameFile) {
Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
} else {
Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
}
if(Err != NO_ERROR) {
// Can't replace the file that got copied in place of
// the original one--try to move that one to a tempname
// and schedule it for delayed deletion.
WriteLogEntry(LogContext,
SETUP_LOG_ERROR|SETUP_LOG_BUFFER,
MSG_LOG_UNWIND_TRY1_FAILED,
NULL,
CurrentFilename,
TargetFilename
);
WriteLogError(LogContext,
SETUP_LOG_ERROR,
Err
);
// First, strip the filename off the path.
_tcscpy(TempPath, TargetFilename);
TempNamePtr = (PTSTR)MyGetFileTitle(TempPath);
*TempNamePtr = TEXT('\0');
// Now get a temp filename within that directory...
if(GetTempFileName(TempPath, TEXT("OLD"), 0, TempFilename) == 0 ) {
// Uh oh!
Err = GetLastError();
} else if(!DoMove(TargetFilename, TempFilename)) {
Err = GetLastError();
} else {
// OK, we were able to rename the current file to a
// temp filename--now attempt to copy or move the
// original file back to its original name.
if(RenameFile) {
Err = DoMove(CurrentFilename, TargetFilename) ? NO_ERROR : GetLastError();
} else {
Err = CopyFile(CurrentFilename, TargetFilename, FALSE) ? NO_ERROR : GetLastError();
}
if(Err != NO_ERROR) {
// This is very bad--put the current file back (it's probably
// better to have something than nothing at all).
DoMove(TempFilename, TargetFilename);
}
}
if(Err == NO_ERROR) {
// We successfully moved the current file to a temp
// filename, and put the original file back. Now
// queue a delayed delete for the temp file.
if(!DelayedMove(TempFilename, NULL)) {
// All this means is that a file turd will get
// left on the disk--simply log an event about
// this.
Err = GetLastError();
WriteLogEntry(LogContext,
SETUP_LOG_WARNING | SETUP_LOG_BUFFER,
MSG_LOG_RENAME_EXISTING_DELAYED_DELETE_FAILED,
NULL,
TargetFilename,
TempFilename
);
WriteLogError(LogContext,
SETUP_LOG_WARNING,
Err
);
}
} else {
// We were unable to put the original file back--we
// can't fail, so just log an error about this and
// keep on going.
// BUGBUG (lonnym)--in the case of a backed-up file,
// we might get away with queueing the original file
// for a delayed rename and then prompting the user
// to reboot. However, that won't work for renamed
// files, because they're typically needed very
// early on in the boot (i.e., before session
// manager has had a chance to process the delayed
// rename operations).
WriteLogEntry(LogContext,
SETUP_LOG_ERROR | SETUP_LOG_BUFFER,
(RenameFile
? MSG_LOG_RENAME_EXISTING_RESTORE_FAILED
: MSG_LOG_BACKUP_EXISTING_RESTORE_FAILED),
NULL,
CurrentFilename,
TargetFilename
);
WriteLogError(LogContext,
SETUP_LOG_ERROR,
Err
);
}
}
if (LogTag) {
ReleaseLogInfoSlot(LogContext,LogTag);
}
}
BOOL
UnPostDelayedMove(
IN PSP_FILE_QUEUE Queue,
IN PCTSTR CurrentName,
IN PCTSTR NewName OPTIONAL
)
/*++
Routine Description:
Locates a delay-move node (either for rename or delete), and removes it
from the delay-move queue.
Arguments:
Queue Queue that the move was applied to
CurrentName Name of file to be moved
NewName Name to move file to (NULL if delayed-delete)
Return Value:
If successful, the return value is TRUE, otherwise it is FALSE.
--*/
{
PSP_DELAYMOVE_NODE CurNode, PrevNode;
PCTSTR SourceFilename, TargetFilename;
// Since the path string IDs in the delay-move nodes are case-sensitive, we
// don't attempt to match on ID. We instead retrieve the strings, and do
// case-insensitive string compares. Since this routine is rarely used, the
// performance hit isn't a big deal.
for(CurNode = Queue->DelayMoveQueue, PrevNode = NULL;
CurNode;
PrevNode = CurNode, CurNode = CurNode->NextNode) {
if(NewName) {
// We're searching for a delayed rename, so we must pay attention
// to the target filename.
if(CurNode->TargetFilename == -1) {
continue;
} else {
TargetFilename = StringTableStringFromId(Queue->StringTable, CurNode->TargetFilename);
MYASSERT(TargetFilename);
if(lstrcmpi(NewName, TargetFilename)) {
// Target filenames differ--move on.
continue;
}
}
} else {
// We're searching for a delayed delete.
if(CurNode->TargetFilename != -1) {
// This is a rename, not a delete--move on.
continue;
}
}
// If we get to here, then the target filenames match (if this is a
// rename), or they're both empty (if it's a delete). Now compare the
// source filenames.
MYASSERT(CurNode->SourceFilename != -1);
SourceFilename = StringTableStringFromId(Queue->StringTable, CurNode->SourceFilename);
MYASSERT(SourceFilename);
if(lstrcmpi(CurrentName, SourceFilename)) {
// Source filenames differ--move on.
continue;
} else {
// We have a match--remove the node from the delay-move queue.
if(PrevNode) {
PrevNode->NextNode = CurNode->NextNode;
} else {
Queue->DelayMoveQueue = CurNode->NextNode;
}
if(!CurNode->NextNode) {
MYASSERT(Queue->DelayMoveQueueTail == CurNode);
Queue->DelayMoveQueueTail = PrevNode;
}
MyFree(CurNode);
return TRUE;
}
}
// We didn't find a match.
return FALSE;
}