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

5865 lines
140 KiB

/*++
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, &sectorsPerCluster, &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;
}