965 lines
23 KiB
C
965 lines
23 KiB
C
|
/*
|
||
|
|
||
|
Copyright (c) 1992 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
sda.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains the session data area manipulation routines.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Jameel Hyder (microsoft!jameelh)
|
||
|
|
||
|
|
||
|
Revision History:
|
||
|
25 Apr 1992 Initial Version
|
||
|
|
||
|
Notes: Tab stop: 4
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#define _SDA_LOCALS
|
||
|
#define FILENUM FILE_SDA
|
||
|
|
||
|
#include <afp.h>
|
||
|
#include <scavengr.h>
|
||
|
#include <access.h>
|
||
|
#include <client.h>
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text( INIT, AfpSdaInit)
|
||
|
#pragma alloc_text( PAGE, afpCloseSessionAndFreeSda)
|
||
|
#pragma alloc_text( PAGE, AfpSdaCheckSession)
|
||
|
#endif
|
||
|
|
||
|
/*** AfpSdaInit
|
||
|
*
|
||
|
* Initialize Sda Data structures. Called at init time.
|
||
|
*/
|
||
|
NTSTATUS
|
||
|
AfpSdaInit(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
ASSERT(sizeof(SDA) <= 512);
|
||
|
INITIALIZE_SPIN_LOCK(&AfpSdaLock);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpSdaReferenceSessionById
|
||
|
*
|
||
|
* Reference a session by its id. The SDA cannot be referenced if it is
|
||
|
* marked closed. The SDA should be referenced before using it.
|
||
|
*
|
||
|
* LOCKS: AfpSdaLock (SPIN), sda_Lock (SPIN)
|
||
|
* LOCK_ORDER: sda_Lock after AfpSdaLock
|
||
|
*/
|
||
|
PSDA FASTCALL
|
||
|
AfpSdaReferenceSessionById(
|
||
|
IN DWORD SessId
|
||
|
)
|
||
|
{
|
||
|
PSDA pSda=NULL;
|
||
|
PSDA pRetSda=NULL;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
for (pSda = AfpSessionList;
|
||
|
(pSda != NULL) && (pSda->sda_SessionId >= SessId);
|
||
|
pSda = pSda->sda_Next)
|
||
|
{
|
||
|
if (pSda->sda_SessionId == SessId)
|
||
|
{
|
||
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pSda->sda_Lock);
|
||
|
|
||
|
if ((pSda->sda_Flags & SDA_CLOSING) == 0)
|
||
|
{
|
||
|
ASSERT((pSda->sda_RefCount > 0) &&
|
||
|
(pSda->sda_SessionId != 0));
|
||
|
|
||
|
pSda->sda_RefCount ++;
|
||
|
pRetSda = pSda;
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("AfpSdaReferenceSessionById: New Count %d\n", pSda->sda_RefCount));
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
return pRetSda;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpSdaReferenceSessionByPointer
|
||
|
*
|
||
|
* Reference a session by pointer. The SDA cannot be referenced if it is
|
||
|
* marked closed. The SDA should be referenced before using it.
|
||
|
*
|
||
|
*/
|
||
|
PSDA FASTCALL
|
||
|
AfpSdaReferenceSessionByPointer(
|
||
|
IN PSDA pSda
|
||
|
)
|
||
|
{
|
||
|
PSDA pRetSda=NULL;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
|
||
|
ASSERT(VALID_SDA(pSda));
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
||
|
|
||
|
if ((pSda->sda_Flags & SDA_CLOSING) == 0)
|
||
|
{
|
||
|
pSda->sda_RefCount ++;
|
||
|
pRetSda = pSda;
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
||
|
|
||
|
return pRetSda;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*** afpSdaCloseSessionAndFreeSda
|
||
|
*
|
||
|
* Run by the scavenger in the worker context. This is initiated as part of
|
||
|
* session cleanup.
|
||
|
*/
|
||
|
LOCAL AFPSTATUS FASTCALL
|
||
|
afpCloseSessionAndFreeSda(
|
||
|
IN PSDA pSda
|
||
|
)
|
||
|
{
|
||
|
POPENFORKSESS pOpenForkSess;
|
||
|
PASP_XPORT_ENTRIES XportTable;
|
||
|
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("afpCloseSessionAndFreeSda: Cleaning up session %lx\n",
|
||
|
pSda->sda_SessionId));
|
||
|
|
||
|
PAGED_CODE( );
|
||
|
|
||
|
#ifdef OPTIMIZE_GUEST_LOGONS
|
||
|
if (pSda->sda_ClientType != SDA_CLIENT_GUEST)
|
||
|
{
|
||
|
#endif
|
||
|
#ifndef INHERIT_DIRECTORY_PERMS
|
||
|
// First free up paged memory used by the sda
|
||
|
if (pSda->sda_pSecDesc != NULL)
|
||
|
{
|
||
|
if (pSda->sda_pSecDesc->Dacl != NULL)
|
||
|
AfpFreeMemory(pSda->sda_pSecDesc->Dacl);
|
||
|
AfpFreeMemory(pSda->sda_pSecDesc);
|
||
|
}
|
||
|
#endif
|
||
|
if ((pSda->sda_UserSid != NULL) && (pSda->sda_UserSid != &AfpSidWorld))
|
||
|
AfpFreeMemory(pSda->sda_UserSid);
|
||
|
|
||
|
if ((pSda->sda_GroupSid != NULL) && (pSda->sda_GroupSid != &AfpSidWorld))
|
||
|
AfpFreeMemory(pSda->sda_GroupSid);
|
||
|
|
||
|
if (pSda->sda_pGroups != NULL)
|
||
|
AfpFreeMemory(pSda->sda_pGroups);
|
||
|
#ifdef OPTIMIZE_GUEST_LOGONS
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Free any chains of fork entries that we created
|
||
|
pOpenForkSess = pSda->sda_OpenForkSess.ofs_Link;
|
||
|
while (pOpenForkSess != NULL)
|
||
|
{
|
||
|
POPENFORKSESS pFree;
|
||
|
|
||
|
pFree = pOpenForkSess;
|
||
|
pOpenForkSess = pOpenForkSess->ofs_Link;
|
||
|
AfpFreeMemory(pFree);
|
||
|
}
|
||
|
|
||
|
// Next log-out the user
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("afpCloseSessionAndFreeSda: Closing User Token\n"));
|
||
|
|
||
|
#ifdef OPTIMIZE_GUEST_LOGON
|
||
|
if ((pSda->sda_UserToken != NULL) && (pSda->sda_ClientType != SDA_CLIENT_GUEST))
|
||
|
#else
|
||
|
if (pSda->sda_UserToken != NULL)
|
||
|
#endif
|
||
|
NtClose(pSda->sda_UserToken);
|
||
|
|
||
|
// Make sure there are no resource leaks
|
||
|
ASSERT (pSda->sda_cOpenVolumes == 0);
|
||
|
ASSERT (pSda->sda_pConnDesc == 0);
|
||
|
ASSERT (pSda->sda_cOpenForks == 0);
|
||
|
|
||
|
XportTable = pSda->sda_XportTable;
|
||
|
|
||
|
(*(XportTable->asp_FreeConn))(pSda->sda_SessHandle);
|
||
|
|
||
|
if (pSda->sda_ReplyBuf)
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_ERR,
|
||
|
("afpCloseSessionAndFreeSda: %d bytes would have leaked!\n",pSda->sda_ReplySize));
|
||
|
|
||
|
// TRUE => lie that the lock is held: we don't need lock no more
|
||
|
AfpFreeReplyBuf(pSda, TRUE);
|
||
|
}
|
||
|
|
||
|
// Free the sda memory, finally
|
||
|
AfpFreeMemory(pSda);
|
||
|
|
||
|
|
||
|
// If the server is stopping and the count of sessions has gone to zero
|
||
|
// clear the termination confirmation event to unblock the admin thread
|
||
|
if (((AfpServerState == AFP_STATE_STOP_PENDING) ||
|
||
|
(AfpServerState == AFP_STATE_SHUTTINGDOWN)) &&
|
||
|
(AfpNumSessions == 0))
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_WARN,
|
||
|
("afpSdaCloseSessionAndFreeSda: Unblocking server stop\n"));
|
||
|
KeSetEvent(&AfpStopConfirmEvent, IO_NETWORK_INCREMENT, False);
|
||
|
}
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO, ("afpCloseSessionAndFreeSda: Done\n"));
|
||
|
|
||
|
return AFP_ERR_NONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*** AfpSdaDereferenceSession
|
||
|
*
|
||
|
* Dereference this SDA. This is called in response to either a AfpLogout
|
||
|
* request, a forced shutdown of this session by either SessionClose() api,
|
||
|
* the server shutdown or the session shutdown from the client end.
|
||
|
* The Sda should not be dereferenced until the entire request is processed
|
||
|
* i.e. the reply has ALSO COMPLETED.
|
||
|
*
|
||
|
* NOTE: We unlink the session from the list but do not update the count till
|
||
|
* the entire cleanup is complete.
|
||
|
*
|
||
|
* LOCKS: AfpSdaLock (SPIN), sda_Lock (SPIN), AfpStatisticsLock (SPIN)
|
||
|
*/
|
||
|
VOID FASTCALL
|
||
|
AfpSdaDereferenceSession(
|
||
|
IN PSDA pSda
|
||
|
)
|
||
|
{
|
||
|
PSDA * ppSda;
|
||
|
KIRQL OldIrql;
|
||
|
LONG RefCount;
|
||
|
BOOLEAN fTcpSession=TRUE;
|
||
|
|
||
|
ASSERT(VALID_SDA(pSda));
|
||
|
ASSERT((pSda->sda_RefCount > 0) &&
|
||
|
(pSda->sda_SessionId != 0));
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
||
|
RefCount = -- (pSda->sda_RefCount);
|
||
|
if (RefCount == 0)
|
||
|
{
|
||
|
pSda->sda_Flags |= SDA_CLOSING;
|
||
|
|
||
|
fTcpSession = (pSda->sda_Flags & SDA_SESSION_OVER_TCP)? TRUE : FALSE;
|
||
|
}
|
||
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
||
|
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("AfpSdaDereferenceSession: Session %ld, New Count %ld\n",
|
||
|
pSda->sda_SessionId, RefCount));
|
||
|
|
||
|
// If there are more references, then we are done.
|
||
|
if (RefCount > 0)
|
||
|
return;
|
||
|
|
||
|
ASSERT(RefCount == 0); // catch refcount going below 0!
|
||
|
|
||
|
ASSERT (!(pSda->sda_Flags & SDA_REQUEST_IN_PROCESS));
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("AfpSdaDereferenceSession: Closing down session %ld\n",
|
||
|
pSda->sda_SessionId));
|
||
|
|
||
|
// Cancel the scavenger event for checking this user's kickoff time
|
||
|
AfpScavengerKillEvent(AfpSdaCheckSession,
|
||
|
(PVOID)((ULONG_PTR)(pSda->sda_SessionId)));
|
||
|
|
||
|
// First unlink the Sda from the sessions list
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
for (ppSda = &AfpSessionList;
|
||
|
*ppSda != NULL;
|
||
|
ppSda = &(*ppSda)->sda_Next)
|
||
|
if (pSda == *ppSda)
|
||
|
break;
|
||
|
|
||
|
ASSERT(*ppSda == pSda);
|
||
|
|
||
|
*ppSda = pSda->sda_Next;
|
||
|
|
||
|
// Update the count of active sessions
|
||
|
ASSERT (AfpNumSessions > 0);
|
||
|
--AfpNumSessions;
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
// Free all buffers associated with the Sda
|
||
|
// Now free the SDA memory
|
||
|
if (pSda->sda_WSName.Buffer != NULL)
|
||
|
AfpFreeMemory(pSda->sda_WSName.Buffer);
|
||
|
|
||
|
if ((pSda->sda_ClientType != SDA_CLIENT_GUEST) &&
|
||
|
(pSda->sda_UserName.Buffer != NULL))
|
||
|
AfpFreeMemory(pSda->sda_UserName.Buffer);
|
||
|
|
||
|
if (pSda->sda_Challenge != NULL)
|
||
|
AfpFreeMemory(pSda->sda_Challenge);
|
||
|
|
||
|
if (pSda->sda_DomainName.Buffer != NULL)
|
||
|
AfpFreeMemory(pSda->sda_DomainName.Buffer);
|
||
|
|
||
|
if (pSda->sda_Message != NULL)
|
||
|
AfpFreeMemory(pSda->sda_Message);
|
||
|
|
||
|
// Finally update statistics
|
||
|
INTERLOCKED_ADD_ULONG((PLONG)&AfpServerStatistics.stat_CurrentSessions,
|
||
|
(ULONG)-1,
|
||
|
&AfpStatisticsLock);
|
||
|
|
||
|
// it was a TCP session? update that counter as well
|
||
|
if (fTcpSession)
|
||
|
{
|
||
|
INTERLOCKED_ADD_ULONG((PLONG)&AfpServerStatistics.stat_TcpSessions,
|
||
|
(ULONG)-1,
|
||
|
&AfpStatisticsLock);
|
||
|
}
|
||
|
|
||
|
// The balance of the clean-up has to happen at LOW_LEVEL in the context
|
||
|
// of the worker thread. So queue it up
|
||
|
if ((OldIrql == DISPATCH_LEVEL) ||
|
||
|
(PsGetCurrentProcess() != AfpProcessObject))
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("AfpSdaDereferenceSession: Queuing Close&Free to Scavenger\n"));
|
||
|
|
||
|
AfpScavengerScheduleEvent(afpCloseSessionAndFreeSda,
|
||
|
(PVOID)pSda,
|
||
|
0,
|
||
|
True);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
afpCloseSessionAndFreeSda(pSda);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*** AfpSdaCreateNewSession
|
||
|
*
|
||
|
* A new session has been initiated. Allocate and initialize a Sda and return a
|
||
|
* pointer to it. The new sda is linked into the list of active sessions.
|
||
|
*
|
||
|
* LOCKS: AfpSdaLock (SPIN), AfpServerGlobalLock (SPIN)
|
||
|
* LOCK_ORDER: AfpServerGlobalLock after AfpSdaLock
|
||
|
*/
|
||
|
PSDA FASTCALL
|
||
|
AfpSdaCreateNewSession(
|
||
|
IN PVOID SessionHandle,
|
||
|
IN BOOLEAN fOverTcp
|
||
|
)
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
PSDA *ppSda, pSda = NULL;
|
||
|
|
||
|
|
||
|
if ((DWORD)AfpNumSessions < AfpServerMaxSessions)
|
||
|
{
|
||
|
if ((pSda = (PSDA)AfpAllocNonPagedMemory(SDA_SIZE)) == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
// Initialize SDA fields
|
||
|
RtlZeroMemory(pSda, sizeof(SDA));
|
||
|
#if DBG
|
||
|
pSda->Signature = SDA_SIGNATURE;
|
||
|
#endif
|
||
|
INITIALIZE_SPIN_LOCK(&pSda->sda_Lock);
|
||
|
|
||
|
// Reference this Session
|
||
|
pSda->sda_RefCount = 1;
|
||
|
|
||
|
pSda->sda_Flags = SDA_USER_NOT_LOGGEDIN;
|
||
|
|
||
|
if (fOverTcp)
|
||
|
{
|
||
|
pSda->sda_Flags |= SDA_SESSION_OVER_TCP;
|
||
|
pSda->sda_XportTable = &AfpDsiEntries;
|
||
|
pSda->sda_MaxWriteSize = DSI_SERVER_REQUEST_QUANTUM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pSda->sda_XportTable = &AfpAspEntries;
|
||
|
pSda->sda_MaxWriteSize = ASP_QUANTUM;
|
||
|
}
|
||
|
|
||
|
pSda->sda_SessHandle = SessionHandle;
|
||
|
|
||
|
InitializeListHead(&pSda->sda_DeferredQueue);
|
||
|
AfpInitializeWorkItem(&pSda->sda_WorkItem,
|
||
|
AfpStartApiProcessing,
|
||
|
pSda);
|
||
|
AfpGetCurrentTimeInMacFormat(&pSda->sda_TimeLoggedOn);
|
||
|
pSda->sda_tTillKickOff = MAXLONG;
|
||
|
|
||
|
// Initialize space for request/response
|
||
|
pSda->sda_SizeNameXSpace = (USHORT)(SDA_SIZE - sizeof(SDA));
|
||
|
pSda->sda_NameXSpace = (PBYTE)pSda + sizeof(SDA);
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
// Assign a new session id to this session
|
||
|
pSda->sda_SessionId = afpNextSessionId ++;
|
||
|
|
||
|
ASSERT(pSda->sda_SessionId != 0);
|
||
|
|
||
|
// Link the Sda into the active session list
|
||
|
for (ppSda = &AfpSessionList;
|
||
|
*ppSda != NULL;
|
||
|
ppSda = &((*ppSda)->sda_Next))
|
||
|
{
|
||
|
if ((*ppSda)->sda_SessionId < pSda->sda_SessionId)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pSda->sda_Next = *ppSda;
|
||
|
*ppSda = pSda;
|
||
|
|
||
|
// Update the count of active sessions
|
||
|
AfpNumSessions++;
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
// Finally update statistics
|
||
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
||
|
AfpServerStatistics.stat_CurrentSessions ++;
|
||
|
AfpServerStatistics.stat_TotalSessions ++;
|
||
|
if (AfpServerStatistics.stat_CurrentSessions >
|
||
|
AfpServerStatistics.stat_MaxSessions)
|
||
|
AfpServerStatistics.stat_MaxSessions =
|
||
|
AfpServerStatistics.stat_CurrentSessions;
|
||
|
if (fOverTcp)
|
||
|
{
|
||
|
AfpServerStatistics.stat_TcpSessions++;
|
||
|
|
||
|
if (AfpServerStatistics.stat_TcpSessions >
|
||
|
AfpServerStatistics.stat_MaxTcpSessions)
|
||
|
{
|
||
|
AfpServerStatistics.stat_MaxTcpSessions =
|
||
|
AfpServerStatistics.stat_TcpSessions;
|
||
|
}
|
||
|
}
|
||
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
||
|
}
|
||
|
|
||
|
return pSda;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpSdaCloseSession
|
||
|
*
|
||
|
* Setup to close the session
|
||
|
*/
|
||
|
AFPSTATUS FASTCALL
|
||
|
AfpSdaCloseSession(
|
||
|
IN PSDA pSda
|
||
|
)
|
||
|
{
|
||
|
LONG ConnRef;
|
||
|
DWORD ForkRef, MaxOForkRefNum;
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("AfpSdaCloseSession: Entered for session %lx\n",
|
||
|
pSda->sda_SessionId));
|
||
|
|
||
|
// We should be coming in from the scavenger worker in its context i.e. at LOW_LEVEL.
|
||
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
||
|
|
||
|
// Close down the open forks from this session
|
||
|
MaxOForkRefNum = pSda->sda_MaxOForkRefNum;
|
||
|
for (ForkRef = 1; ForkRef <= MaxOForkRefNum; ForkRef++)
|
||
|
{
|
||
|
POPENFORKENTRY pOpenForkEntry;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
||
|
pOpenForkEntry = AfpForkReferenceByRefNum(pSda, ForkRef);
|
||
|
KeLowerIrql(OldIrql);
|
||
|
|
||
|
if (pOpenForkEntry != NULL)
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_VOLUME, DBG_LEVEL_INFO,
|
||
|
("AfpSdaCloseSession: Forcing close of fork %lx, id %lx\n",
|
||
|
ForkRef, pOpenForkEntry->ofe_ForkId));
|
||
|
|
||
|
AfpForkClose(pOpenForkEntry);
|
||
|
|
||
|
AfpForkDereference(pOpenForkEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now close down all open volumes.
|
||
|
// Note that AfpConnectionClose will unlink the ConnDesc from the Sda
|
||
|
for (ConnRef = 1; ConnRef <= afpLargestVolIdInUse; ConnRef++)
|
||
|
{
|
||
|
PCONNDESC pConnDesc;
|
||
|
|
||
|
if ((pConnDesc = AfpConnectionReference(pSda, ConnRef)) != NULL)
|
||
|
{
|
||
|
AfpConnectionClose(pConnDesc);
|
||
|
AfpConnectionDereference(pConnDesc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO, ("AfpCloseSession: Done\n"));
|
||
|
|
||
|
ASSERT (pSda->sda_Flags & SDA_SESSION_CLOSED);
|
||
|
|
||
|
AfpSdaDereferenceSession(pSda);
|
||
|
|
||
|
return AFP_ERR_NONE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpAdmWSessionClose
|
||
|
*
|
||
|
* Close a session forcibly. This is an admin operation and must be queued
|
||
|
* up since this can potentially cause filesystem operations that are valid
|
||
|
* only in the system process context.
|
||
|
*
|
||
|
* LOCKS: AfpSdaLock (SPIN), sda_Lock (SPIN)
|
||
|
* LOCK_ORDER: sda_Lock after AfpSdaLock
|
||
|
*/
|
||
|
NTSTATUS
|
||
|
AfpAdmWSessionClose(
|
||
|
IN OUT PVOID InBuf OPTIONAL,
|
||
|
IN LONG OutBufLen OPTIONAL,
|
||
|
OUT PVOID OutBuf OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
AFPSTATUS Status = AFPERR_InvalidId;
|
||
|
DWORD SessId;
|
||
|
PSDA pSda;
|
||
|
KIRQL OldIrql;
|
||
|
PAFP_SESSION_INFO pSessInfo = (PAFP_SESSION_INFO)InBuf;
|
||
|
USHORT AttnWord;
|
||
|
BOOLEAN Shoot = False;
|
||
|
|
||
|
AttnWord = ATTN_USER_DISCONNECT;
|
||
|
if ((AfpServerState == AFP_STATE_SHUTTINGDOWN) ||
|
||
|
(AfpServerState == AFP_STATE_STOP_PENDING))
|
||
|
AttnWord = ATTN_SERVER_SHUTDOWN;
|
||
|
|
||
|
if ((SessId = pSessInfo->afpsess_id) != 0)
|
||
|
{
|
||
|
if ((pSda = AfpSdaReferenceSessionById(SessId)) != NULL)
|
||
|
{
|
||
|
Status = AFP_ERR_NONE;
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
||
|
|
||
|
if ((pSda->sda_Flags & (SDA_CLOSING | SDA_SESSION_CLOSED | SDA_CLIENT_CLOSE)) == 0)
|
||
|
{
|
||
|
Shoot = True;
|
||
|
pSda->sda_Flags |= SDA_CLOSING | SDA_SESSION_CLOSED;
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
||
|
|
||
|
if (Shoot)
|
||
|
{
|
||
|
// Tell the client (s)he's being shot
|
||
|
AfpSpSendAttention(pSda, AttnWord, True);
|
||
|
|
||
|
AfpSpCloseSession(pSda);
|
||
|
}
|
||
|
AfpSdaDereferenceSession(pSda);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_ERR,
|
||
|
("AfpAdmWSessionClose: no pSda found for SessId %d\n",pSessInfo->afpsess_id));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PSDA pSdaNext;
|
||
|
|
||
|
// Here we want to block incoming sessions while we kill the existing ones
|
||
|
AfpSpDisableListens();
|
||
|
|
||
|
Status = AFP_ERR_NONE;
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
for (pSda = AfpSessionList; pSda != NULL; pSda = pSdaNext)
|
||
|
{
|
||
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pSda->sda_Lock);
|
||
|
|
||
|
Shoot = False;
|
||
|
pSdaNext = pSda->sda_Next;
|
||
|
if ((pSda->sda_Flags & (SDA_CLOSING | SDA_SESSION_CLOSED | SDA_CLIENT_CLOSE)) == 0)
|
||
|
{
|
||
|
pSda->sda_Flags |= SDA_CLOSING | SDA_SESSION_CLOSED;
|
||
|
pSda->sda_RefCount ++;
|
||
|
Shoot = True;
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
|
||
|
if (Shoot)
|
||
|
{
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
// Tell the client (s)he's being shot
|
||
|
AfpSpSendAttention(pSda, AttnWord, True);
|
||
|
|
||
|
AfpSpCloseSession(pSda);
|
||
|
|
||
|
AfpSdaDereferenceSession(pSda);
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
pSdaNext = AfpSessionList;
|
||
|
}
|
||
|
}
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
// Enable new incoming sessions - but only if we are not paused
|
||
|
if ((AfpServerState & (AFP_STATE_PAUSED | AFP_STATE_PAUSE_PENDING)) == 0)
|
||
|
{
|
||
|
AfpSpEnableListens();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpSdaCheckSession
|
||
|
*
|
||
|
* Check if a session needs to be forcibly closed. If we are called by the
|
||
|
* scavenger for the first time and our scavenger interval is not
|
||
|
* SESSION_CHECK_TIME, we need to reschedule ourselves to run at this
|
||
|
* interval.
|
||
|
*
|
||
|
* Since the sda_tTillKickOff is only used here, it needs
|
||
|
* no protection.
|
||
|
*/
|
||
|
AFPSTATUS FASTCALL
|
||
|
AfpSdaCheckSession(
|
||
|
IN PVOID pContext
|
||
|
)
|
||
|
{
|
||
|
PSDA pSda;
|
||
|
DWORD SessionId = (DWORD)((ULONG_PTR)pContext);
|
||
|
AFPSTATUS Status = AFP_ERR_REQUEUE;
|
||
|
BOOLEAN RequeueOurself = False;
|
||
|
|
||
|
PAGED_CODE( );
|
||
|
|
||
|
if ((pSda = AfpSdaReferenceSessionById(SessionId)) != NULL)
|
||
|
{
|
||
|
if (pSda->sda_tTillKickOff > SESSION_WARN_TIME)
|
||
|
{
|
||
|
// This is the first time this routine has run, and it was
|
||
|
// originally scheduled to run at a time greater than
|
||
|
// SESSION_WARN_TIME. Therefore, we need to reschedule this
|
||
|
// scavenger event ourselves with a new SESSION_CHECK_TIME
|
||
|
// interval. This is the only time we need to do this.
|
||
|
pSda->sda_tTillKickOff = SESSION_WARN_TIME;
|
||
|
Status = AFP_ERR_NONE;
|
||
|
RequeueOurself = True;
|
||
|
}
|
||
|
else if (pSda->sda_tTillKickOff > 0)
|
||
|
pSda->sda_tTillKickOff -= SESSION_CHECK_TIME;
|
||
|
|
||
|
ASSERT(pSda->sda_tTillKickOff <= SESSION_WARN_TIME);
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_WARN,
|
||
|
("AfpSdaCheckSession: Below warn level %ld\n",
|
||
|
pSda->sda_tTillKickOff));
|
||
|
|
||
|
// If we are at 0, kick this user out. Else just
|
||
|
// send him a friendly warning.
|
||
|
if (pSda->sda_tTillKickOff == 0)
|
||
|
{
|
||
|
AFP_SESSION_INFO SessionInfo;
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_WARN,
|
||
|
("AfpSdaCheckSession: Booting session %ld\n", SessionId));
|
||
|
|
||
|
SessionInfo.afpsess_id = SessionId;
|
||
|
AfpAdmWSessionClose(&SessionInfo, 0, NULL);
|
||
|
Status = AFP_ERR_NONE; // Do not reschedule
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_WARN,
|
||
|
("AfpSdaCheckSession: Warning session %ld\n", SessionId));
|
||
|
|
||
|
AfpSpSendAttention(pSda,
|
||
|
(USHORT)(ATTN_USER_DISCONNECT |
|
||
|
((pSda->sda_tTillKickOff/60) & ATTN_TIME_MASK)),
|
||
|
True);
|
||
|
}
|
||
|
|
||
|
if (RequeueOurself)
|
||
|
{
|
||
|
AfpScavengerScheduleEvent(
|
||
|
AfpSdaCheckSession,
|
||
|
(PVOID)((ULONG_PTR)pSda->sda_SessionId),
|
||
|
SESSION_CHECK_TIME,
|
||
|
True);
|
||
|
}
|
||
|
|
||
|
AfpSdaDereferenceSession(pSda);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpKillSessionsOverProtocol
|
||
|
*
|
||
|
* Kill all the sessions over a specific protocol (TCP/IP or Appletalk)
|
||
|
*/
|
||
|
VOID FASTCALL
|
||
|
AfpKillSessionsOverProtocol(
|
||
|
IN BOOLEAN fAppletalkSessions
|
||
|
)
|
||
|
{
|
||
|
PSDA pSda;
|
||
|
PSDA pSdaNext;
|
||
|
KIRQL OldIrql;
|
||
|
USHORT AttnWord;
|
||
|
|
||
|
|
||
|
AttnWord = ATTN_USER_DISCONNECT;
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
for (pSda = AfpSessionList; pSda != NULL; pSda = pSdaNext)
|
||
|
{
|
||
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pSda->sda_Lock);
|
||
|
|
||
|
pSdaNext = pSda->sda_Next;
|
||
|
|
||
|
//
|
||
|
// if this session is already being closing, skip it
|
||
|
//
|
||
|
if (pSda->sda_Flags & (SDA_CLOSING | SDA_SESSION_CLOSED | SDA_CLIENT_CLOSE))
|
||
|
{
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if we are asked to kill all Appletalk sessions and this session
|
||
|
// over Appletalk, skip it
|
||
|
//
|
||
|
if (fAppletalkSessions)
|
||
|
{
|
||
|
if (pSda->sda_Flags & SDA_SESSION_OVER_TCP)
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,
|
||
|
("AfpKillSessionsOverProtocol: skipping TCP session %lx\n",pSda));
|
||
|
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// we are asked to kill all TCP/IP sessions, and this session is
|
||
|
// over Appletalk: skip it
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
if (!(pSda->sda_Flags & SDA_SESSION_OVER_TCP))
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,
|
||
|
("AfpKillSessionsOverProtocol: skipping Appletalk session %lx\n",pSda));
|
||
|
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
pSda->sda_Flags |= SDA_CLOSING | SDA_SESSION_CLOSED;
|
||
|
|
||
|
// put a DISCONNECT refcount
|
||
|
pSda->sda_RefCount ++;
|
||
|
|
||
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
// give client the bad news
|
||
|
AfpSpSendAttention(pSda, AttnWord, True);
|
||
|
|
||
|
AfpSpCloseSession(pSda);
|
||
|
|
||
|
// and remove that DISCONNECT refcount
|
||
|
AfpSdaDereferenceSession(pSda);
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
|
||
|
|
||
|
// start from the head of the list
|
||
|
pSdaNext = AfpSessionList;
|
||
|
}
|
||
|
|
||
|
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** AfpFreeReplyBuf
|
||
|
*
|
||
|
* Free up the reply buffer
|
||
|
*
|
||
|
*/
|
||
|
VOID
|
||
|
AfpFreeReplyBuf(
|
||
|
IN PSDA pSda,
|
||
|
IN BOOLEAN fLockHeld
|
||
|
)
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
BOOLEAN fRelease=FALSE;
|
||
|
PBYTE pReplyBuffer;
|
||
|
|
||
|
|
||
|
if (!fLockHeld)
|
||
|
{
|
||
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
||
|
fRelease = TRUE;
|
||
|
}
|
||
|
|
||
|
ASSERT(pSda->sda_ReplyBuf != NULL);
|
||
|
|
||
|
pReplyBuffer = pSda->sda_ReplyBuf - DSI_BACKFILL_OFFSET(pSda);
|
||
|
|
||
|
if (pReplyBuffer == pSda->sda_NameXSpace)
|
||
|
{
|
||
|
ASSERT(pSda->sda_Flags & SDA_NAMEXSPACE_IN_USE);
|
||
|
pSda->sda_Flags &= ~SDA_NAMEXSPACE_IN_USE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AfpFreeMemory(pReplyBuffer);
|
||
|
}
|
||
|
|
||
|
pSda->sda_ReplyBuf = NULL;
|
||
|
pSda->sda_ReplySize = 0;
|
||
|
|
||
|
if (fRelease)
|
||
|
{
|
||
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*** afpUpdateDiskQuotaInfo
|
||
|
*
|
||
|
* Get Available disk quota for this user
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
VOID FASTCALL
|
||
|
afpUpdateDiskQuotaInfo(
|
||
|
IN PCONNDESC pConnDesc
|
||
|
)
|
||
|
{
|
||
|
|
||
|
FILE_FS_SIZE_INFORMATION fssizeinfo;
|
||
|
IO_STATUS_BLOCK IoStsBlk;
|
||
|
NTSTATUS rc;
|
||
|
LONG BytesPerAllocationUnit;
|
||
|
LARGE_INTEGER FreeBytes, VolumeSize;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
|
||
|
ASSERT(VALID_CONNDESC(pConnDesc));
|
||
|
|
||
|
ASSERT(VALID_VOLDESC(pConnDesc->cds_pVolDesc));
|
||
|
|
||
|
ASSERT(VALID_VOLDESC(pConnDesc->cds_pVolDesc) &&
|
||
|
VALID_FSH(&pConnDesc->cds_pVolDesc->vds_hRootDir) &&
|
||
|
(KeGetCurrentIrql() < DISPATCH_LEVEL));
|
||
|
|
||
|
AfpImpersonateClient(pConnDesc->cds_pSda);
|
||
|
|
||
|
rc = NtQueryVolumeInformationFile(
|
||
|
pConnDesc->cds_pVolDesc->vds_hRootDir.fsh_FileHandle,
|
||
|
&IoStsBlk,
|
||
|
(PVOID)&fssizeinfo,
|
||
|
sizeof(fssizeinfo),
|
||
|
FileFsSizeInformation);
|
||
|
|
||
|
AfpRevertBack();
|
||
|
|
||
|
if (!NT_SUCCESS(rc))
|
||
|
{
|
||
|
DBGPRINT(DBG_COMP_FILEIO, DBG_LEVEL_ERR,
|
||
|
("afpUpdateDiskQuotaInfo: NtQueryVolInfoFile failed 0x%lx\n",rc));
|
||
|
|
||
|
// take away the refcount we put before calling this routine
|
||
|
AfpConnectionDereference(pConnDesc);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// note Macintosh can only handle 2Gb volume size. So kludge appropriately.
|
||
|
// System 7.5 and beyond has upped this to 4GB. Optionally handle this if
|
||
|
// the volume has that bit turned on.
|
||
|
//
|
||
|
|
||
|
BytesPerAllocationUnit =
|
||
|
(LONG)(fssizeinfo.BytesPerSector * fssizeinfo.SectorsPerAllocationUnit);
|
||
|
|
||
|
FreeBytes = RtlExtendedIntegerMultiply(fssizeinfo.AvailableAllocationUnits,
|
||
|
BytesPerAllocationUnit);
|
||
|
|
||
|
VolumeSize = RtlExtendedIntegerMultiply(fssizeinfo.TotalAllocationUnits,
|
||
|
BytesPerAllocationUnit);
|
||
|
|
||
|
ACQUIRE_SPIN_LOCK(&pConnDesc->cds_ConnLock, &OldIrql);
|
||
|
pConnDesc->cds_QuotaLimit = VolumeSize;
|
||
|
pConnDesc->cds_QuotaAvl = FreeBytes;
|
||
|
RELEASE_SPIN_LOCK(&pConnDesc->cds_ConnLock, OldIrql);
|
||
|
|
||
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_INFO,
|
||
|
("afpUpdateDiskQuotaInfo: Conn %lx Limit=%ld, Available=%ld\n",
|
||
|
pConnDesc,pConnDesc->cds_QuotaLimit.LowPart,pConnDesc->cds_QuotaAvl.LowPart));
|
||
|
|
||
|
// take away the refcount we put before calling this routine
|
||
|
AfpConnectionDereference(pConnDesc);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|