/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    scejet.c

Abstract:

    Sce-Jet service APIs

Author:

    Jin Huang (jinhuang) 13-Jan-1997

Revision History:

--*/

#include "serverp.h"
#include <io.h>

#include <objbase.h>
#include <initguid.h>

#include <crtdbg.h>
#include <stddef.h>
#include <atlconv.h>
#include <atlbase.h>

//#define SCEJET_DBG    1

//
// should be controlled by critical section for static variables
//

static JET_INSTANCE    JetInstance=0;
static BOOL            JetInited=FALSE;
extern CRITICAL_SECTION JetSync;




#define SCE_JET_CORRUPTION_ERROR(Err) (Err == JET_errDatabaseCorrupted ||\
                                       Err == JET_errDiskIO ||\
                                       Err == JET_errReadVerifyFailure ||\
                                       Err == JET_errBadPageLink ||\
                                       Err == JET_errDbTimeCorrupted ||\
                                       Err == JET_errLogFileCorrupt ||\
                                       Err == JET_errCheckpointCorrupt ||\
                                       Err == JET_errLogCorruptDuringHardRestore ||\
                                       Err == JET_errLogCorruptDuringHardRecovery ||\
                                       Err == JET_errCatalogCorrupted ||\
                                       Err == JET_errDatabaseDuplicate)

DEFINE_GUID(CLSID_SceWriter,0x9cb9311a, 0x6b16, 0x4d5c, 0x85, 0x3e, 0x53, 0x79, 0x81, 0x38, 0xd5, 0x51);
// 9cb9311a-6b16-4d5c-853e-53798138d551

typedef struct _FIND_CONTEXT_ {
    DWORD           Length;
    WCHAR           Prefix[SCEJET_PREFIX_MAXLEN];
} SCEJET_FIND_CONTEXT;

//
// each thread has its own FindContext
//
SCEJET_FIND_CONTEXT Thread FindContext;


JET_ERR
SceJetpSeek(
    IN PSCESECTION hSection,
    IN PWSTR LinePrefix,
    IN DWORD PrefixLength,
    IN SCEJET_SEEK_FLAG SeekBit,
    IN BOOL bOkNoMatch
    );

JET_ERR
SceJetpCompareLine(
    IN PSCESECTION   hSection,
    IN JET_GRBIT    grbit,
    IN PWSTR        LinePrefix OPTIONAL,
    IN DWORD        PrefixLength,
    OUT INT         *Result,
    OUT DWORD       *ActualLength OPTIONAL
    );

JET_ERR
SceJetpMakeKey(
    IN JET_SESID SessionID,
    IN JET_TABLEID  TableID,
    IN DOUBLE SectionID,
    IN PWSTR LinePrefix,
    IN DWORD PrefixLength
    );

JET_ERR
SceJetpBuildUpperLimit(
    IN PSCESECTION hSection,
    IN PWSTR      LinePrefix,
    IN DWORD      Len,
    IN BOOL       bReserveCase
    );

SCESTATUS
SceJetpGetAvailableSectionID(
    IN PSCECONTEXT cxtProfile,
    OUT DOUBLE *SectionID
    );

SCESTATUS
SceJetpAddAllSections(
    IN PSCECONTEXT cxtProfile
    );

SCESTATUS
SceJetpConfigJetSystem(
    IN JET_INSTANCE *hinstance
    );

SCESTATUS
SceJetpGetValueFromVersion(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR TableName,
    IN LPSTR ColumnName,
    OUT LPSTR Value OPTIONAL,
    IN DWORD  ValueLen, // number of bytes
    OUT PDWORD pRetLen
    );

SCESTATUS
SceJetpAddGpo(
    IN PSCECONTEXT cxtProfile,
    IN JET_TABLEID TableID,
    IN JET_COLUMNID GpoIDColumnID,
    IN PCWSTR      Name,
    OUT LONG       *pGpoID
    );

//
// Code to handle profile
//
SCESTATUS
SceJetOpenFile(
    IN LPSTR       ProfileFileName,
    IN SCEJET_OPEN_TYPE Flags,
    IN DWORD       dwTableOptions,
    OUT PSCECONTEXT  *cxtProfile
    )
/* ++
Routine Description:

    This routine opens the profile (database) and outputs the context handle.
    The information returned in the context handle include the Jet session ID,
    Jet database ID, Jet table ID for SCP table, Jet column ID for column
    "Name" and "Value" in the SCP table, and optional information for SAP and
    SMP table.

    If the context handle passed in contains not NULL information, this routine
    will close all tables and the database in the context (use the same session).

    The context handle must be freed by LocalFree after its use.

    A new jet session is created when the context handle is created.

Arguments:

    ProfileFileName - ASCII name of a database (profile)

    Flags           - flags to open the database

    cxtProfile      - the context handle (See SCECONTEXT structure)

Return value:

    SCESTATUS_SUCCESS
    SCESTATUS_NOT_ENOUGH_RESOURCE
    SCESTATUS_PROFILE_NOT_FOUND
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_BAD_FORMAT
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_OTHER_ERROR

-- */
{
    JET_ERR     JetErr;
    SCESTATUS   rc;
    BOOL        FreeContext=FALSE;
    JET_GRBIT   JetDbFlag;
    DWORD dwScpTable=0;


    if ( ProfileFileName == NULL || cxtProfile == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( *cxtProfile && ScepIsValidContext(*cxtProfile) ) {
        __try {
            //
            // Close previous opened database
            //
            rc = SceJetCloseFile(
                            *cxtProfile,
                            FALSE,
                            FALSE
                            );
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            //
            // this is a invalid pointer
            //
            *cxtProfile = NULL;
        }
    }

    if ( *cxtProfile == NULL ) {
        //
        // no session
        //
        *cxtProfile = (PSCECONTEXT)LocalAlloc( LMEM_ZEROINIT, sizeof(SCECONTEXT));
        if ( *cxtProfile == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);
        }
        (*cxtProfile)->Type = 0xFFFFFF02L;
        (*cxtProfile)->JetSessionID = JET_sesidNil;
        (*cxtProfile)->JetDbID = JET_dbidNil;
        (*cxtProfile)->OpenFlag = SCEJET_OPEN_READ_WRITE;
        (*cxtProfile)->JetScpID = JET_tableidNil;
        (*cxtProfile)->JetSapID = JET_tableidNil;
        (*cxtProfile)->JetSmpID = JET_tableidNil;
        (*cxtProfile)->JetTblSecID = JET_tableidNil;

        FreeContext = TRUE;

    }

    //
    // Begin a session
    //
    if ( (*cxtProfile)->JetSessionID == JET_sesidNil ) {
        JetErr = JetBeginSession(
                        JetInstance,
                        &((*cxtProfile)->JetSessionID),
                        NULL,
                        NULL
                        );
        rc = SceJetJetErrorToSceStatus(JetErr);
        if ( rc != SCESTATUS_SUCCESS )
            goto Done;
    }

    switch (Flags) {
    case SCEJET_OPEN_EXCLUSIVE:
    case SCEJET_OPEN_NOCHECK_VERSION:
        JetDbFlag = 0;  // read & write
//        JetDbFlag = JET_bitDbExclusive;
        (*cxtProfile)->OpenFlag = SCEJET_OPEN_EXCLUSIVE;
        break;
    case SCEJET_OPEN_READ_ONLY:
        JetDbFlag = JET_bitDbReadOnly;

        (*cxtProfile)->OpenFlag = Flags;
        break;
    default:
        JetDbFlag = 0;
        (*cxtProfile)->OpenFlag = SCEJET_OPEN_READ_WRITE;
        break;
    }

    //
    // Attach database
    //
    JetErr = JetAttachDatabase(
                    (*cxtProfile)->JetSessionID,
                    ProfileFileName,
                    JetDbFlag
                    );
#ifdef SCEJET_DBG
    printf("Attach database JetErr=%d\n", JetErr);
#endif
    if ( JetErr == JET_wrnDatabaseAttached )
        JetErr = JET_errSuccess;

    rc = SceJetJetErrorToSceStatus(JetErr);
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    //
    // Open database
    //
    JetErr = JetOpenDatabase(
                    (*cxtProfile)->JetSessionID,
                    ProfileFileName,
                    NULL,
                    &((*cxtProfile)->JetDbID),
                    JetDbFlag  //JET_bitDbExclusive
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);
#ifdef SCEJET_DBG
    printf("Open database %s return code %d (%d) \n", ProfileFileName, rc, JetErr);
#endif
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    if ( Flags != SCEJET_OPEN_NOCHECK_VERSION ) {

        //
        // Check database format (for security manager, version#)
        //
        rc = SceJetCheckVersion( *cxtProfile, NULL );
        if ( rc != SCESTATUS_SUCCESS )
            goto Done;

#ifdef SCEJET_DBG
    printf("Open: Version check OK\n");
#endif
    }

    //
    // Open section table. must be there
    //
    rc = SceJetOpenTable(
                    *cxtProfile,
                    "SmTblSection",
                    SCEJET_TABLE_SECTION,
                    Flags,
                    NULL
                    );

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    //
    // open smp table -- optional
    //
    rc = SceJetOpenTable(
                    *cxtProfile,
                    "SmTblSmp",
                    SCEJET_TABLE_SMP,
                    Flags,
                    NULL
                    );

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    //
    // get the last used merge table (SCP) to open
    // shouldn't fail
    // 1 - SmTblScp  2 - SmTblScp2  0 - no policy merge
    //
    DWORD Actual;

    rc = SceJetpGetValueFromVersion(
                *cxtProfile,
                "SmTblVersion",
                "LastUsedMergeTable",
                (LPSTR)&dwScpTable,
                4, // number of bytes
                &Actual
                );

    if ( (dwScpTable != SCEJET_MERGE_TABLE_1) &&
         (dwScpTable != SCEJET_MERGE_TABLE_2) ) {

        dwScpTable = SCEJET_LOCAL_TABLE;
    }

    rc = SCESTATUS_SUCCESS;
    (*cxtProfile)->Type &= 0xFFFFFF0FL;

    if ( dwTableOptions & SCE_TABLE_OPTION_MERGE_POLICY ) {
        //
        // in policy propagation
        //
        if ( ( dwScpTable == SCEJET_MERGE_TABLE_2 ) ) {
            //
            // the second table is already propped
            //
            rc = SceJetOpenTable(
                            *cxtProfile,
                            "SmTblScp",
                            SCEJET_TABLE_SCP,
                            Flags,
                            NULL
                            );
            (*cxtProfile)->Type |= SCEJET_MERGE_TABLE_1;

        } else {
            rc = SceJetOpenTable(
                            *cxtProfile,
                            "SmTblScp2",
                            SCEJET_TABLE_SCP,
                            Flags,
                            NULL
                            );
            (*cxtProfile)->Type |= SCEJET_MERGE_TABLE_2;
        }
    } else {

        switch ( dwScpTable ) {
        case SCEJET_MERGE_TABLE_2:
            //
            // the second table
            //
            rc = SceJetOpenTable(
                            *cxtProfile,
                            "SmTblScp2",
                            SCEJET_TABLE_SCP,
                            Flags,
                            NULL
                            );
            break;

        case SCEJET_MERGE_TABLE_1:

            rc = SceJetOpenTable(
                            *cxtProfile,
                            "SmTblScp",
                            SCEJET_TABLE_SCP,
                            Flags,
                            NULL
                            );

            break;

        default:
            //
            // open SMP table instead, because SCP table doesn't have information
            //
            (*cxtProfile)->JetScpID = (*cxtProfile)->JetSmpID;
            (*cxtProfile)->JetScpSectionID = (*cxtProfile)->JetSmpSectionID;
            (*cxtProfile)->JetScpNameID = (*cxtProfile)->JetSmpNameID;
            (*cxtProfile)->JetScpValueID = (*cxtProfile)->JetSmpValueID;
            (*cxtProfile)->JetScpGpoID = 0;

            break;
        }

        (*cxtProfile)->Type |= dwScpTable;
    }

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    if ( dwTableOptions & SCE_TABLE_OPTION_TATTOO ) {

        rc = SceJetOpenTable(
                        *cxtProfile,
                        "SmTblTattoo",
                        SCEJET_TABLE_TATTOO,
                        Flags,
                        NULL
                        );

    } else {
        //
        // open sap table -- optional
        //
        rc = SceJetOpenTable(
                        *cxtProfile,
                        "SmTblSap",
                        SCEJET_TABLE_SAP,
                        Flags,
                        NULL
                        );
    }

Done:

    if ( rc != SCESTATUS_SUCCESS ) {
        SceJetCloseFile(
                *cxtProfile,
                FALSE,
                FALSE
                );

        if ( FreeContext == TRUE ) {
            if ( (*cxtProfile)->JetSessionID != JET_sesidNil ) {
                JetEndSession(
                    (*cxtProfile)->JetSessionID,
                    JET_bitForceSessionClosed
                    );
            }
            LocalFree(*cxtProfile);
            *cxtProfile = NULL;
        }
    }

    return(rc);

}


SCESTATUS
SceJetCreateFile(
    IN LPSTR        ProfileFileName,
    IN SCEJET_CREATE_TYPE    Flags,
    IN DWORD        dwTableOptions,
    OUT PSCECONTEXT  *cxtProfile
    )
/* ++
Routine Description:

    This routine creates a database (profile) and outputs the context handle.
    See comments in SceJetOpenFile for information contained in the context.

    If the database name already exists in the system, there are 3 options:
        Flags = SCEJET_OVERWRITE_DUP - the existing database will be erased and
                                          recreated.
        Flags = SCEJET_OPEN_DUP      - the existing database will be opened and
                                          format is checked
        Flags = SCEJET_OPEN_DUP_EXCLUSIVE - the existing database will be opened
                                            exclusively.
        Flags = SCEJET_RETURN_ON_DUP - a error code SCESTATUS_FILE_EXIST is returned.

    When creating the database, only SCP table is created initially. SAP and SMP
    tables will be created when analysis is performed.

    The context handle must be freed by LocalFree after its use.

Arguments:

    ProfileFileName - ASCII name of a database to create.

    Flags           - This flag is used when there is an duplicate database
                            SCEJET_OVERWRITE_DUP
                            SCEJET_OPEN_DUP
                            SCEJET_OPEN_DUP_EXCLUSIVE
                            SCEJET_RETURN_ON_DUP

    cxtProfile      - The context handle

Return value:

    SCESTATUS_SUCCESS
    SCESTATUS_NOT_ENOUGH_RESOURCE
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_PROFILE_NOT_FOUND
    SCESTATUS_OBJECT_EXIST
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_CANT_DELETE
    SCESTATUS_OTHER_ERROR

    SCESTATUS from SceJetOpenFile

-- */
{
    JET_ERR     JetErr;
    SCESTATUS    rc=SCESTATUS_SUCCESS;
    BOOL        FreeContext=FALSE;
    DWORD       Len;
    FLOAT       Version=(FLOAT)1.2;
    JET_TABLEID TableID;
    JET_COLUMNID ColumnID;


    if ( ProfileFileName == NULL || cxtProfile == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( *cxtProfile && ScepIsValidContext(*cxtProfile) ) {
        //
        // Close previous opened database
        //
        rc = SceJetCloseFile(
                        *cxtProfile,
                        FALSE,
                        FALSE
                        );
    } else {
        *cxtProfile = NULL;
    }

    if ( *cxtProfile == NULL ) {
        //
        // no session
        //
        *cxtProfile = (PSCECONTEXT)LocalAlloc( LMEM_ZEROINIT, sizeof(SCECONTEXT));
        if ( *cxtProfile == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);
        }
        (*cxtProfile)->Type = 0xFFFFFF02L;
        (*cxtProfile)->JetSessionID = JET_sesidNil;
        (*cxtProfile)->JetDbID = JET_dbidNil;
        (*cxtProfile)->OpenFlag = SCEJET_OPEN_READ_WRITE;
        (*cxtProfile)->JetScpID = JET_tableidNil;
        (*cxtProfile)->JetSapID = JET_tableidNil;
        (*cxtProfile)->JetSmpID = JET_tableidNil;
        (*cxtProfile)->JetTblSecID = JET_tableidNil;

        FreeContext = TRUE;

    }

    (*cxtProfile)->Type &= 0xFFFFFF0FL;

    //
    // Begin a session
    //
    if ( (*cxtProfile)->JetSessionID == JET_sesidNil ) {
        JetErr = JetBeginSession(
                        JetInstance,
                        &((*cxtProfile)->JetSessionID),
                        NULL,
                        NULL
                        );
        rc = SceJetJetErrorToSceStatus(JetErr);
        if ( rc != SCESTATUS_SUCCESS )
            goto Done;
    }
    //
    // Create database
    //
    JetErr = JetCreateDatabase(
                    (*cxtProfile)->JetSessionID,
                    ProfileFileName,
                    NULL,
                    &((*cxtProfile)->JetDbID),
                    JET_bitDbExclusive
                    );
    if ( JET_errFileNotFound == JetErr ) {
        //
        // if no access to create a file in the path
        // ESENT returns this error. It's fixed in ESE98
        // we have to mask it to access denied error for now
        //
        JetErr = JET_errFileAccessDenied;
    }
#ifdef SCEJET_DBG
    printf("Create database %s JetErr = %d\n", ProfileFileName, JetErr);
#endif
    rc = SceJetJetErrorToSceStatus(JetErr);

    (*cxtProfile)->OpenFlag = SCEJET_OPEN_EXCLUSIVE;

    if ( rc == SCESTATUS_OBJECT_EXIST ) {
        switch ( Flags ) {
        case SCEJET_OVERWRITE_DUP:
            //
            // erase the database
            //

            JetDetachDatabase(
                    (*cxtProfile)->JetSessionID,
                    ProfileFileName
                    );

            if ( !DeleteFileA(ProfileFileName) &&
                 GetLastError() != ERROR_FILE_NOT_FOUND ) {

                ScepLogOutput3(1,GetLastError(), SCEDLL_ERROR_DELETE_DB );
            }

            //
            // if delete database failed, log the error but continue to
            // create the database. This call will fail with Jet error.
            //
            JetErr = JetCreateDatabase(
                            (*cxtProfile)->JetSessionID,
                            ProfileFileName,
                            NULL,
                            &((*cxtProfile)->JetDbID),
                            JET_bitDbExclusive
                            );
            if ( JET_errFileNotFound == JetErr ) {
                //
                // if no access to create a file in the path
                // ESENT returns this error. It's fixed in ESE98
                // we have to mask it to access denied error for now
                //
                JetErr = JET_errFileAccessDenied;
            }

            rc = SceJetJetErrorToSceStatus(JetErr);

            break;

        case SCEJET_OPEN_DUP:
            //
            // Open the database
            //
            rc = SceJetOpenFile(
                    ProfileFileName,
                    SCEJET_OPEN_READ_WRITE,
                    dwTableOptions,
                    cxtProfile
                    );
            goto Done;
            break;

        case SCEJET_OPEN_DUP_EXCLUSIVE:
            //
            // Open the database
            //
            rc = SceJetOpenFile(
                    ProfileFileName,
                    SCEJET_OPEN_EXCLUSIVE,
                    dwTableOptions,
                    cxtProfile
                    );
            goto Done;
            break;
        }
    }

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;
#ifdef SCEJET_DBG
    printf("Create/Open database\n");
#endif

    //
    // create required tables - SmTblVersion
    //

    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblVersion",
                    SCEJET_TABLE_VERSION,
                    SCEJET_CREATE_IN_BUFFER,
                    &TableID,
                    &ColumnID
                    );
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    //
    // insert one record into the version table
    //
    JetErr = JetPrepareUpdate((*cxtProfile)->JetSessionID,
                              TableID,
                              JET_prepInsert
                              );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // set value "1.2" in "Version" column
        //

        JetErr = JetSetColumn(
                        (*cxtProfile)->JetSessionID,
                        TableID,
                        ColumnID,
                        (void *)&Version,
                        4,
                        0, //JET_bitSetOverwriteLV,
                        NULL
                        );

        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc != SCESTATUS_SUCCESS ) {
            //
            // if setting fails, cancel the prepared record
            //
            JetPrepareUpdate( (*cxtProfile)->JetSessionID,
                              TableID,
                              JET_prepCancel
                              );
        } else {

            //
            // Setting columns succeed. Update the record
            //
            JetErr = JetUpdate( (*cxtProfile)->JetSessionID,
                               TableID,
                               NULL,
                               0,
                               &Len
                               );
            rc = SceJetJetErrorToSceStatus(JetErr);
        }
    }

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

