/*++ 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 #define AFPADMIN_LOCALS #include #include #include #include #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 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; }