/*++ Copyright (c) 1996 Microsoft Corporation Module Name: buildinf.c Abstract: The functions in this file are a set of APIs to build an INF, merge existing INFs, and write the INF to disk. Author: Jim Schmidt (jimschm) 20-Sept-1996 Revision History: marcw 30-Jun-1999 pWriteDelAndMoveFiles is now done in a seperate function so that it can be on the progress bar and not cause an uncomfortable delay after the user passes the progress bar. marcw 09-Feb-1999 Filter out quotes in values -- Not supported by inf parser in NTLDR. ovidiut 03-Feb-1999 Ensure that directory collision moves are done first in mov/del files. jimschm 23-Sep-1998 Updated for new fileops marcw 23-Jul-1998 Removed lots of intermediary conversions (MB->W->MB->W) in writing the win9xmov and win9xdel txt files. (Fixes problems with certain characters that were messed up in the translations.) marcw 10-Jun-1998 Added support for multiple keys with the same name. marcw 08-Apr-1998 Fixed problems associated with some conversions. marcw 08-Apr-1998 All values are automatically quoted now..Matches winnt32 behavior. calinn 25-Mar-1998 fixed unicode header writing error marcw 16-Dec-1997 pWriteDelAndMoveFiles now writes to a unicode file instead of winnt.sif. mikeco 11-Nov-1997 DBCS issues. jimschm 21-Jul-1997 pWriteDelAndMoveFiles (see fileops.c in memdb) mikeco 10-Jun-1997 DBCS issues. marcw 09-Apr-1997 Performance tuned memdb usage. marcw 01-Apr-1997 Re-engineered this code..Now memdb based. shorter. also added smart-merge for migration DLLs. jimschm 03-Jan-1997 Re-enabled memory-based INF building code --*/ #include "pch.h" #define MAX_LINE_LENGTH 512 #define MIN_SPLIT_COL 490 #define MAX_OEM_PATH (MAX_PATH*2) #define WACKREPLACECHAR 2 #define REPLACE_WACKS TRUE #define RESTORE_WACKS FALSE #define SECTION_NAME_SIZE 8192 // // These levels refer to the MEMDB_ENUM structure component nCurPos. // They were determined empirically in test to be the correct values // #define SECTIONLEVEL 3 #define KEYLEVEL 4 #define VALUELEVEL 5 #define BUILDINF_UNIQUEKEY_PREFIX TEXT("~BUILDINF~UNIQUE~") #define BUILDINF_NULLKEY_PREFIX TEXT("~BUILDINF~NULLKEY~") #define BUILDINF_UNIQUEKEY_PREFIX_SIZE 17 TCHAR g_DblQuote[] = TEXT("\""); TCHAR g_Comments[] = TEXT(";\r\n; Setup-generated migration INF file\r\n;\r\n\r\n"); extern HINF g_Win95UpgInf; BOOL g_AnswerFileAlreadyWritten; BOOL pWriteStrToFile (HANDLE File, PCTSTR String, BOOL ConvertToUnicode); BOOL pWriteSectionString (HANDLE File, PCTSTR szString, BOOL ConvertToUnicode, BOOL Quote); BOOL pWriteSections (HANDLE File,BOOL ConvertToUnicode); BOOL pWriteDelAndMoveFiles (VOID); BOOL WINAPI BuildInf_Entry(IN HINSTANCE hinstDLL, IN DWORD dwReason, IN LPVOID lpv) /*++ Routine Description: DllMain is called after the C runtime is initialized, and its purpose is to initialize the globals for this process. For this DLL, it initializes g_hInst and g_hHeap. Arguments: hinstDLL - (OS-supplied) Instance handle for the DLL dwReason - (OS-supplied) Type of initialization or termination lpv - (OS-supplied) Unused Return Value: TRUE because DLL always initializes properly. --*/ { switch (dwReason) { case DLL_PROCESS_ATTACH: g_AnswerFileAlreadyWritten = FALSE; break; case DLL_PROCESS_DETACH: break; } UNREFERENCED_PARAMETER(hinstDLL); UNREFERENCED_PARAMETER(lpv); return TRUE; } VOID pHandleWacks( IN OUT PSTR String, IN BOOL Operation ) /*++ Routine Description: pHandleWack is a simple helper function who's purpose is to remove/restore the wacks in a string. This is necessary because there are cases where we want to have wacks in keys and values without invoking the memdb functionality of using those wacks to indicate new tree nodes. Arguments: String - The string to perform the replacement on. Operation - set to REPLACE_WACKS if the function should replace the wacks in a string and RESTORE_WACKS if they should restore them. Return Value: None. --*/ { TCHAR findChar; TCHAR replaceChar; PSTR curChar; // // Set find and replace chars based on operation. // if (Operation == REPLACE_WACKS) { findChar = TEXT('\\'); replaceChar = TEXT(WACKREPLACECHAR); } else { findChar = TEXT(WACKREPLACECHAR); replaceChar = TEXT('\\'); } if ((curChar = _tcschr(String,findChar)) != NULL) { do { *curChar = replaceChar; } while ((curChar = _tcschr(curChar,findChar)) != NULL); } } BOOL WriteInfToDisk ( IN PCTSTR OutputFile ) /*++ Routine Description: WriteInfToDisk outputs the memory based INF file that has been built to disk. Arguments: OutputFile - A full path to the output INF file. Return Value: Returns TRUE if the INF file was successfully written, or FALSE if an I/O error was encountered. Call GetLastError upon failure for a Win32 error code. --*/ { HANDLE hFile; MEMDB_ENUM e; BOOL b = FALSE; TCHAR category[MEMDB_MAX]; if (g_AnswerFileAlreadyWritten) { LOG ((LOG_ERROR,"Answer file has already been written to disk.")); return FALSE; } // // set flag indicating that answerfile is closed for business. // g_AnswerFileAlreadyWritten = TRUE; // // Write an INF // hFile = CreateFile (OutputFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { __try { #ifdef UNICODE // // Write UNICODE signature // if (!pWriteStrToFile (hFile, (LPCTSTR) "\xff\xfe\0",FALSE)) __leave; #endif // // Write comments // if (!pWriteStrToFile (hFile, g_Comments,FALSE)) __leave; // // Write out sections. // if (!pWriteSections (hFile,FALSE)) __leave; // // Delete Answer file parts of memdb. // MemDbDeleteTree(MEMDB_CATEGORY_UNATTENDRESTRICTRIONS); MemDbDeleteTree(MEMDB_CATEGORY_AF_VALUES); if (MemDbEnumItems(&e,MEMDB_CATEGORY_AF_SECTIONS)) { do { wsprintf(category,S_ANSWERFILE_SECTIONMASK,e.szName); MemDbDeleteTree(category); } while (MemDbEnumNextValue(&e)); } MemDbDeleteTree(MEMDB_CATEGORY_AF_SECTIONS); b = TRUE; // indicate success } __finally { CloseHandle (hFile); } } return b; } BOOL pWriteStrToFile ( IN HANDLE File, IN LPCTSTR String, IN BOOL ConvertToUnicode ) /*++ Routine Description: pWriteStrToFile takes a zero-terminated string and writes it to the open file indicated by File. Optionally, the function can convert its string argument into a unicode string. Arguments: File - A handle to an open file with write permission. String - A pointer to the string to write. The string is written to the file, but the zero terminator is not written to the file. ConvertToUnicode - True if you wish to convert to unicode, false otherwise. Return Value: TRUE if the write succeeds, or FALSE if an I/O error occurred. GetLastError() can be used to get more error information. --*/ { DWORD bytesWritten = 0; DWORD size = 0; BOOL rFlag = TRUE; PBYTE data = NULL; WCHAR unicodeString[MEMDB_MAX]; if (ConvertToUnicode) { size = _mbslen(String); MultiByteToWideChar ( CP_OEMCP, 0, String, -1, unicodeString, MEMDB_MAX ); data = (PBYTE) unicodeString; size = ByteCountW(unicodeString); } else { data = (PBYTE) String; size = ByteCount(String); } rFlag = WriteFile (File, data, size, &bytesWritten, NULL); if (bytesWritten != size) { rFlag = FALSE; } return rFlag; } BOOL pIsDoubledChar ( CHARTYPE Char ) /*++ Routine Description: This simple function simply returns true if the character in question is a double quote or a percent sign. Arguments: Char - The character in question. Return Value: --*/ { return Char == TEXT('\"') || Char == TEXT('%'); } BOOL pWriteSectionString ( HANDLE File, LPCTSTR String, BOOL ConvertToUnicode, BOOL Quote ) /*++ Routine Description: pWriteSectionString writes a line in a section to disk. If the string has a character that requires quote mode, the string is surrounded by quotes, and all literal quotes and percent signs are doubled-up. Arguments: File - A handle to an open file with write permission. String - A pointer to the string holding the section line. ConvertToUnicode - TRUE if the line in question should be converted to UNICODE, FALSE otherwise. Quote - Automatically Quotes the string if set to TRUE. Return Value: TRUE if the write succeeds, or FALSE if an I/O error occurred. GetLastError() can be used to get more error information. --*/ { TCHAR buffer[256]; int i; int doubles; PTSTR dest; CHARTYPE tc; // // Initialize variables first time through the loop. // doubles = 0; dest = buffer; while (*String) { // // Add initial quote // if (Quote) { StringCopy (dest, g_DblQuote); dest = _tcsinc (dest); } // // Copy as many characters as can fit on the line // tc = 0; for (i = 0 ; *String && i + doubles < MAX_LINE_LENGTH ; i++) { if (i + doubles > MIN_SPLIT_COL) tc = _tcsnextc (String); // // Double up certain characters when in quote mode // if (Quote && pIsDoubledChar (_tcsnextc (String))) { doubles++; // // Copy a UNICODE/MBCS char, leaving src in place // _tcsncpy (dest, String, (_tcsinc(String)-String)); dest = _tcsinc (dest); } // // Copy a UNICODE/MBCS char, advancing src/tgt // _tcsncpy (dest, String, (_tcsinc(String)-String)); dest = _tcsinc (dest); String = _tcsinc (String); // // Test split condition (NOTE: tc == 0 when i + doubles <= MIN_SPLIT_COL) // if (tc && (tc == TEXT(',') || tc == TEXT(' ') || tc == TEXT('\\'))) { break; } } // // Add a trailing quote // if (Quote) { StringCopy (dest, g_DblQuote); dest = _tcsinc (dest); } // // Add trailing wack if line is split // if (*String) { *dest = TEXT('\\'); dest = _tcsinc (dest); } *dest = 0; // // Write line to disk // if (!pWriteStrToFile (File, buffer,ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSectionString: pWriteStrToFile failed")); return FALSE; } // // Reset for next time through the while loop // doubles = 0; dest = buffer; } return TRUE; } BOOL pWriteSections ( IN HANDLE File, IN BOOL ConvertToUnicode ) /*++ Routine Description: pWriteSections enumerates all of the sections stored by previous calls to WriteInfKey and WriteInfKeyEx, and writes these sections to the open file specified by File. Arguments: File - An open file. ConvertToUnicode - Set to TRUE if the lines should be converted to UNICODE, FALSE otherwise. Return Value: TRUE if the write succeeds, or FALSE if an I/O error occurred. GetLastError() can be used to get more error information. --*/ { MEMDB_ENUM e; TCHAR buffer[MAX_TCHAR_PATH]; PTSTR sectionName; PTSTR p; BOOL firstTime = FALSE; BOOL nullKey = FALSE; // // enumerate the memdb sections and keys section. // if (MemDbEnumFirstValue (&e, MEMDB_CATEGORY_AF_SECTIONS, MEMDB_ALL_SUBLEVELS, MEMDB_ALL_BUT_PROXY)) { do { switch(e.PosCount) { case SECTIONLEVEL: // // Write the Section Name to the File, surrounded by Brackets. // if (!pWriteStrToFile (File, TEXT("\r\n\r\n"),ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed")); return FALSE; } if (!pWriteStrToFile (File, TEXT("["),ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed")); return FALSE; } if ((sectionName = _tcschr(e.szName,TEXT('\\'))) == NULL || (sectionName = _tcsinc(sectionName)) == NULL) { LOG ((LOG_ERROR,"Invalid section name for answer file.")); return FALSE; } // // put wacks back in if necessary. // pHandleWacks(sectionName,RESTORE_WACKS); if (!pWriteStrToFile (File, sectionName,ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed")); return FALSE; } if (!pWriteStrToFile (File, TEXT("]"),ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed")); return FALSE; } break; case KEYLEVEL: // // Key name. // if (!pWriteStrToFile (File, TEXT("\r\n"),ConvertToUnicode)) { DEBUGMSG ((DBG_ERROR, "pWriteSections: pWriteStrToFile failed")); return FALSE; } if (!MemDbBuildKeyFromOffset(e.dwValue,buffer,1,NULL)) { DEBUGMSG((DBG_ERROR,"pWriteSections: MemDb failure!")); return FALSE; } // // Skip NULL keys // if (!StringMatch (buffer, BUILDINF_NULLKEY_PREFIX)) { // // Strip out "uniqueified" prefix, if present. // if (!StringCompareTcharCount(BUILDINF_UNIQUEKEY_PREFIX,buffer,BUILDINF_UNIQUEKEY_PREFIX_SIZE)) { p = buffer + BUILDINF_UNIQUEKEY_PREFIX_SIZE + 4; } else { p = buffer; } if (!pWriteSectionString(File,p,ConvertToUnicode,FALSE)) { DEBUGMSG((DBG_ERROR,"WriteSections: pWriteStrToFile failed")); return FALSE; } nullKey = FALSE; } else { nullKey = TRUE; } firstTime = TRUE; break; case VALUELEVEL: // // Value. // if (MemDbBuildKeyFromOffset(e.dwValue,buffer,1,NULL)) { if (!nullKey) { if (!pWriteStrToFile(File, (firstTime ? TEXT("=") : TEXT(",")),ConvertToUnicode)) { DEBUGMSG((DBG_ERROR,"pWriteSections: pWriteStrToFile failed")); return FALSE; } } firstTime = FALSE; if (!pWriteSectionString(File, buffer,ConvertToUnicode,TRUE)) { DEBUGMSG((DBG_ERROR,"pWriteSections: pWriteSectionString failed.")); return FALSE; } } break; default: // // Not a bug if it gets to this case, we just don't care. // break; } } while (MemDbEnumNextValue(&e)); } pWriteStrToFile (File, TEXT("\r\n\r\n"),ConvertToUnicode); return TRUE; } BOOL pRestrictedKey ( IN PCTSTR Section, IN PCTSTR Key ) /*++ Routine Description: pRestrictedKey returns TRUE if a key is restricted in win95upg.inf and FALSE otherwise. Certain keys are restricted to prevent migration dlls from overwriting important upgrade information. Arguments: Section - A string containing the section to be written. Key - A String containing the Key to be written. Return Value: TRUE if the key is restricted, and FALSE otherwise. --*/ { TCHAR memDbKey[MEMDB_MAX+1]; MemDbBuildKey(memDbKey,MEMDB_CATEGORY_UNATTENDRESTRICTRIONS,Section,Key,NULL); return MemDbGetPatternValue(memDbKey,NULL); } BOOL pDoMerge ( IN PCTSTR InputFile, IN BOOL Restricted ) { /*++ Routine Description: pDoMerge is responsible for merging in information from a file into the currently maintained answer file in memdb. The data being added from file will take precidence over what has already been written. Therefore, if a section/key pair is added that has previously been added, the new section/key will take precedence. The one caveat is that if Restricted is set to true, only keys that are not restricted in win95upg.inf may be merged in. Arguments: InputFile - The name of the file containing answerfile data to be merged into memdb. Restricted - TRUE if Answer File Restrictions should be applied to the file, FALSE otherwise. Return Value: TRUE if the merge was successful, FALSE otherwise. --*/ HINF hInf = INVALID_HANDLE_VALUE; PTSTR sectionNames; PTSTR stringKey; PTSTR currentField; PTSTR workBuffer; POOLHANDLE pool = NULL; PTSTR firstValue; DWORD fieldCount; DWORD valueSectionIndex = 0; DWORD index; PTSTR currentSection; INFCONTEXT ic; BOOL b = FALSE; BOOL result = FALSE; __try { // // Make sure the answer file has not already been written to the disk. // if (g_AnswerFileAlreadyWritten) { LOG ((LOG_ERROR,"Answer file has already been written to disk.")); SetLastError (ERROR_SUCCESS); __leave; } pool = PoolMemInitNamedPool ("SIF Merge"); if (!pool) { __leave; } sectionNames = (PTSTR) PoolMemGetMemory (pool, SECTION_NAME_SIZE * sizeof (TCHAR)); stringKey = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR)); currentField = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR)); workBuffer = (PTSTR) PoolMemGetMemory (pool, MEMDB_MAX * sizeof (TCHAR)); if (!sectionNames || !stringKey || !currentField || !workBuffer) { __leave; } GetPrivateProfileSectionNames (sectionNames, SECTION_NAME_SIZE, InputFile); hInf = InfOpenInfFile (InputFile); if (hInf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Can't open %s for merge", InputFile)); __leave; } // // Loop through each section name, get each line and add it // for (currentSection = sectionNames ; *currentSection ; currentSection = GetEndOfString (currentSection) + 1) { // // Get each line in the section and add it to memory // if (SetupFindFirstLine (hInf, currentSection, NULL, &ic)) { do { fieldCount = SetupGetFieldCount(&ic); // // Get the StringKey // SetupGetStringField(&ic,0,stringKey,MEMDB_MAX,NULL); b = SetupGetStringField(&ic,1,currentField,MEMDB_MAX,NULL); firstValue = currentField; // // If key and value are the same, we might not have an equals. // Due to setupapi limitations, we have to use another INF // parser to determine this case. // if (StringMatch (stringKey, firstValue)) { if (!GetPrivateProfileString ( currentSection, firstValue, TEXT(""), workBuffer, MEMDB_MAX, InputFile )) { valueSectionIndex = WriteInfKey (currentSection, NULL, currentField); DEBUGMSG_IF ((!valueSectionIndex, DBG_ERROR, "AnswerFile: WriteInfKey returned 0")); continue; } } if (!Restricted || !pRestrictedKey(currentSection,stringKey)) { // // Write the first line first, remember the valuesectionid from it. // if (b) { valueSectionIndex = WriteInfKey(currentSection,stringKey,firstValue); DEBUGMSG_IF((!valueSectionIndex,DBG_ERROR,"AnswerFile: WriteInfKey returned 0...")); } for (index = 2;index <= fieldCount;index++) { b = SetupGetStringField(&ic,index,currentField,MEMDB_MAX,NULL); if (b) { WriteInfKeyEx(currentSection,stringKey,currentField,valueSectionIndex,FALSE); } } // // Kludge to make sure that we respect the JoinWorkgroup/JoinDomain specified in the unattend // file over what we may have already written. // if (StringIMatch (currentSection, S_PAGE_IDENTIFICATION) && StringIMatch (stringKey, S_JOINDOMAIN)) { WriteInfKey (currentSection, S_JOINWORKGROUP, TEXT("")); } else if (StringIMatch (currentSection, S_PAGE_IDENTIFICATION) && StringIMatch (stringKey, S_JOINWORKGROUP)) { WriteInfKey (currentSection, S_JOINDOMAIN, TEXT("")); } } ELSE_DEBUGMSG((DBG_VERBOSE,"AnswerFile: Not merging restricted key %s",stringKey)); } while (b && SetupFindNextLine (&ic, &ic)); if (!b) { LOG (( LOG_ERROR, "An error occured merging the section [%s] from %s. " "Some settings from this section may have been lost.", currentSection, InputFile )); __leave; } } } result = TRUE; } __finally { PushError(); if (hInf != INVALID_HANDLE_VALUE) { InfCloseInfFile (hInf); } if (pool) { PoolMemDestroyPool (pool); } PopError(); } return result; } BOOL MergeMigrationDllInf ( IN PCTSTR InputFile ) /*++ Routine Description: MergeMigrationDllInf is responsible for merging information from a migration dll supplied answer file into the memdb based answer file being maintained. This is done by first ensuring that the answer file restrictions have been initialized and then merging in the data using those restrictions. Arguments: InputFile - A String containing the name of the file to merge into the answer file being maintained. Return Value: TRUE if the merge was successful, FALSE otherwise. --*/ { static BOOL initialized = FALSE; if (!initialized) { INFCONTEXT context; TCHAR section[MAX_TCHAR_PATH]; TCHAR key[MAX_TCHAR_PATH]; // // Add the contents of the unattended constraints section to // MemDb. // // // Each line is of the form
= where
need // not be unique and is a section key pattern that may contain // wildcard characters. // if (SetupFindFirstLine (g_Win95UpgInf, MEMDB_CATEGORY_UNATTENDRESTRICTRIONS, NULL, &context)) { do { if (SetupGetStringField (&context, 0, section, MAX_TCHAR_PATH, NULL) && SetupGetStringField (&context, 1, key, MAX_TCHAR_PATH, NULL)) { // // Add to Memdb. // MemDbSetValueEx( MEMDB_CATEGORY_UNATTENDRESTRICTRIONS, section, key, NULL, 0, NULL ); } ELSE_DEBUGMSG((DBG_WARNING,"BuildInf: SetupGetStringField failed.")); } while (SetupFindNextLine (&context, &context)); } ELSE_DEBUGMSG((DBG_WARNING,"BuildInf: No %s section in w95upg.inf.",MEMDB_CATEGORY_UNATTENDRESTRICTRIONS)); initialized = TRUE; } // // At this point, all restrictions are accounted for. Merge the file with // the memory based structure. // return pDoMerge(InputFile,TRUE); } BOOL MergeInf ( IN PCTSTR InputFile ) /*++ Routine Description: MergeInf opens an INF file using the Setup APIs, enumerates all the sections and merges the strings in the INF file with what is in memory . Arguments: InputFile - The path to the INF file. It is treated as an old-style INF. Return Value: TRUE if the file was read and merged successfully, or FALSE if an error occurred. Call GetLastError to get a failure error code. --*/ { return pDoMerge(InputFile,FALSE); } DWORD pWriteInfKey ( LPCTSTR Section, LPCTSTR Key, OPTIONAL LPCTSTR Value, OPTIONAL DWORD ValueSectionId, BOOL EnsureKeyIsUnique ) /*++ Routine Description: pWriteInfKey is responsible for adding an inf key to the memdb data being maintained for the answer file. Arguments: Section - A string containing the section to add the information to. Key - A string containing the key to add information under. If not specified, then Value represents the complete line text. Value - A String containing the value to add under the section/key. If not specified, Key will be removed. ValueSectionId - A DWORD offset that is used to add multiple values to the same key. 0 indicates no offset, and causes the old key to be overwritten, if it exists or a new key to be created, if it does not. pWriteInfKey returns this offset when successful. EnsureKeyIsUnique - TRUE if the key should be unique in memory, FALSE otherwise. This is used to create multiple keys in the answer file under the same section with the same name. Return Value: A valid offset if the call was successful, FALSE otherwise. ++*/ { BOOL b; TCHAR aKey[MEMDB_MAX]; TCHAR keySection[MEMDB_MAX]; TCHAR massagedSection[MEMDB_MAX]; DWORD testValue; TCHAR valueId[20]; TCHAR sequence[20]; static DWORD idSeed = 1; static DWORD seqSeed = 1; DWORD rSeed = 1; DWORD sequenceValue; BOOL keyFound; DWORD valueOffset; DWORD keyOffset; static DWORD uniqueNumber = 1; TCHAR uniqueKey[MEMDB_MAX]; PTSTR keyPtr = NULL; // // Guard against rouge parameters. // if (!Section || !*Section) { DEBUGMSG ((DBG_WHOOPS, "Missing Section or Key for SIF")); return 0; } if (!Key && !Value) { DEBUGMSG ((DBG_WHOOPS, "Missing Value or Key for SIF")); return 0; } if (Key && !*Key) { DEBUGMSG ((DBG_WHOOPS, "Empty key specified for SIF")); return 0; } // // ensure key/value do not have quotes. // if (Value && _tcschr (Value,TEXT('\"'))) { DEBUGMSG ((DBG_WHOOPS, "Quotes found in SIF value %s", Value)); return 0; } if (Key && _tcschr (Key, TEXT('\"'))) { DEBUGMSG ((DBG_WHOOPS, "Quotes found in SIF key %s", Key)); return 0; } if (g_AnswerFileAlreadyWritten) { DEBUGMSG ((DBG_WHOOPS, "Answer file has already been written to disk.")); rSeed = 0; } else { // // Make sure Key is not NULL // if (!Key) { Key = BUILDINF_NULLKEY_PREFIX; } // // Massage the section in case it has any wacks in it. // StringCopy (massagedSection,Section); pHandleWacks(massagedSection,REPLACE_WACKS); // // Ensure the key is unique, if requested. // if (EnsureKeyIsUnique) { // // Add the prefix on.. // wsprintf(uniqueKey,TEXT("%s%04X%s"),BUILDINF_UNIQUEKEY_PREFIX,ValueSectionId ? uniqueNumber - 1 : uniqueNumber,Key); if (!ValueSectionId) { uniqueNumber++; } keyPtr = uniqueKey; } else { keyPtr = (PTSTR) Key; } // // See if the key exists already. // wsprintf(keySection,S_ANSWERFILE_SECTIONMASK,massagedSection); MemDbBuildKey(aKey,keySection,keyPtr,NULL,NULL); keyFound = MemDbGetValue(aKey,&testValue); // // Prepare Id and Sequence strings, compute return Seed. // idSeed++; wsprintf(valueId,TEXT("%04x"),idSeed); if (keyFound) { sequenceValue = testValue; } else { sequenceValue = seqSeed++; } wsprintf(sequence,TEXT("%04x"),sequenceValue); // // Delete case // if (!Value) { MemDbBuildKey (aKey, MEMDB_CATEGORY_AF_SECTIONS, massagedSection, sequence, NULL); MemDbDeleteTree (aKey); return 0; } if (ValueSectionId && !keyFound) { LOG ((LOG_ERROR,"%u is not associated with %s.",testValue,aKey)); return 0; } if (!ValueSectionId) { // // This is not a continuation. Need to save the section and key into memdb. // if (keyFound) { // // Need to replace the key that already exists. // MemDbBuildKey(aKey,MEMDB_CATEGORY_AF_SECTIONS,massagedSection,sequence,NULL); MemDbDeleteTree(aKey); } // // Save away key. // b = MemDbSetValueEx( keySection, keyPtr, NULL, NULL, sequenceValue, &keyOffset ); if (!b) { DEBUGMSG((DBG_ERROR,"BuildInf: Unable to save key into MemDb.")); rSeed = 0; } else { // // Save away section // b = MemDbSetValueEx( MEMDB_CATEGORY_AF_SECTIONS, massagedSection, sequence, NULL, keyOffset, NULL ); if (!b) { DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value.")); rSeed = 0; } } } // // Add the value into the database. // b = MemDbSetValueEx( MEMDB_CATEGORY_AF_VALUES, Value, NULL, NULL, 0, &valueOffset ); if (!b) { DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value.")); rSeed = 0; } else { b = MemDbSetValueEx( MEMDB_CATEGORY_AF_SECTIONS, massagedSection, sequence, valueId, valueOffset, NULL ); if (!b) { DEBUGMSG((DBG_ERROR,"BuildInf: Unable to set value.")); rSeed = 0; } } } return rSeed; } /*++ Routine Description: WriteInfKeyEx and WriteInfKey are the external wrapper apis for pWriteInfKey. Each is used to add information to the memory based answer file being constructed. WriteInfKeyEx provides greateer control over how thiings are written to this file. Arguments: Section - A string containing the section to add the information to. Key - A string containing the key to add information under. Value - A String containing the value to add under the section/key. If not specified, Key will be removed. ValueSectionId - A DWORD offset that is used to add multiple values to the same key. 0 indicates no offset, and causes the old key to be overwritten, if it exists or a new key to be created, if it does not. pWriteInfKey returns this offset when successful. EnsureKeyIsUnique - TRUE if the key should be unique in memory, FALSE otherwise. This is used to create multiple keys in the answer file under the same section with the same name. Return Value: A valid offset, if the call was successful, 0 otherwise. --*/ DWORD WriteInfKeyEx ( PCTSTR Section, PCTSTR Key, OPTIONAL PCTSTR Value, OPTIONAL DWORD ValueSectionId, BOOL EnsureKeyIsUnique ) { return pWriteInfKey(Section,Key,Value,ValueSectionId,EnsureKeyIsUnique); } DWORD WriteInfKey ( IN PCTSTR Section, IN PCTSTR Key, OPTIONAL IN PCTSTR Value OPTIONAL ) { return pWriteInfKey(Section,Key,Value,0,FALSE); } #define S_WIN9XDEL_FILE TEXT("WIN9XDEL.TXT") #define S_WIN9XMOV_FILE TEXT("WIN9XMOV.TXT") BOOL pWriteDelAndMoveFiles ( VOID ) /*++ Routine Description: pWriteDelAndMoveFiles actually creates two files unrelated to the winnt.sif file. These files contain information on the files to be deleted during textmode and moved during textmode respectively. Because of the size of these sections, and due to certain answer file restrictions, these sections are no longer written into the winnt.sif file. They are processed seperately during textmode. Arguments: none. Return Value: TRUE if the files were created successfully, FALSE otherwise. --*/ { MEMDB_ENUMW e; WCHAR SrcFile[MEMDB_MAX]; WCHAR buffer[MEMDB_MAX]; HANDLE file = INVALID_HANDLE_VALUE; PTSTR fileString = NULL; PWSTR unicodeString = NULL; DWORD bytesWritten; ALL_FILEOPS_ENUMW OpEnum; UINT unused; DWORD Count; HASHTABLE fileTable = HtAllocW(); BOOL result = FALSE; MOVELISTW moveList = NULL; POOLHANDLE moveListPool = NULL; __try { // // Special code for netcfg.exe tool. Of course, in the real project, g_TempDir will never be null.. // (We would've exited long ago!!) // #ifdef DEBUG if (!g_TempDir) { return TRUE; } #endif moveListPool = PoolMemInitNamedPool ("Move List"); if (!moveListPool) { __leave; } moveList = AllocateMoveListW (moveListPool); if (!moveList) { __leave; } fileString = JoinPaths(g_TempDir,WINNT32_D_WIN9XDEL_FILE); file = CreateFile ( fileString, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); DeclareTemporaryFile(fileString); if (file == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR,"Error creating file %s.", fileString)); __leave; } // // Enumerate all TEXTMODE FileDel entries and add them to winnt.sif // if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_DELETE, NULL)) { do { // // For each file that should be deleted during textmode, // write it to the win9xdel.txt file. // HtAddStringW (fileTable, OpEnum.Path); Count++; if (!(Count % 128)) { TickProgressBar(); DEBUGLOGTIME (("pWriteDelAndMoveFiles: FILE_DELETE enum 128 files")); } if (CANCELLED()) { return TRUE; } //WriteFile (file, OpEnum.Path, ByteCountW (OpEnum.Path), &bytesWritten, NULL); //pWriteStrToFile(file, TEXT("\r\n"),TRUE); } while (EnumNextFileOpW (&OpEnum)); } if (!HtWriteToFile (fileTable, file, WRITE_UNICODE_HEADER)) { LOG ((LOG_ERROR,"Unable to write to win9xdel.txt.")); __leave; } // // Clean up resources. // CloseHandle(file); FreePathString(fileString); HtFree (fileTable); fileTable = NULL; // // Create WIN9XMOV.TXT file. // fileString = JoinPaths(g_TempDir,WINNT32_D_WIN9XMOV_FILE); file = CreateFile ( fileString, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); DeclareTemporaryFile(fileString); if (file == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR,"Error creating file %s.",fileString)); __leave; } // // Add all the files to be moved // if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_MOVE|OPERATION_TEMP_PATH, NULL)) { do { // // only take into account the first destination of a file // (when OpEnum.PropertyNum == 0) // all other destinations are not relevant for textmode move // if (OpEnum.PropertyValid && OpEnum.PropertyNum == 0) { InsertMoveIntoListW ( moveList, OpEnum.Path, OpEnum.Property ); Count++; if (!(Count % 256)) { TickProgressBar(); DEBUGLOGTIME (("pWriteDelAndMoveFiles: FILE_MOVE|TEMP_PATH enum 256 files")); } if (CANCELLED()) { return TRUE; } } } while (EnumNextFileOpW (&OpEnum)); } // // Enumerate all the SfTemp values and add them to the list of things to move. // if (MemDbGetValueExW (&e, MEMDB_CATEGORY_SF_TEMPW, NULL, NULL)) { do { if (MemDbBuildKeyFromOffsetW (e.dwValue, SrcFile, 1, NULL)) { InsertMoveIntoListW ( moveList, SrcFile, e.szName ); Count++; if (!(Count % 128)) { TickProgressBar(); DEBUGLOGTIME (("pWriteDelAndMoveFiles: MEMDB_CATEGORY_SF_TEMPW enum 128 files")); } if (CANCELLED()) { return TRUE; } } ELSE_DEBUGMSGW (( DBG_WHOOPS, "MemDbBuildKeyFromOffset: Cannot create key from offset %u of %s (2)", e.dwValue, e.szName )); } while (MemDbEnumNextValueW (&e)); } // // Enumerate all DirsCollision values and add them to the list of things to move. // if (MemDbGetValueExW (&e, MEMDB_CATEGORY_DIRS_COLLISIONW, NULL, NULL)) { do { if (EnumFirstFileOpW (&OpEnum, OPERATION_FILE_MOVE, e.szName)) { InsertMoveIntoListW ( moveList, e.szName, OpEnum.Property ); if (CANCELLED()) { return TRUE; } } //ELSE_DEBUGMSGW (( // DBG_WHOOPS, // "EnumFirstFileOpW: failed for FileSpec=%s", // e.szName // )); } while (MemDbEnumNextValueW (&e)); } moveList = RemoveMoveListOverlapW (moveList); if (!OutputMoveListW (file, moveList, FALSE)) { LOG ((LOG_ERROR,"Unable to write to win9xmov.txt.")); __leave; } CloseHandle(file); FreePathString(fileString); // // Finally, we need to write any 'absolutely make sure everything is deleted in this dir' dirs. // Textmode will blast away everything in the dir it finds. This *shouldn't* be needed, but // beta2 had a problem where there were some INFs left in %windir%\inf even after we had // supposedly enumerated all the files there and added them to the delete file. // As luck would have it, this was on a reviewer's machine... // // // Create W9XDDIR.TXT file. // fileString = JoinPaths(g_TempDir,WINNT32_D_W9XDDIR_FILE); file = CreateFile ( fileString, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); DeclareTemporaryFile(fileString); if (file == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR,"Error creating file %s.",fileString)); __leave; } WriteFile (file, "\xff\xfe", 2, &bytesWritten, NULL); // // Add any enumeration we want to do later. Right now, we only have the one dir to try. // FreePathString (fileString); fileString = JoinPaths (g_WinDir, TEXT("inf")); pWriteStrToFile (file, fileString, TRUE); // TRUE == convert to unicode pWriteStrToFile (file, "\r\n", TRUE); if (MemDbGetValueExW (&e, MEMDB_CATEGORY_FULL_DIR_DELETESW, NULL, NULL)) { do { if (!WriteFile (file, e.szName, ByteCountW (e.szName), &unused, NULL)) { LOG ((LOG_ERROR,"Unable to write to w9xddir.txt.")); __leave; } if (!WriteFile (file, L"\r\n", 4, &unused, NULL)) { LOG ((LOG_ERROR,"Unable to write to w9xddir.txt.")); __leave; } if (CANCELLED()) { return TRUE; } } while (MemDbEnumNextValueW (&e)); } result = TRUE; } __finally { // // Free resources. // if (file != INVALID_HANDLE_VALUE) { CloseHandle(file); } FreePathString(fileString); HtFree (fileTable); PoolMemDestroyPool (moveListPool); } return result; } DWORD CreateFileLists ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: if (REPORTONLY ()) { return 0; } else { return TICKS_CREATE_FILE_LISTS; } case REQUEST_RUN: ProgressBar_SetComponentById (MSG_PROCESSING_SYSTEM_FILES); pWriteDelAndMoveFiles (); ProgressBar_SetComponent (NULL); break; } return ERROR_SUCCESS; }