/*++ Copyright (c) 1997 Microsoft Corporation Module Name: fileops.c Abstract: This file implements routines that manage the operations on files. Callers can set and remove operations on any path. The operations can have optional properties. The operation combinations and the number of properties are well-defined, so that potential collisions can be found during testing. Author: Jim Schmidt (jimschm) 18-Jul-1997 Revision History: jimschm 26-Aug-1998 Redesigned!! Consolidated functionality into generic linkage: path<->operation(s)->attrib(s) jimschm 24-Aug-1998 Added shell folder support jimschm 01-May-1998 Added handled directory to GetFileStatusOnNt calinn 21-Apr-1998 added AddCompatibleShell, AddCompatibleRunKey and AddCompatibleDos calinn 02-Apr-1998 added DeclareTemporaryFile calinn 18-Jan-1998 added MigrationPhase_AddCompatibleFile turned off warning in MigrationPhase_CreateFile modified MigrationPhase_DeleteFile and MigrationPhase_MoveFile modified GetFileInfoOnNt for short file names calinn 05-Jan-1998 added IsFileMarkedForAnnounce, AnnounceFileInReport, GetFileInfoOnNt, GetFileStatusOnNt, GetPathStringOnNt --*/ #include "pch.h" #define DBG_MEMDB "MemDb" #define FO_ENUM_BEGIN 0 #define FO_ENUM_BEGIN_PATH_ENUM 1 #define FO_ENUM_BEGIN_PROP_ENUM 2 #define FO_ENUM_RETURN_PATH 3 #define FO_ENUM_RETURN_DATA 4 #define FO_ENUM_NEXT_PROP 5 #define FO_ENUM_NEXT_PATH 6 #define FO_ENUM_END 7 // //140 - header for compresion file, 10 + 2 timestamp + //MAX_PATH file name in Unicode // #define STARTUP_INFORMATION_BYTES_NUMBER (140 + (sizeof(WCHAR) * MAX_PATH) + 26) #define COMPRESSION_RATE_DEFAULT 70 #define BACKUP_DISK_SPACE_PADDING_DEFAULT (5<<20) #define UNKNOWN_DRIVE '?' PCWSTR g_CurrentUser = NULL; BOOL pGetPathPropertyW ( IN PCWSTR FileSpec, IN DWORD Operations, IN DWORD Property, OUT PWSTR PropertyBuf OPTIONAL ); BOOL pIsFileMarkedForOperationW ( IN PCWSTR FileSpec, IN DWORD Operations ); UINT pAddOperationToPathW ( IN PCWSTR FileSpec, IN OPERATION Operation, IN BOOL Force, IN BOOL AlreadyLong ); VOID pFileOpsSetPathTypeW ( IN PCWSTR LongFileSpec ) { WCHAR ShortFileSpec[MAX_WCHAR_PATH]; WCHAR LongFileSpecCopy[MAX_WCHAR_PATH]; WCHAR MixedFileSpec[MAX_WCHAR_PATH]; PWSTR p; PWSTR LongStart, LongEnd; PWSTR ShortStart, ShortEnd; PWSTR MixedFileName; WCHAR ch; // // Make sure the file spec is marked as a long path // if (!pIsFileMarkedForOperationW (LongFileSpec, OPERATION_LONG_FILE_NAME)) { pAddOperationToPathW (LongFileSpec, OPERATION_LONG_FILE_NAME, FALSE, TRUE); // // Obtain the short path, and if it is different than the // long path, add an operation for it. // if (OurGetShortPathNameW (LongFileSpec, ShortFileSpec, MAX_WCHAR_PATH)) { if (!StringIMatchW (LongFileSpec, ShortFileSpec)) { // // The short and long paths differ, so record the short path. // if (!pIsFileMarkedForOperationW (ShortFileSpec, OPERATION_SHORT_FILE_NAME)) { AssociatePropertyWithPathW ( ShortFileSpec, OPERATION_SHORT_FILE_NAME, LongFileSpec ); } // // Make sure each short piece of the file spec is added. This // allows us to support mixed short and long paths. It is // critical that we have the long path with a short file name. // _wcssafecpy (LongFileSpecCopy, LongFileSpec, sizeof (LongFileSpecCopy)); LongStart = LongFileSpecCopy; ShortStart = ShortFileSpec; MixedFileName = MixedFileSpec; while (*LongStart && *ShortStart) { LongEnd = wcschr (LongStart, L'\\'); if (!LongEnd) { LongEnd = GetEndOfStringW (LongStart); } ShortEnd = wcschr (ShortStart, L'\\'); if (!ShortEnd) { ShortEnd = GetEndOfStringW (ShortStart); } StringCopyABW (MixedFileName, ShortStart, ShortEnd); if (!StringIMatchABW (MixedFileName, LongStart, LongEnd)) { if (!pIsFileMarkedForOperationW (MixedFileSpec, OPERATION_SHORT_FILE_NAME)) { ch = *LongEnd; *LongEnd = 0; AssociatePropertyWithPathW ( MixedFileSpec, OPERATION_SHORT_FILE_NAME, LongFileSpecCopy ); *LongEnd = ch; } StringCopyABW (MixedFileName, LongStart, LongEnd); } p = MixedFileName + (LongEnd - LongStart); *p = L'\\'; MixedFileName = p + 1; LongStart = LongEnd; if (*LongStart) { LongStart++; } // skip paths that have double-wacks while (*LongStart == L'\\') { LongStart++; } ShortStart = ShortEnd; if (*ShortStart) { ShortStart++; } } MYASSERT (!*LongStart && !*ShortStart); } } } } VOID pFileOpsGetLongPathW ( IN PCWSTR FileSpec, OUT PWSTR LongFileSpec ) { WCHAR Replacement[MEMDB_MAX]; PCWSTR MixedStart, MixedEnd; PWSTR OutStr; UINT u; // // Get the short property from the long property // if (!pIsFileMarkedForOperationW (FileSpec, OPERATION_LONG_FILE_NAME)) { if (!pGetPathPropertyW (FileSpec, OPERATION_SHORT_FILE_NAME, 0, LongFileSpec)) { // // The short and long properties aren't there. Try each piece. // MixedStart = FileSpec; OutStr = LongFileSpec; while (*MixedStart) { MixedEnd = wcschr (MixedStart, L'\\'); if (!MixedEnd) { MixedEnd = GetEndOfStringW (MixedStart); } if (OutStr != LongFileSpec) { *OutStr++ = L'\\'; } StringCopyABW (OutStr, MixedStart, MixedEnd); if (pGetPathPropertyW (LongFileSpec, OPERATION_SHORT_FILE_NAME, 0, Replacement)) { u = OutStr - LongFileSpec; MYASSERT (StringIMatchTcharCountW (LongFileSpec, Replacement, u)); StringCopyW (LongFileSpec + u, Replacement + u); } OutStr = GetEndOfStringW (OutStr); MixedStart = MixedEnd; if (*MixedStart) { MixedStart++; } } *OutStr = 0; } } else { StringCopyW (LongFileSpec, FileSpec); } } PCSTR GetSourceFileLongNameA ( IN PCSTR ShortName ) { PCWSTR UShortName; PCWSTR ULongName; PCSTR ALongName; PCSTR LongName; UShortName = ConvertAtoW (ShortName); ULongName = GetSourceFileLongNameW (UShortName); ALongName = ConvertWtoA (ULongName); LongName = DuplicatePathStringA (ALongName, 0); FreeConvertedStr (ALongName); FreePathString (ULongName); FreeConvertedStr (UShortName); return LongName; } PCWSTR GetSourceFileLongNameW ( IN PCWSTR ShortName ) { WCHAR LongName[MEMDB_MAX]; pFileOpsGetLongPathW (ShortName, LongName); return (DuplicatePathStringW (LongName, 0)); } PCWSTR SetCurrentUserW ( PCWSTR User ) { PCWSTR tempUser = g_CurrentUser; g_CurrentUser = User; return tempUser; } DWORD g_MasterSequencer = 0; #define ONEBITSET(x) ((x) && !((x) & ((x) - 1))) typedef struct { DWORD Bit; PCSTR Name; DWORD SharedOps; UINT MaxProps; } OPERATIONFLAGS, *POPERATIONFLAGS; #define UNLIMITED 0xffffffff #define DEFMAC(bit,name,memdbname,maxattribs) {bit,#memdbname,0,maxattribs}, OPERATIONFLAGS g_OperationFlags[] = { PATH_OPERATIONS /* , */ {0, NULL, 0, 0} }; #undef DEFMAC UINT pWhichBitIsSet ( OPERATION Value ) { UINT Bit = 0; MYASSERT (ONEBITSET(Value)); while (Value /= 2) { Bit++; } MYASSERT (Bit < 24); return Bit; } VOID pProhibitOperationCombination ( IN DWORD SourceOperations, IN DWORD ProhibitedOperations ) { DWORD w1, w2; OPERATION OperationA; OPERATION OperationB; for (w1 = SourceOperations ; w1 ; w1 ^= OperationA) { OperationA = w1 & (~(w1 - 1)); g_OperationFlags[pWhichBitIsSet (OperationA)].SharedOps &= ~ProhibitedOperations; for (w2 = ProhibitedOperations ; w2 ; w2 ^= OperationB) { OperationB = w2 & (~(w2 - 1)); g_OperationFlags[pWhichBitIsSet (OperationB)].SharedOps &= ~OperationA; } } } VOID InitOperationTable ( VOID ) /*++ Routine Description: InitOperationsTable sets the prohibited operation mask for each operation. When an operation combination is prohibited, both operations involved have the corresponding bit cleared. Arguments: None. Return Value: None. --*/ { POPERATIONFLAGS p; for (p = g_OperationFlags ; p->Name ; p++) { p->SharedOps = ALL_OPERATIONS; } // // Please try to keep this in the same order as the // macro expansion list in fileops.h. The list of // prohibited operations should get smaller as // we go. // pProhibitOperationCombination ( OPERATION_FILE_DELETE, OPERATION_TEMP_PATH ); pProhibitOperationCombination ( OPERATION_FILE_DELETE, OPERATION_FILE_MOVE| OPERATION_FILE_MOVE_EXTERNAL| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_FILE_COPY| OPERATION_CLEANUP| OPERATION_MIGDLL_HANDLED| OPERATION_LINK_EDIT| OPERATION_LINK_STUB ); pProhibitOperationCombination ( OPERATION_FILE_DELETE_EXTERNAL, OPERATION_FILE_MOVE| OPERATION_FILE_MOVE_EXTERNAL| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_FILE_COPY| OPERATION_CLEANUP| OPERATION_LINK_EDIT| OPERATION_LINK_STUB ); pProhibitOperationCombination ( OPERATION_FILE_MOVE, OPERATION_FILE_MOVE| OPERATION_FILE_COPY| OPERATION_FILE_MOVE_EXTERNAL| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_FILE_MOVE_BY_NT| OPERATION_CLEANUP| OPERATION_MIGDLL_HANDLED| OPERATION_CREATE_FILE| OPERATION_TEMP_PATH ); pProhibitOperationCombination ( OPERATION_FILE_COPY, OPERATION_FILE_COPY| OPERATION_FILE_MOVE_EXTERNAL| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_CLEANUP| OPERATION_MIGDLL_HANDLED ); pProhibitOperationCombination ( OPERATION_FILE_MOVE_EXTERNAL, OPERATION_FILE_MOVE_EXTERNAL| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_FILE_MOVE_BY_NT| OPERATION_CLEANUP ); pProhibitOperationCombination ( OPERATION_FILE_MOVE_SHELL_FOLDER, OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_FILE_MOVE_BY_NT| OPERATION_CLEANUP| OPERATION_MIGDLL_HANDLED| OPERATION_CREATE_FILE| OPERATION_TEMP_PATH ); pProhibitOperationCombination ( OPERATION_FILE_MOVE_BY_NT, OPERATION_FILE_MOVE_BY_NT ); pProhibitOperationCombination ( OPERATION_CLEANUP, OPERATION_MIGDLL_HANDLED| OPERATION_CREATE_FILE| OPERATION_LINK_EDIT| OPERATION_LINK_STUB ); pProhibitOperationCombination ( OPERATION_MIGDLL_HANDLED, OPERATION_MIGDLL_HANDLED| OPERATION_CREATE_FILE| OPERATION_LINK_EDIT| OPERATION_LINK_STUB ); pProhibitOperationCombination ( OPERATION_LINK_EDIT, OPERATION_LINK_EDIT ); pProhibitOperationCombination ( OPERATION_LINK_STUB, OPERATION_LINK_STUB ); pProhibitOperationCombination ( OPERATION_SHELL_FOLDER, OPERATION_SHELL_FOLDER ); pProhibitOperationCombination ( OPERATION_SHORT_FILE_NAME, OPERATION_SHORT_FILE_NAME ); } VOID pBuildOperationCategory ( IN PWSTR Node, IN UINT OperationNum ) { // IMPORTANT: wsprintfW is buggy and does not always work with %hs, the use of // swprintf is intentional swprintf (Node, L"%hs", g_OperationFlags[OperationNum].Name); } VOID pBuildOperationKey ( IN PWSTR Node, IN UINT OperationNum, IN UINT Sequencer ) { // IMPORTANT: wsprintfW is buggy and does not always work with %hs, the use of // swprintf is intentional swprintf (Node, L"%hs\\%x", g_OperationFlags[OperationNum].Name, Sequencer); } VOID pBuildPropertyKey ( IN PWSTR Node, IN UINT OperationNum, IN UINT Sequencer, IN DWORD Property ) { // IMPORTANT: wsprintfW is buggy and does not always work with %hs, the use of // swprintf is intentional swprintf (Node, L"%hs\\%x\\%x", g_OperationFlags[OperationNum].Name, Sequencer, Property); } BOOL CanSetOperationA ( IN PCSTR FileSpec, IN OPERATION Operation ) { PCWSTR UnicodeFileSpec; BOOL result; UnicodeFileSpec = ConvertAtoW (FileSpec); result = CanSetOperationW (UnicodeFileSpec, Operation); FreeConvertedStr (UnicodeFileSpec); return result; } BOOL CanSetOperationW ( IN PCWSTR FileSpec, IN OPERATION Operation ) { WCHAR LongFileSpec[MEMDB_MAX]; WCHAR Node[MEMDB_MAX]; DWORD Flags; UINT SetBitNum; MYASSERT (ONEBITSET (Operation)); pFileOpsGetLongPathW (FileSpec, LongFileSpec); // // Get existing sequencer and flags, if they exist // MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, LongFileSpec, NULL, NULL); if (!MemDbGetValueAndFlagsW (Node, NULL, &Flags)) { return TRUE; } SetBitNum = pWhichBitIsSet (Operation); return ((Flags & g_OperationFlags[SetBitNum].SharedOps) == Flags); } BOOL pSetPathOperationW ( IN PCWSTR FileSpec, OUT PDWORD Offset, OUT PUINT SequencerPtr, IN OPERATION SetBit, IN OPERATION ClrBit, IN BOOL Force ) /*++ Routine Description: pSetPathOperation adds the operation bit to the specified path. It also verifies that the operation combination is legal. Arguments: FileSpec - Specifies the path the operation applies to. Offset - Receives the offset of the memdb key created for the path. SequencePtr - Receives the operation sequence number, used for property linkage. SetBit - Specifies one operation bit to set. ClrBit - Specifies one operation bit to clear. Either SetBit or ClrBit can be used, but not both. Return Value: TRUE if the operation was set, FALSE otherwise. --*/ { DWORD Sequencer; WCHAR Node[MEMDB_MAX]; DWORD Flags; UINT SetBitNum; MYASSERT ((SetBit && !ClrBit) || (ClrBit && !SetBit)); MYASSERT (ONEBITSET (SetBit | ClrBit)); // // Get existing sequencer and flags, if they exist // MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, FileSpec, NULL, NULL); if (!MemDbGetValueAndFlagsW (Node, &Sequencer, &Flags) || !Flags) { Flags = 0; if (!g_MasterSequencer && ISNT()) { if (!MemDbGetValue ( MEMDB_CATEGORY_STATE TEXT("\\") MEMDB_ITEM_MASTER_SEQUENCER, &g_MasterSequencer )) { g_MasterSequencer = 1 << 24; } } g_MasterSequencer++; Sequencer = g_MasterSequencer; MYASSERT (Sequencer); } // // Is bit adjustment legal? // if (SetBit) { SetBitNum = pWhichBitIsSet (SetBit); #ifdef DEBUG { PSTR p; PCSTR DebugInfPath; CHAR DbgBuf[32]; BOOL Break = FALSE; PCSTR AnsiFileSpec; DebugInfPath = JoinPathsA (g_BootDrivePathA, "debug.inf"); AnsiFileSpec = ConvertWtoA (FileSpec); p = _mbsrchr (AnsiFileSpec, L'\\'); p++; if (GetPrivateProfileStringA ("FileOps", AnsiFileSpec, "", DbgBuf, 32, DebugInfPath)) { Break = TRUE; } else if (GetPrivateProfileStringA ("FileOps", p, "", DbgBuf, 32, DebugInfPath)) { Break = TRUE; } if (Break) { if ((SetBit & strtoul (DbgBuf, NULL, 16)) == 0) { Break = FALSE; } } if (Break) { DEBUGMSG (( DBG_WHOOPS, "File %ls now being marked for operation %hs", FileSpec, g_OperationFlags[SetBitNum].Name )); } FreePathStringA (DebugInfPath); FreeConvertedStr (AnsiFileSpec); } #endif if (!Force) { if ((Flags & g_OperationFlags[SetBitNum].SharedOps) != Flags) { DEBUGMSG (( DBG_WHOOPS, "File %ls already marked, %hs cannot be combined with 0x%04X", FileSpec, g_OperationFlags[SetBitNum].Name, Flags )); return FALSE; } } } // // Adjust the bits // Flags |= SetBit; Flags &= ~ClrBit; // // Save // MemDbSetValueAndFlagsW (Node, Sequencer, Flags, 0); MemDbGetOffsetW (Node, Offset); *SequencerPtr = Sequencer; return TRUE; } UINT pAddOperationToPathW ( IN PCWSTR FileSpec, IN OPERATION Operation, IN BOOL Force, IN BOOL AlreadyLong ) /*++ Routine Description: pAddOperationToPath adds an operation to a path. The caller receives a sequencer so additional properties can be added. Arguments: FileSpec - Specifies the path to add the operation to Operation - Specifies the operation to add Force - Specifies TRUE if the operation combinations should be ignored. This is only for special-case use. AlreadyLong - Specifies TRUE if FileSpec is a long path, FALSE otherwise. Return Value: A sequencer that can be used to add properties, or INVALID_SEQUENCER if an error occured. --*/ { UINT OperationNum; UINT Sequencer; WCHAR Node[MEMDB_MAX]; DWORD Offset; WCHAR LongFileSpec[MAX_WCHAR_PATH]; if (!FileSpec || FileSpec[0] == 0) { return INVALID_SEQUENCER; } // // Make sure FileSpec is in long format and is recorded in memdb // if (Operation != OPERATION_SHORT_FILE_NAME && Operation != OPERATION_LONG_FILE_NAME ) { if (!AlreadyLong) { MYASSERT (ISNT()); if (FileSpec[0] && (FileSpec[1]==L':')) { if (OurGetLongPathNameW (FileSpec, LongFileSpec, MAX_WCHAR_PATH)) { FileSpec = LongFileSpec; } } } pFileOpsSetPathTypeW (FileSpec); } // // Create the path sequencer and set the operation bit // MYASSERT (ONEBITSET(Operation)); #ifdef DEBUG Offset = INVALID_OFFSET; #endif if (!pSetPathOperationW (FileSpec, &Offset, &Sequencer, Operation, 0, Force)) { return INVALID_SEQUENCER; } MYASSERT (Offset != INVALID_OFFSET); // // Add the opereration // OperationNum = pWhichBitIsSet (Operation); pBuildOperationKey (Node, OperationNum, Sequencer); if (!MemDbGetValueW (Node, NULL)) { MemDbSetValueW (Node, Offset); } return Sequencer; } UINT AddOperationToPathA ( IN PCSTR FileSpec, IN OPERATION Operation ) { PCWSTR UnicodeFileSpec; UINT u; CHAR longFileSpec[MAX_MBCHAR_PATH]; CopyFileSpecToLongA (FileSpec, longFileSpec); UnicodeFileSpec = ConvertAtoW (longFileSpec); u = pAddOperationToPathW (UnicodeFileSpec, Operation, FALSE, TRUE); FreeConvertedStr (UnicodeFileSpec); return u; } UINT AddOperationToPathW ( IN PCWSTR FileSpec, IN OPERATION Operation ) { if (!ISNT()) { #ifdef DEBUG // // If we are calling the W version on Win9x, then we know // that the path is long. Otherwise the caller must call // the A version. // { PCSTR ansiFileSpec; CHAR longFileSpec[MAX_MBCHAR_PATH]; PCWSTR unicodeFileSpec; ansiFileSpec = ConvertWtoA (FileSpec); CopyFileSpecToLongA (ansiFileSpec, longFileSpec); FreeConvertedStr (ansiFileSpec); unicodeFileSpec = ConvertAtoW (longFileSpec); MYASSERT (StringIMatchW (FileSpec, unicodeFileSpec)); FreeConvertedStr (unicodeFileSpec); } #endif return pAddOperationToPathW (FileSpec, Operation, FALSE, TRUE); } return pAddOperationToPathW (FileSpec, Operation, FALSE, FALSE); } UINT ForceOperationOnPathA ( IN PCSTR FileSpec, IN OPERATION Operation ) { PCWSTR UnicodeFileSpec; UINT u; CHAR longFileSpec[MAX_MBCHAR_PATH]; CopyFileSpecToLongA (FileSpec, longFileSpec); UnicodeFileSpec = ConvertAtoW (longFileSpec); u = pAddOperationToPathW (UnicodeFileSpec, Operation, TRUE, TRUE); FreeConvertedStr (UnicodeFileSpec); return u; } UINT ForceOperationOnPathW ( IN PCWSTR FileSpec, IN OPERATION Operation ) /*++ Routine Description: ForceOperationOnPath is used only in special cases where the caller knows that a normally prohibited operation combination is OK. This is usually because Path was somehow changed from its original state, yet the operations cannot be removed via RemoveOperationsFromPath. This function should only be used if absolutely necessary. Arguments: FileSpec - Specifies the path to add the operation to. Operation - Specifies the single operation to add to the path. Return Value: A sequencer that can be used to add properties to the path. --*/ { return pAddOperationToPathW (FileSpec, Operation, TRUE, FALSE); } BOOL AddPropertyToPathExA ( IN UINT Sequencer, IN OPERATION Operation, IN PCSTR Property, IN PCSTR AlternateDataSection OPTIONAL ) { PCWSTR UnicodeProperty; PCWSTR UnicodeAlternateDataSection; BOOL b; UnicodeProperty = ConvertAtoW (Property); UnicodeAlternateDataSection = ConvertAtoW (AlternateDataSection); b = AddPropertyToPathExW ( Sequencer, Operation, UnicodeProperty, UnicodeAlternateDataSection ); FreeConvertedStr (UnicodeProperty); FreeConvertedStr (UnicodeAlternateDataSection); return b; } BOOL AddPropertyToPathExW ( IN UINT Sequencer, IN OPERATION Operation, IN PCWSTR Property, IN PCWSTR AlternateDataSection OPTIONAL ) { /*++ Routine Description: AddPropertyToPathEx adds an operation to a path, and then adds a property. The caller can also specify an alternate data section (for special-case uses). Arguments: Sequencer - Specifies the sequencer of the path to add operations and properties to Operation - Specifies the operation to add Property - Specfieis the property data to add AlternateDataSection - Specifies an alternate memdb root for the property data Return Value: TRUE if the operation was added, FALSE otherwise. --*/ DWORD DataOffset; WCHAR Node[MEMDB_MAX]; UINT OperationNum; DWORD UniqueId; DWORD PathOffset; DWORD DataValue; DWORD DataFlags; // // Verify the sequencer and operation are valid // OperationNum = pWhichBitIsSet (Operation); pBuildOperationKey (Node, OperationNum, Sequencer); if (!MemDbGetValueAndFlagsW (Node, &PathOffset, &UniqueId)) { DEBUGMSG ((DBG_WHOOPS, "Can't set property on non-existent operation")); return FALSE; } // // Can this operation have another property? // if (UniqueId == g_OperationFlags[OperationNum].MaxProps) { DEBUGMSG (( DBG_WHOOPS, "Maximum properties specified for %hs (property %ls)", g_OperationFlags[OperationNum].Name, Property )); return FALSE; } // // Increment the unique ID // MemDbSetValueAndFlagsW (Node, PathOffset, (DWORD) (UniqueId + 1), 0); // // Get the existing data value and flags, preserving them // if they exist // if (!AlternateDataSection) { AlternateDataSection = MEMDB_CATEGORY_DATAW; } swprintf (Node, L"%s\\%s", AlternateDataSection, Property); if (!MemDbGetValueAndFlagsW (Node, &DataValue, &DataFlags)) { DataValue = 0; DataFlags = 0; } // // Write the data section node and get the offset // MemDbSetValueAndFlagsW (Node, DataValue, DataFlags, 0); MemDbGetOffsetW (Node, &DataOffset); // // Write the operation node // pBuildPropertyKey (Node, OperationNum, Sequencer, UniqueId); MemDbSetValueW (Node, DataOffset); return TRUE; } BOOL pAssociatePropertyWithPathW ( IN PCWSTR FileSpec, IN OPERATION Operation, IN PCWSTR Property, IN BOOL AlreadyLong ) /*++ Routine Description: AssociatePropertyWithPath adds a property to a path operation. The maximum property count is enforced. Arguments: FileSpec - Specifies the path to add an operation and property Operation - Specifies the operation to add Property - Specifies the property data to associate with FileSpec AlreadyLong - Specifies TRUE if FileSpec is a long path name, FALSE otherwise Return Value: TRUE if the operation and property was added, FALSE otherwise. It is possible that the operation will be added but the property will not. --*/ { UINT Sequencer; Sequencer = pAddOperationToPathW (FileSpec, Operation, FALSE, AlreadyLong); if (Sequencer == INVALID_SEQUENCER) { DEBUGMSG ((DBG_WHOOPS, "Can't associate %s with %s", Property, FileSpec)); return FALSE; } // // BUGBUG - When the below fails, we need to reverse the pAddOperationToPathW // call above // return AddPropertyToPathExW (Sequencer, Operation, Property, NULL); } BOOL AssociatePropertyWithPathA ( IN PCSTR FileSpec, IN OPERATION Operation, IN PCSTR Property ) { PCWSTR UnicodeFileSpec; PCWSTR UnicodeProperty; BOOL b; CHAR longFileSpec[MAX_MBCHAR_PATH]; CopyFileSpecToLongA (FileSpec, longFileSpec); UnicodeFileSpec = ConvertAtoW (longFileSpec); UnicodeProperty = ConvertAtoW (Property); b = pAssociatePropertyWithPathW (UnicodeFileSpec, Operation, UnicodeProperty, TRUE); FreeConvertedStr (UnicodeFileSpec); FreeConvertedStr (UnicodeProperty); return b; } BOOL AssociatePropertyWithPathW ( IN PCWSTR FileSpec, IN OPERATION Operation, IN PCWSTR Property ) { return pAssociatePropertyWithPathW (FileSpec, Operation, Property, FALSE); } UINT GetSequencerFromPathA ( IN PCSTR FileSpec ) { PCWSTR UnicodeFileSpec; UINT u; UnicodeFileSpec = ConvertAtoW (FileSpec); u = GetSequencerFromPathW (UnicodeFileSpec); FreeConvertedStr (UnicodeFileSpec); return u; } UINT GetSequencerFromPathW ( IN PCWSTR FileSpec ) /*++ Routine Description: GetSequencerFromPath returns the sequencer for a particular path. The path must have at least one operation. Arguments: FileSpec - Specifies the path to get the sequencer for. Return Value: The sequencer for the path, or INVALID_SEQUENCER if there are no operationf for the path. --*/ { WCHAR LongFileSpec[MEMDB_MAX]; WCHAR Node[MEMDB_MAX]; DWORD Sequencer; pFileOpsGetLongPathW (FileSpec, LongFileSpec); MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, LongFileSpec, NULL, NULL); if (!MemDbGetValueW (Node, &Sequencer)) { return INVALID_SEQUENCER; } return (UINT) Sequencer; } BOOL GetPathFromSequencerA ( IN UINT Sequencer, OUT PSTR PathBuf ) { WCHAR UnicodePathBuf[MAX_WCHAR_PATH]; BOOL b; b = GetPathFromSequencerW (Sequencer, UnicodePathBuf); if (b) { KnownSizeWtoA (PathBuf, UnicodePathBuf); } return b; } BOOL GetPathFromSequencerW ( IN UINT Sequencer, OUT PWSTR PathBuf ) /*++ Routine Description: GetPathFromSequencer returns the path from the specified sequencer. Arguments: Sequencer - Specifies the sequencer of the path. PathBuf - Receives the path. The caller must make sure the buffer is big enough for the path. Return Value: TRUE if the path was copied to PathBuf, FALSE otherwise. --*/ { WCHAR Node[MEMDB_MAX]; DWORD PathOffset = 0; DWORD w; UINT u; BOOL b = FALSE; // // Search all operations for sequencer // for (w = 1, u = 0 ; g_OperationFlags[u].Name ; w <<= 1, u++) { pBuildOperationKey (Node, u, Sequencer); if (MemDbGetValueW (Node, &PathOffset)) { break; } } // // For the first match found, use the offset to find the path // if (w) { b = MemDbBuildKeyFromOffsetW (PathOffset, PathBuf, 1, NULL); } return b; } VOID RemoveOperationsFromSequencer ( IN UINT Sequencer, IN DWORD Operations ) /*++ Routine Description: RemoveOperationsFromSequencer removes all operation bits from the specified path. It does not however remove the properties; they become abandoned. Arguments: Sequencer - Specifies the sequencer for the path to remove operations from Operations - Specifies one or more operations to remove Return Value: None. --*/ { WCHAR Node[MEMDB_MAX]; UINT u; DWORD PathOffset; DWORD PathSequencer; for (u = 0 ; g_OperationFlags[u].Name ; u++) { if (!(Operations & g_OperationFlags[u].Bit)) { continue; } pBuildOperationKey (Node, u, Sequencer); if (MemDbGetValueW (Node, &PathOffset)) { // // Delete linkage from operation to properties // MemDbDeleteTreeW (Node); // // Remove operation bits // MemDbBuildKeyFromOffsetExW ( PathOffset, Node, NULL, 0, &PathSequencer, NULL ); MYASSERT (PathSequencer == Sequencer); MemDbSetValueAndFlagsW (Node, PathSequencer, 0, Operations); } } } VOID RemoveOperationsFromPathA ( IN PCSTR FileSpec, IN DWORD Operations ) { PCWSTR UnicodeFileSpec; UnicodeFileSpec = ConvertAtoW (FileSpec); RemoveOperationsFromPathW (UnicodeFileSpec, Operations); FreeConvertedStr (UnicodeFileSpec); } VOID RemoveOperationsFromPathW ( IN PCWSTR FileSpec, IN DWORD Operations ) { UINT Sequencer; Sequencer = GetSequencerFromPathW (FileSpec); if (Sequencer != INVALID_SEQUENCER) { RemoveOperationsFromSequencer (Sequencer, Operations); } } BOOL IsFileMarkedForOperationA ( IN PCSTR FileSpec, IN DWORD Operations ) { PCWSTR UnicodeFileSpec; BOOL b; UnicodeFileSpec = ConvertAtoW (FileSpec); b = IsFileMarkedForOperationW (UnicodeFileSpec, Operations); FreeConvertedStr (UnicodeFileSpec); return b; } BOOL IsFileMarkedForOperationW ( IN PCWSTR FileSpec, IN DWORD Operations ) /*++ Routine Description: IsFileMarkedForOperation tests a path for one or more operations. Arguments: FileSpec - Specifies the path to test Operations - Specifies one or more operations to test for. Return Value: TRUE if at least one operation from Operations is set on FileSpec, FALSE otherwise. --*/ { WCHAR LongFileSpec [MEMDB_MAX]; DWORD Flags; WCHAR Node[MEMDB_MAX]; pFileOpsGetLongPathW (FileSpec, LongFileSpec); MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, LongFileSpec, NULL, NULL); if (MemDbGetValueAndFlagsW (Node, NULL, &Flags)) { return (Flags & Operations) != 0; } return FALSE; } BOOL pIsFileMarkedForOperationW ( IN PCWSTR FileSpec, IN DWORD Operations ) /*++ Routine Description: pIsFileMarkedForOperation tests a path for one or more operations. It does not convert short paths to long paths Arguments: FileSpec - Specifies the path to test Operations - Specifies one or more operations to test for. Return Value: TRUE if at least one operation from Operations is set on FileSpec, FALSE otherwise. --*/ { DWORD Flags; WCHAR Node[MEMDB_MAX]; MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, FileSpec, NULL, NULL); if (MemDbGetValueAndFlagsW (Node, NULL, &Flags)) { return (Flags & Operations) != 0; } return FALSE; } BOOL IsFileMarkedInDataA ( IN PCSTR FileSpec ) { PCWSTR UnicodeFileSpec; BOOL b; UnicodeFileSpec = ConvertAtoW (FileSpec); b = IsFileMarkedInDataW (UnicodeFileSpec); FreeConvertedStr (UnicodeFileSpec); return b; } BOOL IsFileMarkedInDataW ( IN PCWSTR FileSpec ) /*++ Routine Description: IsFileMarkedInData tests the common property data section for FileSpec. Arguments: FileSpec - Specifies the path to test. This may also be any arbitrary property value. Return Value: TRUE if FileSpec is a property of some operation, FALSE otherwise. --*/ { WCHAR Node[MEMDB_MAX]; MemDbBuildKeyW (Node, MEMDB_CATEGORY_DATAW, FileSpec, NULL, NULL); return MemDbGetValueW (Node, NULL); } DWORD GetPathPropertyOffset ( IN UINT Sequencer, IN OPERATION Operation, IN DWORD Property ) /*++ Routine Description: GetPathPropertyOffset returns the MemDb offset to the specified property. Arguments: Sequencer - Specifies the path sequencer Operation - Specifies the operation the property is associated with Property - Specifies the property index Return Value: The MemDb offset to the property data, or INVALID_OFFSET. --*/ { WCHAR Node[MEMDB_MAX]; DWORD Offset; UINT OperationNum; OperationNum = pWhichBitIsSet (Operation); pBuildPropertyKey (Node, OperationNum, Sequencer, Property); if (MemDbGetValueW (Node, &Offset)) { return Offset; } return INVALID_OFFSET; } DWORD GetOperationsOnPathA ( IN PCSTR FileSpec ) { PCWSTR UnicodeFileSpec; DWORD w; UnicodeFileSpec = ConvertAtoW (FileSpec); w = GetOperationsOnPathW (UnicodeFileSpec); FreeConvertedStr (UnicodeFileSpec); return w; } DWORD GetOperationsOnPathW ( IN PCWSTR FileSpec ) /*++ Routine Description: GetOperationsOnPath returns the operation flags for a path. Arguments: FileSpec - Specifies the path to return operations for Return Value: The operation bits for FileSpec --*/ { WCHAR LongFileSpec [MEMDB_MAX]; DWORD Operations; WCHAR Node[MEMDB_MAX]; pFileOpsGetLongPathW (FileSpec, LongFileSpec); MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, LongFileSpec, NULL, NULL); if (MemDbGetValueAndFlagsW (Node, NULL, &Operations)) { return Operations; } return 0; } BOOL GetPathPropertyA ( IN PCSTR FileSpec, IN DWORD Operations, IN DWORD Property, OUT PSTR PropertyBuf OPTIONAL ) { PCWSTR UnicodeFileSpec; BOOL b; WCHAR UnicodeProperty[MEMDB_MAX]; UnicodeFileSpec = ConvertAtoW (FileSpec); b = GetPathPropertyW ( UnicodeFileSpec, Operations, Property, PropertyBuf ? UnicodeProperty : NULL ); FreeConvertedStr (UnicodeFileSpec); if (b && PropertyBuf) { KnownSizeWtoA (PropertyBuf, UnicodeProperty); } return b; } BOOL pGetPathPropertyW ( IN PCWSTR FileSpec, IN DWORD Operations, IN DWORD Property, OUT PWSTR PropertyBuf OPTIONAL ) /*++ Routine Description: pGetPathProperty obtains a specific property for a path. Arguments: FileSpec - Specifies the path the property is associated with Operations - Specifies the operation flags to search. The function will return the first property to match. Property - Specifies the property index ProperyBuf - Receives the property data Return Value: TRUE if a property was copied to PropertyBuf, FALSE otherwise. --*/ { WCHAR Node[MEMDB_MAX]; DWORD Sequencer; DWORD Flags; DWORD Operation; DWORD PropertyOffset; BOOL b = FALSE; // // Make sure operation is specified for FileSpec, then return // the property requested. // MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, FileSpec, NULL, NULL); if (MemDbGetValueAndFlagsW (Node, &Sequencer, &Flags)) { Flags &= Operations; if (Flags) { Operation = Flags & (~(Flags - 1)); MYASSERT (ONEBITSET (Operation)); PropertyOffset = GetPathPropertyOffset (Sequencer, Operation, Property); if (PropertyOffset != INVALID_OFFSET) { if (PropertyBuf) { b = MemDbBuildKeyFromOffsetW (PropertyOffset, PropertyBuf, 1, NULL); } else { b = TRUE; } } } } return b; } BOOL GetPathPropertyW ( IN PCWSTR FileSpec, IN DWORD Operations, IN DWORD Property, OUT PWSTR PropertyBuf OPTIONAL ) /*++ Routine Description: GetPathProperty obtains a specific property for a path. Arguments: FileSpec - Specifies the path the property is associated with Operations - Specifies the operation flags to search. The function will return the first property to match. Property - Specifies the property index ProperyBuf - Receives the property data Return Value: TRUE if a property was copied to PropertyBuf, FALSE otherwise. --*/ { WCHAR LongFileSpec[MEMDB_MAX]; MYASSERT (!(Operations & OPERATION_SHORT_FILE_NAME)); pFileOpsGetLongPathW (FileSpec, LongFileSpec); return pGetPathPropertyW (LongFileSpec, Operations, Property, PropertyBuf); } BOOL pEnumFirstPathInOperationWorker ( IN OUT PMEMDB_ENUMW EnumPtr, OUT PWSTR EnumPath, OUT PDWORD Sequencer, IN OPERATION Operation ) { WCHAR Node[MEMDB_MAX]; UINT OperationNum; BOOL b; OperationNum = pWhichBitIsSet (Operation); pBuildOperationCategory (Node, OperationNum); StringCopyW (AppendWack (Node), L"*"); b = MemDbEnumFirstValueW (EnumPtr, Node, MEMDB_THIS_LEVEL_ONLY, MEMDB_ENDPOINTS_ONLY); if (b) { MemDbBuildKeyFromOffsetW (EnumPtr->dwValue, EnumPath, 1, Sequencer); } return b; } BOOL pEnumNextFileOpOrProperty ( IN OUT PMEMDB_ENUMW EnumPtr, OUT PWSTR EnumPathOrData, OUT PWSTR PropertyName, OPTIONAL OUT PDWORD Sequencer OPTIONAL ) { BOOL b; WCHAR Temp[MEMDB_MAX]; PWSTR p; b = MemDbEnumNextValueW (EnumPtr); if (b) { if (PropertyName) { MemDbBuildKeyFromOffsetW (EnumPtr->dwValue, Temp, 0, Sequencer); p = wcschr (Temp, L'\\'); if (!p) { p = GetEndOfStringW (Temp); } StringCopyABW (PropertyName, Temp, p); if (*p) { p++; } StringCopyW (EnumPathOrData, p); } else { MemDbBuildKeyFromOffsetW (EnumPtr->dwValue, EnumPathOrData, 1, Sequencer); } } return b; } BOOL EnumFirstPathInOperationA ( OUT PFILEOP_ENUMA EnumPtr, IN OPERATION Operation ) { BOOL b; WCHAR EnumPath[MAX_WCHAR_PATH]; ZeroMemory (EnumPtr, sizeof (FILEOP_ENUMA)); b = pEnumFirstPathInOperationWorker ( &EnumPtr->MemDbEnum, EnumPath, &EnumPtr->Sequencer, Operation ); if (b) { KnownSizeWtoA (EnumPtr->Path, EnumPath); } return b; } BOOL EnumFirstPathInOperationW ( OUT PFILEOP_ENUMW EnumPtr, IN OPERATION Operation ) /*++ Routine Description: EnumFirstPathInOperation begins an enumeration of all paths for a particular operation. Arguments: EnumPtr - Receives the first enumerated item. Operation - Specifies the operation to enumerate. Return Value: TRUE if a path was enumerated, or FALSE if the operation is not applied to any path. --*/ { ZeroMemory (EnumPtr, sizeof (FILEOP_ENUMW)); return pEnumFirstPathInOperationWorker ( &EnumPtr->MemDbEnum, EnumPtr->Path, &EnumPtr->Sequencer, Operation ); } BOOL EnumNextPathInOperationA ( IN OUT PFILEOP_ENUMA EnumPtr ) { BOOL b; WCHAR EnumPath[MAX_WCHAR_PATH]; b = pEnumNextFileOpOrProperty ( &EnumPtr->MemDbEnum, EnumPath, NULL, &EnumPtr->Sequencer ); if (b) { KnownSizeWtoA (EnumPtr->Path, EnumPath); } return b; } BOOL EnumNextPathInOperationW ( IN OUT PFILEOP_ENUMW EnumPtr ) { return pEnumNextFileOpOrProperty ( &EnumPtr->MemDbEnum, EnumPtr->Path, NULL, &EnumPtr->Sequencer ); } BOOL pEnumFirstPropertyWorker ( IN OUT PMEMDB_ENUMW EnumPtr, OUT PWSTR EnumData, OUT PWSTR PropertyName, IN UINT Sequencer, IN OPERATION Operation ) { WCHAR Node[MEMDB_MAX]; PWSTR p; UINT OperationNum; BOOL b; OperationNum = pWhichBitIsSet (Operation); pBuildOperationKey (Node, OperationNum, Sequencer); StringCopyW (AppendWack (Node), L"*"); b = MemDbEnumFirstValueW (EnumPtr, Node, MEMDB_THIS_LEVEL_ONLY, MEMDB_ENDPOINTS_ONLY); if (b) { MemDbBuildKeyFromOffsetW (EnumPtr->dwValue, Node, 0, NULL); p = wcschr (Node, L'\\'); if (!p) { p = GetEndOfStringW (Node); } StringCopyABW (PropertyName, Node, p); if (*p) { p++; } StringCopyW (EnumData, p); } return b; } BOOL EnumFirstFileOpPropertyA ( OUT PFILEOP_PROP_ENUMA EnumPtr, IN UINT Sequencer, IN OPERATION Operation ) { BOOL b; WCHAR EnumData[MEMDB_MAX]; WCHAR PropertyName[MEMDB_MAX]; ZeroMemory (EnumPtr, sizeof (FILEOP_PROP_ENUMA)); b = pEnumFirstPropertyWorker ( &EnumPtr->MemDbEnum, EnumData, PropertyName, Sequencer, Operation ); if (b) { KnownSizeWtoA (EnumPtr->Property, EnumData); KnownSizeWtoA (EnumPtr->PropertyName, PropertyName); } return b; } BOOL EnumFirstFileOpPropertyW ( OUT PFILEOP_PROP_ENUMW EnumPtr, IN UINT Sequencer, IN OPERATION Operation ) /*++ Routine Description: EnumFirstFileOpProperty enumerates the first property associated with an operation on a specific path. Arguments: EnumPtr - Receives the enumerated item data Sequencer - Specifies the sequencer of the path to enumerate Operation - Specifies the operation to enumerate Return Value: TRUE if a property was enumerated, or FALSE if the path and operation does not have any properties. --*/ { ZeroMemory (EnumPtr, sizeof (FILEOP_PROP_ENUMW)); return pEnumFirstPropertyWorker ( &EnumPtr->MemDbEnum, EnumPtr->Property, EnumPtr->PropertyName, Sequencer, Operation ); } BOOL EnumNextFileOpPropertyA ( IN OUT PFILEOP_PROP_ENUMA EnumPtr ) { BOOL b; WCHAR EnumData[MEMDB_MAX]; WCHAR PropertyName[MEMDB_MAX]; b = pEnumNextFileOpOrProperty ( &EnumPtr->MemDbEnum, EnumData, PropertyName, NULL ); if (b) { KnownSizeWtoA (EnumPtr->Property, EnumData); KnownSizeWtoA (EnumPtr->PropertyName, PropertyName); } return b; } BOOL EnumNextFileOpPropertyW ( IN OUT PFILEOP_PROP_ENUMW EnumPtr ) { return pEnumNextFileOpOrProperty ( &EnumPtr->MemDbEnum, EnumPtr->Property, EnumPtr->PropertyName, NULL ); } BOOL pEnumFileOpWorkerA ( IN OUT PALL_FILEOPS_ENUMA EnumPtr ) { // // Transfer UNICODE results to enum struct // KnownSizeWtoA (EnumPtr->Path, EnumPtr->Enum.Path); KnownSizeWtoA (EnumPtr->Property, EnumPtr->Enum.Property); EnumPtr->Sequencer = EnumPtr->Enum.Sequencer; EnumPtr->PropertyNum = EnumPtr->Enum.PropertyNum; EnumPtr->CurrentOperation = EnumPtr->Enum.CurrentOperation; EnumPtr->PropertyValid = EnumPtr->Enum.PropertyValid; return TRUE; } BOOL EnumFirstFileOpA ( OUT PALL_FILEOPS_ENUMA EnumPtr, IN DWORD Operations, IN PCSTR FileSpec OPTIONAL ) { BOOL b; PCWSTR UnicodeFileSpec; if (FileSpec) { UnicodeFileSpec = ConvertAtoW (FileSpec); } else { UnicodeFileSpec = NULL; } b = EnumFirstFileOpW (&EnumPtr->Enum, Operations, UnicodeFileSpec); if (UnicodeFileSpec) { FreeConvertedStr (UnicodeFileSpec); } if (b) { return pEnumFileOpWorkerA (EnumPtr); } return FALSE; } BOOL EnumFirstFileOpW ( OUT PALL_FILEOPS_ENUMW EnumPtr, IN DWORD Operations, IN PCWSTR FileSpec OPTIONAL ) /*++ Routine Description: EnumFirstFileOp is a general-purpose enumerator. It enumerates the paths and all properties from a set of operations. Arguments: EnumPtr - Receives the enumerated item data Operations - Specifies one or more operations to enumerate FileSpec - Specifies a specific path to enumerate, or NULL to enumerate all paths that have the specified operation(s) Return Value: TRUE if data was enuemrated, or FALSE if no data matches the specified operations and file spec. --*/ { WCHAR LongFileSpec [MEMDB_MAX]; ZeroMemory (EnumPtr, sizeof (ALL_FILEOPS_ENUMW)); EnumPtr->State = FO_ENUM_BEGIN; EnumPtr->Operations = Operations; EnumPtr->Path = EnumPtr->OpEnum.Path; EnumPtr->Property = EnumPtr->PropEnum.Property; if (FileSpec) { pFileOpsGetLongPathW (FileSpec, LongFileSpec); _wcssafecpy (EnumPtr->FileSpec, LongFileSpec, MAX_WCHAR_PATH); } else { StringCopyW (EnumPtr->FileSpec, L"*"); } return EnumNextFileOpW (EnumPtr); } BOOL EnumNextFileOpA ( IN OUT PALL_FILEOPS_ENUMA EnumPtr ) { BOOL b; b = EnumNextFileOpW (&EnumPtr->Enum); if (b) { return pEnumFileOpWorkerA (EnumPtr); } return FALSE; } BOOL EnumNextFileOpW ( IN OUT PALL_FILEOPS_ENUMW EnumPtr ) { DWORD w; while (EnumPtr->State != FO_ENUM_END) { switch (EnumPtr->State) { case FO_ENUM_BEGIN: // // Find the next operation // if (!EnumPtr->Operations) { EnumPtr->State = FO_ENUM_END; break; } w = EnumPtr->Operations & (~(EnumPtr->Operations - 1)); MYASSERT (ONEBITSET (w)); EnumPtr->CurrentOperation = w; EnumPtr->OperationNum = pWhichBitIsSet (w); EnumPtr->Operations ^= w; EnumPtr->State = FO_ENUM_BEGIN_PATH_ENUM; break; case FO_ENUM_BEGIN_PATH_ENUM: if (EnumFirstPathInOperationW (&EnumPtr->OpEnum, EnumPtr->CurrentOperation)) { EnumPtr->State = FO_ENUM_BEGIN_PROP_ENUM; } else { EnumPtr->State = FO_ENUM_BEGIN; } break; case FO_ENUM_BEGIN_PROP_ENUM: if (!IsPatternMatchW (EnumPtr->FileSpec, EnumPtr->Path)) { EnumPtr->State = FO_ENUM_NEXT_PATH; break; } EnumPtr->Sequencer = EnumPtr->OpEnum.Sequencer; EnumPtr->PropertyNum = 0; if (EnumFirstFileOpPropertyW ( &EnumPtr->PropEnum, EnumPtr->Sequencer, EnumPtr->CurrentOperation )) { EnumPtr->State = FO_ENUM_RETURN_DATA; break; } EnumPtr->State = FO_ENUM_RETURN_PATH; break; case FO_ENUM_RETURN_PATH: EnumPtr->State = FO_ENUM_NEXT_PATH; EnumPtr->PropertyValid = FALSE; return TRUE; case FO_ENUM_RETURN_DATA: EnumPtr->State = FO_ENUM_NEXT_PROP; EnumPtr->PropertyValid = TRUE; return TRUE; case FO_ENUM_NEXT_PROP: EnumPtr->PropertyNum++; if (EnumNextFileOpPropertyW (&EnumPtr->PropEnum)) { EnumPtr->State = FO_ENUM_RETURN_DATA; } else { EnumPtr->State = FO_ENUM_NEXT_PATH; } break; case FO_ENUM_NEXT_PATH: if (EnumNextPathInOperationW (&EnumPtr->OpEnum)) { EnumPtr->State = FO_ENUM_BEGIN_PROP_ENUM; } else { EnumPtr->State = FO_ENUM_BEGIN; } break; } } return FALSE; } BOOL TestPathsForOperationsA ( IN PCSTR BaseFileSpec, IN DWORD OperationsToFind ) { BOOL b; PCWSTR UnicodeBaseFileSpec; UnicodeBaseFileSpec = ConvertAtoW (BaseFileSpec); b = TestPathsForOperationsW (UnicodeBaseFileSpec, OperationsToFind); FreeConvertedStr (UnicodeBaseFileSpec); return b; } BOOL TestPathsForOperationsW ( IN PCWSTR BaseFileSpec, IN DWORD OperationsToFind ) /*++ Routine Description: TestPathsForOperations scans all subpaths of the given base for a specific operation. This function is typically used to test a directory for an operation on one of its files or subdirectories. Arguments: BaseFileSpec - Specifies the base path to scan OperationsToFind - Specifies one or more operations to look for Return Value: TRUE if one of the operations was found within BaseFileSpec, or FALSE if no subpath of BaseFileSpec has one of the operations. --*/ { WCHAR LongFileSpec [MEMDB_MAX]; WCHAR Node[MEMDB_MAX]; MEMDB_ENUMW e; DWORD Operation; if (MemDbGetValueAndFlagsW (BaseFileSpec, NULL, &Operation)) { if (Operation & OperationsToFind) { return TRUE; } } pFileOpsGetLongPathW (BaseFileSpec, LongFileSpec); MemDbBuildKeyW ( Node, MEMDB_CATEGORY_PATHROOTW, LongFileSpec, L"*", NULL ); if (MemDbEnumFirstValueW (&e, Node, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { if (e.Flags & OperationsToFind) { return TRUE; } } while (MemDbEnumNextValueW (&e)); } return FALSE; } BOOL IsFileMarkedAsKnownGoodA ( IN PCSTR FileSpec ) { CHAR Node[MEMDB_MAX]; MemDbBuildKeyA ( Node, MEMDB_CATEGORY_KNOWN_GOODA, FileSpec, NULL, NULL); return MemDbGetValueA (Node, NULL); } /*++ Routine Description: IsFileMarkedForAnnounce determines if a file is listed in DeferredAnnounce category. Arguments: FileSpec - Specifies the file to query in long filename format Return Value: TRUE if the file is listed or FALSE if it is not. --*/ BOOL IsFileMarkedForAnnounceA ( IN PCSTR FileSpec ) { CHAR Node[MEMDB_MAX]; MemDbBuildKeyA ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEA, FileSpec, NULL, NULL); return MemDbGetValueA (Node, NULL); } BOOL IsFileMarkedForAnnounceW ( IN PCWSTR FileSpec ) { WCHAR Node[MEMDB_MAX]; MemDbBuildKeyW ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEW, FileSpec, NULL, NULL); return MemDbGetValueW (Node, NULL); } /*++ Routine Description: GetFileAnnouncement returnes the announcement value for a particular file. The possible values are ACT_... values in fileops.h Arguments: FileSpec - Specifies the file to query in long filename format Return Value: The announcement value. --*/ DWORD GetFileAnnouncementA ( IN PCSTR FileSpec ) { CHAR Node[MEMDB_MAX]; DWORD result = ACT_UNKNOWN; MemDbBuildKeyA ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEA, FileSpec, NULL, NULL); MemDbGetValueAndFlagsA (Node, NULL, &result); return result; } DWORD GetFileAnnouncementW ( IN PCWSTR FileSpec ) { WCHAR Node[MEMDB_MAX]; DWORD result = ACT_UNKNOWN; MemDbBuildKeyW ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEW, FileSpec, NULL, NULL); MemDbGetValueAndFlagsW (Node, NULL, &result); return result; } /*++ Routine Description: GetFileAnnouncementContext returnes the context of a file that is marked for announcement. Arguments: FileSpec - Specifies the file to query in long filename format Return Value: The announcement context. --*/ DWORD GetFileAnnouncementContextA ( IN PCSTR FileSpec ) { CHAR Node[MEMDB_MAX]; DWORD result = 0; MemDbBuildKeyA ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEA, FileSpec, NULL, NULL); MemDbGetValueAndFlagsA (Node, &result, NULL); return result; } DWORD GetFileAnnouncementContextW ( IN PCWSTR FileSpec ) { WCHAR Node[MEMDB_MAX]; DWORD result = 0; MemDbBuildKeyW ( Node, MEMDB_CATEGORY_DEFERREDANNOUNCEW, FileSpec, NULL, NULL); MemDbGetValueAndFlagsW (Node, &result, NULL); return result; } /*++ Routine Description: IsFileProvidedByNt checks to see if a specific file is going to be installed by standard NT setup. This list was generated from calls to FileIsProviedByNt. Arguments: FileName - Specifies the name of the file in long filename format Return Value: TRUE if the file will be installed by standard NT installation, or FALSE if it will not. --*/ BOOL IsFileProvidedByNtA ( IN PCSTR FileName ) { CHAR Node[MEMDB_MAX]; MemDbBuildKeyA (Node, MEMDB_CATEGORY_NT_FILESA, FileName, NULL, NULL); return MemDbGetValueA (Node, NULL); } BOOL IsFileProvidedByNtW ( IN PCWSTR FileName ) { WCHAR Node[MEMDB_MAX]; MemDbBuildKeyW (Node, MEMDB_CATEGORY_NT_FILESW, FileName, NULL, NULL); return MemDbGetValueW (Node, NULL); } /*++ Routine Description: GetNewPathForFile copies the move path to the caller-supplied buffer if the file is marked to be moved. Arguments: SrcFileSpec - Specifies the src file to query in long filename format NewPath - Receives a copy of the new location, or if the file is not being moved, receives a copy of the original file. Return Value: TRUE if the file is marked to be moved and the destination was copied to NewPath, or FALSE if the file is not makred to be moved and SrcFileSpec was copied to NewPath. --*/ BOOL GetNewPathForFileA ( IN PCSTR SrcFileSpec, OUT PSTR NewPath ) { BOOL b; PCWSTR UnicodeSrcFileSpec; WCHAR UnicodeNewPath[MAX_WCHAR_PATH]; UnicodeSrcFileSpec = ConvertAtoW (SrcFileSpec); b = GetNewPathForFileW (UnicodeSrcFileSpec, UnicodeNewPath); FreeConvertedStr (UnicodeSrcFileSpec); if (b) { KnownSizeWtoA (NewPath, UnicodeNewPath); } return b; } BOOL GetNewPathForFileW ( IN PCWSTR SrcFileSpec, OUT PWSTR NewPath ) { DWORD Offset = INVALID_OFFSET; DWORD w; OPERATION Operation; UINT Sequencer; Sequencer = GetSequencerFromPathW (SrcFileSpec); StringCopyW (NewPath, SrcFileSpec); w = ALL_MOVE_OPERATIONS; while (w && Offset == INVALID_OFFSET) { Operation = w & (~(w - 1)); w ^= Operation; Offset = GetPathPropertyOffset (Sequencer, Operation, 0); } if (Offset != INVALID_OFFSET) { return MemDbBuildKeyFromOffsetW (Offset, NewPath, 1, NULL); } return FALSE; } BOOL AnnounceFileInReportA ( IN PCSTR FileSpec, IN DWORD ContextPtr, IN DWORD Action ) /*++ Routine Description: Adds a file to the memdb DeferredAnnounce category. Arguments: FileSpec - Specifies the file to delete in long name format Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { CHAR Key[MEMDB_MAX]; MemDbBuildKeyA (Key, MEMDB_CATEGORY_DEFERREDANNOUNCEA, FileSpec, NULL, NULL); return MemDbSetValueAndFlagsA (Key, ContextPtr, Action, 0); } BOOL MarkFileAsKnownGoodA ( IN PCSTR FileSpec ) /*++ Routine Description: Adds a file to the memdb KnownGood category. Arguments: FileSpec - Specifies the file name Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { return MemDbSetValueExA (MEMDB_CATEGORY_KNOWN_GOODA, FileSpec, NULL, NULL, 0, NULL); } BOOL AddCompatibleShellA ( IN PCSTR FileSpec, IN DWORD ContextPtr OPTIONAL ) /*++ Routine Description: Adds a file to the memdb CompatibleShell category. Arguments: FileSpec - Specifies the file to delete in long name format ContextPtr - Specifies the MigDb context, cast as a DWORD (can be 0 if no context is available) Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { CHAR Key[MEMDB_MAX]; MemDbBuildKeyA (Key, MEMDB_CATEGORY_COMPATIBLE_SHELLA, FileSpec, NULL, NULL); return MemDbSetValueAndFlagsA (Key, ContextPtr, 0, 0); } BOOL AddCompatibleRunKeyA ( IN PCSTR FileSpec, IN DWORD ContextPtr ) /*++ Routine Description: Adds a file to the memdb CompatibleRunKey category. Arguments: FileSpec - Specifies the file to delete in long name format ContextPtr - Specifies the MigDb context, cast as a DWORD (can be 0 if no context is available) Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { CHAR Key[MEMDB_MAX]; MemDbBuildKeyA (Key, MEMDB_CATEGORY_COMPATIBLE_RUNKEYA, FileSpec, NULL, NULL); return MemDbSetValueAndFlagsA (Key, ContextPtr, 0, 0); } BOOL AddCompatibleDosA ( IN PCSTR FileSpec, IN DWORD ContextPtr OPTIONAL ) /*++ Routine Description: Adds a file to the memdb CompatibleDos category. Arguments: FileSpec - Specifies the file in long name format ContextPtr - Specifies the MigDb context, cast as a DWORD (can be 0 if no context is available) Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { CHAR Key[MEMDB_MAX]; MemDbBuildKeyA (Key, MEMDB_CATEGORY_COMPATIBLE_DOSA, FileSpec, NULL, NULL); return MemDbSetValueAndFlagsA (Key, ContextPtr, 0, 0); } BOOL AddCompatibleHlpA ( IN PCSTR FileSpec, IN DWORD ContextPtr ) /*++ Routine Description: Adds a file to the memdb CompatibleHlp category. Arguments: FileSpec - Specifies the file in long name format ContextPtr - Specifies the MigDb context, cast as a DWORD (can be 0 if no context is available) Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { CHAR Key[MEMDB_MAX]; MemDbBuildKeyA (Key, MEMDB_CATEGORY_COMPATIBLE_HLPA, FileSpec, NULL, NULL); return MemDbSetValueAndFlagsA (Key, ContextPtr, 0, 0); } // // Compute the number of CHARs allowed for the normal and long temp // locations. MAX_PATH includes the nul terminator, and we subtract // that terminator via sizeof. // // NORMAL_MAX is the number of chars left after the subdir c:\user~tmp.@01\, // including the nul // // LONG_MAX is the number of chars left after the subdir // c:\user~tmp.@02\12345\, including the nul. 12345 is a %05X sequencer. // #define NORMAL_MAX (MAX_PATH - (sizeof(S_SHELL_TEMP_NORMAL_PATHA)/sizeof(CHAR)) + 2) #define LONG_MAX (MAX_PATH - (sizeof(S_SHELL_TEMP_LONG_PATHA)/sizeof(CHAR)) - 6) VOID ComputeTemporaryPathA ( IN PCSTR SourcePath, IN PCSTR SourcePrefix, OPTIONAL IN PCSTR TempPrefix, OPTIONAL IN PCSTR SetupTempDir, OUT PSTR TempPath ) /*++ Routine Description: ComputeTemporaryPath builds a temporary path rooted in S_SHELL_TEMP_NORMAL_PATH for a path that fits within MAX_PATH, or S_SHELL_TEMP_LONG_PATH for a longer path. It attempts to use the original subpath name in the "normal" path subdirectory. If that doesn't fit, then a unique "long" subdirectory is created, and a subpath is computed by taking the longest possible subpath (the right side). Arguments: SourcePath - Specifies the full file or directory path of the source SourcePrefix - Specifies a prefix that will be stripped from SourcePath TempPrefix - Specifies a prefix that will be inserted at the start of TempPath SetupTempDir - Specifies the setup temp dir, typically %windir%\setup, to be used when no suitable path can be computed. (Unlikely case.) TempPath - Receives the temp path string. This buffer will receive up to MAX_PATH characters (includes the nul). Return Value: None. --*/ { PCSTR subPath = NULL; PCSTR smallerSubPath; PSTR pathCopy = NULL; PSTR lastWack; static UINT dirSequencer = 0; UINT prefixLen; MBCHAR ch; UINT normalMax = NORMAL_MAX; // // Build a temporary file name using the inbound file as a suggestion. // StringCopyA (TempPath, S_SHELL_TEMP_NORMAL_PATHA); TempPath[0] = SourcePath[0]; if (SourcePrefix) { prefixLen = TcharCountA (SourcePrefix); if (StringIMatchTcharCountA (SourcePath, SourcePrefix, prefixLen)) { ch = _mbsnextc (SourcePath + prefixLen); if (!ch || ch == '\\') { subPath = SourcePath + prefixLen; if (*subPath) { subPath++; } } } } if (!subPath) { subPath = _mbschr (SourcePath, '\\'); if (!subPath) { subPath = SourcePath; } else { subPath++; } } DEBUGMSGA_IF ((_mbschr (subPath, ':') != NULL, DBG_WHOOPS, "Bad temp path: %s", SourcePath)); if (TempPrefix) { StringCopyA (AppendWackA (TempPath), TempPrefix); normalMax = MAX_PATH - TcharCountA (TempPath); } if (TcharCountA (subPath) < normalMax) { // // typical case: source path fits within MAX_PATH; use it // if (*subPath) { StringCopyA (AppendWackA (TempPath), subPath); } } else { // // subpath is too big, just take the right side of the src // dirSequencer++; wsprintfA (TempPath, S_SHELL_TEMP_LONG_PATHA "\\%05x", dirSequencer); TempPath[0] = SourcePath[0]; // compute end of string + nul terminator - backslash - (MAX_PATH - TcharCount of TempPath) subPath = GetEndOfStringA (SourcePath) - LONG_MAX; // // try to eliminate a truncated subdirectory on the left // smallerSubPath = _mbschr (subPath, '\\'); if (smallerSubPath && smallerSubPath[1]) { subPath = smallerSubPath + 1; } else { // // still no subpath, try just the file name // subPath = _mbsrchr (subPath, '\\'); if (subPath) { subPath++; if (!(*subPath)) { // // file spec ends in backslash // pathCopy = DuplicateTextA (SourcePath); if (!pathCopy) { subPath = NULL; } else { for (;;) { lastWack = _mbsrchr (pathCopy, '\\'); if (!lastWack || lastWack[1]) { break; } *lastWack = 0; } subPath = lastWack; } } else if (TcharCountA (subPath) > LONG_MAX) { // // very long file name; truncate it // subPath = GetEndOfStringA (subPath) - LONG_MAX; } } } if (subPath) { StringCopyA (AppendWackA (TempPath), subPath); } else { dirSequencer++; wsprintfA (TempPath, "%s\\tmp%05x", SetupTempDir, dirSequencer); } if (pathCopy) { FreeTextA (pathCopy); } } } BOOL pMarkFileForTemporaryMoveA ( IN PCSTR SrcFileSpec, IN PCSTR FinalDest, IN PCSTR TempSpec, IN BOOL TempSpecIsFile, IN PCSTR TempFileIn, OPTIONAL OUT PSTR TempFileOut OPTIONAL ) /*++ Routine Description: This routine adds operations to move a file to a temporary location in text mode, and optionally move it to a final destination. Arguments: SrcFileSpec - Specifies the file that is to be moved to a safe place (out of the way of normal NT installation), and then moved back after NT is installed. FinalDest - Specifies the final destination for FileSpec. If NULL, file is moved to a temporary location but is not copied to a final location in GUI mode. TempSpec - Specifies the temp dir or file to relocate the file to. The temp dir must be on the same drive as SrcFileSpec. TempSpecIsFile - Specifies TRUE if the prev param is a file TempFileIn - If non-NULL, specifies the temporary file to use instead of automatically generated name. Provided only for MarkHiveForTemporaryMove. TempFileOut - If non-NULL, receives the path to the temporary file location. Return Value: TRUE if the operation was recorded, or FALSE otherwise. --*/ { BOOL b = TRUE; CHAR TempFileSpec[MAX_MBCHAR_PATH]; static DWORD FileSequencer = 0; // // Move the file from source to temporary location // if (!CanSetOperationA (SrcFileSpec, OPERATION_TEMP_PATH)) { return FALSE; } if (TempFileIn) { MYASSERT (!TempSpecIsFile); wsprintfA (TempFileSpec, "%s\\%s", TempSpec, TempFileIn); } else if (TempSpecIsFile) { StringCopyA (TempFileSpec, TempSpec); } else { FileSequencer++; wsprintfA (TempFileSpec, "%s\\tmp%05x", TempSpec, FileSequencer); } DEBUGMSGA ((DBG_MEMDB, "MarkFileForTemporaryMove: %s -> %s", SrcFileSpec, TempFileSpec)); if (TempFileOut) { StringCopyA (TempFileOut, TempFileSpec); } RemoveOperationsFromPathA (SrcFileSpec, OPERATION_TEMP_PATH | OPERATION_FILE_DELETE_EXTERNAL | OPERATION_FILE_MOVE_EXTERNAL); b = AssociatePropertyWithPathA (SrcFileSpec, OPERATION_TEMP_PATH, TempFileSpec); // // Optionally move the file from temporary location to final dest // if (FinalDest) { // // We are adding additional properties to the temp path operation that // already exists. So the properties are defined as zero being the temp // path, and one and higher being destinations. That's how we achieve // a one-to-many capability. // b = b && AssociatePropertyWithPathA (SrcFileSpec, OPERATION_TEMP_PATH, FinalDest); // // Now we add an external move operation, so the registry is updated // correctly. // b = b && MarkFileForMoveExternalA (SrcFileSpec, FinalDest); } else { // // Because the source file is going to be moved to a temporary location // and never moved back, it is effectively going to be deleted. // b = b && MarkFileForExternalDeleteA (SrcFileSpec); } return b; } BOOL MarkFileForTemporaryMoveExA ( IN PCSTR SrcFileSpec, IN PCSTR FinalDest, IN PCSTR TempSpec, IN BOOL TempSpecIsFile ) { return pMarkFileForTemporaryMoveA (SrcFileSpec, FinalDest, TempSpec, TempSpecIsFile, NULL, NULL); } PCSTR GetTemporaryLocationForFileA ( IN PCSTR SourceFile ) { UINT sequencer; PCSTR result = NULL; FILEOP_PROP_ENUMA eOpProp; sequencer = GetSequencerFromPathA (SourceFile); if (sequencer) { if (EnumFirstFileOpPropertyA (&eOpProp, sequencer, OPERATION_TEMP_PATH)) { result = DuplicatePathStringA (eOpProp.Property, 0); } } return result; } PCWSTR GetTemporaryLocationForFileW ( IN PCWSTR SourceFile ) { UINT sequencer; PCWSTR result = NULL; FILEOP_PROP_ENUMW eOpProp; sequencer = GetSequencerFromPathW (SourceFile); if (sequencer) { if (EnumFirstFileOpPropertyW (&eOpProp, sequencer, OPERATION_TEMP_PATH)) { result = DuplicatePathStringW (eOpProp.Property, 0); } } return result; } BOOL MarkHiveForTemporaryMoveA ( IN PCSTR HivePath, IN PCSTR TempDir, IN PCSTR UserName, IN BOOL DefaultHives, IN BOOL CreateOnly ) /*++ Routine Description: Adds a file or directory path to the TempReloc memdb category. The file or dir is moved during text mode and is never moved back. If the file name is user.dat, the destination location is written to the UserDatLoc category. All hives are deleted at the end of setup. Arguments: HivePath - Specifies the Win9x path to a user.dat or system.dat file TempDir - Specifies the path to the setup temporary dir on the same drive as HivePath UserName - Specifies the current user or NULL if default user or no user DefaultHives - Specifies TRUE if the HivePath is a system default path, or FALSE if the HivePath is specific to a user profile. CreateOnly - Specifies TRUE if this account is create-only (such as Administrator), or FALSE if this account gets full migration. Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ { BOOL b = TRUE; CHAR OurTempFileSpec[MAX_MBCHAR_PATH]; CHAR RealTempFileSpec[MAX_MBCHAR_PATH]; static DWORD Sequencer = 0; PSTR p, q; if (!UserName || !UserName[0]) { UserName = S_DOT_DEFAULTA; } // // Has hive already been moved? If so, point two or more users to // the same hive. // RealTempFileSpec[0] = 0; p = (PSTR) GetFileNameFromPathA (HivePath); GetPathPropertyA (HivePath, OPERATION_TEMP_PATH, 0, RealTempFileSpec); if (!(RealTempFileSpec[0])) { // // Hive has not been moved yet -- move it now // if (!DefaultHives) { Sequencer++; wsprintfA (OurTempFileSpec, "hive%04u\\%s", Sequencer, p); } else { wsprintfA (OurTempFileSpec, "defhives\\%s", p); } b = pMarkFileForTemporaryMoveA ( HivePath, NULL, TempDir, FALSE, OurTempFileSpec, RealTempFileSpec ); if (b && DefaultHives) { // // Save defhives location in Paths\RelocWinDir // q = _mbsrchr (RealTempFileSpec, '\\'); MYASSERT(q); *q = 0; b = MemDbSetValueExA ( MEMDB_CATEGORY_PATHSA, // "Paths" MEMDB_ITEM_RELOC_WINDIRA, // "RelocWinDir" RealTempFileSpec, // Path to default hives NULL, 0, NULL ); *q = '\\'; } } if (b && StringIMatchA (p, "USER.DAT")) { // // Save location to all user.dat files in UserDatLoc // b = MemDbSetValueExA ( MEMDB_CATEGORY_USER_DAT_LOCA, UserName, NULL, RealTempFileSpec, (DWORD) CreateOnly, NULL ); } if (b) { DEBUGMSGA ((DBG_NAUSEA, "%s -> %s", HivePath, RealTempFileSpec)); } return b; } VOID MarkShellFolderForMoveA ( IN PCSTR SrcPath, IN PCSTR TempPath ) { DWORD Offset; // // Add an entry so the specified source file or directory // is moved to the temp path. // MemDbSetValueExA ( MEMDB_CATEGORY_SHELL_FOLDERS_PATHA, SrcPath, NULL, NULL, 0, &Offset ); MemDbSetValueExA ( MEMDB_CATEGORY_SF_TEMPA, TempPath, NULL, NULL, Offset, NULL ); } BOOL EnumFirstFileRelocA ( OUT PFILERELOC_ENUMA EnumPtr, IN PCSTR FileSpec OPTIONAL ) { if (EnumFirstFileOpA (&EnumPtr->e, ALL_DEST_CHANGE_OPERATIONS, FileSpec)) { if (!EnumPtr->e.PropertyValid) { return EnumNextFileRelocA (EnumPtr); } else { StringCopyA (EnumPtr->SrcFile, EnumPtr->e.Path); StringCopyA (EnumPtr->DestFile, EnumPtr->e.Property); return TRUE; } } return FALSE; } BOOL EnumNextFileRelocA ( IN OUT PFILERELOC_ENUMA EnumPtr ) { do { if (!EnumNextFileOpA (&EnumPtr->e)) { return FALSE; } } while (!EnumPtr->e.PropertyValid); StringCopyA (EnumPtr->SrcFile, EnumPtr->e.Path); StringCopyA (EnumPtr->DestFile, EnumPtr->e.Property); return TRUE; } /*++ Routine Description: DeclareTemporaryFile adds a file to the memdb FileDel and CancelFileDel category. That means the file will get deleted if the user hits CANCEL or at the end of GUI mode setup. Arguments: FileSpec - Specifies the file to declare in long name format Return Value: TRUE if the file was recorded in memdb, or FALSE if it could not be recorded. --*/ BOOL DeclareTemporaryFileA ( IN PCSTR FileSpec ) { return MarkFileForCleanUpA (FileSpec) && MemDbSetValueExA (MEMDB_CATEGORY_CANCELFILEDELA, FileSpec, NULL, NULL, 0, NULL); } BOOL DeclareTemporaryFileW ( IN PCWSTR FileSpec ) { return MarkFileForCleanUpW (FileSpec) && MemDbSetValueExW (MEMDB_CATEGORY_CANCELFILEDELW, FileSpec, NULL, NULL, 0, NULL); } /*++ Routine Description: FileIsProvidedByNt identifies a file as being installed by Windows NT. An entry is made in the NtFiles category for the file name, and the file name is linked to the full path in NtDirs. This funciton is implemented as an A version only because the list is created on the Win9x side of the upgrade. Arguments: FullPath - Specifies the full path, including the file name. FileName - Specifiles the file name only UserFlags - Specifies if the existence of NT file should be verified very first thing on NT side. Return Value: TRUE if memdb was updated, or FALSE if an error occurred. --*/ BOOL FileIsProvidedByNtA ( IN PCSTR FullPath, IN PCSTR FileName, IN DWORD UserFlags ) { DWORD Offset; PSTR DirOnly; CHAR Key[MEMDB_MAX]; PSTR p; BOOL b; DirOnly = DuplicatePathStringA (FullPath, 0); p = _mbsrchr (DirOnly, '\\'); if (p) { *p = 0; } b = MemDbSetValueExA ( MEMDB_CATEGORY_NT_DIRSA, DirOnly, NULL, NULL, 0, &Offset ); if (b) { MemDbBuildKeyA (Key, MEMDB_CATEGORY_NT_FILESA, FileName, NULL, NULL); b = MemDbSetValueAndFlagsA (Key, Offset, UserFlags, 0); } FreePathStringA (DirOnly); return b; } /*++ Routine Description: GetNtFilePath looks in the NtFiles category for the specified file, and if found builds the complete path. Arguments: FileName - Specifies the file that may be installed by NT FullPath - Receives the full path to the file as it will be installed Return Value: TRUE if the file exists and there were no errors building the path, or FALSE if the file does not exist or the path could not be built. --*/ BOOL GetNtFilePathA ( IN PCSTR FileName, OUT PSTR FullPath ) { DWORD Offset; CHAR Node[MEMDB_MAX]; MemDbBuildKeyA (Node, MEMDB_CATEGORY_NT_FILESA, FileName, NULL, NULL); if (MemDbGetValueA (Node, &Offset)) { if (MemDbBuildKeyFromOffsetA (Offset, FullPath, 1, NULL)) { StringCopyA (AppendPathWackA (FullPath), FileName); return TRUE; } DEBUGMSG ((DBG_WHOOPS, "GetNtFilePath: Could not build path from offset")); } return FALSE; } BOOL GetNtFilePathW ( IN PCWSTR FileName, OUT PWSTR FullPath ) { DWORD Offset; WCHAR Node[MEMDB_MAX]; MemDbBuildKeyW (Node, MEMDB_CATEGORY_NT_FILESW, FileName, NULL, NULL); if (MemDbGetValueW (Node, &Offset)) { if (MemDbBuildKeyFromOffsetW (Offset, FullPath, 1, NULL)) { StringCopyW (AppendPathWackW (FullPath), FileName); return TRUE; } DEBUGMSG ((DBG_WHOOPS, "GetNtFilePath: Could not build path from offset")); } return FALSE; } DWORD GetFileInfoOnNtW ( IN PCWSTR FileSpec, OUT PWSTR NewFileSpec, // OPTIONAL IN UINT BufferChars // Required if NewFileSpec is specified ) { WCHAR Node[MEMDB_MAX]; DWORD Operations; DWORD Offset; WCHAR NtFilePath[MEMDB_MAX]; WCHAR DestPath[MEMDB_MAX]; PCWSTR InboundPath; BOOL UserFile = FALSE; DWORD status = FILESTATUS_UNCHANGED; BOOL ShortFileNameFlag; PCWSTR UltimateDestiny; BOOL NtProvidesThisFile; WCHAR LongFileSpec[MAX_WCHAR_PATH]; PCWSTR SanitizedPath; PCSTR ansiPath; CHAR ansiOutput[MAX_MBCHAR_PATH]; PWSTR lastWack; // // Require FileSpec to be a local path and less than MAX_WCHAR_PATH // if (lstrlen (FileSpec) >= MAX_WCHAR_PATH) { if (NewFileSpec) { _wcssafecpy (NewFileSpec, FileSpec, BufferChars * sizeof (WCHAR)); } return 0; } // // Now get the file status of an actual path // SanitizedPath = SanitizePathW (FileSpec); if (!SanitizedPath) { SanitizedPath = DuplicatePathStringW (FileSpec, 0); } lastWack = wcsrchr (SanitizedPath, L'\\'); if (lastWack) { if (lastWack[1] != 0 || lastWack == wcschr (SanitizedPath, L'\\')) { lastWack = NULL; } else { *lastWack = 0; } } pFileOpsGetLongPathW (SanitizedPath, LongFileSpec); if (!StringIMatchW (SanitizedPath, LongFileSpec)) { InboundPath = LongFileSpec; ShortFileNameFlag = TRUE; } else { InboundPath = SanitizedPath; ShortFileNameFlag = FALSE; } DestPath[0] = 0; UltimateDestiny = InboundPath; // // Get all operations set on the file // MemDbBuildKeyW (Node, MEMDB_CATEGORY_PATHROOTW, InboundPath, NULL, NULL); if (!MemDbGetValueAndFlagsW (Node, NULL, &Operations)) { Operations = 0; } // // Migration DLLs have priority over all other operations // if (Operations & OPERATION_MIGDLL_HANDLED) { if (Operations & OPERATION_FILE_DELETE_EXTERNAL) { status = FILESTATUS_DELETED; } else { status = FILESTATUS_REPLACED; if (Operations & OPERATION_FILE_MOVE_EXTERNAL) { status |= FILESTATUS_MOVED; GetNewPathForFileW (InboundPath, DestPath); UltimateDestiny = DestPath; } } } else { // // Check for per-user move // if (g_CurrentUser) { MemDbBuildKeyW ( Node, MEMDB_CATEGORY_USERFILEMOVE_SRCW, InboundPath, g_CurrentUser, NULL ); if (MemDbGetValueW (Node, &Offset)) { if (MemDbBuildKeyFromOffsetW (Offset, DestPath, 1, NULL)) { status = FILESTATUS_MOVED; UltimateDestiny = DestPath; } UserFile = TRUE; } } // // Check for move or delete // if (!UserFile) { if (Operations & ALL_MOVE_OPERATIONS) { status = FILESTATUS_MOVED; GetNewPathForFileW (InboundPath, DestPath); UltimateDestiny = DestPath; if (Operations & OPERATION_FILE_MOVE_EXTERNAL) { status |= FILESTATUS_REPLACED; } } else if (Operations & ALL_DELETE_OPERATIONS) { status = FILESTATUS_DELETED; } } // // Check if the file (or the new destination) is an NT file // NtProvidesThisFile = GetNtFilePathW (GetFileNameFromPathW (UltimateDestiny), NtFilePath); if (status != FILESTATUS_UNCHANGED && NtProvidesThisFile) { // // Status may be either FILESTATUS_MOVED or FILESTATUS_DELETED. // if (StringIMatchW (UltimateDestiny, NtFilePath)) { // // NT installs the same file, so now we know that the ultimate // destiny isn't deleted. // status &= ~FILESTATUS_DELETED; status |= FILESTATUS_REPLACED|FILESTATUS_NTINSTALLED; } else if (Operations & ALL_DELETE_OPERATIONS) { // // NT installs the same file but in a different location // and the original file was to be deleted. The // ultimate destiny is the NT location, and we know that the // file is moved. // status = FILESTATUS_MOVED|FILESTATUS_REPLACED|FILESTATUS_NTINSTALLED; UltimateDestiny = NtFilePath; } else { status |= FILESTATUS_NTINSTALLED; } } else if (NtProvidesThisFile) { // // Status is FILESTATUS_UNCHANGED // status = FILESTATUS_NTINSTALLED; if (StringIMatchW (UltimateDestiny, NtFilePath)) { status |= FILESTATUS_REPLACED; } } if (!ShortFileNameFlag && (status == FILESTATUS_UNCHANGED)) { // // let's check for this case: undetected short file name query, NT installs this file in the same path // if (ISNT()) { OurGetLongPathNameW (SanitizedPath, LongFileSpec, MAX_WCHAR_PATH); if (!StringMatchW (UltimateDestiny, LongFileSpec)) { // // this was an undetected short file name query // NtProvidesThisFile = GetNtFilePathW (GetFileNameFromPathW (UltimateDestiny), NtFilePath); if (StringIMatchW (UltimateDestiny, NtFilePath)) { status |= FILESTATUS_REPLACED; } } } } } // // Return the new path to the caller // if (NewFileSpec) { if (lastWack) { // // BUGBUG - ugly truncation can happen here // BufferChars -= sizeof (WCHAR); } if (status & FILESTATUS_MOVED) { if (ShortFileNameFlag) { if (ISNT()) { if (!OurGetShortPathNameW (UltimateDestiny, NewFileSpec, MAX_WCHAR_PATH)) { _wcssafecpy (NewFileSpec, UltimateDestiny, BufferChars * sizeof (WCHAR)); } } else { ansiPath = ConvertWtoA (UltimateDestiny); if (!OurGetShortPathNameA (ansiPath, ansiOutput, ARRAYSIZE(ansiOutput))) { _mbssafecpy (ansiOutput, ansiPath, BufferChars); } FreeConvertedStr (ansiPath); KnownSizeAtoW (NewFileSpec, ansiOutput); } } else { _wcssafecpy (NewFileSpec, UltimateDestiny, BufferChars * sizeof (WCHAR)); } } else { _wcssafecpy (NewFileSpec, SanitizedPath, BufferChars * sizeof (WCHAR)); } if (lastWack) { AppendWackW (NewFileSpec); } } if (Operations & ALL_CONTENT_CHANGE_OPERATIONS) { status |= FILESTATUS_BACKUP; } FreePathStringW (SanitizedPath); return status; } DWORD GetFileStatusOnNtW ( IN PCWSTR FileName ) { return GetFileInfoOnNtW (FileName, NULL, 0); } PWSTR GetPathStringOnNtW ( IN PCWSTR FileName ) { PWSTR newFileName; newFileName = AllocPathStringW (MEMDB_MAX); GetFileInfoOnNtW (FileName, newFileName, MEMDB_MAX); return newFileName; } DWORD GetFileInfoOnNtA ( IN PCSTR FileName, OUT PSTR NewFileName, // OPTIONAL IN UINT BufferChars // Required if NewFileSpec is specified ) { PCWSTR UnicodeFileName; PWSTR UnicodeNewFileName = NULL; DWORD fileStatus; if (NewFileName && BufferChars) { UnicodeNewFileName = AllocPathStringW (BufferChars); } UnicodeFileName = ConvertAtoW (FileName); fileStatus = GetFileInfoOnNtW (UnicodeFileName, UnicodeNewFileName, BufferChars); FreeConvertedStr (UnicodeFileName); if (NewFileName && BufferChars) { KnownSizeWtoA (NewFileName, UnicodeNewFileName); FreePathStringW (UnicodeNewFileName); } return fileStatus; } DWORD GetFileStatusOnNtA ( IN PCSTR FileName ) { return GetFileInfoOnNtA (FileName, NULL, 0); } PSTR GetPathStringOnNtA ( IN PCSTR FileName ) { PSTR newFileName; newFileName = AllocPathStringA (MEMDB_MAX); GetFileInfoOnNtA (FileName, newFileName, MEMDB_MAX); return newFileName; } /*++ Routine Description: ExtractArgZero locates the first argument in a command line and copies it to Buffer. Assumes the break is the first space character, the ending quote of a quoted argument, or the nul terminator. Arguments: CmdLine - Specifies the full command line that has zero or more arguments Buffer - Receives the first argument on the command line if it exists, or an empty string if it does not exist. Must hold MAX_TCHAR_PATH bytes. TerminatingChars - Specifies character set that terminates the command line arg. If NULL, the set " ,;" Return Value: none --*/ PCSTR ExtractArgZeroExA ( IN PCSTR CmdLine, OUT PSTR Buffer, IN PCSTR TerminatingChars, OPTIONAL IN BOOL KeepQuotes ) { CHAR cmdLine1 [MAX_CMDLINE]; CHAR cmdLine2 [MAX_CMDLINE]; PSTR spacePtr1 [MAX_PATH]; PSTR spacePtr2 [MAX_PATH]; UINT spaceIdx = 0; PSTR ptr1 = cmdLine1; PSTR ptr2 = cmdLine2; PSTR end; CHAR saved; PCSTR s = CmdLine; BOOL inQuote = FALSE; BOOL skipQuotes = FALSE; MBCHAR ch; BOOL fullPath = FALSE; WIN32_FIND_DATAA FindData; ch = _mbsnextc (CmdLine); fullPath = (isalpha (ch) && *(_mbsinc (CmdLine)) == ':'); for (;;) { ch = _mbsnextc (s); if (ch == 0) { break; } if (ch == '\"') { skipQuotes = TRUE; inQuote = !inQuote; } else { if (!inQuote) { if (TerminatingChars && _mbschr (TerminatingChars, ch)) { break; } if (isspace (ch)) { if (spaceIdx < MAX_PATH) { spacePtr1 [spaceIdx] = ptr1; spacePtr2 [spaceIdx] = ptr2; spaceIdx ++; } else { // too many spaces. We better stop now. break; } } } } if (KeepQuotes && skipQuotes) { _copymbchar (ptr2, s); ptr2 = _mbsinc (ptr2); } if (skipQuotes) { skipQuotes = FALSE; } else { _copymbchar (ptr1, s); ptr1 = _mbsinc (ptr1); _copymbchar (ptr2, s); ptr2 = _mbsinc (ptr2); } s = _mbsinc(s); } saved = 0; *ptr1 = 0; *ptr2 = 0; end = ptr2; for (;;) { if (fullPath && DoesFileExistExA (cmdLine1, &FindData)) { break; } if (ISNT()) { if (GetOperationsOnPathA (cmdLine1)) { break; } } if (spaceIdx) { spaceIdx --; *ptr2 = saved; ptr1 = spacePtr1 [spaceIdx]; ptr2 = spacePtr2 [spaceIdx]; if (fullPath) { saved = *ptr2; } *ptr1 = 0; *ptr2 = 0; } else { *ptr2 = saved; break; } } StringCopyA (Buffer, cmdLine2); if (*ptr2) { return (CmdLine + (end - cmdLine2)); } else { return (CmdLine + (ptr2 - cmdLine2)); } } PCWSTR ExtractArgZeroExW ( IN PCWSTR CmdLine, OUT PWSTR Buffer, IN PCWSTR TerminatingChars, OPTIONAL IN BOOL KeepQuotes ) { WCHAR cmdLine1 [MAX_CMDLINE]; WCHAR cmdLine2 [MAX_CMDLINE]; PWSTR spacePtr1 [MAX_PATH]; PWSTR spacePtr2 [MAX_PATH]; UINT spaceIdx = 0; PWSTR ptr1 = cmdLine1; PWSTR ptr2 = cmdLine2; PWSTR end; WCHAR saved; PCWSTR s = CmdLine; BOOL inQuote = FALSE; BOOL skipQuotes = FALSE; BOOL fullPath = FALSE; WIN32_FIND_DATAW FindData; fullPath = (iswalpha (CmdLine[0]) && (CmdLine[1] == L':')); for (;;) { if (*s == 0) { break; } if (*s == '\"') { skipQuotes = TRUE; inQuote = !inQuote; } else { if (!inQuote) { if (TerminatingChars && wcschr (TerminatingChars, *s)) { break; } if (iswspace (*s)) { if (spaceIdx < MAX_PATH) { spacePtr1 [spaceIdx] = ptr1; spacePtr2 [spaceIdx] = ptr2; spaceIdx ++; } else { // too many spaces. We better stop now. break; } } } } if (KeepQuotes && skipQuotes) { *ptr2 = *s; ptr2 ++; } if (skipQuotes) { skipQuotes = FALSE; } else { *ptr1 = *s; ptr1 ++; *ptr2 = *s; ptr2 ++; } s ++; } saved = 0; *ptr1 = 0; *ptr2 = 0; end = ptr2; for (;;) { if (fullPath && DoesFileExistExW (cmdLine1, &FindData)) { break; } if (ISNT()) { if (GetOperationsOnPathW (cmdLine1)) { break; } } if (spaceIdx) { spaceIdx --; *ptr2 = saved; ptr1 = spacePtr1 [spaceIdx]; ptr2 = spacePtr2 [spaceIdx]; if (fullPath) { saved = *ptr2; } *ptr1 = 0; *ptr2 = 0; } else { *ptr2 = saved; break; } } StringCopyW (Buffer, cmdLine2); if (*ptr2) { return (CmdLine + (end - cmdLine2)); } else { return (CmdLine + (ptr2 - cmdLine2)); } } BOOL pIsExcludedFromBackupW ( IN PCWSTR Path, IN PCWSTR TempDir OPTIONAL ) { PCWSTR fileName; fileName = GetFileNameFromPathW (Path); if (!fileName) { return TRUE; } if (StringIMatchW (fileName, L"win386.swp")) { return TRUE; } if (StringIMatchW (fileName, L"backup.txt")) { return TRUE; } if (StringIMatchW (fileName, L"moved.txt")) { return TRUE; } if (StringIMatchW (fileName, L"delfiles.txt")) { return TRUE; } if (StringIMatchW (fileName, L"deldirs.txt")) { return TRUE; } if (StringIMatchW (Path, L"c:\\boot.ini")) { return TRUE; } if (TempDir) { if (StringIPrefixW (Path, TempDir)) { fileName = Path + TcharCountW (TempDir) + 1; if (wcschr (fileName, L'\\')) { return TRUE; } } } return FALSE; } HANDLE pCreateFileList ( IN PCSTR TempDir, IN PCSTR FileName, IN BOOL InUninstallSubDir ) { HANDLE file; PCSTR fileString; DWORD bytesWritten; CHAR decoratedFile[MAX_PATH]; PCSTR fileToUse; if (!InUninstallSubDir) { fileToUse = FileName; } else { wsprintfA (decoratedFile, "uninstall\\%s", FileName); fileToUse = decoratedFile; } fileString = JoinPathsA (TempDir, fileToUse); file = CreateFileA ( fileString, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (file == INVALID_HANDLE_VALUE) { LOGA ((LOG_ERROR,"Error creating file %s.", fileString)); FreePathStringA (fileString); return INVALID_HANDLE_VALUE; } DeclareTemporaryFileA (fileString); FreePathStringA (fileString); // // Write UNICODE signature // // Do not write as a string. FE is a lead byte. // if ((!WriteFile (file, "\xff\xfe", 2, &bytesWritten, NULL)) || (bytesWritten != 2) ) { LOG ((LOG_ERROR,"Unable to write unicode header.")); CloseHandle (file); return INVALID_HANDLE_VALUE; } return file; } BOOL WriteHashTableToFileW ( IN HANDLE File, IN HASHTABLE FileTable ) { UINT unused; HASHTABLE_ENUMW e; BOOL result = TRUE; if (!FileTable || File == INVALID_HANDLE_VALUE) { return TRUE; } if (EnumFirstHashTableStringW (&e, FileTable)) { do { if (!WriteFile (File, e.String, ByteCountW (e.String), &unused, NULL)) { result = FALSE; } if (!WriteFile (File, L"\r\n", 4, &unused, NULL)) { result = FALSE; } } while (result && EnumNextHashTableStringW (&e)); } return result; } PWSTR pGetParentDirPathFromFilePathW( IN PCWSTR FilePath, OUT PWSTR DirPath ) { PWSTR ptr; if(!FilePath || !DirPath){ MYASSERT(FALSE); return NULL; } StringCopyW(DirPath, FilePath); ptr = wcsrchr(DirPath, '\\'); if(ptr){ *ptr = '\0'; } return DirPath; } PSTR pGetParentDirPathFromFilePathA( IN PCSTR FilePath, OUT PSTR DirPath ) { PSTR ptr; if(!FilePath || !DirPath){ MYASSERT(FALSE); return NULL; } StringCopyA(DirPath, FilePath); ptr = _mbsrchr(DirPath, '\\'); if(ptr){ *ptr = '\0'; } return DirPath; } BOOL IsDirEmptyA( IN PCSTR DirPathPtr ) { TREE_ENUMA e; BOOL result; if (!EnumFirstFileInTreeExA ( &e, DirPathPtr, NULL, FALSE, FALSE, FILE_ENUM_ALL_LEVELS )) { result = TRUE; } else{ AbortEnumFileInTreeA(&e); result = FALSE; } return result; } BOOL IsDirEmptyW( IN PCWSTR DirPathPtr ) { TREE_ENUMW e; BOOL result; if (!EnumFirstFileInTreeExW ( &e, DirPathPtr, NULL, FALSE, FALSE, FILE_ENUM_ALL_LEVELS )) { result = TRUE; } else{ AbortEnumFileInTreeW(&e); result = FALSE; } return result; } VOID pAddDirWorkerW ( IN PCWSTR DirPathPtr, IN BOOL AddParentDirIfFile, OPTIONAL IN BOOL AddParentDirIfFileExist, OPTIONAL IN DWORD InitialAttributes ) { DWORD fileAttributes; BOOL addToCategory; WCHAR parentDirPath[MAX_WCHAR_PATH]; PCWSTR parentDirPathPtr; FILE_ENUMW e; // // We are adding to the empty dirs category, which once held // empty dirs, but now holds all kinds of dirs and their attributes // if (!DirPathPtr) { MYASSERT(FALSE); return; } // // Ignore root dir // if (!DirPathPtr[0] || // C !DirPathPtr[1] || // : !DirPathPtr[2] || // backslash !DirPathPtr[3] ) { return; } addToCategory = FALSE; fileAttributes = InitialAttributes; if (fileAttributes == INVALID_ATTRIBUTES) { fileAttributes = GetFileAttributesW (DirPathPtr); } if (fileAttributes != INVALID_ATTRIBUTES){ if (!(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // // Ignore files. If caller wants the parent dir, then // process it now. // if (AddParentDirIfFile) { parentDirPathPtr = pGetParentDirPathFromFilePathW (DirPathPtr, parentDirPath); if (parentDirPathPtr) { AddDirPathToEmptyDirsCategoryW (parentDirPathPtr, FALSE, FALSE); } } return; } // // This is a dir, add it to memdb, and add attributes if they aren't normal // addToCategory = TRUE; if (fileAttributes == FILE_ATTRIBUTE_DIRECTORY) { fileAttributes = 0; } } else { // // This file does not exist. If it is a dir spec, then // add it with no attributes. // if (!AddParentDirIfFile || !AddParentDirIfFileExist) { fileAttributes = 0; addToCategory = TRUE; } } if (addToCategory) { // // Add only if fileAttributes are non normal or // dir is empty // if (!fileAttributes) { if (EnumFirstFileW (&e, DirPathPtr, NULL)) { addToCategory = FALSE; AbortFileEnumW (&e); } } } if (addToCategory) { MemDbSetValueExW ( MEMDB_CATEGORY_EMPTY_DIRSW, DirPathPtr, NULL, NULL, fileAttributes, NULL ); } } VOID AddDirPathToEmptyDirsCategoryW ( IN PCWSTR DirPathPtr, IN BOOL AddParentDirIfFile, OPTIONAL IN BOOL AddParentDirIfFileExist OPTIONAL ) { pAddDirWorkerW ( DirPathPtr, AddParentDirIfFile, AddParentDirIfFileExist, INVALID_ATTRIBUTES ); } VOID pAddDirWorkerA ( IN PCSTR DirPathPtr, IN BOOL AddParentDirIfFile, OPTIONAL IN BOOL AddParentDirIfFileExist, OPTIONAL IN DWORD InitialAttributes ) { DWORD fileAttributes; BOOL addToCategory; CHAR parentDirPath[MAX_MBCHAR_PATH]; PCSTR parentDirPathPtr; FILE_ENUMA e; // // We are adding to the empty dirs category, which once held // empty dirs, but now holds all kinds of dirs and their attributes // if (!DirPathPtr) { MYASSERT(FALSE); return; } addToCategory = FALSE; fileAttributes = InitialAttributes; if (fileAttributes == INVALID_ATTRIBUTES) { fileAttributes = GetFileAttributesA (DirPathPtr); } if (fileAttributes != INVALID_ATTRIBUTES){ if (!(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // // Ignore files. If caller wants the parent dir, then // process it now. // if (AddParentDirIfFile) { parentDirPathPtr = pGetParentDirPathFromFilePathA (DirPathPtr, parentDirPath); if (parentDirPathPtr) { AddDirPathToEmptyDirsCategoryA (parentDirPathPtr, FALSE, FALSE); } } return; } // // This is a dir, add it to memdb, and add attributes if they aren't normal // addToCategory = TRUE; if (fileAttributes == FILE_ATTRIBUTE_DIRECTORY) { fileAttributes = 0; } } else { // // This file does not exist. If it is a dir spec, then // add it with no attributes. // if (!AddParentDirIfFile || !AddParentDirIfFileExist) { fileAttributes = 0; addToCategory = TRUE; } } if (addToCategory) { // // Add only if fileAttributes are non normal or // dir is empty // if (!fileAttributes) { if (EnumFirstFileA (&e, DirPathPtr, NULL)) { addToCategory = FALSE; AbortFileEnumA (&e); } } } if (addToCategory) { MemDbSetValueExA ( MEMDB_CATEGORY_EMPTY_DIRSA, DirPathPtr, NULL, NULL, fileAttributes, NULL ); } } VOID AddDirPathToEmptyDirsCategoryA( IN PCSTR DirPathPtr, IN BOOL AddParentDirIfFile, OPTIONAL IN BOOL AddParentDirIfFileExist OPTIONAL ) { pAddDirWorkerA ( DirPathPtr, AddParentDirIfFile, AddParentDirIfFileExist, INVALID_ATTRIBUTES ); } BOOL GetDiskSpaceForFilesList ( IN HASHTABLE FileTable, OUT ULARGE_INTEGER * AmountOfSpace, OPTIONAL OUT ULARGE_INTEGER * AmountOfSpaceIfCompressed, OPTIONAL IN INT CompressionFactor, OPTIONAL IN INT BootCabImagePadding, OPTIONAL IN BOOL ProcessDirs, OPTIONAL OUT ULARGE_INTEGER * AmountOfSpaceClusterAligned OPTIONAL ) /*++ Routine Description: GetDiskSpaceForFilesList calculate amount of space to store all files from FileTable hashtable. Arguments: FileTable - Specifies container for paths of files. AmountOfSpace - Receives the amount of space required to store files. AmountOfSpaceIfCompressed - Receives the amount of space required to store files, if compression will apply on files. CompressionFactor - Receives the compression factor in 0..100 range. BootCabImagePadding - Receives the backup disk space padding for additional files like boot.cab. Return Value: TRUE if IN parameters is correct, FALSE otherwise --*/ { HASHTABLE_ENUMW e; WIN32_FIND_DATAA fileAttributeData; HANDLE h; ULARGE_INTEGER sizeOfFiles; ULARGE_INTEGER fileSize; unsigned int numberOfFiles = 0; char filePathNameA[MAX_PATH * 2]; ULARGE_INTEGER BootCabImagePaddingInBytes; TCHAR DirPath[MAX_PATH * 2]; ULARGE_INTEGER clusterSize = {512, 0}; char drive[_MAX_DRIVE] = "?:"; DWORD sectorsPerCluster; DWORD bytesPerSector; if (!FileTable) { return FALSE; } sizeOfFiles.QuadPart = 0; if (EnumFirstHashTableStringW (&e, FileTable)) { do { KnownSizeUnicodeToDbcsN(filePathNameA, e.String, wcslen(e.String) + 1); h = FindFirstFileA (filePathNameA, &fileAttributeData); if(h != INVALID_HANDLE_VALUE) { FindClose (h); if(!(fileAttributeData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)){ fileSize.LowPart = fileAttributeData.nFileSizeLow; fileSize.HighPart = fileAttributeData.nFileSizeHigh; sizeOfFiles.QuadPart += fileSize.QuadPart; numberOfFiles++; if(AmountOfSpaceClusterAligned){ if(UNKNOWN_DRIVE == drive[0]){ drive[0] = filePathNameA[0]; if(GetDiskFreeSpaceA(drive, §orsPerCluster, &bytesPerSector, NULL, NULL)){ clusterSize.QuadPart = sectorsPerCluster * bytesPerSector; AmountOfSpaceClusterAligned->QuadPart = 0; } else{ DEBUGMSG((DBG_VERBOSE, "GetDiskFreeSpace failed in GetDiskSpaceForFilesList")); AmountOfSpaceClusterAligned = NULL; continue; } } AmountOfSpaceClusterAligned->QuadPart += fileSize.QuadPart % clusterSize.QuadPart? // || sizeOfFiles.QuadPart == NULL ((fileSize.QuadPart / clusterSize.QuadPart) + 1) * clusterSize.QuadPart: fileSize.QuadPart; } } if(ProcessDirs){ AddDirPathToEmptyDirsCategoryA(filePathNameA, TRUE, TRUE); } MYASSERT(DirPath); } else { //DEBUGMSGA((DBG_VERBOSE, "SETUP: GetDiskSpaceForFilesList - file does not exist: %s", filePathNameA)); } } while (EnumNextHashTableStringW (&e)); } if(!BootCabImagePadding) { BootCabImagePaddingInBytes.QuadPart = BACKUP_DISK_SPACE_PADDING_DEFAULT; DEBUGMSG ((DBG_VERBOSE, "Disk space padding for backup image: %i MB (DEFAULT)", BootCabImagePadding)); } else{ BootCabImagePaddingInBytes.QuadPart = BootCabImagePadding; BootCabImagePaddingInBytes.QuadPart <<= 20; DEBUGMSG ((DBG_VERBOSE, "Disk space padding for backup image: %i MB", BootCabImagePadding)); } if(AmountOfSpaceClusterAligned){ AmountOfSpaceClusterAligned->QuadPart += BootCabImagePaddingInBytes.QuadPart; } if(AmountOfSpace) { AmountOfSpace->QuadPart = sizeOfFiles.QuadPart + BootCabImagePaddingInBytes.QuadPart; } if(AmountOfSpaceIfCompressed) { if(!CompressionFactor) { CompressionFactor = COMPRESSION_RATE_DEFAULT; DEBUGMSG ((DBG_VERBOSE, "Compression factor: %i (DEFAULT)", CompressionFactor)); } ELSE_DEBUGMSG ((DBG_VERBOSE, "Compression factor: %i", CompressionFactor)); AmountOfSpaceIfCompressed->QuadPart = (sizeOfFiles.QuadPart * CompressionFactor) / 100 + STARTUP_INFORMATION_BYTES_NUMBER * numberOfFiles + BootCabImagePaddingInBytes.QuadPart;//boot.cab } return TRUE; } #if 0 BOOL pGetTruePathName ( IN PCWSTR InPath, OUT PWSTR OutPath ) { PCSTR start; PCSTR end; WIN32_FIND_DATAA fd; PCSTR ansiInPath; CHAR ansiOutPath[MAX_MBCHAR_PATH]; HANDLE findHandle; PSTR p; // // If not a local path, ignore it. If longer than MAX_PATH, ignore it. // if (!InPath[0] || InPath[1] != L':' || InPath[2] != L'\\') { StringCopyW (OutPath, InPath); return; } if (TcharCount (InPath) >= MAX_PATH) { StringCopyW (OutPath, InPath); return; } // // Convert down to ANSI because Win9x API requirements // ansiInPath = ConvertWtoA (InPath); // // Copy the drive spec // start = ansiInPath; end = start + 2; MYASSERT (*end == '\\'); p = ansiOutPath; StringCopyABA (p, start, end); p = GetEndOfStringA (p); // // Walk the path, and for each segment, get the full name via // FindFirstFile // start = end + 1; end = _mbschr (start, '\\'); if (!end) { end = GetEndOfStringA (start); } for (;;) { if (end > start + 1) { *p++ = '\\'; StringCopyABA (p, start, end); findHandle = FindFirstFileA (ansiOutPath, &fd); if (findHandle == INVALID_HANDLE_VALUE) { // // File/directory does not exist. Use the remaining // string as passed in. // StringCopyA (p, start); DEBUGMSGA ((DBG_ERROR, "File %s not found", ansiInPath)); KnownSizeAtoW (OutPath, ansiOutPath); FreeConvertedStr (ansiInPath); return FALSE; } // // Copy the file system's value to the out buffer // StringCopyA (p, fd.cFileName); p = GetEndOfStringA (p); FindClose (findHandle); } // // Advance to the next segment // if (*end) { start = end + 1; end = _mbschr (start, '\\'); if (!end) { end = GetEndOfStringA (start); } } else { break; } } KnownSizeAtoW (OutPath, ansiOutPath); FreeConvertedStr (ansiInPath); return TRUE; } #endif VOID pPutInBackupTable ( IN HASHTABLE BackupTable, IN HASHTABLE SourceTable, IN PCWSTR Path ) { if (pIsExcludedFromBackupW (Path, NULL)) { return; } if (!HtFindStringW (SourceTable, Path)) { HtAddStringW (SourceTable, Path); MarkFileForBackupW (Path); HtAddStringW (BackupTable, Path); } } VOID pPutInDelFileTable ( IN HASHTABLE DelFileTable, IN HASHTABLE DestTable, IN PCWSTR Path ) { if (!HtFindStringW (DestTable, Path)) { HtAddStringW (DestTable, Path); HtAddStringW (DelFileTable, Path); } } BOOL pIsWinDirProfilesPath ( IN PCWSTR PathToTest ) { static WCHAR winDirProfiles[MAX_PATH]; CHAR winDirProfilesA[MAX_PATH]; if (!(winDirProfiles[0])) { GetWindowsDirectoryA (winDirProfilesA, MAX_PATH - 9); KnownSizeAtoW (winDirProfiles, winDirProfilesA); StringCatW (winDirProfiles, L"\\Profiles"); } return StringIMatchW (PathToTest, winDirProfiles); } BOOL WriteBackupFilesA ( IN BOOL Win9xSide, IN PCSTR TempDir, OUT ULARGE_INTEGER * OutAmountOfSpaceIfCompressed, OPTIONAL OUT ULARGE_INTEGER * OutAmountOfSpace, OPTIONAL IN INT CompressionFactor, OPTIONAL IN INT BootCabImagePadding, OPTIONAL OUT ULARGE_INTEGER * OutAmountOfSpaceForDelFiles, OPTIONAL OUT ULARGE_INTEGER * OutAmountOfSpaceClusterAligned OPTIONAL ) /*++ Routine Description: WriteBackupFiles outputs the files needed by the text mode backup engine to create a backup image. This includes: backup.txt - Lists all files that need to be backed up, either because they are Win9x-specific, or are replaced during the upgrade. moved.txt - Lists all files that were moved from a Win9x location to a temporary or NT location delfiles.txt - Lists all files that are new to the upgraded OS deldirs.txt - Lists subdirectories that are new to the upgraded OS Arguments: Win9xSide - Specifies TRUE if setup is running on Win9x. This causes the files to be generated for the rollback of an incomplete setup. Specifies FALSE if setup is running on NT. This causes the files to be generated for the rollback of the final NT OS. TempDir - Specifies the setup temporary directory (%windir%\setup). OutAmountOfSpaceIfCompressed - return amount of space for backup files, if compression will apply. OutAmountOfSpace - return amount of space for backup files, if compression will not apply. CompressionFactor - receives the compression factor in 0..100 range. BootCabImagePadding - receives the backup disk space padding for additional files. Return Value: TRUE if the files were created successfully, FALSE otherwise. --*/ { MEMDB_ENUMW e; TREE_ENUMA treeEnumA; PCSTR ansiRoot; PCSTR ansiFullPath; PCWSTR unicodeFullPath; PCWSTR unicodeTempDir = NULL; PBYTE bufferRoot; PWSTR buffer; WCHAR pattern[MAX_PATH]; DWORD bytesWritten; DWORD Count = 0; PWSTR srcFile = NULL; PWSTR destFile = NULL; DWORD status; HANDLE backupFileList = INVALID_HANDLE_VALUE; HANDLE movedOutput = INVALID_HANDLE_VALUE; HANDLE delDirsList = INVALID_HANDLE_VALUE; HANDLE mkDirsList = INVALID_HANDLE_VALUE; HANDLE delFilesList = INVALID_HANDLE_VALUE; HASHTABLE backupTable = NULL; HASHTABLE delFileTable = NULL; HASHTABLE delDirTable = NULL; HASHTABLE mkDirsTable = NULL; HASHTABLE srcTable = HtAllocExW (FALSE, 0, 41911); HASHTABLE destTable = HtAllocExW (FALSE, 0, 41911); UINT type; ULARGE_INTEGER AmountOfSpace; ULARGE_INTEGER AmountOfSpaceIfCompressed; ULARGE_INTEGER AmountOfSpaceClusterAligned; ULARGE_INTEGER FreeBytesAvailableUser; ULARGE_INTEGER TotalNumberOfBytes; ULARGE_INTEGER FreeBytesAvailable; DWORD attribs; PCSTR ansiFile; PWSTR entryName; DWORD fileAttributes; PCSTR dirName; BOOL IsDirExist; UINT depth; POOLHANDLE moveListPool; MOVELISTW moveList; BOOL result = FALSE; HASHTABLE_ENUM htEnum; PWSTR thisDir; PWSTR lastDir; PWSTR p; FILEOP_ENUMW OpEnum; ALL_FILEOPS_ENUMW allOpEnum; __try { bufferRoot = MemAllocUninit ((MEMDB_MAX * 6) * sizeof (WCHAR)); if (!bufferRoot) { __leave; } srcFile = (PWSTR) bufferRoot; destFile = srcFile + MEMDB_MAX; buffer = destFile + MEMDB_MAX; thisDir = buffer + MEMDB_MAX; lastDir = thisDir + MEMDB_MAX; entryName = lastDir + MEMDB_MAX; // // Open the output files // backupTable = HtAllocExW(TRUE, 0, 31013); delFileTable = HtAllocExW(TRUE, 0, 10973); delDirTable = HtAllocExW(TRUE, 0, 23); delFilesList = pCreateFileList (TempDir, "delfiles.txt", TRUE); delDirsList = pCreateFileList (TempDir, "deldirs.txt", TRUE); moveListPool = PoolMemInitNamedPool ("Reverse Move List"); if (!moveListPool) { DEBUGMSG ((DBG_ERROR, "Can't create move list pool")); __leave; } moveList = AllocateMoveListW (moveListPool); if (!moveList) { DEBUGMSG ((DBG_ERROR, "Can't create move list")); __leave; } if (delFilesList == INVALID_HANDLE_VALUE || delDirsList == INVALID_HANDLE_VALUE ) { DEBUGMSG ((DBG_ERROR, "Can't open one of the backup files")); __leave; } if (Win9xSide) { mkDirsTable = HtAllocExW(TRUE, 0, 0); backupFileList = pCreateFileList (TempDir, "backup.txt", FALSE); mkDirsList = pCreateFileList (TempDir, "mkdirs.txt", TRUE); if (backupFileList == INVALID_HANDLE_VALUE || mkDirsList == INVALID_HANDLE_VALUE ) { DEBUGMSG ((DBG_ERROR, "Can't open one of the backup files")); __leave; } } unicodeTempDir = ConvertAtoW (TempDir); // // Go through the registered operations and put the reverse action // in the undo hash tables. As files are processed here, they are // recorded in the source and dest hash tables, so they don't end // up in multiple hash tables. // if (EnumFirstPathInOperationW (&OpEnum, OPERATION_LONG_FILE_NAME)) { do { // // Ignore excluded files // Ignore already processed files // // we fix this with case-insensitive srcTable and rely on // the first entry being the proper case //pGetTruePathName (OpEnum.Path, caseCorrectName); if (pIsExcludedFromBackupW (OpEnum.Path, unicodeTempDir)) { continue; } if (HtFindStringW (srcTable, OpEnum.Path)) { continue; } // // If this is a preserved dir, then put it in mkdirs.txt // if (mkDirsTable) { MYASSERT (Win9xSide); if (IsDirectoryMarkedAsEmptyW (OpEnum.Path)) { ansiFile = ConvertWtoA (OpEnum.Path); if (ansiFile) { attribs = GetFileAttributesA (ansiFile); if (attribs != INVALID_ATTRIBUTES) { if (attribs & FILE_ATTRIBUTE_DIRECTORY) { HtAddStringW (mkDirsTable, OpEnum.Path); } } FreeConvertedStr (ansiFile); } } } // // Process the source file given in OpEnum.Path // status = GetFileInfoOnNtW (OpEnum.Path, destFile, MEMDB_MAX); if (status & FILESTATUS_BACKUP) { // // This file is going to change -- back it up // if (backupFileList != INVALID_HANDLE_VALUE) { // // If this file is a directory, then back up the whole tree // ansiFile = ConvertWtoA (OpEnum.Path); attribs = GetFileAttributesA (ansiFile); if (attribs != INVALID_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY) ) { if (EnumFirstFileInTreeA (&treeEnumA, ansiFile, NULL, FALSE)) { do { unicodeFullPath = ConvertAtoW (treeEnumA.FullPath); pPutInBackupTable (backupTable, srcTable, unicodeFullPath); FreeConvertedStr (unicodeFullPath); } while (EnumNextFileInTreeA (&treeEnumA)); } } else if (attribs != INVALID_ATTRIBUTES) { pPutInBackupTable (backupTable, srcTable, OpEnum.Path); } FreeConvertedStr (ansiFile); } // // If the file is also going to be moved, remove the dest copy // if (status & FILESTATUS_MOVED) { HtAddStringW (delFileTable, destFile); } // // Keep track that we're done for good with the source // file and dest file // HtAddStringW (srcTable, OpEnum.Path); HtAddStringW (destTable, destFile); } else if (!Win9xSide && (status & FILESTATUS_MOVED)) { if (!pIsWinDirProfilesPath (OpEnum.Path)) { // // This file isn't going to change, but it will be moved // InsertMoveIntoListW ( moveList, destFile, OpEnum.Path ); // // Keep track that we're done for good with the source // file and dest file // HtAddStringW (srcTable, OpEnum.Path); HtAddStringW (destTable, destFile); } } // // Update UI // Count++; if (!(Count % 128)) { if (!TickProgressBar ()) { __leave; } } } while (EnumNextPathInOperationW (&OpEnum)); } // // On the Win9x side, put the temp file moves in the move hash table, so // that they are returned back to their original locations. // if (Win9xSide) { if (EnumFirstFileOpW (&allOpEnum, OPERATION_FILE_MOVE|OPERATION_TEMP_PATH, NULL)) { do { // // only take into account the first destination of a file // (when allOpEnum.PropertyNum == 0) // all other destinations are not relevant for textmode move // if (allOpEnum.PropertyValid && allOpEnum.PropertyNum == 0) { if (!pIsWinDirProfilesPath (allOpEnum.Path)) { InsertMoveIntoListW ( moveList, allOpEnum.Property, allOpEnum.Path ); } Count++; if (!(Count % 256)) { if (!TickProgressBar ()) { __leave; } } } } while (EnumNextFileOpW (&allOpEnum)); } // // 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)) { if (!pIsWinDirProfilesPath (srcFile)) { InsertMoveIntoListW ( moveList, e.szName, srcFile ); } Count++; if (!(Count % 256)) { if (!TickProgressBar ()) { __leave; } } } } 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 (&allOpEnum, OPERATION_FILE_MOVE, e.szName)) { if (!pIsWinDirProfilesPath (allOpEnum.Path)) { InsertMoveIntoListW ( moveList, allOpEnum.Property, e.szName ); } } } while (MemDbEnumNextValueW (&e)); } } // // Process the NT file list, adding files specific to NT to the delete hash table // if (delFilesList != INVALID_HANDLE_VALUE) { MemDbBuildKeyW (pattern, MEMDB_CATEGORY_NT_FILESW, L"*", NULL, NULL); if (MemDbEnumFirstValueW (&e, pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { if (MemDbBuildKeyFromOffsetW (e.dwValue, buffer, 1, NULL)) { StringCopyW (AppendWackW (buffer), e.szName); pPutInDelFileTable (delFileTable, destTable, buffer); } Count++; if (!(Count % 128)) { if (!TickProgressBar ()) { __leave; } } } while (MemDbEnumNextValueW (&e)); } } // // Append the remaining files to the backup (Win9xSide) or delete // (!Win9xSide) lists by scanning the current file system. These specs // mostly come from win95upg.inf's [Backup] section. This INF is // parsed during WINNT32 and converted into memdb operations. The // memdb operations persist to the GUI mode side automatically, as // memdb is saved before reboot to text mode and is reloaded in GUI // mode. // if (MemDbEnumFirstValueW ( &e, MEMDB_CATEGORY_CLEAN_OUTW L"\\*", MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY )) { do { type = e.dwValue; // // If on Win9x, and if type is BACKUP_SUBDIRECTORY_TREE, then // back up the entire tree as well as putting it in the // deldirs.txt file. // if (Win9xSide) { if (type == BACKUP_SUBDIRECTORY_TREE) { type = BACKUP_AND_CLEAN_TREE; } else if (type == BACKUP_AND_CLEAN_SUBDIR) { type = BACKUP_SUBDIRECTORY_FILES; } } if (type == BACKUP_FILE) { // // file // if (Win9xSide) { // // This is a single file or directory specification. // - If it exists as a file, then back it up. // - If it exists as a directory, then back up its // contents (if any). // - If it does not exist, put it in the delete list. // ansiFile = ConvertWtoA (e.szName); attribs = GetFileAttributesA (ansiFile); if (attribs != INVALID_ATTRIBUTES) { if (attribs & FILE_ATTRIBUTE_DIRECTORY) { if (EnumFirstFileInTreeA (&treeEnumA, ansiFile, NULL, FALSE)) { do { unicodeFullPath = ConvertAtoW (treeEnumA.FullPath); pPutInBackupTable (backupTable, srcTable, unicodeFullPath); FreeConvertedStr (unicodeFullPath); } while (EnumNextFileInTreeA (&treeEnumA)); } } else { pPutInBackupTable (backupTable, srcTable, e.szName); } } else { pPutInDelFileTable (delFileTable, destTable, e.szName); } FreeConvertedStr (ansiFile); } else { // // Put this file/subdirectory in the delete list // unless it was either backed up or is the // destination of a move. Note we rely on the fact // that every time a file was put in the backup table // on Win9xSide, it was also marked for explicit // backup. This causes the loop at the top of this // function to put the proper file spec in destTable. // GetNewPathForFileW (e.szName, buffer); pPutInDelFileTable (delFileTable, destTable, buffer); } } else { // // directory or tree // if (Win9xSide || type != BACKUP_AND_CLEAN_TREE) { // // Record backup or single dir cleanup // if (!Win9xSide) { GetNewPathForFileW (e.szName, buffer); ansiRoot = ConvertWtoA (buffer); } else { ansiRoot = ConvertWtoA (e.szName); } if (type == BACKUP_SUBDIRECTORY_FILES || type == BACKUP_AND_CLEAN_SUBDIR ) { depth = 1; } else { depth = FILE_ENUM_ALL_LEVELS; } if (EnumFirstFileInTreeExA ( &treeEnumA, ansiRoot, NULL, FALSE, FALSE, depth )) { do { if (treeEnumA.Directory) { continue; } unicodeFullPath = ConvertAtoW (treeEnumA.FullPath); if (Win9xSide) { // // Mark this file for backup and put it in the txt file. // pPutInBackupTable (backupTable, srcTable, unicodeFullPath); } else { // // Put this file in the delete list unless it was either // backed up or is the destination of a move. This is // the same logic as the call to pPutInDelFileTable above. // pPutInDelFileTable (delFileTable, destTable, unicodeFullPath); } FreeConvertedStr (unicodeFullPath); } while (EnumNextFileInTreeA (&treeEnumA)); } FreeConvertedStr (ansiRoot); } // // Write deldirs entry if subdir should be blown away on // rollback. (Backup files might be restored after the // subdir is blown away.) // // For backups of type BACKUP_SUBDIRECTORY_TREE, put // this subdirectory in the deldirs.txt file on the Win9x // side. Deldirs.txt will be re-written in GUI mode // without it. // if (type == BACKUP_AND_CLEAN_TREE) { // // Record tree deletes // GetNewPathForFileW (e.szName, buffer); HtAddStringW (delDirTable, buffer); if (Win9xSide) { ansiFullPath = ConvertWtoA (e.szName); AddDirPathToEmptyDirsCategoryA(ansiFullPath, TRUE, TRUE); FreeConvertedStr (ansiFullPath); } } } } while (MemDbEnumNextValue (&e)); } // // Disk Space calculation and check for availability // if(OutAmountOfSpaceIfCompressed || OutAmountOfSpace || OutAmountOfSpaceClusterAligned) { AmountOfSpace.QuadPart = 0; AmountOfSpaceIfCompressed.QuadPart = 0; AmountOfSpaceClusterAligned.QuadPart = 0; if(!GetDiskSpaceForFilesList( backupTable, &AmountOfSpace, &AmountOfSpaceIfCompressed, CompressionFactor, BootCabImagePadding, FALSE, &AmountOfSpaceClusterAligned )) { DEBUGMSG((DBG_WHOOPS, "Can't calculate disk space for files. GetDiskSpaceForFilesList - failed.\n")); } else { // // The disk space numbers include the padding necessary to ensure // a user's hard disk does not get filled completely // if (OutAmountOfSpaceIfCompressed) { OutAmountOfSpaceIfCompressed->QuadPart = AmountOfSpaceIfCompressed.QuadPart; } if (OutAmountOfSpace) { OutAmountOfSpace->QuadPart = AmountOfSpace.QuadPart; } if(OutAmountOfSpaceClusterAligned){ OutAmountOfSpaceClusterAligned->QuadPart = AmountOfSpaceClusterAligned.QuadPart; } DEBUGMSG((DBG_VERBOSE, "AmountOfSpace: %dMB\nAmountOfSpaceIfCompressed: %dMB\nAmountOfSpaceClusterAligned: %dMB", (UINT)(AmountOfSpace.QuadPart>>20), (UINT)(AmountOfSpaceIfCompressed.QuadPart>>20), (UINT)(AmountOfSpaceClusterAligned.QuadPart>>20))); } } // // Disk Space calculation for deldirs // if(OutAmountOfSpaceForDelFiles) { if(!GetDiskSpaceForFilesList( delFileTable, NULL, NULL, 0, 1, FALSE, OutAmountOfSpaceForDelFiles )) { DEBUGMSG((DBG_WHOOPS, "Can't calculate disk space for del files. GetDiskSpaceForFilesList - failed.\n")); } else { DEBUGMSG((DBG_VERBOSE, "AmountOfSpaceForDelFiles: %d MB", (UINT)(OutAmountOfSpaceForDelFiles->QuadPart>>20))); } } // // preserve attributes of all the backup file parent dirs // if (Win9xSide) { lastDir[0] = 0; if (EnumFirstHashTableStringW (&htEnum, backupTable)) { do { // // Put the dir attributes or file's parent attributes in // memdb. Optimize for the case where there are several // files in a row all from the same parent. // ansiFullPath = ConvertWtoA (htEnum.String); attribs = GetFileAttributesA (ansiFullPath); if (attribs != INVALID_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY) ) { StringCopyTcharCountW (thisDir, htEnum.String, MEMDB_MAX); p = wcsrchr (thisDir, L'\\'); if (p) { *p = 0; } _wcslwr (thisDir); MYASSERT (thisDir[0]); } else { thisDir[0] = 0; lastDir[0] = 0; if (attribs != INVALID_ATTRIBUTES) { // // Optimize for case where dir is normal // if (attribs == FILE_ATTRIBUTE_DIRECTORY) { attribs = INVALID_ATTRIBUTES; } } } // // record attributes in memdb // if (attribs != INVALID_ATTRIBUTES) { if ((!thisDir[0]) || (!StringMatchW (lastDir, thisDir))) { pAddDirWorkerA (ansiFullPath, TRUE, TRUE, attribs); StringCopyW (lastDir, thisDir); } } // // continue with loop, remembering current dir // FreeConvertedStr (ansiFullPath); Count++; if (!(Count % 256)) { if (!TickProgressBar ()) { __leave; } } } while (EnumNextHashTableStringW (&htEnum)); } } // // Transfer the empty dirs to a hash table. We could just output the // file now but to be consistent at the expense of a few milliseconds // we'll use the hash table. // if (mkDirsTable && MemDbEnumFirstValueW ( &e, MEMDB_CATEGORY_EMPTY_DIRSW L"\\*", MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY )) { do { if (!e.szName[0] || e.szName[1] != L':' || e.szName[2] != L'\\' || !e.szName[3] ) { // // Ignore roots & malformed entries // continue; } swprintf( entryName, e.dwValue? L"%s,%u": L"%s", e.szName, e.dwValue ); ansiFile = ConvertWtoA (e.szName); if (ansiFile) { attribs = GetFileAttributesA (ansiFile); if (attribs != INVALID_ATTRIBUTES) { if (attribs & FILE_ATTRIBUTE_DIRECTORY) { HtAddStringW (mkDirsTable, entryName); } } FreeConvertedStr (ansiFile); } } while (MemDbEnumNextValue (&e)); } // // blast the lists to disk // if (!WriteHashTableToFileW (backupFileList, backupTable)) { LOG ((LOG_ERROR, "Unable to write to backup.txt")); __leave; } if (!WriteHashTableToFileW (delFilesList, delFileTable)) { LOG ((LOG_ERROR, "Unable to write to delfiles.txt")); __leave; } if (!WriteHashTableToFileW (delDirsList, delDirTable)) { LOG ((LOG_ERROR, "Unable to write to deldirs.txt")); __leave; } if (!WriteHashTableToFileW (mkDirsList, mkDirsTable)) { LOG ((LOG_ERROR, "Unable to write to mkdirs.txt")); __leave; } // // Finalize move list processing. If we are on the Win9x side, then // allow nesting collisions (the second arg). // moveList = RemoveMoveListOverlapW (moveList); ansiFullPath = JoinPathsA (TempDir, "uninstall\\moved.txt"); if (!ansiFullPath) { __leave; } movedOutput = CreateFileA ( ansiFullPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); FreePathStringA (ansiFullPath); if (!OutputMoveListW (movedOutput, moveList, !Win9xSide)) { LOG ((LOG_ERROR,"Unable to write to moved.txt.")); __leave; } // // Success // result = TRUE; } __finally { if (backupFileList != INVALID_HANDLE_VALUE) { CloseHandle (backupFileList); } if (movedOutput != INVALID_HANDLE_VALUE) { CloseHandle (movedOutput); } if (delDirsList != INVALID_HANDLE_VALUE) { CloseHandle (delDirsList); } if (mkDirsList != INVALID_HANDLE_VALUE) { CloseHandle (mkDirsList); } if (delFilesList != INVALID_HANDLE_VALUE) { CloseHandle (delFilesList); } HtFree (backupTable); HtFree (delFileTable); HtFree (delDirTable); HtFree (mkDirsTable); HtFree (destTable); HtFree (srcTable); PoolMemDestroyPool (moveListPool); FreeConvertedStr (unicodeTempDir); if (bufferRoot) { FreeMem (bufferRoot); } } return result; }