#ifdef SCEJET_DBG
    printf("create version table\n");
#endif
    //
    // create section table and insert pre-defined sections
    //
    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblSection",
                    SCEJET_TABLE_SECTION,
                    SCEJET_CREATE_IN_BUFFER,
                    NULL,
                    NULL
                    );
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

#ifdef SCEJET_DBG
    printf("create section table\n");
#endif

    rc = SceJetpAddAllSections(
                *cxtProfile
                );
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

#ifdef SCEJET_DBG
    printf("add sections\n");
#endif


    //
    // create scp table
    //
    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblScp",
                    SCEJET_TABLE_SCP,
                    SCEJET_CREATE_IN_BUFFER,
                    NULL,
                    NULL
                    );
#ifdef SCEJET_DBG
    printf("Create table scp %d\n", rc);
#endif
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    if ( dwTableOptions & SCE_TABLE_OPTION_MERGE_POLICY ) {
        (*cxtProfile)->Type |= SCEJET_MERGE_TABLE_1;
    } else {
        (*cxtProfile)->Type |= SCEJET_LOCAL_TABLE;
    }

    //
    // create scp table
    //
    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblSmp",
                    SCEJET_TABLE_SMP,
                    SCEJET_CREATE_IN_BUFFER,
                    NULL,
                    NULL
                    );
#ifdef SCEJET_DBG
    printf("Create table smp %d\n", rc);
#endif

    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblScp2",
                    SCEJET_TABLE_SCP,
                    SCEJET_CREATE_NO_TABLEID,
                    NULL,
                    NULL
                    );
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    rc = SceJetCreateTable(
                    *cxtProfile,
                    "SmTblGpo",
                    SCEJET_TABLE_GPO,
                    SCEJET_CREATE_NO_TABLEID,
                    NULL,
                    NULL
                    );
    if ( rc != SCESTATUS_SUCCESS )
        goto Done;

    if ( dwTableOptions & SCE_TABLE_OPTION_TATTOO ) {
        rc = SceJetCreateTable(
                        *cxtProfile,
                        "SmTblTattoo",
                        SCEJET_TABLE_TATTOO,
                        SCEJET_CREATE_IN_BUFFER,
                        NULL,
                        NULL
                        );
    }

Done:

    //
    // clearn up if error out
    //
    if ( rc != SCESTATUS_SUCCESS ) {

        SceJetCloseFile(
                *cxtProfile,
                FALSE,
                FALSE
                );
        if ( FreeContext == TRUE ) {
            if ( (*cxtProfile)->JetSessionID != JET_sesidNil ) {
                JetEndSession(
                    (*cxtProfile)->JetSessionID,
                    JET_bitForceSessionClosed
                    );
            }
            LocalFree(*cxtProfile);
            *cxtProfile = NULL;
        }
    }

    return(rc);

}


SCESTATUS
SceJetCloseFile(
    IN PSCECONTEXT   hProfile,
    IN BOOL         TermSession,
    IN BOOL         Terminate
    )
/* ++
Routine Description:

    This routine closes a context handle, which closes all tables opened in
    the database and then closes the database.

    Terminate parameter is ignored and Jet engine is not stoppped when this parameter
    is set to TRUE, because there might be other clients using Jet and Jet writer is
    dependent on it.

Arguments:

    hProfile    - The context handle

    Terminate   - TRUE = Terminate the Jet session and engine.

Return value:

    SCESTATUS_SUCCESS

-- */
{

    JET_ERR     JetErr;


    if ( hProfile == NULL )
        goto Terminate;

    CHAR szDbName[1025];

    //
    // Close SCP table if it is opened
    //
    if ( (hProfile->JetScpID != JET_tableidNil) ) {

        if ( hProfile->JetScpID != hProfile->JetSmpID ) {
            JetErr = JetCloseTable(
                        hProfile->JetSessionID,
                        hProfile->JetScpID
                        );
        }
        hProfile->JetScpID = JET_tableidNil;
    }
    //
    // Close SAP table if it is opened
    //
    if ( hProfile->JetSapID != JET_tableidNil ) {
        JetErr = JetCloseTable(
                    hProfile->JetSessionID,
                    hProfile->JetSapID
                    );
        hProfile->JetSapID = JET_tableidNil;
    }
    //
    // Close SMP table if it is opened
    //
    if ( hProfile->JetSmpID != JET_tableidNil ) {
        JetErr = JetCloseTable(
                    hProfile->JetSessionID,
                    hProfile->JetSmpID
                    );
        hProfile->JetSmpID = JET_tableidNil;
    }

    //
    // get database name
    // do not care if there is error
    //
    szDbName[0] = '\0';
    szDbName[1024] = '\0';

    if ( hProfile->JetDbID != JET_dbidNil ) {

        JetGetDatabaseInfo(hProfile->JetSessionID,
                           hProfile->JetDbID,
                           (void *)szDbName,
                           1024,
                           JET_DbInfoFilename
                           );

        //
        // Close the database
        //
        JetErr = JetCloseDatabase(
                        hProfile->JetSessionID,
                        hProfile->JetDbID,
                        0
                        );
        hProfile->JetDbID = JET_dbidNil;

        //
        // should detach the database if the database name is not NULL
        // the database is always attached when it's to open
        // do not care error
        //
        if ( szDbName[0] != '\0' ) {
            JetDetachDatabase(hProfile->JetSessionID, szDbName);
        }
    }

    if ( TermSession || Terminate ) {
        if ( hProfile->JetSessionID != JET_sesidNil ) {

            JetEndSession(
                hProfile->JetSessionID,
                JET_bitForceSessionClosed
                );
            hProfile->JetSessionID = JET_sesidNil;
        }

        hProfile->Type = 0;

        LocalFree(hProfile);
    }

Terminate:

/*
    if ( Terminate ) {

        JetTerm(JetInstance);
        JetInstance = 0;
        JetInited = FALSE;

    }
*/
    return(SCESTATUS_SUCCESS);

}


//
// Code to handle sections
//

SCESTATUS
SceJetOpenSection(
    IN PSCECONTEXT   hProfile,
    IN DOUBLE        SectionID,
    IN SCEJET_TABLE_TYPE        tblType,
    OUT PSCESECTION   *hSection
    )
/* ++
Routine Description:

    This routine saves table and section information in the section context
    handle for other section API's use. SCP, SAP, and SMP tables have the
    same section names. The table type indicates which table this section is
    in.

    The section context handle must be freed by LocalFree after its use.

Arguments:

    hProfile    - The profile context handle

    SectionID   - ID of the section to open

    tblType     - The type of the table for this section
                        SCEJET_TABLE_SCP
                        SCEJET_TABLE_SAP
                        SCEJET_TABLE_SMP

    hSection    - The seciton context handle

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_NOT_ENOUGH_RESOURCE

-- */
{
    if ( hProfile == NULL ||
         hSection == NULL ||
         SectionID == (DOUBLE)0 ||
         (tblType != SCEJET_TABLE_SCP &&
          tblType != SCEJET_TABLE_SAP &&
          tblType != SCEJET_TABLE_SMP &&
          tblType != SCEJET_TABLE_TATTOO) )
        return(SCESTATUS_INVALID_PARAMETER);

    if ( hProfile->JetSessionID == JET_sesidNil ||
         hProfile->JetDbID == JET_dbidNil ||
         (tblType == SCEJET_TABLE_SCP && hProfile->JetScpID == JET_tableidNil ) ||
         (tblType == SCEJET_TABLE_SMP && hProfile->JetSmpID == JET_tableidNil ) ||
         (tblType == SCEJET_TABLE_SAP && hProfile->JetSapID == JET_tableidNil ) ||
         (tblType == SCEJET_TABLE_TATTOO && hProfile->JetSapID == JET_tableidNil ) )
        return(SCESTATUS_BAD_FORMAT);


    if ( *hSection == NULL ) {
        //
        // Allocate memory
        //
        *hSection = (PSCESECTION)LocalAlloc( (UINT)0, sizeof(SCESECTION));
        if ( *hSection == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);
        }
    }

    (*hSection)->SectionID = SectionID;

    //
    // assign other info to the section context
    //
    (*hSection)->JetSessionID = hProfile->JetSessionID;
    (*hSection)->JetDbID = hProfile->JetDbID;

    switch (tblType) {
    case SCEJET_TABLE_SCP:
        (*hSection)->JetTableID = hProfile->JetScpID;
        (*hSection)->JetColumnSectionID = hProfile->JetScpSectionID;
        (*hSection)->JetColumnNameID = hProfile->JetScpNameID;
        (*hSection)->JetColumnValueID = hProfile->JetScpValueID;
        (*hSection)->JetColumnGpoID = hProfile->JetScpGpoID;
        break;
    case SCEJET_TABLE_SAP:
    case SCEJET_TABLE_TATTOO:
        (*hSection)->JetTableID = hProfile->JetSapID;
        (*hSection)->JetColumnSectionID = hProfile->JetSapSectionID;
        (*hSection)->JetColumnNameID = hProfile->JetSapNameID;
        (*hSection)->JetColumnValueID = hProfile->JetSapValueID;
        (*hSection)->JetColumnGpoID = 0;
        break;
    default:
        (*hSection)->JetTableID = hProfile->JetSmpID;
        (*hSection)->JetColumnSectionID = hProfile->JetSmpSectionID;
        (*hSection)->JetColumnNameID = hProfile->JetSmpNameID;
        (*hSection)->JetColumnValueID = hProfile->JetSmpValueID;
        (*hSection)->JetColumnGpoID = 0;
        break;
    }

    return(SCESTATUS_SUCCESS);
}


SCESTATUS
SceJetGetLineCount(
    IN PSCESECTION hSection,
    IN PWSTR      LinePrefix OPTIONAL,
    IN BOOL       bExactCase,
    OUT DWORD      *Count
    )
/* ++
Fucntion Description:

    This routine counts the number of lines matching the LinePrefix (Key)
    in the section. If LinePrefix is NULL, all lines is counted.

Arguments:

    hSection    - The context handle for the section.

    LinePrefix  - The whole or partial key to match. If NULL, all lines in the
                    section is counted.

    Count       - The output count.

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_OTHER_ERROR

-- */
{
    SCESTATUS    rc;
    JET_ERR     JetErr;
    DWORD       Len;
    INT         Result=0;
    SCEJET_SEEK_FLAG  SeekFlag;


    if ( hSection == NULL || Count == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // initialize
    //
    *Count = 0;

    if ( LinePrefix == NULL ) {
        Len = 0;
        SeekFlag = SCEJET_SEEK_GE;
    } else {
        Len = wcslen(LinePrefix)*sizeof(WCHAR);

        if ( bExactCase )
            SeekFlag = SCEJET_SEEK_GE;
        else
            SeekFlag = SCEJET_SEEK_GE_NO_CASE;
    }

    //
    // seek the first occurance
    //

    rc = SceJetSeek(
                hSection,
                LinePrefix,
                Len,
                SeekFlag
                );

    if ( rc == SCESTATUS_RECORD_NOT_FOUND ) {
        // no matching record is found
        return(SCESTATUS_SUCCESS);
    }

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // Find the record or the next record
        // Define the upper index range
        //
        if ( Len <= 247 ) {

            JetErr = SceJetpBuildUpperLimit(
                            hSection,
                            LinePrefix,
                            Len,
                            bExactCase
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);
            if ( rc != SCESTATUS_SUCCESS )
                return(rc);

            //
            // count from the current position to the end of the range
            //
            JetErr = JetIndexRecordCount(
                            hSection->JetSessionID,
                            hSection->JetTableID,
                            (unsigned long *)Count,
                            (unsigned long)0xFFFFFFFF   // maximal count
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);

            //
            // reset the index range. don't care the error code returned
            //
            JetErr = JetSetIndexRange(
                            hSection->JetSessionID,
                            hSection->JetTableID,
                            JET_bitRangeRemove
                            );

        } else {
            //
            // Prefix is longer than 247. The index built does not contan all info
            // loop through each record to count
            //
            do {
                // current record is the same.
                *Count = *Count + 1;
                //
                // move to next record
                //
                JetErr = JetMove(hSection->JetSessionID,
                                 hSection->JetTableID,
                                 JET_MoveNext,
                                 0
                                 );
                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( rc == SCESTATUS_SUCCESS ) {
                    // check the record

                    JetErr = SceJetpCompareLine(
                                    hSection,
                                    JET_bitSeekGE,
                                    LinePrefix,
                                    Len,
                                    &Result,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);
                }

            } while ( rc == SCESTATUS_SUCCESS && Result == 0 );
        }

        if ( rc == SCESTATUS_RECORD_NOT_FOUND )
            rc = SCESTATUS_SUCCESS;

    }

    return(rc);
}



SCESTATUS
SceJetDelete(
    IN PSCESECTION  hSection,
    IN PWSTR       LinePrefix,
    IN BOOL        bObjectFolder,
    IN SCEJET_DELETE_TYPE    Flags
    )
/* ++
Fucntion Description:

    This routine deletes the current record, prefix records, or the whole
    section, depending on the Flags.

Arguments:

    hSection    - The context handle of the section

    LinePrefix  - The prefix to start with for the deleted lines. This value
                  is only used when Flags is set to SCEJET_DELETE_PARTIAL

    Flags       - Options
                    SCEJET_DELETE_SECTION
                    SCEJET_DELETE_LINE
                    SCEJET_DELETE_PARTIAL

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_ACCESS_DEINED
    SCESTATUS_RECORD_NOT_FOUND
    SCESTATUS_OTHER_ERROR

-- */
{
    JET_ERR     JetErr;
    SCESTATUS    rc;
    INT         Result = 0;
    PWSTR       TempPrefix=NULL;
    DWORD       Len;
    SCEJET_SEEK_FLAG  SeekFlag;
    PWSTR  NewPrefix=NULL;
    DOUBLE      SectionID;
    DWORD       Actual;


    if ( hSection == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( Flags == SCEJET_DELETE_PARTIAL ||
         Flags == SCEJET_DELETE_PARTIAL_NO_CASE ) {

        if ( LinePrefix == NULL )
            return(SCESTATUS_INVALID_PARAMETER);

        Len = wcslen(LinePrefix);
        //
        // delete this node exact match first
        //
        if ( Flags == SCEJET_DELETE_PARTIAL )
            SeekFlag = SCEJET_SEEK_EQ;
        else
            SeekFlag = SCEJET_SEEK_EQ_NO_CASE;

        rc = SceJetSeek(hSection,
                        LinePrefix,
                        Len*sizeof(WCHAR),
                        SeekFlag
                        );

        if ( rc == SCESTATUS_SUCCESS ) {
            JetErr = JetDelete(hSection->JetSessionID, hSection->JetTableID);
            rc = SceJetJetErrorToSceStatus(JetErr);
        }

        if ( SCESTATUS_RECORD_NOT_FOUND == rc ) {
            rc = SCESTATUS_SUCCESS;
        }

        if ( rc == SCESTATUS_SUCCESS ) {

            if ( bObjectFolder &&
                 LinePrefix[Len-1] != L'\\' ) {

                Len++;
                NewPrefix = (PWSTR)ScepAlloc(0, (Len+1)*sizeof(WCHAR));

                if ( NewPrefix == NULL ) {
                    return(SCESTATUS_NOT_ENOUGH_RESOURCE);
                }
                wcscpy(NewPrefix, LinePrefix);
                NewPrefix[Len-1] = L'\\';
            }

        } else {
            return(rc);
        }

        Len = Len*sizeof(WCHAR);

    }

    if ( Flags == SCEJET_DELETE_LINE ||
         Flags == SCEJET_DELETE_LINE_NO_CASE ) {
        if ( LinePrefix == NULL ) {
            //
            // delete current line
            // check the current's sectionID before deleting
            //

            rc = SceJetJetErrorToSceStatus(JetRetrieveColumn(
                                                    hSection->JetSessionID,
                                                    hSection->JetTableID,
                                                    hSection->JetColumnSectionID,
                                                    (void *)&SectionID,
                                                    8,
                                                    &Actual,
                                                    0,
                                                    NULL
                                                    ));


            if (rc == SCESTATUS_SUCCESS && hSection->SectionID != SectionID)
                rc = SCESTATUS_RECORD_NOT_FOUND;

            if (rc == SCESTATUS_SUCCESS) {
                JetErr = JetDelete(hSection->JetSessionID, hSection->JetTableID);
                rc = SceJetJetErrorToSceStatus(JetErr);
            }

        } else {
            if ( Flags == SCEJET_DELETE_LINE )
                SeekFlag = SCEJET_SEEK_EQ;
            else
                SeekFlag = SCEJET_SEEK_EQ_NO_CASE;

            rc = SceJetSeek(hSection,
                               LinePrefix,
                               wcslen(LinePrefix)*sizeof(WCHAR),
                               SeekFlag
                               );
            if ( rc == SCESTATUS_SUCCESS ) {
                JetErr = JetDelete(hSection->JetSessionID, hSection->JetTableID);
                rc = SceJetJetErrorToSceStatus(JetErr);
            }

        }

        return(rc);
    }

    if ( Flags == SCEJET_DELETE_SECTION ||
         Flags == SCEJET_DELETE_PARTIAL ||
         Flags == SCEJET_DELETE_PARTIAL_NO_CASE ) {

        if ( Flags == SCEJET_DELETE_SECTION ) {
             //
            // delete the whole section
            // seek the first line of the section
            //
            TempPrefix = NULL;
            Len = 0;
            SeekFlag = SCEJET_SEEK_GE;
        } else {
            //
            // delete all lines begin with the prefix
            // seek the first line of the prefix
            //
            if ( NewPrefix ) {
                TempPrefix = NewPrefix;
            } else {
                TempPrefix = LinePrefix;
            }
            if ( Flags == SCEJET_DELETE_PARTIAL_NO_CASE )
                SeekFlag = SCEJET_SEEK_GE_NO_CASE;
            else
                SeekFlag = SCEJET_SEEK_GE;
        }

        rc = SceJetSeek(hSection, TempPrefix, Len, SeekFlag);

        if ( rc != SCESTATUS_SUCCESS ) {
            if ( NewPrefix ) {
                ScepFree(NewPrefix);
            }
            return(rc);
        }

        do {

            //
            // delete current line
            //
            JetErr = JetDelete(hSection->JetSessionID, hSection->JetTableID);
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc != SCESTATUS_SUCCESS )
                break;

            //
            // move cursor to next line
            //
            JetErr = JetMove(hSection->JetSessionID,
                             hSection->JetTableID,
                             JET_MoveNext,
                             0
                             );
            if ( JetErr == JET_errSuccess ) {
                //
                // compare section ID
                //
                JetErr = SceJetpCompareLine(
                                hSection,
                                JET_bitSeekGE,
                                TempPrefix,
                                Len,
                                &Result,
                                NULL
                                );

                if ( JetErr == JET_errSuccess && Result != 0 )
                    JetErr = JET_errRecordNotFound;

            }

            if ( JetErr == JET_errRecordDeleted ) {
                //
                // skip the deleted record
                //
                JetErr = JET_errSuccess;
                Result = 0;
            }
            rc = SceJetJetErrorToSceStatus(JetErr);


        } while ( rc == SCESTATUS_SUCCESS && Result == 0 );

        if ( rc == SCESTATUS_RECORD_NOT_FOUND )
            rc = SCESTATUS_SUCCESS;

        if ( NewPrefix ) {
            ScepFree(NewPrefix);
        }

        return(rc);
    }

    return(SCESTATUS_SUCCESS);
}

