/*++ Copyright (c) 1995 Microsoft Corporation Module Name: fileq3.c Abstract: Setup file queue routines for enqueing delete and rename operations. Author: Ted Miller (tedm) 15-Feb-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop BOOL _SetupQueueDelete( IN HSPFILEQ QueueHandle, IN PCTSTR PathPart1, IN PCTSTR PathPart2, OPTIONAL IN UINT Flags ) /*++ Routine Description: Place a delete operation on a setup file queue. Note that delete operations are assumed to be on fixed media. No prompting will be performed for delete operations when the queue is committed. Arguments: QueueHandle - supplies a handle to a setup file queue, as returned by SetupOpenFileQueue. PathPart1 - Supplies the first part of the path of the file to be deleted. If PathPart2 is not specified, then this is the full path of the file to be deleted. PathPart2 - if specified, supplies the second part of the path of the file to be deleted. This is concatenated to PathPart1 to form the full pathname. Flags - specified flags controlling delete operation. DELFLG_IN_USE - if the file is in use, queue it for delayed delete, on next reboot. Otherwise in-use files are not deleted. DELFLG_IN_USE1 - same behavior as DELFLG_IN_USE--used when the same file list section is used for both a CopyFiles and DelFiles. (Since DELFLG_IN_USE (0x1) is also COPYFLG_WARN_IF_SKIP!) Return Value: Boolean value indicating outcome. If FALSE, GetLastError() returns extended error information. --*/ { PSP_FILE_QUEUE Queue; PSP_FILE_QUEUE_NODE QueueNode, TempNode, PrevQueueNode; Queue = (PSP_FILE_QUEUE)QueueHandle; // // Allocate a queue structure. // QueueNode = MyMalloc(sizeof(SP_FILE_QUEUE_NODE)); if(!QueueNode) { goto clean0; } ZeroMemory(QueueNode, sizeof(SP_FILE_QUEUE_NODE)); // // Operation is delete. // QueueNode->Operation = FILEOP_DELETE; // // Initialize unused fields. // QueueNode->SourceRootPath = -1; QueueNode->SourcePath = -1; QueueNode->SourceFilename = -1; // // Set internal flag to indicate whether we should queue a delayed delete // for this file if it's in-use. // QueueNode->InternalFlags = (Flags & (DELFLG_IN_USE|DELFLG_IN_USE1)) ? IQF_DELAYED_DELETE_OK : 0; // // NOTE: When adding the following strings to the string table, we cast away // their CONST-ness to avoid a compiler warning. Since we are adding them // case-sensitively, we are guaranteed they will not be modified. // // // Set up the target directory. // QueueNode->TargetDirectory = pSetupStringTableAddString(Queue->StringTable, (PTSTR)PathPart1, STRTAB_CASE_SENSITIVE ); if(QueueNode->TargetDirectory == -1) { goto clean1; } // // Set up the target filename. // if(PathPart2) { QueueNode->TargetFilename = pSetupStringTableAddString(Queue->StringTable, (PTSTR)PathPart2, STRTAB_CASE_SENSITIVE ); if(QueueNode->TargetFilename == -1) { goto clean1; } } else { QueueNode->TargetFilename = -1; } // // Link the node onto the end of the delete queue. // QueueNode->Next = NULL; if(Queue->DeleteQueue) { // // Check to see if this same rename operation has already been enqueued, // and if so, get rid of the new one, to avoid duplicates. NOTE: We // don't check the "InternalFlags" field, since if the node already // exists in the queue (based on all the other relevant fields comparing // successfully), then any internal flags that were set on the // previously-existing node should be preserved (i.e., our new node // always is created with InternalFlags set to zero). // for(TempNode=Queue->DeleteQueue, PrevQueueNode = NULL; TempNode; PrevQueueNode = TempNode, TempNode=TempNode->Next) { if((TempNode->TargetDirectory == QueueNode->TargetDirectory) && (TempNode->TargetFilename == QueueNode->TargetFilename)) { // // We've found a duplicate. However, we need to make sure that // if our new node specifies "delayed delete OK", then the // existing node has that internal flag set as well. // MYASSERT(!(QueueNode->InternalFlags & ~IQF_DELAYED_DELETE_OK)); if(QueueNode->InternalFlags & IQF_DELAYED_DELETE_OK) { TempNode->InternalFlags |= IQF_DELAYED_DELETE_OK; } // // Kill the newly-created queue node and return success. // MyFree(QueueNode); return TRUE; } } MYASSERT(PrevQueueNode); PrevQueueNode->Next = QueueNode; } else { Queue->DeleteQueue = QueueNode; } Queue->DeleteNodeCount++; return(TRUE); clean1: MyFree(QueueNode); clean0: SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } #ifdef UNICODE // // ANSI version // BOOL SetupQueueDeleteA( IN HSPFILEQ QueueHandle, IN PCSTR PathPart1, IN PCSTR PathPart2 OPTIONAL ) { PWSTR p1,p2; DWORD d; BOOL b; b = FALSE; d = pSetupCaptureAndConvertAnsiArg(PathPart1,&p1); if(d == NO_ERROR) { if(PathPart2) { d = pSetupCaptureAndConvertAnsiArg(PathPart2,&p2); } else { p2 = NULL; } if(d == NO_ERROR) { b = _SetupQueueDelete(QueueHandle,p1,p2,0); d = GetLastError(); if(p2) { MyFree(p2); } } MyFree(p1); } SetLastError(d); return(b); } #else // // Unicode stub // BOOL SetupQueueDeleteW( IN HSPFILEQ QueueHandle, IN PCWSTR PathPart1, IN PCWSTR PathPart2 OPTIONAL ) { UNREFERENCED_PARAMETER(QueueHandle); UNREFERENCED_PARAMETER(PathPart1); UNREFERENCED_PARAMETER(PathPart2); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL SetupQueueDelete( IN HSPFILEQ QueueHandle, IN PCTSTR PathPart1, IN PCTSTR PathPart2 OPTIONAL ) /*++ Routine Description: Place a delete operation on a setup file queue. Note that delete operations are assumed to be on fixed media. No prompting will be performed for delete operations when the queue is committed. Arguments: QueueHandle - supplies a handle to a setup file queue, as returned by SetupOpenFileQueue. PathPart1 - Supplies the first part of the path of the file to be deleted. If PathPart2 is not specified, then this is the full path of the file to be deleted. PathPart2 - if specified, supplies the second part of the path of the file to be deleted. This is concatenated to PathPart1 to form the full pathname. Return Value: Boolean value indicating outcome. If FALSE, GetLastError() returns extended error information. --*/ { PTSTR p1,p2; DWORD d; BOOL b; b = FALSE; d = CaptureStringArg(PathPart1,&p1); if(d == NO_ERROR) { if(PathPart2) { d = CaptureStringArg(PathPart2,&p2); } else { p2 = NULL; } if(d == NO_ERROR) { b = _SetupQueueDelete(QueueHandle,p1,p2,0); d = GetLastError(); if(p2) { MyFree(p2); } } MyFree(p1); } SetLastError(d); return(b); } #ifdef UNICODE // // ANSI version // BOOL SetupQueueDeleteSectionA( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCSTR Section ) { PWSTR section; DWORD d; BOOL b; d = pSetupCaptureAndConvertAnsiArg(Section,§ion); if(d == NO_ERROR) { b = SetupQueueDeleteSectionW(QueueHandle,InfHandle,ListInfHandle,section); d = GetLastError(); MyFree(section); } else { b = FALSE; } SetLastError(d); return(b); } #else // // Unicode stub // BOOL SetupQueueDeleteSectionW( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCWSTR Section ) { UNREFERENCED_PARAMETER(QueueHandle); UNREFERENCED_PARAMETER(InfHandle); UNREFERENCED_PARAMETER(ListInfHandle); UNREFERENCED_PARAMETER(Section); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL SetupQueueDeleteSection( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCTSTR Section ) /*++ Routine Description: Queue an entire section in an inf file for delete. The section must be in delete-section format and the inf file must contain [DestinationDirs]. Arguments: QueueHandle - supplies a handle to a setup file queue, as returned by SetupOpenFileQueue. InfHandle - supplies a handle to an open inf file, that contains the [DestinationDirs] section. ListInfHandle - if specified, supplies a handle to the open inf file containing the section named by Section. If not specified this section is assumed to be in InfHandle. Section - supplies the name of the section to be queued for delete. Return Value: Boolean value indicating outcome. If FALSE, GetLastError() returns extended error information. Some files may have been queued successfully. --*/ { BOOL b; PTSTR TargetDirectory; PCTSTR TargetFilename; INFCONTEXT LineContext; DWORD SizeRequired; DWORD rc; UINT Flags; if(!ListInfHandle) { ListInfHandle = InfHandle; } // // The section has to exist and there sas to be at least one line in it. // b = SetupFindFirstLine(ListInfHandle,Section,NULL,&LineContext); if(!b) { rc = GetLastError(); pSetupLogSectionError(ListInfHandle,NULL,NULL,QueueHandle,Section,MSG_LOG_NOSECTION_DELETE,rc,NULL); SetLastError(ERROR_SECTION_NOT_FOUND); // this is not the real error, but might be what caller expects return(FALSE); } // // Iterate every line in the section. // do { // // Get the target filename out of the line. // TargetFilename = pSetupFilenameFromLine(&LineContext,FALSE); if(!TargetFilename) { SetLastError(ERROR_INVALID_DATA); return(FALSE); } // // Determine the target path for the file. // b = SetupGetTargetPath(InfHandle,&LineContext,NULL,NULL,0,&SizeRequired); if(!b) { return(FALSE); } TargetDirectory = MyMalloc(SizeRequired*sizeof(TCHAR)); if(!TargetDirectory) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } SetupGetTargetPath(InfHandle,&LineContext,NULL,TargetDirectory,SizeRequired,NULL); // // If present flags are field 4 // if(!SetupGetIntField(&LineContext,4,(PINT)&Flags)) { Flags = 0; } // // Add to queue. // b = _SetupQueueDelete(QueueHandle,TargetDirectory,TargetFilename,Flags); rc = GetLastError(); MyFree(TargetDirectory); if(!b) { SetLastError(rc); return(FALSE); } } while(SetupFindNextLine(&LineContext,&LineContext)); return(TRUE); } #ifdef UNICODE // // ANSI version // BOOL SetupQueueRenameA( IN HSPFILEQ QueueHandle, IN PCSTR SourcePath, IN PCSTR SourceFilename, OPTIONAL IN PCSTR TargetPath, OPTIONAL IN PCSTR TargetFilename ) { PWSTR sourcepath = NULL; PWSTR sourcefilename = NULL; PWSTR targetpath = NULL; PWSTR targetfilename = NULL; DWORD d; BOOL b; b = FALSE; d = pSetupCaptureAndConvertAnsiArg(SourcePath,&sourcepath); if((d == NO_ERROR) && SourceFilename) { d = pSetupCaptureAndConvertAnsiArg(SourceFilename,&sourcefilename); } if((d == NO_ERROR) && TargetPath) { d = pSetupCaptureAndConvertAnsiArg(TargetPath,&targetpath); } if(d == NO_ERROR) { d = pSetupCaptureAndConvertAnsiArg(TargetFilename,&targetfilename); } if(d == NO_ERROR) { b = SetupQueueRenameW(QueueHandle,sourcepath,sourcefilename,targetpath,targetfilename); d = GetLastError(); } if(sourcepath) { MyFree(sourcepath); } if(sourcefilename) { MyFree(sourcefilename); } if(targetpath) { MyFree(targetpath); } if(targetfilename) { MyFree(targetfilename); } SetLastError(d); return(b); } #else // // Unicode stub // BOOL SetupQueueRenameW( IN HSPFILEQ QueueHandle, IN PCWSTR SourcePath, IN PCWSTR SourceFilename, OPTIONAL IN PCWSTR TargetPath, OPTIONAL IN PCWSTR TargetFilename ) { UNREFERENCED_PARAMETER(QueueHandle); UNREFERENCED_PARAMETER(SourcePath); UNREFERENCED_PARAMETER(SourceFilename); UNREFERENCED_PARAMETER(TargetPath); UNREFERENCED_PARAMETER(TargetFilename); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL SetupQueueRename( IN HSPFILEQ QueueHandle, IN PCTSTR SourcePath, IN PCTSTR SourceFilename, OPTIONAL IN PCTSTR TargetPath, OPTIONAL IN PCTSTR TargetFilename ) /*++ Routine Description: Place a rename operation on a setup file queue. Note that rename operations are assumed to be on fixed media. No prompting will be performed for rename operations when the queue is committed. Arguments: QueueHandle - supplies a handle to a setup file queue, as returned by SetupOpenFileQueue. SourcePath - Supplies the source path of the file to be renamed. If SourceFilename is specified, this is the part part only. If SourceFilename is not specified, this is the fully-qualified path. SourceFilename - if specified, supplies the filename part of the file to be renamed. If not specified, SourcePath is the fully- qualified path of the file to be renamed. TargetPath - if specified, supplies the target directory, and the rename is actually a move operation. If not specified, then the rename takes place without moving the file. TargetFilename - supplies the new name (no path) of the file. Return Value: Boolean value indicating outcome. If FALSE, GetLastError() returns extended error information. --*/ { PSP_FILE_QUEUE Queue; PSP_FILE_QUEUE_NODE QueueNode, TempNode, PrevQueueNode; DWORD err = NO_ERROR; // // validate parameters so that we return correct error // if(SourcePath == NULL || TargetFilename == NULL) { err = ERROR_INVALID_PARAMETER; goto clean0; } Queue = (PSP_FILE_QUEUE)QueueHandle; // // Allocate a queue structure. // QueueNode = MyMalloc(sizeof(SP_FILE_QUEUE_NODE)); if(!QueueNode) { err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } ZeroMemory(QueueNode, sizeof(SP_FILE_QUEUE_NODE)); // // Operation is rename. // QueueNode->Operation = FILEOP_RENAME; // // Initialize unused SourceRootPath field. // QueueNode->SourceRootPath = -1; // // NOTE: When adding the following strings to the string table, we cast away // their CONST-ness to avoid a compiler warning. Since we are adding them // case-sensitively, we are guaranteed they will not be modified. // // // Set up the source path. // QueueNode->SourcePath = pSetupStringTableAddString(Queue->StringTable, (PTSTR)SourcePath, STRTAB_CASE_SENSITIVE ); if(QueueNode->SourcePath == -1) { err = ERROR_NOT_ENOUGH_MEMORY; goto clean1; } // // Set up the source filename. // if(SourceFilename) { QueueNode->SourceFilename = pSetupStringTableAddString(Queue->StringTable, (PTSTR)SourceFilename, STRTAB_CASE_SENSITIVE ); if(QueueNode->SourceFilename == -1) { err = ERROR_NOT_ENOUGH_MEMORY; goto clean1; } } else { QueueNode->SourceFilename = -1; } // // Set up the target directory. // if(TargetPath) { QueueNode->TargetDirectory = pSetupStringTableAddString(Queue->StringTable, (PTSTR)TargetPath, STRTAB_CASE_SENSITIVE ); if(QueueNode->TargetDirectory == -1) { err = ERROR_NOT_ENOUGH_MEMORY; goto clean1; } } else { QueueNode->TargetDirectory = -1; } // // Set up the target filename. // QueueNode->TargetFilename = pSetupStringTableAddString(Queue->StringTable, (PTSTR)TargetFilename, STRTAB_CASE_SENSITIVE ); if(QueueNode->TargetFilename == -1) { err = ERROR_NOT_ENOUGH_MEMORY; goto clean1; } // // Link the node onto the end of the rename queue. // QueueNode->Next = NULL; if(Queue->RenameQueue) { // // Check to see if this same rename operation has already been enqueued, // and if so, get rid of the new one, to avoid duplicates. NOTE: We // don't check the "InternalFlags" field, since if the node already // exists in the queue (based on all the other relevant fields comparing // successfully), then any internal flags that were set on the // previously-existing node should be preserved (i.e., our new node // always is created with InternalFlags set to zero). // for(TempNode=Queue->RenameQueue, PrevQueueNode = NULL; TempNode; PrevQueueNode = TempNode, TempNode=TempNode->Next) { if((TempNode->SourcePath == QueueNode->SourcePath) && (TempNode->SourceFilename == QueueNode->SourceFilename) && (TempNode->TargetDirectory == QueueNode->TargetDirectory) && (TempNode->TargetFilename == QueueNode->TargetFilename)) { // // We have a duplicate--kill the newly-created queue node and // return success. // MYASSERT(TempNode->StyleFlags == 0); MyFree(QueueNode); return TRUE; } } MYASSERT(PrevQueueNode); PrevQueueNode->Next = QueueNode; } else { Queue->RenameQueue = QueueNode; } Queue->RenameNodeCount++; return(TRUE); clean1: MyFree(QueueNode); clean0: SetLastError(err); return(FALSE); } #ifdef UNICODE // // ANSI version // BOOL SetupQueueRenameSectionA( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCSTR Section ) { PWSTR section; DWORD d; BOOL b; d = pSetupCaptureAndConvertAnsiArg(Section,§ion); if(d == NO_ERROR) { b = SetupQueueRenameSectionW(QueueHandle,InfHandle,ListInfHandle,section); d = GetLastError(); MyFree(section); } else { b = FALSE; } SetLastError(d); return(b); } #else // // Unicode stub // BOOL SetupQueueRenameSectionW( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCWSTR Section ) { UNREFERENCED_PARAMETER(QueueHandle); UNREFERENCED_PARAMETER(InfHandle); UNREFERENCED_PARAMETER(ListInfHandle); UNREFERENCED_PARAMETER(Section); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL SetupQueueRenameSection( IN HSPFILEQ QueueHandle, IN HINF InfHandle, IN HINF ListInfHandle, OPTIONAL IN PCTSTR Section ) /*++ Routine Description: Queue an entire section in an inf file for delete. The section must be in delete-section format and the inf file must contain [DestinationDirs]. The format of a rename list section dictates that only renames within the same directory is supported (ie, you cannot queue file moves with this API). Arguments: QueueHandle - supplies a handle to a setup file queue, as returned by SetupOpenFileQueue. InfHandle - supplies a handle to an open inf file, that contains the [DestinationDirs] section. ListInfHandle - if specified, supplies a handle to the open inf file containing the section named by Section. If not specified this section is assumed to be in InfHandle. Section - supplies the name of the section to be queued for delete. Return Value: Boolean value indicating outcome. If FALSE, GetLastError() returns extended error information. --*/ { BOOL b; INFCONTEXT LineContext; PCTSTR TargetFilename; PCTSTR SourceFilename; PTSTR Directory; DWORD SizeRequired; DWORD rc; if(!ListInfHandle) { ListInfHandle = InfHandle; } // // The section has to exist and there has to be at least one line in it. // b = SetupFindFirstLine(ListInfHandle,Section,NULL,&LineContext); if(!b) { rc = GetLastError(); pSetupLogSectionError(ListInfHandle,NULL,NULL,QueueHandle,Section,MSG_LOG_NOSECTION_RENAME,rc,NULL); SetLastError(ERROR_SECTION_NOT_FOUND); // this is not the real error, but might be what caller expects return(FALSE); } // // Iterate every line in the section. // do { // // Get the target filename out of the line. // TargetFilename = pSetupFilenameFromLine(&LineContext,FALSE); if(!TargetFilename) { SetLastError(ERROR_INVALID_DATA); return(FALSE); } // // Get source filename out of the line. // SourceFilename = pSetupFilenameFromLine(&LineContext,TRUE); if(!SourceFilename || (*SourceFilename == 0)) { SetLastError(ERROR_INVALID_DATA); return(FALSE); } // // Determine the path of the file. // b = SetupGetTargetPath(InfHandle,&LineContext,NULL,NULL,0,&SizeRequired); if(!b) { return(FALSE); } Directory = MyMalloc(SizeRequired*sizeof(TCHAR)); if(!Directory) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } SetupGetTargetPath(InfHandle,&LineContext,NULL,Directory,SizeRequired,NULL); // // Add to queue. // b = SetupQueueRename( QueueHandle, Directory, SourceFilename, NULL, TargetFilename ); rc = GetLastError(); MyFree(Directory); if(!b) { SetLastError(rc); return(FALSE); } } while(SetupFindNextLine(&LineContext,&LineContext)); return(TRUE); }