From 1ed0460435ec05150526c951d1ff32dd3b5a9c39 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Tue, 28 Nov 2006 09:16:36 +0000 Subject: [PATCH] windows-smb-dont-crash-vista-20061128 Unlike previous versions of the OS, Vista performs a shutdown on the Microsoft Loopback adapter just like it would on a real adapter. This causes the smb_Listener threads to trigger a panic during a suspend/hibernate power event. The fix is to unbind from the network adapters in response to a suspend/hibernate power event and then rebind to the adapters when a resume power event is received. Note that the resume events are not reliably delivered so it is possible the afs service will not be accessible. However, this is the best we can do. --- src/WINNT/afsd/afsd_service.c | 20 ++++- src/WINNT/afsd/smb.c | 137 ++++++++++++++++++++++++++++------ src/WINNT/afsd/smb.h | 5 ++ 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/WINNT/afsd/afsd_service.c b/src/WINNT/afsd/afsd_service.c index d755f5e8c..09941a4f5 100644 --- a/src/WINNT/afsd/afsd_service.c +++ b/src/WINNT/afsd/afsd_service.c @@ -232,6 +232,12 @@ afsd_ServiceControlHandlerEx( DWORD dummyLen, doTrace; long code; DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED; + OSVERSIONINFO osVersion; + + /* Get the version of Windows */ + memset(&osVersion, 0x00, sizeof(osVersion)); + osVersion.dwOSVersionInfoSize = sizeof(osVersion); + GetVersionEx(&osVersion); switch (ctrlCode) { @@ -318,23 +324,33 @@ afsd_ServiceControlHandlerEx( /* allow remaining case PBT_WhatEver */ case PBT_APMSUSPEND: - afsi_log("SERVICE_CONTROL_APMSUSPEND"); + afsi_log("SERVICE_CONTROL_APMSUSPEND"); + if (osVersion.dwMajorVersion >= 6) + smb_StopListeners(); dwRet = NO_ERROR; break; case PBT_APMSTANDBY: afsi_log("SERVICE_CONTROL_APMSTANDBY"); + if (osVersion.dwMajorVersion >= 6) + smb_StopListeners(); dwRet = NO_ERROR; break; case PBT_APMRESUMECRITICAL: afsi_log("SERVICE_CONTROL_APMRESUMECRITICAL"); + if (osVersion.dwMajorVersion >= 6) + smb_RestartListeners(); dwRet = NO_ERROR; break; case PBT_APMRESUMESUSPEND: afsi_log("SERVICE_CONTROL_APMRESUMESUSPEND"); + if (osVersion.dwMajorVersion >= 6) + smb_RestartListeners(); dwRet = NO_ERROR; break; case PBT_APMRESUMESTANDBY: afsi_log("SERVICE_CONTROL_APMRESUMESTANDBY"); + if (osVersion.dwMajorVersion >= 6) + smb_RestartListeners(); dwRet = NO_ERROR; break; case PBT_APMBATTERYLOW: @@ -355,6 +371,8 @@ afsd_ServiceControlHandlerEx( break; case PBT_APMRESUMEAUTOMATIC: afsi_log("SERVICE_CONTROL_APMRESUMEAUTOMATIC"); + if (osVersion.dwMajorVersion >= 6) + smb_RestartListeners(); dwRet = NO_ERROR; break; default: diff --git a/src/WINNT/afsd/smb.c b/src/WINNT/afsd/smb.c index 54babb9e4..23971ef52 100644 --- a/src/WINNT/afsd/smb.c +++ b/src/WINNT/afsd/smb.c @@ -31,7 +31,8 @@ /* These characters are illegal in Windows filenames */ static char *illegalChars = "\\/:*?\"<>|"; -int smbShutdownFlag = 0; +static int smbShutdownFlag = 0; +static int smb_ListenerState = SMB_LISTENER_STOPPED; int smb_LogoffTokenTransfer; time_t smb_LogoffTransferTimeout; @@ -7858,7 +7859,7 @@ void smb_Listener(void *parmp) GetComputerName(cname, &cnamelen); _strupr(cname); - while (1) { + while (smb_ListenerState == SMB_LISTENER_STARTED) { memset(ncbp, 0, sizeof(NCB)); flags = 0; @@ -7878,12 +7879,29 @@ void smb_Listener(void *parmp) code = Netbios(ncbp); - if (code != 0) - { + if (code == NRC_BRIDGE) { + if (smb_ListenerState == SMB_LISTENER_STOPPED || smbShutdownFlag == 1) { + ExitThread(1); + } + + osi_Log2(smb_logp, + "NCBLISTEN lana=%d failed with NRC_BRIDGE. Listener thread exiting.", + ncbp->ncb_lana_num, code); + + if (code != 0) + for (i = 0; i < lana_list.length; i++) { + if (lana_list.lana[i] == ncbp->ncb_lana_num) { + lana_list.lana[i] = 255; + break; + } + } + return; + } else if (code != 0) + { char tbuffer[256]; /* terminate silently if shutdown flag is set */ - if (smbShutdownFlag == 1) { + if (smb_ListenerState == SMB_LISTENER_STOPPED || smbShutdownFlag == 1) { ExitThread(1); } @@ -8080,6 +8098,8 @@ void smb_Listener(void *parmp) /* unlock */ lock_ReleaseMutex(&smb_ListenerLock); } /* dispatch while loop */ + + FreeNCB(ncbp); } /* initialize Netbios */ @@ -8091,12 +8111,6 @@ void smb_NetbiosInit() int delname_tried=0; int len; int lana_found = 0; - OSVERSIONINFO Version; - - /* Get the version of Windows */ - memset(&Version, 0x00, sizeof(Version)); - Version.dwOSVersionInfoSize = sizeof(Version); - GetVersionEx(&Version); /* setup the NCB system */ ncbp = GetNCB(); @@ -8160,7 +8174,9 @@ void smb_NetbiosInit() afsi_log("Netbios NCBADDNAME added new name >%s<",name); } - if (code == 0) code = ncbp->ncb_retcode; + if (code == 0) + code = ncbp->ncb_retcode; + if (code == 0) { afsi_log("Netbios NCBADDNAME succeeded on lana %d\n", lana); } @@ -8213,6 +8229,91 @@ void smb_NetbiosInit() FreeNCB(ncbp); } +void smb_StartListeners() +{ + int i; + int lpid; + thread_t phandle; + + if (smb_ListenerState == SMB_LISTENER_STARTED) + return; + + smb_ListenerState = SMB_LISTENER_STARTED; + + for (i = 0; i < lana_list.length; i++) { + if (lana_list.lana[i] == 255) + continue; + phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_Listener, + (void*)lana_list.lana[i], 0, &lpid, "smb_Listener"); + osi_assert(phandle != NULL); + thrd_CloseHandle(phandle); + } +} + +void smb_RestartListeners() +{ + if (smb_ListenerState == SMB_LISTENER_STARTED) + return; + + smb_NetbiosInit(); + smb_StartListeners(); +} + +void smb_StopListeners() +{ + NCB *ncbp; +#ifdef DJGPP + dos_ptr dos_ncb; +#endif /* DJGPP */ + int lana, code, l; + + if (smb_ListenerState == SMB_LISTENER_STOPPED) + return; + + smb_ListenerState = SMB_LISTENER_STOPPED; + + ncbp = GetNCB(); + + /* Unregister the SMB name */ + for (l = 0; l < lana_list.length; l++) { + lana = lana_list.lana[l]; + + memset(ncbp, 0, sizeof(*ncbp)); + ncbp->ncb_command = NCBDELNAME; + ncbp->ncb_lana_num = lana; + memcpy(ncbp->ncb_name,smb_sharename,NCBNAMSZ); +#ifndef DJGPP + code = Netbios(ncbp); +#else /* DJGPP */ + code = Netbios(ncbp, dos_ncb); +#endif /* !DJGPP */ + + afsi_log("Netbios NCBDELNAME lana=%d code=%d retcode=%d complete=%d", + lana, code, ncbp->ncb_retcode, ncbp->ncb_cmd_cplt); + + /* and then reset the LANA; this will cause the listener threads to exit */ + ncbp->ncb_command = NCBRESET; + ncbp->ncb_callname[0] = 100; + ncbp->ncb_callname[2] = 100; + ncbp->ncb_lana_num = lana_list.lana[l]; + code = Netbios(ncbp); + if (code == 0) + code = ncbp->ncb_retcode; + if (code != 0) { + afsi_log("Netbios NCBRESET lana %d error code %d", lana_list.lana[l], code); + } else { + afsi_log("Netbios NCBRESET lana %d succeeded", lana_list.lana[l]); + } + + /* mark the adapter invalid */ + lana_list.lana[l] = 255; /* invalid lana */ + } + + lana_list.length = 0; + FreeNCB(ncbp); + Sleep(1000); /* give the listener threads a chance to exit */ +} + void smb_Init(osi_log_t *logp, char *snamep, int useV3, int LANadapt, int nThreads , void *aMBfunc @@ -8461,6 +8562,7 @@ void smb_Init(osi_log_t *logp, char *snamep, int useV3, int LANadapt, * performance by removing the network access and works around a bug * seen at sites which are using a MIT Kerberos principal to login * to machines joined to a non-root domain in a multi-domain forest. + * MsV1_0SetProcessOption was added in Windows XP. */ PVOID pResponse = NULL; ULONG cbResponse = 0; @@ -8551,14 +8653,7 @@ void smb_Init(osi_log_t *logp, char *snamep, int useV3, int LANadapt, /* Start listeners, waiters, servers, and daemons */ - for (i = 0; i < lana_list.length; i++) { - if (lana_list.lana[i] == 255) - continue; - phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_Listener, - (void*)lana_list.lana[i], 0, &lpid, "smb_Listener"); - osi_assert(phandle != NULL); - thrd_CloseHandle(phandle); - } + smb_StartListeners(); phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_ClientWaiter, NULL, 0, &lpid, "smb_ClientWaiter"); @@ -8703,7 +8798,7 @@ void smb_Shutdown(void) } } lock_ReleaseWrite(&smb_rctLock); - + FreeNCB(ncbp); TlsFree(smb_TlsRequestSlot); } diff --git a/src/WINNT/afsd/smb.h b/src/WINNT/afsd/smb.h index 60cadfe59..b05e81652 100644 --- a/src/WINNT/afsd/smb.h +++ b/src/WINNT/afsd/smb.h @@ -721,6 +721,11 @@ extern DWORD smb_ServerExceptionFilter(void); extern void smb_UpdateServerPriority(void); extern void smb_SetRequestStartTime(void); extern void smb_ResetServerPriority(void); +extern void smb_RestartListeners(void); +extern void smb_StopListeners(void); + +#define SMB_LISTENER_STOPPED 0 +#define SMB_LISTENER_STARTED 1 /* include other include files */ #include "smb3.h" -- 2.39.5