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.
1150 lines
30 KiB
1150 lines
30 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|