4001 lines
112 KiB
C
4001 lines
112 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ops.c
|
|
|
|
Abstract:
|
|
|
|
Implements the operation interface for the ISM. Operations are used to
|
|
track changes to state. An object can have any number of operations
|
|
applied to it, as long as the combinations are legal. Each operation
|
|
has an optional source and destination property per object.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 01-Mar-2000
|
|
|
|
Revision History:
|
|
|
|
<alias> <date> <comments>
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "ism.h"
|
|
#include "ismp.h"
|
|
|
|
#define DBG_OPS "Ops"
|
|
|
|
//
|
|
// Strings
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define OP_HASH_BUCKETS 503
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
typedef struct {
|
|
MIG_OPERATIONID OperationId;
|
|
LONGLONG SrcData;
|
|
LONGLONG DestData;
|
|
} OPERATION_PROPERTY_LINKAGE, *POPERATION_PROPERTY_LINKAGE;
|
|
|
|
typedef struct {
|
|
PUINT LinkageList;
|
|
UINT Count;
|
|
UINT Index;
|
|
POPERATION_PROPERTY_LINKAGE OpPropLinkList;
|
|
UINT OpPropCount;
|
|
GROWBUFFER SrcPropBuf;
|
|
GROWBUFFER DestPropBuf;
|
|
MIG_BLOB SrcData;
|
|
MIG_BLOB DestData;
|
|
BOOL ReturnAllPrivateOps;
|
|
} OBJECTOPERATION_HANDLE, *POBJECTOPERATION_HANDLE;
|
|
|
|
typedef struct {
|
|
PUINT LinkageList;
|
|
UINT Count;
|
|
UINT Index;
|
|
PCTSTR ObjectFromMemdb;
|
|
} OBJECTWITHOPERATION_HANDLE, *POBJECTWITHOPERATION_HANDLE;
|
|
|
|
typedef struct {
|
|
POPMFILTERCALLBACK FilterCallbackHp;
|
|
POPMFILTERCALLBACK FilterCallback;
|
|
BOOL TreeFilterHp;
|
|
BOOL TreeFilter;
|
|
BOOL NoRestoreFilterHp;
|
|
BOOL NoRestoreFilter;
|
|
POPMAPPLYCALLBACK ApplyCallbackHp;
|
|
POPMAPPLYCALLBACK ApplyCallback;
|
|
UINT CombinationsCount;
|
|
BOOL IgnoreCollision;
|
|
MIG_OPERATIONID *Combinations;
|
|
} OPERATION_DATA, *POPERATION_DATA;
|
|
|
|
typedef struct TAG_OPERATION_DATA_ITEM {
|
|
struct TAG_OPERATION_DATA_ITEM *NextBucketItem;
|
|
MIG_OPERATIONID OperationId;
|
|
POPERATION_DATA Data;
|
|
} OPERATION_DATA_ITEM, *POPERATION_DATA_ITEM;
|
|
|
|
typedef struct {
|
|
MIG_OBJECTID ObjectId;
|
|
PCMIG_BLOB SourceData;
|
|
PCMIG_BLOB DestinationData;
|
|
LONGLONG SourceDataOffset;
|
|
LONGLONG DestinationDataOffset;
|
|
} SETOPERATIONARG, *PSETOPERATIONARG;
|
|
|
|
typedef struct {
|
|
MIG_OBJECTID ObjectId;
|
|
LONGLONG SourceDataOffset;
|
|
LONGLONG DestinationDataOffset;
|
|
} SETOPERATIONARG2, *PSETOPERATIONARG2;
|
|
|
|
typedef struct TAG_GLOBALFILTER {
|
|
|
|
struct TAG_GLOBALFILTER *Next, *Prev;
|
|
UINT Priority;
|
|
|
|
MIG_PLATFORMTYPEID Platform;
|
|
MIG_OPERATIONID OperationId;
|
|
POPMFILTERCALLBACK Callback;
|
|
BOOL TreeFilter;
|
|
BOOL NoRestoreFilter;
|
|
|
|
} GLOBALFILTER, *PGLOBALFILTER;
|
|
|
|
typedef struct TAG_GLOBALEDIT {
|
|
|
|
struct TAG_GLOBALEDIT *Next, *Prev;
|
|
UINT Priority;
|
|
|
|
MIG_PLATFORMTYPEID Platform;
|
|
MIG_OPERATIONID OperationId;
|
|
POPMAPPLYCALLBACK Callback;
|
|
|
|
} GLOBALEDIT, *PGLOBALEDIT;
|
|
|
|
typedef struct TAG_GLOBALFILTERINDEX {
|
|
struct TAG_GLOBALFILTERINDEX *Next;
|
|
MIG_OBJECTTYPEID ObjectTypeId;
|
|
|
|
// normal priority
|
|
PGLOBALFILTER FilterFirstHead;
|
|
PGLOBALFILTER FilterLastHead;
|
|
PGLOBALEDIT EditFirstHead;
|
|
PGLOBALEDIT EditLastHead;
|
|
|
|
// high priority
|
|
PGLOBALFILTER FilterFirstHeadHp;
|
|
PGLOBALFILTER FilterLastHeadHp;
|
|
PGLOBALEDIT EditFirstHeadHp;
|
|
PGLOBALEDIT EditLastHeadHp;
|
|
} GLOBALFILTERINDEX, *PGLOBALFILTERINDEX;
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
PMHANDLE g_OpPool;
|
|
OPERATION_DATA_ITEM g_OpHashTable[OP_HASH_BUCKETS];
|
|
PGLOBALFILTERINDEX g_FirstGlobalOperation;
|
|
|
|
//
|
|
// Macro expansion list
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
MIG_OPERATIONID
|
|
pRegisterOperation (
|
|
IN PCTSTR OperationName,
|
|
IN BOOL Private,
|
|
IN PCTSTR CurrentGroup
|
|
);
|
|
|
|
//
|
|
// Macro expansion definition
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Code
|
|
//
|
|
|
|
PCTSTR
|
|
pGetOpNameForDebug (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
static TCHAR Name[256];
|
|
|
|
if (!IsmGetOperationName (OperationId, Name, ARRAYSIZE(Name), NULL, NULL, NULL)) {
|
|
StringCopy (Name, TEXT("<invalid operation>"));
|
|
}
|
|
|
|
return Name;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pGetOpNameForDebug2 (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
static TCHAR Name[256];
|
|
|
|
if (!IsmGetOperationName (OperationId, Name, ARRAYSIZE(Name), NULL, NULL, NULL)) {
|
|
StringCopy (Name, TEXT("<invalid operation>"));
|
|
}
|
|
|
|
return Name;
|
|
}
|
|
|
|
|
|
UINT
|
|
pComputeOperationHash (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
return (UINT) OperationId % OP_HASH_BUCKETS;
|
|
}
|
|
|
|
|
|
POPERATION_DATA_ITEM
|
|
pFindOperationBucketItem (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
UINT hash;
|
|
POPERATION_DATA_ITEM item;
|
|
|
|
hash = pComputeOperationHash (OperationId);
|
|
|
|
item = &g_OpHashTable[hash];
|
|
|
|
do {
|
|
if (item->OperationId == OperationId) {
|
|
return item;
|
|
}
|
|
|
|
item = item->NextBucketItem;
|
|
|
|
} while (item);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
POPERATION_DATA_ITEM
|
|
pGetOperationBucketItem (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
POPERATION_DATA_ITEM item;
|
|
POPERATION_DATA_ITEM newItem;
|
|
UINT hash;
|
|
|
|
hash = pComputeOperationHash (OperationId);
|
|
|
|
item = &g_OpHashTable[hash];
|
|
|
|
if (item->OperationId) {
|
|
//
|
|
// Find the last bucket item, then allocate a new bucket item
|
|
//
|
|
|
|
while (item->NextBucketItem) {
|
|
item = item->NextBucketItem;
|
|
}
|
|
|
|
newItem = (POPERATION_DATA_ITEM) PmGetMemory (g_OpPool, sizeof (OPERATION_DATA_ITEM));
|
|
ZeroMemory (newItem, sizeof (OPERATION_DATA_ITEM));
|
|
|
|
item->NextBucketItem = newItem;
|
|
item = newItem;
|
|
}
|
|
|
|
item->OperationId = OperationId;
|
|
item->Data = NULL;
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
VOID
|
|
pUpdateOperationData (
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN POPERATION_DATA Data
|
|
)
|
|
{
|
|
POPERATION_DATA_ITEM item;
|
|
|
|
item = pFindOperationBucketItem (OperationId);
|
|
MYASSERT (item);
|
|
MYASSERT (item->Data);
|
|
|
|
CopyMemory (item->Data, Data, sizeof (OPERATION_DATA));
|
|
}
|
|
|
|
|
|
BOOL
|
|
pGetOperationData (
|
|
IN MIG_OPERATIONID OperationId,
|
|
OUT POPERATION_DATA Data
|
|
)
|
|
{
|
|
POPERATION_DATA_ITEM item;
|
|
|
|
item = pFindOperationBucketItem (OperationId);
|
|
|
|
if (!item) {
|
|
ZeroMemory (Data, sizeof (OPERATION_DATA));
|
|
|
|
item = pGetOperationBucketItem (OperationId);
|
|
MYASSERT (item);
|
|
MYASSERT (!item->Data);
|
|
|
|
item->Data = (POPERATION_DATA) PmDuplicateMemory (
|
|
g_OpPool,
|
|
(PBYTE) Data,
|
|
sizeof (OPERATION_DATA)
|
|
);
|
|
return TRUE;
|
|
}
|
|
|
|
CopyMemory (Data, item->Data, sizeof (OPERATION_DATA));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pAddCombinationPair (
|
|
IN MIG_OPERATIONID OperationIdToChange,
|
|
IN MIG_OPERATIONID OperationIdForTheComboList
|
|
)
|
|
{
|
|
OPERATION_DATA data;
|
|
|
|
//
|
|
// If a data struct does not exist, create one
|
|
//
|
|
|
|
if (!pGetOperationData (OperationIdToChange, &data)) {
|
|
return;
|
|
}
|
|
|
|
if (!data.Combinations) {
|
|
|
|
data.Combinations = (MIG_OPERATIONID *) PmGetMemory (
|
|
g_OpPool,
|
|
sizeof (MIG_OPERATIONID)
|
|
);
|
|
data.CombinationsCount = 0;
|
|
|
|
} else {
|
|
|
|
data.Combinations = (MIG_OPERATIONID *) PmGetMemory (
|
|
g_OpPool,
|
|
(data.CombinationsCount + 1) * sizeof (MIG_OPERATIONID)
|
|
);
|
|
}
|
|
|
|
MYASSERT (data.Combinations);
|
|
|
|
data.Combinations[data.CombinationsCount] = OperationIdForTheComboList;
|
|
data.CombinationsCount++;
|
|
|
|
pUpdateOperationData (OperationIdToChange, &data);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pTestOperationCombination (
|
|
IN MIG_OPERATIONID OperationId1,
|
|
IN MIG_OPERATIONID OperationId2,
|
|
OUT PBOOL IgnoreCollision
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pTestOperationCombination tests if two operations can be combined. The
|
|
return result indicates if the combination is prohibited.
|
|
|
|
Arguments:
|
|
|
|
OperationId1 - Specifies the first operation to test. The ID must be
|
|
valid.
|
|
|
|
OperationId2 - Specifies the second operation to test. This ID must also be
|
|
valid.
|
|
|
|
IgnoreCollision - Receives that the collision is ignored
|
|
|
|
Return Value:
|
|
|
|
TRUE if the combination is prohibited, or FALSE if it is allowed.
|
|
|
|
--*/
|
|
|
|
{
|
|
POPERATION_DATA_ITEM item;
|
|
POPERATION_DATA data;
|
|
UINT u;
|
|
|
|
item = pFindOperationBucketItem (OperationId1);
|
|
if (!item) {
|
|
return FALSE;
|
|
}
|
|
|
|
data = item->Data;
|
|
if (!data) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (u = 0 ; u < data->CombinationsCount ; u++) {
|
|
if (data->Combinations[u] == OperationId2) {
|
|
if (IgnoreCollision) {
|
|
*IgnoreCollision = data->IgnoreCollision;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsOperationProhibitedOnObject (
|
|
IN MIG_OPERATIONID OperationIdToTest,
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN BOOL NoDebugOutput
|
|
)
|
|
{
|
|
UINT count;
|
|
UINT u;
|
|
KEYHANDLE *list = NULL;
|
|
BOOL result = TRUE;
|
|
BOOL ignoreCollision;
|
|
POPERATION_DATA_ITEM item;
|
|
|
|
__try {
|
|
|
|
//
|
|
// First check if this operation is prohibited to be combined with another operation
|
|
//
|
|
|
|
item = pFindOperationBucketItem (OperationIdToTest);
|
|
if (!item || !item->Data || !item->Data->CombinationsCount) {
|
|
//
|
|
// No prohibited pairs exist; return now
|
|
//
|
|
|
|
result = FALSE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Now check each operation set on ObjectId against the prohibited ID list
|
|
//
|
|
|
|
list = MemDbGetDoubleLinkageArrayByKeyHandle (ObjectId, OPERATION_INDEX, &count);
|
|
|
|
if (list) {
|
|
count /= sizeof (KEYHANDLE);
|
|
|
|
for (u = 0 ; u < count ; u++) {
|
|
|
|
if ((MIG_OPERATIONID) list[u] == OperationIdToTest) {
|
|
DEBUGMSG ((
|
|
DBG_VERBOSE,
|
|
"Operation %s already set on object %s; ignoring subsequent set",
|
|
pGetOpNameForDebug (OperationIdToTest),
|
|
GetObjectNameForDebugMsg (ObjectId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
if (pTestOperationCombination (OperationIdToTest, (MIG_OPERATIONID) list[u], &ignoreCollision)) {
|
|
if (!ignoreCollision &&
|
|
!NoDebugOutput) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"Can't set operation %s because it conflicts with %s",
|
|
pGetOpNameForDebug (OperationIdToTest),
|
|
pGetOpNameForDebug2 ((MIG_OPERATIONID) list[u])
|
|
));
|
|
}
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// All operations set on ObjectId are allowed to be combined with OperationIdToTest
|
|
//
|
|
|
|
result = FALSE;
|
|
}
|
|
__finally {
|
|
if (list) {
|
|
MemDbReleaseMemory (list);
|
|
INVALID_POINTER (list);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pOperationPathFromId (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
return MemDbGetKeyFromHandle ((UINT) OperationId, 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
pOperationPathFromName (
|
|
IN PCTSTR OperationName,
|
|
OUT PTSTR Path
|
|
)
|
|
{
|
|
wsprintf (Path, TEXT("Op\\%s"), OperationName);
|
|
}
|
|
|
|
|
|
LONGLONG
|
|
pGetOffsetFromDataHandle (
|
|
IN MIG_DATAHANDLE DataHandle
|
|
)
|
|
{
|
|
if (!DataHandle) {
|
|
return 0;
|
|
}
|
|
|
|
return OffsetFromPropertyDataId ((MIG_PROPERTYDATAID) DataHandle);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddProhibitedCombinations (
|
|
IN PCTSTR InfSection
|
|
)
|
|
{
|
|
INFSTRUCT is = INITINFSTRUCT_PMHANDLE;
|
|
BOOL b = FALSE;
|
|
PCTSTR mainOp;
|
|
PCTSTR mainOpModule;
|
|
PCTSTR combinationOp;
|
|
PCTSTR combinationOpModule;
|
|
UINT u;
|
|
MIG_OPERATIONID mainOpId;
|
|
MIG_OPERATIONID combinationOpId;
|
|
PTSTR p;
|
|
|
|
//
|
|
// The INF tells us about prohibited operation combinations. Given a
|
|
// specific operation ID, we need to be able to tell if it can be
|
|
// combined with another id.
|
|
//
|
|
|
|
__try {
|
|
if (InfFindFirstLine (g_IsmInf, InfSection, NULL, &is)) {
|
|
do {
|
|
|
|
mainOp = InfGetStringField (&is, 1);
|
|
|
|
if (!mainOp) {
|
|
continue;
|
|
}
|
|
|
|
p = _tcschr (mainOp, TEXT(':'));
|
|
|
|
if (p) {
|
|
mainOpModule = mainOp;
|
|
*p = 0;
|
|
mainOp = p + 1;
|
|
|
|
if (!ValidateModuleName (mainOpModule)) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_INVALID_ID_OPS,
|
|
mainOpModule,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
mainOpModule = NULL;
|
|
}
|
|
|
|
mainOpId = pRegisterOperation (mainOp, mainOpModule != NULL, mainOpModule);
|
|
|
|
if (!mainOpId) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_ISM_INF_SYNTAX_ERROR,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
u = 2;
|
|
for (;; u++) {
|
|
combinationOp = InfGetStringField (&is, u);
|
|
if (!combinationOp) {
|
|
break;
|
|
}
|
|
|
|
p = _tcschr (combinationOp, TEXT(':'));
|
|
|
|
if (p) {
|
|
combinationOpModule = combinationOp;
|
|
*p = 0;
|
|
combinationOp = p + 1;
|
|
|
|
if (!ValidateModuleName (combinationOpModule)) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_INVALID_ID_OPS,
|
|
combinationOpModule,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
combinationOpModule = NULL;
|
|
}
|
|
|
|
combinationOpId = pRegisterOperation (
|
|
combinationOp,
|
|
combinationOpModule != NULL,
|
|
combinationOpModule
|
|
);
|
|
|
|
if (!combinationOpId) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_ISM_INF_SYNTAX_ERROR,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
pAddCombinationPair (mainOpId, combinationOpId);
|
|
pAddCombinationPair (combinationOpId, mainOpId);
|
|
}
|
|
|
|
} while (InfFindNextLine (&is));
|
|
}
|
|
|
|
b = TRUE;
|
|
}
|
|
__finally {
|
|
InfCleanUpInfStruct (&is);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
pAddIgnoredCollision (
|
|
IN MIG_OPERATIONID OperationIdToChange
|
|
)
|
|
{
|
|
OPERATION_DATA data;
|
|
|
|
//
|
|
// If a data struct does not exist, create one
|
|
//
|
|
|
|
if (!pGetOperationData (OperationIdToChange, &data)) {
|
|
return;
|
|
}
|
|
|
|
data.IgnoreCollision = TRUE;
|
|
|
|
pUpdateOperationData (OperationIdToChange, &data);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddIgnoredCollisions (
|
|
IN PCTSTR InfSection
|
|
)
|
|
{
|
|
INFSTRUCT is = INITINFSTRUCT_PMHANDLE;
|
|
BOOL b = FALSE;
|
|
PCTSTR mainOp;
|
|
PCTSTR mainOpModule;
|
|
MIG_OPERATIONID mainOpId;
|
|
PTSTR p;
|
|
|
|
//
|
|
// The INF tells us about ignored operation collisions.
|
|
//
|
|
|
|
__try {
|
|
if (InfFindFirstLine (g_IsmInf, InfSection, NULL, &is)) {
|
|
do {
|
|
|
|
mainOp = InfGetStringField (&is, 1);
|
|
|
|
if (!mainOp) {
|
|
continue;
|
|
}
|
|
|
|
p = _tcschr (mainOp, TEXT(':'));
|
|
|
|
if (p) {
|
|
mainOpModule = mainOp;
|
|
*p = 0;
|
|
mainOp = p + 1;
|
|
|
|
if (!ValidateModuleName (mainOpModule)) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_INVALID_ID_OPS,
|
|
mainOpModule,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
mainOpModule = NULL;
|
|
}
|
|
|
|
mainOpId = pRegisterOperation (mainOp, mainOpModule != NULL, mainOpModule);
|
|
|
|
if (!mainOpId) {
|
|
LOG ((
|
|
LOG_WARNING,
|
|
(PCSTR) MSG_ISM_INF_SYNTAX_ERROR,
|
|
InfSection,
|
|
is.Context.Line + 1
|
|
));
|
|
continue;
|
|
}
|
|
|
|
pAddIgnoredCollision (mainOpId);
|
|
|
|
} while (InfFindNextLine (&is));
|
|
}
|
|
|
|
b = TRUE;
|
|
}
|
|
__finally {
|
|
InfCleanUpInfStruct (&is);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitializeOperations (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// The INF tells us about prohibited operation combinations. Given a
|
|
// specific operation ID, we need to be able to tell if it can be
|
|
// combined with another id.
|
|
//
|
|
|
|
g_OpPool = PmCreateNamedPool ("Operation Data");
|
|
|
|
if (!pAddIgnoredCollisions (S_IGNORED_COLLISIONS)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return pAddProhibitedCombinations (S_PROHIBITED_COMBINATIONS);
|
|
}
|
|
|
|
|
|
VOID
|
|
TerminateOperations (
|
|
VOID
|
|
)
|
|
{
|
|
PmEmptyPool (g_OpPool);
|
|
PmDestroyPool (g_OpPool);
|
|
ZeroMemory (&g_OpHashTable, sizeof (g_OpHashTable));
|
|
}
|
|
|
|
|
|
MIG_OPERATIONID
|
|
pRegisterOperation (
|
|
IN PCTSTR OperationName,
|
|
IN BOOL Private,
|
|
IN PCTSTR CurrentGroup
|
|
)
|
|
|
|
{
|
|
TCHAR operationPath[MEMDB_MAX];
|
|
TCHAR decoratedName[MEMDB_MAX];
|
|
UINT offset;
|
|
|
|
if (!IsValidCNameWithDots (OperationName)) {
|
|
DEBUGMSG ((DBG_ERROR, "operation name \"%s\" is illegal", OperationName));
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (Private && !IsValidCName (CurrentGroup)) {
|
|
DEBUGMSG ((DBG_ERROR, "group name \"%s\" is illegal", CurrentGroup));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (Private) {
|
|
wsprintf (decoratedName, TEXT("%s:%s"), CurrentGroup, OperationName);
|
|
} else {
|
|
wsprintf (decoratedName, S_COMMON TEXT(":%s"), OperationName);
|
|
}
|
|
|
|
pOperationPathFromName (decoratedName, operationPath);
|
|
|
|
if (!MarkGroupIds (operationPath)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"%s conflicts with previously registered operation",
|
|
operationPath
|
|
));
|
|
return FALSE;
|
|
}
|
|
|
|
offset = MemDbSetKey (operationPath);
|
|
|
|
if (!offset) {
|
|
EngineError ();
|
|
return 0;
|
|
}
|
|
|
|
return (MIG_OPERATIONID) offset;
|
|
}
|
|
|
|
|
|
MIG_OPERATIONID
|
|
IsmRegisterOperation (
|
|
IN PCTSTR OperationName,
|
|
IN BOOL Private
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsmRegisterOperation creates a public or private Operation and returns the
|
|
ID to the caller. If the Operation already exists, then the existing ID is
|
|
returned to the caller.
|
|
|
|
Arguments:
|
|
|
|
OperationName - Specifies the operation name to register.
|
|
Private - Specifies TRUE if the operation is owned by the calling module
|
|
only, or FALSE if it is shared by all modules. If TRUE is
|
|
specified, the caller must be in an ISM callback function.
|
|
|
|
Return Value:
|
|
|
|
The ID of the operation, or 0 if the registration failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!g_CurrentGroup && Private) {
|
|
DEBUGMSG ((DBG_ERROR, "IsmRegisterOperation called for private operation outside of ISM-managed context"));
|
|
return 0;
|
|
}
|
|
|
|
return pRegisterOperation (OperationName, Private, g_CurrentGroup);
|
|
}
|
|
|
|
|
|
MIG_OPERATIONID
|
|
IsmGetOperationGroup (
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
return (MIG_OPERATIONID) GetGroupOfId ((KEYHANDLE) OperationId);
|
|
}
|
|
|
|
|
|
PGLOBALFILTERINDEX
|
|
pGetGlobalIndex (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN BOOL Create
|
|
)
|
|
{
|
|
PGLOBALFILTERINDEX index;
|
|
|
|
ObjectTypeId &= (~PLATFORM_MASK);
|
|
|
|
index = g_FirstGlobalOperation;
|
|
while (index) {
|
|
if (index->ObjectTypeId == ObjectTypeId) {
|
|
break;
|
|
}
|
|
|
|
index = index->Next;
|
|
}
|
|
|
|
if (!index && Create) {
|
|
index = (PGLOBALFILTERINDEX) PmGetMemory (g_IsmUntrackedPool, sizeof (GLOBALFILTERINDEX));
|
|
|
|
ZeroMemory (index, sizeof (GLOBALFILTERINDEX));
|
|
|
|
index->Next = g_FirstGlobalOperation;
|
|
index->ObjectTypeId = ObjectTypeId;
|
|
|
|
g_FirstGlobalOperation = index;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRegisterGlobalFilterCallback (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN PCTSTR FunctionId,
|
|
IN POPMFILTERCALLBACK Callback,
|
|
IN BOOL TreeFilter,
|
|
IN BOOL CanHandleNoRestore
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsmRegisterGlobalFilterCallback adds a filter to the "global" filter list.
|
|
Functions are prioritized by the ism.inf section [Global Filters]. If a
|
|
function is not listed, or if no name is given, it has the lowest priority.
|
|
In addition, global filters can be prohibited from being processed with
|
|
another non-global filter through the [Prohibit Operation Combination]
|
|
section of ism.inf.
|
|
|
|
Arguments:
|
|
|
|
ObjectTypeId - Specifies the type of the object
|
|
|
|
FunctionId - Specifies the text name of the callback function, for purposes
|
|
of prioritizing and combination management
|
|
|
|
Callback - Specifies the function address to call
|
|
|
|
TreeFilter - Specifies TRUE if the callback potentially modifies a portion
|
|
of an object's node, or FALSE if it modifies the node completely
|
|
or doesn't modify the node at all
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
|
|
{
|
|
PGLOBALFILTERINDEX index;
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
PGLOBALFILTER filter;
|
|
PGLOBALFILTER *head;
|
|
PGLOBALFILTER insertAt;
|
|
|
|
if (!FunctionId) {
|
|
DEBUGMSG ((DBG_ERROR, "All global filters must have a text function ID"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Locate the index for our type
|
|
//
|
|
|
|
index = pGetGlobalIndex (ObjectTypeId, TRUE);
|
|
|
|
//
|
|
// Allocate a new filter node
|
|
//
|
|
|
|
filter = (PGLOBALFILTER) PmGetMemory (g_IsmUntrackedPool, sizeof (GLOBALFILTER));
|
|
filter->Platform = ObjectTypeId & PLATFORM_MASK;
|
|
filter->OperationId = IsmRegisterOperation (FunctionId, FALSE);
|
|
filter->TreeFilter = TreeFilter;
|
|
filter->Callback = Callback;
|
|
filter->NoRestoreFilter = CanHandleNoRestore;
|
|
|
|
//
|
|
// Insert the node into the list by priority
|
|
//
|
|
|
|
if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Filter First"), FunctionId, &is)) {
|
|
|
|
filter->Priority = is.Context.Line;
|
|
head = &index->FilterFirstHead;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Filter Last"), FunctionId, &is)) {
|
|
|
|
filter->Priority = is.Context.Line;
|
|
head = &index->FilterLastHead;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Filter First.High Priority"), FunctionId, &is)) {
|
|
|
|
filter->Priority = is.Context.Line;
|
|
head = &index->FilterFirstHeadHp;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Filter Last.High Priority"), FunctionId, &is)) {
|
|
|
|
filter->Priority = is.Context.Line;
|
|
head = &index->FilterLastHead;
|
|
|
|
} else {
|
|
|
|
filter->Priority = 0xFFFFFFFF;
|
|
head = &index->FilterLastHead;
|
|
}
|
|
|
|
insertAt = *head;
|
|
|
|
while (insertAt) {
|
|
if (insertAt->Priority >= filter->Priority) {
|
|
insertAt = insertAt->Prev;
|
|
break;
|
|
}
|
|
|
|
insertAt = insertAt->Next;
|
|
}
|
|
|
|
if (insertAt) {
|
|
filter->Next = insertAt->Next;
|
|
insertAt->Next = filter;
|
|
|
|
if (filter->Next) {
|
|
filter->Next->Prev = filter;
|
|
}
|
|
} else {
|
|
*head = filter;
|
|
filter->Next = NULL;
|
|
}
|
|
|
|
filter->Prev = insertAt;
|
|
|
|
InfCleanUpInfStruct (&is);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRegisterOperationFilterCallback (
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN POPMFILTERCALLBACK Callback,
|
|
IN BOOL TreeFilter,
|
|
IN BOOL HighPriority,
|
|
IN BOOL CanHandleNoRestore
|
|
)
|
|
{
|
|
OPERATION_DATA data;
|
|
|
|
//
|
|
// If a data struct does not exist, create one
|
|
//
|
|
|
|
if (!pGetOperationData (OperationId, &data)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (HighPriority) {
|
|
|
|
if (data.FilterCallbackHp) {
|
|
DEBUGMSG ((DBG_ERROR, "High Priority Filter Callback for operation 0x%08X already registered", OperationId));
|
|
return FALSE;
|
|
}
|
|
|
|
data.FilterCallbackHp = Callback;
|
|
data.TreeFilterHp = TreeFilter;
|
|
data.NoRestoreFilterHp = CanHandleNoRestore;
|
|
|
|
} else {
|
|
|
|
if (data.FilterCallback) {
|
|
DEBUGMSG ((DBG_ERROR, "Filter Callback for operation 0x%08X already registered", OperationId));
|
|
return FALSE;
|
|
}
|
|
|
|
data.FilterCallback = Callback;
|
|
data.TreeFilter = TreeFilter;
|
|
data.NoRestoreFilter = CanHandleNoRestore;
|
|
|
|
}
|
|
|
|
pUpdateOperationData (OperationId, &data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
IsmRegisterGlobalApplyCallback (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN PCTSTR FunctionId,
|
|
IN POPMAPPLYCALLBACK Callback
|
|
)
|
|
{
|
|
PGLOBALFILTERINDEX index;
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
PGLOBALEDIT editFn;
|
|
PGLOBALEDIT *head;
|
|
PGLOBALEDIT insertAt;
|
|
|
|
if (!FunctionId) {
|
|
DEBUGMSG ((DBG_ERROR, "All global content editor callbacks must have a text function ID"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Locate the index for our type
|
|
//
|
|
|
|
index = pGetGlobalIndex (ObjectTypeId, TRUE);
|
|
|
|
//
|
|
// Allocate a new content edit node
|
|
//
|
|
|
|
editFn = (PGLOBALEDIT) PmGetMemory (g_IsmUntrackedPool, sizeof (GLOBALEDIT));
|
|
editFn->Platform = ObjectTypeId & PLATFORM_MASK;
|
|
editFn->OperationId = IsmRegisterOperation (FunctionId, FALSE);
|
|
editFn->Callback = Callback;
|
|
|
|
//
|
|
// Insert the node into the list by priority
|
|
//
|
|
|
|
if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Apply First"), FunctionId, &is)) {
|
|
|
|
editFn->Priority = is.Context.Line;
|
|
head = &index->EditFirstHead;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Apply Last"), FunctionId, &is)) {
|
|
|
|
editFn->Priority = is.Context.Line;
|
|
head = &index->EditLastHead;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Apply First.High Priority"), FunctionId, &is)) {
|
|
|
|
editFn->Priority = is.Context.Line;
|
|
head = &index->EditFirstHeadHp;
|
|
|
|
} else if (InfFindFirstLine (g_IsmInf, TEXT("Global Operations.Apply Last.High Priority"), FunctionId, &is)) {
|
|
|
|
editFn->Priority = is.Context.Line;
|
|
head = &index->EditLastHead;
|
|
|
|
} else {
|
|
|
|
editFn->Priority = 0xFFFFFFFF;
|
|
head = &index->EditLastHead;
|
|
}
|
|
|
|
insertAt = *head;
|
|
|
|
while (insertAt) {
|
|
if (insertAt->Priority >= editFn->Priority) {
|
|
insertAt = insertAt->Prev;
|
|
break;
|
|
}
|
|
|
|
insertAt = insertAt->Next;
|
|
}
|
|
|
|
if (insertAt) {
|
|
editFn->Next = insertAt->Next;
|
|
insertAt->Next = editFn;
|
|
|
|
if (editFn->Next) {
|
|
editFn->Next->Prev = editFn;
|
|
}
|
|
} else {
|
|
*head = editFn;
|
|
editFn->Next = NULL;
|
|
}
|
|
|
|
editFn->Prev = insertAt;
|
|
|
|
InfCleanUpInfStruct (&is);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmRegisterOperationApplyCallback (
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN POPMAPPLYCALLBACK Callback,
|
|
IN BOOL HighPriority
|
|
)
|
|
{
|
|
OPERATION_DATA data;
|
|
|
|
//
|
|
// If a data struct does not exist, create one
|
|
//
|
|
|
|
if (!pGetOperationData (OperationId, &data)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (HighPriority) {
|
|
if (data.ApplyCallbackHp) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"High Priority Apply Callback for operation %s already registered",
|
|
pGetOpNameForDebug (OperationId)
|
|
));
|
|
return FALSE;
|
|
}
|
|
|
|
data.ApplyCallbackHp = Callback;
|
|
|
|
} else {
|
|
if (data.ApplyCallback) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"Apply Callback for operation %s already registered",
|
|
pGetOpNameForDebug (OperationId)
|
|
));
|
|
return FALSE;
|
|
}
|
|
|
|
data.ApplyCallback = Callback;
|
|
}
|
|
|
|
pUpdateOperationData (OperationId, &data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pGetOperationName (
|
|
IN MIG_OPERATIONID OperationId,
|
|
OUT PTSTR OperationName, OPTIONAL
|
|
IN UINT OperationNameBufChars,
|
|
OUT PBOOL Private, OPTIONAL
|
|
OUT PBOOL BelongsToMe, OPTIONAL
|
|
OUT PUINT ObjectReferences, OPTIONAL
|
|
IN BOOL ReturnAllPrivateOps
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGetOperationName obtains the operation text name from a numeric ID. It
|
|
also identifies private and owned operations.
|
|
|
|
Arguments:
|
|
|
|
OperationId - Specifies the operation ID to look up.
|
|
OperationName - Receives the operation name. The name is filled for
|
|
all valid OperationId values, even when the return
|
|
value is FALSE.
|
|
OperationNameBufChars - Specifies the number of TCHARs that OperationName
|
|
can hold, including the nul terminator.
|
|
Private - Receives TRUE if the operation is private, or FALSE
|
|
if it is public.
|
|
BelongsToMe - Receives TRUE if the operation is private and
|
|
belongs to the caller, FALSE otherwise.
|
|
ObjectReferences - Receives the number of objects that reference the
|
|
operation
|
|
|
|
Return Value:
|
|
|
|
TRUE if the operation is public, or if the operation is private and belongs to
|
|
the caller.
|
|
|
|
FALSE if the operation is private and belongs to someone else. OperationName,
|
|
Private and BelongsToMe are valid in this case.
|
|
|
|
FALSE if OperationId is not valid. Operationname, Private and BelongsToMe are
|
|
not modified in this case. Do not use this function to test if OperationId
|
|
is valid or not.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PCTSTR operationPath = NULL;
|
|
PCTSTR start;
|
|
PTSTR p, q;
|
|
BOOL privateOperation = FALSE;
|
|
BOOL groupMatch = FALSE;
|
|
BOOL result = FALSE;
|
|
UINT references;
|
|
PUINT linkageList;
|
|
|
|
__try {
|
|
//
|
|
// Did caller specify an item id?
|
|
//
|
|
|
|
if (!IsItemId ((KEYHANDLE) OperationId)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"IsmGetOperationName: must specify item id, not group id"
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Get the operation path from memdb, then parse it for group and name
|
|
//
|
|
|
|
operationPath = pOperationPathFromId (OperationId);
|
|
if (!operationPath) {
|
|
__leave;
|
|
}
|
|
|
|
p = _tcschr (operationPath, TEXT('\\'));
|
|
if (!p) {
|
|
__leave;
|
|
}
|
|
|
|
start = _tcsinc (p);
|
|
p = _tcschr (start, TEXT(':'));
|
|
|
|
if (!p) {
|
|
__leave;
|
|
}
|
|
|
|
q = _tcsinc (p);
|
|
*p = 0;
|
|
|
|
if (StringIMatch (start, S_COMMON)) {
|
|
|
|
//
|
|
// This operation is a global operation.
|
|
//
|
|
|
|
privateOperation = FALSE;
|
|
groupMatch = TRUE;
|
|
|
|
} else if (g_CurrentGroup || ReturnAllPrivateOps) {
|
|
|
|
//
|
|
// This operation is private. Check if it is ours.
|
|
//
|
|
|
|
privateOperation = TRUE;
|
|
|
|
if (g_CurrentGroup && StringIMatch (start, g_CurrentGroup)) {
|
|
groupMatch = TRUE;
|
|
} else {
|
|
groupMatch = ReturnAllPrivateOps;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// This is a private operation, but the caller is not
|
|
// a module that can own operations.
|
|
//
|
|
|
|
DEBUGMSG ((DBG_WARNING, "IsmGetOperationName: Caller cannot own private operations"));
|
|
}
|
|
|
|
//
|
|
// Copy the name to the buffer, update outbound BOOLs, set result
|
|
//
|
|
|
|
if (OperationName && OperationNameBufChars >= sizeof (TCHAR)) {
|
|
StringCopyByteCount (OperationName, q, OperationNameBufChars * sizeof (TCHAR));
|
|
}
|
|
|
|
if (Private) {
|
|
*Private = privateOperation;
|
|
}
|
|
|
|
if (BelongsToMe) {
|
|
*BelongsToMe = privateOperation && groupMatch;
|
|
}
|
|
|
|
if (ObjectReferences) {
|
|
linkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
OperationId,
|
|
OPERATION_INDEX,
|
|
&references
|
|
);
|
|
|
|
references /= SIZEOF(KEYHANDLE);
|
|
|
|
if (linkageList) {
|
|
MemDbReleaseMemory (linkageList);
|
|
INVALID_POINTER (linkageList);
|
|
} else {
|
|
references = 0;
|
|
}
|
|
|
|
*ObjectReferences = references;
|
|
}
|
|
|
|
if (groupMatch) {
|
|
result = TRUE;
|
|
}
|
|
}
|
|
__finally {
|
|
if (operationPath) { //lint !e774
|
|
MemDbReleaseMemory (operationPath);
|
|
operationPath = NULL;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmGetOperationName (
|
|
IN MIG_OPERATIONID OperationId,
|
|
OUT PTSTR OperationName, OPTIONAL
|
|
IN UINT OperationNameBufChars,
|
|
OUT PBOOL Private, OPTIONAL
|
|
OUT PBOOL BelongsToMe, OPTIONAL
|
|
OUT PUINT ObjectReferences OPTIONAL
|
|
)
|
|
{
|
|
return pGetOperationName (
|
|
OperationId,
|
|
OperationName,
|
|
OperationNameBufChars,
|
|
Private,
|
|
BelongsToMe,
|
|
ObjectReferences,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
POPERATION_PROPERTY_LINKAGE
|
|
pFindOperationPropertyLinkage (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationIdToFind,
|
|
OUT PBYTE *BlockToFree,
|
|
OUT PUINT BlockSize
|
|
)
|
|
{
|
|
POPERATION_PROPERTY_LINKAGE linkage;
|
|
UINT linkageCount;
|
|
POPERATION_PROPERTY_LINKAGE result = NULL;
|
|
UINT u;
|
|
|
|
__try {
|
|
linkage = (POPERATION_PROPERTY_LINKAGE) MemDbGetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
BlockSize
|
|
);
|
|
|
|
linkageCount = *BlockSize / sizeof (OPERATION_PROPERTY_LINKAGE);
|
|
|
|
if (!linkage || !linkageCount) {
|
|
__leave;
|
|
}
|
|
|
|
for (u = 0 ; u < linkageCount ; u++) {
|
|
|
|
if (linkage[u].OperationId == OperationIdToFind) {
|
|
result = linkage + u;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
__finally {
|
|
if (!result && linkage) {
|
|
MemDbReleaseMemory (linkage);
|
|
INVALID_POINTER (linkage);
|
|
} else {
|
|
*BlockToFree = (PBYTE) linkage;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetOperationOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN PCMIG_BLOB SourceData, OPTIONAL
|
|
IN PCMIG_BLOB DestinationData, OPTIONAL
|
|
IN BOOL QueryOnly,
|
|
IN OUT PLONGLONG SourceDataOffset, OPTIONAL
|
|
IN OUT PLONGLONG DestinationDataOffset OPTIONAL
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
OPERATION_PROPERTY_LINKAGE linkage;
|
|
POPERATION_PROPERTY_LINKAGE reuseLinkage;
|
|
PBYTE freeMe;
|
|
UINT linkageSize;
|
|
KEYHANDLE *list = NULL;
|
|
|
|
__try {
|
|
//
|
|
// Is the operation or object locked?
|
|
//
|
|
|
|
if (TestLock (ObjectId, (KEYHANDLE) OperationId)) {
|
|
SetLastError (ERROR_LOCKED);
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't set operation %s on %s because of lock",
|
|
pGetOpNameForDebug (OperationId),
|
|
GetObjectNameForDebugMsg (ObjectId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Does this operation conflict with itself or another operation?
|
|
//
|
|
|
|
if (pIsOperationProhibitedOnObject (OperationId, ObjectId, FALSE)) {
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If query only, return success
|
|
//
|
|
|
|
if (QueryOnly) {
|
|
result = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Add the operaiton. First, store the properties in property.dat
|
|
//
|
|
|
|
ZeroMemory (&linkage, sizeof (linkage));
|
|
|
|
if (SourceData || SourceDataOffset) {
|
|
|
|
if (SourceDataOffset && *SourceDataOffset) {
|
|
linkage.SrcData = *SourceDataOffset;
|
|
} else {
|
|
linkage.SrcData = AppendProperty (SourceData);
|
|
|
|
if (!linkage.SrcData) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't append src property"));
|
|
__leave;
|
|
}
|
|
|
|
if (SourceDataOffset) {
|
|
*SourceDataOffset = linkage.SrcData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DestinationData || DestinationDataOffset) {
|
|
|
|
if (DestinationDataOffset && *DestinationDataOffset) {
|
|
linkage.DestData = *DestinationDataOffset;
|
|
} else {
|
|
linkage.DestData = AppendProperty (DestinationData);
|
|
|
|
if (!linkage.DestData) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't append dest property"));
|
|
__leave;
|
|
}
|
|
|
|
if (DestinationDataOffset) {
|
|
*DestinationDataOffset = linkage.DestData;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Establish linkage between the object, operation and properties
|
|
//
|
|
|
|
if (SourceData || SourceDataOffset ||
|
|
DestinationData || DestinationDataOffset
|
|
) {
|
|
|
|
linkage.OperationId = OperationId;
|
|
|
|
reuseLinkage = pFindOperationPropertyLinkage (
|
|
ObjectId,
|
|
0,
|
|
&freeMe,
|
|
&linkageSize
|
|
);
|
|
|
|
if (reuseLinkage) {
|
|
//
|
|
// Recovery case -- reuse an empty spot in the blob
|
|
//
|
|
|
|
CopyMemory (reuseLinkage, &linkage, sizeof (linkage));
|
|
|
|
if (!MemDbSetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
freeMe,
|
|
linkageSize
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't update unordered operation blob"));
|
|
__leave;
|
|
}
|
|
|
|
MemDbReleaseMemory (freeMe);
|
|
INVALID_POINTER (freeMe);
|
|
|
|
} else {
|
|
//
|
|
// New case -- add the struct to the end of the blob
|
|
//
|
|
|
|
if (!MemDbGrowUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
(PBYTE) &linkage,
|
|
sizeof (linkage)
|
|
)) {
|
|
|
|
DEBUGMSG ((DBG_ERROR, "Can't grow operation property linkage"));
|
|
__leave;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!MemDbAddDoubleLinkageByKeyHandle (ObjectId, OperationId, OPERATION_INDEX)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't link object to operation"));
|
|
EngineError ();
|
|
__leave;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
if (list) {
|
|
MemDbReleaseMemory (list);
|
|
INVALID_POINTER (list);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetOperationGroup (
|
|
IN KEYHANDLE OperationId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
PSETOPERATIONARG myArg = (PSETOPERATIONARG) Arg;
|
|
|
|
return pSetOperationOnObjectId (
|
|
myArg->ObjectId,
|
|
(MIG_OPERATIONID) OperationId,
|
|
myArg->SourceData,
|
|
myArg->DestinationData,
|
|
FirstPass,
|
|
&myArg->SourceDataOffset,
|
|
&myArg->DestinationDataOffset
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetOperationGroup2 (
|
|
IN KEYHANDLE OperationId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
PSETOPERATIONARG2 myArg = (PSETOPERATIONARG2) Arg;
|
|
|
|
return pSetOperationOnObjectId (
|
|
myArg->ObjectId,
|
|
(MIG_OPERATIONID) OperationId,
|
|
NULL,
|
|
NULL,
|
|
FirstPass,
|
|
myArg->SourceDataOffset ? &myArg->SourceDataOffset : NULL,
|
|
myArg->DestinationDataOffset ? &myArg->DestinationDataOffset : NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmSetOperationOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN PCMIG_BLOB SourceData, OPTIONAL
|
|
IN PCMIG_BLOB DestinationData OPTIONAL
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
SETOPERATIONARG myArg;
|
|
|
|
myArg.ObjectId = ObjectId;
|
|
myArg.SourceData = SourceData;
|
|
myArg.DestinationData = DestinationData;
|
|
myArg.SourceDataOffset = 0;
|
|
myArg.DestinationDataOffset = 0;
|
|
|
|
rc = RecurseForGroupItems (
|
|
OperationId,
|
|
pSetOperationGroup,
|
|
(ULONG_PTR) &myArg,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pSetOperationOnObjectId (
|
|
ObjectId,
|
|
OperationId,
|
|
SourceData,
|
|
DestinationData,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmSetOperationOnObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN PCMIG_BLOB SourceData, OPTIONAL
|
|
IN PCMIG_BLOB DestinationData OPTIONAL
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = GetObjectIdForModification (ObjectTypeId, EncodedObjectName);
|
|
|
|
if (objectId) {
|
|
result = IsmSetOperationOnObjectId (
|
|
objectId,
|
|
OperationId,
|
|
SourceData,
|
|
DestinationData
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
MIG_DATAHANDLE
|
|
IsmRegisterOperationData (
|
|
IN PCMIG_BLOB Data
|
|
)
|
|
{
|
|
return (MIG_DATAHANDLE) IsmRegisterPropertyData (Data);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmSetOperationOnObjectId2 (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN MIG_DATAHANDLE SourceData, OPTIONAL
|
|
IN MIG_DATAHANDLE DestinationData OPTIONAL
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
SETOPERATIONARG2 myArg;
|
|
|
|
myArg.ObjectId = ObjectId;
|
|
if (SourceData) {
|
|
myArg.SourceDataOffset = pGetOffsetFromDataHandle (SourceData);
|
|
if (!myArg.SourceDataOffset) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
myArg.SourceDataOffset = 0;
|
|
}
|
|
|
|
if (DestinationData) {
|
|
myArg.DestinationDataOffset = pGetOffsetFromDataHandle (DestinationData);
|
|
if (!myArg.DestinationDataOffset) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
myArg.DestinationDataOffset = 0;
|
|
}
|
|
|
|
rc = RecurseForGroupItems (
|
|
OperationId,
|
|
pSetOperationGroup2,
|
|
(ULONG_PTR) &myArg,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pSetOperationOnObjectId (
|
|
ObjectId,
|
|
OperationId,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
SourceData ? &myArg.SourceDataOffset : NULL,
|
|
DestinationData ? &myArg.DestinationDataOffset : NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmSetOperationOnObject2 (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN MIG_DATAHANDLE SourceData, OPTIONAL
|
|
IN MIG_DATAHANDLE DestinationData OPTIONAL
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = GetObjectIdForModification (ObjectTypeId, EncodedObjectName);
|
|
|
|
if (objectId) {
|
|
result = IsmSetOperationOnObjectId2 (
|
|
objectId,
|
|
OperationId,
|
|
SourceData,
|
|
DestinationData
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmLockOperation (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
LockHandle (ObjectId, (KEYHANDLE) OperationId);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pClearOperationOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId,
|
|
IN BOOL QueryOnly
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
POPERATION_PROPERTY_LINKAGE linkage;
|
|
PBYTE freeMe;
|
|
UINT linkageSize;
|
|
|
|
__try {
|
|
//
|
|
// Is the operation or object locked?
|
|
//
|
|
|
|
if (TestLock (ObjectId, (KEYHANDLE) OperationId)) {
|
|
SetLastError (ERROR_LOCKED);
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Can't remove operation %s on %s because of lock",
|
|
pGetOpNameForDebug (OperationId),
|
|
GetObjectNameForDebugMsg (ObjectId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If query only, return success
|
|
//
|
|
|
|
if (QueryOnly) {
|
|
result = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Find the reference to this operation within the object's unordered blob
|
|
//
|
|
|
|
linkage = pFindOperationPropertyLinkage (
|
|
ObjectId,
|
|
OperationId,
|
|
&freeMe,
|
|
&linkageSize
|
|
);
|
|
|
|
if (linkage) {
|
|
ZeroMemory (linkage, sizeof (OPERATION_PROPERTY_LINKAGE));
|
|
|
|
if (!MemDbSetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
freeMe,
|
|
linkageSize
|
|
)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't reset unordered operation blob"));
|
|
__leave;
|
|
}
|
|
|
|
MemDbReleaseMemory (freeMe);
|
|
INVALID_POINTER (freeMe);
|
|
}
|
|
|
|
//
|
|
// Remove object-to-operation linkage
|
|
//
|
|
|
|
result = MemDbDeleteDoubleLinkageByKeyHandle (
|
|
ObjectId,
|
|
OperationId,
|
|
OPERATION_INDEX
|
|
);
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pClearOperationGroup (
|
|
IN KEYHANDLE OperationId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
return pClearOperationOnObjectId (
|
|
(MIG_OBJECTID) Arg,
|
|
(MIG_OPERATIONID) OperationId,
|
|
FirstPass
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmClearOperationOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
|
|
//
|
|
// If OperationId is a group, remove all operations in the group
|
|
//
|
|
|
|
rc = RecurseForGroupItems (
|
|
OperationId,
|
|
pClearOperationGroup,
|
|
(ULONG_PTR) ObjectId,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pClearOperationOnObjectId (ObjectId, OperationId, FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmClearOperationOnObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
BOOL result = FALSE;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
result = IsmClearOperationOnObjectId (objectId, OperationId);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pIsOperationSetOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
PCTSTR groupKey;
|
|
PCTSTR enumKey = NULL;
|
|
BOOL result = FALSE;
|
|
BOOL done = FALSE;
|
|
MEMDB_ENUM e;
|
|
|
|
__try {
|
|
//
|
|
// Did caller specify an item id?
|
|
//
|
|
|
|
if (!IsItemId ((KEYHANDLE) OperationId)) {
|
|
if (IsGroupId ((KEYHANDLE) OperationId)) {
|
|
|
|
groupKey = MemDbGetKeyFromHandle (OperationId, 0);
|
|
enumKey = JoinText (groupKey, TEXT(".*"));
|
|
MemDbReleaseMemory (groupKey);
|
|
|
|
//
|
|
// Enumerate all operations (skip operation subgroups)
|
|
//
|
|
|
|
if (MemDbEnumFirst (
|
|
&e,
|
|
enumKey,
|
|
ENUMFLAG_NORMAL,
|
|
ENUMLEVEL_ALLLEVELS,
|
|
ENUMLEVEL_ALLLEVELS
|
|
)) {
|
|
|
|
do {
|
|
if (IsItemId (e.KeyHandle)) {
|
|
//
|
|
// Check if at least one operation is set
|
|
//
|
|
|
|
if (IsmIsOperationSetOnObjectId (
|
|
ObjectId,
|
|
(MIG_OPERATIONID) e.KeyHandle
|
|
)) {
|
|
MemDbAbortEnum (&e);
|
|
result = TRUE;
|
|
done = TRUE;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
} while (MemDbEnumNext (&e));
|
|
MemDbAbortEnum (&e);
|
|
}
|
|
|
|
done = TRUE;
|
|
__leave;
|
|
|
|
} else {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"IsmIsOperationSetOnObjectId: operation id is invalid"
|
|
));
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
if (enumKey) {
|
|
FreeText (enumKey);
|
|
INVALID_POINTER (enumKey);
|
|
}
|
|
}
|
|
|
|
if (done) {
|
|
return result;
|
|
}
|
|
|
|
return MemDbTestDoubleLinkageByKeyHandle (
|
|
ObjectId,
|
|
OperationId,
|
|
OPERATION_INDEX
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pQueryOperationGroup (
|
|
IN KEYHANDLE OperationId,
|
|
IN BOOL FirstPass,
|
|
IN ULONG_PTR Arg
|
|
)
|
|
{
|
|
return pIsOperationSetOnObjectId (
|
|
(MIG_OBJECTID) Arg,
|
|
(MIG_OPERATIONID) OperationId
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmIsOperationSetOnObjectId (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
RECURSERETURN rc;
|
|
|
|
//
|
|
// If OperationId is a group, query all operations in the group
|
|
//
|
|
|
|
rc = RecurseForGroupItems (
|
|
OperationId,
|
|
pQueryOperationGroup,
|
|
(ULONG_PTR) ObjectId,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
if (rc == RECURSE_FAIL) {
|
|
return FALSE;
|
|
} else if (rc == RECURSE_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
MYASSERT (rc == RECURSE_NOT_NEEDED);
|
|
|
|
return pIsOperationSetOnObjectId (ObjectId, OperationId);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmIsOperationSetOnObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
return IsmIsOperationSetOnObjectId (objectId, OperationId);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmGetObjectOperationDataById (
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN MIG_OPERATIONID OperationId,
|
|
OUT PBYTE Buffer, OPTIONAL
|
|
IN UINT BufferSize,
|
|
OUT PUINT BufferSizeNeeded, OPTIONAL
|
|
OUT PMIG_BLOBTYPE Type, OPTIONAL
|
|
IN BOOL DestinationData
|
|
)
|
|
{
|
|
POPERATION_PROPERTY_LINKAGE linkage = NULL;
|
|
UINT dataCount;
|
|
BOOL result = FALSE;
|
|
GROWBUFFER tempBuffer = INIT_GROWBUFFER;
|
|
LONGLONG offset;
|
|
UINT size;
|
|
DWORD error = ERROR_SUCCESS;
|
|
PBYTE freeMe = NULL;
|
|
|
|
__try {
|
|
//
|
|
// Obtain the linkage between the operation and its data
|
|
//
|
|
|
|
linkage = pFindOperationPropertyLinkage (
|
|
ObjectId,
|
|
OperationId,
|
|
&freeMe,
|
|
&dataCount
|
|
);
|
|
|
|
if (!linkage) {
|
|
//
|
|
// No data
|
|
//
|
|
|
|
__leave;
|
|
}
|
|
|
|
offset = DestinationData ? linkage->DestData : linkage->SrcData;
|
|
|
|
if (!offset) {
|
|
//
|
|
// No data
|
|
//
|
|
|
|
__leave;
|
|
}
|
|
|
|
if (!GetProperty (offset, NULL, NULL, &size, Type)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error getting property instance header from dat file"));
|
|
error = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
if (BufferSizeNeeded) {
|
|
*BufferSizeNeeded = size;
|
|
}
|
|
|
|
//
|
|
// If a buffer was specified, check its size and fill it if possible
|
|
//
|
|
|
|
if (Buffer) {
|
|
if (BufferSize >= size) {
|
|
if (!GetProperty (offset, NULL, Buffer, NULL, NULL)) {
|
|
DEBUGMSG ((DBG_ERROR, "Error reading property data from dat file"));
|
|
|
|
//
|
|
// error code is one of the file api error codes
|
|
//
|
|
|
|
error = GetLastError();
|
|
__leave;
|
|
}
|
|
} else {
|
|
error = ERROR_MORE_DATA;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
MemDbReleaseMemory (freeMe);
|
|
INVALID_POINTER (freeMe);
|
|
|
|
GbFree (&tempBuffer);
|
|
}
|
|
|
|
SetLastError (error);
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmGetObjectOperationData (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN MIG_OPERATIONID OperationId,
|
|
OUT PBYTE Buffer, OPTIONAL
|
|
IN UINT BufferSize,
|
|
OUT PUINT BufferSizeNeeded, OPTIONAL
|
|
OUT PMIG_BLOBTYPE Type, OPTIONAL
|
|
IN BOOL DestinationData
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
return IsmGetObjectOperationDataById (
|
|
objectId,
|
|
OperationId,
|
|
Buffer,
|
|
BufferSize,
|
|
BufferSizeNeeded,
|
|
Type,
|
|
DestinationData
|
|
);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pEnumFirstObjectOperationById (
|
|
OUT PMIG_OBJECTOPERATION_ENUM EnumPtr,
|
|
IN MIG_OBJECTID ObjectId,
|
|
IN BOOL ReturnAllPrivateOps
|
|
)
|
|
{
|
|
POBJECTOPERATION_HANDLE handle;
|
|
BOOL b = TRUE;
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTOPERATION_ENUM));
|
|
|
|
EnumPtr->Handle = MemAllocZeroed (sizeof (OBJECTOPERATION_HANDLE));
|
|
handle = (POBJECTOPERATION_HANDLE) EnumPtr->Handle;
|
|
handle->ReturnAllPrivateOps = ReturnAllPrivateOps;
|
|
|
|
//
|
|
// Obtain the linkage up front
|
|
//
|
|
|
|
handle->LinkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
&handle->Count
|
|
);
|
|
|
|
handle->Count /= sizeof(KEYHANDLE);
|
|
|
|
if (!handle->LinkageList || !handle->Count) {
|
|
IsmAbortObjectOperationEnum (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
handle->OpPropLinkList = (POPERATION_PROPERTY_LINKAGE) MemDbGetUnorderedBlobByKeyHandle (
|
|
ObjectId,
|
|
OPERATION_INDEX,
|
|
&handle->OpPropCount
|
|
);
|
|
|
|
handle->OpPropCount /= sizeof (OPERATION_PROPERTY_LINKAGE);
|
|
|
|
if (!handle->OpPropLinkList) {
|
|
handle->OpPropCount = 0;
|
|
}
|
|
|
|
//
|
|
// Continue enumeration in "next" function
|
|
//
|
|
|
|
return IsmEnumNextObjectOperation (EnumPtr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectOperationById (
|
|
OUT PMIG_OBJECTOPERATION_ENUM EnumPtr,
|
|
IN MIG_OBJECTID ObjectId
|
|
)
|
|
{
|
|
return pEnumFirstObjectOperationById (EnumPtr, ObjectId, FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pEnumFirstObjectOperation (
|
|
OUT PMIG_OBJECTOPERATION_ENUM EnumPtr,
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName,
|
|
IN BOOL ReturnAllPrivateOps
|
|
)
|
|
{
|
|
MIG_OBJECTID objectId;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, EncodedObjectName, TRUE);
|
|
|
|
if (objectId) {
|
|
return pEnumFirstObjectOperationById (EnumPtr, objectId, ReturnAllPrivateOps);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectOperation (
|
|
OUT PMIG_OBJECTOPERATION_ENUM EnumPtr,
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE EncodedObjectName
|
|
)
|
|
{
|
|
return pEnumFirstObjectOperation (EnumPtr, ObjectTypeId, EncodedObjectName, FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumNextObjectOperation (
|
|
IN OUT PMIG_OBJECTOPERATION_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTOPERATION_HANDLE handle;
|
|
BOOL b = FALSE;
|
|
BOOL mine;
|
|
UINT u;
|
|
POPERATION_PROPERTY_LINKAGE linkage;
|
|
|
|
handle = (POBJECTOPERATION_HANDLE) EnumPtr->Handle;
|
|
if (!handle) {
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
|
|
MYASSERT (!b);
|
|
|
|
//
|
|
// Check if we hit the end
|
|
//
|
|
|
|
if (handle->Index >= handle->Count) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return the next operation
|
|
//
|
|
|
|
EnumPtr->OperationId = (MIG_OPERATIONID) handle->LinkageList[handle->Index];
|
|
handle->Index++;
|
|
|
|
b = pGetOperationName (
|
|
EnumPtr->OperationId,
|
|
NULL,
|
|
0,
|
|
&EnumPtr->Private,
|
|
&mine,
|
|
NULL,
|
|
handle->ReturnAllPrivateOps
|
|
);
|
|
|
|
//
|
|
// Continue when the operation is not owned by the caller
|
|
//
|
|
|
|
if (b && EnumPtr->Private && !mine) {
|
|
b = FALSE;
|
|
}
|
|
|
|
} while (!b);
|
|
|
|
if (!b) {
|
|
IsmAbortObjectOperationEnum (EnumPtr);
|
|
} else {
|
|
//
|
|
// Before returning match, fill enum structure with property info
|
|
//
|
|
|
|
linkage = handle->OpPropLinkList;
|
|
|
|
for (u = 0 ; u < handle->OpPropCount ; u++) {
|
|
if (linkage->OperationId == EnumPtr->OperationId) {
|
|
break;
|
|
}
|
|
|
|
linkage++;
|
|
}
|
|
|
|
if (u < handle->OpPropCount) {
|
|
//
|
|
// This operation has src property, dest property, or both.
|
|
// Get the data from property.dat and put it in the enum
|
|
// struct.
|
|
//
|
|
|
|
if (linkage->SrcData) {
|
|
|
|
EnumPtr->SourceData = &handle->SrcData;
|
|
CreatePropertyStruct (
|
|
&handle->SrcPropBuf,
|
|
&handle->SrcData,
|
|
linkage->SrcData
|
|
);
|
|
|
|
} else {
|
|
EnumPtr->SourceData = NULL;
|
|
}
|
|
|
|
if (linkage->DestData) {
|
|
|
|
EnumPtr->DestinationData = &handle->DestData;
|
|
CreatePropertyStruct (
|
|
&handle->DestPropBuf,
|
|
&handle->DestData,
|
|
linkage->DestData
|
|
);
|
|
|
|
} else {
|
|
EnumPtr->DestinationData = NULL;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No src or dest properties
|
|
//
|
|
|
|
EnumPtr->SourceData = NULL;
|
|
EnumPtr->DestinationData = NULL;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmAbortObjectOperationEnum (
|
|
IN OUT PMIG_OBJECTOPERATION_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTOPERATION_HANDLE handle;
|
|
|
|
if (EnumPtr->Handle) {
|
|
|
|
handle = (POBJECTOPERATION_HANDLE) EnumPtr->Handle;
|
|
|
|
GbFree (&handle->SrcPropBuf);
|
|
GbFree (&handle->DestPropBuf);
|
|
|
|
if (handle->LinkageList) {
|
|
MemDbReleaseMemory (handle->LinkageList);
|
|
INVALID_POINTER (handle->LinkageList);
|
|
}
|
|
|
|
if (handle->OpPropLinkList) {
|
|
MemDbReleaseMemory (handle->OpPropLinkList);
|
|
INVALID_POINTER (handle->OpPropLinkList);
|
|
}
|
|
|
|
FreeAlloc (EnumPtr->Handle);
|
|
INVALID_POINTER (EnumPtr->Handle);
|
|
}
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTOPERATION_ENUM));
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumFirstObjectWithOperation (
|
|
OUT PMIG_OBJECTWITHOPERATION_ENUM EnumPtr,
|
|
IN MIG_OPERATIONID OperationId
|
|
)
|
|
{
|
|
POBJECTWITHOPERATION_HANDLE handle;
|
|
BOOL b = FALSE;
|
|
|
|
__try {
|
|
//
|
|
// Did caller specify an item id?
|
|
//
|
|
|
|
if (!IsItemId ((KEYHANDLE) OperationId)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"IsmEnumFirstObjectWithOperation: operation id is invalid"
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTWITHOPERATION_ENUM));
|
|
|
|
EnumPtr->Handle = MemAllocZeroed (sizeof (OBJECTWITHOPERATION_HANDLE));
|
|
handle = (POBJECTWITHOPERATION_HANDLE) EnumPtr->Handle;
|
|
|
|
handle->LinkageList = MemDbGetDoubleLinkageArrayByKeyHandle (
|
|
OperationId,
|
|
OPERATION_INDEX,
|
|
&handle->Count
|
|
);
|
|
|
|
handle->Count = handle->Count / SIZEOF(KEYHANDLE);
|
|
|
|
if (!handle->LinkageList || !handle->Count) {
|
|
IsmAbortObjectWithOperationEnum (EnumPtr);
|
|
__leave;
|
|
} else {
|
|
b = IsmEnumNextObjectWithOperation (EnumPtr);
|
|
}
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsmEnumNextObjectWithOperation (
|
|
IN OUT PMIG_OBJECTWITHOPERATION_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTWITHOPERATION_HANDLE handle;
|
|
PCTSTR objectPath = NULL;
|
|
BOOL b = FALSE;
|
|
PTSTR p;
|
|
|
|
__try {
|
|
handle = (POBJECTWITHOPERATION_HANDLE) EnumPtr->Handle;
|
|
if (!handle) {
|
|
__leave;
|
|
}
|
|
|
|
while (!b) {
|
|
|
|
//
|
|
// Check if enum is done
|
|
//
|
|
|
|
if (handle->Index >= handle->Count) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the next object id from the linkage list
|
|
//
|
|
|
|
EnumPtr->ObjectId = handle->LinkageList[handle->Index];
|
|
handle->Index++;
|
|
|
|
if (handle->ObjectFromMemdb) {
|
|
MemDbReleaseMemory (handle->ObjectFromMemdb);
|
|
INVALID_POINTER (handle->ObjectFromMemdb);
|
|
}
|
|
|
|
handle->ObjectFromMemdb = MemDbGetKeyFromHandle ((KEYHANDLE) EnumPtr->ObjectId, 0);
|
|
|
|
if (!handle->ObjectFromMemdb) {
|
|
MYASSERT (FALSE); // this error shouldn't happen -- but don't give up
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Turn the object id into a name
|
|
//
|
|
|
|
p = _tcschr (handle->ObjectFromMemdb, TEXT('\\'));
|
|
|
|
if (p) {
|
|
b = TRUE;
|
|
EnumPtr->ObjectName = _tcsinc (p);
|
|
*p = 0;
|
|
EnumPtr->ObjectTypeId = GetObjectTypeId (handle->ObjectFromMemdb);
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
}
|
|
|
|
if (!b) {
|
|
IsmAbortObjectWithOperationEnum (EnumPtr);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
IsmAbortObjectWithOperationEnum (
|
|
IN PMIG_OBJECTWITHOPERATION_ENUM EnumPtr
|
|
)
|
|
{
|
|
POBJECTWITHOPERATION_HANDLE handle;
|
|
|
|
if (EnumPtr->Handle) {
|
|
handle = (POBJECTWITHOPERATION_HANDLE) EnumPtr->Handle;
|
|
|
|
if (handle->ObjectFromMemdb) {
|
|
MemDbReleaseMemory (handle->ObjectFromMemdb);
|
|
INVALID_POINTER (handle->ObjectFromMemdb);
|
|
}
|
|
|
|
if (handle->LinkageList) {
|
|
MemDbReleaseMemory (handle->LinkageList);
|
|
INVALID_POINTER (handle->LinkageList);
|
|
}
|
|
|
|
FreeAlloc (EnumPtr->Handle);
|
|
INVALID_POINTER (EnumPtr->Handle);
|
|
}
|
|
|
|
ZeroMemory (EnumPtr, sizeof (MIG_OBJECTWITHOPERATION_ENUM));
|
|
}
|
|
|
|
MIG_OBJECTSTRINGHANDLE
|
|
TrackedIsmGetLongName (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE ObjectName
|
|
TRACKING_DEF
|
|
)
|
|
{
|
|
GROWBUFFER growBuf = INIT_GROWBUFFER;
|
|
UINT resultType = FILENAME_UNDECIDED;
|
|
BOOL hadLeaf = FALSE;
|
|
PCTSTR node = NULL;
|
|
PCTSTR leaf = NULL;
|
|
PTSTR nativeName = NULL;
|
|
PTSTR nativeNamePtr;
|
|
PTSTR beginSegPtr;
|
|
PTSTR endSegPtr;
|
|
PCTSTR lastSeg;
|
|
UINT savedEnd;
|
|
UINT beginBuffIdx;
|
|
TCHAR savedChar;
|
|
KEYHANDLE kh1, kh2;
|
|
DWORD value;
|
|
MIG_OBJECTSTRINGHANDLE result = NULL;
|
|
|
|
TRACK_ENTER();
|
|
|
|
if ((ObjectTypeId & (~PLATFORM_MASK)) == MIG_FILE_TYPE) {
|
|
|
|
//
|
|
// fire up the short-long algorithm
|
|
//
|
|
if (IsmCreateObjectStringsFromHandle (ObjectName, &node, &leaf)) {
|
|
|
|
MYASSERT (node);
|
|
if (leaf) {
|
|
nativeName = DuplicatePathString (S_SHORTLONG_TREE, SizeOfString (node) + SizeOfString (leaf));
|
|
hadLeaf = TRUE;
|
|
} else {
|
|
nativeName = DuplicatePathString (S_SHORTLONG_TREE, SizeOfString (node));
|
|
hadLeaf = FALSE;
|
|
}
|
|
nativeNamePtr = AppendWack (nativeName);
|
|
StringCopy (nativeNamePtr, node);
|
|
if (leaf) {
|
|
StringCopy (AppendWack (nativeNamePtr), leaf);
|
|
}
|
|
GbAppendString (&growBuf, S_SHORTLONG_TREE);
|
|
GbAppendString (&growBuf, TEXT("\\"));
|
|
beginBuffIdx = growBuf.End - (1 * sizeof (TCHAR));
|
|
beginSegPtr = GetFirstSeg (nativeNamePtr);
|
|
|
|
if (beginSegPtr) {
|
|
|
|
beginSegPtr = _tcsinc (beginSegPtr);
|
|
|
|
GbAppendStringAB (&growBuf, nativeNamePtr, beginSegPtr);
|
|
|
|
while (beginSegPtr) {
|
|
endSegPtr = _tcschr (beginSegPtr, TEXT('\\'));
|
|
if (!endSegPtr) {
|
|
endSegPtr = GetEndOfString (beginSegPtr);
|
|
MYASSERT (endSegPtr);
|
|
}
|
|
|
|
savedChar = *endSegPtr;
|
|
*endSegPtr = 0;
|
|
|
|
savedEnd = growBuf.End - (1 * sizeof (TCHAR));
|
|
GbAppendStringAB (&growBuf, beginSegPtr, endSegPtr);
|
|
|
|
kh1 = MemDbGetHandleFromKey ((PCTSTR) growBuf.Buf);
|
|
if (kh1) {
|
|
MemDbGetValueByHandle (kh1, &value);
|
|
if (value == FILENAME_LONG) {
|
|
resultType = FILENAME_LONG;
|
|
} else {
|
|
if (resultType != FILENAME_LONG) {
|
|
resultType = FILENAME_SHORT;
|
|
}
|
|
kh2 = MemDbGetDoubleLinkageByKeyHandle (kh1, 0, 0);
|
|
MYASSERT (kh2);
|
|
if (kh2) {
|
|
growBuf.End = savedEnd;
|
|
lastSeg = MemDbGetKeyFromHandle (kh2, MEMDB_LAST_LEVEL);
|
|
GbAppendString (&growBuf, lastSeg);
|
|
MemDbReleaseMemory (lastSeg);
|
|
}
|
|
}
|
|
}
|
|
|
|
*endSegPtr = savedChar;
|
|
if (savedChar) {
|
|
beginSegPtr = _tcsinc (endSegPtr);
|
|
GbAppendStringAB (&growBuf, endSegPtr, beginSegPtr);
|
|
} else {
|
|
beginSegPtr = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
GbAppendString (&growBuf, nativeNamePtr);
|
|
}
|
|
FreePathString (nativeName);
|
|
if (node) {
|
|
IsmDestroyObjectString (node);
|
|
}
|
|
if (leaf) {
|
|
IsmDestroyObjectString (leaf);
|
|
}
|
|
if (hadLeaf) {
|
|
beginSegPtr = _tcsrchr ((PTSTR) growBuf.Buf, TEXT('\\'));
|
|
endSegPtr = _tcsinc (beginSegPtr);
|
|
*beginSegPtr = 0;
|
|
} else {
|
|
endSegPtr = NULL;
|
|
}
|
|
result = IsmCreateObjectHandle ((PTSTR) (growBuf.Buf + beginBuffIdx), endSegPtr);
|
|
}
|
|
GbFree (&growBuf);
|
|
}
|
|
|
|
TRACK_LEAVE();
|
|
|
|
return result;
|
|
}
|
|
|
|
MIG_OBJECTSTRINGHANDLE
|
|
IsmFilterObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE ObjectName,
|
|
OUT MIG_OBJECTTYPEID *NewObjectTypeId, OPTIONAL
|
|
OUT PBOOL ObjectDeleted, OPTIONAL
|
|
OUT PBOOL ObjectReplaced OPTIONAL
|
|
)
|
|
{
|
|
MIG_OBJECTSTRINGHANDLE objectName = ObjectName;
|
|
ENCODEDSTRHANDLE result = NULL;
|
|
MIG_FILTEROUTPUT filterOutput;
|
|
GROWBUFFER growBuf = INIT_GROWBUFFER;
|
|
UINT resultType = FILENAME_UNDECIDED;
|
|
BOOL hadLeaf = FALSE;
|
|
PCTSTR node = NULL;
|
|
PCTSTR leaf = NULL;
|
|
PTSTR nativeName = NULL;
|
|
PTSTR nativeNamePtr;
|
|
PTSTR beginSegPtr;
|
|
PTSTR endSegPtr;
|
|
PCTSTR lastSeg;
|
|
UINT savedEnd;
|
|
UINT beginBuffIdx;
|
|
TCHAR savedChar;
|
|
KEYHANDLE kh1, kh2;
|
|
DWORD value;
|
|
|
|
ObjectTypeId = FixEnumerationObjectTypeId (ObjectTypeId);
|
|
|
|
__try {
|
|
|
|
if ((ObjectTypeId & (~PLATFORM_MASK)) == MIG_FILE_TYPE) {
|
|
//
|
|
// fire up the short-long algorithm
|
|
//
|
|
if (IsmCreateObjectStringsFromHandle (ObjectName, &node, &leaf)) {
|
|
|
|
if (node) {
|
|
if (leaf) {
|
|
nativeName = DuplicatePathString (S_SHORTLONG_TREE, SizeOfString (node) + SizeOfString (leaf));
|
|
hadLeaf = TRUE;
|
|
} else {
|
|
nativeName = DuplicatePathString (S_SHORTLONG_TREE, SizeOfString (node));
|
|
hadLeaf = FALSE;
|
|
}
|
|
nativeNamePtr = AppendWack (nativeName);
|
|
StringCopy (nativeNamePtr, node);
|
|
if (leaf) {
|
|
StringCopy (AppendWack (nativeNamePtr), leaf);
|
|
}
|
|
GbAppendString (&growBuf, S_SHORTLONG_TREE);
|
|
GbAppendString (&growBuf, TEXT("\\"));
|
|
beginBuffIdx = growBuf.End - (1 * sizeof (TCHAR));
|
|
beginSegPtr = GetFirstSeg (nativeNamePtr);
|
|
|
|
if (beginSegPtr) {
|
|
|
|
beginSegPtr = _tcsinc (beginSegPtr);
|
|
|
|
GbAppendStringAB (&growBuf, nativeNamePtr, beginSegPtr);
|
|
|
|
while (beginSegPtr) {
|
|
endSegPtr = _tcschr (beginSegPtr, TEXT('\\'));
|
|
if (!endSegPtr) {
|
|
endSegPtr = GetEndOfString (beginSegPtr);
|
|
MYASSERT (endSegPtr);
|
|
}
|
|
|
|
savedChar = *endSegPtr;
|
|
*endSegPtr = 0;
|
|
|
|
savedEnd = growBuf.End - (1 * sizeof (TCHAR));
|
|
GbAppendStringAB (&growBuf, beginSegPtr, endSegPtr);
|
|
|
|
kh1 = MemDbGetHandleFromKey ((PCTSTR) growBuf.Buf);
|
|
if (kh1) {
|
|
MemDbGetValueByHandle (kh1, &value);
|
|
if (value == FILENAME_LONG) {
|
|
resultType = FILENAME_LONG;
|
|
} else {
|
|
if (resultType != FILENAME_LONG) {
|
|
resultType = FILENAME_SHORT;
|
|
}
|
|
kh2 = MemDbGetDoubleLinkageByKeyHandle (kh1, 0, 0);
|
|
MYASSERT (kh2);
|
|
if (kh2) {
|
|
growBuf.End = savedEnd;
|
|
lastSeg = MemDbGetKeyFromHandle (kh2, MEMDB_LAST_LEVEL);
|
|
GbAppendString (&growBuf, lastSeg);
|
|
MemDbReleaseMemory (lastSeg);
|
|
}
|
|
}
|
|
}
|
|
|
|
*endSegPtr = savedChar;
|
|
if (savedChar) {
|
|
beginSegPtr = _tcsinc (endSegPtr);
|
|
GbAppendStringAB (&growBuf, endSegPtr, beginSegPtr);
|
|
} else {
|
|
beginSegPtr = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
GbAppendString (&growBuf, nativeNamePtr);
|
|
}
|
|
FreePathString (nativeName);
|
|
if (node) {
|
|
IsmDestroyObjectString (node);
|
|
}
|
|
if (leaf) {
|
|
IsmDestroyObjectString (leaf);
|
|
}
|
|
if (hadLeaf) {
|
|
beginSegPtr = _tcsrchr ((PTSTR) growBuf.Buf, TEXT('\\'));
|
|
endSegPtr = _tcsinc (beginSegPtr);
|
|
*beginSegPtr = 0;
|
|
} else {
|
|
endSegPtr = NULL;
|
|
}
|
|
objectName = IsmCreateObjectHandle ((PTSTR) (growBuf.Buf + beginBuffIdx), endSegPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ApplyOperationsOnObject (
|
|
ObjectTypeId,
|
|
objectName,
|
|
FALSE,
|
|
!ShouldObjectBeRestored (
|
|
ObjectTypeId,
|
|
IsmGetObjectIdFromName (ObjectTypeId, objectName, TRUE),
|
|
objectName
|
|
),
|
|
OP_ALL_PRIORITY,
|
|
NULL,
|
|
&filterOutput,
|
|
NULL
|
|
)) {
|
|
__leave;
|
|
}
|
|
|
|
if (ObjectDeleted) {
|
|
*ObjectDeleted = filterOutput.Deleted;
|
|
}
|
|
|
|
if (ObjectReplaced) {
|
|
*ObjectReplaced = filterOutput.Replaced;
|
|
}
|
|
|
|
if (NewObjectTypeId) {
|
|
*NewObjectTypeId = filterOutput.NewObject.ObjectTypeId;
|
|
}
|
|
|
|
if (filterOutput.NewObject.ObjectName == objectName) {
|
|
__leave;
|
|
}
|
|
|
|
if (resultType == FILENAME_SHORT) {
|
|
// NTRAID#NTBUG9-153258-2000/08/01-jimschm Create dummy file (if does not exist) to reserve and get the short file name
|
|
}
|
|
|
|
result = filterOutput.NewObject.ObjectName;
|
|
}
|
|
__finally {
|
|
if (objectName != ObjectName) {
|
|
//
|
|
// free the object name allocated by the short-long algorithm
|
|
//
|
|
IsmDestroyObjectHandle (objectName);
|
|
}
|
|
}
|
|
GbFree (&growBuf);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
pFreeMigObjectStruct (
|
|
IN PMIG_OBJECT Object,
|
|
IN PMIG_OBJECT OriginalObject, OPTIONAL
|
|
IN PMIG_OBJECT NewObject OPTIONAL
|
|
)
|
|
{
|
|
BOOL free = TRUE;
|
|
|
|
if (OriginalObject) {
|
|
if (Object->ObjectName == OriginalObject->ObjectName) {
|
|
free = FALSE;
|
|
}
|
|
}
|
|
|
|
if (NewObject) {
|
|
if (Object->ObjectName == NewObject->ObjectName) {
|
|
free = FALSE;
|
|
}
|
|
}
|
|
|
|
if (free) {
|
|
IsmDestroyObjectHandle (Object->ObjectName);
|
|
}
|
|
|
|
ZeroMemory (Object, sizeof (MIG_OBJECT));
|
|
}
|
|
|
|
|
|
VOID
|
|
pFreeMigContentStruct (
|
|
IN PMIG_CONTENT Content,
|
|
IN PCMIG_CONTENT OriginalContent, OPTIONAL
|
|
IN PCMIG_CONTENT NewContent OPTIONAL
|
|
)
|
|
{
|
|
BOOL free;
|
|
|
|
if (Content->ContentInFile) {
|
|
|
|
free = TRUE;
|
|
|
|
if (OriginalContent &&
|
|
Content->FileContent.ContentPath == OriginalContent->FileContent.ContentPath
|
|
) {
|
|
|
|
free = FALSE;
|
|
|
|
} else if (NewContent &&
|
|
Content->FileContent.ContentPath == NewContent->FileContent.ContentPath
|
|
) {
|
|
|
|
free = FALSE;
|
|
|
|
}
|
|
|
|
if (free) {
|
|
if (Content->FileContent.ContentPath) {
|
|
IsmReleaseMemory (Content->FileContent.ContentPath);
|
|
Content->FileContent.ContentPath = NULL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
free = TRUE;
|
|
|
|
if (OriginalContent &&
|
|
Content->MemoryContent.ContentBytes == OriginalContent->MemoryContent.ContentBytes
|
|
) {
|
|
|
|
free = FALSE;
|
|
|
|
} else if (NewContent &&
|
|
Content->MemoryContent.ContentBytes == NewContent->MemoryContent.ContentBytes
|
|
) {
|
|
|
|
free = FALSE;
|
|
}
|
|
|
|
if (free) {
|
|
if (Content->MemoryContent.ContentBytes) {
|
|
IsmReleaseMemory (Content->MemoryContent.ContentBytes);
|
|
Content->MemoryContent.ContentBytes = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
free = TRUE;
|
|
|
|
if (OriginalContent &&
|
|
OriginalContent->Details.DetailsData == Content->Details.DetailsData
|
|
) {
|
|
|
|
free = FALSE;
|
|
}
|
|
|
|
if (NewContent &&
|
|
NewContent->Details.DetailsData == Content->Details.DetailsData
|
|
) {
|
|
|
|
free = FALSE;
|
|
}
|
|
|
|
if (free && Content->Details.DetailsData) {
|
|
IsmReleaseMemory (Content->Details.DetailsData);
|
|
Content->Details.DetailsData = NULL;;
|
|
}
|
|
|
|
ZeroMemory (Content, sizeof (MIG_CONTENT));
|
|
}
|
|
|
|
|
|
BOOL
|
|
ApplyOperationsOnObject (
|
|
IN MIG_OBJECTTYPEID ObjectTypeId,
|
|
IN MIG_OBJECTSTRINGHANDLE ObjectName,
|
|
IN BOOL TreeFiltersOnly,
|
|
IN BOOL NoRestoreObject,
|
|
IN DWORD OperationPriority,
|
|
IN PMIG_CONTENT ApplyInput, OPTIONAL
|
|
OUT PMIG_FILTEROUTPUT FilterOutput,
|
|
OUT PMIG_CONTENT ApplyOutput OPTIONAL
|
|
)
|
|
{
|
|
MIG_FILTERINPUT filterInput;
|
|
MIG_OBJECTOPERATION_ENUM Enum;
|
|
BOOL result = FALSE;
|
|
MIG_CONTENT currentContent;
|
|
OPERATION_DATA operationData;
|
|
POPMFILTERCALLBACK filterCallback;
|
|
POPMAPPLYCALLBACK applyCallback;
|
|
PGLOBALFILTERINDEX globalFilterIndex;
|
|
PGLOBALFILTER globalFilter;
|
|
PGLOBALEDIT globalEdit;
|
|
BOOL needObjectId;
|
|
MIG_OBJECTID objectId = 0;
|
|
|
|
if (NoRestoreObject) {
|
|
MYASSERT (!ApplyInput);
|
|
MYASSERT (!ApplyOutput);
|
|
if (ApplyInput || ApplyOutput) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
__try {
|
|
|
|
//
|
|
// Save original values
|
|
//
|
|
|
|
ZeroMemory (&filterInput, sizeof (filterInput));
|
|
|
|
filterInput.OriginalObject.ObjectTypeId = ObjectTypeId;
|
|
filterInput.OriginalObject.ObjectName = ObjectName;
|
|
filterInput.CurrentObject = filterInput.OriginalObject;
|
|
if ((ObjectTypeId & PLATFORM_MASK) == PLATFORM_SOURCE) {
|
|
filterInput.Deleted = NoRestoreObject;
|
|
}
|
|
|
|
filterInput.FilterTreeChangesOnly = TreeFiltersOnly;
|
|
|
|
ZeroMemory (¤tContent, sizeof (MIG_CONTENT));
|
|
|
|
if (ApplyInput) {
|
|
CopyMemory (¤tContent, ApplyInput, sizeof (MIG_CONTENT));
|
|
}
|
|
|
|
//
|
|
// Set defaults for output
|
|
//
|
|
|
|
FilterOutput->NewObject = filterInput.CurrentObject;
|
|
FilterOutput->Deleted = filterInput.Deleted;
|
|
FilterOutput->Replaced = filterInput.Replaced;
|
|
|
|
if (ApplyInput) {
|
|
MYASSERT (ApplyOutput);
|
|
CopyMemory (ApplyOutput, ApplyInput, sizeof (MIG_CONTENT));
|
|
}
|
|
|
|
//
|
|
// Find type in filter index and process first pass global filters and editors
|
|
//
|
|
|
|
globalFilterIndex = pGetGlobalIndex (ObjectTypeId, FALSE);
|
|
|
|
if (globalFilterIndex) {
|
|
|
|
//
|
|
// Get object ID
|
|
//
|
|
|
|
needObjectId = FALSE;
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
if (globalFilterIndex->FilterFirstHeadHp ||
|
|
globalFilterIndex->FilterLastHeadHp ||
|
|
globalFilterIndex->EditFirstHeadHp ||
|
|
globalFilterIndex->EditLastHeadHp
|
|
) {
|
|
needObjectId = TRUE;
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
if (globalFilterIndex->FilterFirstHead ||
|
|
globalFilterIndex->FilterLastHead ||
|
|
globalFilterIndex->EditFirstHead ||
|
|
globalFilterIndex->EditLastHead
|
|
) {
|
|
needObjectId = TRUE;
|
|
}
|
|
}
|
|
|
|
if (needObjectId) {
|
|
objectId = IsmGetObjectIdFromName (ObjectTypeId, ObjectName, TRUE);
|
|
}
|
|
|
|
//
|
|
// Perform first pass global filter
|
|
//
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
|
|
globalFilter = globalFilterIndex->FilterFirstHeadHp;
|
|
|
|
while (globalFilter) {
|
|
|
|
if ((!globalFilter->Platform || (globalFilter->Platform & ObjectTypeId)) &&
|
|
(!TreeFiltersOnly || globalFilter->TreeFilter) &&
|
|
(!NoRestoreObject || globalFilter->NoRestoreFilter)
|
|
) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalFilter->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
if (!globalFilter->Callback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: first pass global filter failed for %s",
|
|
pGetOpNameForDebug (globalFilter->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
}
|
|
}
|
|
|
|
globalFilter = globalFilter->Next;
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
|
|
globalFilter = globalFilterIndex->FilterFirstHead;
|
|
|
|
while (globalFilter) {
|
|
|
|
if ((!globalFilter->Platform || (globalFilter->Platform & ObjectTypeId)) &&
|
|
(!TreeFiltersOnly || globalFilter->TreeFilter) &&
|
|
(!NoRestoreObject || globalFilter->NoRestoreFilter)
|
|
) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalFilter->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
if (!globalFilter->Callback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: first pass global filter failed for %s",
|
|
pGetOpNameForDebug (globalFilter->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
}
|
|
}
|
|
|
|
globalFilter = globalFilter->Next;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform first pass apply
|
|
//
|
|
|
|
if (ApplyInput) {
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
|
|
globalEdit = globalFilterIndex->EditFirstHeadHp;
|
|
|
|
while (globalEdit) {
|
|
|
|
if (!globalEdit->Platform || (globalEdit->Platform & ObjectTypeId)) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalEdit->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (!globalEdit->Callback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: first pass global callback failed for %s",
|
|
pGetOpNameForDebug (globalEdit->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
globalEdit = globalEdit->Next;
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
|
|
globalEdit = globalFilterIndex->EditFirstHead;
|
|
|
|
while (globalEdit) {
|
|
|
|
if (!globalEdit->Platform || (globalEdit->Platform & ObjectTypeId)) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalEdit->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (!globalEdit->Callback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: first pass global callback failed for %s",
|
|
pGetOpNameForDebug (globalEdit->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
globalEdit = globalEdit->Next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enumerate all operations on the object, then call filter and apply
|
|
// callbacks.
|
|
//
|
|
|
|
if (pEnumFirstObjectOperation (&Enum, ObjectTypeId, ObjectName, TRUE)) {
|
|
|
|
do {
|
|
|
|
if (pGetOperationData (Enum.OperationId, &operationData)) {
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
|
|
//
|
|
// Screen the non-tree filters if necessary
|
|
//
|
|
|
|
if ((!TreeFiltersOnly || operationData.TreeFilterHp) &&
|
|
(!NoRestoreObject || operationData.NoRestoreFilterHp)
|
|
) {
|
|
|
|
//
|
|
// NOTE: This loop calls the filter for an operation, then the
|
|
// apply for the operation, then the filter for the next
|
|
// operation, the apply for the next operation, and so on.
|
|
//
|
|
// A content-edit of something that points to itself
|
|
// might not be processed correctly. That is, its
|
|
// own path is not completely filtered, and
|
|
// therefore it might not be correct. To fix this,
|
|
// all filters should run first, and then all apply
|
|
// callbacks should run. But because this would
|
|
// cause double-enumeration of operations (costing
|
|
// operation data retrieval), the loop is left
|
|
// as-is. Also, the theoretical case is rare enough
|
|
// that we aren't concerned, and there is a
|
|
// workaround via the operation priorities.
|
|
//
|
|
|
|
//
|
|
// Call the filter associated with this operation
|
|
//
|
|
|
|
filterCallback = operationData.FilterCallbackHp;
|
|
if (filterCallback) {
|
|
|
|
if (!filterCallback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: filter failed for %s",
|
|
pGetOpNameForDebug (Enum.OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
|
|
}
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (ApplyInput) {
|
|
applyCallback = operationData.ApplyCallbackHp;
|
|
} else {
|
|
applyCallback = NULL;
|
|
}
|
|
|
|
if (applyCallback) {
|
|
|
|
if (!applyCallback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: Operation apply callback failed for %s",
|
|
pGetOpNameForDebug (Enum.OperationId)
|
|
));
|
|
IsmAbortObjectOperationEnum (&Enum);
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
|
|
//
|
|
// Screen the non-tree filters if necessary
|
|
//
|
|
|
|
if ((!TreeFiltersOnly || operationData.TreeFilter) &&
|
|
(!NoRestoreObject || operationData.NoRestoreFilter)
|
|
) {
|
|
|
|
//
|
|
// NOTE: This loop calls the filter for an operation, then the
|
|
// apply for the operation, then the filter for the next
|
|
// operation, the apply for the next operation, and so on.
|
|
//
|
|
// A content-edit of something that points to itself
|
|
// might not be processed correctly. That is, its
|
|
// own path is not completely filtered, and
|
|
// therefore it might not be correct. To fix this,
|
|
// all filters should run first, and then all apply
|
|
// callbacks should run. But because this would
|
|
// cause double-enumeration of operations (costing
|
|
// operation data retrieval), the loop is left
|
|
// as-is. Also, the theoretical case is rare enough
|
|
// that we aren't concerned, and there is a
|
|
// workaround via the operation priorities.
|
|
//
|
|
|
|
//
|
|
// Call the filter associated with this operation
|
|
//
|
|
|
|
filterCallback = operationData.FilterCallback;
|
|
if (filterCallback) {
|
|
|
|
if (!filterCallback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: filter failed for %s",
|
|
pGetOpNameForDebug (Enum.OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
|
|
}
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (ApplyInput) {
|
|
applyCallback = operationData.ApplyCallback;
|
|
} else {
|
|
applyCallback = NULL;
|
|
}
|
|
|
|
if (applyCallback) {
|
|
|
|
if (!applyCallback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: Operation apply callback failed for %s",
|
|
pGetOpNameForDebug (Enum.OperationId)
|
|
));
|
|
IsmAbortObjectOperationEnum (&Enum);
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (IsmEnumNextObjectOperation (&Enum));
|
|
}
|
|
|
|
//
|
|
// Execute last pass global filters and content editors
|
|
//
|
|
|
|
if (globalFilterIndex) {
|
|
|
|
//
|
|
// Preform last pass global filter
|
|
//
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
|
|
globalFilter = globalFilterIndex->FilterLastHeadHp;
|
|
|
|
while (globalFilter) {
|
|
|
|
if ((!globalFilter->Platform || (globalFilter->Platform & ObjectTypeId)) &&
|
|
(!TreeFiltersOnly || globalFilter->TreeFilter) &&
|
|
(!NoRestoreObject || globalFilter->NoRestoreFilter)
|
|
) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalFilter->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
if (!globalFilter->Callback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: last pass global filter failed for %s",
|
|
pGetOpNameForDebug (globalFilter->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
}
|
|
}
|
|
|
|
globalFilter = globalFilter->Next;
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
|
|
globalFilter = globalFilterIndex->FilterLastHead;
|
|
|
|
while (globalFilter) {
|
|
|
|
if ((!globalFilter->Platform || (globalFilter->Platform & ObjectTypeId)) &&
|
|
(!TreeFiltersOnly || globalFilter->TreeFilter) &&
|
|
(!NoRestoreObject || globalFilter->NoRestoreFilter)
|
|
) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalFilter->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
if (!globalFilter->Callback (
|
|
&filterInput,
|
|
FilterOutput,
|
|
NoRestoreObject,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: last pass global filter failed for %s",
|
|
pGetOpNameForDebug (globalFilter->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from
|
|
// both the original and new pointer values
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
|
|
//
|
|
// Filter outputs now become the inputs
|
|
//
|
|
|
|
filterInput.CurrentObject = FilterOutput->NewObject;
|
|
filterInput.Deleted = FilterOutput->Deleted;
|
|
filterInput.Replaced = FilterOutput->Replaced;
|
|
}
|
|
}
|
|
|
|
globalFilter = globalFilter->Next;
|
|
}
|
|
}
|
|
|
|
if (ApplyInput) {
|
|
|
|
if (OperationPriority & OP_HIGH_PRIORITY) {
|
|
|
|
globalEdit = globalFilterIndex->EditLastHeadHp;
|
|
|
|
while (globalEdit) {
|
|
|
|
if (!globalEdit->Platform || (globalEdit->Platform & ObjectTypeId)) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalEdit->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (!globalEdit->Callback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: last pass global callback failed for %s",
|
|
pGetOpNameForDebug (globalEdit->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
globalEdit = globalEdit->Next;
|
|
}
|
|
}
|
|
|
|
if (OperationPriority & OP_LOW_PRIORITY) {
|
|
|
|
globalEdit = globalFilterIndex->EditLastHead;
|
|
|
|
while (globalEdit) {
|
|
|
|
if (!globalEdit->Platform || (globalEdit->Platform & ObjectTypeId)) {
|
|
|
|
if (!objectId ||
|
|
!pIsOperationProhibitedOnObject (globalEdit->OperationId, objectId, TRUE)
|
|
) {
|
|
|
|
//
|
|
// Call the apply function associated with this operation
|
|
//
|
|
|
|
if (!globalEdit->Callback (
|
|
ObjectTypeId,
|
|
ObjectName,
|
|
ApplyInput,
|
|
¤tContent,
|
|
ApplyOutput,
|
|
Enum.SourceData,
|
|
Enum.DestinationData
|
|
)) {
|
|
DEBUGMSG ((
|
|
DBG_ERROR,
|
|
"ApplyOperationsOnObject: last pass global callback failed for %s",
|
|
pGetOpNameForDebug (globalEdit->OperationId)
|
|
));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Free input allocations if they are different from both
|
|
// the original and new pointer values
|
|
//
|
|
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
|
|
//
|
|
// Apply outputs now become the current content
|
|
//
|
|
|
|
CopyMemory (¤tContent, ApplyOutput, sizeof (MIG_CONTENT));
|
|
}
|
|
}
|
|
globalEdit = globalEdit->Next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
if (!result) {
|
|
//
|
|
// Free all allocations except for the original (made by
|
|
// the caller)
|
|
//
|
|
|
|
pFreeMigObjectStruct (
|
|
&filterInput.CurrentObject,
|
|
&filterInput.OriginalObject,
|
|
&FilterOutput->NewObject
|
|
);
|
|
pFreeMigObjectStruct (
|
|
&FilterOutput->NewObject,
|
|
&filterInput.OriginalObject,
|
|
NULL
|
|
);
|
|
|
|
if (ApplyInput) {
|
|
pFreeMigContentStruct (¤tContent, ApplyInput, ApplyOutput);
|
|
pFreeMigContentStruct (ApplyOutput, ApplyInput, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeFilterOutput (
|
|
IN MIG_OBJECTSTRINGHANDLE OriginalString,
|
|
IN PMIG_FILTEROUTPUT FilterOutput
|
|
)
|
|
{
|
|
MIG_OBJECT object;
|
|
|
|
object.ObjectName = OriginalString;
|
|
pFreeMigObjectStruct (&FilterOutput->NewObject, &object, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeApplyOutput (
|
|
IN PCMIG_CONTENT OriginalContent,
|
|
IN PMIG_CONTENT FinalContent
|
|
)
|
|
{
|
|
pFreeMigContentStruct (FinalContent, OriginalContent, NULL);
|
|
}
|