windows-nt/Source/XPSP1/NT/shell/osshell/cpls/ports/comdb.c

826 lines
19 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Author:
Doron J. Holan (doronh), 1-22-1998
--*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <msports.h>
#include <tchar.h>
#define GROWTH_VALUE 1024
#define BITS_INA_BYTE 8
typedef struct _DB_INFO {
HANDLE RegChangedEvent;
HANDLE AccessMutex;
HKEY DBKey;
PBYTE Ports;
ULONG PortsLength;
} DB_INFO, * PDB_INFO;
#define HandleToDBInfo(h) ((PDB_INFO) (h))
#define IsEventSignalled(hevent) (WaitForSingleObject(hevent, 0) == WAIT_OBJECT_0)
#define SanityCheckComNumber(num) { if (num > COMDB_MAX_PORTS_ARBITRATED) return ERROR_INVALID_PARAMETER; }
#define SanityCheckDBInfo(dbi) { if ((HANDLE) dbi == INVALID_HANDLE_VALUE) return ERROR_INVALID_PARAMETER; }
const TCHAR szMutexName[] = _T("ComPortNumberDatabaseMutexObject");
const TCHAR szComDBName[] = _T("ComDB");
const TCHAR szComDBMerge[] = _T("ComDB Merge");
const TCHAR szComDBPath[] = _T("System\\CurrentControlSet\\Control\\COM Name Arbiter");
const TCHAR szComDBPathOld[] = _T("System\\CurrentControlSet\\Services\\Serial");
#ifdef malloc
#undef malloc
#endif
#define malloc(size) LocalAlloc(LPTR, (size))
#ifdef free
#undef free
#endif
#define free LocalFree
VOID
DestroyDBInfo(
PDB_INFO DBInfo
)
{
if (DBInfo->AccessMutex &&
DBInfo->AccessMutex != INVALID_HANDLE_VALUE) {
CloseHandle(DBInfo->AccessMutex);
}
if (DBInfo->RegChangedEvent &&
DBInfo->RegChangedEvent != INVALID_HANDLE_VALUE) {
CloseHandle(DBInfo->RegChangedEvent);
}
if (DBInfo->DBKey &&
DBInfo->DBKey != (HKEY) INVALID_HANDLE_VALUE) {
RegCloseKey(DBInfo->DBKey);
}
if (DBInfo->Ports) {
free(DBInfo->Ports);
}
free(DBInfo);
}
LONG
CreationFailure (
PHCOMDB PHComDB,
PDB_INFO DBInfo
)
{
if (DBInfo->AccessMutex != 0)
ReleaseMutex(DBInfo->AccessMutex);
DestroyDBInfo(DBInfo);
*PHComDB = (HCOMDB) INVALID_HANDLE_VALUE;
return ERROR_ACCESS_DENIED;
}
VOID
RegisterForNotification(
PDB_INFO DBInfo
)
{
ResetEvent(DBInfo->RegChangedEvent);
if (RegNotifyChangeKeyValue(DBInfo->DBKey,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
DBInfo->RegChangedEvent,
TRUE) != ERROR_SUCCESS) {
//
// Can't get a notification of when the DB is changed so close the handle
// and we must update the DB at every access no matter what
//
CloseHandle(DBInfo->RegChangedEvent);
DBInfo->RegChangedEvent = INVALID_HANDLE_VALUE;
}
}
BOOL
ResizeDatabase(
PDB_INFO DBInfo,
ULONG NumberPorts
)
{
PBYTE newPorts = NULL;
ULONG newPortsLength;
if (DBInfo->Ports) {
newPortsLength = NumberPorts / BITS_INA_BYTE;
newPorts = (PBYTE) malloc(newPortsLength * sizeof(BYTE));
if (newPorts) {
memcpy(newPorts, DBInfo->Ports, DBInfo->PortsLength);
free(DBInfo->Ports);
DBInfo->Ports = newPorts;
DBInfo->PortsLength = newPortsLength;
return TRUE;
}
else {
return FALSE;
}
}
else {
//
// Just alloc and be done with it
//
DBInfo->PortsLength = NumberPorts / BITS_INA_BYTE;
DBInfo->Ports = (PBYTE) malloc(DBInfo->PortsLength * sizeof(BYTE));
return DBInfo->Ports ? TRUE : FALSE;
}
}
LONG
WINAPI
ComDBOpen (
PHCOMDB PHComDB
)
/*++
Routine Description:
Opens name data base, and returns a handle to be used in future calls.
Arguments:
None.
Return Value:
INVALID_HANDLE_VALUE if the call fails, otherwise a valid handle
If INVALID_HANDLE_VALUE, call GetLastError() to get details (??)
--*/
{
PDB_INFO dbInfo = malloc(sizeof(DB_INFO));
DWORD type, size, disposition = 0x0;
BOOLEAN migrated = FALSE;
LONG res;
BYTE merge[COMDB_MIN_PORTS_ARBITRATED / BITS_INA_BYTE /* 32 */];
if (dbInfo == 0) {
*PHComDB = (HCOMDB) INVALID_HANDLE_VALUE;
return ERROR_ACCESS_DENIED;
}
dbInfo->AccessMutex = CreateMutex(NULL, FALSE, szMutexName);
if (dbInfo->AccessMutex == 0) {
return CreationFailure(PHComDB, dbInfo);
}
//
// Enter the mutex so we can guarantee only one thread pounding on the reg
// key at once
//
WaitForSingleObject(dbInfo->AccessMutex, INFINITE);
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
szComDBPath,
0,
(TCHAR *) NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS | KEY_NOTIFY,
(LPSECURITY_ATTRIBUTES) NULL,
&dbInfo->DBKey,
&disposition) != ERROR_SUCCESS) {
//
// Try again w/out notification caps
//
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
szComDBPath,
0,
(TCHAR *) NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
(LPSECURITY_ATTRIBUTES) NULL,
&dbInfo->DBKey,
&disposition) != ERROR_SUCCESS) {
return CreationFailure(PHComDB, dbInfo);
}
dbInfo->RegChangedEvent = INVALID_HANDLE_VALUE;
}
else {
dbInfo->RegChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (dbInfo->RegChangedEvent == 0) {
dbInfo->RegChangedEvent = INVALID_HANDLE_VALUE;
}
}
if (disposition == REG_CREATED_NEW_KEY) {
//
// Must migrate the previous values from the old com db path
//
HKEY hOldDB;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szComDBPathOld,
0,
KEY_ALL_ACCESS,
&hOldDB) == ERROR_SUCCESS &&
RegQueryValueEx(hOldDB,
szComDBName,
0,
&type,
NULL,
&dbInfo->PortsLength) == ERROR_SUCCESS) {
//
// The old value is still there, get its contents, copy it to the
// new location and delete the old value
//
migrated = TRUE;
ResizeDatabase(dbInfo, dbInfo->PortsLength * BITS_INA_BYTE);
size = dbInfo->PortsLength;
res = RegQueryValueEx(hOldDB,
szComDBName,
0,
&type,
(PBYTE) dbInfo->Ports,
&size);
RegDeleteValue(hOldDB, szComDBName);
//
// The value does not exist, write it out
//
if (RegSetValueEx(dbInfo->DBKey,
szComDBName,
0,
REG_BINARY,
dbInfo->Ports,
dbInfo->PortsLength) != ERROR_SUCCESS) {
RegCloseKey(hOldDB);
return CreationFailure(PHComDB, dbInfo);
}
RegCloseKey(hOldDB);
}
}
//
// If we haven't migrated values from the old path, then either create a
// new chunk or read in values previously written
//
if (!migrated) {
res = RegQueryValueEx(dbInfo->DBKey,
szComDBName,
0,
&type,
NULL,
&dbInfo->PortsLength);
if (res == ERROR_FILE_NOT_FOUND) {
ResizeDatabase(dbInfo, COMDB_MIN_PORTS_ARBITRATED);
//
// The value does not exist, write it out
//
res = RegSetValueEx(dbInfo->DBKey,
szComDBName,
0,
REG_BINARY,
dbInfo->Ports,
dbInfo->PortsLength);
if (res != ERROR_SUCCESS) {
return CreationFailure(PHComDB, dbInfo);
}
}
else if (res == ERROR_MORE_DATA || res != ERROR_SUCCESS || type != REG_BINARY) {
return CreationFailure(PHComDB, dbInfo);
}
else if (res == ERROR_SUCCESS) {
ResizeDatabase(dbInfo, dbInfo->PortsLength * BITS_INA_BYTE);
size = dbInfo->PortsLength;
res = RegQueryValueEx(dbInfo->DBKey,
szComDBName,
0,
&type,
(PBYTE) dbInfo->Ports,
&size);
}
}
size = sizeof(merge);
if (RegQueryValueEx(dbInfo->DBKey,
szComDBMerge,
0,
&type,
(PBYTE) merge,
&size) == ERROR_SUCCESS &&
size <= dbInfo->PortsLength) {
int i;
for (i = 0 ; i < COMDB_MIN_PORTS_ARBITRATED / BITS_INA_BYTE; i++) {
dbInfo->Ports[i] |= merge[i];
}
RegDeleteValue(dbInfo->DBKey, szComDBMerge);
RegSetValueEx(dbInfo->DBKey,
szComDBName,
0,
REG_BINARY,
dbInfo->Ports,
dbInfo->PortsLength);
}
if (dbInfo->RegChangedEvent != INVALID_HANDLE_VALUE) {
RegisterForNotification(dbInfo);
}
ReleaseMutex(dbInfo->AccessMutex);
//
// All done! phew...
//
*PHComDB = (HCOMDB) dbInfo;
return ERROR_SUCCESS;
}
LONG
WINAPI
ComDBClose (
HCOMDB HComDB
)
/*++
Routine Description:
frees a handle to the database returned from OpenComPortDataBase
Arguments:
Handle returned from OpenComPortDataBase.
Return Value:
None
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
SanityCheckDBInfo(dbInfo);
DestroyDBInfo(dbInfo);
return ERROR_SUCCESS;
}
BOOL
EnterDB(
PDB_INFO DBInfo
)
{
BOOL eventSignalled = FALSE;
LONG res;
DWORD type, size;
WaitForSingleObject(DBInfo->AccessMutex, INFINITE);
if (DBInfo->RegChangedEvent == INVALID_HANDLE_VALUE ||
(eventSignalled = IsEventSignalled(DBInfo->RegChangedEvent))) {
size = 0;
res = RegQueryValueEx(DBInfo->DBKey,
szComDBName,
0,
&type,
0,
&size);
//
// Couldn't update the DB ... fail
//
if (res != ERROR_SUCCESS || type != REG_BINARY) {
ReleaseMutex(DBInfo->AccessMutex);
return FALSE;
}
if (size != DBInfo->PortsLength) {
ResizeDatabase(DBInfo, size * BITS_INA_BYTE);
}
RegQueryValueEx(DBInfo->DBKey,
szComDBName,
0,
&type,
DBInfo->Ports,
&size);
//
// Reregister the notification with the registry
//
if (eventSignalled) {
RegisterForNotification(DBInfo);
}
}
return TRUE;
}
LONG
LeaveDB(
PDB_INFO DBInfo,
BOOL CommitChanges
)
{
LONG retVal = ERROR_SUCCESS;
if (CommitChanges) {
if (RegSetValueEx(DBInfo->DBKey,
szComDBName,
0,
REG_BINARY,
DBInfo->Ports,
DBInfo->PortsLength) != ERROR_SUCCESS) {
retVal = ERROR_CANTWRITE;
}
//
// The setting of the value in the reg signals the event...but we don't
// need to resync w/the reg off of this change b/c it is our own! Instead
// reset the event and rereg for the event
//
if (DBInfo->RegChangedEvent != INVALID_HANDLE_VALUE) {
RegisterForNotification(DBInfo);
}
}
ReleaseMutex(DBInfo->AccessMutex);
return retVal;
}
VOID
GetByteAndMask(
PDB_INFO DBInfo,
DWORD ComNumber,
PBYTE *Byte,
PBYTE Mask
)
{
ComNumber--;
*Byte = DBInfo->Ports + (ComNumber / BITS_INA_BYTE);
*Mask = 1 << (ComNumber % BITS_INA_BYTE);
}
LONG
WINAPI
ComDBGetCurrentPortUsage (
HCOMDB HComDB,
PBYTE Buffer,
DWORD BufferSize,
ULONG ReportType,
LPDWORD MaxPortsReported
)
/*++
Handle requests that require no synch w/DB first.
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
PBYTE curSrc, curDest, endDest;
BYTE mask;
SanityCheckDBInfo(dbInfo);
if (!EnterDB(dbInfo)) {
return ERROR_NOT_CONNECTED;
}
if (Buffer == 0) {
if (!MaxPortsReported) {
LeaveDB(dbInfo, FALSE);
return ERROR_INVALID_PARAMETER;
}
else {
*MaxPortsReported = dbInfo->PortsLength * BITS_INA_BYTE;
return LeaveDB(dbInfo, FALSE);
}
}
if (ReportType == CDB_REPORT_BITS) {
if (BufferSize > dbInfo->PortsLength) {
BufferSize = dbInfo->PortsLength;
}
memcpy(Buffer, dbInfo->Ports, BufferSize);
if (MaxPortsReported) {
*MaxPortsReported = BufferSize * BITS_INA_BYTE;
}
}
else if (ReportType == CDB_REPORT_BYTES) {
if (BufferSize > dbInfo->PortsLength * BITS_INA_BYTE) {
BufferSize = dbInfo->PortsLength * BITS_INA_BYTE;
}
curSrc = dbInfo->Ports;
endDest = Buffer + BufferSize;
curDest = Buffer;
for (mask = 1; curDest != endDest; curDest++) {
*curDest = (*curSrc & mask) ? 1 : 0;
if (mask & 0x80) {
mask = 0x1;
curSrc++;
}
else
mask <<= 1;
}
}
else {
LeaveDB(dbInfo, FALSE);
return ERROR_INVALID_PARAMETER;
}
return LeaveDB(dbInfo, FALSE);
}
LONG
WINAPI
ComDBClaimNextFreePort (
HCOMDB HComDB,
LPDWORD ComNumber
)
/*++
Routine Description:
returns the first free COMx value
Arguments:
Handle returned from OpenComPortDataBase.
Return Value:
returns ERROR_SUCCESS if successful. or other ERROR_ if not
if successful, then ComNumber will be that next free com value and claims it in the database
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
DWORD num;
BOOL commit = FALSE;
PBYTE curSrc, srcEnd;
BYTE mask;
LONG ret;
SanityCheckDBInfo(dbInfo);
if (!EnterDB(dbInfo)) {
return ERROR_NOT_CONNECTED;
}
curSrc = dbInfo->Ports;
srcEnd = curSrc + dbInfo->PortsLength;
for (num = 3, mask = 0x4; curSrc != srcEnd; num++) {
if (!(*curSrc & mask)) {
*ComNumber = num;
*curSrc |= mask;
commit = TRUE;
break;
}
else if (mask & 0x80) {
mask = 0x1;
curSrc++;
}
else {
mask <<= 1;
}
}
if (curSrc == srcEnd && !commit && num < COMDB_MAX_PORTS_ARBITRATED) {
// DB entirely full
ResizeDatabase(dbInfo, ((num / GROWTH_VALUE) + 1) * GROWTH_VALUE);
*ComNumber = num;
GetByteAndMask(dbInfo, num, &curSrc, &mask);
*curSrc |= mask;
commit = TRUE;
}
ret = LeaveDB(dbInfo, commit);
if (!commit) {
ret = ERROR_NO_LOG_SPACE;
}
return ret;
}
LONG
WINAPI
ComDBClaimPort (
HCOMDB HComDB,
DWORD ComNumber,
BOOL ForceClaim,
PBOOL Forced
)
/*++
Routine Description:
Attempts to claim a com name in the database
Arguments:
DataBaseHandle - returned from OpenComPortDataBase.
ComNumber - The port value to be claimed
Force - If TRUE, will force the port to be claimed even if in use already
Return Value:
returns ERROR_SUCCESS if port name was not already claimed, or if it was claimed
and Force was TRUE.
ERROR_SHARING_VIOLATION if port name is use and Force is false
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
PBYTE curByte;
BYTE mask;
BOOL commit = TRUE;
LONG res;
ULONG newSize;
BOOL f;
if (!(Forced)) {
Forced = &f;
}
SanityCheckComNumber(ComNumber);
SanityCheckDBInfo(dbInfo);
if (!EnterDB(dbInfo)) {
return ERROR_NOT_CONNECTED;
}
if (ComNumber > dbInfo->PortsLength * BITS_INA_BYTE) {
ResizeDatabase(dbInfo, ((ComNumber / GROWTH_VALUE) + 1) * GROWTH_VALUE);
}
GetByteAndMask(dbInfo, ComNumber, &curByte, &mask);
if (*curByte & mask) {
commit = FALSE;
if (ForceClaim) {
if (Forced)
*Forced = TRUE;
}
else {
res = LeaveDB(dbInfo, commit);
if (res == ERROR_SUCCESS) {
return ERROR_SHARING_VIOLATION;
}
else {
return res;
}
}
}
else {
if (Forced)
*Forced = FALSE;
*curByte |= mask;
}
return LeaveDB(dbInfo, commit);
}
LONG
WINAPI
ComDBReleasePort (
HCOMDB HComDB,
DWORD ComNumber
)
/*++
Routine Description:
un-claims the port in the database
Arguments:
DatabaseHandle - returned from OpenComPortDataBase.
ComNumber - port to be unclaimed in database
Return Value:
returns ERROR_SUCCESS if successful. or other ERROR_ if not
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
PBYTE byte;
BYTE mask;
SanityCheckDBInfo(dbInfo);
if (!EnterDB(dbInfo)) {
return ERROR_NOT_CONNECTED;
}
if (ComNumber > dbInfo->PortsLength * BITS_INA_BYTE) {
LeaveDB(dbInfo, FALSE);
return ERROR_INVALID_PARAMETER;
}
GetByteAndMask(dbInfo, ComNumber, &byte, &mask);
*byte &= ~mask;
return LeaveDB(dbInfo, TRUE);
}
LONG
WINAPI
ComDBResizeDatabase (
HCOMDB HComDB,
DWORD NewSize
)
/*++
Routine Description:
Resizes the database to the new size. To get the current size, call
ComDBGetCurrentPortUsage with a Buffer == NULL.
Arguments:
DatabaseHandle - returned from OpenComPortDataBase.
NewSize - must be a multiple of 1024, with a max of 4096
Return Value:
returns ERROR_SUCCESS if successful
ERROR_BAD_LENGTH if NewSize is not greater than the current size or
NewSize is greater than COMDB_MAX_PORTS_ARBITRATED
--*/
{
PDB_INFO dbInfo = HandleToDBInfo(HComDB);
BOOL commit = FALSE;
SanityCheckDBInfo(dbInfo);
if (NewSize % GROWTH_VALUE) {
return ERROR_INVALID_PARAMETER;
}
if (!EnterDB(dbInfo)) {
return ERROR_NOT_CONNECTED;
}
if (NewSize > COMDB_MAX_PORTS_ARBITRATED ||
dbInfo->PortsLength * BITS_INA_BYTE >= NewSize) {
LeaveDB(dbInfo, FALSE);
return ERROR_BAD_LENGTH;
}
ResizeDatabase(dbInfo, NewSize);
return LeaveDB(dbInfo, TRUE);
}