You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
8.7 KiB
354 lines
8.7 KiB
/*
|
|
* LIBRARY.C
|
|
*
|
|
* RSM Service : Library management
|
|
*
|
|
* Author: ErvinP
|
|
*
|
|
* (c) 2001 Microsoft Corporation
|
|
*
|
|
*/
|
|
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <wtypes.h>
|
|
|
|
#include <ntmsapi.h>
|
|
#include "internal.h"
|
|
#include "resource.h"
|
|
#include "debug.h"
|
|
|
|
|
|
LIBRARY *NewRSMLibrary(ULONG numDrives, ULONG numSlots, ULONG numTransports)
|
|
{
|
|
LIBRARY *lib;
|
|
|
|
lib = (LIBRARY *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, sizeof(LIBRARY));
|
|
if (lib){
|
|
BOOL success = FALSE;
|
|
|
|
lib->state = LIBSTATE_INITIALIZING;
|
|
|
|
InitializeCriticalSection(&lib->lock);
|
|
|
|
InitializeListHead(&lib->allLibrariesListEntry);
|
|
InitializeListHead(&lib->mediaPoolsList);
|
|
InitializeListHead(&lib->freeWorkItemsList);
|
|
InitializeListHead(&lib->pendingWorkItemsList);
|
|
InitializeListHead(&lib->completeWorkItemsList);
|
|
|
|
/*
|
|
* Enqueue the new library
|
|
*/
|
|
EnterCriticalSection(&g_globalServiceLock);
|
|
InsertTailList(&g_allLibrariesList, &lib->allLibrariesListEntry);
|
|
LeaveCriticalSection(&g_globalServiceLock);
|
|
|
|
|
|
lib->somethingToDoEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
/*
|
|
* Allocate arrays for drives, slots, and transports.
|
|
* If the library has zero of any of these,
|
|
* go ahead and allocate a zero-length array for consistency.
|
|
*/
|
|
lib->drives = GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, numDrives*sizeof(DRIVE));
|
|
lib->slots = GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, numSlots*sizeof(SLOT));
|
|
lib->transports = GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, numTransports*sizeof(TRANSPORT));
|
|
|
|
if (lib->somethingToDoEvent &&
|
|
lib->drives && lib->slots && lib->transports){
|
|
|
|
lib->numDrives = numDrives;
|
|
lib->numSlots = numSlots;
|
|
lib->numTransports = numTransports;
|
|
|
|
lib->objHeader.objType = OBJECTTYPE_LIBRARY;
|
|
lib->objHeader.refCount = 1;
|
|
|
|
success = TRUE;
|
|
}
|
|
else {
|
|
ASSERT(0);
|
|
}
|
|
|
|
if (!success){
|
|
FreeRSMLibrary(lib);
|
|
lib = NULL;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(lib);
|
|
}
|
|
|
|
return lib;
|
|
}
|
|
|
|
|
|
VOID FreeRSMLibrary(LIBRARY *lib)
|
|
{
|
|
WORKITEM *workItem;
|
|
LIST_ENTRY *listEntry;
|
|
|
|
ASSERT(lib->state == LIBSTATE_HALTED);
|
|
|
|
/*
|
|
* Dequeue library
|
|
*/
|
|
EnterCriticalSection(&g_globalServiceLock);
|
|
ASSERT(!IsEmptyList(&lib->allLibrariesListEntry));
|
|
ASSERT(!IsEmptyList(&g_allLibrariesList));
|
|
RemoveEntryList(&lib->allLibrariesListEntry);
|
|
InitializeListHead(&lib->allLibrariesListEntry);
|
|
LeaveCriticalSection(&g_globalServiceLock);
|
|
|
|
/*
|
|
* Free all the workItems
|
|
*/
|
|
while (workItem = DequeueCompleteWorkItem(lib, NULL)){
|
|
DBGERR(("there shouldn't be any completed workItems left"));
|
|
FreeWorkItem(workItem);
|
|
}
|
|
while (workItem = DequeuePendingWorkItem(lib, NULL)){
|
|
DBGERR(("there shouldn't be any pending workItems left"));
|
|
FreeWorkItem(workItem);
|
|
}
|
|
while (workItem = DequeueFreeWorkItem(lib, FALSE)){
|
|
FreeWorkItem(workItem);
|
|
}
|
|
ASSERT(lib->numTotalWorkItems == 0);
|
|
|
|
/*
|
|
* Free other internal resources.
|
|
* Note that this is also called from a failed NewRSMLibrary() call,
|
|
* so check each resource before freeing.
|
|
*/
|
|
if (lib->somethingToDoEvent) CloseHandle(lib->somethingToDoEvent);
|
|
if (lib->drives) GlobalFree(lib->drives);
|
|
if (lib->slots) GlobalFree(lib->slots);
|
|
if (lib->transports) GlobalFree(lib->transports);
|
|
|
|
DeleteCriticalSection(&lib->lock);
|
|
|
|
GlobalFree(lib);
|
|
}
|
|
|
|
|
|
LIBRARY *FindLibrary(LPNTMS_GUID libId)
|
|
{
|
|
LIBRARY *lib = NULL;
|
|
|
|
if (libId){
|
|
OBJECT_HEADER *objHdr;
|
|
|
|
objHdr = FindObjectInGuidHash(libId);
|
|
if (objHdr){
|
|
if (objHdr->objType == OBJECTTYPE_LIBRARY){
|
|
lib = (LIBRARY *)objHdr;
|
|
}
|
|
else {
|
|
DerefObject(objHdr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return lib;
|
|
}
|
|
|
|
|
|
BOOL StartLibrary(LIBRARY *lib)
|
|
{
|
|
DWORD threadId;
|
|
BOOL result;
|
|
|
|
lib->hThread = CreateThread(NULL, 0, LibraryThread, lib, 0, &threadId);
|
|
if (lib->hThread){
|
|
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
ASSERT(lib->hThread);
|
|
lib->state = LIBSTATE_ERROR;
|
|
result = FALSE;
|
|
}
|
|
|
|
ASSERT(result);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* HaltLibrary
|
|
*
|
|
* Take a library offline.
|
|
*/
|
|
VOID HaltLibrary(LIBRARY *lib)
|
|
{
|
|
|
|
// BUGBUG - deal with multiple threads trying to halt at the same time
|
|
// (e.g. can't use PulseEvent)
|
|
|
|
EnterCriticalSection(&lib->lock);
|
|
lib->state = LIBSTATE_OFFLINE;
|
|
PulseEvent(lib->somethingToDoEvent);
|
|
LeaveCriticalSection(&lib->lock);
|
|
|
|
/*
|
|
* The library thread may be doing some work.
|
|
* Wait here until it has exited its loop.
|
|
* (a thread handle gets signalled when the thread terminates).
|
|
*/
|
|
WaitForSingleObject(lib->hThread, INFINITE);
|
|
CloseHandle(lib->hThread);
|
|
}
|
|
|
|
|
|
DWORD __stdcall LibraryThread(void *context)
|
|
{
|
|
LIBRARY *lib = (LIBRARY *)context;
|
|
enum libraryStates libState;
|
|
|
|
ASSERT(lib);
|
|
|
|
EnterCriticalSection(&lib->lock);
|
|
ASSERT((lib->state == LIBSTATE_INITIALIZING) || (lib->state == LIBSTATE_OFFLINE));
|
|
libState = lib->state = LIBSTATE_ONLINE;
|
|
LeaveCriticalSection(&lib->lock);
|
|
|
|
while (libState == LIBSTATE_ONLINE){
|
|
|
|
Library_DoWork(lib);
|
|
|
|
EnterCriticalSection(&lib->lock);
|
|
libState = lib->state;
|
|
LeaveCriticalSection(&lib->lock);
|
|
|
|
WaitForSingleObject(lib->somethingToDoEvent, INFINITE);
|
|
|
|
}
|
|
|
|
ASSERT(libState == LIBSTATE_OFFLINE);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
VOID Library_DoWork(LIBRARY *lib)
|
|
{
|
|
WORKITEM *workItem;
|
|
|
|
while (workItem = DequeuePendingWorkItem(lib, NULL)){
|
|
BOOL complete;
|
|
|
|
/*
|
|
* Service the work item.
|
|
* The workItem is 'complete' if we are done with it,
|
|
* regardless of whether or not there was an error.
|
|
*/
|
|
complete = ServiceOneWorkItem(lib, workItem);
|
|
if (complete){
|
|
/*
|
|
* All done.
|
|
* Put the workItem in the complete queue and signal
|
|
* the originating thread.
|
|
*/
|
|
EnqueueCompleteWorkItem(lib, workItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT DeleteLibrary(LIBRARY *lib)
|
|
{
|
|
HRESULT result;
|
|
|
|
EnterCriticalSection(&lib->lock);
|
|
|
|
/*
|
|
* Take the library offline.
|
|
*/
|
|
lib->state = LIBSTATE_OFFLINE;
|
|
|
|
// BUGBUG FINISH - move any media to the offline library
|
|
|
|
// BUGBUG FINISH - delete all media pools, etc.
|
|
|
|
// BUGBUG FINISH - wait for all workItems, opReqs, etc to complete
|
|
|
|
ASSERT(0);
|
|
result = ERROR_CALL_NOT_IMPLEMENTED; // BUGBUG ?
|
|
|
|
/*
|
|
* Mark the library as deleted.
|
|
* This will cause it not to get any new references.
|
|
*/
|
|
ASSERT(!lib->objHeader.isDeleted);
|
|
lib->objHeader.isDeleted = TRUE;
|
|
|
|
/*
|
|
* This dereference will cause the library's refcount to eventually go
|
|
* to zero, upon which it will get deleted. We can still use our pointer
|
|
* though because the caller added a refcount to get his lib pointer.
|
|
*/
|
|
DerefObject(lib);
|
|
|
|
LeaveCriticalSection(&lib->lock);
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
SLOT *FindLibrarySlot(LIBRARY *lib, LPNTMS_GUID slotId)
|
|
{
|
|
SLOT *slot = NULL;
|
|
|
|
if (slotId){
|
|
ULONG i;
|
|
|
|
EnterCriticalSection(&lib->lock);
|
|
|
|
for (i = 0; i < lib->numSlots; i++){
|
|
SLOT *thisSlot = &lib->slots[i];
|
|
if (RtlEqualMemory(&thisSlot->objHeader.guid, slotId, sizeof(NTMS_GUID))){
|
|
/*
|
|
* Found the slot. Reference it since we're returning a pointer to it.
|
|
*/
|
|
ASSERT(thisSlot->slotIndex == i);
|
|
slot = thisSlot;
|
|
RefObject(slot);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&lib->lock);
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
|
|
HRESULT StopCleanerInjection(LIBRARY *lib, LPNTMS_GUID lpInjectOperation)
|
|
{
|
|
HRESULT result;
|
|
|
|
// BUGBUG FINISH
|
|
ASSERT(0);
|
|
result = ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
HRESULT StopCleanerEjection(LIBRARY *lib, LPNTMS_GUID lpEjectOperation)
|
|
{
|
|
HRESULT result;
|
|
|
|
// BUGBUG FINISH
|
|
ASSERT(0);
|
|
result = ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
return result;
|
|
}
|
|
|