windows-nt/Source/XPSP1/NT/net/sfm/afp/server/fsd.c

1151 lines
29 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
fsd.c
Abstract:
This module implements the File System Driver for the AFP Server. All of
the initialization, admin request handler etc. is here.
Author:
Jameel Hyder (microsoft!jameelh)
Revision History:
01 Jun 1992 Initial Version
--*/
#define FILENUM FILE_FSD
#include <afp.h>
#define AFPADMIN_LOCALS
#include <afpadmin.h>
#include <client.h>
#include <scavengr.h>
#include <secutil.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, DriverEntry)
#pragma alloc_text( INIT, afpInitServer)
#pragma alloc_text( PAGE, afpFsdDispatchAdminRequest)
#pragma alloc_text( PAGE, afpFsdHandleAdminRequest)
#pragma alloc_text( PAGE, afpHandleQueuedAdminRequest)
#pragma alloc_text( PAGE, afpFsdUnloadServer)
#pragma alloc_text( PAGE, afpAdminThread)
#pragma alloc_text( PAGE, afpFsdHandleShutdownRequest)
#endif
/*** afpFsdDispatchAdminRequest
*
* This is the driver entry point. This is for the sole use by the server
* service which opens the driver for EXCLUSIVE use. The admin request is
* received here as a request packet defined in admin.h.
*/
LOCAL NTSTATUS
afpFsdDispatchAdminRequest(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS Status;
BOOLEAN LockDown = True;
static DWORD afpOpenCount = 0;
pDeviceObject; // prevent compiler warnings
PAGED_CODE( );
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrp->IoStatus.Information = 0;
if ((pIrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) ||
(pIrpSp->MajorFunction == IRP_MJ_CREATE) ||
(pIrpSp->MajorFunction == IRP_MJ_CLOSE))
{
LockDown = False;
}
else
{
afpStartAdminRequest(pIrp); // Lock admin code
}
switch (pIrpSp->MajorFunction)
{
case IRP_MJ_CREATE:
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("afpFsdDispatchAdminRequest: Open Handle\n"));
INTERLOCKED_INCREMENT_LONG(&afpOpenCount);
// Fall through
case IRP_MJ_CLOSE:
Status = STATUS_SUCCESS;
break;
case IRP_MJ_DEVICE_CONTROL:
Status = afpFsdHandleAdminRequest(pIrp);
break;
case IRP_MJ_FILE_SYSTEM_CONTROL:
Status = AfpSecurityUtilityWorker(pIrp, pIrpSp);
break;
case IRP_MJ_CLEANUP:
Status = STATUS_SUCCESS;
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("afpFsdDispatchAdminRequest: Close Handle\n"));
INTERLOCKED_DECREMENT_LONG(&afpOpenCount);
#if 0
// If the service is closing its handle. Force a service stop
if ((afpOpenCount == 0) &&
(AfpServerState != AFP_STATE_STOPPED))
AfpAdmServiceStop(NULL, 0, NULL);
#endif
break;
case IRP_MJ_SHUTDOWN:
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("afpFsdDispatchAdminRequest: Received shutdown notification !!\n"));
Status = afpFsdHandleShutdownRequest(pIrp);
break;
default:
Status = STATUS_NOT_IMPLEMENTED;
break;
}
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
if (Status != STATUS_PENDING)
{
pIrp->IoStatus.Status = Status;
if (LockDown)
{
afpStopAdminRequest(pIrp); // Unlock admin code (and complete request)
}
else
{
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
}
}
return Status;
}
/*** afpFsdHandleAdminRequest
*
* This is the admin request handler. The list of admin requests are defined
* in admin.h. The admin requests must happen in a pre-defined order. The
* service start must happen after atleast the following.
*
* ServerSetInfo
*
* Preferably all VolumeAdds should also happen before server start. This is
* not enforced, obviously since the server can start w/o any volumes defined.
*
*/
LOCAL NTSTATUS
afpFsdHandleAdminRequest(
IN PIRP pIrp
)
{
NTSTATUS Status = STATUS_PENDING;
USHORT FuncCode;
USHORT Method;
PVOID pBufIn;
PVOID pBufOut;
LONG i, Off, iBufLen, oBufLen;
LONG NumEntries;
PADMQREQ pAdmQReq;
IN PIO_STACK_LOCATION pIrpSp;
struct _AdminApiDispatchTable *pDispTab;
PAGED_CODE( );
// Initialize the I/O Status block
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
iBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
pBufIn = pIrp->AssociatedIrp.SystemBuffer;
FuncCode = (USHORT)AFP_CC_BASE(pIrpSp->Parameters.DeviceIoControl.IoControlCode);
Method = (USHORT)AFP_CC_METHOD(pIrpSp->Parameters.DeviceIoControl.IoControlCode);
if (Method == METHOD_BUFFERED)
{
// Get the output buffer and its length. Input and Output buffers are
// both pointed to by the SystemBuffer
pBufOut = pBufIn;
oBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
}
else if ((Method == METHOD_IN_DIRECT) && (pIrp->MdlAddress != NULL))
{
pBufOut = MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress,
NormalPagePriority);
if (pBufOut == NULL)
{
ASSERT(0);
return STATUS_INSUFFICIENT_RESOURCES;
}
oBufLen = MmGetMdlByteCount(pIrp->MdlAddress);
}
else
{
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_ERR,
("afpFsdHandleAdminRequest: Invalid Request %d/%d\n", FuncCode, Method));
return STATUS_INVALID_PARAMETER;
}
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("afpFsdHandleAdminRequest Entered, Function %d\n", FuncCode));
// Validate the function code
if (FuncCode == 0 || FuncCode >= CC_BASE_MAX)
return STATUS_INVALID_PARAMETER;
pDispTab = &AfpAdminDispatchTable[FuncCode - 1];
if ((pDispTab->_MinBufLen > (SHORT)iBufLen) ||
(pDispTab->_OpCode != pIrpSp->Parameters.DeviceIoControl.IoControlCode))
{
return STATUS_INVALID_PARAMETER;
}
INTERLOCKED_INCREMENT_LONG( &AfpServerStatistics.stat_NumAdminReqs );
if (pDispTab->_CausesChange)
INTERLOCKED_INCREMENT_LONG( &AfpServerStatistics.stat_NumAdminChanges );
// Now validate the DESCRIPTOR of the input buffer
for (i = 0; i < MAX_FIELDS; i++)
{
if (pDispTab->_Fields[i]._FieldDesc == DESC_NONE)
break;
Off = pDispTab->_Fields[i]._FieldOffset;
switch (pDispTab->_Fields[i]._FieldDesc)
{
case DESC_STRING:
ASSERT(pBufIn != NULL);
// Make Sure that the string is pointing to somewhere within
// the buffer and also the end of the buffer is a UNICODE_NULL
if ((*(PLONG)((PBYTE)pBufIn + Off) > iBufLen) ||
(*(LPWSTR)((PBYTE)pBufIn + iBufLen - sizeof(WCHAR)) != UNICODE_NULL))
{
return STATUS_INVALID_PARAMETER;
}
// Convert the offset to a pointer
OFFSET_TO_POINTER(*(PBYTE *)((PBYTE)pBufIn + Off),
(PBYTE)pBufIn + pDispTab->_OffToStruct);
break;
case DESC_ETC:
ASSERT(pBufIn != NULL);
// Make Sure that there are as many etc mappings as the
// structure claims
NumEntries = *(PLONG)((PBYTE)pBufIn + Off);
if ((LONG)(NumEntries * sizeof(ETCMAPINFO) + sizeof(DWORD)) > iBufLen)
{
return STATUS_INVALID_PARAMETER;
}
if (NumEntries > (LONG)((iBufLen/sizeof(ETCMAPINFO)) + 1))
{
return STATUS_INVALID_PARAMETER;
}
break;
case DESC_ICON:
// Validate that the buffer is atleast big enough to hold the
// icon that this purports to.
ASSERT(pBufIn != NULL);
if ((LONG)((*(PLONG)((PBYTE)pBufIn + Off) +
sizeof(SRVICONINFO))) > iBufLen)
{
return STATUS_INVALID_PARAMETER;
}
break;
case DESC_SID:
// Validate that the buffer is big enough to hold the Sid
ASSERT(pBufIn != NULL);
{
LONG Offst, SidSize;
Offst = *(PLONG)((PBYTE)pBufIn + Off);
// If no SID is being sent then we're done
if (Offst == 0)
{
break;
}
if ((Offst > iBufLen) ||
(Offst < (LONG)(sizeof(AFP_DIRECTORY_INFO))) ||
((Offst + (LONG)sizeof(SID)) > iBufLen))
{
return STATUS_INVALID_PARAMETER;
}
// Convert the offset to a pointer
OFFSET_TO_POINTER(*(PBYTE *)((PBYTE)pBufIn + Off),
(PBYTE)pBufIn + pDispTab->_OffToStruct);
// Finally check if the buffer is big enough for the real
// sid
SidSize = RtlLengthSid(*((PSID *)((PBYTE)pBufIn + Off)));
if ((Off + SidSize) > iBufLen)
{
return STATUS_INVALID_PARAMETER;
}
}
break;
case DESC_SPECIAL:
// Validate that the buffer is big enough to hold all the
// information. The information consists of limits on non-paged
// and paged memory and a list of domain sids and their corres.
// posix offsets
ASSERT(pBufIn != NULL);
{
LONG i;
LONG SizeRemaining;
PAFP_SID_OFFSET pSidOff;
SizeRemaining = iBufLen - (sizeof(AFP_SID_OFFSET_DESC) -
sizeof(AFP_SID_OFFSET));
for (i = 0;
i < (LONG)(((PAFP_SID_OFFSET_DESC)pBufIn)->CountOfSidOffsets);
i++, pSidOff++)
{
pSidOff = &((PAFP_SID_OFFSET_DESC)pBufIn)->SidOffsetPairs[i];
if (SizeRemaining < (sizeof(AFP_SID_OFFSET) + sizeof(SID)))
return STATUS_INVALID_PARAMETER;
OFFSET_TO_POINTER(pSidOff->pSid, pSidOff);
if ((LONG)(((PBYTE)(pSidOff->pSid) - (PBYTE)pBufIn +
RtlLengthSid(pSidOff->pSid))) > iBufLen)
return STATUS_INVALID_PARAMETER;
SizeRemaining -= (RtlLengthSid(pSidOff->pSid) +
sizeof(AFP_SID_OFFSET));
}
}
break;
}
}
// Can this request be handled/validated at this level
if (pDispTab->_AdminApiWorker != NULL)
{
Status = (*pDispTab->_AdminApiWorker)(pBufIn, oBufLen, pBufOut);
if (NT_SUCCESS(Status))
{
if (Method != METHOD_BUFFERED)
pIrp->IoStatus.Information = oBufLen;
}
}
if (Status == STATUS_PENDING)
{
ASSERT (pDispTab->_AdminApiQueuedWorker != NULL);
// Mark this as a pending Irp as we are about to queue it up
IoMarkIrpPending(pIrp);
if ((pAdmQReq =
(PADMQREQ)AfpAllocNonPagedMemory(sizeof(ADMQREQ))) == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
PWORK_ITEM pWI = &pAdmQReq->aqr_WorkItem;
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("afpFsdHandleAdminRequest: Queuing to worker\n"));
AfpInitializeWorkItem(pWI,
afpHandleQueuedAdminRequest,
pAdmQReq);
pAdmQReq->aqr_AdminApiWorker = pDispTab->_AdminApiQueuedWorker;
pAdmQReq->aqr_pIrp = pIrp;
// Insert item in admin queue
INTERLOCKED_ADD_ULONG(&AfpWorkerRequests, 1, &AfpServerGlobalLock);
KeInsertQueue(&AfpAdminQueue, &pAdmQReq->aqr_WorkItem.wi_List);
}
}
return Status;
}
/*** afpHandleQueuedAdminRequest
*
* This handles queued admin requests. It is called in the context of the
* worker thread.
*/
LOCAL VOID FASTCALL
afpHandleQueuedAdminRequest(
IN PADMQREQ pAdmQReq
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
PVOID pBufOut = NULL;
LONG oBufLen = 0;
USHORT Method;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("afpHandleQueuedAdminRequest Entered\n"));
// Get the IRP and the IRP Stack location out of the request
pIrp = pAdmQReq->aqr_pIrp;
ASSERT (pIrp != NULL);
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
ASSERT (pIrpSp != NULL);
Method = (USHORT)AFP_CC_METHOD(pIrpSp->Parameters.DeviceIoControl.IoControlCode);
if (Method == METHOD_BUFFERED)
{
// Get the output buffer and its length. Input and Output buffers are
// both pointed to by the SystemBuffer
oBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
pBufOut = pIrp->AssociatedIrp.SystemBuffer;
}
else if ((Method == METHOD_IN_DIRECT) && (pIrp->MdlAddress != NULL))
{
pBufOut = MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress,
NormalPagePriority);
if (pBufOut == NULL)
{
ASSERT(0);
pAdmQReq->aqr_pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(pAdmQReq->aqr_pIrp, IO_NETWORK_INCREMENT);
AfpFreeMemory(pAdmQReq);
return;
}
oBufLen = MmGetMdlByteCount(pIrp->MdlAddress);
}
else ASSERTMSG(0, "afpHandleQueuedAdminRequest: Invalid method\n");
// Call the worker and complete the IoRequest
pIrp->IoStatus.Status = (*pAdmQReq->aqr_AdminApiWorker)(pIrp->AssociatedIrp.SystemBuffer,
oBufLen,
pBufOut);
if (NT_SUCCESS(pIrp->IoStatus.Status))
{
if (Method != METHOD_BUFFERED)
pIrp->IoStatus.Information = oBufLen;
}
ASSERT(pIrp->IoStatus.Status != STATUS_PENDING);
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
afpStopAdminRequest(pIrp); // Unlock admin code and complete request
AfpFreeMemory(pAdmQReq);
}
/*** afpFsdUnloadServer
*
* This is the unload routine for the Afp Server. The server can ONLY be
* unloaded in its passive state i.e. either before recieving a ServiceStart
* or after recieving a ServiceStop. This is ensured by making the service
* dependent on the server. Also the IO system ensures that there are no open
* handles to our device when this happens.
*/
LOCAL VOID
afpFsdUnloadServer(
IN PDRIVER_OBJECT DeviceObject
)
{
NTSTATUS Status;
LONG i;
LONG LastThreadCount = 0;
PETHREAD pLastThrdPtr;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpFsdUnloadServer Entered\n"));
ASSERT((AfpServerState == AFP_STATE_STOPPED) || (AfpServerState == AFP_STATE_IDLE));
// Stop our threads before unloading
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpFsdUnloadServer: Stopping worker threads\n"));
//
// tell TDI we don't care to know if the stack is going away
//
if (AfpTdiNotificationHandle)
{
Status = TdiDeregisterPnPHandlers(AfpTdiNotificationHandle);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("afpFsdUnloadServer: TdiDeregisterNotificationHandler failed with %lx\n",Status));
}
AfpTdiNotificationHandle = NULL;
}
DsiShutdown();
// Stop the scavenger. This also happens during server stop but we can get here
// another way as well
AfpScavengerFlushAndStop();
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpFsdUnloadServer: Stopping admin thread\n"));
if (AfpNumAdminThreads > 0)
{
KeClearEvent(&AfpStopConfirmEvent);
KeInsertQueue(&AfpAdminQueue, &AfpTerminateThreadWI.wi_List);
do
{
Status = AfpIoWait(&AfpStopConfirmEvent, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("afpFsdUnloadServer: Timeout Waiting for admin thread, re-waiting\n"));
}
} while (Status == STATUS_TIMEOUT);
}
KeRundownQueue(&AfpAdminQueue);
if (AfpNumNotifyThreads > 0)
{
for (i = 0; i < NUM_NOTIFY_QUEUES; i++)
{
KeClearEvent(&AfpStopConfirmEvent);
KeInsertQueue(&AfpVolumeNotifyQueue[i], &AfpTerminateNotifyThread.vn_List);
do
{
Status = AfpIoWait(&AfpStopConfirmEvent, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("afpFsdUnloadServer: Timeout Waiting for Notify Thread %d, re-waiting\n", i));
}
} while (Status == STATUS_TIMEOUT);
KeRundownQueue(&AfpVolumeNotifyQueue[i]);
}
}
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
// Cleanup virtual memory used by volumes for private notifies
afpFreeNotifyBlockMemory();
// Stop worker threads
if (AfpNumThreads > 0)
{
KeClearEvent(&AfpStopConfirmEvent);
KeInsertQueue(&AfpWorkerQueue, &AfpTerminateThreadWI.wi_List);
do
{
Status = AfpIoWait(&AfpStopConfirmEvent, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("afpFsdUnloadServer: Timeout Waiting for worker threads, re-waiting\n"));
}
} while (Status == STATUS_TIMEOUT);
}
// See how many threads are around
// Loop around until we have exactly one thread left or if no worker
// thread was started
do
{
pLastThrdPtr = NULL;
LastThreadCount = 0;
for (i=0; i<AFP_MAX_THREADS; i++)
{
if (AfpThreadPtrsW[i] != NULL)
{
pLastThrdPtr = AfpThreadPtrsW[i];
LastThreadCount++;
if (LastThreadCount > 1)
{
Status = AfpIoWait(pLastThrdPtr, &FiveSecTimeOut);
break;
}
}
}
if ((LastThreadCount == 1) || (LastThreadCount == 0))
{
break;
}
} while (TRUE);
// wait on the last thread pointer. When that thread quits, we are signaled. This
// is the surest way of knowing that the thread has really really died
if (pLastThrdPtr)
{
do
{
Status = AfpIoWait(pLastThrdPtr, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("afpFsdUnloadServer: Timeout Waiting for last threads, re-waiting\n"));
}
} while (Status == STATUS_TIMEOUT);
ObDereferenceObject(pLastThrdPtr);
}
KeRundownQueue(&AfpDelAllocQueue);
KeRundownQueue(&AfpWorkerQueue);
// Close the cloned process token
if (AfpFspToken != NULL)
NtClose(AfpFspToken);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpFsdUnloadServer: De-initializing sub-systems\n"));
// De-initialize all sub-systems now
AfpDeinitializeSubsystems();
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpFsdUnloadServer: Deleting device\n"));
// Destroy the DeviceObject for our device
IoDeleteDevice(AfpDeviceObject);
#ifdef PROFILING
ASSERT(AfpServerProfile->perf_cAllocatedIrps == 0);
ASSERT(AfpServerProfile->perf_cAllocatedMdls == 0);
ExFreePool(AfpServerProfile);
#endif
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO, ("Current Sessions = %ld\n NonPaged usage = %ld\n CurrPagedUsage = %ld \n CurrentFileLocks = %ld \n CurrentFileOpen = %ld \n CurrentInternalOpens = %ld, NotifyBlockCount = %ld, NotifyCount = %d \n",
AfpServerStatistics.stat_CurrentSessions,
AfpServerStatistics.stat_CurrNonPagedUsage,
AfpServerStatistics.stat_CurrPagedUsage,
AfpServerStatistics.stat_CurrentFileLocks,
AfpServerStatistics.stat_CurrentFilesOpen,
AfpServerStatistics.stat_CurrentInternalOpens,
afpNotifyBlockAllocCount,
afpNotifyAllocCount
));
// Make sure we do not have resource leaks
ASSERT(AfpServerStatistics.stat_CurrentSessions == 0);
ASSERT(AfpServerStatistics.stat_CurrNonPagedUsage == 0);
ASSERT(AfpServerStatistics.stat_CurrPagedUsage == 0);
ASSERT(AfpServerStatistics.stat_CurrentFileLocks == 0);
ASSERT(AfpServerStatistics.stat_CurrentFilesOpen == 0);
ASSERT(AfpServerStatistics.stat_CurrentInternalOpens == 0);
ASSERT (AfpLockCount == 0);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO, ("afpFsdUnloadServer Done\n"));
// Give the worker threads a chance to really, really die
AfpSleepAWhile(1000);
}
/*** afpAdminThread
*
* This thread is used to do all the work of the queued admin threads.
*
* LOCKS: AfpServerGlobalLock (SPIN)
*/
LOCAL VOID
afpAdminThread(
IN PVOID pContext
)
{
PLIST_ENTRY pList;
PWORK_ITEM pWI;
ULONG BasePriority;
NTSTATUS Status;
AfpThread = PsGetCurrentThread();
IoSetThreadHardErrorMode( FALSE );
// Boost our priority to just below low realtime.
// The idea is get the work done fast and get out of the way.
BasePriority = LOW_REALTIME_PRIORITY;
Status = NtSetInformationThread(NtCurrentThread(),
ThreadBasePriority,
&BasePriority,
sizeof(BasePriority));
ASSERT(NT_SUCCESS(Status));
do
{
// Wait for admin request to process.
pList = KeRemoveQueue(&AfpAdminQueue,
KernelMode, // Do not let the kernel stack be paged
NULL);
ASSERT(Status == STATUS_SUCCESS);
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
pWI = CONTAINING_RECORD(pList, WORK_ITEM, wi_List);
if (pWI == &AfpTerminateThreadWI)
{
break;
}
(*pWI->wi_Worker)(pWI->wi_Context);
INTERLOCKED_ADD_ULONG(&AfpWorkerRequests, (ULONG)-1, &AfpServerGlobalLock);
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
} while (True);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO, ("afpAdminThread: Quitting\n"));
KeSetEvent(&AfpStopConfirmEvent, IO_NETWORK_INCREMENT, False);
}
/*** afpStartStopAdminRequest
*
* Called whenever an admin request is started/stopped. The admin code is locked
* or unlocked accordingly.
*/
LOCAL VOID
afpStartStopAdminRequest(
IN PIRP pIrp,
IN BOOLEAN Start
)
{
// EnterCriticalSection
AfpIoWait(&AfpPgLkMutex, NULL);
ASSERT (AfpLockHandle != NULL);
if (Start)
{
if (AfpLockCount == 0)
{
MmLockPagableSectionByHandle(AfpLockHandle);
}
AfpLockCount ++;
pIrp->IoStatus.Status = STATUS_PENDING;
}
else
{
ASSERT (AfpLockCount > 0);
AfpLockCount --;
if (AfpLockCount == 0)
{
MmUnlockPagableImageSection(AfpLockHandle);
}
}
// LeaveCriticalSection
KeReleaseMutex(&AfpPgLkMutex, False);
if (!Start)
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
}
/*** afpFsdHandleShutdownRequest
*
* This is the shutdown request handler. All sessions are shutdown and volumes
* flushed.
*/
LOCAL NTSTATUS
afpFsdHandleShutdownRequest(
IN PIRP pIrp
)
{
PADMQREQ pAdmQReq;
NTSTATUS Status;
if ((pAdmQReq =
(PADMQREQ)AfpAllocNonPagedMemory(sizeof(ADMQREQ))) == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
PWORK_ITEM pWI = &pAdmQReq->aqr_WorkItem;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("afpFsdHandleShutdownRequest: Queuing to worker\n"));
AfpInitializeWorkItem(&pAdmQReq->aqr_WorkItem,
afpHandleQueuedAdminRequest,
pAdmQReq);
pAdmQReq->aqr_AdminApiWorker = AfpAdmSystemShutdown;
pAdmQReq->aqr_pIrp = pIrp;
// Insert item in admin queue
KeInsertQueue(&AfpAdminQueue, &pAdmQReq->aqr_WorkItem.wi_List);
Status = STATUS_PENDING;
}
return Status;
}
/*** DriverEntry
*
* This is the initialization routine for the AFP server file
* system driver. This routine creates the device object for the
* AfpServer device and performs all other driver initialization.
*/
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
UNICODE_STRING DeviceName;
LONG i;
NTSTATUS Status;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AFP Server Fsd initialization started\n"));
//
// Initialize global data event log insertion strings
//
KeInitializeQueue(&AfpDelAllocQueue, 0);
KeInitializeQueue(&AfpWorkerQueue, 0);
KeInitializeQueue(&AfpAdminQueue, 0);
AfpProcessObject = IoGetCurrentProcess();
Status = AfpInitializeDataAndSubsystems();
if (!NT_SUCCESS(Status))
{
return Status;
}
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AFP Server Fsd Data initialized %lx\n", Status));
// Create the device object. (IoCreateDevice zeroes the memory
// occupied by the object.)
//
// Should we apply an ACL to the device object ?
RtlInitUnicodeString(&DeviceName, AFPSERVER_DEVICE_NAME);
Status = IoCreateDevice(DriverObject, // DriverObject
0, // DeviceExtension
&DeviceName, // DeviceName
FILE_DEVICE_NETWORK, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
False, // Exclusive
&AfpDeviceObject); // DeviceObject
if (!NT_SUCCESS(Status))
{
// Do not errorlog here since logging uses the device object
AfpDeinitializeSubsystems();
return Status;
}
do
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("DriverEntry: Creating Admin Thread\n"));
// Create the Admin thread. This handles all queued operations
Status = AfpCreateNewThread(afpAdminThread, 0);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_FATAL,
("afpInitServer: Admin Thread creation failed %lx\n", Status));
break;
}
AfpNumAdminThreads = 1;
for (i = 0; i < NUM_NOTIFY_QUEUES; i++)
{
// Initialize volume change notify queue
KeInitializeQueue(&AfpVolumeNotifyQueue[i], 0);
// Start a thread to process change notifies
Status = AfpCreateNewThread(AfpChangeNotifyThread, i);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_FATAL,
("afpInitServer: Notify Thread %d, creation failed %lx\n", i+1, Status));
break;
}
}
if (!NT_SUCCESS(Status))
{
for (--i; i >= 0; i--)
{
KeClearEvent(&AfpStopConfirmEvent);
KeInsertQueue(&AfpVolumeNotifyQueue[i], &AfpTerminateNotifyThread.vn_List);
AfpIoWait(&AfpStopConfirmEvent, NULL);
}
break;
}
AfpNumNotifyThreads = NUM_NOTIFY_QUEUES;
for (i = 0; i < AFP_MIN_THREADS; i++)
{
AfpThreadState[i] = AFP_THREAD_STARTED;
Status = AfpCreateNewThread(AfpWorkerThread, i);
if (!NT_SUCCESS(Status))
{
AfpThreadState[i] = AFP_THREAD_DEAD;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_FATAL,
("afpInitServer: Thread creation failed %d\n", i+1));
if (i > 0)
{
KeClearEvent(&AfpStopConfirmEvent);
KeInsertQueue(&AfpWorkerQueue, &AfpTerminateThreadWI.wi_List);
AfpIoWait(&AfpStopConfirmEvent, NULL);
}
break;
}
#if DBG
AfpSleepAWhile(50); // Make it so threads do not time out together
// Helps with debugging
#endif
}
AfpNumThreads = AFP_MIN_THREADS;
if (!NT_SUCCESS(Status))
break;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AFP Server Fsd initialization completed %lx\n", Status));
// initialize DSI specific things
DsiInit();
Status = AfpTdiRegister();
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("TdiRegisterNotificationHandler failed %lx\n", Status));
break;
}
Status = afpInitServer();
if (NT_SUCCESS(Status))
{
// Initialize the driver object for this file system driver.
DriverObject->DriverUnload = afpFsdUnloadServer;
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = afpFsdDispatchAdminRequest;
}
// Register for shutdown notification. We don't care if this fails.
Status = IoRegisterShutdownNotification(AfpDeviceObject);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR,
("Afp Server Fsd: IoRegisterShutdownNotification failed %lx\n", Status));
}
Status = STATUS_SUCCESS;
}
} while (False);
if (!NT_SUCCESS(Status))
{
afpFsdUnloadServer(DriverObject);
Status = STATUS_UNSUCCESSFUL;
}
KeClearEvent(&AfpStopConfirmEvent);
return Status;
}
/*** afpInitServer
*
* Initialize the AFP Server. This happens on FSD initialization.
* The initialization consists of the following steps.
*
* - Create a socket on the appletalk stack.
* - Create a token for ourselves.
* - Initialize security
* - Open the Authentication pacakage
*
* Note: Any errorlogging done from here must use AFPLOG_DDERROR since we
* will not have a usermode thread to do our errorlogging if anything
* goes wrong here.
*/
NTSTATUS
afpInitServer(
VOID
)
{
NTSTATUS Status;
ANSI_STRING LogonProcessName;
ULONG OldSize;
HANDLE ProcessToken;
TOKEN_PRIVILEGES ProcessPrivileges, PreviousPrivilege;
OBJECT_ATTRIBUTES ObjectAttr;
UNICODE_STRING PackageName;
WCHAR PkgBuf[5];
TimeStamp Expiry; // unused on the server side (i.e. us)
InitSecurityInterface();
do
{
// Open our socket on the ASP Device. Implicitly checks out the
// Appletalk stack
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpInitServer: Initializing Atalk\n"));
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("afpInitServer: Creating token\n"));
// Clone the system process token and add the required privilges that
// we need. This token will be used to impersonate when we set permissions
Status = NtOpenProcessToken(NtCurrentProcess(),
TOKEN_ALL_ACCESS,
&ProcessToken);
if (!NT_SUCCESS(Status))
{
AFPLOG_DDERROR(AFPSRVMSG_PROCESS_TOKEN, Status, NULL, 0, NULL);
break;
}
InitializeObjectAttributes(&ObjectAttr, NULL, 0, NULL, NULL);
ObjectAttr.SecurityQualityOfService = &AfpSecurityQOS;
Status = NtDuplicateToken(ProcessToken,
TOKEN_ALL_ACCESS,
&ObjectAttr,
False,
TokenImpersonation,
&AfpFspToken);
NtClose(ProcessToken);
if (!NT_SUCCESS(Status))
{
AFPLOG_DDERROR(AFPSRVMSG_PROCESS_TOKEN, Status, NULL, 0, NULL);
break;
}
ProcessPrivileges.PrivilegeCount = 1L;
ProcessPrivileges.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_USED_FOR_ACCESS;
ProcessPrivileges.Privileges[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
Status = NtAdjustPrivilegesToken(AfpFspToken,
False,
&ProcessPrivileges,
sizeof(TOKEN_PRIVILEGES),
&PreviousPrivilege,
&OldSize);
if (!NT_SUCCESS(Status))
{
AFPLOG_DDERROR(AFPSRVMSG_PROCESS_TOKEN, Status, NULL, 0, NULL);
break;
}
PackageName.Length = 8;
PackageName.Buffer = (LPWSTR)PkgBuf;
RtlCopyMemory( PackageName.Buffer, NTLMSP_NAME, 8);
Status = AcquireCredentialsHandle(NULL, // Default principal
(PSECURITY_STRING)&PackageName,
SECPKG_CRED_INBOUND,
NULL,
NULL,
NULL,
(PVOID) NULL,
&AfpSecHandle,
&Expiry);
if(!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("AfpInitServer: AcquireCredentialsHandle() failed with %X\n", Status));
ASSERT(0);
if (AfpFspToken != NULL)
{
NtClose(AfpFspToken);
AfpFspToken = NULL;
}
break;
}
// Finally obtain a handle to our conditionally locked section
AfpLockHandle = MmLockPagableCodeSection((PVOID)AfpAdmWServerSetInfo);
MmUnlockPagableImageSection(AfpLockHandle);
} while (False);
return Status;
}