Windows-Server-2003/net/dhcp/server/mm/server.c

857 lines
25 KiB
C

//================================================================================
// Copyright (C) 1997 Microsoft Corporation
// Author: RameshV
// Description: implements the basic structures for a server object
// ThreadSafe: no
// Locks: none
// Please read stdinfo.txt for programming style.
//================================================================================
#include <mm.h>
#include <winbase.h>
#include <array.h>
#include <opt.h>
#include <optl.h>
#include <optclass.h>
#include <bitmask.h>
#include <range.h>
#include <reserve.h>
#include <subnet.h>
#include <optdefl.h>
#include <classdefl.h>
#include <oclassdl.h>
#include <sscope.h>
#include "server.h"
#include "server\uniqid.h"
//BeginExport(function)
DWORD
MemServerInit(
OUT PM_SERVER *Server,
IN DWORD Address,
IN DWORD State,
IN DWORD Policy,
IN LPWSTR Name,
IN LPWSTR Comment
) //EndExport(function)
{
DWORD Error;
DWORD Size;
PM_SERVER Srv;
AssertRet(Server, ERROR_INVALID_PARAMETER );
Size = ROUND_UP_COUNT(sizeof(M_SERVER), ALIGN_WORST);
if(Name) Size += sizeof(WCHAR)*(1+wcslen(Name));
if(Comment) Size += sizeof(WCHAR)*(1+wcslen(Comment));
Srv = MemAlloc(Size);
if( NULL == Srv ) return ERROR_NOT_ENOUGH_MEMORY;
Srv->LastUniqId = 0;
Srv->Address = Address;
Srv->State = State;
Srv->Policy = Policy;
Error = MemArrayInit(&Srv->Subnets);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Error = MemArrayInitLoc(&Srv->Subnets, &Srv->Loc);
// Require(ERROR_SUCCESS == Error); // guaranteed failure as the array is empty now..
Error = MemArrayInit(&Srv->MScopes);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Error = MemArrayInit(&Srv->SuperScopes);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Error = MemOptClassInit(&Srv->Options);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Error = MemOptClassDefListInit(&Srv->OptDefs);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Error = MemClassDefListInit(&Srv->ClassDefs);
if( ERROR_SUCCESS != Error ) { MemFree(Srv); return Error; }
Size = ROUND_UP_COUNT(sizeof(M_SERVER), ALIGN_WORST);
if( Name ) {
Srv->Name = (LPWSTR)(Size + (LPBYTE)Srv);
Size += sizeof(WCHAR)*(1 + wcslen(Name));
wcscpy(Srv->Name, Name);
}
if( Comment ) {
Srv->Comment = (LPWSTR)(Size + (LPBYTE)Srv);
wcscpy(Srv->Comment, Comment);
}
*Server = Srv;
return ERROR_SUCCESS;
}
//BeginExport(function)
DWORD
MemServerCleanup(
IN OUT PM_SERVER Server
) //EndExport(function)
{
DWORD Error;
AssertRet(Server, ERROR_INVALID_PARAMETER);
Error = MemArrayCleanup(&Server->Subnets);
Require( ERROR_SUCCESS == Error);
Error = MemArrayCleanup(&Server->MScopes);
Require( ERROR_SUCCESS == Error);
Error = MemArrayCleanup(&Server->SuperScopes);
Require( ERROR_SUCCESS == Error);
Error = MemOptClassCleanup(&Server->Options);
Require( ERROR_SUCCESS == Error);
Error = MemOptClassDefListCleanup(&Server->OptDefs);
Require( ERROR_SUCCESS == Error);
Error = MemClassDefListCleanup(&Server->ClassDefs);
Require( ERROR_SUCCESS == Error);
MemFree(Server);
return ERROR_SUCCESS;
}
//================================================================================
// subnet related functions on the server
//================================================================================
//BeginExport(function)
DWORD
MemServerGetUAddressInfo(
IN PM_SERVER Server,
IN DWORD Address,
OUT PM_SUBNET *Subnet, // OPTIONAL
OUT PM_RANGE *Range, // OPTIONAL
OUT PM_EXCL *Excl, // OPTIONAL
OUT PM_RESERVATION *Reservation // OPTIONAL
) //EndExport(function)
{
ARRAY_LOCATION Loc;
PM_SUBNET ThisSubnet;
DWORD Error;
LONG Start, End, Mid;
AssertRet(Server && (Subnet || Range || Excl || Reservation ), ERROR_INVALID_PARAMETER);
Require( !CLASSD_HOST_ADDR( Address ) );
//
// more efficient binary search
//
if( Subnet ) {
*Subnet = NULL;
}
Start = 0;
End = MemArraySize(&Server->Subnets) - 1;
while( Start <= End ) { // still got an element to go by..
Mid = (Start + End) /2 ;
Error = MemArrayGetElement(&Server->Subnets, &Mid, &ThisSubnet);
Require( ERROR_SUCCESS == Error );
Require(ThisSubnet->fSubnet); // can't work if something inbetween aint a subnet
if( Address < ThisSubnet->Address) { // not in this subnet ..
End = Mid - 1;
} else if( ThisSubnet->Address == (ThisSubnet->Mask & Address) ) {
//
// We got the subnet we're looking for..
//
if( Range || Excl || Reservation ) {
Error = MemSubnetGetAddressInfo(
ThisSubnet,
Address,
Range,
Excl,
Reservation
);
} // if
if( Subnet ) {
*Subnet = ThisSubnet;
}
//
// if we got a subnet, but didn't suceed above.. still we got something
// so return success... otherwise return whatever above returned..
//
return ( Subnet && (*Subnet) ) ? ERROR_SUCCESS: Error;
} else {
//
// Has to be one of the furhter down subnets..
//
Start = Mid + 1;
}
} // while
//
// couldn't find it unfortunately..
//
return ERROR_FILE_NOT_FOUND;
}
//BeginExport(function)
DWORD
MemServerGetMAddressInfo(
IN PM_SERVER Server,
IN DWORD Address,
OUT PM_SUBNET *Subnet, // OPTIONAL
OUT PM_RANGE *Range, // OPTIONAL
OUT PM_EXCL *Excl, // OPTIONAL
OUT PM_RESERVATION *Reservation // OPTIONAL
) //EndExport(function)
{
ARRAY_LOCATION Loc;
PM_SUBNET ThisMScope;
DWORD Error,Error2;
PM_RANGE RangeTmp;
AssertRet(Server && (Subnet || Range || Excl || Reservation ), ERROR_INVALID_PARAMETER);
if( NULL == Range ) Range = &RangeTmp;
Error = MemArrayInitLoc(&Server->MScopes, &Loc);
while ( ERROR_FILE_NOT_FOUND != Error ) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->MScopes, &Loc, (LPVOID*)&ThisMScope);
Require(ERROR_SUCCESS == Error);
Error2 = MemSubnetGetAddressInfo(
ThisMScope,
Address,
Range,
Excl,
Reservation
);
if (ERROR_SUCCESS == Error2) {
if( Subnet ) *Subnet = ThisMScope;
return ERROR_SUCCESS;
}
Error = MemArrayNextLoc(&Server->MScopes, &Loc);
continue;
}
return ERROR_FILE_NOT_FOUND;
}
//BeginExport(function)
DWORD
MemServerAddSubnet(
IN OUT PM_SERVER Server,
IN PM_SUBNET Subnet, // completely created subnet, must not be
IN ULONG UniqId // in Server's list tho'
) //EndExport(function)
{
DWORD Error;
PM_SUBNET OldSubnet;
ARRAY_LOCATION Loc;
AssertRet(Server && Subnet, ERROR_INVALID_PARAMETER);
AssertRet((Subnet->Mask & Subnet->Address), ERROR_INVALID_PARAMETER);
Subnet->ServerPtr = Server; // set the backptr for future use
//
// First check if subnet duplicates exist and avoid that
//
//
for(
Error = MemArrayInitLoc(&Server->Subnets, &Loc);
NO_ERROR == Error ;
Error = MemArrayNextLoc(&Server->Subnets, &Loc)
) {
Error = MemArrayGetElement(&Server->Subnets, &Loc, &OldSubnet);
Require(ERROR_SUCCESS == Error);
if( (Subnet->Address & OldSubnet->Mask) == OldSubnet->Address
||
(OldSubnet->Address & Subnet->Mask) == Subnet->Address
) {
return ERROR_OBJECT_ALREADY_EXISTS;
}
} // for
//
// Subnets are stored in ascending order of IP addresses.. so insert
// at the right location
//
Subnet->UniqId = UniqId;
for(
Error = MemArrayInitLoc(&Server->Subnets, &Loc)
; ERROR_SUCCESS == Error ;
Error = MemArrayNextLoc(&Server->Subnets, &Loc)
) {
Error = MemArrayGetElement(&Server->Subnets, &Loc, &OldSubnet);
Require(ERROR_SUCCESS == Error);
if( Subnet->Address == OldSubnet->Address ) {
//
// Subnet already present?
//
return ERROR_OBJECT_ALREADY_EXISTS;
}
if( Subnet->Address < OldSubnet->Address ) {
//
// right place to insert the new subnet..
//
Error = MemArrayInitLoc(&Server->Subnets, &Server->Loc);
Require(ERROR_SUCCESS == Error);
Error = MemArrayInsElement(&Server->Subnets, &Loc, Subnet);
Require(ERROR_SUCCESS == Error);
return Error;
}
} // for
//
// This subnet's address is greater than all others.. so add it at end
//
Error = MemArrayAddElement(&Server->Subnets, Subnet);
if( ERROR_SUCCESS != Error) return Error;
Error = MemArrayInitLoc(&Server->Subnets, &Server->Loc);
Require(ERROR_SUCCESS == Error);
return ERROR_SUCCESS;
} // MemServerAddSubnet()
//
// Delete all the elements attached to this subnet.
//
DWORD
MemServerDelSubnetElements(
IN PM_SUBNET Subnet
)
{
DWORD Error;
ARRAY_LOCATION Loc, Loc2;
PM_EXCL Excl;
PM_RANGE Range;
PM_RESERVATION Resrv;
PM_ONECLASS_OPTLIST OptList;
PM_OPTION Option;
// Delete Ranges
Error = MemArrayInitLoc( &Subnet->Ranges, &Loc );
while (( MemArraySize( &Subnet->Ranges ) > 0 ) &&
( ERROR_FILE_NOT_FOUND != Error )) {
Error = MemArrayGetElement( &Subnet->Ranges, &Loc,
( LPVOID * ) &Range );
Require( ERROR_SUCCESS == Error );
Error = DeleteRecord( Range->UniqId );
if ( ERROR_SUCCESS != Error ) {
MemFree( Range );
return Error;
}
Error = MemArrayDelElement( &Subnet->Ranges, &Loc,
( LPVOID * ) &Range );
Require( ERROR_SUCCESS == Error );
MemFree( Range );
} // while
// Delete all exclusions
Error = MemArrayInitLoc( &Subnet->Exclusions, &Loc );
while (( MemArraySize( &Subnet->Exclusions ) > 0 ) &&
( ERROR_FILE_NOT_FOUND != Error )) {
Error = MemArrayGetElement( &Subnet->Exclusions, &Loc,
( LPVOID * ) &Excl );
Require( ERROR_SUCCESS == Error );
Error = DeleteRecord( Excl->UniqId );
if ( ERROR_SUCCESS != Error ) {
MemFree( Excl );
return Error;
}
Error = MemArrayDelElement( &Subnet->Exclusions, &Loc,
( LPVOID * ) &Excl );
Require( ERROR_SUCCESS == Error );
MemFree( Excl );
} // while
// Delete all Reservations
Error = MemArrayInitLoc( &Subnet->Reservations, &Loc );
while (( MemArraySize( &Subnet->Reservations ) > 0 ) &&
( ERROR_FILE_NOT_FOUND != Error )) {
Error = MemArrayGetElement( &Subnet->Reservations, &Loc,
( LPVOID * ) &Resrv );
Require( ERROR_SUCCESS == Error );
Error = MemReserveDel( &Subnet->Reservations, Resrv->Address );
// MemFree( Resrv );
} // while
// Delete all option values
Error = MemOptClassDelClass( &Subnet->Options );
return Error;
} // MemServerDelSubnetElements()
//BeginExport(function)
DWORD
MemServerDelSubnet(
IN OUT PM_SERVER Server,
IN DWORD SubnetAddress,
OUT PM_SUBNET *Subnet
) //EndExport(function)
{
DWORD Error;
PM_SUBNET DeletedSubnet;
ARRAY_LOCATION Loc;
AssertRet(Server && Subnet, ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->Subnets, &Loc);
while( ERROR_FILE_NOT_FOUND != Error ) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->Subnets, &Loc, (LPVOID*)&DeletedSubnet);
Require(ERROR_SUCCESS == Error && DeletedSubnet);
if( SubnetAddress == DeletedSubnet->Address) {
Error = MemServerDelSubnetElements( DeletedSubnet );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = DeleteRecord( DeletedSubnet->UniqId );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = MemArrayDelElement(&Server->Subnets, &Loc, (LPVOID*)Subnet);
Require(ERROR_SUCCESS == Error && Subnet);
Require(*Subnet == DeletedSubnet);
Error = MemArrayInitLoc(&Server->Subnets, &Server->Loc);
// Require(ERROR_SUCCESS == Error); // this may fail if this is the last subnet being deleted
return ERROR_SUCCESS;
}
Error = MemArrayNextLoc(&Server->Subnets, &Loc);
} // while
return ERROR_FILE_NOT_FOUND;
} // MemServerDelSubnet()
//BeginExport(function)
DWORD
MemServerFindSubnetByName(
IN PM_SERVER Server,
IN LPWSTR Name,
OUT PM_SUBNET *Subnet
) //EndExport(function)
{
DWORD Error;
PM_SUBNET ThisSubnet;
ARRAY_LOCATION Loc;
AssertRet(Server && Name && Subnet, ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->Subnets, &Loc);
while( ERROR_FILE_NOT_FOUND != Error ) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->Subnets, &Loc, (LPVOID*)&ThisSubnet);
Require(ERROR_SUCCESS == Error && ThisSubnet);
if( 0 == wcscmp(Name, ThisSubnet->Name) ) {
*Subnet = ThisSubnet;
return ERROR_SUCCESS;
}
Error = MemArrayNextLoc(&Server->Subnets, &Loc);
}
return ERROR_FILE_NOT_FOUND;
}
//================================================================================
// superscope functionality
//================================================================================
//BeginExport(constant)
#define INVALID_SSCOPE_ID 0xFFFFFFFF
#define INVALID_SSCOPE_NAME NULL
//EndExport(constant)
//BeginExport(function)
DWORD
MemServerFindSScope( // find matching with EITHER scopeid ir sscopename
IN OUT PM_SERVER Server,
IN DWORD SScopeId, // 0xFFFFFFFF == invalid scope id, dont use for search
IN LPWSTR SScopeName, // NULL == invalid scope name
OUT PM_SSCOPE *SScope
) //EndExport(function)
{
ARRAY_LOCATION Loc;
DWORD Error;
PM_SSCOPE ThisSScope;
AssertRet(Server && SScope, ERROR_INVALID_PARAMETER);
AssertRet(SScopeId != INVALID_SSCOPE_ID || SScopeName != INVALID_SSCOPE_NAME, ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->SuperScopes, &Loc);
while( ERROR_FILE_NOT_FOUND != Error ) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->SuperScopes, &Loc, &ThisSScope);
Require(ERROR_SUCCESS == Error && ThisSScope);
if( ThisSScope->SScopeId == SScopeId ||
(INVALID_SSCOPE_NAME != SScopeName && 0 == wcscmp(ThisSScope->Name, SScopeName) )) {
*SScope = ThisSScope;
return ERROR_SUCCESS;
}
Error = MemArrayNextLoc(&Server->SuperScopes, &Loc);
}
return ERROR_FILE_NOT_FOUND;
}
//
// Each scope has a field that specifies a superscope. The list of super scopes in
// the memory do not contain any individual physical record in the database. The
// superscope name associated with the scope is the real physical entry.
//
//BeginExport(function)
DWORD
MemServerAddSScope(
IN OUT PM_SERVER Server,
IN PM_SSCOPE SScope
) //EndExport(function)
{
DWORD Error;
PM_SSCOPE OldSScope;
AssertRet(
Server && SScope && INVALID_SSCOPE_ID != SScope->SScopeId && INVALID_SSCOPE_NAME != SScope->Name,
ERROR_INVALID_PARAMETER
);
Error = MemServerFindSScope(
Server,
SScope->SScopeId,
SScope->Name,
&OldSScope
);
if( ERROR_SUCCESS == Error && OldSScope ) return ERROR_OBJECT_ALREADY_EXISTS;
Error = MemArrayAddElement(&Server->SuperScopes, SScope);
return Error;
} // MemServerAddSScope()
//BeginExport(function)
DWORD
MemServerDelSScope(
IN OUT PM_SERVER Server,
IN DWORD SScopeId,
OUT PM_SSCOPE *SScope
) //EndExport(function)
{
DWORD Error;
ARRAY_LOCATION Loc;
PM_SSCOPE ThisSScope;
AssertRet(Server && SScope && INVALID_SSCOPE_ID != SScopeId, ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->SuperScopes, &Loc);
while( ERROR_FILE_NOT_FOUND != Error ) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->SuperScopes, &Loc, (LPVOID *)&ThisSScope);
Require(ERROR_SUCCESS == Error && ThisSScope );
if( ThisSScope->SScopeId == SScopeId ) {
Error = MemArrayDelElement(&Server->SuperScopes, &Loc, (LPVOID *)SScope);
Require(ERROR_SUCCESS == Error && *SScope == ThisSScope);
return Error;
} // if
Error = MemArrayNextLoc(&Server->SuperScopes, &Loc);
} // while
return ERROR_FILE_NOT_FOUND;
} // MemServerDelSScope()
//================================================================================
// MCAST scope functionality
//================================================================================
//BeginExport(constants)
#define INVALID_MSCOPE_ID 0x0
#define INVALID_MSCOPE_NAME NULL
//EndExport(constants)
//BeginExport(function)
DWORD
MemServerFindMScope( // search either based on ScopeId or ScopeName
IN PM_SERVER Server,
IN DWORD MScopeId, // Multicast scope id, or 0 if this is not the key to search on
IN LPWSTR Name, // Multicast scope name or NULL if this is not the key to search on
OUT PM_MSCOPE *MScope
) //EndExport(function)
{
ARRAY_LOCATION Loc;
DWORD Error;
PM_MSCOPE ThisMScope;
AssertRet(Server && MScope, ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->MScopes, &Loc);
while (ERROR_FILE_NOT_FOUND != Error) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->MScopes, &Loc, &ThisMScope);
Require(ERROR_SUCCESS == Error && ThisMScope );
if( MScopeId == ThisMScope->MScopeId ||
(Name != INVALID_MSCOPE_NAME && 0 == wcscmp(Name, ThisMScope->Name)) ) {
*MScope = ThisMScope;
return ERROR_SUCCESS;
}
Error = MemArrayNextLoc(&Server->MScopes, &Loc);
}
return ERROR_FILE_NOT_FOUND;
}
//BeginExport(function)
DWORD
MemServerAddMScope(
IN OUT PM_SERVER Server,
IN OUT PM_MSCOPE MScope,
IN ULONG UniqId
) //EndExport(function)
{
DWORD Error;
PM_MSCOPE OldMScope;
AssertRet(Server && MScope, ERROR_INVALID_PARAMETER);
AssertRet(MScope->MScopeId != INVALID_MSCOPE_ID && MScope->Name != INVALID_MSCOPE_NAME, ERROR_INVALID_PARAMETER);
MScope->ServerPtr = Server; // set the backptr for future use
Error = MemServerFindMScope(
Server,
MScope->Address,
MScope->Name,
&OldMScope
);
if( ERROR_SUCCESS == Error && OldMScope ) return ERROR_OBJECT_ALREADY_EXISTS;
MScope->UniqId = UniqId;
Error = MemArrayAddElement(&Server->MScopes, (LPVOID)MScope);
Require(ERROR_SUCCESS == Error);
return Error;
}
//BeginExport(function)
DWORD
MemServerDelMScope(
IN OUT PM_SERVER Server,
IN DWORD MScopeId,
IN LPWSTR MScopeName,
OUT PM_MSCOPE *MScope
) //EndExport(function)
{
DWORD Error;
ARRAY_LOCATION Loc;
PM_MSCOPE ThisMScope;
AssertRet(Server && MScope && MScopeName != INVALID_MSCOPE_NAME,
ERROR_INVALID_PARAMETER);
Error = MemArrayInitLoc(&Server->MScopes, &Loc);
while (ERROR_FILE_NOT_FOUND != Error) {
Require(ERROR_SUCCESS == Error);
Error = MemArrayGetElement(&Server->MScopes, &Loc, &ThisMScope);
Require(ERROR_SUCCESS == Error && ThisMScope );
if ( INVALID_MSCOPE_ID != MScopeId ) {
if( MScopeId == ThisMScope->MScopeId ) {
Error = MemServerDelSubnetElements( ThisMScope );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = DeleteRecord( ThisMScope->UniqId );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = MemArrayDelElement(&Server->MScopes, &Loc, MScope);
Require(ERROR_SUCCESS == Error && *MScope == ThisMScope);
return Error;
} // if
} // if
if ( INVALID_MSCOPE_NAME != MScopeName ) {
if( !wcscmp(MScopeName, ThisMScope->Name ) ) {
Error = MemServerDelSubnetElements( ThisMScope );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = DeleteRecord( ThisMScope->UniqId );
if ( ERROR_SUCCESS != Error ) {
return Error;
}
Error = MemArrayDelElement(&Server->MScopes, &Loc, MScope);
Require(ERROR_SUCCESS == Error && *MScope == ThisMScope);
return Error;
}
} // if
Error = MemArrayNextLoc(&Server->MScopes, &Loc);
} // while
return ERROR_FILE_NOT_FOUND;
} // MemServerDelMScope()
//================================================================================
// ClassId related stuff
//================================================================================
//BeginExport(function)
DWORD
MemServerGetOptDef(
IN OUT PM_SERVER Server,
IN DWORD ClassId, // required, strict search, no defaulting class to zero
IN DWORD VendorId, // required, strict search, no defaulting vendor to zero
IN DWORD OptId, // OPTIONAL - search by this or following param
IN LPWSTR OptName, // OPTIONAL - search by name or above param
OUT PM_OPTDEF *OptDef // if found return the opt def here
) //EndExport(function)
{
DWORD Error;
PM_OPTDEFLIST OptDefList;
Require(OptDef);
Error = MemOptClassDefListFindOptDefList(
&Server->OptDefs,
ClassId,
VendorId,
&OptDefList
);
if( ERROR_SUCCESS != Error ) return Error;
Require(OptDefList);
return MemOptDefListFindOptDef(
OptDefList,
OptId,
OptName,
OptDef
);
}
//BeginExport(function)
DWORD
MemServerAddOptDef(
IN OUT PM_SERVER Server,
IN DWORD ClassId,
IN DWORD VendorId,
IN DWORD OptId,
IN LPWSTR OptName,
IN LPWSTR OptComment,
IN DWORD Type,
IN LPBYTE OptVal,
IN DWORD OptValLen,
IN ULONG UniqId
) //EndExport(function)
{
DWORD Error;
PM_OPTDEF OptDef;
return MemOptClassDefListAddOptDef(
&Server->OptDefs,
ClassId,
VendorId,
OptId,
Type,
OptName,
OptComment,
OptVal,
OptValLen,
UniqId
);
} // MemOptClassDefListAddOptDef()
//BeginExport(function)
DWORD
MemServerDelOptDef(
IN OUT PM_SERVER Server,
IN DWORD ClassId,
IN DWORD VendorId,
IN DWORD OptId
) //EndExport(function)
{
DWORD Error;
PM_OPTDEFLIST OptDefList;
Error = MemOptClassDefListFindOptDefList(
&Server->OptDefs,
ClassId,
VendorId,
&OptDefList
);
if( ERROR_SUCCESS != Error ) return Error;
return MemOptDefListDelOptDef(
OptDefList,
OptId
);
}
//================================================================================
// end of File
//================================================================================