SCESTATUS
SceJetDeleteAll(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR TblName OPTIONAL,
    IN SCEJET_TABLE_TYPE  TblType
    )
/* ++
Fucntion Description:

    This routine deletes everything in the table (specified by name or by type)

Arguments:

    cxtProfile  - The context handle of the database

    TblName     - optional table name to delete (if not to use the table id in context)

    TblType     - specify the table type to use the table id in context, ignored
                    if TblName is specified.

Return Value:

-- */
{
    JET_ERR     JetErr;
    SCESTATUS    rc;

    JET_TABLEID     tmpTblID;

    if ( cxtProfile == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( TblName ) {

        JetErr = JetOpenTable(
                        cxtProfile->JetSessionID,
                        cxtProfile->JetDbID,
                        TblName,
                        NULL,
                        0,
                        0,
                        &tmpTblID
                        );
        if ( JET_errSuccess != JetErr ) {
            return(SceJetJetErrorToSceStatus(JetErr));
        }

    } else {

        switch ( TblType ) {
        case SCEJET_TABLE_SCP:
            tmpTblID = cxtProfile->JetScpID;
            break;
        case SCEJET_TABLE_SMP:
            tmpTblID = cxtProfile->JetSmpID;
            break;
        case SCEJET_TABLE_SAP:
        case SCEJET_TABLE_TATTOO:
            tmpTblID = cxtProfile->JetSapID;
            break;
        case SCEJET_TABLE_SECTION:
            tmpTblID = cxtProfile->JetTblSecID;
            break;
        default:
            return(SCESTATUS_INVALID_PARAMETER);
        }
    }

    //
    // move cursor to next line
    //
    JetErr = JetMove(cxtProfile->JetSessionID,
                     tmpTblID,
                     JET_MoveFirst,
                     0
                     );

    while ( JET_errSuccess == JetErr ) {

        //
        // delete current line
        //
        JetErr = JetDelete(cxtProfile->JetSessionID, tmpTblID);

        //
        // move cursor to next line
        //
        JetErr = JetMove(cxtProfile->JetSessionID,
                         tmpTblID,
                         JET_MoveNext,
                         0
                         );

    }

    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_RECORD_NOT_FOUND )
        rc = SCESTATUS_SUCCESS;

    if ( TblName ) {
        JetCloseTable(cxtProfile->JetSessionID, tmpTblID);
    }

    return(rc);
}


SCESTATUS
SceJetCloseSection(
    IN PSCESECTION   *hSection,
    IN BOOL         DestroySection
    )
/* ++
Fucntion Description:

    Closes a section context handle.

Arguments:

    hSection    - The section context handle to close

Return Value:

    SCE_SUCCESS

-- */
{
    if ( hSection == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( *hSection != NULL ) {
        (*hSection)->JetColumnSectionID = 0;
        (*hSection)->JetColumnNameID = 0;
        (*hSection)->JetColumnValueID = 0;

        (*hSection)->SectionID = (DOUBLE)0;

        if ( DestroySection ) {
            ScepFree(*hSection);
            *hSection = NULL;
        }

    }

    return(SCESTATUS_SUCCESS);
}


//
// Code to handle line
//
SCESTATUS
SceJetGetValue(
    IN PSCESECTION hSection,
    IN SCEJET_FIND_TYPE    Flags,
    IN PWSTR      LinePrefix OPTIONAL,
    IN PWSTR      ActualName  OPTIONAL,
    IN DWORD      NameBufLen,
    OUT DWORD      *RetNameLen OPTIONAL,
    IN PWSTR      Value       OPTIONAL,
    IN DWORD      ValueBufLen,
    OUT DWORD      *RetValueLen OPTIONAL
    )
/* ++
Fucntion Description:

    This routine retrieves a line from the opened section or close the
    previous search context. When Flag is SCEJET_EXACT_MATCH, this routine
    returns the exact matched line for LinePrefix (LinePrefix can't be NULL).
    If this routine is used to get multiple lines, a SCEJET_PREFIX_MATCH
    must be used for the Flags when the first time it is called. If LinePrefix
    is NULL, the first line in the section is returned; otherwise, the first
    line matching the prefix is returned. When continous call is made for the
    same prefix, use SCEJET_NEXT_LINE for the Flags. LinePrefix is not used
    for continous calls. When finish with the continuous calls, a
    SCEJET_CLOSE_VALUE must be used to close the search handle context.

    ActualName and Value contains the actual name and value stored in the
    database for the current line. If these two buffers are not big enough,
    an error will return SCE_BUFFER_TOO_SMALL.

    Passing NULL for ActualName or Value will return the required length for
    that buffer if the RetLength buffer is not NULL.

Arguments:

    hSection    - The context handle of the section

    LinePrefix  - The prefix for the line to start with. This is used only
                    when Flags is set to SCEJET_PREFIX_MATCH

    Flags       - Options for the operation
                    SCEJET_EXACT_MATCH
                    SCEJET_PREFIX_MATCH
                    SCEJET_NEXT_LINE
                    SCEJET_CLOSE_VALUE
                    SCEJET_CURRENT              -- get current record's value

    ActualName  - The buffer for column "Name"

    NameBufLen  - The buffer length of ActualName

    RetNameLen  - the required buffer length for "Name" column

    Value       - The buffer for column "Value"

    ValueBufLen - The buffer length of Value

    RetValueLen - The required buffer length for "Value" column


Return Value:

    SCESTATUS_SUCCESS if success
    SCESTATUS_RECORD_NOT_FOUND if no more match
    other errors:
        SCESTATUS_INVALID_PARAMETER
        SCESTATUS_BUFFER_TOO_SMALL
        SCESTATUS_OTHER_ERROR


-- */
{
    JET_ERR         JetErr;
    SCESTATUS        rc=SCESTATUS_SUCCESS;
    SCESTATUS        rc1;
    DWORD           Len=0;

    JET_RETINFO     RetInfo;
    WCHAR           Buffer[128];
    PVOID           pTemp=NULL;
    INT             Result=0;
    SCEJET_SEEK_FLAG   SeekFlag=SCEJET_SEEK_GT;


    if ( hSection == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    if ( Flags == SCEJET_CLOSE_VALUE ) {
        //
        // close the index range
        //
        if ( FindContext.Length > 0 ) {
            memset(FindContext.Prefix, '\0', FindContext.Length);
            FindContext.Length = 0;
        }

        JetErr = JetSetIndexRange(
                     hSection->JetSessionID,
                     hSection->JetTableID,
                     JET_bitRangeRemove
                     );
        if ( JetErr != JET_errSuccess &&
             JetErr != JET_errKeyNotMade &&
             JetErr != JET_errNoCurrentRecord ) {

            return(SceJetJetErrorToSceStatus(JetErr));
        }
        return(SCESTATUS_SUCCESS);
    }

    //
    // when name/value is requested (not NULL), the return length buffer
    // cannot be NULL.
    // both return length buffer cannot be NULL at the same time
    //
    if ( (ActualName != NULL && RetNameLen == NULL) ||
         (Value != NULL && RetValueLen == NULL) ) {

        return(SCESTATUS_INVALID_PARAMETER);
    }

    switch ( Flags ) {

    case SCEJET_EXACT_MATCH:
    case SCEJET_EXACT_MATCH_NO_CASE:

        if ( LinePrefix == NULL )
            return(SCESTATUS_INVALID_PARAMETER);

        Len = wcslen(LinePrefix)*sizeof(WCHAR);

        if ( Flags == SCEJET_EXACT_MATCH )
            SeekFlag = SCEJET_SEEK_EQ;
        else
            SeekFlag = SCEJET_SEEK_EQ_NO_CASE;

        rc = SceJetSeek(
                    hSection,
                    LinePrefix,
                    Len,
                    SeekFlag
                    );
        break;


    case SCEJET_PREFIX_MATCH:
    case SCEJET_PREFIX_MATCH_NO_CASE:

        if ( LinePrefix != NULL ) {
            Len = wcslen(LinePrefix)*sizeof(WCHAR);

            if ( Len > SCEJET_PREFIX_MAXLEN )
                return(SCESTATUS_PREFIX_OVERFLOW);

        } else {
            Len = 0;
        }

        if ( Flags == SCEJET_PREFIX_MATCH )
            SeekFlag = SCEJET_SEEK_GE;
        else
            SeekFlag = SCEJET_SEEK_GE_NO_CASE;

        rc = SceJetSeek(
                        hSection,
                        LinePrefix,
                        Len,
                        SeekFlag
                        );

        if ( rc == SCESTATUS_SUCCESS ) {
            //
            // remember the find context
            //
            if ( Len > 247 ) {
                //
                // in reality JET doesn't allow keys of more than 255 bytes
                //
                wcsncpy(FindContext.Prefix, LinePrefix, SCEJET_PREFIX_MAXLEN-2);

                if ( Flags == SCEJET_PREFIX_MATCH_NO_CASE )
                    _wcslwr(FindContext.Prefix);

                FindContext.Length = Len;
            }
            //
            // set the upper range limit
            //
            JetErr = SceJetpBuildUpperLimit(
                        hSection,
                        LinePrefix,
                        Len,
                        (Flags == SCEJET_PREFIX_MATCH)
                        );
            rc = SceJetJetErrorToSceStatus(JetErr);
        }
        break;

    case SCEJET_NEXT_LINE:
        //
        // Move to next line
        //
        JetErr = JetMove(hSection->JetSessionID,
                        hSection->JetTableID,
                        JET_MoveNext,
                        0);
        //
        // compare to the prefix
        //
        if ( JetErr == JET_errSuccess && FindContext.Length > 0 ) {

#ifdef SCEJET_DBG
            printf("NextLine: Length is greater than 247\n");
#endif
            JetErr = SceJetpCompareLine(
                            hSection,
                            JET_bitSeekGE,
                            FindContext.Prefix,
                            FindContext.Length,
                            &Result,
                            NULL
                            );
            if ( JetErr == JET_errSuccess && Result != 0 )
                JetErr = JET_errRecordNotFound;

        }
        rc = SceJetJetErrorToSceStatus(JetErr);
        break;

    default:
        //
        // Everything else passed in is treated as the current line
        //
        rc = SCESTATUS_SUCCESS;
        break;
    }

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    //
    // Get this line's value
    //
    RetInfo.ibLongValue = 0;
    RetInfo.itagSequence = 1;
    RetInfo.cbStruct = sizeof(JET_RETINFO);

    if ( ActualName != NULL || RetNameLen != NULL ) {
        //
        // get name field (long binary)
        // if ActualName is NULL, then get the actual bytes
        //
        if ( ActualName != NULL ) {
            Len = NameBufLen;
            pTemp = (void *)ActualName;
        } else {
            Len = 256;
            pTemp = (void *)Buffer;
        }

        JetErr = JetRetrieveColumn(
                        hSection->JetSessionID,
                        hSection->JetTableID,
                        hSection->JetColumnNameID,
                        pTemp,
                        Len,
                        RetNameLen,
                        0,
                        &RetInfo
                        );
#ifdef SCEJET_DBG
        printf("\tJetErr=%d, Len=%d, RetNameLen=%d\n", JetErr, Len, *RetNameLen);
#endif
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_BUFFER_TOO_SMALL ) {
            //
            // if only length is requested, don't care buffer_too_small
            //
            if ( ActualName == NULL )
                rc = SCESTATUS_SUCCESS;
        }

        if ( rc != SCESTATUS_SUCCESS &&
             rc != SCESTATUS_BUFFER_TOO_SMALL )
            return(rc);
    }

    if ( Value != NULL || RetValueLen != NULL ) {
        //
        // Get value field
        // if Value is NULL, then get the actual bytes
        //

        if ( Value != NULL ) {
            Len = ValueBufLen;
            pTemp = (PVOID)Value;
        } else {
            Len = 256;
            pTemp = (PVOID)Buffer;
        }

        JetErr = JetRetrieveColumn(
                        hSection->JetSessionID,
                        hSection->JetTableID,
                        hSection->JetColumnValueID,
                        pTemp,
                        Len,
                        RetValueLen,
                        0,
                        &RetInfo
                        );
#ifdef SCEJET_DBG
        printf("\tJetErr=%d, Len=%d, RetValueLen=%d\n", JetErr, Len, *RetValueLen);
#endif
        rc1 = SceJetJetErrorToSceStatus(JetErr);

        if ( rc1 == SCESTATUS_BUFFER_TOO_SMALL ) {
            //
            // if only length is requested, don't care buffer_too_small
            //
            if ( Value == NULL )
                rc1 = SCESTATUS_SUCCESS;
        }

        if ( rc1 != SCESTATUS_SUCCESS &&
             rc1 != SCESTATUS_BUFFER_TOO_SMALL )
            return(rc1);

        //
        // rc is the status from retrieving Name field
        //
        if ( rc != SCESTATUS_SUCCESS )
            return(rc);
        else
            return(rc1);
    }

    return(rc);
}


SCESTATUS
SceJetSetLine(
    IN PSCESECTION hSection,
    IN PWSTR      Name,
    IN BOOL       bReserveCase,
    IN PWSTR      Value,
    IN DWORD      ValueLen,
    IN LONG       GpoID
    )
/* ++
Fucntion Description:

    This routine writes the Name and Value to the section (hSection).
    If a exact matched name is found, overwrite, else insert a new
    record.

Arguments:

    hSection    - The context handle of the section

    Name        - The info set to Column "Name"

    Value       - The info set to Column "Value"

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_OTHER_ERROR
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_DATA_OVERFLOW

-- */
{
    JET_ERR     JetErr;
    DWORD       Len;
    SCESTATUS    rc;
    DWORD       prep;
    JET_SETINFO SetInfo;
    PWSTR       LwrName=NULL;

    if ( hSection == NULL ||
         Name == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    Len = wcslen(Name)*sizeof(WCHAR);

    if ( Len <= 0 ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( bReserveCase ) {
        LwrName = Name;

    } else {
        //
        // lower cased
        //
        LwrName = (PWSTR)ScepAlloc(0, Len+2);
        if ( LwrName == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);
        }
        wcscpy(LwrName, Name);
        LwrName = _wcslwr(LwrName);

    }

    SetInfo.cbStruct = sizeof(JET_SETINFO);
    SetInfo.itagSequence = 1;
    SetInfo.ibLongValue = 0;

    //
    // check to see if the same key name already exists
    //
    JetErr = SceJetpSeek(
                    hSection,
                    LwrName,
                    Len,
                    SCEJET_SEEK_EQ,
                    FALSE
                    );

    if ( JetErr == JET_errSuccess ||
         JetErr == JET_errRecordNotFound ) {
        if ( JetErr == JET_errSuccess )
            // find a match. overwrite the value
            prep = JET_prepReplace;
        else
            // no match. prepare the record for insertion
            prep = JET_prepInsert;

        JetErr = JetBeginTransaction(hSection->JetSessionID);

        if ( JetErr == JET_errSuccess ) {
            JetErr = JetPrepareUpdate(hSection->JetSessionID,
                                      hSection->JetTableID,
                                      prep
                                      );
            if ( JetErr != JET_errSuccess ) {
                //
                // rollback the transaction
                //
                JetRollback(hSection->JetSessionID,0);
            }
        }
    }

    if ( JetErr != JET_errSuccess)
        return(SceJetJetErrorToSceStatus(JetErr));


    if ( prep == JET_prepInsert ) {
        //
        // set the sectionID column
        //
        JetErr = JetSetColumn(
                        hSection->JetSessionID,
                        hSection->JetTableID,
                        hSection->JetColumnSectionID,
                        (void *)&(hSection->SectionID),
                        8,
                        0, //JET_bitSetOverwriteLV,
                        NULL
                        );
        if ( JetErr == JET_errSuccess ) {
            //
            // set the new key in "Name" column
            //
            JetErr = JetSetColumn(
                            hSection->JetSessionID,
                            hSection->JetTableID,
                            hSection->JetColumnNameID,
                            (void *)LwrName,
                            Len,
                            0, //JET_bitSetOverwriteLV,
                            &SetInfo
                            );
        }

    }

    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // set value column
        //

        JetErr = JetSetColumn(
                        hSection->JetSessionID,
                        hSection->JetTableID,
                        hSection->JetColumnValueID,
                        (void *)Value,
                        ValueLen,
                        0, //JET_bitSetOverwriteLV,
                        &SetInfo
                        );
        if ( JetErr == JET_errSuccess ) {
            //
            // if GPO ID is provided and there is a GPOID column, set it
            //
            if ( GpoID > 0 && hSection->JetColumnGpoID > 0 ) {

                JetErr = JetSetColumn(
                                hSection->JetSessionID,
                                hSection->JetTableID,
                                hSection->JetColumnGpoID,
                                (void *)&GpoID,
                                sizeof(LONG),
                                0,
                                NULL
                                );
                if ( JET_errColumnNotUpdatable == JetErr ) {
                    JetErr = JET_errSuccess;
                }
            }
            // else
            // if can't find the column, ignore the error
            //

            if ( JET_errSuccess == JetErr ) {

                //
                // Setting columns succeed. Update the record
                //
                JetErr = JetUpdate(hSection->JetSessionID,
                                   hSection->JetTableID,
                                   NULL,
                                   0,
                                   &Len
                                   );

            }

        }
        rc = SceJetJetErrorToSceStatus(JetErr);

    }

    if ( rc == SCESTATUS_SUCCESS )
        JetCommitTransaction(hSection->JetSessionID, JET_bitCommitLazyFlush);

    if ( rc != SCESTATUS_SUCCESS ) {
        //
        // if setting fails, cancel the prepared record
        //
        JetPrepareUpdate(hSection->JetSessionID,
                          hSection->JetTableID,
                          JET_prepCancel
                          );
        //
        // Rollback the transaction
        //
        JetRollback(hSection->JetSessionID,0);

    }

    if ( LwrName != Name ) {
        ScepFree(LwrName);
    }

    return(rc);

}


//
// Exported helper APIs
//
SCESTATUS
SceJetCreateTable(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR tblName,
    IN SCEJET_TABLE_TYPE tblType,
    IN SCEJET_CREATE_FLAG nFlags,
    OUT JET_TABLEID *TableID OPTIONAL,
    OUT JET_COLUMNID *ColumnID OPTIONAL
    )
/* ++
Routine Description:

    This routine creates a table in the database opened in the context handle.
    SCP/SAP/SMP tables created in the database have 3 columns: Section, Name,
    and Value, with one index "SectionKey" which is Section+Name ascending.
    Version table has only one column "Version".

Arguments:

    cxtProfile  - The context handle

    tblName     - ASCII name of the table to create

    tblType     - The type of the table. It may be one of the following
                    SCEJET_TABLE_SCP
                    SCEJET_TABLE_SAP
                    SCEJET_TABLE_SMP
                    SCEJET_TABLE_VERSION
                    SCEJET_TABLE_SECTION
                    SCEJET_TABLE_TATTOO
                    SCEJET_TABLE_GPO

    TableID     - SmTblVersion table id when tblType = SCEJET_TABLE_VERSION.

    ColumnID    - The column ID for Version when tblType = SCEJET_TABLE_VERSION

Return value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_OBJECT_EXIST
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR


-- */
{
    JET_ERR             JetErr;
    SCESTATUS            rc;
    JET_TABLECREATE     TableCreate;
    JET_COLUMNCREATE    ColumnCreate[5];
    JET_INDEXCREATE     IndexCreate[2];
    DWORD               numColumns;


    if ( cxtProfile == NULL || tblName == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( TableID ) {
        *TableID = JET_tableidNil;
    }

    if ( ColumnID ) {
        *ColumnID = 0;
    }

    switch ( tblType ) {
    case SCEJET_TABLE_VERSION:

        if ( TableID == NULL || ColumnID == NULL )
            return(SCESTATUS_INVALID_PARAMETER);
        //
        // There is only one column in this table
        //
        ColumnCreate[0].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[0].szColumnName = "Version";
        ColumnCreate[0].coltyp = JET_coltypIEEESingle;
        ColumnCreate[0].cbMax = 4;
        ColumnCreate[0].grbit = JET_bitColumnNotNULL;
        ColumnCreate[0].pvDefault = NULL;
        ColumnCreate[0].cbDefault = 0;
        ColumnCreate[0].cp = 0;
        ColumnCreate[0].columnid = 0;

        ColumnCreate[1].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[1].szColumnName = "AnalyzeTimeStamp";
        ColumnCreate[1].coltyp = JET_coltypBinary;
        ColumnCreate[1].cbMax = 16; // should be 8 bytes - change later
        ColumnCreate[1].grbit = 0;
        ColumnCreate[1].pvDefault = NULL;
        ColumnCreate[1].cbDefault = 0;
        ColumnCreate[1].cp = 0;
        ColumnCreate[1].columnid = 0;

        ColumnCreate[2].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[2].szColumnName = "ConfigTimeStamp";
        ColumnCreate[2].coltyp = JET_coltypBinary;
        ColumnCreate[2].cbMax = 16; // should be 8 bytes - change later
        ColumnCreate[2].grbit = 0;
        ColumnCreate[2].pvDefault = NULL;
        ColumnCreate[2].cbDefault = 0;
        ColumnCreate[2].cp = 0;
        ColumnCreate[2].columnid = 0;

        ColumnCreate[3].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[3].szColumnName = "LastUsedMergeTable";
        ColumnCreate[3].coltyp = JET_coltypLong;
        ColumnCreate[3].cbMax = 4;
        ColumnCreate[3].grbit = 0;
        ColumnCreate[3].pvDefault = NULL;
        ColumnCreate[3].cbDefault = 0;
        ColumnCreate[3].cp = 0;
        ColumnCreate[3].columnid = 0;

        ColumnCreate[4].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[4].szColumnName = "ProfileDescription";
        ColumnCreate[4].coltyp = JET_coltypLongBinary;
        ColumnCreate[4].cbMax = 1024;
        ColumnCreate[4].grbit = 0;
        ColumnCreate[4].pvDefault = NULL;
        ColumnCreate[4].cbDefault = 0;
        ColumnCreate[4].cp = 0;
        ColumnCreate[4].columnid = 0;

        //
        // Assign table info
        //
        TableCreate.cbStruct = sizeof(JET_TABLECREATE);
        TableCreate.szTableName = tblName;
        TableCreate.szTemplateTableName = NULL;
        TableCreate.ulPages = 1;
        TableCreate.ulDensity = 90;
        TableCreate.rgcolumncreate = ColumnCreate;
        TableCreate.cColumns = 5;
        TableCreate.rgindexcreate = NULL;
        TableCreate.cIndexes = 0;
        TableCreate.grbit = 0;
        TableCreate.tableid = 0;

        break;

    case SCEJET_TABLE_SCP:
    case SCEJET_TABLE_SAP:
    case SCEJET_TABLE_SMP:
    case SCEJET_TABLE_TATTOO:

        //
        // There are 3 columns in each table.
        // Assign each column info
        //
        ColumnCreate[0].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[0].szColumnName = "SectionID";
        ColumnCreate[0].coltyp = JET_coltypIEEEDouble;
        ColumnCreate[0].cbMax = 8;
        ColumnCreate[0].grbit = JET_bitColumnNotNULL;
        ColumnCreate[0].pvDefault = NULL;
        ColumnCreate[0].cbDefault = 0;
        ColumnCreate[0].cp = 0;
        ColumnCreate[0].columnid = 0;

        ColumnCreate[1].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[1].szColumnName = "Name";
        ColumnCreate[1].coltyp = JET_coltypLongBinary;
        ColumnCreate[1].cbMax = 1024;
        ColumnCreate[1].grbit = 0;  //JET_bitColumnNotNULL;
        ColumnCreate[1].pvDefault = NULL;
        ColumnCreate[1].cbDefault = 0;
        ColumnCreate[1].cp = 0;
        ColumnCreate[1].columnid = 0;

        ColumnCreate[2].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[2].szColumnName = "Value";
        ColumnCreate[2].coltyp = JET_coltypLongBinary;
        ColumnCreate[2].cbMax = (unsigned long)0x7FFFFFFF;    // 2GB
        ColumnCreate[2].grbit = 0;
        ColumnCreate[2].pvDefault = NULL;
        ColumnCreate[2].cbDefault = 0;
        ColumnCreate[2].cp = 0;
        ColumnCreate[2].columnid = 0;

        numColumns = 3;

        if ( tblType == SCEJET_TABLE_SCP ) {

            ColumnCreate[3].cbStruct = sizeof(JET_COLUMNCREATE);
            ColumnCreate[3].szColumnName = "GpoID";
            ColumnCreate[3].coltyp = JET_coltypLong;
            ColumnCreate[3].cbMax = 4;
            ColumnCreate[3].grbit = 0;
            ColumnCreate[3].pvDefault = NULL;
            ColumnCreate[3].cbDefault = 0;
            ColumnCreate[3].cp = 0;
            ColumnCreate[3].columnid = 0;

            numColumns = 4;
        }

        //
        // Assign index info - one index in each table.
        //
        memset(IndexCreate, 0, sizeof(JET_INDEXCREATE) );
        IndexCreate[0].cbStruct = sizeof(JET_INDEXCREATE);
        IndexCreate[0].szIndexName = "SectionKey";
        IndexCreate[0].szKey = "+SectionID\0+Name\0\0";
        IndexCreate[0].cbKey = 18;
        IndexCreate[0].grbit = 0; // JET_bitIndexPrimary; // | JET_bitIndexUnique;
        IndexCreate[0].ulDensity = 50;
        //
        // Assign table info
        //
        TableCreate.cbStruct = sizeof(JET_TABLECREATE);
        TableCreate.szTableName = tblName;
        TableCreate.szTemplateTableName = NULL;
        TableCreate.ulPages = 20;
        TableCreate.ulDensity = 50;
        TableCreate.rgcolumncreate = ColumnCreate;
        TableCreate.cColumns = numColumns;
        TableCreate.rgindexcreate = IndexCreate;
        TableCreate.cIndexes = 1;
        TableCreate.grbit = 0;
        TableCreate.tableid = 0;

        break;

    case SCEJET_TABLE_SECTION:
        //
        // There are 2 columns in this table.
        // Assign each column info
        //
        ColumnCreate[0].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[0].szColumnName = "SectionID";
        ColumnCreate[0].coltyp = JET_coltypIEEEDouble;
        ColumnCreate[0].cbMax = 8;
        ColumnCreate[0].grbit = JET_bitColumnNotNULL;
        ColumnCreate[0].pvDefault = NULL;
        ColumnCreate[0].cbDefault = 0;
        ColumnCreate[0].cp = 0;
        ColumnCreate[0].columnid = 0;

        ColumnCreate[1].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[1].szColumnName = "Name";
        ColumnCreate[1].coltyp = JET_coltypBinary;
        ColumnCreate[1].cbMax = 255;
        ColumnCreate[1].grbit = JET_bitColumnNotNULL;
        ColumnCreate[1].pvDefault = NULL;
        ColumnCreate[1].cbDefault = 0;
        ColumnCreate[1].cp = 0;
        ColumnCreate[1].columnid = 0;

        //
        // Assign index info - one index in each table.
        //
        memset(IndexCreate, 0, 2*sizeof(JET_INDEXCREATE) );
        IndexCreate[0].cbStruct = sizeof(JET_INDEXCREATE);
        IndexCreate[0].szIndexName = "SectionKey";
        IndexCreate[0].szKey = "+Name\0\0";
        IndexCreate[0].cbKey = 7;
        IndexCreate[0].grbit = JET_bitIndexPrimary; // | JET_bitIndexUnique;
        IndexCreate[0].ulDensity = 80;

        IndexCreate[1].cbStruct = sizeof(JET_INDEXCREATE);
        IndexCreate[1].szIndexName = "SecID";
        IndexCreate[1].szKey = "+SectionID\0\0";
        IndexCreate[1].cbKey = 12;
        IndexCreate[1].grbit = 0;
        IndexCreate[1].ulDensity = 80;
        //
        // Assign table info
        //
        TableCreate.cbStruct = sizeof(JET_TABLECREATE);
        TableCreate.szTableName = tblName;
        TableCreate.szTemplateTableName = NULL;
        TableCreate.ulPages = 10;
        TableCreate.ulDensity = 80;
        TableCreate.rgcolumncreate = ColumnCreate;
        TableCreate.cColumns = 2;
        TableCreate.rgindexcreate = IndexCreate;
        TableCreate.cIndexes = 2;
        TableCreate.grbit = 0;
        TableCreate.tableid = 0;

        break;

    case SCEJET_TABLE_GPO:
        //
        // There are 3 columns in this table.
        // Assign each column info
        //
        ColumnCreate[0].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[0].szColumnName = "GpoID";
        ColumnCreate[0].coltyp = JET_coltypLong;
        ColumnCreate[0].cbMax = 4;
        ColumnCreate[0].grbit = JET_bitColumnNotNULL;
        ColumnCreate[0].pvDefault = NULL;
        ColumnCreate[0].cbDefault = 0;
        ColumnCreate[0].cp = 0;
        ColumnCreate[0].columnid = 0;

        ColumnCreate[1].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[1].szColumnName = "Name";
        ColumnCreate[1].coltyp = JET_coltypBinary;
        ColumnCreate[1].cbMax = 255;
        ColumnCreate[1].grbit = JET_bitColumnNotNULL;
        ColumnCreate[1].pvDefault = NULL;
        ColumnCreate[1].cbDefault = 0;
        ColumnCreate[1].cp = 0;
        ColumnCreate[1].columnid = 0;

        ColumnCreate[2].cbStruct = sizeof(JET_COLUMNCREATE);
        ColumnCreate[2].szColumnName = "DisplayName";
        ColumnCreate[2].coltyp = JET_coltypBinary;
        ColumnCreate[2].cbMax = 255;
        ColumnCreate[2].grbit = 0;
        ColumnCreate[2].pvDefault = NULL;
        ColumnCreate[2].cbDefault = 0;
        ColumnCreate[2].cp = 0;
        ColumnCreate[2].columnid = 0;

        //
        // Assign index info - one index in each table.
        //
        memset(IndexCreate, 0, 2*sizeof(JET_INDEXCREATE) );
        IndexCreate[0].cbStruct = sizeof(JET_INDEXCREATE);
        IndexCreate[0].szIndexName = "SectionKey";
        IndexCreate[0].szKey = "+GpoID\0\0";
        IndexCreate[0].cbKey = 8;
        IndexCreate[0].grbit = JET_bitIndexPrimary; // | JET_bitIndexUnique;
        IndexCreate[0].ulDensity = 80;

        IndexCreate[1].cbStruct = sizeof(JET_INDEXCREATE);
        IndexCreate[1].szIndexName = "GpoName";
        IndexCreate[1].szKey = "+Name\0\0";
        IndexCreate[1].cbKey = 7;
        IndexCreate[1].grbit = 0;
        IndexCreate[1].ulDensity = 80;

        //
        // Assign table info
        //
        TableCreate.cbStruct = sizeof(JET_TABLECREATE);
        TableCreate.szTableName = tblName;
        TableCreate.szTemplateTableName = NULL;
        TableCreate.ulPages = 10;
        TableCreate.ulDensity = 80;
        TableCreate.rgcolumncreate = ColumnCreate;
        TableCreate.cColumns = 3;
        TableCreate.rgindexcreate = IndexCreate;
        TableCreate.cIndexes = 2;
        TableCreate.grbit = 0;
        TableCreate.tableid = 0;

        break;

    default:
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // Create the table, column, and index together
    //
    JetErr = JetCreateTableColumnIndex(
                    cxtProfile->JetSessionID,
                    cxtProfile->JetDbID,
                    &TableCreate
                    );

    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( SCESTATUS_OBJECT_EXIST == rc &&
         TableCreate.tableid != JET_tableidNil ) {
        rc = SCESTATUS_SUCCESS;

    } else if ( rc == SCESTATUS_SUCCESS &&
                TableCreate.tableid == JET_tableidNil ) {

        rc = SCESTATUS_OTHER_ERROR;
    }

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // Save the tableid and columnid in the context
        //
        if ( SCEJET_CREATE_NO_TABLEID == nFlags ) {
            //
            // do not need table ID to be returned
            //
            if ( TableCreate.tableid != JET_tableidNil ) {
                JetCloseTable(
                    cxtProfile->JetSessionID,
                    TableCreate.tableid
                    );
            }

        } else {

            if ( tblType == SCEJET_TABLE_VERSION ) {

                *TableID = TableCreate.tableid;
                *ColumnID = ColumnCreate[0].columnid;

            } else if ( TableID ) {
                *TableID = TableCreate.tableid;

            } else {

                switch ( tblType ) {
                case SCEJET_TABLE_SCP:
                    cxtProfile->JetScpID = TableCreate.tableid;
                    cxtProfile->JetScpSectionID = ColumnCreate[0].columnid;
                    cxtProfile->JetScpNameID = ColumnCreate[1].columnid;
                    cxtProfile->JetScpValueID = ColumnCreate[2].columnid;
                    cxtProfile->JetScpGpoID = ColumnCreate[3].columnid;
                    break;
                case SCEJET_TABLE_SMP:
                    cxtProfile->JetSmpID = TableCreate.tableid;
                    cxtProfile->JetSmpSectionID = ColumnCreate[0].columnid;
                    cxtProfile->JetSmpNameID = ColumnCreate[1].columnid;
                    cxtProfile->JetSmpValueID = ColumnCreate[2].columnid;
                    break;
                case SCEJET_TABLE_SAP:
                case SCEJET_TABLE_TATTOO: // use the SAP handle
                    cxtProfile->JetSapID = TableCreate.tableid;
                    cxtProfile->JetSapSectionID = ColumnCreate[0].columnid;
                    cxtProfile->JetSapNameID = ColumnCreate[1].columnid;
                    cxtProfile->JetSapValueID = ColumnCreate[2].columnid;
                    break;
                case SCEJET_TABLE_SECTION:
                    cxtProfile->JetTblSecID = TableCreate.tableid;
                    cxtProfile->JetSecNameID = ColumnCreate[1].columnid;
                    cxtProfile->JetSecID = ColumnCreate[0].columnid;
                    break;
                }
            }

            if ( tblType != SCEJET_TABLE_VERSION ) {

                //
                // Set current index in this table
                //
                JetErr = JetSetCurrentIndex(
                                cxtProfile->JetSessionID,
                                TableCreate.tableid,
                                "SectionKey"
                                );
                rc = SceJetJetErrorToSceStatus(JetErr);
            }
        }
    }

    return(rc);
}


SCESTATUS
SceJetOpenTable(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR tblName,
    IN SCEJET_TABLE_TYPE tblType,
    IN SCEJET_OPEN_TYPE OpenType,
    OUT JET_TABLEID *TableID
    )
/* ++
Routine Description:

    This routine opens a table, gets column IDs for the column "Name" and
    "Value" and saves them in the context.

Arguments:

    cxtProfile  - The context handle

    tblName     - ASCII name of a table to open

    tblType     - The type of the table. It may be one of the following
                    SCEJET_TABLE_SCP
                    SCEJET_TABLE_SAP
                    SCEJET_TABLE_SMP
                    SCEJET_TABLE_VERSION
                    SCEJET_TABLE_SECTION
                    SCEJET_TABLE_GPO
                    SCEJET_TABLE_TATTOO
Return value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_BAD_FORMAT
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_NOT_ENOUGH_RESOURCE
    SCESTATUS_OTHER_ERROR

-- */
{
    JET_ERR         JetErr;
    JET_TABLEID     *tblID;
    JET_TABLEID     tmpTblID;
    JET_COLUMNDEF   ColumnDef;
    JET_COLUMNID    NameID=0;
    JET_COLUMNID    ValueID=0;
    JET_COLUMNID    SectionID=0;
    JET_COLUMNID    GpoColID=0;
    SCESTATUS       rc;
    JET_GRBIT       grbit=0;

    if ( cxtProfile == NULL || tblName == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    // get address of table id

    if ( TableID ) {
        tblID = TableID;

    } else {

        switch (tblType) {
        case SCEJET_TABLE_SCP:
            tblID = &(cxtProfile->JetScpID);
            break;
        case SCEJET_TABLE_SAP:
        case SCEJET_TABLE_TATTOO:
            tblID = &(cxtProfile->JetSapID);
            break;
        case SCEJET_TABLE_SMP:
            tblID = &(cxtProfile->JetSmpID);
            break;
        case SCEJET_TABLE_SECTION:
            tblID = &(cxtProfile->JetTblSecID);
            break;

        default:
            return(SCESTATUS_INVALID_PARAMETER);
        }
    }

    if ( OpenType == SCEJET_OPEN_READ_ONLY ) {
        grbit = JET_bitTableReadOnly;
    }

    // open this table
    JetErr = JetOpenTable(
                    cxtProfile->JetSessionID,
                    cxtProfile->JetDbID,
                    tblName,
                    NULL,
                    0,
                    grbit,
                    &tmpTblID
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS )
        *tblID = tmpTblID;

    if ( TableID ) {
        return(rc);
    }

    if ( rc != SCESTATUS_SUCCESS ) {
        //
        // SCP and SMP table must exist. SAP and Tattoo tables are optional.
        //
        if ( tblType != SCEJET_TABLE_SCP &&
             tblType != SCEJET_TABLE_SMP &&
             tblType != SCEJET_TABLE_SECTION &&
             ( rc == SCESTATUS_BAD_FORMAT ||
               rc == SCESTATUS_PROFILE_NOT_FOUND) ) {
            return(SCESTATUS_SUCCESS);
        }
        return(rc);
    }

    //
    // get column id for Column "SectionID"
    //
    JetErr = JetGetTableColumnInfo(
                    cxtProfile->JetSessionID,
                    *tblID,
                    "SectionID",
                    (VOID *)&ColumnDef,
                    sizeof(JET_COLUMNDEF),
                    0
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);
    if ( rc != SCESTATUS_SUCCESS ) {
        return(rc);
    }
    SectionID = ColumnDef.columnid;

    //
    // get column id for Column "Name"
    //
    JetErr = JetGetTableColumnInfo(
                    cxtProfile->JetSessionID,
                    *tblID,
                    "Name",
                    (VOID *)&ColumnDef,
                    sizeof(JET_COLUMNDEF),
                    0
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);
    if ( rc != SCESTATUS_SUCCESS ) {
        return(rc);
    }
    NameID = ColumnDef.columnid;

    if ( tblType == SCEJET_TABLE_SCP ||
         tblType == SCEJET_TABLE_SAP ||
         tblType == SCEJET_TABLE_SMP ||
         tblType == SCEJET_TABLE_TATTOO ) {

        //
        // get column id for Column "Value"
        //
        JetErr = JetGetTableColumnInfo(
                        cxtProfile->JetSessionID,
                        *tblID,
                        "Value",
                        (VOID *)&ColumnDef,
                        sizeof(JET_COLUMNDEF),
                        0
                        );
        rc = SceJetJetErrorToSceStatus(JetErr);
        if ( rc != SCESTATUS_SUCCESS ) {
            return(rc);
        }
        ValueID = ColumnDef.columnid;

        if ( tblType == SCEJET_TABLE_SCP ) {
            //
            // get column id for column GpoID
            //
            JetErr = JetGetTableColumnInfo(
                            cxtProfile->JetSessionID,
                            *tblID,
                            "GpoID",
                            (VOID *)&ColumnDef,
                            sizeof(JET_COLUMNDEF),
                            0
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);
            if ( rc != SCESTATUS_SUCCESS ) {
                return(rc);
            }
            GpoColID = ColumnDef.columnid;
        }
    }

    //
    // save the column ids
    //
    switch (tblType) {
    case SCEJET_TABLE_SCP:
        cxtProfile->JetScpSectionID = SectionID;
        cxtProfile->JetScpNameID = NameID;
        cxtProfile->JetScpValueID = ValueID;
        cxtProfile->JetScpGpoID = GpoColID;
        break;
    case SCEJET_TABLE_SAP:
    case SCEJET_TABLE_TATTOO:
        cxtProfile->JetSapSectionID = SectionID;
        cxtProfile->JetSapNameID = NameID;
        cxtProfile->JetSapValueID = ValueID;
        break;
    case SCEJET_TABLE_SMP:
        cxtProfile->JetSmpSectionID = SectionID;
        cxtProfile->JetSmpNameID = NameID;
        cxtProfile->JetSmpValueID = ValueID;
        break;
    case SCEJET_TABLE_SECTION:
        cxtProfile->JetSecID = SectionID;
        cxtProfile->JetSecNameID = NameID;
   }

    //
    // Set current index
    //

    JetErr = JetSetCurrentIndex(
                    cxtProfile->JetSessionID,
                    *tblID,
                    "SectionKey"
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);

    return(rc);

}


SCESTATUS
SceJetDeleteTable(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR tblName,
    IN SCEJET_TABLE_TYPE tblType
    )
{
    JET_ERR         JetErr;
    JET_TABLEID     *tblID;
    SCESTATUS        rc=SCESTATUS_SUCCESS;


    if ( cxtProfile == NULL || tblName == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    // get address of table id
    switch (tblType) {
    case SCEJET_TABLE_SCP:
        tblID = &(cxtProfile->JetScpID);
        break;
    case SCEJET_TABLE_SAP:
    case SCEJET_TABLE_TATTOO:
        tblID = &(cxtProfile->JetSapID);
        break;
    case SCEJET_TABLE_SMP:
        tblID = &(cxtProfile->JetSmpID);
        break;
    case SCEJET_TABLE_SECTION:
        tblID = &(cxtProfile->JetTblSecID);
        break;
    default:
        return(SCESTATUS_INVALID_PARAMETER);
    }

    // close this table
    if ( *tblID != JET_tableidNil ) {
        JetErr = JetCloseTable(
                        cxtProfile->JetSessionID,
                        *tblID
                        );
        rc = SceJetJetErrorToSceStatus(JetErr);
        if ( rc != SCESTATUS_SUCCESS )
            return(rc);

        *tblID = JET_tableidNil;

        //
        // reset each column id
        //
        switch (tblType) {
        case SCEJET_TABLE_SCP:
            cxtProfile->JetScpSectionID = 0;
            cxtProfile->JetScpNameID = 0;
            cxtProfile->JetScpValueID = 0;
            cxtProfile->JetScpGpoID = 0;
            break;
        case SCEJET_TABLE_SAP:
        case SCEJET_TABLE_TATTOO:
            cxtProfile->JetSapSectionID = 0;
            cxtProfile->JetSapNameID = 0;
            cxtProfile->JetSapValueID = 0;
            break;
        case SCEJET_TABLE_SMP:
            cxtProfile->JetSmpSectionID = 0;
            cxtProfile->JetSmpNameID = 0;
            cxtProfile->JetSmpValueID = 0;
            break;
        case SCEJET_TABLE_SECTION:
            cxtProfile->JetSecNameID = 0;
            cxtProfile->JetSecID = 0;
            break;
        }
    }

    JetErr = JetDeleteTable(cxtProfile->JetSessionID,
                            cxtProfile->JetDbID,
                            tblName
                            );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_BAD_FORMAT )
        rc = SCESTATUS_SUCCESS;

    return(rc);

}


SCESTATUS
SceJetCheckVersion(
    IN PSCECONTEXT   cxtProfile,
    OUT FLOAT *pVersion OPTIONAL
    )
/* ++
Routine Description:

    This routine checks the version table in the database to see if the
    database is for the security manager, also if the version # is the
    correct one.

    The version table is named "SmTblVersion" and has a Version column
    in it. The current version # is 1.2

Arguments:

    cxtProfile  - The profile context

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR
    SCESTATUS from SceJetOpenTable

-- */
{
    SCESTATUS        rc;
    FLOAT           Version=(FLOAT)1.0;
    DWORD           Actual;


    if ( cxtProfile == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    rc = SceJetpGetValueFromVersion(
                cxtProfile,
                "SmTblVersion",
                "Version",
                (LPSTR)&Version,
                4, // number of bytes
                &Actual
                );

    if ( rc == SCESTATUS_SUCCESS ||
         rc == SCESTATUS_BUFFER_TOO_SMALL ) {

        if ( Version != (FLOAT)1.2 )
            rc = SCESTATUS_BAD_FORMAT;
        else
            rc = SCESTATUS_SUCCESS;
    }

    if ( pVersion ) {
        *pVersion = Version;
    }

    return(rc);
}


SCESTATUS
SceJetGetSectionIDByName(
    IN PSCECONTEXT cxtProfile,
    IN PCWSTR Name,
    OUT DOUBLE *SectionID OPTIONAL
    )
/* ++
Routine Description:

    This routine retrieve the section ID for the name in the Section table.
    If SectionID is NULL, this routine really does a seek by name. The cursor
    will be on the record if there is a successful match.

Arguments:

    cxtProfile  - The profile context handle

    Name        - The section name looked for

    SectionID   - The output section ID if there is a successful match

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_RECORD_NOT_FOUND
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR

-- */
{
    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Actual;
    PWSTR     LwrName=NULL;
    DWORD     Len;

    if ( cxtProfile == NULL || Name == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    if ( cxtProfile->JetTblSecID <= 0) {
        //
        // Section table is not opened yet
        //
        rc = SceJetOpenTable(
                        cxtProfile,
                        "SmTblSection",
                        SCEJET_TABLE_SECTION,
                        SCEJET_OPEN_READ_ONLY,
                        NULL
                        );

        if ( rc != SCESTATUS_SUCCESS )
            return(rc);
    }

    //
    // set current index to SectionKey (the name)
    //
    JetErr = JetSetCurrentIndex(
                cxtProfile->JetSessionID,
                cxtProfile->JetTblSecID,
                "SectionKey"
                );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    Len = wcslen(Name);
    LwrName = (PWSTR)ScepAlloc(0, (Len+1)*sizeof(WCHAR));

    if ( LwrName != NULL ) {

        wcscpy(LwrName, Name);
        LwrName = _wcslwr(LwrName);

        JetErr = JetMakeKey(
                    cxtProfile->JetSessionID,
                    cxtProfile->JetTblSecID,
                    (VOID *)LwrName,
                    Len*sizeof(WCHAR),
                    JET_bitNewKey
                    );

        if ( JetErr == JET_errKeyIsMade ) {
            //
            // Only one key is needed, it may return this code, even on success.
            //
            JetErr = JET_errSuccess;
        }
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {
            JetErr = JetSeek(
                        cxtProfile->JetSessionID,
                        cxtProfile->JetTblSecID,
                        JET_bitSeekEQ
                        );
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {
                //
                // find the section name, retrieve column SectionID
                //
                if ( SectionID != NULL) {
                    JetErr = JetRetrieveColumn(
                                    cxtProfile->JetSessionID,
                                    cxtProfile->JetTblSecID,
                                    cxtProfile->JetSecID,
                                    (void *)SectionID,
                                    8,
                                    &Actual,
                                    0,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);
                }

            }

        }
        ScepFree(LwrName);

    } else
        rc = SCESTATUS_NOT_ENOUGH_RESOURCE;

    return(rc);

}


SCESTATUS
SceJetGetSectionNameByID(
    IN PSCECONTEXT cxtProfile,
    IN DOUBLE SectionID,
    OUT PWSTR Name OPTIONAL,
    IN OUT LPDWORD pNameLen OPTIONAL
    )
/* ++
Routine Description:

    This routine retrieve the section name for the ID in the Section table.
    If Name is NULL, this routine really does a seek by ID. The cursor will
    be on the record if there is a successful match.

Arguments:

    cxtProfile  - The profile context handle

    SectionID   - The section ID looking for

    Name        - The optional output buffer for section name

    pNameLen  - The name buffer's length


Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_RECORD_NOT_FOUND
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR

-- */
{
    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Actual;


    if ( cxtProfile == NULL || (Name != NULL && pNameLen == NULL) )
        return(SCESTATUS_INVALID_PARAMETER);

    if ( cxtProfile->JetTblSecID <= 0) {
        //
        // Section table is not opened yet
        //
        rc = SceJetOpenTable(
                        cxtProfile,
                        "SmTblSection",
                        SCEJET_TABLE_SECTION,
                        SCEJET_OPEN_READ_ONLY,
                        NULL
                        );

        if ( rc != SCESTATUS_SUCCESS )
            return(rc);
    }

    //
    // set current index to SecID (the ID)
    //
    JetErr = JetSetCurrentIndex(
                cxtProfile->JetSessionID,
                cxtProfile->JetTblSecID,
                "SecID"
                );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    JetErr = JetMakeKey(
                cxtProfile->JetSessionID,
                cxtProfile->JetTblSecID,
                (VOID *)(&SectionID),
                8,
                JET_bitNewKey
                );

    if ( JetErr == JET_errKeyIsMade ) {
        //
        // Only one key is needed, it may return this code, even on success.
        //
        JetErr = JET_errSuccess;
    }
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        JetErr = JetSeek(
                    cxtProfile->JetSessionID,
                    cxtProfile->JetTblSecID,
                    JET_bitSeekEQ
                    );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {
            //
            // find the section ID, retrieve column Name
            //
            if ( Name != NULL ) {
                JetErr = JetRetrieveColumn(
                            cxtProfile->JetSessionID,
                            cxtProfile->JetTblSecID,
                            cxtProfile->JetSecNameID,
                            (void *)Name,
                            *pNameLen,
                            &Actual,
                            0,
                            NULL
                            );
                *pNameLen = Actual;
                rc = SceJetJetErrorToSceStatus(JetErr);
            }
        }

    }

    return(rc);

}


SCESTATUS
SceJetAddSection(
    IN PSCECONTEXT cxtProfile,
    IN PCWSTR      Name,
    OUT DOUBLE *SectionID
    )
/* ++
Routine Description:

Arguments:

Return Value:

-- */
{
    SCESTATUS  rc;
    DWORD     Len;
    JET_ERR   JetErr;
    PWSTR     LwrName=NULL;


    if ( cxtProfile == NULL ||
         Name == NULL ||
        SectionID == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    rc = SceJetGetSectionIDByName(
                    cxtProfile,
                    Name,
                    SectionID
                    );
    if ( rc == SCESTATUS_RECORD_NOT_FOUND ) {
        //
        // the record is not there. add it in
        // get the next available section ID first.
        //
        Len = wcslen(Name)*sizeof(WCHAR);
        LwrName = (PWSTR)ScepAlloc(0, Len+2);

        if ( LwrName != NULL ) {

            rc = SceJetpGetAvailableSectionID(
                        cxtProfile,
                        SectionID
                        );
            if ( rc == SCESTATUS_SUCCESS ) {
                //
                // add a record to the section table
                //
                JetErr = JetPrepareUpdate(cxtProfile->JetSessionID,
                                          cxtProfile->JetTblSecID,
                                          JET_prepInsert
                                          );
                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( rc == SCESTATUS_SUCCESS ) {
                    //
                    // set SectionID and name
                    //

                    JetErr = JetSetColumn(
                                    cxtProfile->JetSessionID,
                                    cxtProfile->JetTblSecID,
                                    cxtProfile->JetSecID,
                                    (void *)SectionID,
                                    8,
                                    0, //JET_bitSetOverwriteLV,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);

                    if ( rc == SCESTATUS_SUCCESS ) {
                        //
                        // set Name column
                        //
                        wcscpy(LwrName, Name);
                        LwrName = _wcslwr(LwrName);

                        JetErr = JetSetColumn(
                                        cxtProfile->JetSessionID,
                                        cxtProfile->JetTblSecID,
                                        cxtProfile->JetSecNameID,
                                        (void *)LwrName,
                                        Len,
                                        0,
                                        NULL
                                        );
                        rc = SceJetJetErrorToSceStatus(JetErr);

                    }

                    if ( rc != SCESTATUS_SUCCESS ) {
                        //
                        // if setting fails, cancel the prepared record
                        //
                        JetPrepareUpdate( cxtProfile->JetSessionID,
                                          cxtProfile->JetTblSecID,
                                          JET_prepCancel
                                          );
                    } else {

                        //
                        // Setting columns succeed. Update the record
                        //
                        JetErr = JetUpdate(cxtProfile->JetSessionID,
                                           cxtProfile->JetTblSecID,
                                           NULL,
                                           0,
                                           &Len
                                           );
                        rc = SceJetJetErrorToSceStatus(JetErr);
                    }
                }
            }
            ScepFree(LwrName);
        }
    }

    return(rc);
}


SCESTATUS
SceJetDeleteSectionID(
    IN PSCECONTEXT cxtProfile,
    IN DOUBLE SectionID,
    IN PCWSTR  Name
    )
/* ++
Routine Description:

    This routine deletes a record from the SmTblSection table. If SectionID
    is not 0, the record will be deleted by ID if there is a match on ID.
    Otherwise, the record will be deleted by Name if there is a match on Name.

Arguments:

    cxtProfile  - The profile context handle

    SectionID   - The SectionID to delete (if it is not 0)

    Name        - The section name to delete (if it is not NULL ).

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_OTHER_ERROR

    SCESTATUS from SceJetGetSectionIDByName
    SCESTATUS from SceJetGetSectionNameByID

-- */
{
    SCESTATUS    rc;
    JET_ERR     JetErr;


    if ( cxtProfile == NULL )
        return(SCESTATUS_INVALID_PARAMETER);


    if ( SectionID > (DOUBLE)0 ) {
        //
        // delete by SectionID
        //
        rc = SceJetGetSectionNameByID(
                    cxtProfile,
                    SectionID,
                    NULL,
                    NULL
                    );

        if ( rc == SCESTATUS_SUCCESS ) {
            // find it
            JetErr = JetDelete(cxtProfile->JetSessionID, cxtProfile->JetTblSecID);
            rc = SceJetJetErrorToSceStatus(JetErr);

        }

        return(rc);

    }

    if ( Name != NULL && wcslen(Name) > 0 ) {
        //
        // delete by Name
        //
        rc = SceJetGetSectionIDByName(
                    cxtProfile,
                    Name,
                    NULL
                    );
        if ( rc == SCESTATUS_SUCCESS ) {
            // find it
            JetErr = JetDelete(cxtProfile->JetSessionID, cxtProfile->JetTblSecID);
            rc = SceJetJetErrorToSceStatus(JetErr);

        }

        return(rc);
    }

    return(SCESTATUS_INVALID_PARAMETER);

}


//
// Other private APIs
//
JET_ERR
SceJetpSeek(
    IN PSCESECTION hSection,
    IN PWSTR LinePrefix,
    IN DWORD PrefixLength,
    IN SCEJET_SEEK_FLAG SeekBit,
    IN BOOL bOkNoMatch
    )
/* ++
Routine Description:

    This routine seeks to the current key as built with SceJetpMakeKey.
    If there is no records start with the SectionID+LinePrefix, a
    JET_errRecordNotFound is returned. This is similar to exact or partial
    match search.

    There is a 255 bytes limit on Jet engine's index. If SectionID plus
    the line prefix is over this limit, this routine will scroll to the next
    record until find a line starting with SectionID + LinePrefix.

Arguments:

    hSection    - the context handle of the section

    LinePrefix  - The prefix for fields to start with

    PrefixLength- The length of the prefix in BYTES

    grbit       - The option for JetSeek

Return Value:

    JET_ERR returned from JetMakeKey,JetSeek,JetRetrieveColumn, JetMove

-- */
{
    JET_ERR     JetErr;
    INT         Result=0;
    JET_GRBIT   grbit;
    DWORD       Actual;

    //
    // make the key first
    //
    JetErr = SceJetpMakeKey(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    hSection->SectionID,
                    LinePrefix,
                    PrefixLength
                    );
    if ( JetErr != JET_errSuccess ) {
        return(JetErr);
    }
    //
    // Call Jet engine's JetSeek to take to the first line
    // to start with.
    //
    switch ( SeekBit ) {
    case SCEJET_SEEK_EQ:
        grbit = JET_bitSeekEQ;
        break;
    case SCEJET_SEEK_GT:
        if ( LinePrefix != NULL && PrefixLength > 247 )
            grbit = JET_bitSeekGE;
        else
            grbit = JET_bitSeekGT;
        break;
    default:
        grbit = JET_bitSeekGE;
    }
    JetErr = JetSeek(
                hSection->JetSessionID,
                hSection->JetTableID,
                grbit
                );

    if ( JetErr == JET_errSuccess ||
         JetErr == JET_wrnSeekNotEqual ) {

        if ( LinePrefix != NULL && PrefixLength > 247 ) {
            //
            // info is truncated
            // The current record may be before the actual one
            //
            do {
                //
                // check the current record
                //
                JetErr = SceJetpCompareLine(
                                hSection,
                                grbit,
                                LinePrefix,
                                PrefixLength,
                                &Result,
                                &Actual
                                );
                if ( JetErr == JET_errSuccess &&
                    ( Result < 0 || (Result == 0 && SeekBit == SCEJET_SEEK_GT) )) {
                    //
                    // current record's data is less than the prefix, move to next
                    //
                    JetErr = JetMove(hSection->JetSessionID,
                                     hSection->JetTableID,
                                     JET_MoveNext,
                                     0
                                     );
                    if ( JetErr == JET_errNoCurrentRecord )
                        JetErr = JET_errRecordNotFound;
                }
            } while ( JetErr == JET_errSuccess &&
                      ( (Result < 0 && SeekBit != SCEJET_SEEK_EQ) ||
                        (Result == 0 && SeekBit == SCEJET_SEEK_GT) ) );

            if ( SeekBit == SCEJET_SEEK_EQ && JetErr == JET_errSuccess &&
                 Result == 0 && Actual > PrefixLength ) {
                //
                // no exact match
                //
                return(JET_errRecordNotFound);

            } // for SEEK_GE check, see below

        } else {
            //
            // Prefix is not overlimit. Check the current record only.
            //
            if (SeekBit != SCEJET_SEEK_EQ)
                JetErr = SceJetpCompareLine(
                        hSection,
                        grbit,
                        LinePrefix,
                        PrefixLength,
                        &Result,
                        0
                        );
        }

        if ( JetErr == JET_errSuccess && Result > 0 ) {
            if ( SeekBit == SCEJET_SEEK_EQ ) {
                //
                // Prefix is less than the current line, which is OK if for SEEK_GE and SEEK_GT
                //
                return(JET_errRecordNotFound);

            } else if ( SeekBit == SCEJET_SEEK_GE && LinePrefix && PrefixLength && !bOkNoMatch ) {
                //
                return(JET_errRecordNotFound);
            }
        }

    }

    return(JetErr);
}


JET_ERR
SceJetpCompareLine(
    IN PSCESECTION   hSection,
    IN JET_GRBIT    grbit,
    IN PWSTR        LinePrefix OPTIONAL,
    IN DWORD        PrefixLength,
    OUT INT         *Result,
    OUT DWORD       *ActualLength OPTIONAL
    )
/* ++
Routine Description:

    This routine comapre the current line with the SectionID in the section
    handle and name column with LinePrefix if LinePrefix is not NULL. The
    purpose of this routine is to see if the cursor is still on a record
    which has the same sectionID and prefix.

    The comparsion result is output from Result. If JET_errSuccess returns
    and Result < 0, the current record is BEFORE the prefix; If Result = 0,
    the current record has the same key with prefix; If Result > 0, the
    current record is AFTER the prefix. If no more record is available to
    be compared, JET_errRecordNotFound returns. Any other error occurs inside
    the routine is returned.

Arguments:

    hSection    - the section handle

    LinePrefix  - The prefix to match

    PrefixLength - The number of BYTES in LinePrefix

Return Value:

    JET_errSuccess
    JET_errRecordNotFound
    JET_errOutOfMemory
    JET_ERR returned from JetRetrieveColumn

-- */
{
    JET_ERR     JetErr;
    DOUBLE      SectionID;
    DWORD       Actual;
    JET_RETINFO RetInfo;
    PWSTR       Buffer=NULL;

//    *Result = 0;
//    return(JET_errSuccess);
    //
    // Compare the section first
    //
    JetErr = JetRetrieveColumn(
                hSection->JetSessionID,
                hSection->JetTableID,
                hSection->JetColumnSectionID,
                (void *)&SectionID,
                8,
                &Actual,
                0,
                NULL
                );
    if ( JetErr == JET_errNoCurrentRecord )
        return(JET_errRecordNotFound);

    else if ( JetErr != JET_errSuccess )
        return(JetErr);

    if ( hSection->SectionID < SectionID ) {
        *Result = 1;
//        if ( grbit != JET_bitSeekGT )
            return(JET_errRecordNotFound);

    } else if ( hSection->SectionID == SectionID )
        *Result = 0;
    else
        *Result = -1;

    if ( *Result != 0 || grbit == JET_bitSeekGT )
        return(JetErr);

    //
    // check Name column
    //
    if ( LinePrefix != NULL && PrefixLength > 0 ) {
        RetInfo.ibLongValue = 0;
        RetInfo.cbStruct = sizeof(JET_RETINFO);
        RetInfo.itagSequence = 1;

        Buffer = (PWSTR)LocalAlloc(LMEM_ZEROINIT, PrefixLength+2);
        if ( Buffer == NULL )
            return(JET_errOutOfMemory);

        JetErr = JetRetrieveColumn(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    hSection->JetColumnNameID,
                    (void *)Buffer,
                    PrefixLength,
                    &Actual,
                    0,
                    &RetInfo
                    );

        if ( JetErr == JET_errNoCurrentRecord )
            JetErr = JET_errRecordNotFound;

        if ( JetErr != JET_errSuccess &&
             JetErr != JET_wrnBufferTruncated ) {

            if ( JetErr > 0 ) {
                // warnings, do not return equal
                JetErr = JET_errSuccess;
                *Result = 1;
            }
            LocalFree(Buffer);
            return(JetErr);
        }

        JetErr = JET_errSuccess;

        //
        // Compare the first PrefixLength bytes.
        //
        *Result = _wcsnicmp(Buffer,
                           LinePrefix,
                           PrefixLength/sizeof(WCHAR));
//printf("Compare %ws to %ws for Length %d: Result=%d\n", Buffer, LinePrefix, PrefixLength/2, *Result);
        LocalFree(Buffer);

        if ( ActualLength != NULL )
            *ActualLength = Actual;
    }

    return(JetErr);
}


JET_ERR
SceJetpMakeKey(
    IN JET_SESID SessionID,
    IN JET_TABLEID TableID,
    IN DOUBLE SectionID,
    IN PWSTR LinePrefix,
    IN DWORD PrefixLength
    )
/* ++
Routine Description:

    This routine constructs a normalized key value for Seek. It constructs
    the section name in the section context first. Then the LinePrefix is
    added if it is not NULL.

    The scp, sap and smp tables all have one index which is Section+Name.

Arguments:

    SessionID   - the Jet session ID

    TableID     - The Jet table ID to work in

    SectionID   - The ID in column "SectionID"

    LinePrefix  - The prefix for fields to start with

    PrefixLength- The length of the prefix in BYTES

Return Value:

    JET_ERR from JetMakeKey
-- */
{
    JET_ERR         JetErr;
    JET_GRBIT       grbit;


    if ( LinePrefix == NULL ) {
        grbit = JET_bitNewKey; // | JET_bitStrLimit;  having StrLimit set takes you to the next key
    } else {
        grbit = JET_bitNewKey;
    }

    //
    // Add section ID to the key
    //
    JetErr = JetMakeKey(
                SessionID,
                TableID,
                (VOID *)(&SectionID),
                8,
                grbit
                );

    if ( JetErr != JET_errSuccess )
        return(JetErr);

    //
    // add prefix to the key if it is not NULL
    //
    if ( LinePrefix != NULL ) {
        JetErr = JetMakeKey(
                    SessionID,
                    TableID,
                    (VOID *)LinePrefix,
                    PrefixLength,
                    JET_bitSubStrLimit
                    );
    }

    if ( JetErr == JET_errKeyIsMade ) {
        //
        // When 2 keys are provided, it may return this code, even on success.
        //
        JetErr = JET_errSuccess;
    }

    return(JetErr);

}


JET_ERR
SceJetpBuildUpperLimit(
    IN PSCESECTION hSection,
    IN PWSTR      LinePrefix,
    IN DWORD      Len,
    IN BOOL       bReserveCase
    )
/* ++
Function Descripton:

    This routine builts an upper index range based on a section and an
    optional prefix. If prefix is NULL, the upper limit is the next
    available sectionID. If prefix is not NULL, the upper limit is the
    last character 's next character in the key.

    For example, if prefix is a\b\c\d\e\f\g, the upper limit is then
    a\b\c\d\e\f\h. If prefix is over 247 (index limit), e.g.,

    aaa...\b..\c...\d...\e...\f\x\t\y\z

                              ^
                              |
                            the 247th byte.
    then the upper limit is built to aaa...\b..\c...\d...\e...\g

Arguments:

    hSection    - The seciton's handle

    LinePrefix  - The prefix

    Len         - The number of bytes in the prefix

Return Value:

    JET_ERR from SceJetpMakeKey, JetSetIndexRange

-- */
{
    JET_ERR     JetErr;
    DWORD       indx;
    WCHAR       UpperLimit[128];


    if ( Len == 0 ) {
        // no prefix. The upper limit is the next available section ID
        JetErr = SceJetpMakeKey(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    hSection->SectionID+(DOUBLE)1,
                    NULL,
                    0
                    );

    } else {

        memset(UpperLimit, 0, 128*sizeof(WCHAR));

        if ( Len < 247 )
            // prefix is not overlimit.
            // The upper limit is the last character + 1
            indx = Len / sizeof(WCHAR);
        else
            // prefix is overlimit (247)
            // built range on 247 bytes
            indx = 123;

        wcsncpy(UpperLimit, LinePrefix, indx);
        UpperLimit[indx] = L'\0';

        if ( !bReserveCase ) {
            _wcslwr(UpperLimit);
        }
        UpperLimit[indx-1] = (WCHAR) (UpperLimit[indx-1] + 1);

        JetErr = SceJetpMakeKey(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    hSection->SectionID,
                    UpperLimit,
                    Len
                    );
    }

    if ( JetErr != JET_errSuccess )
        return(JetErr);

    //
    // set upper limit
    //
    JetErr = JetSetIndexRange(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    JET_bitRangeUpperLimit //| JET_bitRangeInclusive
                    );

    return(JetErr);
}


SCESTATUS
SceJetJetErrorToSceStatus(
    IN JET_ERR  JetErr
    )
/* ++
Routine Description:

    This routine converts error returned from Jet engine (JET_ERR) to SCESTATUS.

Arguments:

    JetErr  - The error returned from Jet engine

Return Value:

    All available SCESTATUS error codes

-- */
{
    SCESTATUS rc;

    switch ( JetErr ) {
    case JET_errSuccess:
    case JET_wrnSeekNotEqual:
    case JET_wrnNoErrorInfo:
    case JET_wrnColumnNull:
    case JET_wrnColumnSetNull:
    case JET_wrnTableEmpty:
    case JET_errAlreadyInitialized:

        rc = SCESTATUS_SUCCESS;
        break;

    case JET_errDatabaseInvalidName:

        rc = SCESTATUS_INVALID_PARAMETER;
        break;

    case JET_errNoCurrentRecord:
    case JET_errRecordNotFound:

        rc = SCESTATUS_RECORD_NOT_FOUND;
        break;

    case JET_errColumnDoesNotFit:
    case JET_errColumnTooBig:

        rc = SCESTATUS_INVALID_DATA;
        break;

    case JET_errDatabaseDuplicate:
    case JET_errTableDuplicate:
    case JET_errColumnDuplicate:
    case JET_errIndexDuplicate:
    case JET_errKeyDuplicate:

        rc = SCESTATUS_OBJECT_EXIST;
        break;

    case JET_wrnBufferTruncated:

        rc = SCESTATUS_BUFFER_TOO_SMALL;
        break;

    case JET_errFileNotFound:
    case JET_errDatabaseNotFound:

        rc = SCESTATUS_PROFILE_NOT_FOUND;
        break;

    case JET_errObjectNotFound:
    case JET_errIndexNotFound:
    case JET_errColumnNotFound:
    case JET_errDatabaseCorrupted:

        rc = SCESTATUS_BAD_FORMAT;
        break;

    case JET_errTooManyOpenDatabases:
    case JET_errTooManyOpenTables:
    case JET_errDiskFull:
    case JET_errOutOfMemory:
    case JET_errVersionStoreOutOfMemory:

        rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
        break;

    case JET_errPermissionDenied:
    case JET_errFileAccessDenied:
    case JET_errTableInUse:
    case JET_errTableLocked:
    case JET_errWriteConflict:

        rc = SCESTATUS_ACCESS_DENIED;
        break;

    case JET_errFeatureNotAvailable:
    case JET_errQueryNotSupported:
    case JET_errSQLLinkNotSupported:
    case JET_errLinkNotSupported:
    case JET_errIllegalOperation:

        rc = SCESTATUS_SERVICE_NOT_SUPPORT;
        break;

    default:
//printf("JetErr=%d\n", JetErr);
        rc = SCESTATUS_OTHER_ERROR;
        break;
    }
    return(rc);
}



SCESTATUS
SceJetpGetAvailableSectionID(
    IN PSCECONTEXT cxtProfile,
    OUT DOUBLE *SectionID
    )
/* ++
Routine Description:


Arguments:

    cxtProfile  - The profile context handle

    SectionID   - The output section ID


Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_RECORD_NOT_FOUND
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR

-- */
{
    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Actual;


    if ( cxtProfile == NULL || SectionID == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    if ( cxtProfile->JetTblSecID <= 0) {
        //
        // Section table is not opened yet
        //
        rc = SceJetOpenTable(
                        cxtProfile,
                        "SmTblSection",
                        SCEJET_TABLE_SECTION,
                        SCEJET_OPEN_READ_ONLY,
                        NULL
                        );

        if ( rc != SCESTATUS_SUCCESS )
            return(rc);
    }

    *SectionID = (DOUBLE)0;

    //
    // set current index to SecID (the ID)
    //
    JetErr = JetSetCurrentIndex(
                cxtProfile->JetSessionID,
                cxtProfile->JetTblSecID,
                "SecID"
                );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    //
    // Move to the last record
    //
    JetErr = JetMove(
                  cxtProfile->JetSessionID,
                  cxtProfile->JetTblSecID,
                  JET_MoveLast,
                  0
                  );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // find the section ID, retrieve column Name
        //
        JetErr = JetRetrieveColumn(
                    cxtProfile->JetSessionID,
                    cxtProfile->JetTblSecID,
                    cxtProfile->JetSecID,
                    (void *)SectionID,
                    8,
                    &Actual,
                    0,
                    NULL
                    );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {
            //
            // The next available ID is current ID + 1
            //
            *SectionID = *SectionID + (DOUBLE)1;
        }
    } else if ( rc == SCESTATUS_RECORD_NOT_FOUND ) {

        *SectionID = (DOUBLE)1;
        rc = SCESTATUS_SUCCESS;
    }

    return(rc);

}


SCESTATUS
SceJetpAddAllSections(
    IN PSCECONTEXT cxtProfile
    )
/* ++
Routine Description:

    This routine adds all pre-defined sections into the section table.
    This routine is used when creating the section table.

Arguments:

    cxtProfile - The profile context

Return Value:

    SCESTATUS from SceJetAddSection

-- */
{
    SCESTATUS rc;
    DOUBLE SectionID;


    rc = SceJetAddSection(
        cxtProfile,
        szSystemAccess,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szPrivilegeRights,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szGroupMembership,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szAccountProfiles,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szRegistryKeys,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szFileSecurity,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szDSSecurity,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szAuditSystemLog,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szAuditSecurityLog,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szAuditApplicationLog,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szAuditEvent,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szUserList,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szKerberosPolicy,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szServiceGeneral,
        &SectionID
        );
    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    rc = SceJetAddSection(
        cxtProfile,
        szRegistryValues,
        &SectionID
        );

    return(rc);
}


SCESTATUS
SceJetpConfigJetSystem(
    IN JET_INSTANCE *hinstance
    )
{
    SCESTATUS rc=SCESTATUS_SUCCESS;
    DWORD    Win32rc;
    JET_ERR  JetErr;

    DWORD    Len;
    PWSTR SysRoot=NULL;

    PWSTR ProfileLocation=NULL;
    CHAR FileName[512];

    PSECURITY_DESCRIPTOR pSD=NULL;
    SECURITY_INFORMATION SeInfo;
    DWORD SDsize;

    //
    // the default Jet working directory is always in %SystemRoot%\security
    // no matter who is logged on.
    // this way allows one jet working directory
    //
    Len =  0;
    Win32rc = ScepGetNTDirectory( &SysRoot, &Len, SCE_FLAG_WINDOWS_DIR );

    if ( Win32rc == NO_ERROR ) {

        if ( SysRoot != NULL ) {
            Len += 9;  // profile location

            ProfileLocation = (PWSTR)ScepAlloc( 0, (Len+1)*sizeof(WCHAR));

            if ( ProfileLocation == NULL ) {

                Win32rc = ERROR_NOT_ENOUGH_MEMORY;
            } else {

                swprintf(ProfileLocation, L"%s\\Security", SysRoot );
                ProfileLocation[Len] = L'\0';
            }

            ScepFree(SysRoot);

        } else
            Win32rc = ERROR_INVALID_DATA;
    }

    if ( Win32rc == NO_ERROR ) {

#ifdef SCEJET_DBG
    wprintf(L"Default location: %s\n", ProfileLocation);
#endif
        //
        // convert WCHAR into ANSI
        //
        memset(FileName, '\0', 512);
        Win32rc = RtlNtStatusToDosError(
                      RtlUnicodeToMultiByteN(
                            (PCHAR)FileName,
                            512,
                            NULL,
                            ProfileLocation,
                            Len*sizeof(WCHAR)
                            ));

        if ( Win32rc == NO_ERROR ) {
            //
            // a backslash is required by Jet
            //
            strcat(FileName, "\\");

            //
            // set everyone change, admin full control to the directory
            // the directory is created in the function.
            //
            Win32rc = ConvertTextSecurityDescriptor (
                            L"D:P(A;CIOI;GRGW;;;WD)(A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY)",
                            &pSD,
                            &SDsize,
                            &SeInfo
                            );
            if ( Win32rc == NO_ERROR ) {

                ScepChangeAclRevision(pSD, ACL_REVISION);

                rc = ScepCreateDirectory(
                            ProfileLocation,
                            TRUE,      // a dir name
                            pSD        // take parent's security setting
                            );
#ifdef SCEJET_DBG
    if ( rc != SCESTATUS_SUCCESS )
        wprintf(L"Cannot create directory %s\n", ProfileLocation );
#endif

                if ( rc == SCESTATUS_SUCCESS ) {

                    __try {

                        JetErr = JetSetSystemParameter( hinstance, 0, JET_paramSystemPath, 0, (const char *)FileName );

                        rc = SceJetJetErrorToSceStatus(JetErr);

                    } __except (EXCEPTION_EXECUTE_HANDLER) {
                        //
                        // esent is not loaded
                        //
                        rc = SCESTATUS_MOD_NOT_FOUND;
                    }
                }

                if ( rc == SCESTATUS_SUCCESS ) {

                    JetErr = JetSetSystemParameter( hinstance, 0, JET_paramTempPath, 0, (const char *)FileName );

                    if ( JetErr == JET_errSuccess ) {
                        JetErr = JetSetSystemParameter( hinstance, 0, JET_paramLogFilePath, 0, (const char *)FileName );

                        if ( JetErr == JET_errSuccess ) {
                            JetErr = JetSetSystemParameter( hinstance, 0, JET_paramDatabasePageSize, 4096, NULL );
                        }
                    }

                    rc = SceJetJetErrorToSceStatus(JetErr);

                    if ( rc == SCESTATUS_SUCCESS ) {
                        //
                        // set log size to 1M
                        //
                        JetSetSystemParameter( hinstance, 0, JET_paramLogFileSize, 1024, NULL );
                        //
                        // defer the event log to when event log service is available
                        // (for example, in NT setup, there is no event log)
                        //
                        JetSetSystemParameter( hinstance, 0, JET_paramEventLogCache, 128, NULL );

                        JetSetSystemParameter( hinstance, 0, JET_paramMaxVerPages, 128, NULL );

                        //
                        // set minimize = maximum cache size to disable DBA in jet
                        // recommended setting for minimum is 4 * number of sessions
                        // maximum is up to the app (for performance)
                        //

                        JetSetSystemParameter( hinstance, 0, JET_paramMaxSessions, 64, NULL );

                        //
                        // performance is about 10% faster when using cache size 512 than 256
                        //

                        JetSetSystemParameter( hinstance, 0, JET_paramStartFlushThreshold, 50, NULL ); // sugguested by Exchange
                        JetSetSystemParameter( hinstance, 0, JET_paramStopFlushThreshold, 100, NULL ); // suggested by Exchange

                        //
                        // can't set to 512 because that's Jet's default value
                        // jet won't turn off DBA if value is set to 512.
                        //
                        JetSetSystemParameter( hinstance, 0, JET_paramCacheSizeMax, 496, NULL );  //256

                        JetSetSystemParameter( hinstance, 0, JET_paramCacheSizeMin, 496, NULL );  //256

                        //
                        // other system parameters, such as memory size in beta2
                        //
                        JetErr = JetSetSystemParameter( hinstance, 0, JET_paramCircularLog, 1, NULL );

                        JetErr = JetSetSystemParameter( hinstance, 0, JET_paramNoInformationEvent, 1, NULL );

                    }
                }

                ScepFree(pSD);

            }
        }

        ScepFree(ProfileLocation);
    }

    if ( rc == SCESTATUS_SUCCESS ) {

        rc = ScepDosErrorToSceStatus(Win32rc);
    }

    return(rc);
}



SCESTATUS
SceJetGetTimeStamp(
    IN PSCECONTEXT   cxtProfile,
    OUT PLARGE_INTEGER ConfigTimeStamp,
    OUT PLARGE_INTEGER AnalyzeTimeStamp
    )
/* ++
Routine Description:

    This routine queries the time stamp of last analysis.

    The time stamp is saved in the "SmTblVersion" table.

Arguments:

    cxtProfile  - The profile context

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR
    SCESTATUS from SceJetOpenTable

-- */
{
    SCESTATUS        rc=SCESTATUS_SUCCESS;
    DWORD           RetLen = 0;

    if (cxtProfile == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    //
    // Open version table
    //
    if ( ConfigTimeStamp != NULL ) {

        rc = SceJetpGetValueFromVersion(
                    cxtProfile,
                    "SmTblVersion",
                    "ConfigTimeStamp",
                    (CHAR*)ConfigTimeStamp, //TimeStamp,
                    8,  // 16, // number of bytes
                    &RetLen
                    );
        if ( rc == SCESTATUS_SUCCESS ||
             rc == SCESTATUS_BUFFER_TOO_SMALL )
            rc = SCESTATUS_SUCCESS;

        if ( RetLen < 8 ) {
            (*ConfigTimeStamp).LowPart = 0;
            (*ConfigTimeStamp).HighPart = 0;
        }
    }

    if ( AnalyzeTimeStamp != NULL ) {

        rc |= SceJetpGetValueFromVersion(
                    cxtProfile,
                    "SmTblVersion",
                    "AnalyzeTimeStamp",
                    (CHAR*)AnalyzeTimeStamp, //TimeStamp,
                    8,  // 16, // number of bytes
                    &RetLen
                    );

        if ( rc == SCESTATUS_SUCCESS ||
             rc == SCESTATUS_BUFFER_TOO_SMALL )
            rc = SCESTATUS_SUCCESS;

        if ( RetLen < 8 ) {
            (*AnalyzeTimeStamp).LowPart = 0;
            (*AnalyzeTimeStamp).HighPart = 0;
        }
    }

    return(rc);
}



SCESTATUS
SceJetSetTimeStamp(
    IN PSCECONTEXT   cxtProfile,
    IN BOOL        Flag,
    IN LARGE_INTEGER NewTimeStamp
    )
/* ++
Routine Description:

    This routine sets the time stamp (LARGE_INTEGER) of a analysis.

    The time stamp is saved in the "SmTblVersion" table.

Arguments:

    cxtProfile  - The profile context

    Flag        - indicates analyze or configure
                    Flag = TRUE - AnalyzeTimeStamp
                    Flag = FALSE - ConfigTimeStamp

    NewTimeStamp - the new time stamp of a analysis

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR
    SCESTATUS from SceJetOpenTable

-- */
{
    SCESTATUS        rc;

#ifdef SCE_JETDBG
    CHAR            CharTimeStamp[17];

    sprintf(CharTimeStamp, "%08x%08x", NewTimeStamp.HighPart, NewTimeStamp.LowPart);
    CharTimeStamp[16] = '\0';

    printf("New time stamp is %s\n", CharTimeStamp);
#endif

    if ( cxtProfile == NULL ) {

        return(SCESTATUS_INVALID_PARAMETER);
    }
    //
    // set
    //
    if ( Flag ) {

        rc = SceJetSetValueInVersion(
                    cxtProfile,
                    "SmTblVersion",
                    "AnalyzeTimeStamp",
                    (PWSTR)(&NewTimeStamp), //(PWSTR)CharTimeStamp,
                    8, // 16, // number of bytes
                    JET_prepReplace
                    );
    } else {

        rc = SceJetSetValueInVersion(
                    cxtProfile,
                    "SmTblVersion",
                    "ConfigTimeStamp",
                    (PWSTR)(&NewTimeStamp), //(PWSTR)CharTimeStamp,
                    8, // 16, // number of bytes
                    JET_prepReplace
                    );
    }
    return(rc);
}


SCESTATUS
SceJetGetDescription(
    IN PSCECONTEXT   cxtProfile,
    OUT PWSTR *Description
    )
/* ++
Routine Description:

    This routine queries the profile description from the "SmTblVersion" table.

Arguments:

    cxtProfile  - The profile context

    Description - The description buffer

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR
    SCESTATUS from SceJetOpenTable

-- */
{
    SCESTATUS        rc;
    DWORD           RetLen = 0;

    if ( cxtProfile == NULL || Description == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // Open version table
    //
    rc = SceJetpGetValueFromVersion(
                cxtProfile,
                "SmTblVersion",
                "ProfileDescription",
                NULL,
                0, // number of bytes
                &RetLen
                );

    if ( rc == SCESTATUS_BUFFER_TOO_SMALL ) {

        *Description = (PWSTR)ScepAlloc( LPTR, RetLen+2 );

        if ( *Description == NULL )
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);

        rc = SceJetpGetValueFromVersion(
                    cxtProfile,
                    "SmTblVersion",
                    "ProfileDescription",
                    (LPSTR)(*Description),
                    RetLen, // number of bytes
                    &RetLen
                    );
        if ( rc != SCESTATUS_SUCCESS ) {
            ScepFree( *Description );
            *Description = NULL;
        }
    }

    return(rc);
}


SCESTATUS
SceJetpGetValueFromVersion(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR TableName,
    IN LPSTR ColumnName,
    OUT LPSTR Value OPTIONAL,
    IN DWORD  ValueLen, // number of bytes
    OUT PDWORD pRetLen
    )
{
    SCESTATUS   rc;
    JET_TABLEID     TableID;
    JET_ERR         JetErr;
    JET_COLUMNDEF   ColumnDef;

    //
    // Open version table
    //
    rc = SceJetOpenTable(
                    cxtProfile,
                    TableName,
                    SCEJET_TABLE_VERSION,
                    SCEJET_OPEN_READ_ONLY,
                    &TableID
                    );
    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // go to the first record
        //
        JetErr = JetMove(cxtProfile->JetSessionID,
                         TableID,
                         JET_MoveFirst,
                         0
                         );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS) {
            //
            // get column ID for "Version"
            //
            JetErr = JetGetTableColumnInfo(
                            cxtProfile->JetSessionID,
                            TableID,
                            ColumnName,
                            (VOID *)&ColumnDef,
                            sizeof(JET_COLUMNDEF),
                            0
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {
                //
                // retrieve the column
                //
                JetErr = JetRetrieveColumn(
                                cxtProfile->JetSessionID,
                                TableID,
                                ColumnDef.columnid,
                                (void *)Value,
                                ValueLen,
                                pRetLen,
                                0,
                                NULL
                                );
                rc = SceJetJetErrorToSceStatus(JetErr);
            }
        }
        JetCloseTable(cxtProfile->JetSessionID, TableID);
    }

    return(rc);

}


SCESTATUS
SceJetSetValueInVersion(
    IN PSCECONTEXT cxtProfile,
    IN LPSTR TableName,
    IN LPSTR ColumnName,
    IN PWSTR Value,
    IN DWORD ValueLen, // number of bytes
    IN DWORD Prep
    )
{
    SCESTATUS   rc;
    DWORD      Len;
    JET_TABLEID     TableID;
    JET_ERR         JetErr;
    JET_COLUMNDEF   ColumnDef;


    if ( cxtProfile == NULL || TableName == NULL || ColumnName == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // Open version table
    //
    rc = SceJetOpenTable(
                    cxtProfile,
                    TableName,
                    SCEJET_TABLE_VERSION,
                    SCEJET_OPEN_READ_WRITE, // read and write
                    &TableID
                    );
    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // go to the first record
        //
        JetErr = JetMove(cxtProfile->JetSessionID,
                         TableID,
                         JET_MoveFirst,
                         0
                         );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS) {
            //
            // get column ID for "Version"
            //
            JetErr = JetGetTableColumnInfo(
                            cxtProfile->JetSessionID,
                            TableID,
                            ColumnName,
                            (VOID *)&ColumnDef,
                            sizeof(JET_COLUMNDEF),
                            0
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {

                JetErr = JetPrepareUpdate(cxtProfile->JetSessionID,
                                          TableID,
                                          Prep
                                          );
                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( rc == SCESTATUS_SUCCESS ) {
                    //
                    // set value
                    //

                    JetErr = JetSetColumn(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    ColumnDef.columnid,
                                    (void *)Value,
                                    ValueLen,
                                    0, //JET_bitSetOverwriteLV,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);

                    if ( rc != SCESTATUS_SUCCESS ) {
                        //
                        // if setting fails, cancel the prepared record
                        //
                        JetPrepareUpdate( cxtProfile->JetSessionID,
                                          TableID,
                                          JET_prepCancel
                                          );
                    } else {

                        //
                        // Setting columns succeed. Update the record
                        //
                        JetErr = JetUpdate( cxtProfile->JetSessionID,
                                           TableID,
                                           NULL,
                                           0,
                                           &Len
                                           );
                        rc = SceJetJetErrorToSceStatus(JetErr);
                    }
                }
            }
        }
        JetCloseTable(cxtProfile->JetSessionID, TableID);
    }

    return(rc);
}


SCESTATUS
SceJetSeek(
    IN PSCESECTION hSection,
    IN PWSTR LinePrefix,
    IN DWORD PrefixLength,
    IN SCEJET_SEEK_FLAG SeekBit
    )
{
    PWSTR LwrPrefix=NULL;
    SCESTATUS rc;
    SCEJET_SEEK_FLAG NewSeekBit;

    if ( hSection == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( LinePrefix != NULL && SeekBit > SCEJET_SEEK_GE ) {
        //
        // do lowercase search
        //
        LwrPrefix = (PWSTR)ScepAlloc(0, PrefixLength+sizeof(WCHAR));

        if ( LwrPrefix == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);

        } else {
            wcscpy(LwrPrefix, LinePrefix);
            LwrPrefix = _wcslwr(LwrPrefix);

            switch ( SeekBit ) {
            case SCEJET_SEEK_GT_NO_CASE:
                NewSeekBit = SCEJET_SEEK_GT;
                break;
            case SCEJET_SEEK_EQ_NO_CASE:
                NewSeekBit = SCEJET_SEEK_EQ;
                break;
            default:
                NewSeekBit = SCEJET_SEEK_GE;
                break;
            }

            rc = SceJetJetErrorToSceStatus(
                        SceJetpSeek(
                                    hSection,
                                    LwrPrefix,
                                    PrefixLength,
                                    NewSeekBit,
                                    (SeekBit == SCEJET_SEEK_GE_DONT_CARE)
                                    ));
            ScepFree(LwrPrefix);
        }
    } else {
        //
        // do case sensitive search, or NULL search
        //
        rc = SceJetJetErrorToSceStatus(
                    SceJetpSeek(
                                hSection,
                                LinePrefix,
                                PrefixLength,
                                SeekBit,
                                FALSE
                                ));
    }

    return(rc);

}

SCESTATUS
SceJetMoveNext(
    IN PSCESECTION hSection
    )
{
    JET_ERR  JetErr;
    INT      Result;

    if ( hSection == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    //
    // skip deleted records
    //
    do {
        JetErr = JetMove(hSection->JetSessionID,
                         hSection->JetTableID,
                         JET_MoveNext,
                         0
                         );
        if ( JetErr == JET_errSuccess ) {
            // compare section ID
            JetErr = SceJetpCompareLine(
                hSection,
                JET_bitSeekGE,
                NULL,
                0,
                &Result,
                NULL
                );
            if ( JetErr == JET_errSuccess && Result != 0 )
                JetErr = JET_errRecordNotFound;

        }

    } while ( JetErr == JET_errRecordDeleted );


    return(SceJetJetErrorToSceStatus(JetErr));

}

/*

SCESTATUS
SceJetRenameLine(
    IN PSCESECTION hSection,
    IN PWSTR      Name,
    IN PWSTR      NewName,
    IN BOOL       bReserveCase
    )
{
    PWSTR       LwrName=NULL;
    DWORD       Len;
    JET_ERR     JetErr;
    JET_SETINFO SetInfo;


    if ( !hSection || !Name || !NewName ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    Len = wcslen(NewName)*sizeof(WCHAR);

    if ( Len <= 0 ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( bReserveCase ) {
        LwrName = NewName;

    } else {
        //
        // lower cased
        //
        LwrName = (PWSTR)ScepAlloc(0, Len+2);
        if ( LwrName == NULL ) {
            return(SCESTATUS_NOT_ENOUGH_RESOURCE);
        }
        wcscpy(LwrName, NewName);
        LwrName = _wcslwr(LwrName);

    }

    SetInfo.cbStruct = sizeof(JET_SETINFO);
    SetInfo.itagSequence = 1;
    SetInfo.ibLongValue = 0;

    //
    // check to see if the same key name already exists
    //
    JetErr = SceJetSeek(
                    hSection,
                    Name,
                    wcslen(Name)*sizeof(WCHAR),
                    SCEJET_SEEK_EQ_NO_CASE
                    );

    if ( JetErr == JET_errSuccess ) {
        //
        // find a match. overwrite the value
        //
        JetErr = JetBeginTransaction(hSection->JetSessionID);

        if ( JetErr == JET_errSuccess ) {
            JetErr = JetPrepareUpdate(hSection->JetSessionID,
                                      hSection->JetTableID,
                                      JET_prepReplace
                                      );
            if ( JetErr == JET_errSuccess ) {
                //
                // set the new key in "Name" column
                //
                JetErr = JetSetColumn(
                                hSection->JetSessionID,
                                hSection->JetTableID,
                                hSection->JetColumnNameID,
                                (void *)LwrName,
                                Len,
                                JET_bitSetOverwriteLV,
                                &SetInfo
                                );
            }

            if ( JET_errSuccess == JetErr ) {
                //
                // commit the transaction
                //
                JetCommitTransaction(hSection->JetSessionID, JET_bitCommitLazyFlush);
            } else {
                //
                // rollback the transaction
                //
                JetRollback(hSection->JetSessionID,0);
            }
            JetPrepareUpdate(hSection->JetSessionID,
                              hSection->JetTableID,
                              JET_prepCancel
                              );
        }
    }

    if ( LwrName != NewName ) {
        ScepFree(LwrName);
    }

    return( SceJetJetErrorToSceStatus(JetErr) );
}
*/



SCESTATUS
SceJetRenameLine(
    IN PSCESECTION hSection,
    IN PWSTR      Name,
    IN PWSTR      NewName,
    IN BOOL       bReserveCase
    )
{
    PWSTR       Value=NULL;
    DWORD       ValueLen;
    SCESTATUS   rc;
    JET_ERR     JetErr;


    if ( !hSection || !Name || !NewName ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    rc = SceJetGetValue(
                hSection,
                SCEJET_EXACT_MATCH_NO_CASE,
                Name,
                NULL,
                0,
                NULL,
                NULL,
                0,
                &ValueLen
                );

    if ( SCESTATUS_SUCCESS == rc ) {
        //
        // continue only when this record is found.
        //
        if ( ValueLen ) {
            Value = (PWSTR)ScepAlloc(0, ValueLen+2);

            if ( Value ) {
                rc = SceJetGetValue(
                            hSection,
                            SCEJET_CURRENT,
                            NULL,
                            NULL,
                            0,
                            NULL,
                            Value,
                            ValueLen,
                            &ValueLen
                            );
            } else
                rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
        }

        if ( SCESTATUS_SUCCESS == rc ) {

            JetErr = JetBeginTransaction(hSection->JetSessionID);

            if ( JetErr == JET_errSuccess ) {
                //
                // now delete this line
                //
                rc = SceJetDelete(hSection, NULL, FALSE, SCEJET_DELETE_LINE);

                if ( SCESTATUS_SUCCESS == rc ) {
                    //
                    // add the new line in.
                    //
                    rc = SceJetSetLine(
                            hSection,
                            NewName,
                            bReserveCase,
                            Value,
                            ValueLen,
                            0
                            );
                }

                if ( SCESTATUS_SUCCESS == rc ) {
                    //
                    // commit the transaction
                    //
                    JetCommitTransaction(hSection->JetSessionID, JET_bitCommitLazyFlush);
                } else {
                    //
                    // rollback the transaction
                    //
                    JetRollback(hSection->JetSessionID,0);
                }
            } else
                rc = SceJetJetErrorToSceStatus(JetErr);

        }
    }

    return( rc );
}

//////////////////////////////////////////////////////////////
//
//  Helpers
//
//////////////////////////////////////////////////////////////

VOID
SceJetInitializeData()
//
// only be called during server initialization code
//
{
   JetInited = FALSE;
   JetInstance = 0;
}

SCESTATUS
SceJetInitialize(
    OUT JET_ERR *pJetErr OPTIONAL
    )
/*
Routine Description:

    Initialize jet engine for sce server

Arguments:

    None

Return Value:

    SCESTATUS
*/
{

    SCESTATUS rc=SCESTATUS_SUCCESS;
    JET_ERR JetErr=0;

    //
    // cancel any pending timer queue
    //
    ScepServerCancelTimer();

    EnterCriticalSection(&JetSync);

    if ( !JetInited ) {

        //
        // set system configuration for Jet engine
        //
        rc = SceJetpConfigJetSystem( &JetInstance);
        if ( SCESTATUS_SUCCESS == rc ) {

            //
            // initialize jet engine
            //
            __try {

                JetErr = JetInit(&JetInstance);

                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( JetErr == JET_errSuccess ) {

                    JetInited = TRUE;

                    //
                    // if failed to initialize Jet writer (for backup/restore)
                    // don't fail the engine
                    //
                } else {

                    //
                    // this will happen only if jet cannot recover the
                    // database by itself (JetInit() claims to attempt recovery only)
                    // repair might help - so only spew out a message advising the user
                    //
                    // map error so setup/policy propagation clients
                    // can log events
                    //

//                    if ( SCE_JET_CORRUPTION_ERROR(JetErr) ) {

                        rc = SCESTATUS_JET_DATABASE_ERROR;

                        ScepLogOutput3(0, ERROR_DATABASE_FAILURE, SCEDLL_ERROR_RECOVER_DB );
//                    }
                    JetInstance = 0;
                }

            } __except (EXCEPTION_EXECUTE_HANDLER) {
                //
                // for some reason, esent is not loaded
                //
                rc = SCESTATUS_MOD_NOT_FOUND;
                JetInstance = 0;
            }

        } else {
            JetInstance = 0;
        }
    }

    LeaveCriticalSection(&JetSync);

    if ( pJetErr ) *pJetErr = JetErr;

    return(rc);
}


SCESTATUS
SceJetTerminate(BOOL bCleanVs)
/*
Routine Description:

    Terminate jet engine

Arguments:

    bCleanVs  - if to clean up the version store completely

Return Value:

    SCESTATUS
*/
{

    EnterCriticalSection(&JetSync);

    //
    // destroy the jet backup/restore writer
    //
    if ( JetInited || JetInstance ) {

        if ( bCleanVs ) {
            //
            // clean up version store
            //
            JetTerm2(JetInstance, JET_bitTermComplete);
        } else {
            //
            // do not clean up version store
            //
            JetTerm(JetInstance);
        }
        JetInstance = 0;
        JetInited = FALSE;
    }

    LeaveCriticalSection(&JetSync);

    return(SCESTATUS_SUCCESS);
}

SCESTATUS
SceJetTerminateNoCritical(BOOL bCleanVs)
/*
Routine Description:

    Terminate jet engine, NOT critical sectioned!!!

Arguments:

    bCleanVs  - if to clean up the version store completely

Return Value:

    SCESTATUS
*/
{
    //
    // the critical section is entered outside of this function
    //
    // destroy the jet backup/restore writer
    //
    if ( JetInited || JetInstance ) {

        if ( bCleanVs ) {
            //
            // clean up version store
            //
            JetTerm2(JetInstance, JET_bitTermComplete);
        } else {
            //
            // do not clean up version store
            //
            JetTerm(JetInstance);
        }
        JetInstance = 0;
        JetInited = FALSE;
    }

    return(SCESTATUS_SUCCESS);
}


SCESTATUS
SceJetStartTransaction(
    IN PSCECONTEXT cxtProfile
    )
/*
Routine Description:

    Start a transaction on the session

Arguments:

    cxtProfile  - the database context

Return Value:

    SCESTATUS
*/
{
    JET_ERR  JetErr;

    if ( cxtProfile == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    JetErr = JetBeginTransaction( cxtProfile->JetSessionID);

    return( SceJetJetErrorToSceStatus(JetErr));

}

SCESTATUS
SceJetCommitTransaction(
    IN PSCECONTEXT cxtProfile,
    IN JET_GRBIT grbit
    )
/*
Routine Description:

    Commit a transaction on the session

Arguments:

    cxtProfile  - the database context

    grbit       - flag for the commission

Return Value:

    SCESTATUS
*/
{
    JET_ERR     JetErr;

    if ( cxtProfile == NULL )
        return(SCESTATUS_INVALID_PARAMETER);

    JetErr = JetCommitTransaction(cxtProfile->JetSessionID, grbit );

    return( SceJetJetErrorToSceStatus(JetErr) );

}

SCESTATUS
SceJetRollback(
    IN PSCECONTEXT cxtProfile,
    IN JET_GRBIT grbit
    )
/*
Routine Description:

    Rollback a transaction on the session

Arguments:

    cxtProfile  - the database context

    grbit       - the flag for transaction rollback

Return Value:

    SCESTATUS
*/
{
    JET_ERR     JetErr;

    if ( cxtProfile == NULL )
        return(SCESTATUS_SUCCESS);

    __try {
        JetErr = JetRollback(cxtProfile->JetSessionID, grbit);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        JetErr = JET_errOutOfMemory;
    }
    return( SceJetJetErrorToSceStatus(JetErr) );

}


BOOL
SceJetDeleteJetFiles(
    IN PWSTR DbFileName
    )
{
   TCHAR TempFileName[MAX_PATH];
   PWSTR SysRoot=NULL;
   DWORD SysLen;
   DWORD rc;
   intptr_t            hFile;
   struct _wfinddata_t    fInfo;


   BOOL bRet = FALSE;

   EnterCriticalSection(&JetSync);

   if ( JetInited == FALSE ) {

       SysLen =  0;
       rc = ScepGetNTDirectory( &SysRoot, &SysLen, SCE_FLAG_WINDOWS_DIR );

       if ( rc == NO_ERROR && SysRoot != NULL ) {

           swprintf(TempFileName, L"%s\\Security\\res1.log\0", SysRoot);
           TempFileName[MAX_PATH-1] = L'\0';

           DeleteFile(TempFileName);

           swprintf(TempFileName, L"%s\\Security\\res2.log\0", SysRoot);
           TempFileName[MAX_PATH-1] = L'\0';

           DeleteFile(TempFileName);

           //
           // delete edb files
           //
           swprintf(TempFileName, L"%s\\Security\\edb*.*\0", SysRoot);
           TempFileName[MAX_PATH-1] = L'\0';

           hFile = _wfindfirst(TempFileName, &fInfo);

           if ( hFile != -1 ) {

               do {

                   swprintf(TempFileName, L"%s\\Security\\%s\0", SysRoot, fInfo.name);
                   TempFileName[MAX_PATH-1] = L'\0';

                   DeleteFile(TempFileName);

               } while ( _wfindnext(hFile, &fInfo) == 0 );

               _findclose(hFile);
           }

           //
           // delete temp files
           //
           swprintf(TempFileName, L"%s\\Security\\tmp*.edb\0", SysRoot);
           TempFileName[MAX_PATH-1] = L'\0';

           hFile = _wfindfirst(TempFileName, &fInfo);

           if ( hFile != -1 ) {

               do {

                   swprintf(TempFileName, L"%s\\Security\\%s\0", SysRoot, fInfo.name);
                   TempFileName[MAX_PATH-1] = L'\0';

                   DeleteFile(TempFileName);

               } while ( _wfindnext(hFile, &fInfo) == 0 );

               _findclose(hFile);
           }

           ScepFree(SysRoot);

           //
           // delete the database file if it's passed in.
           //
           if ( DbFileName ) {
               DeleteFile(DbFileName);
           }

           bRet = TRUE;

       }
   }

   LeaveCriticalSection(&JetSync);

   return(bRet);

}


SCESTATUS
SceJetSetCurrentLine(
    IN PSCESECTION hSection,
    IN PWSTR      Value,
    IN DWORD      ValueLen
    )
/* ++
Fucntion Description:

    This routine writes the Value to the current line in section (hSection).
    Make sure the cursor is on the right line before calling this API

Arguments:

    hSection    - The context handle of the section

    Value       - The info set to Column "Value"

    ValueLen    - The size of the value field.

Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_OTHER_ERROR
    SCESTATUS_ACCESS_DENIED
    SCESTATUS_DATA_OVERFLOW

-- */
{
    JET_ERR     JetErr;
    DWORD       Len;
    SCESTATUS    rc;
    DWORD       prep;
    JET_SETINFO SetInfo;

    if ( hSection == NULL ||
         Value == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    SetInfo.cbStruct = sizeof(JET_SETINFO);
    SetInfo.itagSequence = 1;
    SetInfo.ibLongValue = 0;

    prep = JET_prepReplace;

    JetErr = JetBeginTransaction(hSection->JetSessionID);

    if ( JetErr == JET_errSuccess ) {
        JetErr = JetPrepareUpdate(hSection->JetSessionID,
                                  hSection->JetTableID,
                                  prep
                                  );
        if ( JetErr != JET_errSuccess ) {
            //
            // rollback the transaction
            //
            JetRollback(hSection->JetSessionID,0);
        }
    }

    if ( JetErr != JET_errSuccess)
        return(SceJetJetErrorToSceStatus(JetErr));


    //
    // set value column
    //

    JetErr = JetSetColumn(
                    hSection->JetSessionID,
                    hSection->JetTableID,
                    hSection->JetColumnValueID,
                    (void *)Value,
                    ValueLen,
                    0, //JET_bitSetOverwriteLV,
                    &SetInfo
                    );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( JetErr == JET_errSuccess ) {
        //
        // Setting columns succeed. Update the record
        //
        JetErr = JetUpdate(hSection->JetSessionID,
                           hSection->JetTableID,
                           NULL,
                           0,
                           &Len
                           );
    } else {
        goto CleanUp;
    }

    if ( rc == SCESTATUS_SUCCESS )
        JetCommitTransaction(hSection->JetSessionID, JET_bitCommitLazyFlush);

CleanUp:

    if ( rc != SCESTATUS_SUCCESS ) {
        //
        // if setting fails, cancel the prepared record
        //
        JetPrepareUpdate(hSection->JetSessionID,
                          hSection->JetTableID,
                          JET_prepCancel
                          );
        //
        // Rollback the transaction
        //
        JetRollback(hSection->JetSessionID,0);

    }

    return(rc);

}


BOOL
ScepIsValidContext(
    PSCECONTEXT context
    )
{
    if ( context == NULL ) {
        return FALSE;
    }

    __try {

        if ( (context->Type & 0xFFFFFF02L) == 0xFFFFFF02L ) {

            return TRUE;

        } else {

            return FALSE;
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {

        return FALSE;
    }

}


LONG
SceJetGetGpoIDByName(
    IN PSCECONTEXT cxtProfile,
    IN PWSTR       szGpoName,
    IN BOOL        bAdd
    )
/*
Routine Description:

    Search for a GPO by name in the GPO table. If bAdd is TRUE and the GPO name
    is not found, it will be added to the GPO table

Arguments:

    cxtProfile    - the database handle

    szGpoName   - the GPO name

    bAdd        - TRUE to add the GPO name to the GPO table if it's not found

Return Value:

    The GPO ID. If -1 is returned, GetLastError to get the SCE error code.

*/
{


    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Actual;
    PWSTR     LwrName=NULL;
    DWORD     Len;

    if ( cxtProfile == NULL || szGpoName == NULL ||
         szGpoName[0] == L'\0' ) {

        SetLastError(SCESTATUS_INVALID_PARAMETER);
        return (-1);
    }

    JET_TABLEID  TableID;

    rc = SceJetOpenTable(
                    cxtProfile,
                    "SmTblGpo",
                    SCEJET_TABLE_GPO,
                    bAdd ? SCEJET_OPEN_READ_WRITE : SCEJET_OPEN_READ_ONLY,
                    &TableID
                    );

    if ( rc != SCESTATUS_SUCCESS ) {
        SetLastError(rc);
        return(-1);
    }

    JET_COLUMNDEF ColumnDef;
    LONG GpoID = 0;

    JetErr = JetGetTableColumnInfo(
                    cxtProfile->JetSessionID,
                    TableID,
                    "GpoID",
                    (VOID *)&ColumnDef,
                    sizeof(JET_COLUMNDEF),
                    JET_ColInfo
                    );

    if ( JET_errSuccess == JetErr ) {

        //
        // set current index to SectionKey (the name)
        //
        JetErr = JetSetCurrentIndex(
                    cxtProfile->JetSessionID,
                    TableID,
                    "GpoName"
                    );

    }

    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // search for the name
        //
        Len = wcslen(szGpoName);
        LwrName = (PWSTR)ScepAlloc(0, (Len+1)*sizeof(WCHAR));

        if ( LwrName != NULL ) {

            wcscpy(LwrName, szGpoName);
            LwrName = _wcslwr(LwrName);

            JetErr = JetMakeKey(
                        cxtProfile->JetSessionID,
                        TableID,
                        (VOID *)LwrName,
                        Len*sizeof(WCHAR),
                        JET_bitNewKey
                        );

            if ( JetErr == JET_errKeyIsMade ) {
                //
                // Only one key is needed, it may return this code, even on success.
                //
                JetErr = JET_errSuccess;
            }
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {

                JetErr = JetSeek(
                            cxtProfile->JetSessionID,
                            TableID,
                            JET_bitSeekEQ
                            );
                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( rc == SCESTATUS_SUCCESS ) {
                    //
                    // find the Gpo name, retrieve gpo id
                    //
                    JetErr = JetRetrieveColumn(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    ColumnDef.columnid,
                                    (void *)&GpoID,
                                    4,
                                    &Actual,
                                    0,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);

                } else if ( SCESTATUS_RECORD_NOT_FOUND == rc ) {

                    GpoID = 0;
                    rc = SCESTATUS_SUCCESS;

                    if ( bAdd ) {

                        //
                        // if not found and add is requested
                        //
                        rc = SceJetpAddGpo(cxtProfile,
                                          TableID,
                                          ColumnDef.columnid,
                                          LwrName,
                                          &GpoID
                                         );
                    }

                }

            }

            ScepFree(LwrName);

        } else
            rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
    }

    JetCloseTable( cxtProfile->JetSessionID, TableID );

    if ( rc != SCESTATUS_SUCCESS ) {
        SetLastError(rc);
        GpoID = -1;
    }

    return(GpoID);

}


SCESTATUS
SceJetGetGpoNameByID(
    IN PSCECONTEXT cxtProfile,
    IN LONG GpoID,
    OUT PWSTR Name OPTIONAL,
    IN OUT LPDWORD pNameLen,
    OUT PWSTR DisplayName OPTIONAL,
    IN OUT LPDWORD pDispNameLen
    )
/* ++
Routine Description:

    This routine retrieve the GPO name for the ID in the GPO table.
    If Name is NULL, this routine really does a seek by ID. The cursor will
    be on the record if there is a successful match.

Arguments:

    cxtProfile  - The profile context handle

    GpoID       - The GPO ID looking for

    Name        - The optional output buffer for section name

    pNameLen    - The name buffer's length


Return Value:

    SCESTATUS_SUCCESS
    SCESTATUS_INVALID_PARAMETER
    SCESTATUS_RECORD_NOT_FOUND
    SCESTATUS_BAD_FORMAT
    SCESTATUS_OTHER_ERROR

-- */
{
    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Actual;


    if ( cxtProfile == NULL ||
         ( pDispNameLen == NULL && pNameLen == NULL) ) {

        return(SCESTATUS_INVALID_PARAMETER);
    }

    if ( GpoID <= 0 ) {
        return(SCESTATUS_RECORD_NOT_FOUND);
    }

    //
    // reset buffers
    //
    if ( Name == NULL && pNameLen ) {
        *pNameLen = 0;
    }

    if ( DisplayName == NULL && pDispNameLen ) {
        *pDispNameLen = 0;
    }

    JET_TABLEID  TableID=0;

    //
    // Open GPO table
    //
    rc = SceJetOpenTable(
                    cxtProfile,
                    "SmTblGpo",
                    SCEJET_TABLE_GPO,
                    SCEJET_OPEN_READ_ONLY,
                    &TableID
                    );

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    //
    // set current index to SecID (the ID)
    //
    JetErr = JetSetCurrentIndex(
                cxtProfile->JetSessionID,
                TableID,
                "SectionKey"
                );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {

        JetErr = JetMakeKey(
                    cxtProfile->JetSessionID,
                    TableID,
                    (void *)(&GpoID),
                    4,
                    JET_bitNewKey
                    );

        if ( JetErr == JET_errKeyIsMade ) {
            //
            // Only one key is needed, it may return this code, even on success.
            //
            JetErr = JET_errSuccess;
        }
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {

            JetErr = JetSeek(
                        cxtProfile->JetSessionID,
                        TableID,
                        JET_bitSeekEQ
                        );
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {

                //
                // find the GPO ID, retrieve column Name if requested
                //

                if ( pNameLen != NULL ) {

                    JET_COLUMNDEF ColumnDef;

                    JetErr = JetGetTableColumnInfo(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    "Name",
                                    (VOID *)&ColumnDef,
                                    sizeof(JET_COLUMNDEF),
                                    JET_ColInfo
                                    );

                    rc = SceJetJetErrorToSceStatus(JetErr);

                    if ( SCESTATUS_SUCCESS == rc ) {

                        JetErr = JetRetrieveColumn(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    ColumnDef.columnid,
                                    (void *)Name,
                                    *pNameLen,
                                    &Actual,
                                    0,
                                    NULL
                                    );
                        *pNameLen = Actual;
                    }

                    rc = SceJetJetErrorToSceStatus(JetErr);
                }

                //
                // retrieve column DisplayName if requested
                //

                if ( ( SCESTATUS_SUCCESS == rc) &&
                     ( pDispNameLen != NULL) ) {

                    JET_COLUMNDEF ColumnDef;

                    JetErr = JetGetTableColumnInfo(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    "DisplayName",
                                    (VOID *)&ColumnDef,
                                    sizeof(JET_COLUMNDEF),
                                    JET_ColInfo
                                    );

                    rc = SceJetJetErrorToSceStatus(JetErr);

                    if ( SCESTATUS_SUCCESS == rc ) {

                        JetErr = JetRetrieveColumn(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    ColumnDef.columnid,
                                    (void *)DisplayName,
                                    *pDispNameLen,
                                    &Actual,
                                    0,
                                    NULL
                                    );
                        *pDispNameLen = Actual;
                    }

                    rc = SceJetJetErrorToSceStatus(JetErr);
                }
            }
        }

    }

    JetCloseTable( cxtProfile->JetSessionID, TableID);

    return(rc);

}


SCESTATUS
SceJetpAddGpo(
    IN PSCECONTEXT cxtProfile,
    IN JET_TABLEID TableID,
    IN JET_COLUMNID GpoIDColumnID,
    IN PCWSTR      Name,
    OUT LONG       *pGpoID
    )
/* ++
Routine Description:

Arguments:

Return Value:

-- */
{
    SCESTATUS  rc;
    JET_ERR   JetErr;
    DWORD     Len;

    if ( cxtProfile == NULL ||
         Name == NULL ||
        pGpoID == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    *pGpoID = 0;

    //
    // get the next available GPO ID first.
    // set current index to the ID
    //
    JetErr = JetSetCurrentIndex(
                cxtProfile->JetSessionID,
                TableID,
                "SectionKey"
                );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc != SCESTATUS_SUCCESS )
        return(rc);

    //
    // Move to the last record
    //
    JetErr = JetMove(
                  cxtProfile->JetSessionID,
                  TableID,
                  JET_MoveLast,
                  0
                  );
    rc = SceJetJetErrorToSceStatus(JetErr);

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // find the GPO ID, retrieve column Name
        //
        JetErr = JetRetrieveColumn(
                    cxtProfile->JetSessionID,
                    TableID,
                    GpoIDColumnID,
                    (void *)pGpoID,
                    4,
                    &Len,
                    0,
                    NULL
                    );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {
            //
            // The next available ID is current ID + 1
            //
            *pGpoID = *pGpoID + 1;
        }

    } else if ( rc == SCESTATUS_RECORD_NOT_FOUND ) {

        *pGpoID = 1;
        rc = SCESTATUS_SUCCESS;
    }

    if ( rc == SCESTATUS_SUCCESS ) {
        //
        // add a record to the GPO table
        //
        JetErr = JetPrepareUpdate(cxtProfile->JetSessionID,
                                  TableID,
                                  JET_prepInsert
                                  );
        rc = SceJetJetErrorToSceStatus(JetErr);

        if ( rc == SCESTATUS_SUCCESS ) {
            //
            // set GpoID
            //

            JetErr = JetSetColumn(
                            cxtProfile->JetSessionID,
                            TableID,
                            GpoIDColumnID,
                            (void *)pGpoID,
                            4,
                            0, //JET_bitSetOverwriteLV,
                            NULL
                            );
            rc = SceJetJetErrorToSceStatus(JetErr);

            if ( rc == SCESTATUS_SUCCESS ) {
                //
                // set Name column
                //

                JET_COLUMNDEF ColumnDef;

                JetErr = JetGetTableColumnInfo(
                                cxtProfile->JetSessionID,
                                TableID,
                                "Name",
                                (VOID *)&ColumnDef,
                                sizeof(JET_COLUMNDEF),
                                JET_ColInfo
                                );

                rc = SceJetJetErrorToSceStatus(JetErr);

                if ( SCESTATUS_SUCCESS == rc ) {

                    Len = wcslen(Name)*sizeof(WCHAR);

                    JetErr = JetSetColumn(
                                    cxtProfile->JetSessionID,
                                    TableID,
                                    ColumnDef.columnid,
                                    (void *)Name,
                                    Len,
                                    0,
                                    NULL
                                    );
                    rc = SceJetJetErrorToSceStatus(JetErr);
                }

            }

            if ( rc != SCESTATUS_SUCCESS ) {
                //
                // if setting fails, cancel the prepared record
                //
                JetPrepareUpdate( cxtProfile->JetSessionID,
                                  TableID,
                                  JET_prepCancel
                                  );
            } else {

                //
                // Setting columns succeed. Update the record
                //
                JetErr = JetUpdate(cxtProfile->JetSessionID,
                                   TableID,
                                   NULL,
                                   0,
                                   &Len
                                   );
                rc = SceJetJetErrorToSceStatus(JetErr);
            }
        }
    }

    return(rc);
}

//
// request the GPO ID (if there is any) for the object
//
SCESTATUS
SceJetGetGpoID(
    IN PSCESECTION hSection,
    IN PWSTR      ObjectName,
    IN JET_COLUMNID JetColGpoID OPTIONAL,
    OUT LONG      *pGpoID
    )
{
    if ( hSection == NULL || ObjectName == NULL || pGpoID == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    SCESTATUS rc;

    *pGpoID = 0;

    JET_COLUMNID  ColGpoID = 0;

    if ( JetColGpoID == 0 ) {

        ColGpoID = hSection->JetColumnGpoID;
    } else {
        ColGpoID = JetColGpoID;
    }

    if ( ColGpoID > 0 ) {

        rc = SceJetSeek(
                    hSection,
                    ObjectName,
                    wcslen(ObjectName)*sizeof(WCHAR),
                    SCEJET_SEEK_EQ_NO_CASE
                    );

        if ( rc == SCESTATUS_SUCCESS ) {

            DWORD Actual;
            JET_ERR JetErr;

            JetErr = JetRetrieveColumn(
                            hSection->JetSessionID,
                            hSection->JetTableID,
                            ColGpoID,
                            (void *)pGpoID,
                            4,
                            &Actual,
                            0,
                            NULL
                            );
            if ( JET_errSuccess != JetErr ) {
                //
                // if the column is nil (no value), it will return warning
                // but the buffer pGpoID is trashed
                //
                *pGpoID = 0;
            }

            rc = SceJetJetErrorToSceStatus(JetErr);
        }

    } else {
        rc = SCESTATUS_RECORD_NOT_FOUND;
    }

    return rc;
}